JVM脑图
2021-01-19 20:55:20 8 举报
AI智能生成
JVM虚拟机脑图不断更新
作者其他创作
大纲/内容
对象的创建过程
类加载检查
分配内存
指针碰撞
空闲列表
解决并发问题的方法
CAS配上失败重试
本地线程分配缓冲
初始化
设置对象头
执行<init>方法
对象内存分配
对象栈上分配
标量替换
逃逸分析
栈上分配依赖于逃逸分析和标量替换
对象在Eden区分配
Minor GC/Young GC
Major GC/Full GC
Eden与Survivor区默认8:1:1
大对象直接进入老年代
长期存活的对象将进入老年代
对象动态年龄判断
老年代空间分配担保机制
对象内存回收
引用计数法
可达性分析算法
常见引用类型
强引用
软引用
弱引用
虚引用
finalize()方法
finalize()能做的所有工作, 使用try-finally或者其他方式都可以做得更好、更及时
如何判断一个类是无用的类
类所有的对象实例都已经被回收,也就是 Java 堆中不存在该类的任何实例
加载该类的 ClassLoader 已经被回收。
该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
垃圾收集算法
分代收集理论
根据对象存活周期的不同将内存分为几块
一般将java堆分为新生代和老年代
标记-复制算法
内存分为大小相同的两块
这一块的内存使用完后,就将还存活的对象复制到另一块去
再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收
标记-清除算法
标记存活的对象
统一回收所有未被标记的对象
带来的问题
效率问题 (如果需要标记的对象太多,效率不高)
空间问题(标记清除后会产生大量不连续的碎片)
标记-整理算法
根据老年代的特点
标记过程仍然与“标记-清除”算法一样
后续步骤不是直接对可回收对象回收
让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
类加载器
扩展类加载器
应用程序类加载器
JVM默认
自定义加载器
类加载注意
JVM双亲委派机制
简单解释:先找父亲加载,不行再由儿子自己加载
为什么要设计双亲委派机制?
沙箱安全机制
避免类的重复加载
为什么是由应用程序加载器向上查找呢?
我们的程序90%都是由应用程序类加载器去加载
第一次加载的时候确实会走一遍全流程,但以后直接加载到应用程序加载器里了,以后直接用就行了
JVM内存参数
-Xss:每个线程的栈大小
-Xms:初始堆大小,默认物理内存的1/64
JVM 的 Xms 和 Xmx 设置一样大小的内存容量,避免在 GC 后调整
堆大小带来的压力
堆大小带来的压力
-Xmx:最大堆大小,默认物理内存的1/4
-Xmn:新生代大小
-XX:NewSize:设置新生代初始大小
-XX:NewRatio:默认2,表示新生代占整个堆内存的1/3。
-XX:MaxMetaspaceSize:元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。
-XX:MetaspaceSize|:空间触发Fullgc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M
一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大,对于8G物理内存的机器来说,一般我会将这两个值都设置为256M。
垃圾收集器
Serial收集器(-XX:+UseSerialGC -XX:+UseSerialOldGC)
新生代采用复制算法,老年代采用标记-整理算法。
它的 “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作
在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" ),直到它收集结束
Serial Old收集器是Serial收集器的老年代版本
作为CMS收集器的后备方案
Parallel Scavenge收集器
新生代采用复制算法,老年代采用标记-整理算法。
Serial收集器的多线程版本
关注点是吞吐量(高效率的利用CPU
Parallel Old收集器是Parallel Scavenge收集器的老年代版本
JDK8默认的新生代和老年代收集器
Parallel Scavenge
Parallel Old
ParNew收集器(-XX:+UseParNewGC)
新生代采用复制算法,老年代采用标记-整理算法。
ParNew收集器其实跟Parallel收集器很类似
区别主要在于它可以和CMS收集器配合使用
CMS收集器(-XX:+UseConcMarkSweepGC(old))
关注点更多的是用户线程的停顿时间
一种以获取最短回收停顿时间为目标的收集器
第一次实现了让垃圾收集线程与用户线程(基本上)同时工作
过程
初始标记
并发标记
重新标记
并发清理
并发重置
优点:并发收集、低停顿
缺点:
对CPU资源敏感(会和服务抢资源)
无法处理浮动垃圾
标记-清除”算法会导致收集结束时会有大量空间碎片产生
concurrent mode failure
执行过程中的不确定性,一边回收,一边发现没有空间了
特别是在并发标记和并发清理阶段会出现
会进入stop the world,用serial old垃圾收集器来回收
G1收集器
简介
主要针对配备多颗处理器及大容量内存的机器
极高概率满足GC停顿时间要求
高吞吐量性能特征
G1将Java堆划分为多个大小相等的独立区域(Region)
一般Region大小等于堆大小除以2048,比如堆大小为4096M,则Region大小为2M
一个Region可能之前是年轻代,如果Region进行了垃圾回收,之后可能又会变成老年代,也就是说Region的区域功能可能会动态变化
大对象的处理
G1有专门分配大对象的Region叫Humongous区
在G1中,大对象的判定规则就是一个大对象超过了一个Region大小的50%
比如按照上面算的,每个Region是2M,只要一个大对象超过了1M,就会被放入Humongous中
一个大对象如果太大,可能会横跨多个Region来存放。
Full GC的时候除了收集年轻代和老年代之外,也会将Humongous区一并回收。
G1收集特点
简述
G1收集器在后台维护了一个优先列表
每次根据允许的收集时间,优先选择回收价值最大的Region(这也就是它的名字Garbage-First的由来)
比如一个Region花200ms能回收10M垃圾,另外一个Region花50ms能回收20M垃圾
在回收时间有限情况下,G1当然会优先选择后面这个Region回收
空间整合
可预测的停顿
通常把期望停顿时间设置为一两百毫秒或者两三百毫秒会是比较合理的
如果我们把停顿时间调得非常低, 譬如设置为二十毫秒, 很可能出现的结果就是由于停顿目标时间太短
导致每次选出来的回收集只占堆内存很小的一部分, 收集器收集的速度逐渐跟不上分配器分配的速度, 导致垃圾慢慢堆积
最终占满堆引发Full GC反而降低性能
分代收集
并行与并发
垃圾收集器总结
JVM调优工具
Jmap
此命令可以用来查看内存信息,实例个数以及占用内存大小
jmap -histo 14660 #查看历史生成的实例
jmap -histo:live 14660 #查看当前存活的实例,执行过程中可能会触发一次full gc
查看堆信息
jmap -heap 14620
堆内存dump
jmap -dump:format=b,file=my.hprof 14660
Jstack
用jstack加进程id查找死锁
jstack找出占用cpu最高的线程堆栈信息
使用命令top -p <pid> ,显示你的java进程的内存情况,pid是你的java进程号
按H,获取每个线程的内存情况
找到内存和cpu占用最高的线程tid,比如19664
转为十六进制得到 0x4cd0,此为线程id的十六进制表示
执行 jstack 19663|grep -A 10 4cd0
远程连接jvisualvm
Jinfo
Jstat
jstat [-命令选项] [vmid] [间隔时间(毫秒)] [查询次数]
注意:使用的jdk版本是jdk8
注意:使用的jdk版本是jdk8
jstat -gc pid 最常用,可以评估程序内存使用及GC压力整体情况
给 JVM 环境参数设置-XX:+HeapDumpOnOutOfMemoryError 参数,让 JVM 碰到
OOM 场景时输出 dump 信息。
OOM 场景时输出 dump 信息。
0 条评论
下一页