标签:内存分配 address jit shc ram null apply reading 编译
1、关于.NET的内存管理,网上的一些前辈高人写了很多科普文章,基本上所有稍有深度的.NET开发书籍都会做为重中之重来介绍。在此,归纳如下:
1.1 基于性能考虑.NET的内存值内存对象是有栈来管理,而引用对象的内存是由堆来管理的,而大于85000字节的内存对象则是由LOH堆来管理。
1.2 内存是按照代(GEN)来管理的,分为0,1,2代三代内存。其中第0代内存最为年轻,所管理的对象生命周期最短,垃圾回收最为频繁。2代内存生命周期最长,垃圾回收的消耗代价最大。
1.3 内存分配机制是按照先0代分配,如果申请内存时,0代内存空间不够分配,就会触发垃圾回收机制,GC会将0代内存中尚存活的对象提升至1代,1代中的存活对象提升至2代。同时新开辟一个段里的一段空间作为0代内存,来存放申请的对象。
1.4 内存可以有多个段,每个段中可以有0代,1代,2代内存空间,空间按照1.3的规则来进行管理。释放的对象,由GC释放。
2、以上只是一个简单的归纳,实际上的机制要远比这些复杂。写了一个简单的程序,用Windbg来验证以上规则。这个程序,就是循环分配内存,放在一个队列里,当队列长度大于3的时候,出队,如果申请空间大于90K,则释放,如果小于90K,就保留。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace WindbugDemo 7 { 8 public class MemoryApply 9 { 10 public long Ticks { get; set; } 11 public int MemSize { get; set; } 12 private byte[] AllocateByte; 13 14 private void AllocateMemory() 15 { 16 AllocateByte = new byte[this.MemSize*1000]; 17 } 18 public MemoryApply(long ticks,int size) 19 { 20 this.Ticks = ticks; 21 this.MemSize = size; 22 this.AllocateMemory(); 23 } 24 25 public string Allocated() 26 { 27 string text = "TicksId = "+Ticks.ToString()+":"+this.MemSize.ToString() + " K memory is allocated!"; 28 return text; 29 } 30 31 public string Released() 32 { 33 string text = "TicksId = " + Ticks.ToString() + ":" + this.MemSize.ToString() + " K memory is released!"; 34 return text; 35 } 36 } 37 38 public class MemoryGen 39 { 40 [STAThread] 41 static void Main(string[] args) 42 { 43 Queue<MemoryApply> queue = new Queue<MemoryApply>(); 44 Random random = new Random(); 45 while (true) 46 { 47 int size = random.Next(60, 120); 48 MemoryApply apply = new MemoryApply(DateTime.UtcNow.Ticks, size); 49 Console.WriteLine(apply.Allocated()); 50 queue.Enqueue(apply); 51 if (queue.Count > 2) 52 { 53 MemoryApply release = queue.Dequeue(); 54 if (release.MemSize > 90) 55 { 56 Console.WriteLine(release.Released()); 57 release = null; 58 } 59 System.GC.Collect(); 60 } 61 System.Threading.Thread.Sleep(500); 62 } 63 } 64 } 65 }
3、还是跟以前调试一样,我们用Windbg打开编译后的EXE文件。
Opened log file ‘d:\log\mem11.txt‘ 0:000> sxe ld:clrjit 0:000> g (ad0.a2c): Unknown exception - code 04242420 (first chance) ModLoad: 6f290000 6f30d000 C:\Windows\Microsoft.NET\Framework\v4.0.30319\clrjit.dll eax=00000000 ebx=00000000 ecx=011737a4 edx=08000000 esi=7ffde000 edi=001be8d0 eip=778f70b4 esp=001be7e8 ebp=001be83c iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!KiFastSystemCallRet: 778f70b4 c3 ret 0:000> .loadby sos clr 0:000> !dumpdomain ... -------------------------------------- Domain 1: 00334d38 LowFrequencyHeap: 0033518c HighFrequencyHeap: 003351d4 StubHeap: 0033521c Stage: OPEN SecurityDescriptor: 00336908 ... Assembly: 0038fb80 [D:\bin\MemoryGen.exe] ClassLoader: 0038fc48 SecurityDescriptor: 0038f820 Module Name 01172edc D:\bin\MemoryGen.exe 0:000> !dumpmodule -mt 01172edc Name: D:\bin\MemoryGen.exe Attributes: PEFile Assembly: 0038fb80 LoaderHeap: 00000000 TypeDefToMethodTableMap: 01170038 TypeRefToMethodTableMap: 01170048 MethodDefToDescMap: 01170090 FieldDefToDescMap: 011700bc MemberRefToDescMap: 00000000 FileReferencesMap: 011700d0 AssemblyReferencesMap: 011700d4 MetaData start address: 0121223c (1836 bytes) Types defined in this module MT TypeDef Name ------------------------------------------------------------------------------ 011737b8 0x02000003 WindbugDemo.MemoryGen Types referenced in this module MT TypeRef Name ------------------------------------------------------------------------------ 682e41b8 0x02000001 System.Object 0:000> !dumpmt -md 011737b8 EEClass: 011712d4 Module: 01172edc Name: WindbugDemo.MemoryGen mdToken: 02000003 File: D:\bin\MemoryGen.exe BaseSize: 0xc ComponentSize: 0x0 Slots in VTable: 6 Number of IFaces in IFaceMap: 0 -------------------------------------- MethodDesc Table Entry MethodDe JIT Name 681c952c 67ee612c PreJIT System.Object.ToString() 681dec30 67ee6134 PreJIT System.Object.Equals(System.Object) 681de860 67ee6154 PreJIT System.Object.GetHashCode() 681de2a0 67ee6168 PreJIT System.Object.Finalize() 0117c015 011737b0 NONE WindbugDemo.MemoryGen..ctor() 0117c011 011737a4 NONE WindbugDemo.MemoryGen.Main(System.String[]) 0:000> !bpmd -md 011737a4 MethodDesc = 011737a4 Adding pending breakpoints... sxe -c "!HandleCLRN" clrn 0:000> g (ad0.a2c): CLR notification exception - code e0444143 (first chance) JITTED MemoryGen!WindbugDemo.MemoryGen.Main(System.String[]) Setting breakpoint: bp 011E0099 [WindbugDemo.MemoryGen.Main(System.String[])] bp 011E0099 g Breakpoint 0 hit eax=00000000 ebx=001bf33c ecx=01602198 edx=00000000 esi=01602198 edi=001bf288 eip=011e0099 esp=001bf238 ebp=001bf298 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 011e0099 90 nop 0:000> !u 011737a4 Normal JIT generated code WindbugDemo.MemoryGen.Main(System.String[]) Begin 011e0050, size 1a9 *** WARNING: Unable to verify checksum for MemoryGen.exe d:\source\03MemoryGen\MemoryGen.cs @ 42: 011e0050 55 push ebp 011e0051 8bec mov ebp,esp 011e0053 57 push edi 011e0054 56 push esi 011e0055 83ec58 sub esp,58h ... d:\source\03MemoryGen\MemoryGen.cs @ 57: 011e01cd 33d2 xor edx,edx 011e01cf 8955b8 mov dword ptr [ebp-48h],edx ... 0:000> bp 011e01cd 0:000> !bpmd mscorlib.dll System.GC.Collect Found 4 methods in module 67ee1000... MethodDesc = 67f709ec MethodDesc = 67f709f8 MethodDesc = 67f70a04 MethodDesc = 67f70a10 Setting breakpoint: bp 681BEF6C [System.GC.Collect(Int32, System.GCCollectionMode, Boolean)] bp 681BEF6C Setting breakpoint: bp 688A5B80 [System.GC.Collect(Int32, System.GCCollectionMode)] bp 688A5B80 Setting breakpoint: bp 688A5B2D [System.GC.Collect()] bp 688A5B2D Setting breakpoint: bp 688A5B10 [System.GC.Collect(Int32)] bp 688A5B10 Adding pending breakpoints... sxe -c "!HandleCLRN" clrn 0:000> g
4、在【release = null;】的这句代码加上断点,对象赋空后,GC操作后,我们可以再查阅一个该对象的引用。!bpmd mscorlib.dll System.GC.Collect命令,表示当我们显式调用GC.Collect时,Windbg能中止程序,进入调试命令。
5、当程序中断时,调用!clrstack -a命令,观察一下是否是由于大对象(>90)这个if分支引起的中断,否则我们就执行dumpgen(需要加载sosex.dll调试扩展)
0:000> !clrstack -a OS Thread Id: 0xa2c (0) Child SP IP Call Site 001bf208 688a5b2d System.GC.Collect() 001bf20c 011e01d8 [InlinedCallFrame: 001bf20c] 001bf238 011e01d8 WindbugDemo.MemoryGen.Main(System.String[]) [d:\source\03MemoryGen\MemoryGen.cs @ 59] PARAMETERS: args (0x001bf28c) = 0x01602198 LOCALS: 0x001bf25c = 0x016021a8 0x001bf258 = 0x016021d8 0x001bf288 = 0x00000064 0x001bf254 = 0x01624ab0 0x001bf250 = 0x016022d8 0x001bf280 = 0x65280834 0x001bf27c = 0x00000001 0:000> !do 0x016022d8 Name: WindbugDemo.MemoryApply MethodTable: 011745e8 EEClass: 01171360 Size: 24(0x18) bytes File: D:\bin\MemoryGen.exe Fields: MT Field Offset Type VT Attr Value Name 682e6d34 4000001 c System.Byte[] 0 instance 016022f0 AllocateByte 682f05f4 4000002 4 System.Int64 1 instance 637300683725008226 <Ticks>k__BackingField 682e560c 4000003 10 System.Int32 1 instance 69 <MemSize>k__BackingField 0:000> !eeheap -gc Number of GC Heaps: 1 generation 0 starts at 0x01601018 generation 1 starts at 0x0160100c generation 2 starts at 0x01601000 ephemeral segment allocation context: (0x01624c54, 0x01625bf4) segment begin allocated size 01600000 01601000 01625bf4 0x24bf4(150516) Large object heap starts at 0x02601000 segment begin allocated size 02600000 02601000 0261da58 0x1ca58(117336) Total Size: Size: 0x4164c (267852) bytes. ------------------------------ GC Heap Size: Size: 0x4164c (267852) bytes. 0:000> .load sosex.dll 0:000> !dumpgen 0 -type WindbugDemo.MemoryApply Object MT Size Name --------------------------------------------------- 016021a8 01174638 32 System.Collections.Generic.Queue`1[[WindbugDemo.MemoryApply, MemoryGen]] 016022d8 011745e8 24 WindbugDemo.MemoryApply 01615abc 011745e8 24 WindbugDemo.MemoryApply 01624ab0 011745e8 24 WindbugDemo.MemoryApply 4 objects, 104 bytes 0:000> !dumpgen 1 -type WindbugDemo.MemoryApply 0 objects, 0 bytes 0:000> !dumpgen 2 -type WindbugDemo.MemoryApply 0 objects, 0 bytes 0:000> !eeheap -gc Number of GC Heaps: 1 generation 0 starts at 0x01601018 generation 1 starts at 0x0160100c generation 2 starts at 0x01601000 ephemeral segment allocation context: (0x01624c54, 0x01625bf4) segment begin allocated size 01600000 01601000 01625bf4 0x24bf4(150516) Large object heap starts at 0x02601000 segment begin allocated size 02600000 02601000 0261da58 0x1ca58(117336) Total Size: Size: 0x4164c (267852) bytes. ------------------------------ GC Heap Size: Size: 0x4164c (267852) bytes.
运行 !do命令,观察0x01601018对象,跟eeheap命令输出的内存段相比,的确位于0代内存上。用!dumpgen 0 -type WindbugDemo.MemoryApply命令,输出结果也验证了我们的猜想。
6、运行g命令,再次启动程序。
0:000> g Breakpoint 4 hit eax=001bf418 ebx=001bf33c ecx=01615abc edx=00000000 esi=0036d090 edi=001bf20c eip=688a5b2d esp=001bf208 ebp=001bf230 iopl=0 nv up ei pl nz ac po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212 mscorlib_ni+0x9c5b2d: 688a5b2d 6a02 push 2 0:000> !clrstack -a OS Thread Id: 0xa2c (0) Child SP IP Call Site 001bf208 688a5b2d System.GC.Collect() 001bf20c 011e01d8 [InlinedCallFrame: 001bf20c] 001bf238 011e01d8 WindbugDemo.MemoryGen.Main(System.String[]) [d:\source\03MemoryGen\MemoryGen.cs @ 59] PARAMETERS: args (0x001bf28c) = 0x01602198 LOCALS: 0x001bf25c = 0x016021a8 0x001bf258 = 0x016021d8 0x001bf288 = 0x00000043 0x001bf254 = 0x01624be0 0x001bf250 = 0x01615abc 0x001bf280 = 0x13c9dc2e 0x001bf27c = 0x00000001 001bf418 68f52552 [GCFrame: 001bf418] 0:000> !dumpgen 0 -type WindbugDemo.MemoryApply Object MT Size Name --------------------------------------------------- 01624be0 011745e8 24 WindbugDemo.MemoryApply 1 object, 24 bytes 0:000> !dumpgen 1 -type WindbugDemo.MemoryApply Object MT Size Name --------------------------------------------------- 016021a8 01174638 32 System.Collections.Generic.Queue`1[[WindbugDemo.MemoryApply, MemoryGen]] 016022d8 011745e8 24 WindbugDemo.MemoryApply 01615abc 011745e8 24 WindbugDemo.MemoryApply 01624ab0 011745e8 24 WindbugDemo.MemoryApply 4 objects, 104 bytes 0:000> !dumpgen 2 -type WindbugDemo.MemoryApply 0 objects, 0 bytes
我们发现原来位于0代的内存对象,已经被系统提升至了1代。而2代内存对象没有。
7、运行g命令,让程序继续执行,当程序再次中断时
0:000> g Breakpoint 1 hit eax=00000001 ebx=001bf33c ecx=016159b8 edx=00000000 esi=001bf270 edi=001bf280 eip=011e01cd esp=001bf238 ebp=001bf298 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 011e01cd 33d2 xor edx,edx 0:000> !clrstack -a OS Thread Id: 0xa2c (0) Child SP IP Call Site 001bf238 011e01cd WindbugDemo.MemoryGen.Main(System.String[]) [d:\source\03MemoryGen\MemoryGen.cs @ 57] PARAMETERS: args (0x001bf28c) = 0x01602198 LOCALS: 0x001bf25c = 0x016021a8 0x001bf258 = 0x016021d8 0x001bf288 = 0x00000073 0x001bf254 = 0x016352d4 0x001bf250 = 0x01624ab0 0x001bf280 = 0x387ad227 0x001bf27c = 0x00000000 001bf418 68f52552 [GCFrame: 001bf418] 0:000> !do 0x387ad227 <Note: this object has an invalid CLASS field> Invalid object 0:000> !do 0x01624ab0 Name: WindbugDemo.MemoryApply MethodTable: 011745e8 EEClass: 01171360 Size: 24(0x18) bytes File: D:\bin\MemoryGen.exe Fields: MT Field Offset Type VT Attr Value Name 682e6d34 4000001 c System.Byte[] 0 instance 026053a8 AllocateByte 682f05f4 4000002 4 System.Int64 1 instance 637300683735304244 <Ticks>k__BackingField 682e560c 4000003 10 System.Int32 1 instance 100 <MemSize>k__BackingField 0:000> !gcroot 0x01624ab0 Thread a2c: 001bf238 011e01cd WindbugDemo.MemoryGen.Main(System.String[]) [d:\source\03MemoryGen\MemoryGen.cs @ 57] ebp+48: 001bf250 -> 01624ab0 WindbugDemo.MemoryApply 001bf238 011e01cd WindbugDemo.MemoryGen.Main(System.String[]) [d:\source\03MemoryGen\MemoryGen.cs @ 57] ebp+5c: 001bf23c -> 01624ab0 WindbugDemo.MemoryApply Found 2 unique roots (run ‘!GCRoot -all‘ to see all roots).
这时,我们发现,是因为出队的这个对象尺寸大于90K,那么对象会被置为空,查看GCroot,这时的对象尚未被回收,我们继续运行g命令。
0:000> g Breakpoint 4 hit eax=001bf418 ebx=001bf33c ecx=01624be0 edx=00000000 esi=0036d090 edi=001bf20c eip=688a5b2d esp=001bf208 ebp=001bf230 iopl=0 nv up ei pl nz ac po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212 mscorlib_ni+0x9c5b2d: 688a5b2d 6a02 push 2 0:000> !clrstack -a OS Thread Id: 0xa2c (0) Child SP IP Call Site 001bf208 688a5b2d System.GC.Collect() 001bf20c 011e01d8 [InlinedCallFrame: 001bf20c] 001bf238 011e01d8 WindbugDemo.MemoryGen.Main(System.String[]) [d:\source\03MemoryGen\MemoryGen.cs @ 59] PARAMETERS: args (0x001bf28c) = 0x01602198 LOCALS: 0x001bf25c = 0x016021a8 0x001bf258 = 0x016021d8 0x001bf288 = 0x00000056 0x001bf254 = 0x016355cc 0x001bf250 = 0x01624be0 0x001bf280 = 0x1abedf72 0x001bf27c = 0x00000001 001bf418 68f52552 [GCFrame: 001bf418] 0:000> !eeheap -gc Number of GC Heaps: 1 generation 0 starts at 0x016355c0 generation 1 starts at 0x016351bc generation 2 starts at 0x01601000 ephemeral segment allocation context: (0x0163576c, 0x016375cc) segment begin allocated size 01600000 01601000 016375cc 0x365cc(222668) Large object heap starts at 0x02601000 segment begin allocated size 02600000 02601000 0264ebc0 0x4dbc0(318400) Total Size: Size: 0x8418c (541068) bytes. ------------------------------ GC Heap Size: Size: 0x8418c (541068) bytes. 0:000> !do 0x01624ab0 Name: WindbugDemo.MemoryApply MethodTable: 011745e8 EEClass: 01171360 Size: 24(0x18) bytes File: D:\bin\MemoryGen.exe Fields: MT Field Offset Type VT Attr Value Name 682e6d34 4000001 c System.Byte[] 0 instance 026053a8 AllocateByte 682f05f4 4000002 4 System.Int64 1 instance 637300683735304244 <Ticks>k__BackingField 682e560c 4000003 10 System.Int32 1 instance 100 <MemSize>k__BackingField 0:000> !FinalizeQueue SyncBlocks to be cleaned up: 0 Free-Threaded Interfaces to be released: 0 MTA Interfaces to be released: 0 STA Interfaces to be released: 0 ---------------------------------- generation 0 has 0 finalizable objects (00376d20->00376d20) generation 1 has 0 finalizable objects (00376d20->00376d20) generation 2 has 6 finalizable objects (00376d08->00376d20) Ready for finalization 0 objects (00376d20->00376d20) Statistics for all finalizable objects (including all objects ready for finalization): MT Count TotalSize Class Name 682e7048 1 20 Microsoft.Win32.SafeHandles.SafePEFileHandle 682e6068 1 20 Microsoft.Win32.SafeHandles.SafeFileHandle 682dc234 1 20 Microsoft.Win32.SafeHandles.SafeFileMappingHandle 682dc1e4 1 20 Microsoft.Win32.SafeHandles.SafeViewOfFileHandle 682e76d0 1 44 System.Threading.ReaderWriterLock 682e4960 1 52 System.Threading.Thread Total 6 objects 0:000> !gcroot 0x01624ab0 Found 0 unique roots (run ‘!GCRoot -all‘ to see all roots). 0:000> !eeheap -gc Number of GC Heaps: 1 generation 0 starts at 0x016355c0 generation 1 starts at 0x016351bc generation 2 starts at 0x01601000 ephemeral segment allocation context: (0x0163576c, 0x016375cc) segment begin allocated size 01600000 01601000 016375cc 0x365cc(222668) Large object heap starts at 0x02601000 segment begin allocated size 02600000 02601000 0264ebc0 0x4dbc0(318400) Total Size: Size: 0x8418c (541068) bytes. ------------------------------ GC Heap Size: Size: 0x8418c (541068) bytes. 0:000> !dumpgen 0 -type WindbugDemo.MemoryApply Object MT Size Name --------------------------------------------------- 016355cc 011745e8 24 WindbugDemo.MemoryApply 1 object, 24 bytes 0:000> !dumpgen 1 -type WindbugDemo.MemoryApply Object MT Size Name --------------------------------------------------- 016352d4 011745e8 24 WindbugDemo.MemoryApply 1 object, 24 bytes 0:000> !dumpgen 2 -type WindbugDemo.MemoryApply Object MT Size Name --------------------------------------------------- 016021a8 01174638 32 System.Collections.Generic.Queue`1[[WindbugDemo.MemoryApply, MemoryGen]] 01624ab0 011745e8 24 WindbugDemo.MemoryApply 01624be0 011745e8 24 WindbugDemo.MemoryApply 3 objects, 80 bytes 0:000> .logclose Closing open log file d:\log\mem11.txt
这时,我们发现原来的1代内存已经被提升至第2代了。
标签:内存分配 address jit shc ram null apply reading 编译
原文地址:https://www.cnblogs.com/hzwanglw/p/13285499.html