JVM面试题大全

JVM面试题大全


目录

文档索引

面试题汇总

Q:谈谈你对JVM的理解?

Q:JVM的架构是怎么样的?

Q:什么是双亲委派模型?

Q:如何打破双亲委派模型?

Q:JVM内存各个组件分别有什么作用?

Q:栈帧由什么组成?

Q:谈谈堆GC?

Q:怎么判断对象是否还存活?

Q:有哪些内存清理算法?各有什么优势?

Q:JVM提供了哪些垃圾收集器?优势和使用场景是什么?

Q:什么是三色标记法?

Q:三色标记法存在什么问题?JVM如何解决?

Q:什么是跨代引用问题?JVM如何解决?

Q:CMS是如何进行垃圾收集的?

Q:G1是如何进行垃圾收集的?

Q:JVM如何调优?

Q:JVM有哪些参数?如何确定参数值?

Q:JVM发生大量的GC如何处理?

Q:JVM死锁分析?


文档索引

官方文档:The Java® Virtual Machine Specification

推荐书籍

深入理解Java虚拟机-周志明


面试题汇总

Q:谈谈你对JVM的理解?

A:

  1. 通过JVM屏蔽操作系统,实现了Java程序的跨平台;
  2. JVM提供了多种垃圾回收器,并不断提高垃圾回收的性能
  3. JVM已有多年历史,平台非常成熟

Q:JVM的架构是怎么样的?

A:

JVM的整体架构包括:类加载子系统、运行时内存数据区、执行引擎

整体流程为:

  1. 将Class字节码,通过双亲委派模型进行加载,并通过验证、准备、解析及初始化将类加载完成
  2. 类信息、常量池、静态变量会先放入方法区
  3. 当主线程开始main方法,方法栈中生成对应的栈帧信息,并通过程序计数器获取指令执行
  4. 如有实例化对象,对象实例会被放入堆中
  5. 垃圾回收器会对堆中的对象进行回收

Q:什么是双亲委派模型?

A:

如果我们自己新写一个java.lang.Object类放入ClassPath中,此时,虚拟机会使用rt.jar包里的Object作为运行类还是我们新写的Object作为运行类呢?

答案是虚拟机还是使用rt.jar包中的Object类,而我们新写的Object类永远无法被加载运行,这样保证了Java程序的稳定运行。

那么为什么虚拟机会去加载rt.jar包里的Object类呢?答案就是双亲委派模型。

双亲委派模型将类加载器分为四层

  1. 启动类加载器:顶层类加载器,负责将存放在<JAVA_HOME>\lib目录或被-Xbootclasspath参数所指定路径下,固定文件名(如rt.jar)的类库加载到虚拟机内存中。
  2. 扩展类加载器:第二层类加载器,负责将存放在<JAVA_HOME>\lib\ext目录或被java.ext.dirs系统变量所指定的路径下的类库加载到虚拟机内存中
  3. 应用程序类加载器:第三层类加载器,负责将存放在用户类路径(ClassPaht)目录下的类库加载到虚拟机内存中
  4. 自定义类加载器:第四层加载器,由用户编写的类加载器

如果一个类加载器收到了类加载的请求,它不会自己先去加载这个类,而是把请求委派给父类请求加载器完成,因此所有加载请求最终都会送到顶层的启动类加载器,只有当父加载器反馈自己无法完成这个加载请求时(搜索范围中没有找到所需的类),子加载器才会尝试自己去加载。

Q:如何打破双亲委派模型?

A:打破双亲委派机制则不仅要继承ClassLoader类,还要重写loadClass和findClass方法。

Q:JVM内存各个组件分别有什么作用?

A:主要有:程序计数器、Java虚拟机栈、本地方法栈、堆、方法区

每个线程会有自己的方法栈及程序计数器,当线程调用一个方法,则栈中会压入一个方法对应的栈帧,此时栈顶栈帧为当前执行的方法,当方法执行完后,栈顶栈帧会出栈,通过程序计数器获取下一条字节码指令的地址,从而一步步执行。

