今天我在看 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. springboot(七)JavaMail发送邮件

    JavaMail简介: JavaMail是SUN提供给广大Java开发人员的一款邮件发送和接受的一款开源类库,支持常用的邮件协议,如:SMTP.POP3.IMAP,开发人员使用JavaMail编写邮件 ...

  2. Python3网络爬虫开发实战PDF高清完整版免费下载|百度云盘

    百度云盘:Python3网络爬虫开发实战高清完整版免费下载 提取码:d03u 内容简介 本书介绍了如何利用Python 3开发网络爬虫,书中首先介绍了环境配置和基础知识,然后讨论了urllib.req ...

  3. js:事件(注册、解绑、DOM事件流、事件对象、事件委托)

    1.注册事件 (1)传统方式注册事件 <body> <button id="b1">请点击</button> <script> va ...

  4. Java数据结构和算法(1)之队列

    1.队列的基本概念 队列(queue)是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表.进行插 ...

  5. CORS跨域操作cookie

    CORS 跨域 在服务端设置响应头 ACAO( Access-Control-Allow-Origin )即可 前端代码,运行在 8080 端口上 $.ajax({ url:'http://local ...

  6. 做完这套面试题,你才敢说懂Excel

    下面的题目来自一份商品专员的面试题,其中有涉及到条件格式.自定义排序.数据验证制作下拉菜单.查找引用类函数.文本提取函数等等技能. 满满的干货技能可不是商品专员“专属”,如果你能熟练掌握,在平日工作中 ...

  7. luogu P1446 [HNOI2008]Cards burnside引理 置换 不动点

    LINK:Cards 不太会burnside引理 而这道题则是一个应用. 首先 一个非常舒服的地方是这道题给出了m个本质不同的置换 然后带上单位置换就是m+1个置换. burnside引理: 其中D( ...

  8. C/C++编程笔记:C语言成绩管理系统!链式结构的管理系统源码分享

    最近很多同学因为学校的要求,需要完成自己的那个C语言课程设计,于是就有很多人私信或者加我私聊我,问的最多的还是<学生成绩管理系统>,其实当你项目写多了你就会发现:其实各类的管理系统都离不开 ...

  9. async和await的使用总结 ~ 竟然一直用错了c#中的async和await的使用。。

    对于c#中的async和await的使用,没想到我一直竟然都有一个错误.. ..还是总结太少,这里记录下. 这里以做早餐为例 流程如下: 倒一杯咖啡. 加热平底锅,然后煎两个鸡蛋. 煎三片培根. 烤两 ...

  10. 基于Python+Requests+Pytest+YAML+Allure实现接口自动化

    本项目实现接口自动化的技术选型:Python+Requests+Pytest+YAML+Allure ,主要是针对之前开发的一个接口项目来进行学习,通过 Python+Requests 来发送和处理H ...