Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/302.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当大量内存可用时,OutOfMemoryException_C#_.net_Out Of Memory_Clr - Fatal编程技术网

C# 当大量内存可用时,OutOfMemoryException

C# 当大量内存可用时,OutOfMemoryException,c#,.net,out-of-memory,clr,C#,.net,Out Of Memory,Clr,我们有一个在5个(服务器)节点上运行的应用程序(16个核,每个节点128 GB内存),在每台机器上加载近70 GB的数据。这个应用程序是分布式的,服务于并发客户机,因此有很多套接字使用。类似地,对于多个线程之间的同步,也使用了一些同步技术,主要使用System.Threading.Monitor 现在的问题是,当应用程序运行且数据在这些服务器节点之间以及客户端和服务器之间传输时,一台或两台服务器机器开始接收OutOfMemoryException,即使仍有40%以上的内存可用。我们感觉这个异常来

我们有一个在5个(服务器)节点上运行的应用程序(16个核,每个节点128 GB内存),在每台机器上加载近70 GB的数据。这个应用程序是分布式的,服务于并发客户机,因此有很多套接字使用。类似地,对于多个线程之间的同步,也使用了一些同步技术,主要使用
System.Threading.Monitor

现在的问题是,当应用程序运行且数据在这些服务器节点之间以及客户端和服务器之间传输时,一台或两台服务器机器开始接收
OutOfMemoryException
,即使仍有40%以上的内存可用。我们感觉这个异常来自非托管代码。尽管我们没有直接进行任何非托管调用,但我们已经看到OOM异常堆栈跟踪中的最后一个调用始终是一个内部调用非托管代码的框架调用

下面是几个例子

Exception of type 'System.OutOfMemoryException' was thrown.
   at System.Threading.Monitor.ObjPulseAll(Object obj)
   ....

Exception of type 'System.OutOfMemoryException' was thrown.
   at System.Threading.Monitor.ObjWait(Boolean exitContext, Int32 millisecondsTimeout, Object obj)
   at System.Threading.Monitor.Wait(Object obj, TimeSpan timeout)
   ....
我们不知道是什么导致了这个问题。我们在这些机器上多次诱导GC,但这似乎也没有帮助

任何帮助都将不胜感激

编辑:

以下是一些更详细的信息

  • 应用程序正在x64进程中运行
  • Windows Server 2012 R2
  • .NET Framework 4.5
  • 启用服务器GC
  • AllowLargeObject
    标志已设置

EDIT2:请注意,这不是内存泄漏。70 GB的进程大小在此有效。

即使非托管代码存在内存泄漏,如果您有40%的可用内存,您应该能够分配对象。我想的是,这是一个碎片问题,而不是内存泄漏

1-您试图分配的数据是大数据块还是小数据块

2-是否尝试强制垃圾收集器(通过调用GC.Collect())?垃圾收集不仅释放内存,而且压缩内存,消除碎片

GC.Collect()
仅在对象未被任何其他对象引用的情况下释放内存

可能发生泄漏的常见情况是,在将对象的引用设置为null之前,不断开事件处理程序与对象的连接


作为避免泄漏的练习,最好在对象上实现
IDisposable
(即使它是用来释放非托管对象的),只要从确保所有处理程序都断开连接的角度出发,集合被正确清除,任何其他对象引用被设置为null。

如果这是一个碎片问题,那么在没有某种分析的情况下,您无法解决它。搜索支持碎片检测的内存探查器,以准确了解此碎片的原因

使用LargeObjectHeapCompactionMode=CompactOnce的垃圾收集可能有助于修复碎片

GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();

其他用户提出的一些初步问题很酷,但你有没有考虑过偷懒和分析你的应用程序

我可以想到Redgate的Ants profiler或JetBrains的dotmemory,链接如下


请注意,在订阅事件处理程序时,事件的发布者持有对订阅者的引用。这是.NET中内存泄漏的常见原因,在您的情况下,这不会是严重泄漏,但如果托管对象保留指向非托管对象的指针或句柄,则不会删除此非托管对象,从而导致内存碎片


如果您确定碎片化的原因是非托管组件,并且您没有遗漏任何内容,并且如果您可以访问umnanaged组件的代码,那么您可以重新编译该组件,并使用一个像样的内存分配器(如囤积)将其链接。但这应该在没有其他事情可做的情况下以及在认真分析之后进行

我建议在运行时使用ADPlus或其他工具获取进程的转储。使用此转储,您可以使用调试转储文件。以下所有命令都取自博客文章

调查内存泄漏 为了获得内存视图,我们需要使用以下命令

!dumpheap
“dumpheap”命令将为您提供对象计数和对象的内存使用情况。 然后,您可以调查哪些对象类型使用了大部分内存

!dumpheap -type System.IO.MemoryStream
“dumpheap-type”命令将列出堆上MemoryStream类型的所有对象。
WinDbg的好处是,您可以调查非托管内存泄漏:而且。

一个有根据的猜测是,您在最终确定时遇到了STA死锁问题,特别是从您庞大的硬件需求来看,它似乎是一个高并发系统。不管怎么说,似乎您已经尝试强制GC死锁是有意义的,如果最终结果是死锁的,那么GC将无法完成其工作。希望这对你有帮助

具体来说,我感兴趣的部分如下所述

当您的代码在单线程单元(STA)线程上执行时,会发生与独占锁等效的情况。一次只能有一个线程更新GUI窗口或运行STA中单元线程COM组件中的代码。这些线程拥有一个消息队列,系统和应用程序的其他部分将要处理的信息放入该队列中。GUI使用此队列获取信息,例如重新绘制请求、要处理的设备输入和窗口关闭请求。COM代理使用消息队列将跨单元方法调用转换到组件具有亲缘关系的单元中。STA上运行的所有代码都负责使用消息循环泵送消息队列以查找和处理新消息,否则队列可能会阻塞,导致响应丢失。在Win32术语中,这意味着使用MsgWaitForSingleObject、MsgWaitForMultipleObjects(及其前对应对象)或CoWaitForMultipleHandles API。无泵等待
   /*========================================================================
    ** Sends a notification to all waiting objects. 
    ========================================================================*/
    [System.Security.SecurityCritical]  // auto-generated
    [ResourceExposure(ResourceScope.None)]
    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    private static extern void ObjPulseAll(Object obj);

    [System.Security.SecuritySafeCritical]  // auto-generated
    public static void PulseAll(Object obj)
    {
        if (obj == null)
        {
            throw new ArgumentNullException("obj");
        }
        Contract.EndContractBlock();

        ObjPulseAll(obj);
    }
System::GC::AddMemoryPressure(sizeOfField);
System::GC::RemoveMemoryPressure(sizeOfField);