JVM面试知识点总结

1. JVM内存模型

1.1 运行时数据区

  • 程序计数器:线程私有,记录当前线程执行的字节码行号指示器

  • Java虚拟机栈:线程私有,存储栈帧(局部变量表、操作数栈、动态链接、方法出口等)

  • 本地方法栈:为Native方法服务

  • Java堆:所有线程共享,存放对象实例,是GC主要区域

  • 方法区:存储已被加载的类信息、常量、静态变量等

1.2 内存分配策略

  • 对象优先在Eden区分配

  • 大对象直接进入老年代

  • 长期存活的对象进入老年代(默认15次GC后晋升)

  • 动态对象年龄判定(Survivor区中相同年龄对象总大小超过一半,则大于等于该年龄的对象直接进入老年代)

2. 垃圾回收机制

2.1 判断对象是否可回收

  • 引用计数法:简单但无法解决循环引用问题

  • 可达性分析算法:通过GC Roots对象作为起点,判断对象引用链是否可达

2.2 垃圾回收算法

  • 标记-清除:简单但会产生内存碎片

  • 复制算法:将内存分为两块,每次使用一块,回收时将存活对象复制到另一块

  • 标记-整理:标记后让存活对象向一端移动,然后清理边界外内存

  • 分代收集:根据对象存活周期将堆分为新生代和老年代,采用不同算法

2.3 常见垃圾收集器

  • Serial:单线程,适用于客户端应用

  • ParNew:Serial的多线程版本

  • Parallel Scavenge:吞吐量优先的收集器

  • CMS:低停顿时间的收集器,采用标记-清除算法

  • G1:面向服务端的收集器,整体采用标记-整理算法,局部采用复制算法

3. 类加载机制

3.1 类加载过程

  1. 加载:获取类的二进制字节流

  2. 验证:确保Class文件符合规范

  3. 准备:为类变量分配内存并设置初始值

  4. 解析:将符号引用转为直接引用

  5. 初始化:执行类构造器<clinit>()方法

3.2 类加载器

  • 启动类加载器:加载<JAVA_HOME>/lib目录下的类

  • 扩展类加载器:加载<JAVA_HOME>/lib/ext目录下的类

  • 应用程序类加载器:加载用户类路径上的类

  • 自定义类加载器:用户自定义实现

3.3 双亲委派模型

  • 子类加载器先委托父类加载器加载

  • 父类加载器无法完成加载时,子类加载器才尝试自己加载

  • 优点:避免重复加载,保证核心类安全

4. JVM性能调优

4.1 常用JVM参数

  • -Xms:初始堆大小

  • -Xmx:最大堆大小

  • -Xmn:新生代大小

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

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

  • -XX:MaxTenuringThreshold:对象晋升老年代的年龄阈值

4.2 调优工具

  • jps:查看Java进程

  • jstat:监控JVM统计信息

  • jmap:生成堆转储快照

  • jstack:生成线程快照

  • VisualVM:可视化监控工具

  • MAT:内存分析工具

5. 常见面试问题

5.1 JVM内存结构

JVM内存主要分为以下几个区域:

  • 方法区(Method Area):存储类信息、常量、静态变量等数据

  • 堆(Heap):对象实例的存储区域,是GC主要管理的区域

  • 虚拟机栈(VM Stack):线程私有,存储局部变量表、操作数栈等

  • 本地方法栈(Native Method Stack):为本地方法服务

  • 程序计数器(Program Counter Register):线程私有,记录当前线程执行的位置

5.2 对象回收判断

判断对象是否可回收的算法:

  1. 引用计数法:每个对象维护引用计数器,为0时回收。简单但无法解决循环引用问题。

  2. 可达性分析:从GC Roots对象开始遍历引用链,不可达的对象判定为可回收。GC Roots包括:

    • 虚拟机栈中引用的对象

    • 方法区中类静态属性引用的对象

    • 方法区中常量引用的对象

    • 本地方法栈中JNI引用的对象

5.3 垃圾回收算法

标记-清除算法(Mark-Sweep)

  • 过程:标记所有需要回收的对象,然后统一清除

  • 优点:实现简单

  • 缺点:产生内存碎片,效率不高

复制算法(Copying)

  • 过程:将内存分为两块,每次只使用一块,存活对象复制到另一块

  • 优点:无内存碎片

  • 缺点:内存利用率只有50%

标记-整理算法(Mark-Compact)

  • 过程:标记后让存活对象向一端移动,然后清理边界外的内存

  • 优点:无内存碎片,内存利用率高

  • 缺点:移动对象成本高

分代收集算法(Generational Collection)

  • 新生代:使用复制算法(如Serial、ParNew收集器)

  • 老年代:使用标记-清除或标记-整理算法(如CMS、Serial Old收集器)

