jvm思维导图
2022-01-19 20:33:39 0 举报
AI智能生成
脑图文档主要包括了jvm学习的思维导图,类加载机制,内存模型,内存分配机制,垃圾收集器,调优,常量池均有讲解
作者其他创作
大纲/内容
调优
优化原则
优化原则
1、尽可能的让对象在新生代里分配回收,避免太多对象进入老年代;
2、系统内存充足,避免新生代频繁垃圾回收;
3、尽量消除朝生夕死的对象导致的full gc
4、自己总结找出项目中的朝生夕死的对象和长期存活的对象,尽量让他们呆在自己的位置最好,朝生夕死的在新生代,长时间存活的在老年代、
具体优化思路
最大的元空间值与初始的元空间值设置一致,8g的物理机一般设置两个值都是 256M
新生代的最大空间与初始空间设置一致,避免堆堆自动扩展
经验值:parallel 垃圾收集器在大内存超过4G的内存会停顿时间较长, paralleNew +CMS 垃圾收集器更好
4、优化思路其实简单来说就是尽量让每次Young GC后的存活对象小于Survivor区域的50%,都留存在年轻代里。尽量别让对象进入老年代。尽量减少Full GC的频率,避免频繁Full GC对JVM性能的影响。
java调优牛逼工具
jmap 查看实例,堆信息,dump出文件
jvisualvm 界面监控jvm情况,比如 jvm堆情况,也可分析dump文件, 检测死锁等
jstack 可以查看栈情况 检测死锁
top 配合 jstack 可以找到cpu最高等线程,略麻烦
jinfo 查看正在运行的应用程序的扩展参数
javap -c class文件 反编译class文件
jstat 堆内存各部分的使用量,以及加载类的数量
-gc 垃圾回收统计
-gccapacity 堆内存统计
-gcnew 新生代垃圾回收统计
-gcnewcapacity 新生代内存统计
-gcold 老年代垃圾回收统计
-gcoldcapacity 老年代内存统计
-gcmetacapacity 元数据空间统计
-gcutil 各种空间之间比例统计
arthas 阿尔萨斯
url : https://alibaba.github.io/arthas
常用命令
dashboard 看板
thread -b 查看死锁
jad 反编译代码为java代码
ongl 可以执行命令
GC日志
开启,并存到文件组合命令
gceasy https://gceasy.io 牛逼分析GC日志的网站
full gc 比minor gc多原因
1、元空间不够导致的多余full gc
2、显示调用System.gc()造成多余的full gc,这种一般线上尽量通过-XX:+DisableExplicitGC参数禁用,如果加上了这个JVM启动参数,那么代码中调用System.gc()没有任何效果
3、老年代空间分配担保机制
内存泄漏
JIT
概念
与jvm的相关图
JIT构造
jdk8
C1编译器,别称(Client Compiler)
C2编译器,别称(Server Compiler)
jdk10 开始
C1编译器
Graal 编译器
分层编译
java -version
java -Xint -version
java -Xcomp -version
热点代码
被频繁调用的代码,会放到CodeCache中
热点探测
方法调用计数器
client模式下 1500次
server模式下 10000次
回边计数器 默认10700
一些优化的命令
-XX:ReservedCodeCacheSize ,默认:30M
java -XX:+PrintFlagsFinal –version ,查询一些java 的测试参数命令 和默认值
-XX: CompileThreshold ,默认10000
OnStackReplacePercentage ,默认140
InterpreterProfilePercentage,默认33
FreqInlineSize ,默认 325
MaxInlineSize,默认 35
-XX:+EliminateLocks开启锁消除(jdk1.8默认开启,其它版本未测试)
-XX:-EliminateLocks 关闭锁消除
-XX:-EliminateLocks 关闭锁消除
-XX:+DoEscapeAnalysis开启逃逸分析(jdk1.8默认开启)
-XX:-DoEscapeAnalysis 关闭逃逸分析
-XX:-DoEscapeAnalysis 关闭逃逸分析
-XX:+EliminateAllocations开启标量替换(jdk1.8默认开启)
-XX:-EliminateAllocations 关闭标量替换
-XX:-EliminateAllocations 关闭标量替换
编译技术优化
方法内联
好处
减少GC
避免多次调用栈帧
命令分类,方法体大小配置
经常执行的方法
FreqInlineSize
不经常执行的方法
MaxInlineSize
使用方法内联的方式
通过设置 JVM 参数来减小热点阈值或增加方法体阈值,以便更多的方法可以进行内联,但这种方法意味着需要占用更多地内存;
使用小方法体,一个方法内尽量使用更少的方法
尽量使用 final、private、static 关键字修饰方法,编码方法因为继承,会需要额外的类型检查。
1、热点代码,2、满足方法体大小 会使用方法内联优化
锁消除
在没有线程争抢的情况下,变量读写没有竞争的情况下会去除加锁逻辑
逃逸分析
从不逃逸到 方法逃逸到线程逃逸,叫做逃逸分析的逃逸程度
标量替换
栈上分配
同时开启了逃逸分析和标量替换,程序发现热点代码满足这两个条件就在栈上分配
逃逸分析-标量替换-栈上分配关系
GraalVM
简介
历史
解决问题
java虚拟机启动慢,加载慢,各种慢
矛盾两种
革命派
使用golang
保守派
保留原生的一套,使用GraalVm
特征
高性能
AOT原生镜像编译
多语言编程
支持语言
基于jvm开发的语言
解释型语言
配合LLVM 一起工作的语言
高级工具
可以在不同环境中运行
jvm中
编译为一个本地镜像文件
将java或本地代码模块集成为更大的应用
亲测通过
环境
Mac OS Monterey version 12.0.1
graalvm-ee-java8-darwin-amd64-20.3.5
执行native-image Hello成功
命令
time java Hello
本地镜像执行耗时短
time native-image Hello
官网链接地址
最牛逼之
用到的技术
AOT
JIT 即时编译器
JVMCI
图片
图片
官网地址
说明
1、主要是利用这个接口使JIT 跟hotSport虚拟机进行解耦合
2、C2 跟Graal的一个主要区别是 一个是C++ 的,一个是java写的
graal与springboot
未来为发布 spring6.0 springboot3.0
各种命令
jvm内存设置命令
-Xms 最小堆大小
-Xmx 最大堆大小
-Xmn 新生代大小
-XX:MetaspaceSize: 20M 元空间 大小,默认 20M
-XX:MaxMetaspaceSize:-1 元空间最大值,,默认不设置限制
-Xss 栈空间大小 越大一个线程栈分配的栈帧越大,整体可开启的线程数越少
-XX:NewRatio:2 新生代占整个堆区的1/3 是老年代的1/2
-XX:SurvivorRatio:8 表示一个survivor 占1/8的Eden内存
-XX:+UseSerialGC 使用单线程的垃圾收集器
-XX:+DisableExplicitGC 禁用 System.gc()
-XX:+/-UseTLAB,是否开启TLAB
-XX:+UseCompressedOops 默认:开启对象指针压缩
-XX:+UseCompressedClassPointers 默认开启 klass pointer压缩
-XX:+UseAdaptiveSizePolicy(默认开启) ,新生代老年代指令比例自动变化,可以配置为不变化
打印命令的命令
-XX:+PrintFlagsInitial 打印所有参数的初始值
-XX:+PrintFlagsFinal 所有参数的最终值
-XX:+PrintGC 打印GC的日志
-XX:+PrintGCDetails 运行jvm参数
类加载机制
类加载过程
加载
硬盘io读入字节码文件,放到方法区一个Class对象,使用时才进行加载
验证
验证字节码文件的正确性
准备
给类的静态变量分配内存,并赋予初值
解析
符号引用替换为直接引用,静态链接过程
初始化
对类静态变量进行初始化,执行静态代码块
使用
真正的使用
卸载
垃圾收集器销毁
类的静态动态绑定
静态绑定
发生于解析阶段,实例变量、静态变量、静态方法、private方法 都属于静态绑定
动态绑定
发生于运行阶段,寻找在所有重载版本中最匹配的才能看到的动态类型,叫做动态绑定
jar 或者 war中的类是使用的时候才进行加载
双亲委派机制
加载器分类
引导类加载器
扩展类加载器
应用类加载器
自定义类加载器
设计思路
沙箱安全机制
避免类的重复加载
打破双亲委派
每个webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器,打破了双亲委派机制
tomcat类加载器
加载器
引导类加载器
扩展类加载器
应用类加载器
common类加载器
webApp加载器
jsp类加载器
jsp热部署
卸载jsp类加载器,再重新装在jsp类加载器,达到jsp的热部署
类对象是否一致
除了包,类名一致,还需要类加载器一致,才是相同的两个对象
内存模型
jdk官网截图
跨平台,一次编译到处运行
对象创建与内存分配机制
对象创建过程主要流程
类加载检查,从new指令开始,或者对象克隆,反序列化一些
分配内存
方法
默认是;指针碰撞法(Bump the Pointer),对于绝对规整的内存使用该方法
空闲列表法(free list),对于不规整的内存使用该方法,虚拟机维护了个空闲可用列表,从列表的里面拿,使用后更新列表
解决并发问题
cas(compare and swap)
本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)
是否开启命令,-XX:+/-UseTLAB,
初始化零值
比如: int类型的值初始化为0,如果是 TLAB模式则,该步骤在TLAB时候就完成了
设置对象头
对象头
32位
64位
对象组成
对象头(Header)
Mark Word
32位的占 4字节
64位的占 8字节
Klass Pointer
类型指针,开启占4字节,不开启占8字节
数组长度
4个字节,数组对象才有
对象实例(Instance Data)
对齐填充(Padding) 保证对象是8个字节的整数倍
工具
jol-core: 使用程序用来查看对象的分布
指针压缩
命令
默认开启指针压缩: -XX:+UseCompressedOops (compressed--压缩、oop(ordinary object pointer)--对象指针)
默认开启的 Klass Pointer指针压缩:-XX:+UseCompressedClassPointers
好处
64位机器如果使用8字节指针,内存使用会扩大1.5倍左右,如果是大指针则主存与内存中拷贝压力大,带宽和GC的压力都会增大
减少64位机器的内存消耗
jvm内存中存的是32位的对象,但是寻址的时候又一个解压缩算法,在寄存器中存的是32位以上的,比如是40位的,涉及到计算机原理相关的知识,就是jvm的优化只是在自己的内存中做的优化,内存与cpu寄存器交互还有压缩和解压缩算法
堆内存小于4g的时候不需要启用指针压缩,jvm会直接去除高32位地址,即使用低虚拟地址空间
堆内存不建议设置大于32G,jvm强制使用64位来寻址,出现第一个的问题
对象内存分配
对象栈上分配
逃逸分析
默认开启,命令 -XX:+DoEscapeAnalysis
标量替换
默认开启:命令 -XX:+EliminateAllocations
含义:就是开启了逃逸分析后,发现一个对象不会逃逸出方法,那么实际操作时候就不创建对象,只是为其中的成员变量分配栈帧空间,这样就不会为没有一大块连续的空闲空间 而苦恼
标量与聚合量
标量就是 long int 这些,聚合量就是 实际的对象这些
代码测试通过 ,当时未通过是因为User对象最后重写了 finalize()方法,在这个方法中又有外部对象对该对象进行应用了,导致了问题 AllotOnStack 类
对象在eden上分配
MajorGC的时间通常是 minGC的10倍以上
默认eden 与 survivor 区比例 8:1 : 1
指令比例自动变化:-XX:+UseAdaptiveSizePolicy(默认开启),想固定比例可以关闭
先eden区,再survivor 区,然后太大放不下就放到老年区去
对象老年代上分配
大对象直接老年代
命令:-XX:PretenureSizeThreshold 对象大于多少值 GCTest 代码测试通过
含义:只在 Serial 和ParNew两个收集器下有效
ParNew + CMS 亲测验证通过
配置 Serial 垃圾收集器,亲测同样通过
长期存活代对象到老年代
命令:-XX:MaxTenuringThreshold 默认15岁,CMS 收集器默认是 6岁
对象动态年龄判断
命令:-XX:TargetSurvivorRatio 默认 50
老年代空间分配担保
-XX:-HandlePromotionFailure jdk8默认设置了
对象内存回收
引用计数法
含义:增加一个引用+1,减去一个引用减一,会有对象之间 循环相互引用代问题,主流不用这个
可达性分析
使用GC ROOTS 做起点
eg: 线程栈的本地变量、静态变量、本地方法栈的变量等等
url : https://help.eclipse.org/latest/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Fconcepts%2Fgcroots.html&cp=37_2_3
具体的网页内容
引用类型
强引用 : 正常不会回收
软应用: SoftReference 对象包裹 ,gc完后发现没空间就回收该对象,软引用可用来实现内存敏感的高速缓存
弱引用 : WeakReference 对象包裹的,跟没用几乎一样,gc的时候会直接回收
虚引用 : 最弱的 ,几乎不用
可达性分析 gc roots 未指到的
第一次标记
对象未覆盖 finalize 方法 直接被回收
第二次标记
对象覆盖了 finalize 方法:在此方法中再次建立关联,jvm发现这个后就会避免回收
元空间回收类,判断类是无用的类
类所有实例都被回收
加载类的classLoader 已经回收
类的 java.lang.Class对象在任何地方都没有使用了
常量池
分类
Class常量池
字面量
符号引用
1、类和接口的全限定名
2、字段的名称和描述符
3、方法的名称和描述符
加载到内存后就到了运行时常量池
运行时常量池
Jdk1.6及之前: 有永久代, 运行时常量池在永久代,运行时常量池包含字符串常量池
Jdk1.7:有永久代,但已经逐步“去永久代”,字符串常量池从永久代里的运行时常量池分离到堆里
Jdk1.8及之后: 无永久代,运行时常量池在元空间,字符串常量池里依然在堆里
String 的 intern()
8种基本类型常量池
Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现
Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用对象池
垃圾收集器
垃圾收集算法
分代收集理论
新生代 : 主要是复制算法 (99%对象会死去)
老年代:主要是标记整理和标记清除,比年轻代慢10倍以上
复制算法
一块复制到另一块,就是新生代中的 s0 到 s1之间到互相拷贝
标记清除
老年代算法
问题1:效率问题:太多的话效率不高
问题2:空间问题:产生大量不连续到碎片
标记整理
老年代算法
问题:效率最慢
优势:会有大量连续到内存空间释放
垃圾收集器
Serial
命令:-XX:+UseSerialGC java8 就这一条命令即可
新生代:复制算法 老年代:标记整理
含义:垃圾收集时候有STW (stop the world)
使用场景,1老jdk默认使用,2、老年代收集器是CMS 的备选方案
Parallel(jdk8 默认)
命令:-XX:+UseParallelGC(年轻代) -XX:ParallelGCThreads 默认是cpu核心数,可以指定
使用复制算法
Serial New 的并发版本
jdk8 默认
收集时会有 STW
Parallel Old(jdk 8默认)
-XX:+UseParallelOldGC(老年代)
关注点:吞吐量
使用标记整理算法
jdk8 默认
收集时有 STW
ParNew
命令:-XX:+UseParNewGC
基本与 Parallen 类似,新生代 复制算法,主要配合 CMS使用的新生代并发算法
CMS
使用标记清除算法
流程
应用程序线程
初始标记 会有 STW
并发标记
重新标记 会有 STW
并发清理
并发重制
缺点:
cpu 资源敏感
无法处理浮动垃圾,只能下次回收
会产生垃圾碎片,毕竟是标记清除算法,命令:-XX:+UseCMSCompactAtFullCollection 重新再整理即可
并发标记和并发清理阶段产生的垃圾,赶上垃圾回收会有 concurrent mode failure,会使用单线程回收 Serial Old 收集器
重要,需要在线上避免!!!!
CMS的一些常用命令
-XX:+UseConcMarkSweepGC
-XX:ConcGCThreads
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction
-XX:CMSInitiatingOccupancyFraction
-XX:+UseCMSInitiatingOccupancyOnly
-XX:+CMSScavengeBeforeRemark
-XX:+CMSParallellnitialMarkEnabled
-XX:+CMSParallelRemarkEnabled
CMS垃圾收集原理
三色标记
黑色
灰色
白色
多标问题
漏标问题
解决方案1:CMS 使用的 增量更新(Incremental Update)
解决方案2:G1 使用原始快照 (Snapshot At The Beginning,SATB
增量更新和原始快照都是读写屏障实现的
G1
漏标问题
解决:写屏障 + SATB
算法:主要是复制算法
使用场景
1、:大内存,多核机器
2、50%以上的堆被存活对象占用
3、对象分配和晋升的速度变化非常大
4、垃圾回收时间特别长,超过1秒
5、8GB以上的堆内存(建议值)
6、停顿时间是500ms以内
分区
Region 2048个,不建议超过该值,会动态变化,可能变为eden 或者 old等
Eden 默认占比5%,与survivor 还是 8:1:1
Survivor
Old
Humongous
命令
-XX:+UseG1GC:使用G1收集器
-XX:ParallelGCThreads
-XX:G1NewSizePercent
-XX:G1MaxNewSizePercent
-XX:TargetSurvivorRatio
-XX:MaxTenuringThreshold
-XX:G1HeapRegionSize
-XX:MaxGCPauseMillis
-XX:InitiatingHeapOccupancyPercent
-XX:G1MixedGCLiveThresholdPercent(默认85%)
-XX:G1MixedGCCountTarget
-XX:G1HeapWastePercent(默认5%):
回收步骤,主要是mix回收,忘掉分代收集
初始标记
并发标记
只有这个无STW,其余均有STW
最终标记
筛选回收
与CMS收集器异同
相异点
1、并发回收阶段 CMS 支持jvm和用户线程并发,G1则不支持
2、G1 可控制的停顿时间,默认200ms,而cms收集器不可以配置类似参数
3、解决漏标问题CMS使用增量更新,G1使用SATB
相同点
1、关注点相同,降低停顿时间,用户访问的延迟
2、并发标记的时候都无STW
特性
1、优先收集价值大的region
2、jdk7 以上版本才有
3、并行与并发
4、分代收集
5、空间整合
6、可预测的停顿
垃圾收集
Young GC 计算大约时间到停顿时间了才进行gc
Mix GC 回收 新生代 + 部分老年代+大对象区,根据时间停顿判断,优先收集的region,使用复制算法,要是发现没有足够的空间则进行 full gc
Full GC 使用单线程 标记 清理 压缩,很耗时
kafka实战举例
ZGC
STW问题
android手机卡顿
证券交易系统实时性
大数据平台性能
介绍
2018年jdk11 开始,jdk16已经可以延迟低于 1ms了
停顿时间不会随着堆的增加,或者活跃对象的增加而增加
支持8G-4T级别的堆,15之后支持16T
内存布局
无新生代老年代的分代概念
支持3种页面小页面、中页面、大页面,判断标准临界值256K和4M
小页面 2M
中页面 32M
大页面 > 32M的页面
标准是:小页面优先回收;中页面和大页面尽量不回收
why这样设计
传统页是4K,Huge pages 有两种格式大小: 2MB 和 1GB
是支持NUMA的
小页面存储
中页面和大页面存储
核心:指针着色技术
说明:对于4T的堆空间,低42位放对象的实际地址,ZGC借几位高位做gc相关事情(并发标记,转移,重定位)
ZGC流程
GC ROOTS
虚拟机栈
方法区中的类静态变量
方法区中的常量
方法区中JNI指针
ZGC流程图以及指针着色详解
流程
标记阶段
初始标记 STW
并发标记
会产生漏标问题
再标记 STW
主要解决漏标问题 SATB(原始快照)方式解决,同G1
转移阶段
并发转移准备
初始转移 STW
并发转移
概念说明
标记 mark
转移 relocate
重定位 remap
M0 绿色
M1 红色
Remapped 蓝色
读屏障
就是在业务执行过程中插入一段代码的技术,对象重定位 + 删除转发表的时候会出发 读屏障,保证是原子操作
过程
1、需要从堆中读引用时,需要在之前加一个读屏障 Load Barrier
2、判断当前指针是否是bad Color 也就是,(也就是不是本地gc过程中Mark的颜色)
3、修正指针,对象重定位 + 删除转发表记录 (第二次并发标记的过程中操作)
GC 触发机制 jdk16
预热规则
默认: 基于分配速率的自适应算法
基于固定时间间隔
主动出发规则
阻塞内存分配请求触发,要避免
外部触发
元数据分配触发,一般不需要关注
参数设置
堆大小
GC触发时机
ZAllocationSpikeTolerance 这一段的备注不太懂
ZCollectionInterval
堆线程
ParallelGCThreads
ConcGCThreads
应用场景
超大堆应用, G1如果发生full gc 会在分钟级别,应该使用zgc
低延迟的 高服务级别协议 (Service Level Aggregament) SLA ,低于100ms 无论堆大小,都应该使用 ZGC
生产注意事项
RSS内存异常现象 目前只是拿过来
共享内存调整
mmap 节点上限调整 目前只是拿过来
存在的问题以及改进
直接看官方更新日志,尽量使用新版本
Shenandoah
支持jvm线程和用户线程并发收集
可以理解为G1 的升级版,还未用到
Epsilon
不会做任何垃圾回收工作,做测试用,验证程序的垃圾生成速度等等
0 条评论
下一页