堆是被所有线程共享的一块内存区域,对象实例会被放入堆中,并通过垃圾回收器对堆中的对象进行内存回收

方法区也是被所有线程共享的一块内存区域,存储着虚拟机加载的类信息、常量池、静态变量等数据

Q:栈帧由什么组成?

A:栈帧由局部变量表、操作数栈、方法返回地址组成

局部变量表:存放方法参数和方法内部定义的局部变量。

操作数栈:用于存放方法执行过程中,字节码指令进行入栈和出栈操作时,写入和提取的内容。

方法返回地址:方法退出之后,需要返回到方法被调用的位置,程序才能继续执行。

Q:谈谈堆GC?

A:堆GC分为两步,第一步是根据算法判断对象是否还存活,第二步根据算法对可回收的内存区域进行清理

Q:怎么判断对象是否还存活

A:常用的两种算法:引用计数算法、可达性分析算法

引用计数算法:给对象添加一个引用计数器,如果有一个地方引用它,计算器值就加1,当引用失效,则计数器值减1。当计数器值为0时,对象就是不可能再被使用的,则可以被回收。但引用计数算法没办法解决相互循环引用的问题,例如objA.instance = objB; objB.instance = objA; 将objA = null; objB = null,此时objA和objB都不可能被访问了,但是objA和objB相互引用对方,两者的计数器值都为1,没办法被回收,故java虚拟机没使用该算法

可达性分析算法:通过一系列“GC Roots”的对象作为起始点,从这些节点往下搜索,若从GC Roots到这个对象不可达,即GC Roots没有任何引用链相连到该对象,则此对象是不可用的。java虚拟机使用该算法判断对象是否存活

Java中可以作为GC Roots的对象(两栈两方法)

  1. 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。
  2. 方法区中的类静态属性引用的对象。
  3. 方法区中常量引用的对象。
  4. 本地方法栈中JNI引用的对象。

Q:有哪些内存清理算法?各有什么优势?

A:

1、标记-清除算法
算法原理:最基础的算法,分为标记和清除两个阶段,首先根据判断对象是否存活的算法,标记出所有可回收的对象,接着将标记的对象进行统一回收。

算法缺陷:标记清除后,会产生大量不连续的内存碎片,导致后续如需分配大对象时,无法找到足够的连续内存

2、标记-复制算法
算法原理:将内存分为同等大小的两块,每次只使用一块,当这块内存用完时,将存活的对象复制到另外一块上面,再把原来这块的内存空间一次清理掉。这样内存分配时就不用考虑内存碎片等复杂问题。

算法缺陷:算法将内存缩小为原来的一半,空间使用率低

3、标记-整理算法
算法原理:标记过程与“标记-清除”算法一样,后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存

算法缺陷:整理阶段耗时长,存在效率问题

4、分代收集算法
该算法是当前商业虚拟机都采用的垃圾收集算法

算法原理:根据对象存活周期的不同将内存划分为几块,如Java堆分为新生代和老年代。根据各个年代的特点采用适用的收集算法,在新生代,因为每次垃圾收集都会有大批对象死去,故选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。老年代则因为对象存活率高,则使用“标记-清理”或者“标记-整理”算法来进行回收。

Q:JVM提供了哪些垃圾收集器?优势和使用场景是什么?

A:目前线上常用的有:新生代ParNew+老年代CMS、G1

新生代垃圾收集器:

1、Serial收集器

  • 特点:单线程收集器,只会使用一个CPU或一条收集线程去完成垃圾收集,且进行垃圾收集时,须暂停其他所有的工作线程(STW),直到收集结束。使用“复制”算法
  • 使用场景:适合单核机器,一般不会使用该收集器

2、ParNew收集器

  • 特点:Serial收集器的多线程版本
  • 使用场景:除了Serial收集器外,只有它能与老年代的CMS收集器配合工作。故老年代在使用CMS收集器时,新生代只能使用Serial与ParNew收集器,单CPU环境下Serial效果更好,随着CPU增加,则ParNew收集器会有很高的效率

