唐元志的技术博客 软件设计与开发

.NET 内存管理原理篇

2021-08-10
Michael
       
阅读:

.NET CLR 管理了应用程序占用内存的分配和释放,开发人员在开发托管的应用程序是就不用在开发代码去管理内存了。针对内存的管理依赖于垃圾回收机制(Garbage Collection),分为 标记(Full Mark) -> 回收(Sweep) -> 压缩(Compact)三个阶段,不在使用引用计数的方式了,类似采用GC机制的还有像Java,Node JS, Go 等编程语言。

一个进程占用的内存主要分为Allocated Space、Free、Committed和Reserved。而在.NET应用程序中,在应用程序内转成原生的机器码之前,将由C#编译器先将原始代码和相关的资源与引用编译成托管应用程序集,之后交由CLR进行管理和相关的内存管理。

Stack 和 Heap

.NET主要使用了堆栈、非托管堆和托管堆

  • 堆栈(Stack) 它是按每个线程管理的,用于存储局部变量、方法参数和临时值。当方法返回时,GC不会自动清理堆栈。对对象的引用存储在堆栈上,但实际对象在堆上分配,GC知道这一点。当GC无法找到对象的引用时,它将从堆中删除该对象。
  • 非托管堆 非托管代码将在非托管堆或堆栈上分配对象。托管代码还可以通过调用Win32 api在非托管堆上分配对象。
  • 托管堆(Managed Heap) 托管代码在托管堆上分配对象,而GC负责管理托管堆, 总共有三个代(Generation),分别为Gen0, Gen1和Gen2。GC还维护一个大对象堆,以补偿在内存中移动大对象的成本。 备注: 最初,Gen0为256KB, Gen1为2MB, Gen2为10MB 用于>=85000字节的对象的大对象堆(LOH),可以采用 GC.GetGeneration (obj)去获取当前对象存活于哪一个Gen。
  • 举个列子,比如下面的代码,将会输出 0 0 2 0
public class LargeObjectExample
{
	public string NormalString { get; set; } = "Example";
	public byte[] ByteArray85000 = new byte[85000];
	public byte[] ByteArray80000 = new byte[80000];
}


static void Main(string[] args)
{
	var largeObj = new LargeObjectExample();

	Console.WriteLine(GC.GetGeneration(largeObj));
	Console.WriteLine(GC.GetGeneration(largeObj.NormalString));
	Console.WriteLine(GC.GetGeneration(largeObj.ByteArray85000));
	Console.WriteLine(GC.GetGeneration(largeObj.ByteArray80000));

	Console.ReadKey();
}

垃圾回收

对象的分配与回收流程是:当创建一个新对象是,对象将分配值Gen0,如果Gen0剩余空间不够,那么对将Gen0进行内存回收,Gen0上所在对象将会移至Gen1,如果此时Gen1剩余空间不够存放来自于Gen0的对象,那么将对Gen1进行垃圾回收,将Gen1的对象移动至Gen2。如果此时Gen2剩余空间也不够,那么将会对Gen2进行回收,这个又称为Full GC, 同时也会对大对象进行回收.

比如:现在有10个对象,其中对象2,3,5,6和10拥有Finalize方法,整个GC将经过如下几个阶段

  • 初始
  • 标记
  • 清理
  • 压缩

类似文章

Comments