Java体系的内存管理主要体现在两方面,一方面是给对象分配内存,另一方面则是回收分配给对象的内存;之前我们花了较大的篇幅介绍垃圾收集涉及到的算法,以及垃圾收集器,本文将同大家一起探讨内存的分配。
对象优先在Eden分配
大多数情况下,对象在新生代Eden分配,当Eden没有足够的内存空间时,将发生一次Minor GC。这里可能有些人还不明白Minor GC,Major GC ,Full GC 有什么区别。
Minor GC :指新生代GC,包括Eden和Survivor
Major GC : 指老年代GC
Full GC :指整个堆空间GC
大对象直接进入老年代
所谓的大对象就是指需要连续内存空间的Java对象,例如很长的字符串或者数组;大对象对虚拟机来说是一个坏消息,经常出现大对象导致虚拟机明明还有不少的内存空间时就提前触发垃圾收集已获得连续的内存空间来安置这些大对象。
虚拟机允许通过-XX:PretenureSizeThreShold ,使大小大于设定值的对象直接进入老年代。
长期存活的对象进入老年代
既然垃圾收集器采用分代收集的思想管理内存,那么它应该能够区别出那些对象应该放在新生代,那些对象应该放在老年代;为了实现分代,虚拟机给每个对象创建了一个年龄计数器,如果对象在Eden中创建,经过一次Minor GC 后,如果 Survivor 空间大小能够容纳该对象的话,该对象就被复制到Survivor中并将年龄设置为1,没经过一次Minor GC 对象年龄+1,直到年龄达到老年代的阈值,该对象就会被复制到老年代。
可以通过设定-XX:MaxTenuringThreShold 来设定晋升到老年代的阈值
动态对象年龄判断
为了更好的使用不同的程序,并不是所有的对象都需要达到老年代阈值以后才能进入;当Survivor中相同年龄对象的总和大于Survivor空间的一半,那么大于该年龄的对象就可以直接进入老年代,无需达到XX:MaxTenuringThreShold设定的阈值。
空间分配担保
当虚拟机进行Minor GC 之前,会检查老年代连续的内存空间是否大于新生代所有对象的总空间,如果大于Minor GC确保是安全的;如果小于,并且HandlePromotionFailure设置为允许空间分配担保时,老年代会检查剩余的连续内存空间大小是否大于晋升到老年代对象的平均大小,如果大于平均大小则进行Minor GC,尽管此次Minor GC是有风险的,如果小于平均大小,或者HandlePromotionFailure设置为不允许冒险,则改为进行一次Full GC;如果Minor GC 失败,那么虚拟机会重新发起一次Full GC,但这似乎绕了一个大圈子。
通常情况下为了避免频繁的Full GC,HandlePromotionFailure 都会设置为允许担保。
网友评论