JVM 内存共享区域详解

发布于:2025-07-29 ⋅ 阅读:(28) ⋅ 点赞:(0)

Java 虚拟机在执行 Java 程序时,会将所管理的内存划分为若干个不同的数据区域。其中,堆(Heap)方法区(Method Area)所有线程共享的区域,属于 JVM 的共享内存区。


一、JVM 内存结构总览

区域名称 是否线程共享 说明
程序计数器(Program Counter Register) 当前线程执行字节码的行号指示器
Java 虚拟机栈(JVM Stack) 每个线程私有,方法调用栈帧
本地方法栈(Native Method Stack) 用于调用 Native 方法
Java 堆(Heap) 所有线程共享,用于存放对象实例
方法区(Method Area) 所有线程共享,存储类结构、静态变量、运行时常量池等
运行时常量池(Runtime Constant Pool) 方法区的一部分,存储编译时生成的常量和符号引用
直接内存(Direct Memory) 特殊 不在堆中,使用 JNI 分配的本地内存

二、JVM 共享内存区域有哪些?

1. Java 堆(Heap)

  • 作用:Java 堆是 JVM 中最大的一块内存区域,用于存储对象实例和数组,是垃圾收集器管理的主要区域。

  • 线程共享:是

  • 创建时机:在 JVM 启动时创建。

  • 内存划分

    • 新生代(Young Generation)

      • Eden 区:新对象优先在这里分配。
      • Survivor 区(S0 和 S1):用作对象复制回收。
    • 老年代(Old Generation)

      • 存放生命周期较长的对象。
    • (JDK 1.7 以前)永久代(PermGen)

    • (JDK 1.8 以后)元空间(Metaspace):使用本地内存替代永久代。

新生代对象分配与晋升过程
  • 对象先分配到 Eden 区。
  • 经一次 Minor GC 后存活的对象转移至 Survivor 区。
  • 如果在 Survivor 区多次存活(年龄达阈值如 15),将晋升到老年代。

2. 方法区(Method Area)→ 元空间(Metaspace)

  • 作用:用于存储已被虚拟机加载的类信息、常量、静态变量、JIT 编译后的代码等。

  • 线程共享: 是

  • JDK 变化

    • JDK 1.7 之前方法区由永久代实现。
    • JDK 1.8 开始使用 元空间(Metaspace) 替代,分配在本地内存中。
方法区存储内容包括:
  • 类结构信息(字段、方法、访问修饰符等)
  • 运行时常量池(Runtime Constant Pool)
  • 静态变量
  • 类初始化信息
  • JIT 编译后的代码缓存等

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

  • 所属区域:方法区的一部分

  • 线程共享: 是

  • 内容

    • 编译时生成的字面量(如字符串、整型常量等)
    • 符号引用(类名、方法名等)

4. 字符串常量池(String Constant Pool)

  • 位置

    • JDK 1.6 及以前:在永久代
    • JDK 1.7 起:转移至堆中
  • 目的:避免重复创建相同字符串,提升效率与节省内存

示例
String a = "ab";
String b = "ab";
System.out.println(a == b); // true,共享常量池引用
字符串常量池为何从永久代迁移到堆?
  • 永久代 GC 频率低,字符串长时间不回收。
  • 堆空间更大、GC 更灵活,能提升字符串内存回收效率。

5. 直接内存(Direct Memory)

  • 作用:通过 ByteBuffer.allocateDirect() 分配的内存,常用于性能敏感的 I/O 操作。

  • 使用方式:通过 JNI 方式直接使用本地内存。

  • 特点

    • 不属于 JVM 规范定义的内存区域。
    • 不是堆内存,也不在方法区。
    • 容易出现 OOM 错误(如未手动释放)。

三、JVM 非共享内存区域(仅作对比)

区域 线程共享 用途
程序计数器 记录当前线程执行的位置(字节码行号)
虚拟机栈 方法调用、局部变量、操作数栈等
本地方法栈 执行 native 方法

面试高频考点总结

问题 要点
JVM 哪些区域是线程共享的? Java 堆、方法区(元空间)、运行时常量池、字符串常量池
方法区和堆的区别? 方法区存储类结构信息,堆存储对象实例;两者都线程共享
JDK 1.7/1.8 的内存结构变化? 移除永久代,引入元空间;字符串常量池移动到堆
什么是直接内存? 不在堆、不在方法区,用 JNI 访问的本地内存
方法区中存储的内容有哪些? 类元数据、静态变量、常量池、JIT 编译代码等

网站公告

今日签到

点亮在社区的每一天
去签到