内存分配过程
一. 堆内存结构图
- Java 堆是垃圾收集器管理的主要区域,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。
- JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;
- JVM最大分配的内存由-Xmx指 定,默认是物理内存的1/4。
二. JVM内存分配过程
- 大多数情况下,对象在新生代中 Eden 区分配。
- 当Eden空间足够时,内存申请结束;否则到下一步。
- JVM 试图释放在Eden中所有不活跃的对象(虚拟机将发起一次 Minor GC)。释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区。
- Survivor区被用来作为Eden及Old的中间交换区域,当Old区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区。
- 当Old区空间不够时,JVM 会在Old区进行完全的垃圾收集(虚拟机将发起一次 Major GC )。
- 完全垃圾收集后,若Survivor及Old区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”out of memory”错误。
三. JVM晋升老年代机制
①. 动态对象年龄判定——长期存活的对象将进入老年代
- 对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。
年龄阈值设置
-
Hotspot 遍历所有对象时,按照年龄从小到大对其所占用的大小进行累积,当此年龄的所有对象之和超过了survivor 区的 50% 时(默认值是 50%,可以通过 -XX:TargetSurvivorRatio=percent 来设置 ),取这个年龄和 MaxTenuringThreshold 中更小的一个值,作为新的晋升年龄阈值。
-
默认晋升年龄并不都是 15,这个是要区分垃圾收集器的,CMS 就是 6.
③. 大对象直接进入老年代
- 大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。
- 为了避免为大对象分配内存时由于分配担保机制带来的复制而降低效率。
④. Minor GC后,Survivor仍然放不下
- Survivor区被用来作为Eden及Old的中间交换区域,当Old区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区。
四. GC准确分类
部分收集 (Partial GC):
- 新生代收集(Minor GC / Young GC):只对新生代进行垃圾收集;
- 老年代收集(Major GC / Old GC):只对老年代进行垃圾收集。需要注意的是 Major GC 在有的语境中也用于指代整堆收集;
- 混合收集(Mixed GC):对整个新生代和部分老年代进行垃圾收集。
整堆收集 (Full GC):收集整个 Java 堆和方法区。