今天我在看 Prashant Varanasi 的 Go 发布会演讲:使用火焰图进行生产分析(Analyzing production using Flamegraphs),在演讲开始的第 28 分钟他提到了一种涉及切片的有趣且棘手的内存泄漏。为了自我提升,我将在这里写一下该内存泄漏的一种形式,并说明它是如何发生的。

首先,对于像 Go 这样的垃圾收集语言来说,内存泄漏是保留了对对象的非预期引用所造成的。垃圾收集器会帮你寻找并释放对象,但前提是它们事实上并没有被使用。如果你保留了对它们的引用,它们会留下来。 有时最终结果很简单(也行你故意保留一个较小的结构,但没意识到它引用了一个较大的结构),但有时候这种保留隐藏在某些东西的运行时实现里。这改变了我们对切片的看法。

简化之后,Prashant 处理的代码在一个切片中维护了当前在使用的元素的集合。当一个元素不再被使用时,它被转移到了切片的末尾,然后切片被截断而缩小(保持不变的是切片只保留使用的元素)。然而,缩小切片并不会缩小其依赖的数组,用 Go 的术语来说,减小了切片的长度但是并没有减少容量。由于底层依赖的数组没有变动,而该数组保留了一个理论上已经被丢弃了的元素的引用,以及该元素所引用的所有其他对象。即使是代码不可见的引用被保留,Go 垃圾收集器仍然会将该元素看做是还在使用中。代码认为以及被丢弃了的元素实际上并没有被释放,这就造成了内存泄漏。

现在,我查看了 Go 运行时和编译器代码,并对该问题进行了一些思考,我清楚地意识到了这是任何切片截断的通用问题。Go 绝不会尝试缩小切片的底层数组,而且通常来说这样做是不可能的,因为一个底层数组可能被多个切片或其他引用所共享。这显然会严重影响指向包含指针的对象的切片,但对于指向普通的旧数据的切片也可能很重要,尤其是当它们比较大的时候(比如你有一个 Point 的切片,每个 Point 有三个浮点数)。

对于包含指针或者包含持有着指针的结构的切片来说,明显的修复方式(这是Uber 代码中采用的修复方式)是在截断切片之前将末尾的指针置为空。这样保留了完整的底层数组,但抛弃了对其他内存的引用,而这些其他的内存是真正内存泄漏的地方。

对于实际的底层数组可能会有大量内存消耗的切片来说,我想到可能有两种做法,一种特殊,一种通用。特殊的一种是检查代码中“大小截断为零”的情况,并专门将切片本身置为空,而不是仅仅使用标准的切片截断功能来截断。通用的做法是明确地强制使用切片拷贝而不是仅仅截断(就如我对切片可变性的评论提到的)。强制使用拷贝所带来的缺点是,某些时候可能会带来更大的开销。你可以通过仅在切片的容量远远超出新切片的长度的时候才强制使用拷贝的方式来进行优化。

补充:(对垃圾收集而言)三索引的切片截断是危险的

Go 切片表达式允许在起终点之外,使用很少使用的第三个索引来设置新切片的容量。你也许会想到采用这种形式限制切片,来作为解决垃圾收集问题的办法:

slc = slc[:newlen:newlen]

不幸的是,这样并不会达到你想要的效果,而且会适得其反。设置新切片的容量完全不会改变底层的依赖数组,也不会让 Go 分配一个新的内存,但这却意味着你无法获取数组大小的信息(否则可以通过切片的容量来得到它)。这样造成的唯一影响是强制随后的 append() 重新分配新的底层数组。


via: https://utcc.utoronto.ca/~cks/space/blog/programming/GoSlicesMemoryLeak

作者:ChrisSiebenmann 译者:dust347 校对:polaris1119

本文由 GCTT 原创编译,Go语言中文网 荣誉推出

