欢迎光临
我们一直在努力

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 类加载过程

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

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

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

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

  • 初始化:执行类构造器<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 对象回收判断

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

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

  • 可达性分析:从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)

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

    • 过程:

    • 初始标记(STW)

    • 并发标记

    • 重新标记(STW)

    • 并发清除

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

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

    G1(Garbage-First)

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

    • 优点:

      • 可预测停顿时间

      • 高吞吐量

      • 无内存碎片问题

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

    5.5 双亲委派模型

    类加载器层次结构:

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

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

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

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

    优点:

    • 避免重复加载

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

    5.6 内存泄漏排查

    排查步骤:

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

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

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

  • 常见泄漏点:

    • 静态集合类

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

    • 监听器未注销

    • 不合理的缓存设计

  • 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 线程死锁分析

    排查方法:

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

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

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

  • 预防措施:

    • 按固定顺序获取锁

    • 使用tryLock设置超时

    • 避免嵌套锁

  • 5.9 对象创建过程

  • 类加载检查:检查类是否已加载

  • 内存分配:

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

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

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

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

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

  • 5.10 类加载过程

  • 加载(Loading):

    • 获取类的二进制字节流

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

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

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

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

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

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

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

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

  • 赞(0)
    未经允许不得转载:171主机测评 » JVM面试知识点总结
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址