Liam W
封面

夏虫不可语冰

作者
王亮·发表于 3 年前

此次事件让我再次体会到了什么是“夏虫不可语冰”。博客园有些人压根没看完你的文章,带着已有认知断章取义不暇思索上来就是喷。不过,以前也遇到过,现在也慢慢习惯了。

对于托管堆内存泄漏的说法,不管认不认同,我还是希望我的粉丝们能知道有这么个定义,有这么一回事。相信你们以后在阅读文章或与人交流的时候,还会看到或听到有人管这种现象叫内存泄漏,届时也不至于蒙圈。虽然这是个很小的知识点。

C# 中的匿名函数是头等对象,可以作为变量使用,也可以作为参数传递。Task.Run 中使用匿名函数作为参数就是一个典型的应用场景。如果匿名函数捕获了所在类的成员,对外部调用者来说,这个类的实例什么时候被释放是无法预知的(除非你十分清楚这个类的内部实现),但你知道它最终是会被回收的。有人把这种可能导致对象延迟回收的现象定义为内存泄漏,也有很多人不认为是内存泄漏。这是定义问题,如果你能用一个词来更好地定义这种现象也是可以接受的。

Many share the opinion that managed memory leaks are not memory leaks at all since they are still referenced and theoretically can be de-allocated. It’s a matter of definition and my point of view is that they are indeed memory leaks. They hold memory that can’t be allocated for another instance and will eventually cause an out-of-memory exception.

来源:dwz.date/d48U

很早之前我就看过类似的文章,把匿名方法捕获类成员导致延迟回收的现象定义为内存泄漏。上面引用的这篇是最近阅读到的一篇,这位外国作者写了很多 .NET 内存管理相关的文章,都值得一读。

如果只站在操作系统的角度看内存是否被掌控来定义内存泄漏,.NET 托管堆几乎就没有不被掌控的内存占用(托管堆之所以叫“托管”就是 CRL 会自动管理)。而之所以有人定义了托管内存泄漏,是因为对 CLR 来说,在局部视角不再需要使用的对象依然被其它实例引用,可能导致 GC 恰巧在搜索 Root 的时候不能把这个对象所占内存分配给其它实例。对于 CLR 来说,它对托管对象有全权“托管”之责。如果在被引用期间,恰巧 GC 在搜索 Root,就会导致 GC 错过了一次回收该对象的机会,此次任务就“失职”了,只能在下一次搜索时回收它。但如果使用了本地(局部)变量,就可以避免这种情况。

大部分场景,我们并不需要在意这一点性能,何况这是个概率很低的事件。但是它值得引起注意,哪怕只有万分之一的概率。在一些特别的场景下(比如高并发),遇到这个问题我们需要知道使用本地变量来优化我们的程序。

这个现象是不是内存泄漏,只是个定义问题,如果觉得这个定义不妥,可以不认同这个定义。正如 @楚人Le 说的,你也可以把它定义为“空间泄漏”:

我倾向于认为这属于内存泄漏。Wikipedia上关于Memory leak有这样一句话:“A space leak occurs when a computer program uses more memory than necessary. In contrast to memory leaks, where the leaked memory is never released, the memory consumed by a space leak is released, but later than expected”。如果我们非常较真的话,或许可以使用“空间泄露”这个词。当计算机程序使用超过所需的内存时,就会发生空间泄漏。与内存泄漏相比,空间泄漏所消耗的内存会被释放,但会比预期的要晚。

这只是个定义,这只是个定义,这只是个定义。不必纠结,更没必要因为不认同就恶语相向。

今天的文章夹杂着一点情绪,请大家见谅。要说一点都不在意那些评论,那肯定是假的,心中多少还是会有些不愉悦的。面对网络喷子,如果内心不够强大,还真是不敢随便发表文章。这次也让我意识到,以后有争议性的文章少写,大家也是一样。

自媒体很多大V都经历过这种时段,和他们相比我这个虾兵小卒遇到的这么三两次不算什么。正如一线码农大佬说的,有忠粉就有黑粉。既然走上这条路,必然也要经历这些,这便是成长。

每每遇到这种事情,就特别想和铁粉们说声:感谢!感谢你们一路以来的陪伴、理解、支持和鼓励,感谢你们愿意和我一起学习和成长。