1. young GC 和 full GC
- young gc:回收年轻代垃圾,回收频繁,速度较快
- full gc:回收老年代+年轻代,速度比young gc慢约10倍
2.JVM内存分配与回收
2.1 对象分配时优先进入Eden
2.2 大对象直接进入老年代
- 通过设置JVM参数 -XX:PretenureSizeThreshold,当对象大于该值时,直接放入老年代
2.3 长期存活对象进入老年代
- 默认经过15次young gc
2.4 对象动态年龄判断
+survivor区中,一批对象占据的大小大于survivor区的50%时,大于这批对象最大年龄的对象都放入老年代(一般在young gc之后执行)
2.5 young gc之后,存活的对象survivor区域放不下,这些对象也会直接放入老年代
2.6 老年代空间分配担保机制
在young gc之前,进行判断
(1)如果老年代的剩余大小>=年轻代所有对象的大小之和,则触发young gc;否则执行步骤2
(2)如果没有设置担保参数 -XX:HandlePromotionFailure(jdk8之后默认设置了该值),则执行full gc;否则执行步骤3
(3)判断老年代的可用空间是否大于之前每次young gc之和进入老年代对象的平均值大小,如果剩余空间较大,则执行young gc,否则执行full gc
老年代空间分配担保机制.png
3.如何判断对象是否可被回收
- 引用计数法:对象每被引用一次,次数+1,引用失效,次数-1;引用为0,对象即为可被回收对象
优点:实现简单,效率高
缺点:可能出现循环引用
- 可达性分析:通过一系列GC ROOTs节点作为七点,开始搜索,可以到达的节点标记为非垃圾对象,不可到达的节点就是垃圾对象
可以作为GC Roots的节点:线程栈中的本地变量,静态变量,本地方法栈中的变量
-
finalize()方法最终判断是否存活
即使对象在可达性分析中被标记为垃圾对象,也不是立马被清除的,只有当再被标记时,才会被清除。就如同被判了“死刑”,也不是“非死不可”的。在finalize()方法中,他们还有最后一次不被清除的机会- 对在可达性分析中,没有与GC Root相连的对象,进行第一次标记,并进行一次筛选;筛选的条件是:对象必须要重写finalize()方法,如果没有重写,则直接回收
- 对有重写finalize()方法的对象,在回收之前,先执行该方法,如果在方法中,该对象重新与引用链上的变量产生联系(比如赋值给某个类变量或者成员对象的变量),那么这个对象在第二次标记时,就不会被删除
-
如何判断一个类是无用的类
(1) 所有该类的实例均被回收,即不会再有对象的对象头指向该类的类元数据
(2)该类的类加载器被回收了
(3)该类对于的java.lang.Class对象没有在任何地方被引用,即无法再通过反射访问该对象属性
4.垃圾回收算法
-
标记-清除法:分为标记和清除连个阶段
- 优点:算法简单
- 缺点:
(1)效率低,需要递归与全堆对象遍
(2)回收之后的空间离散不连续
-
复制算法:将内存大小分为两块,每次只用其中一块,当对使用的一块进行回收时,将存活的对象复制到另一块去
- 优点:空间连续
- 缺点:空间使用率低,每次只能使用一半的空间
-
标记-整理算法:先执行标记,然后将存活对象向前移动,最好释放端边界以外的空间
-
分代回收算法:对年轻代和老年代采用不同的算法进行回收
- 对于年轻代:90%的对象都是“朝生夕死”,只有很少的对象存活,可以采用复制算法
- 对于老年代:对象存活几率比较高,可以采用“标记-清除”或是“标记-整理”算法,这两种算法一般比复制算法慢10倍以上
5.垃圾收集器
所谓垃圾收集器,其实就是垃圾搜集算法的具体实现。没有一个一劳永逸的垃圾搜集器,每种垃圾收集器都有其使用场景
-
Serial收集器:串行收集器
在垃圾搜集期间,只有一个线程去执行垃圾搜集工作,此时,其他所有线程都是停止工作(STW)
Serial收集器.png- 有点:单线程简单且高效,没有线程切换
- 缺点:所有线程STW
-
ParNews收集器:Serial的多线程版本,线程数一般与CPU核心数相等
ParNews收集器.png
网友评论