JVM内存分配与回收策略

了解内存分配与垃圾回收之前先来看图:
JVM内存分配与回收策略将GC区分为年轻代,老年代,其次是方法区或者元空间为永久代.
年轻代又分为伊甸区,和survivor0和survivor1.

内存分配与回收

1. 对象优先在Eden分配
  • 大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够的空间进行分配时,虚拟机将发生一 次Minor GC.
2.大对象直接进入老年代
  • 所谓的大对象是指,需要大量连续空间的Java对象,典型的大对象就是那种很长的字符串以及数组。
  • 大对象对虚拟机的内存分配是一个坏消息,经常出现大对象容易导致内存还有不少空间时就提前触发GC以获取足够的连续空间来放置大对象。
3.长期存活的对象将进入老年代
  • 既然虚拟机采用分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应该放在新生代,哪些对象应该放在老年代中。
  • 为了做到这点,虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经过 一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且把对 象年龄设为1。对象在Survivor空间中每"熬过"一次Minor GC,年龄就增加1岁,当它的年龄增加 到一定程度(默认为15岁),就将晋 升到老年代中。
4.动态对象年龄判定
  • 为了能更好的适应不同程序的内存状况,JVM并不是永远要求对象的年龄必须达到 MaxTenuringThreshold才能晋升老年代。
  • 如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
5.空间分配担保
  • 发生Minor GC时,是使用复制算法将Eden区和Survivor区存活对象复制到另一个Survivor区: Survivor区只占新生代10%空间,我们没有办法保证每次回收都只有不多于10%的对象存 活,当Survivor空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)。
  • 内存的分配担保就好比我们去银行借款,如果我们信誉很好,在98%的情况下都能按时偿还,于是银行可能会默认我们下一次也能按时按量地偿还贷款,只需要有一个担保人能保证 如果我不能还款时,可以从他的账户扣钱,那银行就认为没有风险了。 内存的分配担保也一样,如果另外一块Survivor空间没有足够空间存放上一次新生代收集下 来的存活对象时,这些对象将直接通过分配担保机制进入老年代。
  • 在发生Minor GC之前,虚拟机会检查老年代大可用的连续空间是否大于新生代所有对象的总空 间。

1.如果大于,则此次Minor GC是安全的;如果小于,则虚拟机会查看 HandlePromotionFailure设置值是否允许担保失败。

2.如果HandlePromotionFailure=true,那么会继续检查老年代大可用连续空间是否大于历 次晋升到老年代的对象的平均大小,如果大于,则尝试进行一次Minor GC,但这次Minor GC依然是有风险的,如果小于或者HandlePromotionFailure=false,则改为进行一次Full GC。

3.上面提到了Minor GC依然会有风险,是因为新生代采用复制收集算法,假如大量对象在 Minor GC后仍然存活(极端情况为内存回收后新生代中所有对象均存活),而Survivor空 间是比较小的,这时就需要老年代进行分配担保,把Survivor无法容纳的对象放到老年代。 老年代要进行空间分配担保,前提是老年代得有足够空间来容纳这些对象,但一共有多少对 象在内存回收后存活下来是不可预知的,因此只好取之前每次垃圾回收后晋升到老年代的对 象大小的平均值作为参考。使用这个平均值与老年代剩余空间进行比较,来决定是否进行Full GC来让老年代腾出更多空间。取平均值仍然是一种概率性的事件,如果某次Minor GC后存 活对象陡增,远高于平均值的话,必然导致担保失败,如果出现了分配担保失败,就只能在 失败后重新发起一次Full GC。虽然存在发生这种情况的概率,但大部分时候都是能够成功分 配担保的,这样就避免了过于频繁执行Full GC .

垃圾回收过程:
Eden空间不足,触发Minor GC: 用户线程创建的对象优先分配在Eden区,当Eden区空间不够时,会触发Minor GC:将Eden和Survivor中还存活的对象一次 性复制到另一块Survivor空间上,后清理掉Eden和刚才用过的Survivor空间.
JVM内存分配与回收策略
紫色为空闲对象,绿色为存活对象

垃圾回收结束后,用户线程又开始新创建对象并分配在Eden区,当Eden区空间不足时,重复上次 的步骤进行Minor GC.
如果还是不足,年老对象晋升到老年代:
Survivor空间不足,存活对象通过分配担保机制进入老年代
老年代空间不足,触发Major GC当老年代空间不足,还需进行老年代的垃圾回收.

总结:

JVM内存分配与回收策略

JVM详细参数:

-Xmssize 设置堆的初始化大小。如设置堆空间初始化大小为6m:-Xms6291456 或 -Xms6144k 或 -Xms6m
-Xmxsize 设置堆的大值。如设置堆空间的大值为80m:-Xmx83886080 或 -Xmx81920k 或 -Xmx80m
-Xmnsize 设置年轻代的大小(初始化及大值)。如设置256m大小的年轻代:-Xmn256m 或 -Xmn262144k 或 -Xmn268435456
-XX:NewSize 设置年轻代的初始化大小。
-XX:MaxNewSize 设置年轻代的大值。
-XX:PermSize=size 设置永久代的大小(jdk1.7方法区)
-XX:MaxPermSize=size 设置永久代的大值(jdk1.7方法区)
-XX:MetaspaceSize=size 设置永久代的大小(jdk1.8元空间)
-XX:MaxMetaspaceSize=size 设置永久代的大值(jdk1.8元空间)
-XX:SurvivorRatio=ratio 设置Eden区与Survivor区的空间比例,默认为8,即Eden=8,Survivor From=1,Survivor To=1。
-XX:MaxTenuringThreshold=threshold 对象晋升到老年代的年龄阈值
-XX:PretenureSizeThreshold=size 在老年代分配大对象的阈值,单位只能使用byte,如3m,只能写为3145728
-XX:+PrintGCDetails 打印gc详细信息