JVM调优---垃圾收集器&内存分配策略

I  垃圾收集器


对象是死是活?

在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事就是确定对象中哪些还“存活”着,哪些已经“死去”!一般采用引用计数算法和可达性分析算法来进行判断。

引用计数算法:给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器值为0就代表此对象已死。引用计数算法的致命弊端是很难解决对象之间相互循环引用的问题,如下代码清单1所示。

可达性分析算法:通过·一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径为引用链,当一个对象的GC Roots没有任何引用链相联时,则此对象不可用,已死。如下图2所示。

JVM调优---垃圾收集器&内存分配策略_第1张图片

JVM调优---垃圾收集器&内存分配策略_第2张图片

GC Roots的对象包含下面几种:

  1. 虚拟机栈中引用的对象
  2. 方法区中类静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中Native方法引用的对象

Java中引用分为强引用、软引用、弱引用、虚引用(又称为幽灵引用或幻影引用)4种,引用强度依次递减,回收力度依次递增。

对象死亡的判定

对象死亡的判定:①发现与GC Roots断开的对象,然后审核是否有必要执行finalize()方法;②若被判定为要执行finalize()方法,就将对象放入F-Queue队列中;3 虚拟机自动创建FInalizer优先级线程来执行finalize()方法处理F-Queue队列;④GC将对F-Queue中的对象进行第二次小规模标记;5 如果对象在finalize()中未成功拯救自己,就基本认为被回收了。(注:对象在finalize()时,可以通过重新与引用链上的任何一个对象建立关联来拯救自己)整个过程如下图所示。

JVM调优---垃圾收集器&内存分配策略_第3张图片


垃圾收集算法

标记--清除算法

标记--清除算法:法分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。缺点:效率低、产生大量碎片空间。

JVM调优---垃圾收集器&内存分配策略_第4张图片

复制算法(解决效率问题)

复制算法:通过划分两个相等的两块,每次使用其中一块,以复制的方式将GC后的有效对象转到另一块中。提高了效率,但内存要求太高了。

JVM调优---垃圾收集器&内存分配策略_第5张图片

标记--整理算法(针对老年代)

标记--整理算法:与·标记--清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

JVM调优---垃圾收集器&内存分配策略_第6张图片

分代收集算法

分代收集算法:根据对象存活周期的不同将内存划分为几块,一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。


HotSpot算法实现

枚举根节点

枚举根节点:可达性分析对执行时间的敏感还体现在GC停顿上,Sun将这件事情称为“Stop The World”。HotSpot中利用OopMap的数据结构来把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中,也会在特定的位置记录下栈和寄存器中那些位置是引用。

安全点

安全点:在OopMap的协助下,程序执行时在到达安全点时才能暂停。Safepoint的选定基本上是程序“是否具有让程序长时间执行的特征”为标准进行选定的。“长时间执行”的最明显的特征是指令序列复用,例如:方法调用、循环跳转、异常跳转等,所以具有这些功能的指令才会产生Safepoint。对于Safepoint,另一个需要考虑的问题时如何在GC发生时让所有线程(这里不包括执 ——>见下图:

JVM调优---垃圾收集器&内存分配策略_第7张图片 

安全区域

安全区域:当线程处于Sleep转台或者Blocked状态时,线程就无法响应JVM中断请求,对于这种情况采用安全区域(Safe Region)来解决。——>见下图:

 

JVM调优---垃圾收集器&内存分配策略_第8张图片 


垃圾收集器

JVM调优---垃圾收集器&内存分配策略_第9张图片

Serial收集器

Serial收集器:是单线程收集器,必须暂停其他所有的工作线程,直到它收集结束。

JVM调优---垃圾收集器&内存分配策略_第10张图片

ParNew收集器

ParNew收集器:是Serial收集器的多线程版本,能与CMS配合。

JVM调优---垃圾收集器&内存分配策略_第11张图片

Parallel Scavenge收集器

Parallel Scavenge收集器:使用复制算法的并行多线程收集器,强调吞吐量,要求高效的利用CPU。使用Parallel Scavenge收集器配合自适应调节策略,把内存管理的调优任务交给虚拟机去完成。

Serial Old收集器

Serial Old收集器:是Serial收集器的老年代版本,同样是一个单线程收集器。

JVM调优---垃圾收集器&内存分配策略_第12张图片

Parallel Old收集器

Parallel Old收集器:是Parallel Scavenge收集器的老年代版本,使用多线程和标记--整理算法。它能与Parallel Scavenge配合,实现“吞吐量优先”。

JVM调优---垃圾收集器&内存分配策略_第13张图片

CMS收集器

CMS收集器:以获取最短停顿时间为目标的收集器,基于标记--清除算法实现的,整个过程分为4个步骤:初始标记、并发标记、重新标记、并发清除。CMS收集器具有3个明显的缺点:对CPU资源非常敏感、无法处理浮动垃圾、会产生大量空间碎片。

JVM调优---垃圾收集器&内存分配策略_第14张图片

G1收集器

G1收集器:面向服务端应用的垃圾收集器,是当今收集器计数发展的最前沿成果之一。具有并行与并发、分代收集器、空间整理、可预测的停顿的特点;G1收集器的运作大致可划分4个步骤:初始标记、并发标记、最终标记、筛选回收。

JVM调优---垃圾收集器&内存分配策略_第15张图片


看懂GC日志(直接看图说话)

JVM调优---垃圾收集器&内存分配策略_第16张图片

JVM调优---垃圾收集器&内存分配策略_第17张图片


II  内存分配&回收策略

内存分配&回收策略流程图如下:

注:

  1. 新生代GC(Minor GC):指发生在新生代的垃圾收集动作,因为Java对象大都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度较快。
  2. 老年代GC(Major GC / Full GC):指发生在老年代的GC,出现了Major GC,经常会伴随至少一次的Minor GC,Major GC的速度一般会比Minor GC慢10倍以上。

 

JVM调优---垃圾收集器&内存分配策略_第18张图片

说明:虚拟机中利用对象年龄计数器,对象从Eden出生经过第一次Minor GC后仍然存活并且能被Survivor区容纳的话,被移动到Survivor空间中,在设定的年龄阈值内可以多次Minor GC,当达到年龄阈值就进入老年代空间中。


虚拟机提供多种垃圾回收策略为了就是适应不同场景,我们需要根据实际应用需求、实现方式选取最优的收集方式,调配最合适的参数设置,才能使虚拟机发挥最大的作用。

                                                                                       谢谢阅读             ---知飞翀

你可能感兴趣的:(JVM)