
GC垃圾回收 是回收托管堆里面的垃圾
值类型和引用类型
值类型本身储存值 引用类型储存于内存地址 值类型存在地址指向的空间中
值类型和引用类型存在于栈空间还是堆空间是根据定义的位置而定的
值类型的对象会根据定义的位置隐式分配和释放
引用类型的对象需要通过new关键字显示分配,new会从堆空间申请一块空间用于保存值,然后返回这个空间的开始地址
他返回的地址是可以传递到不同的线程的里面的 那么我们的程序只是多一个指向空间的地址
如果你的程序中没有指向这个已分配空间的内存地址 那么这个这块已分配空间内存就会在合适的时候被GC给回收
.NET中的GC
其实GC的工作就是找出你的程序中的堆空间分配的空间中 那些空间不再被程序使用 那么就是程序就有没有被指向的空间 然后回收掉这些空间
在.NET中 最主流的使用方式就是“标记并清除”
这种方式就是选择一部分引用类型作为跟对象 然后再递归标记对象的引用类型成员 那么这些被标记的成员就会存活下来
.NET中的跟对象
包括各个线程上的变量、全局变量、GC句柄和析构队列
GC 分代
GC分代分别为 0,1,2
每一轮GC回收都会标记存活 0代里面的是最有可能被回收的,其实是 1 ,再就是2,
每一轮GC回收之后 存活0代可能会成为1代 ,存活1可能成为2 ,存活2还是存在于2代
0代是怎么来的呢 ,就是每当你托管堆里面创建新对象的时候 他可能会纯在于0代
所以只要你在GC里存活的够久那么你就可能成为第2代
为什么要分代呢?
分代的目的是,尽量增加每次执行垃圾回收时,可回收的对象数量,并减少垃圾回收所需的时间。
但是当对象大于85000字节的时候 他会直接存放于大对象堆里面 就不会被存放于0代了 也就是直接就是2代
压缩
反复执行分配与回收可能会导致我们的的程序有很多空余的空间,这些空间空余空间又被称为碎片空间
压缩机制可以通过移动移动已分配空间再把碎片空间整合到一起,使得堆可以存放更大的对象
大小对象
.NET会根据引用类型对象值所占的空间大小来分配是大对象还是小对象
大对象存在于大对象堆 小对象存在于小对象堆 而大小对象划分的界限就是85000字节
新分配的大对象会直接存在于2代 而小对象会存在于0代
移动大对象需要的成本的很高 随意压缩机制默认只在小对象启用
固定对象
托管代码传递引用类型对象给非托管代码时必须创建固定类型的GC句柄,并在托管代码中保持这个句柄必须存活到非托管代码调用结束
创建了固定类型的GC句柄就被称为固定对象
固定对象所产生的碎片空间是无法合并的 而且固定对象在堆中可能会存在降代
析构队列
如果在垃圾回收的过程中执行析构函数,那么垃圾回收的时间是不可预料的。
如果对象不在存活但是定义了析构函数,那么在本轮的GC中会把对象添加到析构队列并标记存活。
析构函数执行完毕的对象,可以在下一轮的GC中被回收
STW
Stop The World
对象之间的引用关系会随着程序的运行而不断发生改变,让执行GC的线程和执行其他处理的线程同时处理会带来一些问题。
这个时候就是 D本来也该存活但是没活得下来 后面再调用A肯定会出现问题
这个时候就让GC处理以外的线程全部停止运行,这样的操作我们就叫STW(都给我停 我要扫地了)
工作站GC 和 服务器GC
工作站模式,适用于内存占用量小的程序和桌面程序,他可以提供更短的响应时间。
服务器模式,使用yu于内存占用量较大的程序和服务程序,可以提供更高的吞吐量
GC在工作站模式是单线程的 使用的线程是对象分配的线程 使用也更加频繁
GC在服务器模式是多线程的 使用的线程也是多线程 ,应为他需要更高效的GC方式,所以使用起来就不怎么频繁
普通GC和后台GC
普通GC会导致更长的STW停顿时间,但消耗的资源比较小 ,并且支持压缩处理
后台GC每次STW的时间更短,但是停顿次数与消耗的资源会更多,而且不支持压缩处理
.NET的程序的内存结构
分为三个部分
1.非托管部分 --非托管代码-非托管静态变量等等。。。
2托管堆 --小对象堆-大对象堆-短暂堆段-可重用堆
3AppDoman--高频堆-低频堆-字符串池-函数入口代码堆等等。。。
托管堆与堆断
GC回收机制的话 一般是会在四种条件会触发
手动GC
GC.Collect();
但是手动GC的话 一般情况下是不建议这么做的 除非你有什么特殊的用意
因为 GC里面 都是给你封装好了
2.物理内存不足
这就是物理内存不足的时候会触发的GC
3.分配对象时候找不到可用空间
4.分配量超过阈值

