《深刻理解Java虚拟机:JVM高级特性与最佳实践》-笔记 算法
在可达性分析期间整个系统看起来就像被冻结在某个时间点上,不能够出现分析过程当中对象引用关系还在不断变化的状况。 安全
一致性要求致使GC进行时必须停顿全部Java执行线程。(Stop The World)
即便在号称不会发生停顿的CMS收集器中,枚举根节点时也是必须停顿的。 数据结构
HotSpot使用的是准确式GC,当执行系统停顿下来后,并不须要一个不漏地检查完全部执行上下文和全局的引用位置,这是经过一组称为OopMap的数据结构来达到的。 多线程
在类加载完成后,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来;在JIT编译过程当中,也会在特定的位置记录下栈和寄存器中哪些位置是引用。
并发
程序只有在到达安全点时才能暂停。安全点的选定标准是“是否具备让程序长时间执行的特征”。“长时间执行”的最明显特征就是指令序列的复用,如方法调用、循环跳转等,具备这些功能的指令才会产生安全点。 spa
主动式中断(Voluntary Suspension):设一个标志,各个线程主动去轮询这个标志,遇到中断则暂停。轮询地方与安全点重合。 线程
指在一段代码片断中,引用关系不会发生变化。在这个区域的任意地方开始GC都是安全的。 对象
线程执行到安全区域中的代码时,首先标识本身进入了安全区域;在离开安全区域时,要检查系统是否已经完成了枚举根节点(或整个GC过程),完成了就继续执行,不然必须等待直到收到能够安全离开Safe Region的信号。 排序
安全区域是为了解决线程Sleep或Blocked状态的。 队列
前面的垃圾收集算法是理论,垃圾收集器则是具体的实现。
下图是HotSpot里的收集器,中间的横线表示分代,有连线表示能够组合使用。
是一个单线程的收集器,只能使用一个CPU或一条线程去完成垃圾收集;在进行垃圾收集时,必须暂停全部其余工做线程,直到收集完成。
Serial/Serial Old 收集器运行示意图:
缺点:Stop-The-World。
优点:简单。对于但CPU的状况,因为没有多线程交互开销,反而能够更高效。
是Client模式下默认的新生代收集器。
是Serial收集器的多线程版本。
ParNew/Serial Old 收集器运行示意图:
新生代收集器,使用复制算法、并行的多线程收集器。
Parallel Scavenge 收集器的目标是达到一个可控制的吞吐量(Throughput)。这里的吞吐量是指CPU用于运行用户代码的时间与CPU总消耗时间的比值。主要适合在后台运算而不须要太多交互的任务。
Parallel Scavenge 收集器容许采用GC自适应的调节策略,也就是让虚拟机根据收集到的运行时数据自行决定各个分代的大小等与垃圾回收有关的配置。
用于老年代的Serial收集器,单线程,使用“标记-整理”算法。
主要在Client模式下使用。
Parallel Scavenge的老年代版本,多线程,使用“标记-整理”算法。
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。基于“标记-清除”算法。
运做过程分为4个阶段:
其中标记和从新标记两个阶段仍然须要Stop-The-World,整个过程当中耗时最长的并发标记和并发清除过程当中收集器均可以和用户线程一块儿工做。
CMS收集器对CPU资源很是敏感。
浮动垃圾(Floating Garbage):因为CMS并发清理阶段用户线程还在运行着,天然会有新的垃圾产生,而这些垃圾是在标记过程以后,CMS只能在下次GC时回收它们,这些垃圾就称为浮动垃圾。
CMS收集器没法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而致使另外一次Full GC的产生。
在垃圾收集阶段用户线程还在运行,所以须要预留足够的内存给用户线程使用。若是预留内存不能知足用户线程,会出现“Concurrent Mode Failure”,这时虚拟机将启动临时后备预案:临时启用Serial Old收集器来从新进行老年代的垃圾收集。
因为CMS使用的是清除算法,会致使内存碎片问题,所以提供了参数用于控制是否在进行FullGC后进行内存整理,还提供了参数用于控制在多少次FullGC时才进行内存整理。内存整理是不能并发的,也就是要暂停全部用户线程。
G1(Garbage First):是一款面向服务端应用的垃圾收集器,用于替换CMS收集器。
G1将整个Java堆划分为大小相等的独立区域(Region);新生代和老年代再也不是物理隔离的,都由一组不连续的Region组成。
G1的特色:
Region之间的对象引用以及其余收集器中的新生代与老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描的。
G1中每一个Region都有一个与之对应的Remembered Set,虚拟机发现程序对引用类型的数据进行写操做时,会产生一个Write Barrier暂时中断写操做,检查引用的对象是否处于不一样的Region中(在其余收集器中就是检查是否老年代中的对象引用了新生代中对象),若是是, 经过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set中。进行内存回收时,在GC根节点的枚举范围中加入Remembered Set便可保证不对全堆扫描也不会有遗漏。
G1垃圾回收主要有4个阶段:
内存分配与回收规则由垃圾回收器和内存有关参数决定,不是固定的。
两个概念:
通常的规则:
对象优先在Eden分配。当Eden没有足够的空间时,虚拟机将发起一次MinorGC。
大对象直接进入老年代。大对象是指须要连续内存空间的Java对象。目的是避免在Eden区及两个Survivor区之间大量的内存复制(新生代采用复制算法收集内存)。
长期存活对象将进入老年代。虚拟机给每一个对象定义一个对象年龄计数器,若是对象在Eden出生并通过一次 Minor GC后仍然存活,而且可以被Survivor容纳,将被移动到Survivor空间,而且对象年龄设为1。对象在Survivor区中每“熬过”一次 MinorGC,年龄就加1,达到某个阀值就晋升到年老代。
空间分配担保。在发生Minor GC以前,虚拟机会先检查年老代最大可用的连续空间算法大于新生代全部对象总空间,若是是,那么Minor GC能够确保是安全的。若是否,虚拟机会查看HandlePromotionFailure设置是否容许担保失败。若是容许继续检查老年代最大可用的连续 空间是否大于历次晋升到老年代对象的平均大小,若是大于,将尝试进行一次Minor GC,这是有风险的(存活对象占用的内存大于平均大小,将致使HandlePromotionFailure失败,从新发起一次Full GC);若是小于或者HandlePromotionFailure设置不容许冒险,将改成Full GC。