gc即垃圾回收。
.net和java开发时,我们不再需要关注内存的创建和销毁。.net的clr和java的jvm来帮我们管理内存。但是了解gc的一些原理和过程还是有好处的。
总的来说,gc要解决以下这几个问题:
1.什么是垃圾
2.怎么回收垃圾
1.什么是垃圾
垃圾就是一段不再使用的内存。应用程序创建的对象会分配在堆上。由于开发不再自己手动销毁对象,随着程序的运行,堆上的对象越来越多。如果不进行清理,内存就会耗尽。这时候,gc就要进行清理了。
什么对象是垃圾呢?
最早的gc是根据引用计算器来判断对象是否是可用的,是否是垃圾。
现在的gc都是根据循环查找来判断对象是不是垃圾。从一些根对象开始,递归查找那些可达的对象,剩下的都是不可达的,那些不可达的对象就是垃圾。
什么是根对象呢?
1.GC发生时,当前正在执行的方法中那些本地变量/参数。
2.静态变量
3.非托管类似COM+一样通过interop被使用的对象
4.实现了finalizer接口的对象
早期的应用计算器算法,对于那些循环应用的对象是无能为力的。从根对象递归查找来判断是可以解决循环应用的问题的。
2.怎么回收垃圾
回收垃圾时,gc将那些被标记为垃圾的对象在堆上进行清理,然后将剩下的不是垃圾的对象压缩在一起,减少内存碎片。
由于在压缩的时候,堆上的对象的地址发生了改变,所以,引用这些对象的指针变量也需要指向新的地址。垃圾回收可以简单的认为有这几个过程:
1.将被标为垃圾的对象进行回收。
2.将不是垃圾的对象进行压缩,减少内存碎片
3.由于第2步,对象的地址发生了,改变。指向该对象的指针变量也需要指向新的地址。
当垃圾回收开始时,所有工作线程必须挂住,因为这些对象的指针会发生改变。等gc完成后,挂起的线程再继续执行。
gc中还有个概念是分代回收。
如果每次gc都堆上所有的对象进行扫描,是很消耗时间的。
gc中对堆上的对象进行分代。目前有0代,1代和2代。有了代的概念,gc时,就可以只对其中某一代进行回收。或者对某几代进行回收。
但应用初始化完成时,堆上没有数据。随着应用的运行,堆上开始有了新对象。这些对象都是0代的对象。随着对象的越来越多,要进行gc了。gc对堆上所有的对象进行扫描,标记垃圾,将垃圾进行清除。gc结束后,那些没有被回收的对象就从0代变成了1代。这之后新创建的对象就成了0代。随着对象越来越多,进行第二次gc。gc过后,1代对象成为2代对象。0代成为1代对象。gc过后,新创建的对象成为了0代对象。再进行gc时,那些2代对象,还是2代对象,目前还不存在3代对象。
在进行gc时,可以只回收0代的对象。如果0代回收掉后,内存还不够,再回收1代对象。如果1代回收后,仍不够,再回收2代对象。分代回收是为了提高性能。微软和java一直在优化内存回收的算法来提高性能。里面具体的实现细节可能会有改动。但总的思路是不变的。