我对记忆问题很新.希望你不要认为这是一个愚蠢的问题.
我知道大于85,000字节的内存将被放入C#中的LOH中
Byte[] hugeByteCollection = new Byte[85000];
Run Code Online (Sandbox Code Playgroud)
我想知道一个大小为10000 - 20000且包含10个成员变量(字节类型)的对象是否会被放入LOH或SOH?
在我的应用程序中,有一个特定的时间,一次释放大量的大对象.那时我想特别针对大对象堆(LOH)进行垃圾收集.
我知道你不能这样做,你必须打电话,GC.Collect(2)因为只有在进行第2代收集时才会在LOH上调用GC.但是,我在文档中读到,调用GC.Collect(2)仍然会在第1代和第0代运行GC.
是否可以强制GC 仅收集第2代,而不包括第1代或第0代?
如果不可能,是否有理由以这种方式设计GC?
我的应用程序对大对象进行了大量的二进制序列化和压缩.未压缩的序列化数据集大约为14 MB.压缩它是1.5 MB左右.我发现每当我在我的数据集上调用serialize方法时,我的大对象堆性能计数器就会从1 MB以下跳到大约90 MB.我也知道在一个相对繁重的系统下,通常在运行(天)一段时间后,这个序列化过程发生了一段时间,已知应用程序在调用此序列化方法时会丢失内存消除,即使在那里似乎是充足的记忆.我猜测碎片是个问题(虽然我不能说我百分百肯定,我很接近)
最简单的短期修复(我想我正在寻找短期和长期答案)我能想到的是在完成序列化过程后立即调用GC.Collect.在我看来,这将垃圾收集来自LOH的对象,并且可能在其他对象被添加到它之前这样做.这将允许其他对象紧密地紧贴堆中的其余对象,而不会造成太多碎片.
除了这个荒谬的90MB分配,我认为我没有任何其他使用丢失的LOH.这种90 MB的分配也相对较少(每4小时一次).我们当然仍然会有1.5 MB的阵列,也许还有其他一些较小的序列化对象.
有任何想法吗?
由于良好的反应而更新
这是我的代码,它完成了这项工作.我实际上已经尝试将其更改为压缩WHILE序列化,以便序列化同时序列化为流并且我没有得到更好的结果.我还尝试将内存流预先分配到100 MB并尝试连续两次使用相同的流,不过LOH最高可达180 MB.我正在使用Process Explorer来监控它.这太疯狂了.我想我接下来会尝试UnmanagedMemoryStream的想法.
如果你不习惯,我会鼓励你们尝试一下.它不一定是这个确切的代码.只是序列化一个大型数据集,你会得到令人惊讶的结果(我有很多表,15周围和许多字符串和列)
byte[] bytes;
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
System.IO.MemoryStream memStream = new System.IO.MemoryStream();
serializer.Serialize(memStream, obj);
bytes = CompressionHelper.CompressBytes(memStream.ToArray());
memStream.Dispose();
return bytes;
Run Code Online (Sandbox Code Playgroud)
使用UnmanagedMemoryStream尝试二进制序列化后更新
即使我序列化为UnmanagedMemoryStream,LOH也会跳到相同的大小.似乎无论我做什么,调用BinaryFormatter来序列化这个大对象都会使用LOH.至于预分配,它似乎没什么帮助.假设我预分配说我预分配100MB,然后我序列化,它将使用170 MB.这是代码.甚至比上面的代码更简单
BinaryFormatter serializer = new BinaryFormatter();
MemoryStream memoryStream = new MemoryStream(1024*1024*100);
GC.Collect();
serializer.Serialize(memoryStream, assetDS);
Run Code Online (Sandbox Code Playgroud)
中间的GC.Collect()只是为了更新LOH性能计数器.您将看到它将分配正确的100 MB.但是当你调用序列化时,你会注意到它似乎在你已经分配的100之上添加了它.
c# memory-management out-of-memory fragmentation large-object-heap
在我们的应用程序中,我们有一些数据结构,其中包含一个分块的字节列表(当前公开为a List<byte[]>).我们将字节大块化,因为如果我们允许将字节数组放在大对象堆上,那么随着时间的推移,我们会遇到内存碎片.
我们也开始使用Protobuf-net来序列化这些结构,使用我们自己生成的序列化DLL.
但是我们注意到Protobuf-net在序列化时会创建非常大的内存缓冲区.浏览源代码看起来似乎它可能无法刷新其内部缓冲区,直到整个List<byte[]>结构被写入,因为它需要在缓冲区前面写入总长度.
不幸的是,这首先解决了我们的工作,首先将字节分块,最终由于内存碎片而给我们OutOfMemoryExceptions(异常发生在Protobuf-net尝试将缓冲区扩展到84k以上时,这显然是在LOH,我们的整体进程内存使用率相当低.
如果我对Protobuf-net如何工作的分析是正确的,那么有没有解决这个问题的方法呢?
更新
根据Marc的回答,这是我尝试过的:
[ProtoContract]
[ProtoInclude(1, typeof(A), DataFormat = DataFormat.Group)]
public class ABase
{
}
[ProtoContract]
public class A : ABase
{
[ProtoMember(1, DataFormat = DataFormat.Group)]
public B B
{
get;
set;
}
}
[ProtoContract]
public class B
{
[ProtoMember(1, DataFormat = DataFormat.Group)]
public List<byte[]> Data
{
get;
set;
}
}
Run Code Online (Sandbox Code Playgroud)
然后序列化它:
var a = new A();
var b = new B();
a.B = b;
b.Data = new List<byte[]>
{
Enumerable.Range(0, 1999).Select(v => …Run Code Online (Sandbox Code Playgroud) 我正在用C#编写一个分析应用程序,它必须处理大量内存.我使用ANTS Memory Profiler 7.4来优化我的内存管理.在这样做的时候,我意识到我使用的所有双[,]数组(我需要它们)都放在LOH上,尽管这些数组中最大的是大约24.000字节.据我所知,对象不应该放在85.000字节之前.问题是现在,因为我有大约几千个这些双[,]数组的实例,我有很多内存碎片(我的总内存使用量的约25%是我无法使用的可用内存).存储在LOH上的这些数组中的一些甚至只有1.036字节.问题是,有时我必须执行更大的分析,然后由于LOH碎片导致大量内存丢失,我最终会出现内存不足的异常.
有没有人知道为什么会发生这种情况虽然根据定义它不应该是一个大对象?


如果你的应用程序必须对大型对象进行大量的分配/解除分配(> 85000字节),它最终会导致内存碎片,并且应用程序将抛出内存不足异常.
有没有解决这个问题的方法,还是CLR内存管理的限制?
我有一个.NET 3.5应用程序
当我对应用程序进行概要分析时,我可以确认这些字符串存储在LOH中,但稍后它们也会被GC回收,因此在给定时间内,只有最多10个字符串在LOH中(10个线程正在运行).
我的理解是,这些大字符串位于LOH,然后由GC回收,但不知何故由于它们的分配位置(并且在LOH中因此没有被压缩)这导致碎片化.尽管操作中没有内存泄漏,但仍会发生这种情况.
它不会导致~100K次出现问题,但是当它达到1M +时会出现内存异常.
我正在使用ANTS Memory Profiler,这是我在早期执行中得到的结果:
.NET Using 70MB of 210MB total private bytes allocated in to the application
Number of Fragments: 59
Number of Large Fragments : 48 (99.6% of free memory)
Largest Fragment: 9MB
Free Space: 52% of total memory (37MB)
Unmanaged Memory: 66% of total private memory (160MB)
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是我有一些来自MSMQ的大型对象,主要是字符串.我已经将我的内存问题缩小到在大对象堆(LOH)中创建的这些对象,因此将其分段(在探查器的帮助下确认).
在我上面发布的问题中,我得到了一些解决方法,主要是将String拆分为char数组,我做了.
我面临的问题是,在字符串处理结束时(无论采用何种形式),我需要将该字符串发送到另一个我无法控制的系统.所以我想到以下解决方案将此字符串放在LOH中:
无论我做什么 - 不管怎样 - String必须完整(没有char数组或压缩).
我被困在这里吗?我在想如果在这里使用托管环境是一个错误,我们是否应该咬紧牙关去寻找C++类环境.
谢谢,Yannis
编辑:我已经缩小的问题正好代码贴在这里
通过的大字符串放在LOH中.我从已经收到消息的点开始删除了每个处理模块,并且内存消耗趋势保持不变.
所以我想我需要改变这个WorkContext在系统之间传递的方式.
我试图通过HTTPWebRequest将大文件(大约30MB)的字节上传到某个服务器.问题是由于字节大小超过85000,因此它存储在LargeObjectHeap(LOH)中.问题是我的代码在LOH中至少创建了同一个对象的5个实例,即使在关闭响应流之后也没有从内存中删除.以下是导致此问题的代码段.在此代码块之前,LOH中只有一个文件实例.
using (IO.Stream requestStream = webReqest.GetRequestStream())
{
List<byte> uploadData = new List<byte>();
uploadData.AddRange(Encoding.UTF8.GetBytes(stringContainingHeaderInfo));
uploadData.AddRange(bytesOfTheLargeFile);
byte[] fileFullData = uploadData.ToArray();
requestStream.Write(fileFullData, 0, fileFullData.Length);
requestStream.Close();
uploadData.Clear();
uploadData = null;
fileFullData = null;
fileEntityBytes = null;
using (WebResponse webResponse = webRequest.GetResponse())
{
//Do Something with the response
}
}
Run Code Online (Sandbox Code Playgroud)
有没有办法进一步优化此代码块,以便在堆中创建更少数量的副本.
在我的Azure角色中运行64位进程内的C#代码我想下载一个ZIP文件并尽快解压缩.我想我可以执行以下操作:创建一个MemoryStream实例,下载到该实例,MemoryStream然后将流传递到某个ZIP处理库以进行解包,一旦解压缩完成,就丢弃该流.这样我就可以摆脱不必要地执行大量I/O的写 - 读 - 写序列.
但是我读过这个MemoryStream数组支持一个半千兆字节,这个数组肯定会被认为是一个"大对象",并且将被分配在一个大型对象堆中,这个堆不会在垃圾回收中压缩.这使我担心这种用法可能MemoryStream会导致过程记忆碎片化和负面的长期影响.
这可能会对我的流程产生长期的负面影响吗?