Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.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# 来自队列的大型对象堆和字符串对象_C#_.net_Memory Management_Memory Leaks_Large Object Heap - Fatal编程技术网

C# 来自队列的大型对象堆和字符串对象

C# 来自队列的大型对象堆和字符串对象,c#,.net,memory-management,memory-leaks,large-object-heap,C#,.net,Memory Management,Memory Leaks,Large Object Heap,我有一个windows控制台应用程序,应该可以运行几天甚至几个月而不重启。该应用程序从MSMQ检索“工作”并对其进行处理。有30个线程同时处理一个工作块 来自MSMQ的每个工作块约为200kb,其中大部分分配在单个字符串对象中 我注意到,在处理了大约3-4千个这样的工作块之后,应用程序的内存消耗非常高,消耗了1-1.5 gb的内存 我通过一个分析器运行这个应用程序,发现大部分内存(可能是一个gig左右)在大型对象堆中没有使用,但结构是碎片化的 我发现这些未使用(垃圾收集)的字节中有90%是以前分

我有一个windows控制台应用程序,应该可以运行几天甚至几个月而不重启。该应用程序从MSMQ检索“工作”并对其进行处理。有30个线程同时处理一个工作块

来自MSMQ的每个工作块约为200kb,其中大部分分配在单个字符串对象中

我注意到,在处理了大约3-4千个这样的工作块之后,应用程序的内存消耗非常高,消耗了1-1.5 gb的内存

我通过一个分析器运行这个应用程序,发现大部分内存(可能是一个gig左右)在大型对象堆中没有使用,但结构是碎片化的

我发现这些未使用(垃圾收集)的字节中有90%是以前分配给字符串的。我开始怀疑来自MSMQ的字符串被分配、使用,然后被释放,因此是碎片的原因

我知道像GC.Collect(2或GC.Max…)这样的东西不会有帮助,因为它们对大型对象堆进行GC,但不会压缩它(这就是这里的问题)。所以我认为我需要的是缓存这些字符串并以某种方式重用它们,但由于字符串是不可变的,所以我必须使用StringBuilder

我的问题是:是否仍然可以不更改底层结构(即使用MSMQ,因为这是我无法更改的),同时仍然避免每次初始化新字符串,以避免断开LOH

谢谢, 亚尼斯

更新:关于当前如何检索这些“工作”块

目前,这些对象作为工作块对象存储在MSMQ中。每个对象都包含一个名为Contents的字符串和另一个名为Headers的字符串。这些是实际的文本数据。如果需要,我可以将存储结构更改为其他内容,如果需要,还可以将底层存储机制更改为MSMQ以外的内容

在worker节点端,我们目前正在执行

WorkChunk chunk=_Queue.Receive()

因此,在这个阶段,我们可以缓存的内容很少。如果我们以某种方式改变结构,我想我们可以取得一些进展。在任何情况下,我们都必须解决这个问题,因此我们将尽一切努力避免浪费数月的工作


更新:我继续尝试下面的一些建议,并注意到此问题无法在我的本地计算机上重现(运行Windows 7 x64和64位应用程序)。这使得事情变得更加困难——如果有人知道原因,那么这将真正有助于在本地重新处理此问题。

也许您可以创建一个字符串对象池,在处理工作时使用这些对象,然后在完成后返回

一旦在LOH中创建了一个大型对象,它就不能被删除(AFAIK),因此如果您无法避免创建这些对象,那么最好的计划就是重用它们


如果您可以在两端更改协议,则将“内容”字符串减少为一组较小的字符串(您的问题似乎是由于大对象堆上的内存分配造成的-大对象堆没有被压缩,因此可能是碎片的来源。这里有一篇好文章详细介绍了一些调试步骤,您可以按照这些步骤确认大对象堆正在发生碎片:

您似乎有两个或三个解决方案:

  • 更改应用程序以对块/较短字符串执行处理,其中每个块小于85000字节-这避免了分配大型对象
  • 更改应用程序以预先分配一些大内存块,并通过将新消息复制到分配的内存中来重新使用这些内存块。请参阅
  • 保持现状—只要您没有遇到内存不足异常,并且应用程序没有干扰系统上运行的其他应用程序,您可能应该保持现状
  • 在这里,理解虚拟内存和物理内存之间的区别很重要-即使进程使用大量虚拟内存,如果分配的对象数量相对较低,则该进程的物理内存使用率可能较低(未使用的内存被分页到磁盘)这意味着对系统上的其他进程几乎没有影响。您可能还会发现“VM囤积”选项有助于阅读“大型对象堆未覆盖”一文以了解更多信息


    这两种更改都涉及到将应用程序更改为使用字节数组和短子字符串(而不是单个大字符串)执行部分或全部处理—这对您来说有多困难将取决于您正在执行的处理类型。

    当LOH上存在碎片时,这意味着e分配给它的对象。如果你能支持延迟,你可以偶尔等待,直到所有当前运行的任务完成,然后调用
    GC.Collect()
    。当没有被引用的大型对象时,它们都将被收集,有效地消除LOH的碎片。当然,这只适用于所有大型对象都未被引用


    此外,迁移到64位操作系统也可能有所帮助,因为在64位系统上,由于碎片而导致内存不足的可能性要小得多,因为虚拟空间几乎是无限的。

    使用String.Intern(…)怎么样消除重复引用。这会降低性能,但取决于您的字符串,它可能会产生影响。

    如何接收这些字符串?一旦它们是字符串,您就会被卡住。I它们来自流或字节[]您可能有一些选择。嗨,Henk-查看更新以获取有关这些工作块的更多信息,但这是一个实际问题吗?64位PC上的1.5GB内存>=8GB应该可以继续。但最终会由于分页过多而减慢到爬网速度…这不是一个哲学上的问题