Bohr-L Bohr-L
首页
技术
常见面试题
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

刘博

I'm a slow walker, But I never walk backwards.
首页
技术
常见面试题
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 数据处理与存储类

  • Spring 生态类

  • 缓存问题类

  • 多线程类

  • JVM 类

    • JVM 中一次完整的 GC 流程(从 ygc 到 fgc)是怎样的
    • 如何判断对象可以被回收
    • 你知道哪些垃圾收集算法
    • 虚拟机为什么使用元空间替换了永久代
    • 简述 CMS 垃圾收集器的工作流程,它有什么优缺点?
    • 简述一下 JVM 的内存模型
      • 说说堆和栈的区别
      • 简述 Java 的对象结构
      • JVM 中的即时编译器(JIT)如何工作?
      • 什么是内存屏障?JVM 如何使用内存屏障保证指令执行顺序?
      • 如何排查和解决 JVM 内存泄漏问题?有哪些常用的工具和方法?
      • JVM 如何处理异常?try-catch-finally 结构在字节码层面是如何实现的?
    • MySQL 类

    • Java 8 + 特性类

    • 其他技术类

    • 常见面试题
    • JVM 类
    刘博
    2025-12-28
    目录

    简述一下 JVM 的内存模型

    JVM 内存模型(Java Virtual Machine Memory Model)是JVM 规范中定义的内存划分规则,用于管理 Java 程序运行时的内存,确保内存分配、使用和回收的有序性。不同 JDK 版本的内存模型略有差异(如 JDK 8 移除永久代,引入元空间),当前主流(JDK 8+)的内存模型如下:

    # 1. 内存模型的核心区域(按线程可见性划分)

    JVM 内存分为 “线程私有区域”(每个线程独立拥有,生命周期与线程一致)和 “线程共享区域”(所有线程共享,生命周期与 JVM 一致),具体划分:

    # (1)线程私有区域(Thread-Private Area)

    每个线程启动时创建,线程结束时销毁,无需 GC 回收(除栈帧中的对象引用指向堆内存):

    • 1. 程序计数器(Program Counter Register):
      • 作用:记录当前线程执行的字节码指令的地址(行号),用于线程切换后恢复执行位置(如线程 A 被暂停,切换到线程 B,线程 A 再次执行时,通过程序计数器找到上次执行的指令);
      • 特点:
        • 线程私有,每个线程有独立的程序计数器;
        • 唯一不会抛出OutOfMemoryError的区域(内存大小固定,与 CPU 核心数匹配);
        • 若线程执行的是本地方法(JNI 方法),程序计数器值为undefined(本地方法由 C/C++ 执行,无需字节码指令地址)。
    • 2. 虚拟机栈(VM Stack):
      • 作用:存储线程执行方法时的 “栈帧”(Stack Frame),每个方法调用对应一个栈帧入栈,方法执行完成对应栈帧出栈;
      • 栈帧组成:
        • 局部变量表:存储方法的局部变量(基本数据类型、对象引用),内存大小在编译时确定;
        • 操作数栈:用于方法执行时的临时数据运算(如i + j,先将i和j压入操作数栈,再执行加法指令);
        • 动态链接:指向方法区中该方法的类元信息(如方法的符号引用,用于动态绑定,如多态);
        • 方法返回地址:方法执行完成后,返回的父方法的指令地址(如方法 A 调用方法 B,方法 B 执行完后,通过返回地址回到方法 A 的下一条指令);
      • 特点:
        • 线程私有,栈深度由方法调用链长度决定;
        • 内存大小可固定(-XX:ThreadStackSize,默认 1MB)或动态扩展;
        • 异常:若栈深度超过最大限制,抛出StackOverflowError(如递归调用无终止条件);若栈内存无法动态扩展(如内存不足),抛出OutOfMemoryError。
    • 3. 本地方法栈(Native Method Stack):
      • 作用:与虚拟机栈类似,区别是存储线程执行 “本地方法”(JNI 方法)时的栈帧;
      • 特点:
        • 线程私有,由 JVM 实现(如 HotSpot JVM 将本地方法栈与虚拟机栈合并,统一管理);
        • 异常:与虚拟机栈一致,抛出StackOverflowError或OutOfMemoryError。
    # (2)线程共享区域(Thread-Shared Area)

    所有线程共享,由 JVM 启动时创建,JVM 关闭时销毁,需 GC 回收(堆内存和方法区的部分数据):

    • 1. 堆(Heap):

      • 作用:存储 Java 对象实例(new关键字创建的对象)和数组,是 JVM 内存中最大的区域,也是 GC 的主要区域;
      • 分代划分(基于 “分代回收” 思想):
        • 新生代(Young Generation,占堆内存 1/3):
          • Eden 区(80%):新对象优先分配到 Eden 区;
          • Survivor 区(20%):分为 From Survivor 和 To Survivor(各 10%),存储 Eden 区 GC 后存活的对象;
        • 老年代(Old Generation,占堆内存 2/3):存储从新生代晋升的长期存活对象(如年龄达到MaxTenuringThreshold的对象);
      • 特点:
        • 线程共享,所有线程可访问堆中的对象;
        • 内存大小可通过-Xms(初始堆大小)和-Xmx(最大堆大小)配置(推荐设置为相同值,避免频繁扩容);
        • 异常:若堆内存无法分配新对象(且 GC 无法释放足够内存),抛出OutOfMemoryError: Java heap space。
    • 2. 方法区(Method Area):

      • 作用:存储类元信息(类结构、字段、方法、接口定义)、常量池(字符串常量、数字常量)、静态变量、即时编译器(JIT)编译后的代码等;
      • JDK 版本差异:
        • JDK 7 及之前:方法区被称为 “永久代(PermGen)”,属于堆内存的一部分,大小通过-XX:PermSize和-XX:MaxPermSize配置;
        • JDK 8 及之后:永久代被元空间(Metaspace) 替代,元空间使用本地内存(Native Memory),大小默认无上限(可通过-XX:MetaspaceSize和-XX:MaxMetaspaceSize限制);
      • 特点:
        • 线程共享,所有线程可访问类元信息;
        • 内存回收效率低:仅当类的类加载器被回收且无对象引用该类时,类元信息才会被回收;
        • 异常:JDK 7 及之前,永久代满时抛出OutOfMemoryError: PermGen Space;JDK 8 及之后,元空间满时抛出OutOfMemoryError: Metaspace。
    • 3. 运行时常量池(Runtime Constant Pool):

      • 作用:方法区的一部分,存储类编译后的常量(如字符串常量"abc"、整数常量123)、符号引用(类名、方法名的字符串引用);
      • 特点:
        • 动态性:JDK 7 后,运行时常量池支持动态添加常量(如String.intern()方法,可将字符串动态加入常量池);
        • 内存回收:当常量不再被引用时,可被 GC 回收(如动态添加的字符串常量)。

    上次更新: 12/30/2025
    简述 CMS 垃圾收集器的工作流程,它有什么优缺点?
    说说堆和栈的区别

    ← 简述 CMS 垃圾收集器的工作流程,它有什么优缺点? 说说堆和栈的区别→

    最近更新
    01
    CPU 使用率较高排查和解决
    12-29
    02
    JVM OOM 问题如何排查和解决
    12-29
    03
    接口防刷怎么实现?
    12-29
    更多文章>
    Theme by Vdoing | Copyright © 2025-2026 Bohr-L's note
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式