Go 1.12 关于内存释放的一个"改进"

简介: Go 1.12 关于内存释放的一个"改进"

概述:


“改进” 加引号,因为社区反馈问题多多 弊大于利, 于是Go 1.16又改了回去…

问题包括但不仅限于:

  • 引发用户体验的问题:Go issues 上总是出现以为内存泄露,但其实只是未满足条件,内存没有马上释放的案例。
  • 混淆统计信息和监控工具的情况:在 Grafana 等监控上,发现容器进程内存较高,释放很慢,告警了,很慌。
  • 导致与内存使用有关联的个别管理系统集成不良:例如 Kubernetes HPA ,或者自定义了扩缩容策略这类模式,难以评估。
  • 挤压同主机上的其他应用资源:并不是所有的 Go 程序都一定独立跑在单一主机中,自然就会导致同一台主机上的其他应用受到挤压,这是难以评估的。

(参考自 Go1.16 新特性:详解内存管理机制的变更,你需要了解)

出现这个问题 需要同时满足 Go版本在 1.12~1.15之间(都是闭区间),同时是Linux系统且内核版本>=4.5

通过设置环境变量 GODEBUG:madvdontneed=1即可关闭该功能


Go 1.12~Go 1.15


Go支持两种内存回收方式,即 MADV_DONTNEEDMADV_FREE

其中MADV_FREE

Go 1.12

版本引入,官网上的介绍如下:

1
On Linux, the runtime now uses MADV_FREE to release unused memory. This is more efficient but may result in higher reported RSS. The kernel will reclaim the unused data when it is needed. To revert to the Go 1.11 behavior (MADV_DONTNEED), set the environment variable GODEBUG=madvdontneed=1.

大意就是使用MADV_FREE方式,程序内存不会立刻回收,即

RSS值

不会立刻下降,只有当OS内存紧缺时才会回收Go程序的内存返回给OS;

而Go 1.11以及之前的版本默认采用的是 MADV_DONTNEED方式,程序RSS值下降很快。因此如果需要使程序内存占用下降很快的话,可设置环境变量GODEBUG=madvdontneed=1

注:Go 1.12~1.15版本的这项功能, 仅限于 Linux平台; 且需要内核在 4.5及之后的版本,才默认使用MADV_FREE方式。 如果不支持会用退用之前默认的MADV_DONTNEED

runtime/mem_linux.go源码里注释如下:

go

