使用 GC 和 LOH 正确识别内存泄漏

Properly identifying memory leak with GC and LOH

可能有人以几种不同的方式问过这个问题,但这对我来说是一个相对较新的领域,所以请原谅我是否多余并指出我的方向。

基本上我已经创建了一个数据收集引擎,它可以获取高速数据(每秒高达数千个点)并将它们存储在数据库中。

数据库是动态的,因此输入数据库的语句也是在代码中动态创建的,这反过来又需要大量的字符串操作。但是,所有字符串都在异步事件处理程序方法的范围内声明,因此一旦方法完成,它们就应该超出范围。

随着此应用程序的运行,根据任务管理器/进程资源管理器,它的内存使用量缓慢但稳定地增加,因此似乎有些东西没有得到正确处理和/或收集。

如果我附加 CDB -p(是的,我正在从 CLR 加载 sos.dll)并执行 !dumpheap,我看到其中的大部分也被 System.String 使用如果我 !dumpheap -type System.String 和 !do 地址,我会看到确切的字符串(SQL 语句)。

但是,如果我对任何地址执行 !gcroot,我会得到 "Found 0 unique roots (run '!GCRoot -all' to see all roots)." 如果我按照它的建议尝试,我会得到 "Invalid argument -all" O.o

所以在谷歌搜索和一些关于无根对象最终将被 GC 收集的争论之后,这不是问题..我看了看,我的问题似乎有 84% 是在 LOH 上(根据您查看的线程,可能会或可能不会为 GC 处理 除非 机器上存在内存限制,或者我明确告诉它进行收集,这被认为是错误的我能找到的一切)

所以我需要知道的是,这基本上是真的吗,这不是内存泄漏,它只是系统将东西留在那里,直到它必须被回收,如果是这样,我该如何告诉它我有或没有合法的内存泄漏。

这是我第一次在应用程序外部使用调试器,因为我以前从未遇到过这类问题,所以我对这部分很陌生,这是一次学习经历。

应用程序是用 VS2012 Pro、C# 编写的,它是多线程的,控制台应用程序正在包装 API 进行测试,但最终将成为 Windows 服务。

您读到的是真实的,托管应用程序使用内存模型,在该模型中,对象会堆积起来,直到达到某个内存阈值(根据系统上的物理内存量和应用程序的实际增长率计算),之后all(*) "dead" 对象被剩余的有用内存压缩,使其成为分配速度的一个连续块。

所以,是的,不要担心你的内存在稳步增加,直到你增加了几十 MB 并且没有发生收集。

(*) - 实际上由于多个内存池(基于对象大小和生命周期长度)而变得更加复杂,这样系统就不会不断地探测寿命很长的对象,以及终结器。当一个对象有一个终结器,而不是被释放,内存被压扁,但它们被移动到一个特殊的队列,终结器队列,在那里他们等待终结器在 UI 上 运行线程(记住 GC 运行s 在一个单独的线程上),然后它才最终被释放。