可能存在内存泄漏或其他问题?

6

希望能得到任何形式的帮助。

情景简述 -

服务器上有一个运行在COM+中的程序(使用C#编写)。这个COM的任务是接收一个文件名,多页tif文件的页面编号和分辨率,将其转换为gif文件图像。该COM通过代理从Web应用程序调用。网站获取转换后的图像,并以所请求的分辨率显示。对于打印,它会发出两个请求——第一个是显示分辨率,第二个则会以完整分辨率进行打印(使用window.print())。

问题 -

一段时间后,服务器会耗尽内存,无法在网站上显示图像。需要定期重新启动服务器。

错误

EventType clr20r3, P1 imageCOM.exe, P2 1.0.0.0, P3 4fd65854, P4 prod.web.imaging, P5 1.0.0.0, P6 4fd65853, P7 1a, P8 21, P9 system.outofmemoryexception, P10 NIL.

Here is the error(s) on the web server (these continuously appear every minute) ….

System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it
   at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress)
   at System.Net.Sockets.Socket.InternalConnect(EndPoint remoteEP)
   at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Int32 timeout, Exception& exception)
   --- End of inner exception stack trace ---

我没有访问生产服务器的权限,但系统管理员发送的错误消息显示了"OutOfMemory"。

因此,假设存在内存泄漏并专注于它——根据我有限的处理此类情况的经验,我的发现如下:

  • Perfmon - 我看到进程/私有字节和.Net CLR内存/堆中的字节数正在增加。因此,我认为这是托管内存泄漏。不过我不确定。
  • CPU使用率 - 起始时为8%,最高达到了80%。后来降回并保持在3%-12%之间,除了几次回到75%-85%。不确定这里发生了什么。
  • 我开始调试服务器COM以查看堆、gcroot等。

    1. 每次请求完成后,堆中计数递增的对象有2个。一个对象保存图像数据,另一个对象是事件处理程序,在一定时间后过期时从缓存中删除对象1(图像)。
    2. 通过查看方法调用,这两个对象都指向相同的方法。
    3. 从代码实现的角度来看,请求的图像被缓存(最多一定数量),我可以理解这是为了更好的性能。可能正因为如此,堆中对象的引用会增加,就像第1个项目中提到的那样。

我知道我给出了非常模糊的描述,但我需要一些线索来检测服务器上的实际问题。

编辑: 图像对象已被处理掉,如下

Bitmap retVal;

      using (MemoryStream buffer = new MemoryStream(doc.FileData, 0, doc.DocumentSize, false, false))
      {
        using (Bitmap original = new Bitmap(buffer))
        {
        //select the page to convert and
        //perform scaling - full resolution or the requested resolution.  
        }
      }

      using (MemoryStream buffer = new MemoryStream())
      {
        retVal.Save(buffer, ImageFormat.Gif);
        retVal.Dispose();
        return buffer.GetBuffer();
      }

“句柄”或“GDI对象”的值是否在增加? - ken2k
如果您想了解为什么需要这样做,并且需要小心地完成,那么这里是关于正确处理对象的权威答案链接:https://dev59.com/K3RB5IYBdhLWcg3wuZU1#538238。 - Cᴏʀʏ
@ken2k - 我没有观察到这2个计数器。我会记录下来并回复。 - peacefulmember
2个回答

2

确保在使用完Image对象(如Bitmap)后对其进行处理。我猜测您正在将tiff图像作为位图打开并重新缩放,然后将其另存为gif。如果不处理位图,它会泄露内存(每次通常会泄漏几MB,具体取决于图像的大小)。


3
沿着这条线路:确保正确处理实现 IDisposable 接口的所有内容。最容易记住的方法是使用 using 语句:using (System.Drawing.Bitmap b = new System.Drawing.Bitmap(...)) { /* 处理内容 */ }; (这将自动处理回收而无需强制调用 .Dispose())。 - Cᴏʀʏ
@Cory - 通过使用using语句,图像对象已被处理。 - peacefulmember
我已经编辑了原始消息,以展示图像的处理方式。如果处理不正确,请指出。另一个要提出的问题是,图像的缩放是使用C++的不安全代码完成的。我看到它在开头有LockBits,在结尾有UnlockBits,因此假设对象也被正确处理了。有什么想法! - peacefulmember

1

我认为你的第一个观点可能是问题的原因。你可以尝试减少缓存过期时间,并检查再次出现内存溢出错误所需的时间,你可能需要更改算法以避免这种情况。你的第二个观点不应该是错误的原因。


我如何检查对象1是否被释放?您是指查看堆和gcroot吗?正如我所说,我的知识在这方面有限。 - peacefulmember
您的回答指引了我正确的方向。虽然并未解决所有问题,但我已经能够检测出其中一个重要的内存泄漏。谢谢。 - peacefulmember

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接