JVM高频面试题

时间:2024-07-18 17:24:16

1. 内存模型

线程独享: 虚拟机栈, 本地方法栈, 程序计数器

线程共享: 堆, 方法区

2. 虚拟机栈的作用

存放栈帧, 栈帧又包含局部变量表, 每个方法从被调用到执行结束的过程都对应着一个栈帧在虚拟机栈中从入栈到出栈的过程

3. 程序计数器

存放下一条指令的信息

4. 堆

Java堆是被所有线程共享的区域, 存放几乎所有的对象实例, 是垃圾回收器管理的区域

5. 方法区

存放已加载的类信息, 常量, 静态变量, 编译后的代码缓存

6. 对象的内存布局

  • 对象头:

    • 存储对象自身的运行时数据, 如锁状态, GC年龄分代, 线程持有的锁
    • 类型指针, 指向它的类型元数据,
    • 如果对象是个数组, 那么还有一块用于记录数组长度的数据,
  • 实例数据:程序代码里定义的各种类型的字段.这部分的顺序会收到虚拟机分配策略参数的影响.

  • 对齐填充: 不是必要的, 但是必须保证任何对象的大小都是8字节的整数倍

7. 对象时如何访问定位的

  • 句柄访问
  • 直接指针访问

8. 对象已死

  • 引用计数法

  • 可达性分析

9. 引用

  • 强引用: 最传统的引用, 无论在任何情况下, 只有有强引用, 就不会被回收
  • 软引用: 用来描述还有用, 但是非必要的对象, 在系统将要发生内存溢出前才会被回收
  • 弱引用: 用来描述非必要的对象, 只要垃圾回收器开始工作,就会被回收
  • 虚引用: 最弱的引用关系, 只是为了能在该对象被回收时得到系统的通知

10. 垃圾收集算法

  • 标记-清除: 先标记需要回收的对象, 再统一回收
  • 标记-复制: 将内存划分成相等两块, 一块用完了就把存活的对象复制的另一边, 以此往复, 但是为了避免浪费太多空间, 而且大部分对象具有朝生夕死的特性,现在采用Elden:Survivor=8:1 的方式, 每次都将 Elden 和 正在用的那块Survivor 空间复制到另一块空闲的Survivor, 以此往复
  • 标记-整理: 回收对象后, 将所有存活的对象向内存空间的一端移动, 然后直接清理边界以外的内存. 可以避免内存空间过于碎片化, 有利于内存分配

11. CMS 收集器

CMS (Concurrent Mark Sweep), 基于 标记-清除算法,

运作过程: - 初始标记, - 并发标记, -重新标记, - 并发清楚

优点: 并发收集, 低停顿

缺点: 对处理器资源敏感, 无法处理浮动垃圾, 会产生大量空间碎片

12. G1 收集器

G1 (garbage first) 收集器, 面向服务端, 是一款全功能垃圾收集器, 开创基于Region的堆内存布局.能实现在指定长度为M毫秒的时间片段内, 消耗在垃圾收集上的时间不超过N毫秒的功能.

运作过程: - 初始标记, - 并发标记, - 最终标记, - 筛选回收

13. 内存分配的策略

  • 对象优先在 Eden 分配
  • 大对象直接进入老年代
  • 长期存活的对象进入老年代
  • 动态对象年龄判定
  • 空间分配担保

14. 常用 JVM 调优命令

jsp:虚拟机进程状况工具

jstat:虚拟机统计信息监视工具

jinfo:java配置信息工具

jmap:内存映像工具

jhat:虚拟机堆转存快照分析工具

jstack:java 堆栈跟踪工具

15. 类加载的流程

  • 加载: - 通过类的全限定名来获取此类的二进制字节流, - 将字节流转化为方法区的运行时数据结构 - 在内存中生成一个代表这个类的java.lang.Class对象, 作为数据的访问入口
  • 验证: 确保Class文件的字节流中包含的信息符合 <java虚拟机规范> 的全部要求
  • 准备: 为类中定义的标量赋初值, 注意区分初值与默认值,
  • 解析: 将常量池中的字符串替换为直接引用
  • 初始化: Java虚拟机开始执行编写的Java程序, 执行类构造器 () 方法,

16. 双亲委派模型

如果一个类加载器收到了类加载的请求, 首先会委派上一级的类加载器去完成, 每一次都是如此, 直到最顶层启动类加载器, 如果, 父级类加载器无法完成加载任务, 那么子类才会尝试自己去加载,

好处是, 程序运行时, 某一个类只会被某个特定的类加载器加载, 不会出现多个不同但是又同名的类, 保证了Java程序的稳定性