版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习!
Java中提倡的自动内存管理机制最终可以归结为自动化的解决两个问题:给对象分配内存和回收分配给对象的内存。在之前的博客中已经详细讲解了内存回收体系及原理,下面我们来探讨给对象分配内存那些事儿。
对象的内存分配,总体上讲就是在堆上分配,对象主要分配在新生代的Eden区,少数情况也会直接分配在老年代。分配的规则并不是百分百固定的,其细节取决于当前使用的是哪一种垃圾收集器组合,还有虚拟机中与内存相关的参数配置。
对象优先在Eden区分配
大多数情况下,对象在新生代Eden区分配。当Eden区没有足够空间进行分配时,虚拟机会触发一次Minor GC,Minor GC采用复制算法进行垃圾回收。
大对象直接进入老年代
所谓大对象,是指需要大量连续内存的Java对象,最典型的大对象就是那种很长的字符串以及数组。大对象对虚拟机内存分配来说就是坏消息,经常会出现大对象在内存还有不少空间时就提前触发一次垃圾回收以获取足够的连续内存空间来安置它们。
那么多“大”的对象算大对象呢?虚拟机提供了一个 -XX:PretenureSizeThreshold参数,大于这个参数值的对象直接进入老年代。这样做的目的是避免在Eden区以及两个Survivor区之间发生大量的对象复制(新生代采用复制算法回收内存)。
长期存活的对象进入老年代
由于虚拟机采用了分代收集算法来回收内存,那么内存收集时就必须识别哪些对象应该放在新生代,哪些对象应该放在老年代中。为了做到这一点,虚拟机给每个对象定义了一个年龄计数器,年龄值存放在在对象的header中。如果对象在Eden出生并经过第一次Minor GC后仍然存活下来,并且能被Survivor区容纳的话,将被移动到Survivor区中,并且将对象的年龄值设为1。对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认15岁),就会被移到老年代中。对象晋升老年代的年龄阀值可以通过参数 -XX:MaxTenuringThreshold设置。
顺便提一下,虚拟机也并不是永远要求对象的年龄必须达到了MaxTenuringThreshold参数值才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,那么年龄值大于或等于该年龄的所有对象就可以直接进入老年代,无须等到MaxTenuringThreashold参数要求的年龄。