Go 切片的一种有趣内存泄漏方式
今天我在看 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
Go 切片的一种有趣内存泄漏方式的更多相关文章
- web 开发之js---理解并解决IE的内存泄漏方式
程序当中任何编程内存操作不当都会导致内存泄漏 http://wenku.baidu.com/link?url=8ba4UIn1aaevxTagH-F4vID79-bAfxdcLdeujGFn7PBnv ...
- Dictionary带来的一种隐式内存泄漏
当心Dictionary带来的一种隐式内存泄漏 最近在看Dictionary的源代码的时候, 突然想到Dictionary的不当使用中有一种隐含内存泄漏的可能. 简化使用场景 小A正在写一个简单的图书 ...
- C的内存泄漏检测
一,Windows平台下的内存泄漏检测 检测是否存在内存泄漏问题 Windows平台下面Visual Studio 调试器和 C 运行时 (CRT) 库为我们提供了检测和识别内存泄漏的有效方法,原理大 ...
- iOS开发 如何检查内存泄漏
本文转载至 http://mobile.51cto.com/iphone-423391.htm 在开发的时候内存泄漏是不可避免的,但是也是我们需要尽量减少的,因为内存泄漏可能会很大程度的影响程序的稳定 ...
- hashCode竟然不是根据对象内存地址生成的?还对内存泄漏与偏向锁有影响?
起因 起因是群里的一位童鞋突然问了这么问题: 如果重写 equals 不重写 hashcode 会有什么影响? 这个问题从上午10:45 开始陆续讨论,到下午15:39 接近尾声 (忽略这形同虚设的马 ...
- 为什么各大厂自研的内存泄漏检测框架都要参考 LeakCanary?因为它是真强啊!
请点赞关注,你的支持对我意义重大. Hi,我是小彭.本文已收录到 GitHub · AndroidFamily 中.这里有 Android 进阶成长知识体系,有志同道合的朋友,关注公众号 [彭旭锐] ...
- JAVA内存泄漏解决办法
JVM调优工具 Jconsole,jProfile,VisualVM Jconsole : jdk自带,功能简单,但是可以在系统有一定负荷的情况下使用.对垃圾回收算法有很详细的跟踪.详细说明参考这里 ...
- Java实现操作系统中四种动态内存分配算法:BF+NF+WF+FF
1 概述 本文是利用Java实现操作系统中的四种动态内存分配方式 ,分别是: BF NF WF FF 分两部分,第一部分是介绍四种分配方式的概念以及例子,第二部分是代码实现以及讲解. 2 四种分配方式 ...
- glibc内存管理方式
程序员接触的内存空间和系统接触的物理内存空间是有所区别的.对于一般进程来讲,他面对的是一个线性虚拟内存空间:地址从0到最大值.每一个进程面对的虚拟内存空间都是一样的,都享有全部的内存地址.虚拟内存空间 ...
随机推荐
- element上传功能携带参数
在写element的上传功能时,需要对上传的文件携带参数,但是参数比较多,就需要一个对象合并的方法,Object.assign() Object.assign(target, source1, sou ...
- Day01_SpringBoot
学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"乐优商城"获取视频和教程资料! b站在线视频 0.学习 ...
- 遍历多个 txt 文件进行获取值
import random def load_config(path): with open(path,'r') as tou: return [line for line in tou.readli ...
- PHP sqrt() 函数
实例 返回不同数的平方根: <?phpecho(sqrt(0) . "<br>");echo(sqrt(1) . "<br>"); ...
- PHP srand() 函数
实例 播种随机数生成器: <?phpsrand(mktime());echo(rand());?>高佣联盟 www.cgewang.com 定义和用法 srand() 函数播种随机数生成器 ...
- javascript对象笔记
JS对象 对象是一个具体的事物 在JS中对象是一组无序属性和方法的集合例如字符串,数组,函数等等 对象是由属性和方法组成的 属性:是事物的特征,在对象中用属性来表示一般 ...
- 23、Interpreter 解释器模式
1.Interpreter 解释器模式 解释器模式是一种使用频率相对较低但学习难度较大的设计模式,它用于描述如何使用面向对象语言构成一个简单的语言解释器.在某些情况下,为了更好地描述某一些特定类型的问 ...
- ECS7天实践进阶训练营Day2:基于阿里云ECS部署MediaWiki
一.概述 MediaWiki是全球最著名的开源Wiki程序,运行于PHP+MySQL环境,MediaWiki从2002年被作为维基百科的系统软件,并由大量其他应用实例(例如萌娘百科),因此MediaW ...
- 2020-04-07:说出Servlet的生命周期,并说出Servlet和CGI的区别。
Servlet的生命周期分为5个阶段:实例化:Servlet容器创建Servlet类的实例.初始化:该容器调用init()方法,通常会申请资源.服务:由容器调用service()方法,(也就是doGe ...
- 将Asp.Net Core3.1项目,使用Docker 部署到Centos 8
一.准备工具 Win 10 Centos 8 Visual Studio 2019 Docker Desktop 下载地址:https://download.docker.com/win/stabl ...