JVM知识点
2021-08-17 22:08:21 0 举报
AI智能生成
JVM面试知识点
作者其他创作
大纲/内容
记录线程当前执行到的行号
程序计数器
每个方法调用,就会在当前线程的栈中压入一个栈帧,当压入的栈帧过多时,就会出现StackOverflowError
虚拟机栈
与虚拟机栈类似,区别在于本地方法栈是在线程调用native方法时,压入栈帧
本地方法栈
类型
存放方法中定义的局部变量值,8个基本类型和1个引用类型,引用类型存地址指针
局部变量表
与局部变量表交互,通过压栈、弹栈的方式对操作数做运算
操作数栈
将运行时才能确定的对象类型连接起来,如多态、泛型
动态链接
即方法退出时,线程应该继续到哪个位置执行代码
方法返回地址
栈帧的组成
栈
线程私有
存储初始化后的对象、数组等
堆
存储类信息,static的变量和常量,编译后的代码(字节码),Java8以前就是永久代,Java8以后在元空间中
方法区
线程共享
运行时数据区
加载->验证->准备->解析->初始化->使用->卸载
1. 通过类的完全限定名获取二进制字节流
2. 将二进制字节流转换成方法区的数据结构
3. 实例化一个Class对象放入堆中,作为访问的入口
加载
保证字节流不会对虚拟机造成危害
验证
给类变量分配内存空间,并初始化默认值
准备
符号引用:用符号来描述的类
直接引用:直接指向类的内存地址
将符号引用转换为直接引用
解析
链接
为静态变量赋值,并执行静态代码块
初始化
加载过程
将类文件加载到运行时数据区
作用
加载Java的核心类库, jre/lib/rt.jar
引导类加载器:BootStrapClassLoader
加载Java的扩展类库,jre/lib/*.jar
扩展类加载器:ExtensionClassLoader
加载ClassPath下的指定类库,比如三方jar包
应用类加载器:AppClassLoader
实现某种特殊功能的自定义类加载器
自定义类加载器:CustomerClassLoader
按照优先级加载类,避免核心API被篡改,保证类加载的安全
重写loadClass方法可以破坏双亲委派
如何破坏
双亲委派模型
类加载器
类加载机制
新创建的对象会默认分配的eden区,当eden区满了之后就会触发Minor GC,对新生代做一次垃圾回收
在eden区中没有被回收掉的数据会被复制到survivor区中,然后将eden清空
eden
将survivor划分成s1和s2,是通过多使用一块内存空间的方式来解决内存碎片问题
与eden类似,Minor GC后,会将s1中的存活对象复制到s2中,然后清空s1。然后下次GC会从s2复制到s1,清空s2,以此类推
survivor1/survivor2
eden与s1、s2的比例默认值为8:1:1
新生代
新生代中存活超过一定次数的对象会进入老年代,默认是15次
大对象直接进入老年代(这个判断标准由JVM参数配置)
动态年龄判断:survivor中的某个年龄的对象年龄总和大于所有对象总和的一半,就将所有大于等于这个年龄的对象移入老年代
对象什么时候进入老年代
老年代空间不足会触发Major GC,并同时触发Minor GC,两者连起来就叫做Full GC
老年代
分代模型
每个对象都有一个引用计数器,每多一个地方引用它计数器就+1,每少一个地方引用它,计数器就-1,计数器为0就表示可以回收
引用计数法无法解决互相引用导致的垃圾抱团问题
引用计数法
以GC Roots作为起始点往下一层一层的搜索,形成一条条引用链。当一个对象没有引用链,就可以被标记为垃圾
虚拟机栈本地变量表引用的对象
本地方法栈JNI引用的对象
静态变量或静态常量引用的对象
线程对象
GC Roots有哪些
自己和被自己引用的对象都检查过
黑色
自己被检查过,自己引用的对象没有被检查过
灰色
没有被检查过
白色
三种颜色的对象
1.将GC Roots标记为灰色
2.检查GC Roots引用的对象,标记为灰色,并将GC Roots标记为黑色
3.检查灰色对象引用的对象,将其引用的对象标记为灰色,检查完后自身标记为黑色
4.重复第三步,直到没有灰色对象
5.剩下的所有白色节点都是垃圾
标记流程
CMS或G1的并发标记阶段,用户线程和GC线程并发运行
出现原因
已经被标记为黑色或灰色的节点,被断开了引用,成为垃圾,单本次不能被回收。
浮动垃圾
灰色节点断开了对白色节点的引用,恰好有黑色节点对同一个白色节点建议了引用,这种情况,白色节点会被当作垃圾,显然是不合理的
CMS采用增量更新,重新将黑色节点标记为灰色节点
G1会将灰色节点对白色节点的引用加入栈中,让白色节点可以继续被扫描到
解决方式
漏标问题
可能出现的问题
三色标记算法
可达性分析
确认垃圾对象
将内存划分为同样大小的两块,每次将存活的对象复制到另一块中,然后将待回收的内存区域直接清空。
是一种以空间换时间的算法,优点是内存回收快且不会有空间碎片,缺点就是的需要浪费一半的空间
复制算法
分为两个阶段,第一阶段标记垃圾,第二阶段清除垃圾,会出现内存碎片
标记清除算法
也分为两个阶段,第一阶段标记垃圾,第二阶段做整理,将存活的对象往一端移动,整理完成后将边界外的垃圾对象全部清理
标记整理算法
垃圾回收算法
只有一个GC线程工作,用户线程暂停
单线程收集器
多个GC线程并行工作,但此时的用户线程暂停
并行收集器
用户线程与GC线程一同工作
并发收集器
并行与并发
用户线程暂停所用的时间
停顿时间
用户线程运行时间的比例,即:用户线程运行时间 /(用户线程运行时间+垃圾收集时间)
吞吐量
指垃圾回收的时候,会将所有的用户线程暂停,感觉就像世界停止了一样
STW
基本概念
单线程收集器,使用复制回收算法
serial
并行收集器,serial的多线程版本,使用复制回收算法
ParNew
简称PS,是一种并行收集器,可以配置停顿时间和吞吐量
在配置了停顿时间和吞吐量及-Xmx后不需要去配置新生代老年代的比例,PS会根据运行情况自行调整
Parallel Scavenge
新生代垃圾回收器
与serial对应的老年代收集器,单线程,使用标记整理算法
serial Old
简称PO,PS的老年代版本,并行收集器,使用标记整理算法
Parallel Old
采用标记清除算法的并发垃圾收集器,实现了垃圾线程与用户线程并发执行,注重停顿时间,适合互联网或的服务端B/S架构
初始标记:找GC Roots对象,这一步运行的很快,会STW
并发标记:从GC Roots向下寻址,这一步和用户线程并发执行
重新标记:主要对并发标记过程中产生变动的对象做标记,这一步是并行执行的,会STW
并发清理:对标记的垃圾做清理,这一步与用户线程并发
垃圾回收的几个阶段
并发清理垃圾,减少停顿时间
优点
并发清理时的GC线程占用了一定的用户线程资源
采用标记清除算法,会产生内存碎片
无法处理标记阶段后产生的浮动垃圾
缺点
优缺点
CMS无法整理碎片,它的碎片整理是通过Serial Old来做的,这个过程非常耗时,如果在用户使用高峰的时候做碎片整理,那将是灾难性的后果,对此可以采用在夜深人静的时候,使用定时脚本手动触发垃圾整理,降低在白天的使用高峰整理内存的概率
CMS
老年代垃圾回收器
是一种同时具备低延时和高吞吐的并发垃圾收集器
物理上不再使用以前的新生代、老年代划分,而是使用一个个的region对整块内存做逻辑上的区分
G1对CPU和内存的要求比较高
概念
一共有2048个Region,每个Region的大小为1M至32M
Region的实际大小 = JVM分配的内存大小/2048
Eden
Survivor
Old
存放大对象
Humongous
Region的类型
Eden和Survivor的比例依然是8:1:1,新生代与老年代的占比是动态调整的
Region
初始标记:找到GC Roots直接引用的对象,STW
并发标记:从GC Roots向下寻址,标记存活对象
最终标记:修改并发标记阶段由用户线程运行导致变动的对象,STW
筛选回收:对回收价值进行排序,并根据期望停顿时间,回收最有价值的Region中的垃圾对象
GC过程
G1不再使用原始的分代模型,而是使用一个个的Region来保存对象
并行执行垃圾回收效率更高
G1的回收可以根据配置的停顿时间选择性的回收Region,不需要做全量回收
G1的回收阶段是并行执行的,CMS是并发执行的
与CMS的区别
根据配置的停顿时间做GC,会先计算本次GC需要的时间,如果远小于配置的停顿时间,会先增大Eden的占用比例,直到某次回收的时间接近配置的停顿时间时,才会做Young GC
Young GC
老年代对象达到需要回收的比例后就会执行,回收整个新生代和老年代,此时使用的是复制回收算法,如果复制时内存不够,就会Full GC
Mixed GC
使用单线程标记、整理内存,非常耗时
Full GC
回收模式
G1
分区模型回收器
内存小于100M时,不要多想,使用串行收集器
单核处理器且对停顿时间没有要求,串行收集器
关注吞吐量的跑后台任务的,使用并行收集器Parallel
互联网或B/S架构的服务端,并且服务器CPU的性能高,内存也比较大的情况可以使用CMS或G1
其他情况可以使用PSPO的组合
垃圾回收器的选择
垃圾回收器
垃圾回收
1. 运行出现错误,如OOM
2. 稳定性问题,慢或卡顿
JVM运行过程中的问题
根据不同的业务场景选择合适的垃圾收集器,并不断的调整JVM参数,使JVM处于一个流程且稳定的状态
调优目的
调的是什么
StackOverFlowError
栈溢出
堆内存溢出,分配对象大于了内存空间
OOM:Java heap space
GC开销超出限制,即GC时间占比大但是回收的垃圾占比却很小,导致GC频繁发生
OOM:GC overhead limit exceeded
服务器线程资源耗尽,不能再创建线程
OOM:unable to create new native thread
元空间溢出
OOM:Metaspace
堆溢出
1. 开启OOM时保存Dump文件,-XX:+HeapDumpOnOutOfMemoryError
2. 发生OOM后,获取Dump文件,放到Eclipse的MAT工具中分析
3. 查看Dump信息,获取到是代码哪个位置发生的OOM,然后去查看代码中的问题
OOM排查
OOM
堆中有不再使用的对象却不能被回收
内存泄露
1. 使用top查看任务管理器,shift + p按CPU使用率排序,找到使用率最高的PID,比如PID是11
2. 使用top -Hp 11,然后shift + p排序,找到CPU使用率最高的线程,比如PID为110
3. 将线程的PID转换为16进制,值为6E
4. 使用jstack -l 11 > java11.stack 导出栈信息到文件中
5. 使用 cat java11.stack | grep '0x6E'找到指定的线程,查看原因
Java消耗CPU资源过高
连接Java程序,观察实时内存情况
JvisualVM
分析Dump文件
MAT
性能检测工具
一般是堆太小,通过Xms和Xmx设置堆的最大值最小值
1. 分配大文件导致内存溢出
针对不同的并发压力,做集群处理,使用多态机器来分担一台及其的压力
2. 大并发条件下存活对象过多
排查引起内存泄露的代码,并修复它
3. 内存泄露引起的
内存溢出处理
调优实战
JVM调优
JVM
0 条评论
下一页