JVM自动内存管理机制

Jvm的自动内存管理机制,主要包括内存回收和内存分配。java

内存回收算法

对于内存回收机制主要围绕“哪些内存须要回收?”、“何时回收?”、“如何回收?”这三个问题来展开。安全

1、哪些内存须要回收?

不可能再被任何途径使用的对象须要被回收。多线程

如何判断一个对象是否还能够再被引用?并发

这里有两个方法能够作到:(1 引用计数法  (2 可达性分析布局

引用计数法:给对象维护一个计数器,每次被引用计数器的值+1,每次引用被释放,计数器的值-1,当计数器的值为0时,认为它不可能再被引用了。spa

可达性分析:从GCRoots向下搜索,走过的路径为引用链,当一个对象到GCRoots没有任何引用链相连则证实对象不可用。线程

因为引用计数法很难解决对象循环引用的场景,所以通常都使用可达性分析来判断一个对象是否存活。对象

 

2、何时回收?

2.1    新生代的回收时机

新的对象须要在Eden区申请内存,但Eden区没有足够的连续的空间分配给对象会触发一次minor GC内存

2.2    老年代的回收时机

重新生代过来的对象须要在老年代申请空间,但老年代没有足够的连续的空间来分配,会触发一次major GC。

HotSpot VM 老年代给新生代作空间担保时,若老年代连续可用空间小于历次晋升到老年代对象的平均大小,触发一次major GC。

 

3、如何回收?

3.1    回收算法

  标记-清除(Mark-Sweep):标记存活的对象,把剩下的对象清除掉。问题:会产生大量的空间碎片。

  复制(Copying):把内存分为两块相同大小的空间,一块使用称为From,一块保留称为To,回收时把From里面存活的对象复制到To中,复制完成后清除To中的所有内容。解决了空间碎片的问题,但空间利                                  用率过低。HotSpot利用新生代对象存活时间短,一次GC大部分对象会被回收,小部分对象须要复制这一特色,将新生代分为一块Eden区和两块大小相同的Survior区,每次使用                                            Eden+Survivor空闲一块Survivor,经过参数SuriviorRatio来控制Eden区和Survior的比例从而提高空间利用率。

  标记-整理(Mark-Compact):标记存活的对象,并让它们向一端移动,清除移动边界之外的对象。能够解决空间碎片的问题。

3.2 垃圾回收器

HotSpot虚拟机的垃圾回收器:Serial、ParNew、Parallel Scavenge、CMS、Serial Old、Parallel Old、G1。

 

Young Generation

 

Serial收集器:单线程,收集时暂停全部工做线程,直到它收集完成。

ParNew收集器:多线程,使用多条线程进行垃圾回收,一样须要暂停全部的工做线程

Parallel Scavenge收集器:多线程,和ParNew的区别在于,这个收集器关注的是吞吐量

吞吐量 = 程序执行时间/程序执行时间+垃圾回收时间,这个收集器提供能够精准控制吞吐量的参数:

-XX:MaxGCPauseMillis最大垃圾回收停顿时间

-XX:GCTimeRatio吞吐量大小

-XX:+UseAdaptiveSizePolicy开启自适应调节策略,自动调节各个参数来提供最适合的停顿时间或最大的吞吐量。

 

 

Young Generation通常使用的是复制算法。

Serial、ParNew能够和CMS搭配使用,但Parallel Scavenge不行。

 

Tenured Generation

 

Serial Old收集器:Serial收集器的老年代版本,单线程,使用标记整理算法,能够和Parallel Scavenge搭配使用,也能够做为CMS的后备方案

Parallel Old收集器:Parallel Scavenge的老年代版本,多线程,使用标记整理算法, 能够和Parallel Scavenge搭配使用实现真正的吞吐量优先GC。

CMS(Concurrent Mark Sweep)收集器:并发收集,以获取最短的回收停顿时间为目标的收集器,使用标记清除算法(为何不用标记-整理算法,整理时须要控制并发,停掉工做线程,会延长stop-the-world的时间,和获取最短的回收停顿时间为目标的理念相冲突)。

CMS回收的过程:1、初始标记 2、并发标记 3、从新标记 4、并发清除

初始标记:stop-the-world标记GCRoots能够直接关联的对象

并发标记:完成余下的GCRoots Tracing标记(用户线程和GC线程并发执行)

从新标记:stop-the-world修正并发标记期间因用户程序继续运做而致使标记产生变更的那一部分对象的标记记录

并发清除:执行清除操做,和用户线程并发执行

 

CMS回收器的缺陷:

1、对cpu的资源敏感,因为GC过程有两个阶段是并发进行的,会抢占cpu资源,下降系统的吞吐量。

2、浮动垃圾,在并发清除阶段,也会产生不可达对象,这些对象只能等到下次GC时才能被回收。因为浮动垃圾的存在,CMS不能等到内存被填满才进行垃圾回收,必须预留足够的空间,经过-XX:CMSInitiatingOccupancyFraction参数设置触发CMS回收的百分比阈值。当CMS GC期间预留的内存没法知足程序需求时会出现Concurrent Mode Failure 失败,会临时启用后备方案使用Seria Old进行老年代GC,会出现较长的stop-the-world停顿。

3、标记-清除会产生大量的空间碎片,若老年代还有不少空间,但没法找到足够大的连续空间来分配当前对象会提早出发full GC

–XX:+UseCMSCompactAtFullCollection参数设置在CMS要进行Full GC是开启内存碎片的合并整理过程,整理过程不能并发,须要stop-the-world

-XX:CMSFullGCsBeforeCompaction = ?,设置执行多少次不压缩的Full GC后接着来一次带压缩的Full GC

 

G1收集器:并发收集器,追求更小的stop-the-world时间。

G1收集器的特色:

1)javad堆的内存布局.将整个java堆分为多个大小相等的独立区域(Region),虽然还保留了新生代和老年代的概念,但新生代和老年代不将再是物理隔离的,它们都是部分Region的集合.

2)分代收集,G1收集器能够独自管理整个GC堆,而且能够根据对象的存活时间采用不一样的方式收集。

