Java内存分配规则

内存分配规则

对象的内存分配,就是在堆上分配。

对象主要分配在新生代的Eden区上,少数情况下可能直接分配在老年代。

分配规则不固定,取决于当前使用的垃圾回收器组合以及相关的参数配置。

对象优先在Eden分配

大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。

  • Minor GC 与 Major GC/Full GC 的异同:

Minor GC:回收新生代(包括Eden和Survivor区域),因为Java对象大多都具有朝生夕灭的特性,因此Minor GC非常频繁,一般回收速度也比较快。

Major GC/Full GC:

回收老年代。出现Major GC,经常会伴随至少一次的Minor GC,但并非绝对。

Major GC的速度一般会比Minor GC慢10倍以上。

在JVM规范中,Major GC和Full GC都没有正式的定义,也有人简单认为Major GC清理老年代,Full GC清理整个内存堆。

大对象直接进入老年代

大对象是指需要大量连续内存空间的Java对象,如很长的字符串或数据。

一个大对象能够存入Eden区的概率比较小,发生分配担保的概率比较大,而分配担保需要涉及大量的复制,就会造成效率低下。

虚拟机提供了一个 -XX:PretenureSizeThreshold 参数,令大于这个设置值的对象直接在老年代分配,这样做的目的是避免在Eden区及两个Survivor区之间发生大量的内存复制。

长期存活的对象将进入老年代

JVM给每个对象定义了一个对象年龄计数器。当新生代发生一次Minor GC后,存活下来的对象年龄+1,当年龄超过一定值时,就将超过该值的所有对象转移到老年代中区。

使用 -XXMaxTenuringThreshold 设置新生代的最大年龄,只要超过该参数的新生代对象都会被转移到老年代中去。

动态对象年龄判定

如果当前新生代的Survivor中,相同年龄所有对象大小的总和大于Survivor空间的一半,年龄>=该年龄的对象就可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。

空间分配担保

  • JDK6 Update 24之前

在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,Minor GC可以确保是安全的。

如果不成立,则虚拟机会查看 HandlePromotionFailure 值是否设置为允许担保失败,如果是,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次Minor GC,尽管这次Minor GC是有风险的。

如果小于,或者 HandlePromotionFailure 设置不允许冒险,那此时也要改为进行一次Full GC。

  • JDK6 Update 24之后

老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小,就会进行Minor GC,否则将进行Full GC。

通过清除老年代中的废弃数据来扩大老年代空闲空间,以便给新生代做担保。这个过程就是分配担保。

哪些情况可能会触发JVM进行Full GC

  • 老年代空间不足

老年代空间不足会触发 Full GC 操作,若进行该操作后空间依然不足,则会抛出如下错误:java.lang.OutOfMemoryError: Java heap space

  • 永久代空间不足(方法区)

JVM规范中运行时数据区域中的方法区,在Hotspot虚拟机中也被称为永久代(Permanet Generation),存放一些类信息、常量、静态变量等数据,当系统要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,会触发Full GC。

如果经过Full GC仍然回收不了,那么JVM会抛出如下错误:java.lang.OutOfMemoryError:PermGen space

  • System.gc() 方法调用

此方法的调用是建议JVM进行Full GC,这只是建议而非一定,在很多情况下它会触发Full GC,从而增加Full GC的频率。

通常情况下我们只需要让虚拟机自己管理内存即可,我们可以通过 -XX:+ DisableExplicitGC 来禁止调用 System.gc()。

  • Minor GC晋升到老年代的平均大小大于老年代的剩余空间
  • CMS GC 出现担保失败

CMS GC时出现 promotion failed 和 concurrent mode failure promotion failed,即担保失败,而 concurrent mode failure 是在执行 CMS GC的过程中同时有对象要放入老年代,而此时老年代空间不足造成的。

posted @ 2025-07-21 21:55  biby  阅读(14)  评论(0)    收藏  举报