目录
一、栈上分配
二、堆上分配
2.1对象优先在eden区分配
原理:
- 大多数情况下,对象会被分配到eden区,当eden区没有足够的空间进行分配时,jvm会发起一次minor gc。
TLAB
2.2大对象直接进入老年代
大对象:
- 需要大量连续内存空间的对象,最常见的大对象:很长的字符串、集合(数组)等。
-XX:PretenureSizeThreshold=
- 设置可以分配到新生代的对象的最大值,默认为0。
- 大于该值的对象直接进入老年代。
- 只对Serial收集器和ParNew收集器有效,对Parallel Scavenge收集器无效。
2.3长期存活的对象晋升到老年代
原理
- 如果对象在eden区出生并且经历过一次minor gc后仍然存活,则该对象会被移动到survivor区,并且将该对象的年龄(age)设为1。
- 在survivor区中,该对象每经历一次minor gc,其age就会增加1岁,当该对象的年龄增加到一定数量时(超过-XX:MaxTenuringThreshold设定的值),该对象就会晋升到老年代。
-XX:MaxTenuringThreshold=
- 设置新生代survivor区中对象晋升到老年代的阈值,最大值为15(jvm使用4个bit来表示对象的年龄)。
- Parallel Scavenge中默认值为15,G1中默认值为15。
- CMS中默认值为:
- 若没有手动设置-XX:SurvivorRatio= 则默认值为6
- 若手动设置-XX:SurvivorRatio= 则默认值为15
- 新生代survivor区中对象的年龄超过该值时会被晋升到老年代。
- 你假笨 - MaxTenuringThreshold、关于-XX:MaxTenuringThreshold参数
对象年龄动态计算
- 单纯设定固定的MaxTenuringThreshold 值作为晋升条件的带来问题:
- MaxTenuringThreshold 如果设置得过大,原本应该晋升的对象一直停留在 Survivor 区,直到 Survivor 区溢出,一旦溢出发生,Eden + Survivor 中对象将不再依据年龄全部提升到 Old 区,这样对象老化的机制就失效了。
- MaxTenuringThreshold 如果设置得过小,过早晋升即对象不能在 Young 区充分被回收,大量短期对象被晋升到 Old 区,Old 区空间迅速增长,引起频繁的 major gc,分代回收失去了意义,严重影响 GC 性能。
- 对象年龄动态计算:
- 同一应用在不同时间的表现不同,特殊任务的执行或者流量高低峰的变化,都会导致对象生命周期的分布发生变化。
- Hotspot使用动态计算的方式来调整晋升的阈值,以解决固定阈值过大或过小带来的问题。
- 每次gc中对象晋升年龄阈值计算规则:
- 假设-XX:MaxTenuringThreshold=max,survivor中 age<=n 的对象所占内存大于survivor期望使用的大小 并且 n小于max时,阈值为n,否则为max。
-XX:TargetSurvivorRatio
- 设定survivor区的目标使用率,默认50
- survivor期望使用的大小=TargetSurvivorRatio/100 * survivor大小
- 我们一般不会修改该值。
2.4内存分配担保机制
在新生代GC(Minor GC)前,虚拟机会先检查老年代中可用的最大连续空间是否大于新生代所有对象的总空间。
- 如果满足条件,那么就认为此次gc是安全的,可以进行gc。
- 如果不满足条件,则虚拟机会查看HandlePromotionFailure设置是否允许担保失败。
如果允许担保失败,那么会继续检查老年代中可用的最大连续空间是否大于历次晋升到老年代的对象的平均大小,
- 如果大于,将会进行一次新生代GC,尽管这次新生代GC是有风险的
- 如果小于,那么这时不会进行新生代GC,而是改为进行一次老年代GC(Full GC)
如果不允许担保失败,那么这时不会进行新生代GC,而是改为进行一次老年代GC(Full GC)
三、分配对象的方式:
指针碰撞(Bump The Pointer)
- 概念:堆内存被一个指针一分为二,指针的左边都被塞满了对象,指针的右变是未使用的区域。每一次有新的对象创建,指针就会向右移动一个对象大小的距离,这个过程被称为指针碰撞。
- 特点:适用于规整的堆内存,分配效率较高,
空闲列表(Free List)
- 概念:虚拟机维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的控件划分给对象实例,并更新列表上的记录。
- 特点:适用于堆内存不规整,用过的内存和空闲区相互交错,并且带来了额外的空间消耗。
分配方式的选择:
- 选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定:
- 对于Serial、ParNew等带Compact过程的垃圾收集器,系统采用的是指针碰撞算法。
- 对于CMS这种基于Mark-Sweep算法的收集器,通常采用空闲列表算法。