JVM(Java虚拟机)内存模型定义了Java程序在运行时的内存结构和行为。它不仅是Java程序运行的基础,也是理解垃圾回收、多线程和性能调优的关键。

JVM内存模型的核心组成部分

方法区(Method Area)

  • 作用:存储类的元数据(如类名、方法信息、字段信息、常量池等);

  • 特点

    • 所有线程共享;

    • 在JDK 8之前,方法区通过永久代(PermGen)实现;在JDK 8及之后,方法区被元空间(Metaspace)取代。

  • 相关参数

    • -XX:MetaspaceSize:初始元空间大小;

    • -XX:MaxMetaspaceSize:最大元空间大小。

堆(Heap)

  • 作用:存储对象实例和数组;

  • 特点

    • 所有线程共享;

    • 是垃圾回收的主要区域;

    • 分为新生代(Young Generation)和老年代(Old Generation):

      • 新生代:存放新创建的对象,分为Eden区、Survivor区(From和To);

      • 老年代:存放长期存活的对象。

  • 相关参数

    • -Xms:初始堆大小;

    • -Xmx:最大堆大小;

    • -XX:NewRatio:新生代与老年代的比例;

    • -XX:SurvivorRatio:Eden区与Survivor区的比例。

栈(Stack)

  • 作用:存储方法的局部变量、操作数栈、动态链接和方法返回值;

  • 特点

    • 每个线程独享一个栈;

    • 栈帧(Stack Frame)是栈的基本单位,每个方法调用对应一个栈帧。

  • 相关参数

    • -Xss:设置线程栈的大小。

本地方法栈(Native Method Stack)

  • 作用:支持Native方法(如C/C++代码)的执行;

  • 特点

    • 与栈类似,但专门用于Native方法。

程序计数器(Program Counter Register)

  • 作用:记录当前线程执行的字节码指令地址;

  • 特点

    • 每个线程独享一个程序计数器;

    • 如果执行的是Native方法,程序计数器的值为空(Undefined)。

JVM内存模型的工作机制

对象的创建与回收

  • 对象创建

    • 对象通常在新生代的Eden区分配;

    • 如果Eden区空间不足,会触发Minor GC。

  • 对象晋升

    • 经过多次Minor GC后仍然存活的对象会被晋升到老年代。

  • 垃圾回收

    • 新生代使用复制算法(Copying);

    • 老年代使用标记-清除(Mark-Sweep)或标记-整理(Mark-Compact)算法。

方法调用与栈帧

  • 方法调用

    • 每次方法调用都会创建一个栈帧,栈帧包含局部变量表、操作数栈、动态链接和方法返回地址。

  • 方法返回

    • 方法执行完成后,栈帧被销毁,程序计数器恢复到调用者的位置。

多线程与内存模型

  • 线程私有区域

    • 每个线程独享栈、本地方法栈和程序计数器。

  • 线程共享区域

    • 堆和方法区是所有线程共享的,需要同步机制来保证线程安全。

实例

以下是一个常见的JVM参数设置示例:

java -Xms512m -Xmx2g -Xss256k -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:+UseG1GC -jar app.jar
  • -Xms512m:初始堆大小为512MB;

  • -Xmx2g:最大堆大小为2GB;

  • -Xss256k:每个线程的栈大小为256KB;

  • -XX:MetaspaceSize=128m:初始元空间大小为128MB;

  • -XX:MaxMetaspaceSize=256m:最大元空间大小为256MB;

  • -XX:+UseG1GC:启用G1垃圾回收器。