1. 运行时数据区

源自 JavaGuide

1.1 程序计数器

  • 解释器通过改变计数器的值来读取下一个需要执行的字节码指令。
  • 线程切换后恢复到正确的执行位置。

1.2 Java 虚拟机栈

每一次函数调用都有一个对应的栈帧压入虚拟机栈,栈帧包含:

  • 局部变量表
    存放方法参数和局部变量,在非 static 方法中,局部变量表的索引 0 处存放 this。
  • 操作数栈
    是栈帧中用来计算的一块区域。
  • 动态链接
    指向运行时常量池中该栈帧所属方法的引用,这个引用在运行期间转化为直接引用,来获取方法的元信息。
  • 方法出口
    方法执行结束后应该回到的位置地址。

1.3 本地方法栈

本地方法栈是为虚拟机执行 Native 方法,与虚拟机栈作用相似。

1.4 方法区

用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,从 jdk1.8 开始,方法区的实现从永久代变成了元空间。

  • 运行时常量池
    存放编译期生成的各种字面值和符号引用,从 jdk1.7 开始,字符串常量池移到了堆上。

1.5 堆

几乎所有的对象实例和数组都在堆上分配,从 jdk1.7 开始默认开启逃逸分析,若对象未在方法外使用,那么对象可以在栈上分配;为了更好地回收和分配内存,堆又划分为:

  • 新生代
    Eden 区:大部分情况下对象的出生地。
    Survivor 区:GC 回收后 Eden 区幸存的对象会进入这里,年龄 +1。
  • 老年代
    当对象的年龄达到一定的阈值(对象头中分配了4bit存储分代年龄,因此该值为16),就会进入该区域。

2. 对象的内存布局

侵删

  • 锁状态标记和 synchronized 锁升级有关
  • 开启指针压缩,Class Pointer 占 4 字节

3. 线上内存溢出如何排查?

  • 配置参数 -XX:+HeapDumpOnOutOfMemoryError,线上 OOM 时会产生堆内存快照,用可视化工具对快照分析
  • ......