文章目录
前言
面试过程中不免被问到 JVM 相关知识,总结了JVM一些经典面试题,分享出我自己的解题思路,希望对大家有帮助,有哪里你觉得不正确的话,欢迎指出,后续有空会更新。
想跳槽涨薪,面试怎么能少了他呢。
1.JVM的运行时数据区有哪些?
运行时数据区的定义:Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数 据区域。
根据 JVM规范,标准的JVM运⾏时数据区包括以下部分:
- 程序计数器
- Java虚拟机栈
- 本地方法栈
- 堆内存
- 方法区
- 运行时常量池 具体的JVM实现可根据实际情况进行优化或者合并,满足规范的要求即可。 比如:Hotspot JVM就把本地方法栈和虚拟机栈合二为一的进行了实现。
2.什么是堆内存?堆内存包含哪些部分?
堆是 JVM 上最⼤的内存区域,我们申请的几乎所有的对象,都是在这里存储的。
以Hotspot为例,堆空间为了方便GC模块进行对象分配和回收,可以把堆空间进行以下划分:
- 新生代(Eden+s0+s1)
- 老年代
3.什么是内存溢出?
内存溢出(OOM- Out Of Memory)是指JVM可用内存不足。
JVM运行需要使用的内存超出最大可用值,会导致JVM出现异常。 常见的OOM有以下几种。
- 栈溢出
- 堆溢出
- 方法区溢出
- 本机直接内存溢出
4.什么是内存泄漏?与内存溢出有什么关系?
内存泄漏(Memory Leak)是指本来无用的对象却继续占用内存,没有再恰当的时机释放占用的内存。 不使用的内存,却没有被释放,这个就叫做内存泄漏。
比较典型的场景是: 每一个请求进来,或者每一次操作处理,都分配了内存,却有一部分不能回收(或
未释放),那么随着处理的请求越来越多,内存泄漏也就越来越严重。
与内存溢出的关系!
如果存在严重的内存泄漏问题,随着时间的推移,则必然会引起内存溢出。
内存泄漏一般是资源管理问题和程序BUG,内存溢出则是内存空间不足和内存泄漏的最终结果。
5.你知道哪几种垃圾收集器,各自的优缺点,重点讲下cms和G1,包括原理,流程,优缺点。
几种垃圾收集器:
- Serial收集器: 单线程的收集器,收集垃圾时,必须stop the world,使用复制算法。
- ParNew收集器: Serial收集器的多线程版本,也需要stop the world,复制算法。
- Parallel Scavenge收集器: 新生代收集器,复制算法的收集器,并发的多线程收集器,目标是达到一个可控的吞吐量。如果虚拟机总共运行100分钟,其中垃圾花掉1分钟,吞吐量就是99%。
- Serial Old收集器: 是Serial收集器的老年代版本,单线程收集器,使用标记整理算法。
- Parallel Old收集器: 是Parallel Scavenge收集器的老年代版本,使用多线程,标记-整理算法。
- CMS(Concurrent Mark Sweep) 收集器: 是一种以获得最短回收停顿时间为目标的收集器,标记清除算法,运作过程:初始标记,并发标记,重新标记,并发清除,收集结束会产生大量空间碎片。
- G1收集器: 标记整理算法实现,运作流程主要包括以下:初始标记,并发标记,最终标记,筛选标记。不会产生空间碎片,可以精确地控制停顿。
CMS收集器和G1收集器的区别
- CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用;
- G1收集器收集范围是老年代和新生代,不需要结合其他收集器使用;
- CMS收集器以最小的停顿时间为目标的收集器;
- G1收集器可预测垃圾回收的停顿时间
- CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片
- G1收集器使用的是“标记-整理”算法,进行了空间整合,降低了内存空间碎片。
6.常用的JVM启动参数有哪些?
在绝大部分业务场景下,常用的JVM配置参数也就10来个
7.设置堆空间的最大值(-Xmx)应该要考虑哪些因素?
需要根据系统的配置来确定,要给操作系统和JVM其他内存区域(栈、方法区)留下一定的剩余空间。 推荐配置系统或容器里可用内存的 70%~80%最好。
假设物理内存是8G,设置多大堆内存比较合适?
系统有 8G 物理内存,系统自己可能会用掉一点,大概还有 7.5G 可以用。
那么建议配置-Xmx6g (7.5g*0.8=6g)
8.什么是STW?什么是安全点,什么是安全区域?
因为GC过程中,所有应用线程都需要暂停之后才能执行GC,这时候就称为STW,或者叫做GC暂停。
安全点
用户线程暂停,GC线程要开始⼯作,但是要确保用户线程暂停的这行字节码指令是不会导致引用关系的 变化。所以JVM会在字节码指令中,选一些指令,作为“安全点”,比如方法调用、循环跳转、异常跳转 等,一般是这些指令才会产生安全点。
为什么它叫安全点,是这样的,GC时要暂停业务线程,并不是抢占式中断(立马把业务线程中断)而是 主动是中断。
主动式中断是设置一个标志,这个标志是中断标志,各业务线程在运⾏过程中会不停的主动去轮询这个 标志,一旦发现中断标志为True,就会在自己最近的“安全点”上主动中断挂起。
安全区域
为什么需要安全区域? 要是业务线程都不执行(业务线程处于Sleep或者是Blocked状态),那么程序就没办法进入安全点,对于这种情况,就必须引入安全区域。
安全区域是指能够确保在某一段代码片段之中,引用关系不会发生变化,因此,在这个区域中任意地⽅ 开始垃圾收集都是安全的。我们也可以把安全区城看作被扩展拉伸了的安全点。
当用户线程执⾏到安全区域里面的代码时,首先会标识自己已经进入了安全区域,这段时间里JVM要发 起GC就不必去管这个线程了。 当线程要离开安全区域时,它要JVM是否已经完成了(根节点枚举,或者其他GC 中需要暂停用户线程 的阶段)
1、如果完成了,那线程就当作没事发生过,继续执行。
2、否则它就必须一直等待,直到收到可以离开安全区域的信号为止。
9.如果CPU使用率突然飙升,你会怎么排查?
1、先通过top命令找到消耗cpu很高的进程id(假设是2732)
top 命令是我们在 Linux 下最常用的命令之一,它可以实时显示正在执⾏进程的 CPU 使用率、内存使用率以及系统负载等信息。其中上半部分显示的是系统的统计信息,下半部分显示的是进程的使用率统计信息
2、执行top -p 2732单独监控该进程
3、在第2步的监控界面输入H,获取当前进程下的所有线程信息
4、找到消耗cpu特别高的线程编号(假设是2734)
5、执⾏jstack 2732对当前的进程做dump,输出所有的线程信息
同时将第4步得到的线程十进制编号2734转成16进制(AAE),在堆栈信息里面去找对应线程内容
6、最后解读线程信息,定位具体代码位置
10.判断一个对象应该被回收
1.该对象没有与GC Roots相连
2.该对象没有重写finalize()方法或finalize()已经被执行过则直接回收(第一次标记)、否则将对象加入到F-Queue队列中(优先级很低的队列)在这里finalize()方法被执行,之后进行第二次标记,如果对象仍然应该被GC则GC,否则移除队列。 (在finalize方法中,对象很可能和其他 GC Roots中的某一个对象建立了关联,finalize方法只会被调用一次,且不推荐使用finalize方法)
11.回收方法区
方法区回收价值很低,主要回收废弃的常量和无用的类。
如何判断无用的类:
1.该类所有实例都被回收(Java堆中没有该类的对象)
2.加载该类的ClassLoader已经被回收
3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方利用反射访问该类
12.垃圾收集算法
GC最基础的算法有三种: 标记 -清除算法、复制算法、标记-压缩算法,我们常用的垃圾回收器一般都采用分代收集算法。
- 标记 -清除算法,“标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
- 复制算法,“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
- 标记-压缩算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
- 分代收集算法,“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
13.GC日志分析
摘录GC日志一部分(前部分为年轻代gc回收;后部分为full gc回收):
2016-07-05T10:43:18.093+0800: 25.395: [GC [PSYoungGen: 274931K->10738K(274944K)] 371093K->147186K(450048K), 0.0668480 secs] [Times: user=0.17 sys=0.08, real=0.07 secs]
2016-07-05T10:43:18.160+0800: 25.462: [Full GC [PSYoungGen: 10738K->0K(274944K)] [ParOldGen: 136447K->140379K(302592K)] 147186K->140379K(577536K) [PSPermGen: 85411K->85376K(171008K)], 0.6763541 secs] [Times: user=1.75 sys=0.02, real=0.68 secs]
通过上面日志分析得出,PSYoungGen、ParOldGen、PSPermGen属于Parallel收集器。其中PSYoungGen表示gc回收前后年轻代的内存变化;ParOldGen表示gc回收前后老年代的内存变化;PSPermGen表示gc回收前后永久区的内存变化。young gc 主要是针对年轻代进行内存回收比较频繁,耗时短;full gc 会对整个堆内存进行回城,耗时长,因此一般尽量减少full gc的次数。
14.调优命令
Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo
- jps,JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
- jstat,JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
- jmap,JVM Memory Map命令用于生成heap dump文件
- jhat,JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看
- jstack,用于生成java虚拟机当前时刻的线程快照。
- jinfo,JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。
15.调优工具
常用调优工具分为两类,jdk自带监控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。
- jconsole,Java Monitoring and Management Console是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控
- jvisualvm,jdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等。
- MAT,Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗
- GChisto,一款专业分析gc日志的工具
16.Minor GC与Full GC分别在什么时候发生?
新生代内存不够用时候发生MGC也叫YGC,JVM内存不够的时候发生FGC
17.你知道哪些JVM性能调优
- 设定堆内存大小
-Xmx:堆内存最大限制。
- 设定新生代大小。 新生代不宜太小,否则会有大量对象涌入老年代
-XX:NewSize:新生代大小
-XX:NewRatio 新生代和老生代占比
-XX:SurvivorRatio:伊甸园空间和幸存者空间的占比
- 设定垃圾回收器 年轻代用 -XX:+UseParNewGC 年老代用-XX:+UseConcMarkSweepGC
最后
祝大家工作顺利,升职加薪,面试都能通过,offer拿到手软,早日找到心仪的工作~
给大家一个小福利:2022年最新面试知识点大全
这份pdf已经给大家整理好,点开下面小卡片,扫码添加微信(备注:锦鲤)即可领取
版权归原作者 程序猿锦鲤 所有, 如有侵权,请联系我们删除。