CLR核心机制
堆栈内存分配发生了什么
什么是堆栈
栈和堆(托管堆)都存在于进程的虚拟内存中,为程序运行提供存储空间。
栈(Stack)是操作系统在建立线程时,为这个线程建立的存储区域。
堆(Heap)是应用程序在运行的时候请求操作系统分配给自己内存,是申请-给予的过程。由于从操作系统管理的内存分配所以在分配和销毁时都要占用时间,所以用堆的效率低的多
栈Stack
- 栈中存储值类型;
- 栈上是先进后出原则;
- 栈是自维护的,当元素不再被使用会被抛出;
- 栈空间较小,访问速度快;
堆Heap
- 堆(也叫做托管堆)存储引用类型;
- 堆受垃圾处理器GC管理;
- 堆没有访问限制,按地址索引;
- 堆空间较大,访问速度没有栈快;
C#堆栈如何分配?
值类型分配在栈;
引用类型分配在堆上面;
堆资源是怎么分配的
- 空间有限
- 连续摆放
- 更新指针
- 局部化(locality)
引用类型new对象是发生了什么?
- 调用new的时候 就会去栈上面开辟内存,创建实例
- 把实例的引用传递给构造函数
- 执行构造函数
- 返回引用
值类型new对象是发生了什么?
- 在线程栈上创建一个ValPoint类型的变量vPoint1
- new ValPoint()之前,将vPoint1压到栈上new操作符并不分配内存,也不是创建实例。它仅仅是调用了ValPoint结构的默认构造函数,根据构造函数去初始化vPoint1结构的所有字段
装箱-拆箱
- Box
装箱在值类型向引用类型转换时发生 - unbox
拆箱在引用类型向值类型转换时发生
GC机制和优化
垃圾回收GC
内存是有上限的,不可能无止境的分配空间,
因此就产生了GC(Garbage Collector)的需求 .
- 提高了软件开发的抽象度;
- 程序员可以将精力集中在实际的问题上而不用分心来管理内存的问题;
- 可以使模块的接口更加的清晰,减小模块间的偶合;
- 大大减少了内存人为管理不当所带来的Bug;
- 使内存管理更加高效。
总的说来就是GC可以使程序员可以从复杂的内存问题中摆脱出来,从而提高了软件开发的速度、质量和安全性。
什么样的对象需要垃圾回收
- 托管资源
- 存在堆里(含值类型+引用类型)
托管资源一般是指被CLR控制的内存资源,这些资源的管理可以由CLR来控制,例如程序中分配的对象,作用域内的变量等,大部分对象都是托管资源。
非托管资源是CLR不能控制或者管理的部分,这些资源有很多,比如文件流,数据库的连接,系统的窗口句柄,打印机资源,需要调用Dispose方法
Mark-Compact 标记压缩算法
阶段1: Mark-Sweep 标记清除阶段,先假设heap中所有对象都可以回收,然后找出不能回收的对象,给这些对象打上标记,最后heap中没有打标记的对象都是可以被回收的;
阶段2: Compact 压缩阶段,对象回收之后heap内存空间变得不连续,在heap中移动这些对象,使他们重新从heap基地址开始连续排列,类似于磁盘空间的碎片整理。
回收前后
- 内存移动
- 地址变更
- 阻塞线程
- 继续运行
分代策略
- 第1次,遍历全部对象,找出没有引用的,删除,剩下1代
- 第2次,遍历0代对象,找出没有引用的,删除,剩下升级1代
如果还不够空间,才会遍历1代,没有引用的删除,
还在被使用的,升级为2代。都不够才会检查2代
各代空间大小?
实际上是没有具体值的,由CLR管理。初始化时就会给出0代/1代的预算,然后动态调节!
GC后,对象几乎没有留存,那么就会减小预算,
加快回收频率,每次速度快
如果GC后有很多对象留存,那么就会增加预算,
降低回收频率,每次回收内容多
如果空间不够,就会全面回收,再不够就OutOfMemory了
什么时候GC
- new对象时–临界点
- Windows报告内存不够
- GC.Collect 强制GC
- 程序退出或者卸载AppDomain
怎么用GC. Collect
大多数情况下,应该让GC自己决定回收时间,以更好的控制各代预算,也避免GC降低响应
如果知道大量对象无效,可以手动GC,尤其是2代
大对象策略
85000字节为限
独立的地址空间
不会移动压缩大对象,成本高,但也造成了碎片
总是2代对象
垃圾回收模式
工作站
单线程回收
服务器
多区域并行回收