如下内容为个人总结,如有错误,欢迎评论区指正;
-
对象是否可回收?
-
引用计数法
-
为对象添加一个引用计数器,每当有一个地方引用它时,计数器加1;当引用失效时,计数器减1;任何时刻计数器为0,表示该对象不可能在使用;
-
但是引用计数器存在一个问题:无法解决相互循环引用问题;
-
-
可达性分析法
-
对象与GC Roots之间创建一条链,只要对象没有与GC Roots连接,表示该对象可以被垃圾回收;
-
GC Roots包含如下几种:
-
虚拟机栈中引用的对象
-
方法区中类静态属性引用的对象
-
方法去中常量引用的对象
-
Native方法引用的对象
-
-
-
-
即使对象被认定为不可达,也不一定一定被回收
-
首先当对象与GC Roots间没有引用链,GC将对它进行第一次标记,并筛选是否执行finalize()方法,如果需要执行,则放入队列等待执行,执行完毕后如果对象拯救了自己,则继续存活;
-
-
回收方法区
-
对不使用的常量、类、方法、符号引用都可以回收,只是条件较为苛刻
-
例如类的回收需要满足如下三个条件:
-
该类所有实例都被回收
-
该类的类加载器被回收
-
该类的Class对象没有在任何地方被引用
-
-
-
-
-
垃圾回收算法
-
标记清除算法
-
分为标记-清除两步,该算法有两个缺点:一是效率问题,标记清除两步操作效率不高;二是空间问题,会产生大量碎片,当分配大对象时,可能由于空间不够,再次触发垃圾回收;
-
-
复制算法
-
将内存划分为两部分,每次只使用其中一部分,当一部分用完了,将存活的对象直接复制到另一部分,然后将原来的空间直接清除,避免了碎片,但是空间利用率比较低;
-
考虑到大部分对象生存时间较短,所以不需要按1:1来划分,可以分为Eden区和两个Survivor区,8:1:1,当Survivor不够时,老年代帮忙分担;
-
-
标记整理算法
-
如果对象存活时间较长,则使用复制算法效率较低,所以老年代一般不采用复制算法,而是采用标记整理算法;通过标记-移动-清除来避免碎片;
-
-
分代回收算法
-
将内存分为几个区,比如年轻代、老年代,根据不同的存活周期选用不同的回收算法
-
-
-
垃圾回收器
-
Serial收集器-应用于新生代
-
单线程回收器,单线程不止体现在是用一个cup内核或者一个工作线程回收垃圾,还体现在回收垃圾时,必须停掉其他啊线程,直到收集完成;
-
-
ParNew收集器-应用于新生代
-
Serial收集器的多线程版本,仍会产生STW;
-
-
Parallel Scavenge收集器-应用于新生代
-
吞吐量优先收集器,使用复制算法,通过设置两个参数,最大垃圾回收停顿时间和吞吐量大小来控制垃圾回收;
-
-
Serial Old收集器-应用于老年代
-
作为CMS收集器的备用方案,使用标记-整理算法;
-
-
Parallel Old收集器-应用于老年代
-
Parallel Scavenge收集器的老年代版本,使用标记-整理算法
-
-
CMS收集器-应用于老年代
-
STW时间短,基于标记-清除算法实现
-
该收集器过程分为四个阶段:
-
初始标记-STW
-
并发标记
-
重新标记-STW
-
并发清除
-
-
由于其特性,导致有如下三个问题:
-
收集器并发导致系统吞吐量下降
-
并发清除阶段仍会有垃圾产生,称为浮动垃圾,CMS虽然会在老年代存在预留空间时就开始工作,但仍会产生空间不够的情况,这是就会导致Concurrent Mode Failure,此时虚拟机将会使用Serial Old进行垃圾回收;
-
CMS基于标记-清除算法,所以会产生碎片,当产生大对象而无法分配空间时,则需进行一次Full GC。为了解决这个问题,虚拟机提供了一个参数控制,没进行多少次不压缩空间的Full GC后,进行一次空间压缩的Full GC;
-
-
-
G1垃圾回收
-
低停顿,将内存分为多个Region区,维护一个价值(即回收空间以及回收所需时间的经验值)列表,优先回收优先级较高的Region,
-
也是分为四个阶段
-
初始标记
-
并发标记
-
重新标记
-
筛选回收
-
-
-
ZGC
-
JDK11,低停顿垃圾回收器
-
-
-
内存分配和回收策略
-
对象优先在Eden分配
-
当Eden区没有足够的资源进行分配时,将进行一次Minor GC(复制算法)
-
-
大对象直接进入老年代
-
长期存活的对象进入老年代
-
动态年龄判断
-
如果Survivor区相同年龄大小的对象大于Survivor区的一半,则将大于等于该年龄的对象放到老年代
-
-
空间分配担保
-
Minor GC发生前,将会检查老年代最大可用连续空间是否大于新生代所有对象总空间,如果大于则代表此次Minor GC是安全的;如果不大于,则会判断HandlePromotionFailure是否允许担保失败。如果允许则检查老年代剩余连续空间是否大于每次晋升到老年代的对象平均大小,大于则尝试进行Minor GC;否则进行一次Full GC;
-
-
参考《深入理解Java虚拟机》