JVM是Java面试的核心模块,涉及内存模型、垃圾回收、类加载等关键知识点。

1. JVM内存区域划分?

  • 程序计数器:当前线程执行的字节码行号,线程私有,唯一不会OOM的区域。
  • 虚拟机栈:线程私有,存储栈帧(局部变量表、操作数栈、动态链接、方法出口)。-Xss设置大小。
  • 本地方法栈:服务Native方法,线程私有。
  • :线程共享,存放对象实例和数组。-Xms和-Xmx设置大小,GC主要区域。
  • 方法区:线程共享,存放类信息、常量、静态变量。JDK8后用元空间(Metaspace)实现,使用本地内存。

2. 对象创建过程?

  • 类加载检查:检查常量池中类的符号引用是否已加载、解析、初始化。
  • 分配内存:指针碰撞(内存规整)或空闲列表(内存碎片)。
  • 初始化零值:内存分配后初始化为零值(不包括对象头)。
  • 设置对象头:设置Mark Word(哈希码、GC分代年龄、锁状态)和类元信息引用。
  • 执行init:执行构造方法,为属性赋值。

3. 如何判断对象是否存活?

  • 引用计数法:对象被引用+1,取消-1,为0可回收。缺点:循环引用问题,JVM不采用。
  • 可达性分析:从GC Roots开始遍历对象图,不可达的对象为可回收对象。JVM采用此方法。

GC Roots包括:虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈JNI引用的对象、Java虚拟机内部引用、同步锁持有的对象。

4. JVM垃圾回收算法?

  • 标记-清除:标记存活对象,清除其余。缺点:产生内存碎片。
  • 标记-整理:标记存活对象,向一端移动,清除边界外。无碎片,但移动开销大,适合老年代。
  • 复制算法:将内存分两块,存活对象复制到另一块,清空当前。无碎片,但利用率50%。适合新生代。
  • 分代收集:新生代用复制算法(Eden:Survivor=8:1:1),老年代用标记-清除或标记-整理。

5. 常见垃圾收集器?

  • Serial / Serial Old:单线程,STW,适合Client模式。
  • Parallel Scavenge / Parallel Old:多线程并行,吞吐量优先,适合后台运算。
  • CMS:老年代收集器,标记-清除,低延迟。初始标记(STW)→并发标记→重新标记(STW)→并发清除。已废弃。
  • G1:将堆分为Region,混合回收。可控停顿时间(-XX:MaxGCPauseMillis)。JDK9默认。
  • ZGC:着色指针+读屏障,停顿时间<10ms,支持TB级堆。JDK11引入。

6. 类加载过程?

  • 加载:通过类全限定名获取二进制字节流,生成Class对象。
  • 验证:文件格式、元数据、字节码、符号引用验证。
  • 准备:为静态变量分配内存并赋零值(final修饰的在编译期赋值)。
  • 解析:将常量池中的符号引用替换为直接引用。
  • 初始化:执行类构造器clinit()方法,为静态变量赋值,执行静态代码块。

7. 双亲委派模型及打破场景?

双亲委派:类加载请求先委托给父加载器,父加载器无法加载时才自己加载。

  • 启动类加载器:加载JAVA_HOME/lib。
  • 扩展类加载器:加载JAVA_HOME/lib/ext。
  • 应用类加载器:加载classpath。
  • 自定义类加载器:继承URLClassLoader。

打破场景:SPI机制用Thread.contextClassLoader打破(JDBC);热部署Tomcat每个webapp有独立ClassLoader;OSGi网状结构不遵循双亲委派。

8. JVM调优常用参数?

  • 堆大小:-Xms4g -Xmx4g(建议相同,避免扩容抖动)。
  • 新生代:-Xmn2g 或 -XX:NewRatio=2(老:新=2:1)。
  • 元空间:-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m。
  • GC日志:-Xlog:gc*:file=gc.log(JDK9+)。
  • 收集器:-XX:+UseG1GC。
  • OOM时dump:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof。

9. OOM类型及排查思路?

  • java.lang.OutOfMemoryError: Java heap space:堆内存不足,可能内存泄漏或大对象。jmap -dump导出heap,MAT分析。
  • GC overhead limit exceeded:GC耗时>98%但回收<2%。通常是内存泄漏。
  • Metaspace:元空间溢出,动态生成类过多(如CGLIB)。增大MaxMetaspaceSize。
  • Direct buffer memory:堆外内存不足,NIO使用不当。
  • Unable to create new native thread:线程数超过系统限制。

10. 强引用、软引用、弱引用、虚引用?

  • 强引用:Object obj = new Object(),只要强引用存在就不回收。
  • 软引用:SoftReference,内存不足时才回收。适合缓存。
  • 弱引用:WeakReference,下一次GC就回收。ThreadLocalMap的key。
  • 虚引用:PhantomReference,不影响对象生命周期,仅用于跟踪回收事件。配合ReferenceQueue。

11. 内存泄漏与内存溢出的区别?

内存泄漏:对象不再使用但无法被GC回收,如集合中存放对象引用但未移除。长期积累导致内存溢出

内存溢出(OOM):程序申请内存时没有足够内存。可能是泄漏导致,也可能是堆大小设置不足或瞬时大对象。

By admin

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注