问题描述
我正在测试以检测类的内存泄漏.我使用以下模式执行此操作:
I'm working on tests to detect memory leaks for classes. I do this with the following pattern:
- 在测试设置中获取初始内存(使用
GC.GetTotalMemory(true)
) - 执行操作,例如对象的实例化
- 处置对象(通过
useage
块) - 在测试拆卸中获取内存(再次使用相同的方法,所有对象都超出范围,因此应该被垃圾收集)
- 理想情况下,断言初始和结束内存之间的差异 = 0
在我的测试中使用内存分析工具,我看到 const 字符串(和相关)声明被计入测试的泄漏内存.我希望在进行初始内存测量时已经声明了这些值,这样它们就不会产生测试检测到的泄漏".但是,我不想两次运行操作代码,因为这会阻止我检测静态变量中变量的泄漏(例如没有清理不应该具有应用程序生命周期的单例模式对象).
Using memory profiling tools in my tests, I see that const string (and related) declarations are counted towards the leaked memory for a test. I'd like these values to be already declared when doing the initial memory measurement, such that they do not generate a 'leak' that is detected by the test. However, I don't want to run the action code twice, because that would prevent me from detecting leakage of variables in static variables (like not having cleaned up singleton pattern object that should not have application life-time).
有没有办法做到这一点?我是否必须为每个测试用例对这些变量进行手动补偿(其缺点是必须积极维护,从长远来看可能不会发生)?运行时是否有某个时刻可以用于此目的?还是我必须接受我无法测试接近 0 字节的内存泄漏?
Is there a way to do this? Do I have to do a manual compensation for these variables per test case (which has the drawback of having to be actively maintained, which will probably won't happen in the long run)? Is there a moment in runtime which I can use for this purpose? Or will I have to just accept that I cannot test for a memory leak near 0 bytes?
(对于那些感兴趣的人:我已经在使用 WeakReferences 和 livelyness 检查来监控我在测试中实例化的对象是否被正确垃圾收集,但这不会涵盖泄漏的私有状态)
(For those interested: I'm already using WeakReferences and livelyness checks to monitor if objects that I instantiate in the test are properly garbage collected, but this won't cover private state that leaks)
推荐答案
试图在托管内存中发现小的内存泄漏或多或少是徒劳的.相反,如果可能,请尝试更大的范围 - 将对象分配 100 000 次,然后查看清理是否有显着差异.
It's more or less futile trying to find small memory leaks in managed memory. Instead, if possible, try a much bigger scope - allocate the objects 100 000 times, and see if there's a significant difference on cleanup.
在实践中,您可能想要做一些完全不同的事情.内存量通常不是对托管泄漏的非常可靠的检查.相反,您可能希望让您的应用程序在一个循环中一遍又一遍地运行,并使用例如CLRProfiler 查看对象的生命周期.如果有可疑的东西持续数分钟甚至数小时,那可能是泄漏.这比仅检查内存大小给您一个更好的主意.
In practice, you probably want to do something completely different, though. Amount of memory is generally not a very reliable check for managed leaks. Instead, you might want to let your application run over and over in a cycle, and use e.g. the CLRProfiler to see the lifetime of objects. If there's something suspicious that hangs on for minutes or even hours, that's probably a leak. This gives you a much better idea than just checking memory size.
当然,一般来说,您不应该真正关心.寻找内存泄漏直到它真正伤害到您为止几乎没有什么好处.请注意,我说的是仅关于托管内存.任何你不只使用托管内存的地方都应该得到极大的关注——无论是本机代码、不安全的指针还是任何其他形式的非托管句柄.请注意,这包括网络代码(套接字具有非托管内存)、文件操作(文件句柄)、一些同步原语(等待句柄)、图像数据(最著名的是 GDI+ 的 Bitmap
类,WPF 中充满了这些以及)等等.不过,一般来说,使用高级方法,您通常可以安全地忽略大多数问题,直到它们在现实条件下真正明显地显现出来.
Of course, in general, you shouldn't really care. There's very little benefit in hunting a memory leak until it actually hurts you. Do note that I'm talking only about managed memory. Any place where you're not using managed memory only should be given significant attention - whether it's native code, unsafe pointers or any other form of an unmanaged handle. Do note that this includes networking code (sockets have unmanaged memory), file operations (file handles), some synchronization primitives (wait handles), image data (most notably GDI+'s Bitmap
class, WPF is full of these as well) and more. Generally, though, using high-level approaches, you can usually safely ignore most issues until they actually noticeably manifest under realistic conditions.
现在,综上所述,无论如何,您的方法是可疑的.您必须强制 GC 运行(使用当前的实现,它只会根据内存压力进行收集,而不是因为您离开了范围 - 就 GC 而言,堆栈是一个相当独立的问题).强制执行 full GC 的通常做法是这样的:
Now, all that said, your methodology is suspect anyway. You have to force the GC to run (using the current implementation, it will only collect based on memory pressure, not because you left scope - and stack is quite a separate issue as far as GCing is concerned). The usual practice to force a full GC is like this:
- GC.Collect();
- GC.WaitForPendingFinalizers();
- GC.Collect();
然后整个过程需要你先预热所有东西,即.先做一个完整的测试周期.然后你做一个完整的强制 GC,如上所述.然后你做(比如)一千个循环的测试.之后,您再进行一次完整的 GC.这应该能让您更清楚地了解实际内存使用情况.
The whole process then needs you to first warm everything up, ie. do one complete cycle of the test first. Then you do a full forced GC as noted above. Then you do (say) a thousand cycles of the test. And after that, you do another full GC. This should give you a much clearer picture of real memory usage.
静态字段内存泄漏仅在它们处理大量内存时才有趣(在这种情况下,您应该简单地问为什么会使用这么多内存,我该怎么办??),或者如果它们随着多次调用或随着时间的推移而增加(在这种情况下,这种方法仍然可以让您发现问题).
Static field memory leaks are only interesting if they're either working with a lot of memory (in which case, you should simply ask why the hell is this using so much memory, and what can I do about it?), or if they increase over multiple calls or over time (in which case, this methodology will still let you discover the issue).
另外,你是什么意思,一个没有应用范围的单例对象?如果这就是你所需要的,你真的,真的不应该使用单例!您几乎无法控制何时创建或销毁单例,并且您无法在它被销毁后 重新创建它.当然,现在大多数人可能会告诉你根本不要使用单例 - 原因之一是,除非它们没有状态,否则它们或多或少会扼杀任何使用单元测试等实践的机会.
Also, what do you mean, a singleton object that does not have the scope of the application? If that's what you need, you really, really shouldn't be using a singleton! You have very little control over when the singleton is created or destroyed, and you can't recreate it after it has been destroyed. And of course, most people will probably tell you not to use singletons at all nowadays - one of the reasons being the fact that unless they have no state, they more or less kill any chance of using practices such as unit testing.
这篇关于内存泄漏测试和常量变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!