JVM内存布局

总体内存概念布局

  1. 线程私有数据

    • 虚拟机栈

    记录Java方法调用顺序,每调用一个方法,就会向栈中压入一个栈帧,栈帧中记录着局部变量表、操作数栈、动态链接、方法出口等信息

    • 本地方法栈

    记录native方法调用顺序,每调用一个Native方法,就会向栈中压入一个栈帧,栈帧中记录着局部变量表、操作数栈、动态链接、方法出口等信息

    • 程序计数器

    记录当前线程字节码指令执行的行号

  2. 线程共享数据

    存储所有的类实例和数组,是JVM中占用内存最多的区域,垃圾回收主要也是在这个区域

    • 方法区

    存储Java的类元数据和运行时常量池,包括所有的类信息、常量、类变量、动态编译缓存等

总体内存布局

虚拟机栈

虚拟机栈就是一个非常关键的部分,看名字就知道它是一个栈结构,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(其实就是栈里面的一个元素),栈帧中包括了当前方法的一些信息,比如局部变量表、操作数栈、动态链接、方法出口等。

虚拟机栈

本地方法栈

与虚拟机栈结构类似

堆结构在JDK1.8前后有改变,JDK1.8前堆中的永久代是方法区的实现,之后方法区使用元空间实现,不在堆内存中,使用直接内存不属于JVM内存

堆内存被划分三部分新生代、老年代、永久代;JDK1.8及之后为两部分,只有新生代和老年代,永久代被元空间取代

元空间在直接内存中,不属于JVM的内存

在HotSpot虚拟机中,新生代被划分为三块,一块较大的Eden空间和两块较小的Survivor空间,默认比例为8:1:1,老年代的GC评率相对较低,永久代一般存放类信息等(其实就是方法区的实现)

所有新创建的对象,在一开始都会进入到新生代的Eden区(如果是大对象会被直接丢进老年代)

在一次垃圾回收之后,Eden区域没有被回收的对象,会进入到Survivor区。在一开始From和To都是空的,而GC之后,所有Eden区域存活的对象都会直接被放入到From区,最后From和To会发生一次交换,也就是说目前存放我们对象的From区,变为To区,而To区变为From区

在Eden区的存活对象复制到From区之后,所有To区域中的对象会进行年龄判定(每经历一轮GC年龄+1,如果对象的年龄大于默认值为15,那么会直接进入到老年代,否则移动到From区)

垃圾收集分为:

  • Minor GC - 次要垃圾回收,主要进行新生代区域的垃圾收集。
    • 触发条件:新生代的Eden区容量已满时。
  • Major GC - 主要垃圾回收,主要进行老年代的垃圾收集。
  • Full GC - 完全垃圾回收,对整个Java堆内存和方法区进行垃圾回收。
    • 触发条件1:每次晋升到老年代的对象平均大小大于老年代剩余空间
    • 触发条件2:Minor GC后存活的对象超过了老年代剩余空间
    • 触发条件3:永久代内存不足(JDK8之前)
    • 触发条件4:手动调用System.gc()方法

JDK1.8前

堆-JDK1.8前

JDK1.8

堆-JDK1.8

方法区

方法区也是整个Java应用程序共享的区域,它用于存储所有的类信息、常量、静态变量、动态编译缓存等数据,可以大致分为两个部分,一个是类信息表,一个是运行时常量池。

方法区

完整JVM内存布局

JDK1.8前

完整JVM内存布局-JDK1.8前

JDK1.8

完整JVM内存布局-JDK1.8及以后

参考资料

柏码知识库 | JVM 笔记(二)内存管理