问:谈谈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;