大家好,这里是淇妙小屋,一个分享技术,分享生存的博主
以下是我的主页,各个主页同步更新优质博客,创作不易,还请大家点波关注
掘金主页
知乎主页
Segmentfault主页
开源中国主页
后续会公布更多MySQL,Redis,并发,JVM,分布式等面试热点常识,以及Java学习路线,面试重点,职业规划,面经等相干博客
转载请表明出处!
1. 如何判断对象已死
1.1 援用计数法
给对象增加一个援用计数器,每当有一个中央援用对象,计数器值+1,当援用生效,计数器值-1。当计数器为0时,示意对象已死,但会呈现以下问题
Obj1=null,Obj2=null后,因为两个对象之间依然互相援用,导致两个对象无奈被革除
1.2 可达性剖析
‘GC Roots’ 的对象作为起始点登程,通过援用链达到下一个对象
当一个对象到 GC Roots 没有任何援用链相连的时候阐明对象不可达
能够作为GC Roots对象的货色
- 栈帧中的局部变量表援用的对象
- 本地办法栈中JNI援用的对象
- 动态属性援用的对象
- 常量援用的对象
可达性剖析要求全过程都基于一个 能保障一致性的快照,在该快照中进行 对象图的遍历
- 在可达性剖析时 Stop The World,很容易的满足
但如果用户线程与可达性剖析并发,那么有两种解决方案
- 增量更新——CMS应用
- 原始快照——G1应用
2. 对象被回收的条件
- 可达性剖析,该对象没有与GC Roots相连接的援用链,将进行如下操作
- 如果对象还没有执行finalize()办法,就会被放入F-Queue中
- 当GC触发时,Finalizer线程会F-Queue中的对象的finalize()办法
执行完finalize()办法后,会再次判断对象是否可达,如果不可达,才会被回收
(所以对象能够通过在finalize()中将本人连贯上某个GC Root链的形式来援救本人)
3. 垃圾回收算法
标记-分明算法
将要被清革除对象进行标记,触发GC时,回收被标记的对象(也能够反过来标记存活的对象)
毛病
- 执行效率不稳固
- 非移动式,不须要挪动对象,但会造成内存空间的碎片化
标记-复制算法(JVM新生代应用)
将内存空间分为两块,每次只应用一块,当应用的内存块满了的时候,将该内存块中的存活对象复制到另一个内存块上,而后清空该内存块
- JVM新生代应用的复制算法
空间利用率低下,因为绝大多数新生代熬不过第一轮GC,所以没必要1:1划分内存空间
JVM新生代采纳的就是复制算法,不过新生代中,将内存空间划分为Eden+Survivor1+Survivor2(8:1:1)三块内存空间
每次只会应用Eden和一块Survivor,
当Eden空间有余时,触发GC,将Eden和应用的Survivor中的存活对象复制到另一块Survivor上(如果存活的对象Survivor装不下,那么多进去的对象进入老年代),
而后清空Eden和应用的Survivor 毛病
- 对象存活率较高时,须要进行较多的复制,效率升高——不适用于老年代
- 空间利用率低
- JVM新生代应用的复制算法
标记-整顿算法(JVM老年代应用)
将存活的对象挪动到内存的一端,而后将剩下的局部革除
毛病
- 标记-整顿算法是移动式的,须要挪动存活的对象,挪动存活对象时必须全程暂停用户利用线程(Stop The World)
分代算法(JVM采纳的)
JVM将内存分代,不同的代采纳不同的垃圾回收算法
- 新生代——每次GC都有大量对象死去,采纳下面的复制算法
- 老年代——对象存活率高,采纳下面的标记-整顿算法
永恒代(办法区就是永恒代,jdk1.8破除了永恒代)
永恒代要回收的——废除的常量和不再应用的类(Class对象)- 判断废除常量
个别是判断没有该常量的援用。 判断不再应用的类,必须以下3个条件都满足
- 该类的所有实例都已被回收
- 加载该类的ClassLoader已被回收
- 该类的Class对象没有被援用
- 判断废除常量
4. 援用的类型
强援用
相似于 Object obj = new Object(); 创立的
强援用不置为null的话,其指向的对象不会被回收软援用
SoftReference 类实现软援用,软援用指向的对象,在内存不足时会被回收
弱援用
WeakReference 类实现弱援用,弱援用指向的对象,只有触发GC就会被回收
虚援用
PhantomReference 类实现虚援用。
无奈通过虚援用获取一个对象的实例,
为一个对象设置虚援用关联的惟一目标就是能在这个对象被收集器回收时收到一个零碎告诉。
5. 垃圾回收器
5.1 新生代垃圾回收器
5.1.1 Serial
- 单线程收集器,只会应用一个GC线程来进行实现垃圾收集工作
并且在进行垃圾回收时必须 Stop The World - 应用标记-复制算法
- 应用场景
Client模式下的虚拟机
5.1.2 ParNew
- Serial的多线程版本,应用多个GC线程来实现垃圾收集工作 ,在垃圾回收时,会 Stop The World
- 应用标记-复制算法
应用场景
- 工作在Server模式
- 只有ParNew能与CMS配合工作
5.1.3 Parallel Scavenge
- 并行的多线程垃圾处理器,会触发 Stop The World
- 应用标记-复制算法
- 与ParNew相似,不同在于parallel Scavenge能够采纳GC自适应策略
- 该收集器的指标是达到一个可管制的吞吐量,吞吐量=运行用户代码工夫/(运行用户代码工夫+垃圾收集工夫)
Parallel Scavenge收集器应用2个参数管制吞吐量
- XX:MaxGCPauseMillis :管制最大的垃圾收集进展工夫
- XX:GCRatio:间接设置吞吐量大小
- Parallel Scavenge还提供第三个参数—— -XX:UseAdaptiveSizePolicy,开启GC自适应调节策略
开启这个参数后,不须要手工指定新生代大小,eden和survivor的比例等细节,只须要设置好堆大小,最大垃圾收集工夫玉吞吐量大小,虚构机会依据零碎运行状况,动静调整这些参数
5.2 老年代垃圾回收期
5.2.1 Serial Old
- 单线程垃圾收集器,会导致 Stop The World
- 采纳标记-整顿算法
5.2.2 Parallel Old
- Parallel Scavenge的老年代版本
- 采纳标记-整顿算法
5.2.3 CMS
- Concurrent Mark Sweep
- 应用标记-革除算法
工作流程:
- ①初始标记:(导致Stop The World)标记下GC Roots间接关联到的对象,速度很快
- ②并发标记:(不会导致Stop The World,与用户线程并发)从GC Roots间接关联的对象登程,进行可达性剖析(应用增量更新算法)开始遍历整个对象图,耗时长
- ③从新标记:(导致Stop The World)并发标记期间,用户程序持续运作,可能会导致局部对象的标记变动,从新标记就是为了修改这些对象的标记记录
- ④并发革除:(不会导致Stop The World)革除掉标记为曾经死亡的对象,因为不会挪动存活对象,所以用户线程不用暂停
毛病
对处理器资源非常敏感,会占用一部分处理器资源而导致应用程序变慢
CMS默认启动的回收线程数=(处理器外围数量+3)/4
Concurrent Mode Failure问题——因为CMS进行GC时,大多数时候用户线程扔持续运行,就必须在老年代预留足够的内存空间给用户线程应用,所以CMS不是等老年代内存空间没了才开始GC,而是当老年代内存空间应用了肯定比例后开始GC,这种会呈现一种状况,当CMS开始GC时,预留的内存比拟少,但在CMS执行GC的过程中,用户线程继续执行,耗尽了预留的内存,就会呈现 并发失败(Concurrent Mode Failure),这时JVM会长期启动Serial Old收集器进行老年代的垃圾收集,会 Stop The World
- 解决方案——通过两个参数来设置让老年代内存空间应用超过肯定比例就开始GC
- Promotion Failed——在进行Minor GC时,Survivor空间有余,对象只能放入老年代,而此时老年代也放不下造成的,少数是因为老年代有足够的闲暇空间,然而因为碎片较多,新生代要转移到老年带的对象比拟大,找不到一段间断区域寄存这个对象导致的
CMS采纳 标记-革除算法,会产生大量空间碎片
- 解决方案——设置 -XX:CMSFullGCsBeforeCompaction=n,上一次CMS GC后,要执行n次Full GC后对内存进行压缩
5.3 Garbage First(G1)
5.3.1 G1特点
- 回收的范畴是整个Java堆
G1的Region
- G1基于 Region的堆内存布局,将堆划分为多个大小雷同的Region(默认是2048个)
- 每个Region都能够依据须要,表演4个角色——新生代的Eden,Survivor,老年代,Humongous,G1收集器对表演不同角色的Region采纳不同的策略去解决
每个Region都能够划分成2个局部——已调配的和未调配的,它们之间的界线为top
将一个对象调配到Region,只须要减少top值
- Region是单次回收的最小单元,每次收集到的内存空间都是Region大小的整数倍
对于超过半个Region容量的大对象,会寄存在N个间断的Humongous Region中
从整体上看采纳的是标记-整顿算法,从部分(2个Region)上看采纳的是标记-复制算法
不会产生内存碎片
- 在Stop The World根底上建设了可预测的进展工夫模型,用户能够指定冀望进展工夫,G1会将进展工夫管制在用户设定的进展工夫内(单次STW默认最多200ms)
- 其余垃圾垃圾收集器触发GC时,指标是对负责的区域进行全量回收,然而G1进行垃圾回收时,只谋求在限度的工夫内回收尽可能多的垃圾(STW工夫不会太长)
5.3.2 G1解决思路
- 让G1依据各个region回收所取得的空间大小以及回收所需工夫,保护一个优先级列表,每次依据用户设定容许的收集进展工夫,优先解决回收收益最大的Region
- 依据用户冀望的GC进展工夫来回收(能够通过参数设置,G1会在规定的工夫内尽可能地回收垃圾)
- Minor GC——如果eden和survivor占用的内存超过了整个堆的60%,触发一次Minor GC,只对eden和survivor进行回收
- Full GC——如果老年代超过堆的45%,进行一次FullGC,对新生代和老年代进行回收
5.3.3 G1垃圾回收过程
初始标记
标记下GC Roots能间接关联到的对象,G1会应用SATB记录存活对象的快照
并发标记
与用户线程并发,从 GC Roots开始进行 可达性剖析,找出存活对象
在此期间,用户线程可能批改了本来的援用,所以须要查看存活的对象与其对照是否统一,如果不统一对象图扫描后,要重新处理SATB记录下的在并发时有援用变动的对象
最终标记
Stop The World,解决那些在并发标记阶段发生变化的对象
筛选回收
Stop The World,多条收集器线程并行实现
更新Region的统计数据,对各个Region的回收价值和老本进行排序,依据用户所冀望的进展工夫来制订回收打算,能够自由选择任意多个Region 形成回收集,而后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧 Region的全副空间