3、Parallel Scavenge收集器

  • 特点:与其他收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间不同,该收集器的目标则是达到一个可控制的吞吐量,如虚拟机运行100分钟,其中垃圾收集耗费一分钟,则吞吐量就是99%。该收集器通过提供参数用于精确控制吞吐量。
  • 使用场景:用于控制具体吞吐量的新生代收集器

老年代垃圾收集器:

1、Serial Old收集器

  • 特点:单线程收集器,Serial收集器的老年代版本,使用“标记-整理”算法
  • 使用场景:CMS运行期间,如果预留的内存无法满足,出现“Concurrent Mode Failure”,虚拟机此时会启用Serial Old收集器进行老年代收集

2、Parallel Old收集器

  • 特点:Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法
  • 使用场景:用于控制具体吞吐量的老年代收集器

3、CMS收集器

  • 特点:以获取最短回收停顿时间为目标的收集器,基于“标记-清除”算法
  • 使用场景:重视服务的响应速度的应用,如B/S系统的服务端

不分代的垃圾收集器:

1、G1收集器

2、ZGC收集器

3、Shenandoah收集器

Q:什么是三色标记法?

A:像CMS、G1这类可以与用户线程并发进行的垃圾收集器,是使用一种叫做三色标记法的方式,去标记内存中存活和需要回收的对象

算法:三色标记法将对象分为三个颜色

  • 白色:刚开始遍历的时候所有对象都是白色的
  • 灰色:被垃圾回收器访问过,但至少还有一个引用未被访问
  • 黑色:被垃圾回收器访问过,并且这个对象的所有引用都被访问过,是安全存活的对象(GC ROOT会被标记为黑色)

Q:三色标记法存在什么问题?JVM如何解决?

A:错标问题:因并发标记过程,用户线程和标记线程是同时进行的,用户线程可以更改对象引用关系,此时如对象已被标记为黑色,但该对象引用一个被断开引用的白色对象,此时该白色对象无法被扫描。

JVM提供了两种方法解决该问题

1、增量标记:当黑色对象引用白色对象时,将黑色对象改为灰色对象,下次重新进行扫描,通过写后屏障,记录了所有新增引用。CMS采用该方法

2、原始快照:当灰色对象断开白色对象时,将其记录起来,扫描则是对记录的快照进行扫描,通过写前屏障,记录了所有被删前的引用关系。G1采用该方法

Q:什么是跨代引用问题?JVM如何解决?

A:跨代引用:在进行新生代GC时,新生代对象可能被老年代应用。

JVM引入一个新的结构:卡表(cardTable),卡表将老年代划分为多个区(card),如果某个区中的对象有引用指向新生代,则将其标记为dirty-card,卡表存在在新生代中,当新生代发生GC时,只需要去扫描老年代中被标记为dirty-card的区域即可。

Q:CMS是如何进行垃圾收集的?

A:Cms将耗时长的步骤与用户线程并发进行,从而大大减少了STW时间,其回收总共分四步

  1. 初始标记:标记GCRoots所直接关联的对象,该阶段耗时短但需要STW
  2. 并发标记:用三色标记法对对象进行标记,该阶段耗时长但可以与用户线程并发进行
  3. 重新标记:并发标记过程中,对象状态可能发生变化,此时需要对这些对象进行重新标记,该阶段耗时短但需要STW
  4. 并发清除:将标记的垃圾对象清除,该阶段耗时长但可以与用户线程并发进行

Q:G1是如何进行垃圾收集的?

A:

Q:JVM如何调优?

A:

Q:JVM有哪些参数?如何确定参数值?

A:一、栈参数设置

通过-Xss可以设置每个线程的栈大小,该值过小则会容易引起StackOverFlowError,该值过大则会使线程占用内存增多。默认设置为1M。

二、堆参数设置

通过-Xmx设置堆最大内存,-Xms设置堆初始内存,该值过小容易引起OOM。一般设置为老年代存活对象的3-4倍。

Q:JVM发生大量的GC如何处理?

A:

Q:JVM死锁分析?

A:

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值