java的自动内存管理,要实现的目标有两点:自动给对象分配内存和自动回收分配给对象的内存。内存分配主要指的就是在堆上分配,对象分配的规则取决与当前虚拟机的垃圾回收器和参数设定。
1、对象优先在Eden分配
大多数情况下, 对象在新生代Eden区中分配。 当Eden区没有足够空间进行分配时, 虚拟机将发起一次Minor GC。
2、大对象直接进入老年代
大对象指的是需要大量连续内存空间的java对象,比如长字符串,大数组。如果恰好这个大对象也是朝生夕灭的,对于程序内存分配非常不利。比如它会造成内存明明很大,但因为它提前出发垃圾回收,另外对它的复制也是一比很大的开销。所以虚拟机提供了-XX:PretenureSizeThreshold设置大于该值的对象要在老年代进行分配。
3、长期存活的对象将进入老年代
虚拟机会根据在对象头中存储的对象年龄计数器的大小决定在垃圾回收后仍然存活的对象应该放在哪个区域。对象在Eden区诞生,经过一次MinorGC后存活的话,如果它的大小能被survivor区容纳,就移动到survivor中,年龄加一,并在survivor中每经过一次MinorGC,就加一,
到达XX:MaxTenuringThreshold参数设置的大小后,移动到老年代。
4、动态对象年龄判定
虚拟机并不是永远要求对象的年龄必须达到-XX: MaxTenuringThreshold才能晋升老年代, 如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半, 年龄大于或等于该年龄的对象就可以直接进入老年代, 无须等到-XX:MaxTenuringThreshold中要求的年龄。
5、空间分配担保
新生代使用了复制收集算法,在一次MinorGC后,如果有大量对象存活,需要把Survivor无法容纳的对象直接送入老年代,但它的前提是保证老年代有那么大的空间。老年代的空间大小就承担的担保的职责。内存的分配担保, 如果另外一块Survivor空间没有足够空间存放上一次新生代收集下来的存活对象, 这些对象便将通过分配担保机制直接进入老年代, 这对虚拟机来说就是安全的。
所以在发生Minor GC之前, 虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间, 如果这个条件成立, 那这一次Minor GC可以确保是安全的。 如果不成立, 则虚拟机会先查看-XX: HandlePromotionFailure参数的设置值是否允许担保失败; 如果允许, 那会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小, 如果大于, 将尝试进行一次Minor GC, 尽管这次Minor GC是有风险的; 如果小于, 或者-XX:HandlePromotionFailure设置不允许冒险, 那这时就要改为进行一次Full GC。