目录
垃圾回收主要指堆,栈所占用的内存会随着线程的销毁而自动收回
在程序运行中,会在堆中创建很多对象,如果这些对象已经不在使用,成为死掉的对象,但是他们所占用的内存空间还未释放,这样的对象越来越多时,会对程序运行造成非常大的影响,因此我们要使用合理的方法来清理死掉的对象,来保障程序的良好运行
1,如何标识死亡的对象(垃圾标记)
1.1 引用计数法
引用计数法的思想是:如果一个对象被引用,那么引用次数就加一,反之,如果一个对象失去一个引用,那么引用次数就减一,如果这个对象没有被引用,那说明这个对象已经死掉了
看起来好像并没有什么问题,但是我们来模拟这样一个场景
public class Test {
public Object instance = null;
public static void testGC() {
Test test1 = new Test();
Test test2 = new Test();
test1.instance = test2;
test2.instance = test1;
test1 = null;
test2 = null;
}
}
虽然创建的两个test引用都已经置为null了,但是内存中的对象引用还是不为0,而且再也无法访问到test.instance所引用的的对象了,所以这两个对象将不会被标记为死亡的对象
1.2 可达性分析算法(JVM虚拟机使用)
可达性分析算法的思想是:确定一个GCroot对象,这个GCroot对象可以是虚拟机栈(栈帧中的本地变量表)引用的对象,方法区中类静态属性引用的变量,方法区中常量引用的对象或本地方法栈中JNI(Native)引用的对象。如果堆中的对象不能被GCroot引用(不可达),那么就会被标记为死亡
2 如何删除死亡的对象(垃圾回收)
2.1 标记清除算法
直接对标记死亡的对象内存进行释放
缺点:标记清除掉之后会产生大量的不连续内存碎片,空间碎片过多的话,如果在程序运行时需要分配较大的对象,会因为无法找到连续内存而提前进行一次垃圾清理
2.2 复制算法
将内存空间分为两部分,一部分为存放对象的from区,另一部分为空白的to区,每一次垃圾清除会将from区的死亡对象清除后,移动整理到to区
缺点:空间利用率低,在使用时只有一半的内存可以珍重发挥作用
2.3 标记整理算法
将标记死亡的对象清理掉后按照顺序进行整理
缺点:清除时需要对内存中的对象进行移动整理,势必会降低效率
3 GC中会使用什么垃圾回收算法?
3.1 老年代执行标记整理算法
JVM将堆分成了两部分,一部分是新生代,另一部分是老年代,新生代:老年代是1:2的比例,新创建的对象回先存入新生代中,如果对象活过了15次GC(一般默认为15次),就会存入老年代,由于老年代对象都是活过了15次GC的对象,即认为他们的死亡频率不高,因此老年代执行的是标记整理算法
3.1 新生代执行复制算法
新生代中分为Eden区,to区和from区,Eden区:to区:from区是8:1:1的比例。
新创建的对象会放入Eden区,第一次GC时,会将Eden区的死亡对象占用的内存空间释放掉,之后把Eden区的对象存入to区,之后to区变为from区,from区变为to区
第一次DC之后的GC都是将Eden区和from区的存活对象复制到to区后,to区变为from区,from区变为to区,即新生代执行的是复制算法
当对象在新生代活过了15次GC之后,就会被存入老年代,进入老年代的标记整理算法
新生代GC:Minor GC
老年代GC:Full GC 或 Major GC
4 垃圾收集器
垃圾收集器是对垃圾回收算法的体现
当GC线程执行的时候用户线程需要全部停止,这个停止时间称为stop the word(STW)
4.1 单线程收集器
单线程收集器在GC时只有一个线程扫描内存,在计算机内存越来越大的今天,STW会增到到不能接受的地步
4.2 多线程收集器
CAS多线程收集器作用域老年代垃圾清理,它将垃圾清除这个操作分为了多步,其中垃圾清除过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,大大降低了STW
垃圾收集器的改进归根到底是为了减少STW