在本机代码中,内存由调用Malloc()/Free()的程序(或类似的api)显式管理。在托管代码中,有一个垃圾收集器(GC)。
GC并不意味着您不再有内存泄漏。它只意味着运行时在运行时检测到对象不再可访问后的某个任意点为您调用Free()。这有效地在安全方面出错(不能释放对象),以避免悬空指针问题,这通常会导致崩溃。因此,如果对象总是可访问的,即使GC也无法释放它,因此会出现内存泄漏。发生这种情况的一种常见方法是使用一些委托或静态引用。这是一个常见的关于主题的文章,所以在基本问题上我没有什么要补充的。查看Maoni或Chris Lyon的博客,了解GC的优点。

我想说的是,这是所产生的bug的一种范式转变。


本机内存管理中的常见错误:

Bug Effect
calling Free() twice on the same memory Crash, undefined behavior.
never calling Free()  (or Release()) Memory leak
Using a pointer after the original memory was freed (dangling pointer). Crash, Using undefined memory, arbitrary undefined behavior

注意:这些问题的范围已经超出了内存的使用范围,但由于这是一篇博客文章,而不是一篇论文,所以我的范围是有限的。(例如,如果我们使用C++析构函数而不是Free(),C++ dor会释放OS资源)

GC环境中的常见错误(托管代码):

Bug Effect
Not releasing a reference Memory leak
Finalizer called at random times Native resource not released when expected. This could be a resource leak. Or it could lock resources longer than expected, preventing others from using them)The finalizer can't be sure what state it's called in.

由于在GC环境中不能调用Free(),因此您不必再担心整个类的bug。

从一个角度来看,GCs基本上将bug从崩溃降级为内存泄漏。从debugger+工具的角度来看,用户代码中仍然存在bug,因此用户仍然需要工具来调试它们。但是,针对本机内存泄漏的传统工具在托管内存泄漏的世界中发挥得并不好。

malloc/free错误很难,因为罪魁祸首可能离失败点很远。例如,当Free()被调用两次时,您可能会发现第二个Free()崩溃。但是现在您可能需要找到对Free()的第一个调用,它可能发生在过去的某个任意时刻。一般来说,诊断非托管内存泄漏通常需要日志记录工具来跟踪malloc/free调用并对其进行分析;或者采用捕捉悬空指针的奇特内存页保护方案。(参见UMDH或PageHeap)

托管内存泄漏在概念上很容易,因为您可以通过检查当前堆,从单个快照中及时静态诊断它们。不幸的是,ICorDebug没有提供这个功能,所以这里没有很好的VS集成。实际上,您需要使用SOS。

总而言之:从bug的角度来看,从手动内存管理到GC的转变有两大好处:

-它降低了bug的严重性(从崩溃/未定义的行为到内存泄漏)

-它产生的bug更容易诊断,但需要改变工具范式来解决这些新的bug。

 

标签: none

添加新评论