Go 切片的一种有趣内存泄漏方式的更多相关文章

  1. web 开发之js---理解并解决IE的内存泄漏方式

    程序当中任何编程内存操作不当都会导致内存泄漏 http://wenku.baidu.com/link?url=8ba4UIn1aaevxTagH-F4vID79-bAfxdcLdeujGFn7PBnv ...

  2. Dictionary带来的一种隐式内存泄漏

    当心Dictionary带来的一种隐式内存泄漏 最近在看Dictionary的源代码的时候, 突然想到Dictionary的不当使用中有一种隐含内存泄漏的可能. 简化使用场景 小A正在写一个简单的图书 ...

  3. C的内存泄漏检测

    一,Windows平台下的内存泄漏检测 检测是否存在内存泄漏问题 Windows平台下面Visual Studio 调试器和 C 运行时 (CRT) 库为我们提供了检测和识别内存泄漏的有效方法,原理大 ...

  4. iOS开发 如何检查内存泄漏

    本文转载至 http://mobile.51cto.com/iphone-423391.htm 在开发的时候内存泄漏是不可避免的,但是也是我们需要尽量减少的,因为内存泄漏可能会很大程度的影响程序的稳定 ...

  5. hashCode竟然不是根据对象内存地址生成的?还对内存泄漏与偏向锁有影响?

    起因 起因是群里的一位童鞋突然问了这么问题: 如果重写 equals 不重写 hashcode 会有什么影响? 这个问题从上午10:45 开始陆续讨论,到下午15:39 接近尾声 (忽略这形同虚设的马 ...

  6. 为什么各大厂自研的内存泄漏检测框架都要参考 LeakCanary?因为它是真强啊!

    请点赞关注,你的支持对我意义重大. Hi,我是小彭.本文已收录到 GitHub · AndroidFamily 中.这里有 Android 进阶成长知识体系,有志同道合的朋友,关注公众号 [彭旭锐] ...

  7. JAVA内存泄漏解决办法

    JVM调优工具 Jconsole,jProfile,VisualVM Jconsole : jdk自带,功能简单,但是可以在系统有一定负荷的情况下使用.对垃圾回收算法有很详细的跟踪.详细说明参考这里 ...

  8. Java实现操作系统中四种动态内存分配算法:BF+NF+WF+FF

    1 概述 本文是利用Java实现操作系统中的四种动态内存分配方式 ,分别是: BF NF WF FF 分两部分,第一部分是介绍四种分配方式的概念以及例子,第二部分是代码实现以及讲解. 2 四种分配方式 ...

  9. glibc内存管理方式

    程序员接触的内存空间和系统接触的物理内存空间是有所区别的.对于一般进程来讲,他面对的是一个线性虚拟内存空间:地址从0到最大值.每一个进程面对的虚拟内存空间都是一样的,都享有全部的内存地址.虚拟内存空间 ...

随机推荐

  1. IntelliJ IDEA 2019.3.4永久破解(持续更新)--已更新

    第一步,下载最新破解包: 链接: https://pan.baidu.com/s/1djUF9TiNZC4rIfxczxfIew 提取码: f521 把破解包两个文件放进bin目录下,这一步极为重要! ...

  2. Day14_RabbitMQ及数据同步

    学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"乐优商城"获取视频和教程资料! b站在线视频 0.学习 ...

  3. FPGA内部IP核DDS

    项目当中需要正弦信号与余弦信号,首先想到了DDS芯片,例如AD9833.AD9834.由于还需要用FPGA   做一些数据处理,后来干脆直接用FPGA 内部的DDSIP核,同时根据IP核内部的相位累加 ...

  4. 畅购商城(四):Lua、OpenResty、Canal实现广告缓存与同步

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 畅购商城(一):环境搭建 畅购商 ...

  5. 2080ti的各种问题

    1.循环登录 https://blog.csdn.net/miclover_feng/article/details/79201865 2.多版本cuda切换 https://blog.csdn.ne ...

  6. Ajax中关于xmlhttp.readyState的值及解释

    xmlhttp.readyState的值及解释:0:请求未初始化(还没有调用 open()).1:请求已经建立,但是还没有发送(还没有调用 send()).2:请求已发送,正在处理中(通常现在可以从响 ...

  7. “随手记”开发记录day12

    就我们团队昨天的讨论,今天进行更改. 今天我们先简单的更改了之前的粉色背景图,因为用户反应总览界面的“总览”二字,是深粉色背景不太美观.进过多次更改之后使颜色变得更舒适.

  8. 微信小程序--家庭记账小账本(四)

    今天的进展不太顺利,总的账单表,代码改了又改,最后决定用一个新的表,账单界面中弄了一天删除,发现都无法实现想要的效果,于是把账单界面的删除功能去了,就感觉大功告成的时候,发现收入和支出界面的删除也出现 ...

  9. JS 前端框架笔记

    Swiper轮播图插件使用     官网:https://www.swiper.com.cn/     先引用插件的css.js文件     然后找到需要使用的轮播图文件中去把HTML css和JS代 ...

  10. PXE安装与配置

    PXE 安装与配置 实验环境 VMware Fusion 虚拟机 node1有两块网卡, ens33(172.100.16.10)-->bridge, ens37-->vmnet4(192 ...