深入理解java虚拟机之 内存分配和回收策略

上一篇我们学习了GC的 收集算法和七种收集器,今天我们来学习一下内存的分配和回收策略。
1.优先在Eden中分配
一般情况下 ,大多数的新对象都是在新生代中实例化的,当Eden区的空间不够的时候,发起Minor GC;
2.大对象直接进入老年代
-XX:PretenureSize Threshold参数,大于此值的对象直接在老年代分配,避免在Eden区和Survivor区之间的大量的内存复制。
3.长期存活的对象进入老年代。
JVM 为对象提供了年龄计数器,经过Minor GC之后仍然存活且被Survivor区容纳的,移动到Survivor区,年龄加一。每经历一次Minor GC不被清理则年龄加一,增加到一定年龄之后被移动到老年区(默认15岁,通过 -XX:MaxTenuringThreshold 设置)
4.动态对象年龄判定
若Survivor区中同年龄所有对象大小总和大于Survivor空间一半,则年龄大于等于该年龄的对象可以直接进入老年代;
5.空间分配担保
在发起MinorGC之前,JVM先 检查老年代最大可用连续空间是否大于新生代所有对象总空间,成立的话Minor GC确认是安全的;否则继续检查老年代的最大连续空间是否大于历次晋升到老年代对象的平均大小,大于的话进行Minor GC,否则进行Full GC;
6.Full GC的触发条件:
1).调用了System.gc(); 建议而非一定。很多情况下是触发FullGC,会增加间歇性停顿的次数,因此不建议使用此方法,让JVM自己去管理内存即可。我们可以通过-XX:+ DisableExplicitGC来禁止RMI远程方法调用(Remote Method Invocation)调用System.gc()。
2)老年代的空间不足;此场景的常见情况就是之前上文中提到的大对象以及长期存活的对象直接进入老年代等,当执行Full GC之后空间仍然不足,会抛出Java.lang.OutOfMemoryError:Java Heap Space。为了避免以上两种情况引起的Full GC,我们在调优的时候尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间或者不要创建过大的对象以及数组。
3).空间分配担保失败;使用复制算法的Minor GC需要老年代的内存空间作为担保。如果出现了HandlePromotionFailure担保失败,就会触发Full GC。
4)JDK1.7以及之前的永久代空间不足;在JDK 1.7及之前,HotSpot虚拟机中的方法区是通过永久代实现的,永久代中存放的是一些class的信息、常量、静态变量等数据,当系统要加载、反射的类或者调用的方法较多时,永久代可能会被占满,在未配置为采用CMS GC的情况下也会执行Full GC。如果Full GC仍然回收不了,那么JVM就会抛出如下错误信息:java.lang.OutOfMemoryError:PermGen space。为了避免PermGen占满造成Full GC现象,我们可以增大PermGen空间或转为使用CMS GC。(JDK1.8中用元空间替换了永久代作为方法区的实现,元空间是本地内存,因此减少了一种Full GC触发的可能性)
5)Concurrent Mode Failure
执行CMS GC的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是当前的浮动垃圾过多导致暂时性的空间不足触发Full GC)便会报Concurrent Mode Failure错误,并触发Full GC。

猜你喜欢

转载自blog.csdn.net/mulinsen77/article/details/84525180