《深入理解Java虚拟机》读书笔记:第三章垃圾收集器与内存分配策略

时间:2022-01-15 10:01:29

垃圾收集(Garbage Collection,GC)

程序计数器、虚拟机栈和本地方法栈3个区域随线程而生,随线程而灭

判断对象是否存活:

  • 引用计数(ReferenceCounting)算法判断对象是否存活,但是很难解决对象之间相互循环引用的问题.
  • 可达性分析(Reachability Analysis),判断对象是否存活,通过一系列的称为"GCRoots"的对象作为起始点,从节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,证明此对象是不可用的(GC roots到这个对象不可达)

*Java中可作为GC Roots的对象:1.虚拟机栈中引用的对象;2.方法区中类静态属性引用的对象;3.方法区中常量引用的对象;4.本地方法栈中JNI(Native)引用的方法

 

引用分为 : 强引用、软引用、弱引用、虚引用;依次减弱

  1. 强引用(StrongReference):永远不会被回收
  2. 软引用(SoftReference类):有用但非必需的对象,对于软引用关联着对象,在系统将要发生内存溢出异常之前,将进行第二次回收
  3. 弱引用(WeakReference类):非必需的对象,比软引用弱,被弱引用关联着的对象只能生存到下一次垃圾收集器发生之前
  4. 虚引用(PhantomReference类):无法通过虚引用来取得一个对象实例,为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知

可达性分析后发现不可用,第一次标记并筛选是否有必要执行finalize()方法,当对象没有覆盖finalize()方法或虚拟机已经执行过finalize()方法则被视为"没有必要执行"finalize()方法(缓刑)放在F-Queue队列,并在稍后由一个虚拟机自动建立的,低优先级的Finalizer线程去执行它

对象的自我拯救演示:

public class FinalizeEscapeGC {

public static FinalizeEscapeGC SAVE_HOOK = null;

public void isAlive(){
System.out.println("yes,I am still alive");
}

@Override
protected void finalize() throws Throwable{
super.finalize();
System.out.println("执行finalize()方法");
FinalizeEscapeGC.SAVE_HOOK = this; //拯救自己
}

public static void main(String[] args) throws InterruptedException {
SAVE_HOOK = new FinalizeEscapeGC();
SAVE_HOOK = null;
System.gc();
// finalize()方法优先级很低,所以暂停0.5s以等待它
Thread.sleep(500);
if (SAVE_HOOK != null) {
SAVE_HOOK.isAlive();
} else {
System.out.println("no I am dead");
}

//下面的代码同上面,但是拯救失败了,因为一个对象的finalize()方法最多只会被系统自动调用一次
SAVE_HOOK = null;
System.gc();
Thread.sleep(500);
if (SAVE_HOOK != null) {
SAVE_HOOK.isAlive();
} else {
System.out.println("no I am dead");
}
}
}
//执行结果如下:执行finalize()方法yes,I am still aliveno I am dead

finalize()能做的try-finally或者其他可以做的更好

方法区(HostSpot中的永生代)垃圾收集主要回收废弃常量和无用的类;回收废弃常量与回收堆中对象类似当其没有被引用发生内存回收

判断无用类的条件:1.堆中不存在该类的任何实例;2.加载该类的ClassLoad已经被回收;3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

参数 : -Xnoclassgc     -verbose:class-XX:+TraceClassLoading -XX:TraceClassUnLoading查看类加载和卸载信息

 

垃圾收集算法:

1.标记-清除算法  

标记、清除效率不高,且会产生大量不连续的内存碎片

 

2.复制算法  

将可用内存按容量划分相等的两块,每次使用一块。当这一块使用完了,就将还存活的对象复制到另一块上面,再把已使用过的内存空间一次清理掉

现代虚拟机采用这种算法,但是将内存分为较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor,回收时将Survivor和Eden中存活的对象复制到另一块Survivor空间。因为新生代中的对象98%是"朝生夕死"

