JVM面试题
JVM内存模型
共享内存
虚拟机栈
java方法,存放局部变量
栈桢,每个方法从调用到运行结束的过程,对应一个栈桢从压栈到出栈
局部变量表
操作数栈
本地方法栈
native方法
程序计数器
唯一一个不会OOM的区域
当前线程执行字节码的行号指示器,确保上下文切换后能恢复到正确位置
私有内存
堆
new出来的对象
方法区
1.8 元空间
静态变量、常量、类信息、运行时常量池
JVM垃圾回收
回收堆内存
确认垃圾
引用计数法
对象是否有被引用,引用+1,失去引用-1,对象引用数为0,表示对象可被回收
缺点:存在循环依赖
可达性分析
选取GCRoots作为根节点,对象到根节点不可达,表示可被回收
GCRoots:
- 虚拟机栈中局部变量表
- 本地方法栈中native方法
- 方法区中类静态属性
- 方法区中常量
垃圾回收算法
标记-清除(老年代)
标记可被回收的对象,标记完成后清除
缺点:效率低;产生不连续的内存碎片,容易导致大对象找不到空间存储
复制(新生代)
将内存分为等分的两块,每次只使用其中一块,内存满后将存活对象复制到另一块,已使用内存清除
优点:不会产生碎片
缺点:每次只使用1/2,浪费内存空间;存活对象多,复制效率慢
标记-整理(老年代)
将存活对象压缩到内存一端,清除边界外对象
优点:不会浪费内存,也不会产生碎片
缺点:耗时
分代收集
根据对象存活的不同生命周期,将内存划分为不同的域,分为新生代和老年代
新生代:存活对象少,GC频繁
老年代:存活对象多
采用复制算法,按8:1:1的比例划分为Eden、From Survivor、To Survivor, 每次只使用Eden和From Survivor,将存活对象复制到To Survivor,存活对象年龄+1,默认年龄到15移到老年代,或者To Survivor内存不足无法存储,会直接移到老年代
分区收集
堆划分为连续的不同小区间,独立使用和回收,可减少停顿时间
垃圾收集器
Serial
复制,单线程,1.3之前唯一的垃圾收集器
Par New
复制,Serial的多线程版本
Parallel Scaverge
复制,多线程,自适应
Serial Old
标记整理,单线程,1.5之前唯一的垃圾收集器
Parallel Old
标记整理,多线程,自适应
CMS
标记清除,多线程,获取最短停顿时间
- 初始标记
暂停工作线程,标记 - 并发标记
不需要暂停,耗时长 - 重新标记
暂停工作线程,修复并发标记产生的变动 - 并发清除
不需要暂停,耗时长
G1
标记整理,分区收集
划分区域,跟踪进度,后台维护优先级列表,优先回收垃圾最多的区域
CPU过高排查
- 查询cpu高的进程pid
top - 查询cpu高的线程id
top -H -p pid - 将线程id转换16进制
printf “%x\n” tid - 查询具体代码位置
jstack tid | grep 16进制
调优参数
停顿时间,-XX:MaxGCPauseMillis
吞吐量,垃圾收集的时间和总时间的占比:1/(1+n),吞吐量为1-1/(1+n)。-XX:GCTimeRatio=n
-Xmn:新生代大小
-Xms初始堆大小,-Xmx最大堆大小,这两个参数一般设置为相同,Xms与Xmx相差太大会让堆产生弹性压缩,内存的占用会从Xms开始,内存不够,会往外扩一部分内存,这部分内存又占满后,会再次往外扩,超过Xmx后,会产生OutOfMemory,内存回收后,内存又会往回缩,对系统性能产生影响