Java堆空间原来长这样

50 阅读3分钟

1、什么是堆

  1. 一个JVM实例只存在一个堆内存,堆是Java内存管理的核心区域,是JVM管理的最大一块内存空间,另外堆内存的大小是可以调节的。
  2. 《JAVA虚拟机规范》规定,堆可以是不连续的物理内存空间,但在逻辑上它被视为连续的。对Java堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上。
  3. 所有的线程共享Java堆,在堆上还可以划分线程私有的缓存区(Thread Local Allocation Buffer, TLAB)
  4. 在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时间才会被移除。
  5. 堆是GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域。

堆空间分代示意图

堆空间分代示意图.png

2、对象分配内存

对象分配内存原则

  • 优先分配到Eden区

  • 大对象直接分配到老年代(开发过程中尽量避免出现过多的大对象)

  • 长期存活的对象分配到老年代

    年龄超过 MaxTenuringThreshold 值

  • 动态年龄判断

    如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无须等到MaxTenuringThreshold 中要求的年龄。

  • 空间分配担保

    -XX: HandlePromotionFailure

    空间分配担保是 JVM 在发起一次 Minor GC 之前,先确保老年代有足够空间来容纳可能从新生代晋升过来的所有存活对象的一种策略。它的核心目的是避免因老年代空间不足而导致 Minor GC 失败,从而减少不必要的、更耗时的 Full GC

    对象分配内存流程图

堆空间-对象分配内存.png

3、年轻代GC(Minor GC)触发机制

当Eden区空间不足时,就会触发Minor GC,Survivor区满了不会引发GC。Minor GC 会非常频繁,一般回收速度也比较快。

Minor GC 会引发STW,暂停其他用户线程,等待垃圾回收结束,用户线程才恢复运行。

4、老年代GC(Major GC / Full GC)触发机制

当老年代空间不足时,就会触发Major GC。当出现Major GC,经常会伴随至少一次Minor GC (但在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择)

Major GC的速度一般会比Minor GC 慢10倍以上,STW的时间更长。

如果Major GC后,内存还不足,就会报OOM。

5、常用的JVM参数

-XX:+PrintFlagsInitializing:查看所有的参数默认初始值

-XX:+PrintFlagsFinal:查看所有的参数的最终值

-Xms:初始堆空间内存(默认为物理内存的1/64)

Xmx:最大堆空间内存(默认为物理内存的1/4)

-Xmn:设置新生代大小

-XX:NewRatio=2:配置新生代与老年代在堆中的占比,默认2,1:2,新生代占1份,老年代占2份

-XX: SurvivorRatio=8: 设置Eden与Survivor大小,默认8,8:1:1

-XX: MaxTenuringThreshold=15:设置对象从年轻代到老年代经历的GC次数

-XX: HandlePromotionFailure:是否设置空间分配担保

-XX:+PrintGCDetails:输出详细的GC处理日志

-XX:PrintGC:打印简要信息

-server:启动Server模式

-XX:+DoEscapeAnalysis:启用逃逸分析,在Server模式下,才可以启用逃逸分析

-XX:+EliminateAllocations:开启标量替换(默认开启),允许将对象打散分配在栈上,比如对象拥有name和age字段,那么这两个字段将会被视为两个独立的局部变量进行分配。

更详细的参数可以查看官网文档

官方文档地址

6、常用的调优工具

JDK命令行

Jconsole

VisualVM

Jprofiler