JVM 内存结构有哪些?

1 阅读3分钟

JVM 内存结构,常见可以分成 运行时数据区 这几部分:

1. 程序计数器(Program Counter Register)

  • 每个线程私有
  • 记录当前线程正在执行的字节码指令地址
  • 线程切换后,能恢复到正确执行位置
  • 是 JVM 规范里唯一一个不会发生 OutOfMemoryError 的区域

2. Java 虚拟机栈(JVM Stack)

  • 每个线程私有

  • 方法调用时会创建一个栈帧

  • 栈帧里主要有:

    • 局部变量表
    • 操作数栈
    • 动态链接
    • 方法出口信息
  • 方法执行结束,栈帧出栈

  • 可能出现:

    • StackOverflowError
    • OutOfMemoryError

3. 本地方法栈(Native Method Stack)

  • 每个线程私有
  • 为 JVM 调用 native 方法 服务
  • 和虚拟机栈类似,只不过服务对象是本地方法

4. Java 堆(Heap)

  • 线程共享

  • JVM 中最大的一块内存区域

  • 几乎所有对象实例和数组都在这里分配

  • 是垃圾回收器(GC)管理的主要区域

  • 常见进一步划分:

    • 新生代

      • Eden
      • Survivor(From / To)
    • 老年代

  • 可能出现:

    • OutOfMemoryError: Java heap space

5. 方法区(Method Area)

  • 线程共享

  • 存储类相关信息:

    • 类元数据
    • 运行时常量池
    • 字段信息
    • 方法信息
    • 静态变量
    • 即时编译后的代码缓存等
  • JDK 1.8 以后,HotSpot 用 元空间(Metaspace) 实现方法区,使用本地内存,不再是永久代

  • 可能出现:

    • OutOfMemoryError: Metaspace

6. 运行时常量池(Runtime Constant Pool)

  • 方法区的一部分

  • 存放编译期生成的各种字面量和符号引用

  • 类加载后进入运行时常量池

  • 比如:

    • 字符串字面量
    • 类和方法的符号引用

一张简单总结表

区域线程私有/共享作用
程序计数器私有记录当前执行位置
虚拟机栈私有方法调用和局部变量
本地方法栈私有native 方法调用
共享对象实例和数组
方法区共享类信息、常量、静态变量、JIT代码
运行时常量池共享常量和符号引用

面试里经常追问的点

JDK 1.7 / 1.8 相关

  • 永久代(PermGen) :JDK 1.7 及以前 HotSpot 对方法区的一种实现
  • 元空间(Metaspace) :JDK 1.8 开始替代永久代,使用本地内存

堆和栈的区别

  • :线程私有,存方法执行过程中的局部变量,生命周期随方法
  • :线程共享,存对象实例,生命周期由 GC 管理

为什么要有程序计数器

  • 因为 Java 支持多线程,线程切换后需要知道上次执行到哪里

背诵版

JVM 内存结构主要包括:程序计数器、虚拟机栈、本地方法栈、堆、方法区,其中运行时常量池属于方法区的一部分。程序计数器、虚拟机栈、本地方法栈是线程私有的;堆和方法区是线程共享的。堆主要存对象实例,是 GC 的主要区域;栈主要管理方法调用和局部变量;方法区主要存类信息、常量、静态变量和 JIT 编译代码。JDK 1.8 以后,方法区由元空间实现,替代了永久代。