JVM的内存分配策略主要围绕对象在堆内存中的分配规则展开,核心目标是高效利用内存并减少垃圾回收开销。主要分配策略如下:
1. 优先在Eden区分配
大多数对象在新生代的Eden区中创建。当Eden区没有足够空间时,JVM会触发Minor GC(新生代GC)。
示例:
new Object() → 优先分配到Eden区
2. 大对象直接进入老年代
- 大对象(如长字符串、大数组)会直接分配到老年代,避免在新生代频繁GC时产生大量内存复制
- 通过
-XX:PretenureSizeThreshold
参数设置阈值(单位:字节),超过此值的对象直接进入老年代
示例参数:
-XX:PretenureSizeThreshold=1048576 // 1MB以上的对象直接进入老年代
3. 长期存活的对象进入老年代
- 每个对象有一个年龄计数器,在Eden区出生并经历一次Minor GC后存活,会被移动到Survivor区,年龄变为1
- 之后每在Survivor区经历一次Minor GC存活,年龄就+1
- 当年龄达到阈值(默认15)时,会被晋升到老年代
- 通过
-XX:MaxTenuringThreshold
参数调整晋升阈值
示例参数:
-XX:MaxTenuringThreshold=10 // 年龄达到10的对象进入老年代
4. 动态对象年龄判定
当Survivor区中相同年龄的所有对象大小总和大于Survivor区的一半时,年龄大于或等于该年龄的对象会直接进入老年代,无需等待达到MaxTenuringThreshold。
5. 空间分配担保
- 在Minor GC前,JVM会检查老年代最大可用连续空间是否大于新生代所有对象总空间
- 若满足,则Minor GC安全
- 若不满足,会检查是否允许担保失败(
-XX:+HandlePromotionFailure
) - 若允许,则判断老年代最大可用连续空间是否大于历次晋升到老年代的平均大小,若满足则尝试Minor GC,否则进行Full GC
6. 线程本地分配缓冲区(TLAB)
- JVM为每个线程在Eden区分配一块私有缓存区域(TLAB)
- 线程创建对象时优先在自己的TLAB中分配,减少并发分配的锁竞争
- 通过
-XX:+UseTLAB
开启(默认开启),-XX:TLABSize
设置大小
这些策略共同作用,实现了对象在JVM堆中的合理分配,平衡了内存利用率和GC效率。实际应用中,这些策略会根据JVM版本、垃圾收集器类型和具体参数配置有所调整。