我正在研究的C#/ .NET应用程序正在遭受缓慢的内存泄漏.我已经使用CDB和SOS来尝试确定发生了什么,但数据似乎没有任何意义,所以我希望你们中的一个人之前可能已经经历过这种情况.
该应用程序在64位框架上运行.它不断地计算并将数据序列化到远程主机,并且正在大量地击中大对象堆(LOH).但是,我希望大多数LOH对象都是瞬态的:一旦计算完成并且已经发送到远程主机,就应该释放内存.然而,我所看到的是大量(实时)对象数组与空闲的内存块交织,例如,从LOH中获取一个随机段:
0:000> !DumpHeap 000000005b5b1000 000000006351da10
Address MT Size
...
000000005d4f92e0 0000064280c7c970 16147872
000000005e45f880 00000000001661d0 1901752 Free
000000005e62fd38 00000642788d8ba8 1056 <--
000000005e630158 00000000001661d0 5988848 Free
000000005ebe6348 00000642788d8ba8 1056
000000005ebe6768 00000000001661d0 6481336 Free
000000005f214d20 00000642788d8ba8 1056
000000005f215140 00000000001661d0 7346016 Free
000000005f9168a0 00000642788d8ba8 1056
000000005f916cc0 00000000001661d0 7611648 Free
00000000600591c0 00000642788d8ba8 1056
00000000600595e0 00000000001661d0 264808 Free
...
Run Code Online (Sandbox Code Playgroud)
显然,如果我的应用程序在每次计算期间创建长寿命的大对象,我会期望这种情况.(它确实这样做,我接受会有一定程度的LOH碎片,但这不是问题.)问题是你可以在上面的转储中看到的非常小的(1056字节)对象数组,我在代码中看不到正在创建,并以某种方式保持根深蒂固.
另请注意,转储堆段时CDB不报告类型:我不确定这是否相关.如果我转储标记的(< - )对象,CDB/SOS报告正常:
0:015> !DumpObj 000000005e62fd38
Name: System.Object[]
MethodTable: 00000642788d8ba8
EEClass: 00000642789d7660
Size: 1056(0x420) bytes
Array: Rank 1, Number of elements 128, Type CLASS …Run Code Online (Sandbox Code Playgroud) 我在托管堆中存在数百个MyClass实例。其中一些位于大对象堆中。下面是各种堆结构的样子
0:000> !EEHeap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000000002df9de8
generation 1 starts at 0x0000000002dc6710
generation 2 starts at 0x0000000002a01000
ephemeral segment allocation context: none
segment begin allocated size
0000000002a00000 0000000002a01000 0000000002e3c2c0 0x43b2c0(4436672)
Large object heap starts at 0x0000000012a01000
segment begin allocated size
0000000012a00000 0000000012a01000 000000001a5ed558 0x7bec558(129942872)
000000002a980000 000000002a981000 00000000328110b8 0x7e900b8(132710584)
0000000033e00000 0000000033e01000 000000003bd80d78 0x7f7fd78(133692792)
000000001daf0000 000000001daf1000 0000000025996188 0x7ea5188(132796808)
00000000542b0000 00000000542b1000 000000005a4bf100 0x620e100(102818048)
000000005c2b0000 000000005c2b1000 000000006344df88 0x719cf88(119132040)
000000007fff0000 000000007fff1000 00000000878bfbc0 0x78cebc0(126675904)
Total Size: Size: …Run Code Online (Sandbox Code Playgroud) 我正在准备向我的团队介绍 .net GC 和内存。不同的来源讨论了碎片对大对象堆的潜在影响。由于这将是一个有趣的现象,我试图在代码中展示它。
Thomas Weller 提供了这段代码,当尝试将更大的对象分配到 LOH 中的已释放间隙时,它看起来应该会导致 OOM,但由于某种原因它没有发生。LOH 是否在 .net 4.6 中自动压缩?LOH 碎片在 64 位中根本就不是问题吗?
来源:https : //stackoverflow.com/a/30361185/3374994
class Program
{
static IList<byte[]> small = new List<byte[]>();
static IList<byte[]> big = new List<byte[]>();
static void Main()
{
int totalMB = 0;
try
{
Console.WriteLine("Allocating memory...");
while (true)
{
big.Add(new byte[10*1024*1024]);
small.Add(new byte[85000-3*IntPtr.Size]);
totalMB += 10;
Console.WriteLine("{0} MB allocated", totalMB);
}
}
catch (OutOfMemoryException)
{
Console.WriteLine("Memory is full now. Attach and debug if you like. Press Enter when …Run Code Online (Sandbox Code Playgroud) 所以大学的一位教授告诉我,在C#中使用串联连接(即当你使用加号运算符时)会产生内存碎片,而我应该使用它string.Format.
现在,我已经在堆栈溢出中搜索了很多,我发现了很多关于性能的线程,这些串联串起来.(其中一些包括这个,这个和这个)
我找不到谈论内存碎片的人.我string.Format使用ILspy 打开了.NET ,显然它使用了与string.Concat方法相同的字符串构建器(如果我理解的是+符号被重载到的那个).实际上:它使用的代码string.Concat!
我从2007年开始发现这篇文章,但我怀疑它今天(或永远!)是准确的.显然编译器足够聪明,可以避免今天,因为我似乎无法重现这个问题.使用string.format和plus符号添加字符串最终都会在内部使用相同的代码.如前所述,string.Format使用相同的代码字符串.Concat使用.
所以现在我开始怀疑他的主张.这是真的吗?
我正在将一些字节写入MemoryStream.我给它的大小为100 MB
using (MemoryStream Stream = new MemoryStream())
{
for ( loop 1400 time)
{
func(i, OStream, Stream, );
}
lock (this)
{
Stream.WriteTo(OStream);
}
Run Code Online (Sandbox Code Playgroud)
}
func(i, OStream, Stream)
{
loop 19 times
get buffer;
Stream.Write(buffer, 0, buffer.Length);
}
Run Code Online (Sandbox Code Playgroud)
缓冲区的byte[]大小从65536到116454不等.我循环1000次.我正在写for循环,但我得到OutOfMemoryException异常.
有没有办法扩展MemoryStream并避免异常?