123456789101112
var advise uint32if debug.madvdontneed != 0 { advise = _MADV_DONTNEED} else { advise = atomic.Load(&adviseUnused)}if errno := madvise(v, n, int32(advise)); advise == _MADV_FREE && errno != 0 {  // MADV_FREE was added in Linux 4.5. Fall back to MADV_DONTNEED if it is  // not supported. atomic.Store(&adviseUnused, _MADV_DONTNEED) madvise(v, n, _MADV_DONTNEED)}

image.png


参考:

Go 1.12中出现的top命令RES参数异常增高的问题

Go进程的HeapReleased上升,但是RSS不下降造成内存泄漏?—- 并不是内存泄露

Go 1.12 关于内存释放的一个改进—并不是内存泄露


Go 1.16又改了回去


Go 1.16

中,对这个问题又做了优化,

在 Linux 上,runtime 现在默认会迅速地(使用 MADV_DONTNEED)向操作系统释放内存,而不是在操作系统面临内存压力时(使用 MADV_FREE)惰性地释放内存。

这意味着像 RSS 这样的进程级内存统计信息将更准确地反映 Go 进程所使用的物理内存数量。因此 Go1.16 中,不再需要配置 GODEBUG=madvdontneed=1 来改善内存监控行为。

通过

123456789
func parsedebugvars() {    // defaults    debug.cgocheck = 1    debug.invalidptr = 1    if GOOS == "linux" {        debug.madvdontneed = 1    }  ...}

又 直接指定回了

debug.madvdontneed = 1

也算是Go Team走的一段弯路

参考&值得阅读:

列举一些 Go1.16 中可能对大家有影响的变化

Go1.16 新特性:详解内存管理机制的变更,你需要了解

Go 1.16中值得关注的几个变化


作者:fliter

链接:https://juejin.cn/post/6949039357525229604

来源:稀土掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

目录
相关文章
|
4天前
|
存储 Go iOS开发
掌握Go语言:探索Go语言指针,解锁高效内存操作与动态数据结构的奥秘(19)
掌握Go语言:探索Go语言指针,解锁高效内存操作与动态数据结构的奥秘(19)
|
4天前
|
存储 缓存 安全
Go语言内存模型深度解析
【2月更文挑战第16天】Go语言以其简洁的语法、强大的并发编程能力和高效的内存管理而备受开发者青睐。本文将对Go语言的内存模型进行深度解析,探讨其内存布局、内存分配与回收机制以及内存安全等方面的内容,帮助读者更好地理解和应用Go语言的内存管理特性。
|
7月前
|
存储 编译器 Go
Go 语言内存逃逸案例
Go 语言内存逃逸案例
36 0
|
7月前
|
存储 缓存 Go
Go语言开发者必读:内存缓存技巧
Go语言开发者必读:内存缓存技巧
69 0
|
4天前
|
算法 Java Go
Go vs Java:内存管理与垃圾回收机制对比
对比了Go和Java的内存管理与垃圾回收机制。Java依赖JVM自动管理内存,使用堆栈内存并采用多种垃圾回收算法,如标记-清除和分代收集。Go则提供更多的手动控制,内存分配与释放由分配器和垃圾回收器协同完成,使用三色标记算法并发回收。示例展示了Java中对象自动创建和销毁,而Go中开发者需注意内存泄漏。选择语言应根据项目需求和技术栈来决定。
|
4天前
|
数据可视化 Java 测试技术
【Go语言专栏】Go语言中的内存泄漏检测与修复
【4月更文挑战第30天】Go语言内存泄漏详解:概念、原因、检测与修复。内存泄漏由忘记释放内存、循环引用等引起,Go通过垃圾回收机制管理内存,但仍有泄漏风险。检测方法包括pprof、可视化工具、代码审查和单元测试。修复策略涉及优化代码、使用defer、减少全局变量、弱引用及及时释放资源。实践案例分析有助于理解和解决问题。了解内存管理,防止泄漏,提升Go应用性能和稳定性。
|
4天前
|
监控 算法 测试技术
【Go语言专栏】Go语言的性能优化与内存分析
【4月更文挑战第30天】本文探讨了Go语言的性能优化策略和内存分析方法。性能优化原则包括基准测试、分析瓶颈、避免过早优化和持续监控。优化策略涉及减少内存分配、避免内存逃逸、利用并发、优化算法和数据结构以及减少系统调用。内存分析借助于Go的`pprof`工具、内存分配跟踪和第三方工具,以发现内存泄漏和管理问题。通过这些方法,开发者能提升Go程序效率和资源利用率。
|
4天前
|
Java Go 区块链
【Go语言专栏】Go语言中的指针与内存管理
【4月更文挑战第30天】Go语言,由Google开发,是一种静态强类型、编译型、并发型语言,具有垃圾回收功能,常用于云计算、微服务、区块链等领域。本文聚焦Go中的指针和内存管理。指针表示变量内存地址,可用于直接访问和修改变量,如示例代码所示。指针运算有限制,仅支持相同类型变量和数组元素访问。内存管理由Go运行时的垃圾回收机制处理,自动回收无引用对象,简化管理但引入性能开销。可通过`runtime.GC()`手动触发垃圾回收。
|
4天前
|
监控 Java 编译器
Go语言内存与并发性能综合优化策略
【2月更文挑战第11天】Go语言以其高效的并发处理能力和简洁的内存管理机制成为了现代软件开发中的热门选择。然而,在实际应用中,如何综合优化Go程序的内存使用和并发性能,仍然是一个值得探讨的话题。本文将深入探讨Go语言内存与并发性能的综合优化策略,包括内存布局优化、并发模式设计、资源池化以及性能监控与分析等方面,旨在帮助开发者全面提升Go程序的整体性能。
|
4天前
|
Java 编译器 Go
Go语言内存管理优化实践
【2月更文挑战第11天】随着Go语言在各个领域的应用日益广泛,对其性能的要求也越来越高。内存管理作为影响程序性能的关键因素之一,对Go语言的优化显得尤为重要。本文将深入探讨Go语言的内存管理机制,并提供一系列实用的内存优化策略,帮助开发者更加高效地利用内存资源,提升Go程序的整体性能。