报错:system.outofmemoryexception
system.windows.media.composition.DUCE+channel.syncflush
system.windows.interop.hwndtarget.updatewindowsettings
system.windows.interop.hwndtarget.updatewindowspos
system.windows.interop.hwndsource.hwndtargetfiltermessage
ms.win32.hwndwrapper.wndproc
ms.win32.hwndsubclass.dispatchercallbackoperation...............
这个问题在之前偶然出现,但最近非常频繁,在程序运行几个小时后就会出现界面卡顿,然后点击无响应,最后闪退的情况。
报错中带有Media字样,看上去很像媒体资源未释放导致的,可是我过了一遍确定代码里面的媒体文件处理是安全的。
看任务管理器,进程的句柄、线程数,也都在合理范围内。那会不会不是我代码问题呢?
于是照着前人总结的办法试一下:
试这三个因为客户现场原因花了较多时间,均无效。看了下.NET版本,也是匹配的。
于是开始思索,内存不足,会不会是由于程序内存占用过多,挤占了界面的那一部分,于是这个报错里面才会带有Media类型?
换用VS调试模式运行,通过截取快照对比多个时刻的内存使用,看到内存变化也都是在预期内。
问题总是要解决,从业4年,到现在为止还没有处理不了的问题。
搬出最朴素扎实的办法:排除法。为不影响现场生产,我将程序拆成2个程序,一个程序做一部分动作,区分两个程序的操作设备部分,然后分别观察2个程序会不会闪退。
运行一天,一个程序闪退频率明显高于另一个,对比分析两边调方法的差异,定位分析可能有问题的代码,直到发现了这个(为保护公司信息,已删去业务相关代码,仅保留错误部分):
IntPtr ptr = new IntPtr();
while (m_bStart)
{
try
{
调DLL方法
if (len > 0)
{
try
{
ptr = Marshal.AllocHGlobal((int)len * size);
调DLL方法
}
catch
{
Marshal.FreeHGlobal(ptr);
}
}
}
这段代码是在一个循环调库接收数据的线程里面的。
Marshal.AllocHGlobal 通过使用指定的字节数,从进程的非托管内存中分配内存。
Marshal.FreeHGlobal 释放以前从进程的非托管内存中分配的内存。
GC并不会强制管理非托管资源的释放。
可想而知,这段代码放在接收线程里面使用的非托管内存一定会越积越多。之前的程序员为什么会这样写?除了他谁也不知道。或许他是想将 FreeHGlobal 放到 finally 里面呢?