引:一直在说Java的垃圾回收,既然有回收,那么当然会有分配。
Java技术体系中所提倡的自动内存管理可以归结为自动化地解决了两个问题:
- 给对象分配内存
- 回收分配给对象的内存
之前垃圾回收已经讲了很多,接下来我们详细地来看看内存分配
内存分配总说
对象的内存分配,主要是在堆上分配,对象主要分配在新生代的Eden区上,如果启动了本地线程的分配缓冲,将按线程优先在TLAB上分配,少数情况下(大对象)也可能直接分配在老年代中,分配的规则不是百分百固定的,其细节取决于当前使用的是哪一种垃圾收集器组合,还有虚拟机中与内存相关的参数的设置。
对象优先在Eden分配
- 大多数情况,对象在新生代Eden区分配。当Eden区没有足够空间进行分配时,虚拟机会发起一次MinorGC。
- 当MinorGC发现复制的对象大于Survivor区时,会通过分配担保机制提前转移到老年代中。
大对象直接进入老年代
所谓大对象是指需要大量连续内存空间的Java对象,如数组。
虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配,避免了在Eden区及Survivor区之间发生大量的内存复制。(新生代采用复制算法收集内存)
长期存活的对象将进入老年代
虚拟机给每个对象定义了一个对象年龄计数器。如果对象在Eden出生并经过第一次MinorGC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设置为1.对象在Survivor中每度过一次MinorGC,年龄就增加一岁,当他的年龄达到一定程度(默认15岁),就将被晋升到老年代中。对象的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。
动态对象年龄判定
虚拟机不是永远要求对象的年龄必须达到MaxTenuringThreshold才晋升到老年代,如果Survivor空间中相同年龄所有对象大小总和大于Survivor空间的一半,年龄大于或等于改年龄的对象就可以直接进入老年代。
空间分配担保策略
虽然有空间分配担保,但是也需要考虑老年代能否装下由于担保转存的内存大小,所以这个空间分配担保也需要一定的策略。
- 在发生MinorGC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间
- 如果上面条件成立,那么MinorGC就是安全的,可以进行
- 如果上面条件不成立,虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。
- 如果允许担保失败,就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小
- 如果大于,将尝试进行一次MinorGC
- 如果小于,或者HandlePromotionFailure设置为不允许冒险,那这时需要进行一次FullGC
在jdk 6 update 24之后的规则是只要老年代的连续空间大于新生代对象的总大小或者历次晋升平均大小就会进行MinorGC,否则进行FullGC。
总结
通过上面我们可以知道JAVA虚拟机是怎么自动进行内存分配的,然后在进行GC之后,内存是怎么转换的。
参考
- 《深入理解JAVA虚拟机》