我读过有关Generations和Large object heap的内容.但我仍然不明白拥有大型对象堆的意义(或好处)是什么?
如果CLR只是依赖于第2代(考虑到Gen0和Gen1的阈值很小来处理大型对象)来存储大型对象,那么可能出现什么问题(在性能或内存方面)?
我想知道为什么我无法在32位.NET进程中分配超过1,000 MB的内存.以下迷你应用程序在分配1,000 MB后抛出OutOfMemoryException.为什么1,000 MB,而不是说1.8 GB?是否有可以更改的流程范围设置?
static void Main(string[] args)
{
ArrayList list = new ArrayList();
int i = 0;
while (true)
{
list.Add(new byte[1024 * 1024 * 10]); // 10 MB
i += 10;
Console.WriteLine(i);
}
}
Run Code Online (Sandbox Code Playgroud)
PS:垃圾收集没有帮助.
编辑,澄清我想要的:我编写了一个服务器应用程序,在写入数据库/磁盘之前处理大量数据.我没有为所有内容创建临时文件,而是编写了一个内存缓存,这使整个过程非常快.但记忆是有限的,所以我试图找出限制是什么.并且想知道为什么我的小测试程序在完全1,000 MB之后抛出了OutOfMemoryException.
我有一个C#4.0应用程序(单个生产者/单个消费者),它以块的形式传输大量数据.虽然没有新的内存分配,但一段时间后内存不足.
我使用Redgate内存分析器分析了内存,那里有很多可用内存.它说由于碎片化而无法使用空闲内存.
我使用阻塞集合作为缓冲区和字节数组作为成员:
BlockingCollection<byte[]> segments = new BlockingCollection<byte[]>(8);
// producer:
segments.Add(buffer);
// consumer:
byte[] buffer = _segments.Take();
Run Code Online (Sandbox Code Playgroud)
如何避免托管内存碎片?
我必须使用WCF通过不可靠的连接在计算机之间传输大文件.
因为我希望能够恢复该文件,并且我不希望受到WCF的文件大小限制,我将这些文件分块为1MB.这些"块"以流的形式传输.到目前为止哪个效果很好.
我的步骤是:
我的问题在第2步.我假设当我从字节数组创建一个内存流时,它将最终在LOH上并最终导致outofmemory异常.我实际上无法创建此错误,也许我的假设是错误的.
现在,我不想在消息中发送byte [],因为WCF会告诉我数组大小太大.我可以更改允许的最大数组大小和/或我的块的大小,但我希望有另一种解决方案.
我的实际问题:
顺便说一句:在接收端,我简单地从到达流中读取较小的块并将它们直接写入文件,因此不涉及大字节数组.
编辑:
当前解决方案
for (int i = resumeChunk; i < chunks; i++)
{
byte[] buffer = new byte[chunkSize];
fileStream.Position = i * chunkSize;
int actualLength = fileStream.Read(buffer, 0, (int)chunkSize);
Array.Resize(ref buffer, actualLength);
using (MemoryStream stream = new MemoryStream(buffer))
{
UploadFile(stream);
}
}
Run Code Online (Sandbox Code Playgroud) 我有一个其他有效的问题这里关于这可能涉及LOH碎片可能还有其他未知因素中的一些绝望的内存问题.
我现在的问题是,接受的做事方式是什么?如果我的应用程序需要在Visual C#来完成,并且需要处理大型阵列的INT [4000000]调,我怎么能不被垃圾收集器拒绝处理LOH注定?
似乎我被迫将任何大型数组全局化,并且从不在它们周围使用"new"这个词.所以,我留下了带有"maxindex"变量的不合适的全局数组,而不是由函数传递的整齐大小的数组.
我总是被告知这是不好的做法.还有什么选择?
是否有某种功能System.GC.CollectLOH("Seriously")?是否有可能将垃圾收集外包给System.GC以外的其他方式?
无论如何,处理大型(> 85Kb)变量的普遍接受的规则是什么?
如何在不引起LOH碎片的情况下在大字符串中运行大量RegExes(以查找匹配项)?
它是.NET Framework 4.0所以我正在使用StringBuilder它所以它不在LOH中但是一旦我需要在它上面运行一个RegEx,我必须调用StringBuilder.ToString()它意味着它将在LOH中.
有没有解决这个问题的方法?一个长期运行的应用程序几乎不可能处理大字符串和这样的RegExes.
解决这个问题的想法:
在考虑这个问题时,我想我找到了一个肮脏的解决方案.
在给定的时间我只有5个字符串,这5个字符串(大于85KB)将被传递给RegEx.Match.
由于碎片发生是因为新对象不适合LOH中的空白空间,这应该可以解决问题:
PadRight所有字符串最多 接受的大小,比方说1024KB(我可能需要这样做StringBuider)我想这个设计的最大问题是如果其他大对象在LOH中分配这个位置会导致应用程序分配大量1024 KB字符串,甚至可能会出现更糟糕的碎片.fixed语句可能有帮助但是如何在不实际创建不在固定内存地址中的新字符串的情况下向RegEx发送固定字符串?
关于这个理论的任何想法?(不幸的是我无法轻易地重现问题,我通常会尝试使用内存分析器来观察更改,并且不确定我可以为此编写哪种隔离测试用例)
我们的应用程序不断为大量数据(比如几十到几百兆字节)分配数组,这些数据在丢弃之前会存在很短的时间.
天真地做这会导致大对象堆碎片,最终导致应用程序崩溃与OutOfMemoryException,尽管当前活动对象的大小不会过多.
我们过去成功管理过这种方法的一种方法是对阵列进行分块以确保它们不会最终出现在LOH上,其目的是通过允许内存被垃圾收集器压缩来避免碎片化.
我们的最新应用程序处理的数据比以前更多,并且在单独的AppDomain或单独进程中托管的加载项之间非常频繁地传递此序列化数据.我们采用了与以前相同的方法,确保我们的内存总是被分块并且非常小心地避免大对象堆分配.
但是我们有一个必须在外部32位进程中托管的加载项(因为我们的主应用程序是64位,加载项必须使用32位库).在特别繁重的负载下,当很多SOH内存块被快速分配并在之后不久丢弃时,即使我们的分块方法还不足以保存我们的32位加载项,并且它会因OutOfMemoryException而崩溃.
在发生OutOfMemoryException时使用WinDbg,!heapstat -inclUnrooted显示如下:
Heap Gen0 Gen1 Gen2 LOH
Heap0 24612 4166452 228499692 9757136
Free space: Percentage
Heap0 12 12 4636044 12848SOH: 1% LOH: 0%
Unrooted objects: Percentage
Heap0 72 0 5488 0SOH: 0% LOH: 0%
Run Code Online (Sandbox Code Playgroud)
!dumpheap -stat 显示这个:
-- SNIP --
79b56c28 3085 435356 System.Object[]
79b8ebd4 1 1048592 System.UInt16[]
79b9f9ac 26880 1301812 System.String
002f7a60 34 4648916 Free
79ba4944 6128 87366192 System.Byte[]
79b8ef28 17195 145981324 System.Double[]
Total 97166 objects
Fragmented blocks larger than 0.5 MB: …Run Code Online (Sandbox Code Playgroud) 我有一个Windows控制台应用程序应该运行几天和几个月没有重新启动.该应用程序从MSMQ检索"工作"并进行处理.有30个线程同时处理工作块.
来自MSMQ的每个工作块大约为200kb,其中大部分分配在单个String对象中.
我注意到,在处理了大约3-4千个这些工作块之后,应用程序的内存消耗非常高,消耗了1到1.5 GB的内存.
我通过分析器运行应用程序并注意到大部分内存(可能是一个演出)在大对象堆中未使用但结构是分段的.
我发现这些未使用的(垃圾收集)字节中有90%是先前分配的字符串.我开始怀疑从MSMQ进来的字符串是分配,使用然后解除分配,因此是碎片的原因.
我明白像GC.Collect(2或GC.Max ...)这样的东西不会有用,因为它们是gc大对象堆但不压缩它(这是问题).所以我认为我需要的是缓存这些字符串并以某种方式重用它们,但由于字符串是不可变的,我将不得不使用StringBuilders.
我的问题是:无论如何都没有改变底层结构(即使用MSMQ,因为这是我无法改变的),并且仍然避免每次初始化一个新的String以避免分裂LOH?
谢谢,Yannis
更新:关于如何检索这些"工作"块
目前,它们作为WorkChunk对象存储在MSMQ中.这些对象中的每一个都包含一个名为Contents的String和另一个名为Headers的String.这些是实际的文本数据.如果需要,我可以将存储结构更改为其他内容,如果需要,我可以将基础存储机制更改为MSMQ以外的其他内容.
目前我们在工作节点方面
WorkChunk chunk = _Queue.Receive();
所以在这个阶段我们几乎无法缓存.如果我们以某种方式改变了结构,那么我想我们可以做一些进步.无论如何,我们必须解决这个问题,以便我们做任何需要做的事情,以避免浪费数月的工作.
更新:我继续尝试下面的一些建议,并注意到这个问题无法在我的本地计算机上运行(运行Windows 7 x64和64位应用程序).这使事情变得更加困难 - 如果有人知道为什么那么它真的有助于在本地重新调整这个问题.
我们有一个应用程序可以在几个Dictionarys 中保存大量对象,其中一些在应用程序的生命周期中不断增长(交易应用程序包含大量工具和不断增长的订单/交易).
OutOfMemoryException由于大对象堆的碎片,我们遇到了问题.
为了解决这个问题,我试图写一个"大"字典,它实现为一个两级字典,其中所有的叶子字典都不够大,不能在LOH上分配.我使用了一致的哈希算法,以避免在单个存储桶变得太大时重新整理整个字典.一致的哈希'circle' TreeDictionary来自C5集合库.
我的问题是,C#有没有更好的数据结构(或者我描述的更好的实现)?
更新
这是"大型"字典的实现:https://gist.github.com/956621
我知道它不是万无一失的,因为LOH堆阈值既不在规范中,也不是每个Dictionary条目或缩放算法的大小.然而,这是目前我能想到的最好的,以避免应用程序在中午爆炸.
我有一个Java服务,目前运行14GB堆.我很想试用-XX:+ UseLargePages选项来查看这可能会如何影响系统的性能.我已经使用适当的共享内存和页面值(这些也可以使用在线工具计算)按照Oracle的描述配置操作系统.
配置操作系统后,我可以看到它将预期的内存量分配为大页面.但是,使用-XX:+UseLargePages选项集启动VM 始终会导致以下错误之一:
当-Xms/ -Xmx几乎等于巨大的页面分配时:
Failed to reserve shared memory (errno = 28). // 'No space left on device'
Run Code Online (Sandbox Code Playgroud)
当-Xms/ -Xmx小于巨大的页面分配时:
Failed to reserve shared memory (errno = 12). // 'Out of memory'
Run Code Online (Sandbox Code Playgroud)
我确实试过引入一些余地 - 所以在32GB系统上我分配了24GB的共享内存和大页面,用于配置20GB堆的JVM,其中目前只使用了14GB.我还验证了执行JVM的用户确实拥有与之一致的组权限/proc/sys/vm/hugetlb_shm_group.
谁能给我一些关于我可能出错的地方以及我接下来可以尝试的内容的一些指示?
-Xms/ -Xmx- 20GB/proc/sys/kernel/shmmax - 25769803776(24GB)/proc/sys/vm/nr_hugepages - 12288