3)空间整合,G1收集器从总体来看是基于标记-整理算法来实现,局部来看(两个Region之间)是基于复制算法来实现的,这就意味着G1收集器能够规避掉CMS算法的空间碎片的问题

4)可预测的停顿。G1相对于CMS收集器的另外一个优点是能够创建一个可预测的停顿时间模型。经过-XX:MaxGCPauseMillis=? 来设置最大GC时间。

5)化整为零的GC思想.G1会跟踪各个Region里面的垃圾堆积的价值大小(回收空间、回收时间)并在后台维护一个优先级列表,优先回收优先级高的Region,而不是毫无计划的在堆上进行全区域垃圾回收,这样能够保证G1收集器能够在有限的时间能够得到更可能高的收集效率。

G1收集器在进行可达性分析时为了不全堆扫描,在Region中会记录一个Remember Set来保存哪些对象引用过这个Region

新生代收集:stop-the-world,Eden和Survivor区存活的对象被复制到另外一块Survivor区,若是年龄达到阈值或Survivor区的空间不足会直接分配到老年代,并清空已经处理过的Eden和Survivor区。

老年代收集:初始标记、并发标记、最终标记、筛选回收(最后也会把存活对象复制到另外一个Region中,从而避免了空间碎片的问题)

 

为何选择Serial Old 做为CMS的后备方案而不选择多线程并行的Parallel Old,缘由在于Serial Old能够和年轻代的三种搭配使用,而Parallel Old只能和Parallel Scavenge搭配使用。

并行和并发:这里说的并行是多线程进行GC,但暂停工做线程,并发说的是GC和工做线程一块儿多线程执行,也就是说,串行和并行都须要stop-the-world,并发不须要stop-the-world。

 

内存分配

对象的内存分配主要在堆上进行,对于新对象主要分配在Eden区,少数状况也会直接分配到老年代。

1.1        对象优先在Eden去分配

 新对象进入Eden区,当Eden区容量不足时,进行一次minior GC。GC时,当Eden区+Survivor From区复制到Surivivor To区,Surivivor To区容量不足时,Eden区+Survivor From区的对象直接进入老年代。

1.2        大对象直接进入老年代

-XX:PretenureSizeThreshold参数设置对象大小阈值,大于这个值的对象直接进入老年代

1.3        长期存活的对象直接进入老年代

Survivor区中的对象每熬过一次minor GC年龄就增长1岁,-XX:MaxTenuringThreshold设置年龄阈值,达到阈值的对象直接进入老年代

1.4        年龄相同对象所占空间超过Survivor区的一半,则大于等于这个年龄的对象直接进入老年代

1.5        空间分配担保

当新生代采用Eden、Survivor式的复制算法时须要老年代对其进行内存担保(由于minior GC时,若是Survivor区的容量不足以接纳Survivor区+Eden区的对象,则他们将所有进入老年代)

Minior GC以前先检查是否能够确保这次GC的安全,

先检查老年代的最大可用连续空间是否大于新生代全部对象的总空间,若是成立则能够确保这次monior GC是安全的,若是不成立,检查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,若是大于尝试一次minior GC, 不然进行一次Full GC.

 

HotSpot VM经常使用参数表:

-XX:设置  -XX:+参数 (开启?功能)  -XX:-参数(关闭?功能)  -XX:参数 = ?(设置参数的值)

-Xms? 设置堆的初始大小
-Xmx? 设置堆的最大值
-Xmn? 设置新生代的大小

-Xss? 设置每一个线程的栈的大小
-XX:SurvivorRatio=? 设置Eden区和Survior区的大小比值-XX:CMSInitiatingOccupancyFraction=?设置CMS收集器在老年代空间阈值,达到这个阈值进行垃圾回收-XX:MaxTenuringThreshold=?晋升到老年代的对象年龄阈值