概述
第三章的标题是:垃圾收集器与内存分配策略
GC Roots
首先必须吐槽一下,这个章节里,作者对GC Root的定义的描述的不太清楚的。读完之后,还是一头雾水。为了大概搞清楚这个问题,自己找了蛮多资料,下面简单的汇总描述一下。
GC Root是一组活跃的引用,不是一组对象。
这组引用包含的范围很广,为了说明问题,就以当前线程的线程栈中的引用作为例子。
public void method1() {
Object obj = new Object();
}
当线程A执行了method1方法的时候,创建了一个对象,obj是其引用。那么这个obj就是GC Roots中的一种。当线程A执行完后,存在于当前线程的栈帧中的obj引用会被自动出栈,无需我们理会。而原先obj所引用的对象则存在于堆中,只能等待垃圾回收器回收。这里就有个疑问
obj已经丢失了(出栈了),那么原先obj所引用的对象如何回收呢?
这里必须澄清的是:
所谓从GC Roots这些根引用开始的根搜索算法,是寻找活跃引用对应的对象,能被找到的就是活的对象,不能被找到的对象,就可以判断为死亡,就可以被回收。
public void method2() {
Object obj2 = new Object(); //第一行
。。。。。。 //第二行
。。。。。 //第三行
}
比如说,另外一个线程B执行了method2方法,并且执行了
Object obj2 = new Object();
这个时候刚好发生Young GC,method2方法由于还未执行完(第二行和第三行代码还未执行),obj2引用属于活跃引用,从obj2开始遍历对象图,找到了obj2引用的对象。至于之前线程A创建的obj1引用所引用的对象,从obj2遍历的话,是不可达的,因此会被回收掉的。
CMS收集器
CMS收集器是一种以获取最短回收停顿时间为目标的收集器。
CMS之所以能尽量减少停顿时间,最主要的原因是在并发标记和并发清除阶段中,无需Stop The World。垃圾回收线程可以与用户线程一起工作。CMS收集器分为四个阶段:
1、初始标记:只是找到GC Roots引用能直接关联到的对象,并把这些对象的字段(比如说对象引用字段)保存到扫描栈中;
2、并发标记:对初始化标记中保存到扫描栈中的引用,递归扫描整个堆中的对象图;
3、重新标记:并发标记过程中,运行的用户线程可能导致在并发标记阶段的标记记录有变动,需要重新标记;
4、并发清除 :清除不可达对象,回收内存。
1和3需要Stop The World。
CMS收集器缺点:
1、垃圾回收线程会占用一部分CPU,如果配置的垃圾回收线程比较多,对应CPU敏感类型的应用,可能有影响;
2、会产生浮动垃圾。也即是并发标记阶段产生的垃圾,只能在下次Full GC的时候才能回收掉;
3、会导致内存碎片。
对象要尽量的小
1、不然可能提早触发Young GC;
2、可能直接将短生命周期的对象进入老年代,只能等待Full GC的时候才能回收掉。而它本来应该是在Young GC的时候就被回收掉的。