菜单

Juning
发布于 2022-08-13 / 595 阅读
2
0

JVM相关的面试题

问:谈谈JVM内存模型(这个问题很大,需要结合面试上下文来回答是属于JVM内存模型结构还是解决并发问题的内存模型(JMM)):
答(内存分配):JVM内存模型分为五个区域,Java虚拟机栈(栈)、本地方法栈、堆、程序计数寄存器(PC寄存器)、方法区(元空间);各自的作用如下:
Java虚拟机栈(栈):也可以称为虚拟机线程栈,它是JVM中每个线程所私有的一块空间,每个线程都会有这么一块空间。它的生命周期是与线程的生命周期是绑定的。虚拟机栈描述了Java中方法执行时的内存模型,即每个方法被执行的时候,线程都会在自己的线程栈中同步创建一个栈帧(Stack Frame),用于存放局部变量表、操作数栈、动态连接和方法出口等信息,每个方法从调用到完成的过程,就对应着一个栈帧在线程栈中从入栈到出栈的过程。
本地方法栈:本地方法栈与虚拟机栈的作用是相似,不同的是虚拟机栈为JVM执行的Java方法服务,而本地方法栈为JVM调用的本地方法服务。HotSpot虚拟机直接把本地方法栈和虚拟机栈合二为一,这么做避免了要为不同的语言设计栈, 提高了虚拟机的性能。
堆:Java堆是随着虚拟机的启动而创建的,用于存放对象实例,所有的对象实例和数组都在堆内存分配,它被所有线程共享, Java堆是Java虚拟机管理的内存中最大的一块,也是垃圾回收器管理的主要区域,从内存回收的角度看,Java堆内存还可以被继续划分, 并且和具体的虚拟机实现有关。
程序计数寄存器(PC寄存器):它指向下一条指令的地址,程序靠它跑起来;每条线程都有自己的程序计数器,如果当前线程正在执行一个Java方法,它的计数器记录的是正在执行中Java虚拟机指令的地址。如果执行的是本地方法(比如系统的C语言函数),计数器中的值为空(Undefined);也正是因为它记录的是指令的地址,所以它占用的空间较少,Java虚拟机中也并没有规定这块区域有OOM(内存溢出)的情况。
方法区(元空间):用于存储被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码数据等,它是所有线程共享的,在JDK8及以上的版本中方法区被元空间所代替。

虚拟机规范中说方法区在逻辑上是堆的一部分,但它的别名为“non-Heap”非堆,它和堆也是两块独立的内存空间,至于说在逻辑上是堆区的一部分,是因为在物理实现上,方法区的内存地址包含于堆中,所以说是逻辑上的一部分,实际用的时候是完全不同的部分,这么设计可能是因为便于垃圾收集器统一管理;在JDK7及以下版本方法区也被称为永久代,因为类信息、常量、静态变量等元素很少几乎不被回收,但是这种实现方法区并不是最优解, 比如容易出现内存溢出问题;JDK8及以上的版本中方法区被元空间所代替,字符串、常量池和静态变量等被移至到了Java堆区,剩下的比如类信息是直接存在本地物理内存中,元空间也并不是JVM运行时数据区的一部分,其分配不会受Java堆大小的限制,也和虚拟机进程所分得Java堆内存无关。
答(并发):通常来说是一种虚拟机规范,用于屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果(后面再总结JMM)

问:JVM的数据区域有哪些?作用是什么?

这个问题同上面的JVM内存结构模型,包括PC寄存器、Java虚拟机栈、本地方法栈、方法区、堆、运行常量池等运行时数据区,也有可能是code cache(代码缓存)

答:PC寄存器、Java虚拟机栈、本地方法栈、方法区、堆、运行常量池等运行时数据区,code cache(代码缓存)

问:Java堆内存一定是线程共享的吗?
答:不一定,绝大多数情况一下是线程共享的,但JVM为了加快给对象分配内存的效率、在并发的情况下减少线程之间对于内存空间的竞争,JVM会在Eden区中的TLAB空间内给每个线程分配了一个私有缓存区域,也就是说在为对象分配内存的那一刻,这一块堆内存空间不是线程共享的。

当然,TLAB空间只占Eden区域的1%,在虚拟机中是默认开启的;

  • 通过jinfo -flag UseTLAB 线程号查看
  • 通过-XX:-UseTLAB关闭TLAB空间;
  • 通过-XX:TLABSize=512k设置TLAB空间的大小。

问:JVM堆内存结构是什么样的?哪些情况会触发GC?会触发哪些GC?
答:在JDK1.7之前JVM堆内存结构通常分为新生代,老年代和永久代,新生代又被区分为Eden区、S0和S1区;在JDK1.8之后永久代被元空间替代;新生代空间不够的时候会触发Minor GC/Young GC,老年代空间不够会触发Major GC/Old GC,在G1中也会触发整个新生代和部分老年代的混合回收Mixed GC,甚至有可能触发Full GC,具体和垃圾收集器有关。

Full GC是按照Young GC -> Young GC + Concurrent Marking(新生代 + 并发标) -> Mixed GC(混合回收)的顺序进行垃圾回收,也就是整个Java堆和方法区的GC;
目前经典的垃圾收集器有:

  • 串行回收器:Serial、Serial Old
  • 并行回收器:ParNew、Parallel Scavenge、Parallel old
  • 并发回收器:CMS、G1、ZGC

问:说一说JVM的垃圾回收
答:。。。

问:JVM的四种应用类型
答:强弱软虚

问:JVM回收算法和垃圾收集器
答:回收算法主要分为:标记清除、复制算法、分配担保、标记整理;垃圾收集器如下表:

垃圾收集器 分类 作用位置 使用算法 特点 适用场景
Serial 串行 新生代 复制算法 响应速度优先 适用于单CPU环境下的client模式
ParNew 并行 新生代 复制算法 响应速度优先 多CPU环境Server模式下与CMS配合使用
Parallel 并行 新生代 复制算法 吞吐量优先 适用于后台运算而不需要太多交互的场景
Serial Old 串行 老年代 标记-整理(压缩)算法 响应速度优先 适用于单CPU环境下的Client模式
Paraller Old 并行 老年代 标记-整理(压缩)算法 吞吐量优先 适用于后台运算而不需要太多交互的场景
CMS 并发 老年代 标记-清除算法 响应速度优先 适用于互联网或B/S业务
G1 并发、并行 新生代、老年代 标记-整理(压缩)算法 响应速度优先 响应速度优先

问:如何把Java内存的数据全部dump下来:
答:1:在应用启动时添加虚拟机-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=path参数
2:jmap -heap 命令(这个命令会挂起线程,生产环境需要注意)
3:一些调试工具必须JMC或者VisualVM都有dump按钮

问:jstack是干嘛的?jstat是干嘛的?
答:jstack是主要用来查看某个JVM进程的线程堆栈信息的;jstat是JVM统计监控工具,查看各个区内存和GC的情况。

问:在实际工作中如何定位问题?如何解决问题?说一下解决思路和处理方式
答:

问:CPU使用率过高怎么办?
答:

问:线上应用频繁full gc怎么处理?
答:

问:如果应用周期性的出现卡顿,你会怎么来排查问题?
答:

问:有没有遇到过OutOfMemory问题?你是怎么处理这些问题的?
答:

问:StackOverFlow异常有没有遇到过?这个异常会在什么时候触发?如何指定线程堆栈的大小?
答:一般是递归调用没有退出,或者循环调用造成StackOverFlow异常;堆栈溢出的时候出发;使用-XX:ThreadStackSize=size调整线程堆栈大小,也可简写为-Xss size;


评论