码迷,mamicode.com
首页 > 数据库 > 详细

Windbg探索之.NET内存管理方式

时间:2020-07-11 22:33:35      阅读:58      评论:0      收藏:0      [点我收藏+]

标签:内存分配   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代了。

Windbg探索之.NET内存管理方式

标签:内存分配   address   jit   shc   ram   null   apply   reading   编译   

原文地址:https://www.cnblogs.com/hzwanglw/p/13285499.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!