5.4 CMS与G1收集器比较

CMS(Concurrent Mark Sweep)

  • 特点:以最短回收停顿时间为目标,标记-清除算法

  • 过程

    1. 初始标记(STW)

    2. 并发标记

    3. 重新标记(STW)

    4. 并发清除

  • 优点:并发收集,低停顿

  • 缺点:产生内存碎片,CPU敏感

G1(Garbage-First)

  • 特点:面向服务端应用,整体基于标记-整理,局部基于复制算法

  • 优点

    • 可预测停顿时间

    • 高吞吐量

    • 无内存碎片问题

  • 适用场景:大内存(6GB以上)应用

5.5 双亲委派模型

类加载器层次结构:

  1. 启动类加载器(Bootstrap ClassLoader):加载<JAVA_HOME>/lib目录

  2. 扩展类加载器(Extension ClassLoader):加载<JAVA_HOME>/lib/ext目录

  3. 应用程序类加载器(Application ClassLoader):加载用户类路径

工作流程:子类加载器先委托父类加载器加载,父类无法完成时子类才尝试加载

优点

  • 避免重复加载

  • 保证核心类库安全(如java.lang.Object只能由启动类加载器加载)

5.6 内存泄漏排查

排查步骤:

  1. 监控工具:使用jstat、VisualVM等工具观察内存变化

  2. 堆转储jmap -dump:format=b,file=heap.hprof <pid>

  3. 分析工具:MAT(Memory Analyzer Tool)、JProfiler分析堆转储文件

  4. 常见泄漏点

    • 静态集合类

    • 未关闭的资源(连接、流等)

    • 监听器未注销

    • 不合理的缓存设计

5.7 JVM调优参数

常用参数:

  • 堆内存
    • -Xms:初始堆大小
    • -Xmx:最大堆大小
    • -Xmn:新生代大小
  • GC相关
    • -XX:+UseConcMarkSweepGC:使用CMS收集器
    • -XX:+UseG1GC:使用G1收集器
    • -XX:MaxGCPauseMillis:最大GC停顿时间目标
  • 其他
    • -XX:MetaspaceSize:元空间初始大小
    • -XX:MaxMetaspaceSize:元空间最大大小
    • -XX:+HeapDumpOnOutOfMemoryError:OOM时自动生成堆转储

5.8 线程死锁分析

排查方法:

  1. 获取线程快照jstack <pid> > thread_dump.log

  2. 分析死锁:查找"Found one Java-level deadlock"部分

  3. 工具分析:使用VisualVM、JConsole等工具查看线程状态

  4. 预防措施

    • 按固定顺序获取锁

    • 使用tryLock设置超时

    • 避免嵌套锁

5.9 对象创建过程

  1. 类加载检查:检查类是否已加载

  2. 内存分配

    • 指针碰撞:内存规整时使用

    • 空闲列表:内存不规整时使用

  3. 初始化零值:为对象字段赋默认值

  4. 设置对象头:存储类元数据、GC年龄等信息

  5. 执行<init>方法:按照程序员的意愿初始化对象

5.10 类加载过程

  1. 加载(Loading)

    • 获取类的二进制字节流

    • 将静态存储结构转化为方法区运行时数据结构

    • 生成Class对象作为访问入口

  2. 验证(Verification):确保类文件符合规范

  3. 准备(Preparation):为类变量分配内存并设置初始值

  4. 解析(Resolution):将符号引用转为直接引用

  5. 初始化(Initialization):执行类构造器<clinit>()方法

  6. 使用(Using):程序中使用已加载的类

  7. 卸载(Unloading):从JVM中移除不再需要的类

相关推荐
zdl6862 小时前
spring Profile
java·数据库·spring
m0_706653232 小时前
数据倾斜全面解析与解决方案探析
java
程序员飞哥2 小时前
有个同事因为恐惧 AI 要离职了
java·后端·程序员
vanvivo2 小时前
springboot3.X 无法解析parameter参数问题
java
zhougl9962 小时前
Maven build配置
java·linux·maven
喵喵蒻葉睦2 小时前
力扣 hot100 最小覆盖子串 哈希表 滑动窗口 Java 题解
java·算法·leetcode·哈希算法·散列表·滑动窗口
SimonKing2 小时前
AI时代,一个Skill如何让Java项目结构自动化?
java·后端·程序员
dgfhf2 小时前
使用Python处理计算机图形学(PIL/Pillow)
jvm·数据库·python
闻哥2 小时前
MySQL三大日志深度解析:redo log、undo log、binlog 原理与实战
android·java·jvm·数据库·mysql·adb·面试