当Survivor空间不够用时,需要依赖其他内存(老年代)进行分配担保(Handle Promotion) --- 即Survivor空间不够时,这些存放不下的对象通过分配担保机制进入老年代

缺点:在对象存活率较高时就要进行较多的复制操作,效率将变低

 

3.标记-整理法(Mark-Compact)

标记过程与"标记-清除算法"相同,清理时让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存

 

4.分代收集算法(Generation Collection)

根据对象存活周期的不同将内存划分为几块.一般将Java堆分为新生代和老年代,根据各个年代特点选用最适当的算法.新生代中每次垃圾收集时都有大批对象死去,少量存活,因此选用复制法;老年代中对象存活率高,没有额外空间分配担保,使用"标记-清理"或"标记-整理"算法回收

 

HotSpot的算法实现

枚举根节点(GC Roots枚举):可达性分析在逐个检查引用时消耗很多时间,整个分析期间不可以出现对象引用关系不断变化的情况,即执行系统需要停顿

在HotSpot的实现中,使用一组OopMap的数据结构记录对象的引用位置

 

安全点(Safepoint)即生成OopMap的特定位置.GC发生时让所有线程都跑到最近的安全点上停顿下来(通过抢先式中断或主动式中断)

抢先式中断:GC发生时,中断所有线程,如果有线程中断的地方不在安全点上就恢复线程到安全点上

主动式中断:GC需要中断线程时,设置一个标志,各个线程主动轮询这个标志,发现中断标志为真时就自己中断挂起

 

安全区域(Safe Region)指一段代码片段中,引用关系不会发生变化,在这个区域的任意地方开始GC都是安全的

 

垃圾收集器是内存回收的具体实现

Serial收集器:在进行垃圾收集时必须暂停其他所有的工作线程,直到收集结束(对其他应用难以接受,但相比其他单线程收集器简单高效)

ParNew收集器:Serial收集器的多线程版

ParallelScavenge 收集器:使用复制算法的新生代并行多线程收集器,"吞吐量优先收集器",高效率地利用CPU尽快完成运算任务

SerialOld收集器:Serial收集器的老年代版本    Parallel Old收集器:ParallelScavenge收集器的老年代版本

CMS(ConcurrentMarkSweep)收集器:基于"标记-清除"算法实现,分为初始标记,并发标记,重新标记,并发清除4个步骤

G1(Garbage-First)收集器:具备并行与并发,分代收集,空间整合(不产生内存空间碎片),可预测的停顿(降低停顿时间)特点

 

参数-XX:+PrintGCDetails(打印GC日志)

理解GC日志,日志信息包括GC发生时间,垃圾收集的停顿类型,GC发生区域,[GC前该区域已使用容量 -> GC后该区域已使用容量(该内存区域总容量)],GC前Java堆已使用容量 -> GC后Java堆已使用容量(Java堆总容量),GC占用时间


垃圾收集器参数

 

对象内存分配

对象的内存分配主要在新生代的Eden区上,当Eden区没有足够空间时,发起Minor GC(新生代GC)    *(Major GC/Full GC 老年代GC )

大量连续内存空间的Java对象(很长的字符串以及数组)直接进入老年代.参数-XX:PretenureSizeThreshold ,令大于这个设置值的对象直接在老年代分配

长期存活的对象将直接进入老年代:对象在Eden区出生,GC后移动到Survivor区,每熬过一次Minor GC年龄+1,到一定程度(默认15岁)就将晋升到老年代.参数 -XX:MaxTenuringThreshold 设置晋升老年代的年龄阈值

 

动态对象年龄判断:当相同年龄所有对象大小的总和大于Survivor空间的一半就可以直接进入老年代,无须等到年龄阈值

 

空间分配担保失败的情况,老年代收下新生代GC后的Survivor中的对象(取之前每一次回收晋升到老年代对象容量的平均大小值作为经验值,与老年代的剩余空间比较),当平均值小于实际存活的对象大小时出现担保失败,重新发起一次Full GC