一篇能涨薪水的JVM调优,分析文章
扫描二维码
随时随地手机看文章
文章目录
JVM发展史
一,历代JDK新特性介绍
1996年 SUN JDK 1.0 Classic VM
1997年 JDK1.1 发布
1998年 JDK1.2 Solaris Exact VM
2000年 JDK 1.3 Hotspot 作为默认虚拟机发布
2002年 JDK 1.4 Classic VM退出历史舞台
2004年发布 JDK1.5 即 JDK5 、J2SE 5 、Java 5
2006年发布JDK1.6既JDK6
2011年 JDK7发布
2014年 JDK8发布
2016年JDK9
2018年JDK10
2018年JDK11
2019年JDK12
2019年JDK13
2020年发布JDK14
JVM运行机制
JVM模型
图解:
PC寄存器
方法区
Java堆(新生代,老年代,持久代)
Matespace元数据区(jdk1.8新特性)
Java栈
栈、堆、方法区交互
JVM参数
1,堆设置
2,收集器设置
3,垃圾回收统计信息
4,并行收集器设置
5,并发收集器设置
参数介绍
参数设置(案例)
GC
引用计数
正向可达
串行收集器
并行收集器
GC参数整理
CMS运行过程比较复杂,着重实现了标记的过程,可分为
特点
ParNew
Parallel收集器
-XX:MaxGCPauseMills
-XX:GCTimeRatio
CMS收集器
GC的概念
GC参数
GC算法与种类
如何判断对象是否可回收?
JVM监控工具
JVM发展史
一,历代JDK新特性介绍
1996年 SUN JDK 1.0 Classic VM
初代版本,伟大的一个里程碑,但是是纯解释运行,使用外挂JIT,性能比较差,运行速度慢。
1997年 JDK1.1 发布
AWT、内部类、JDBC、RMI、反射
1998年 JDK1.2 Solaris Exact VM
JIT 解释器混合
Accurate Memory Management 精确内存管理,数据类型敏感
提升的GC性能
JDK1.2开始 称为Java 2 J2SE J2EE J2ME 的出现
加入Swing Collections
2000年 JDK 1.3 Hotspot 作为默认虚拟机发布
加入JavaSound
2002年 JDK 1.4 Classic VM退出历史舞台
Assert 正则表达式 NIO IPV6 日志API 加密类库
2004年发布 JDK1.5 即 JDK5 、J2SE 5 、Java 5
自动装箱拆箱
泛型支持
元数据(注解)
Introspector(内省)
enum(枚举)
静态引入
可变长参数(Varargs)
foreach(高级虚幻)
JMM(内存模型)
concurrent(并发包)
2006年发布JDK1.6既JDK6
命名方式变更
脚本语言
编译API和微型HTTP服务器API
锁与同步
垃圾收集
类加载
JDBC 4.0(jdbc高级)
Java Compiler (Java™ 编程语言编译器的接口)
可插拔注解
Native PKI(公钥基础设)
Java GSS (通用安全服务)
Kerberos ( 一种安全认证的系统)
LDAP (LDAP )
Web Services (web服务即xml传输)
2011年 JDK7发布
switch语句块中允许以字符串作为分支条件
创建泛型对象时应用类型推断
try-with-resources(一个语句块中捕获多种异常)
null值得自动处理
数值类型可以用二进制字符串表示
引入Java NIO.2开发包
动态语言支持
安全的加减乘除
Map集合支持并发请求
2014年 JDK8发布
引入Lambda 表达式
管道和流
新的日期和时间 API(加强对日期与时间的处理)
默认的方法(接口可以编写默认的方法)
类型注解
Nashorn javascript引擎(允许java运行特定JavaScript代码)
Optional class (处理nullPointException)
并行累加器
并行操作
内存错误移除
TLS SNI 服务器名称标识(Server Name Identification)
2016年JDK9
模块化
接口支持编写私有方法
Javadoc改进(支持符合html5 标准输出)
Stream API 增强(简化调用、操作、提供常用便捷的方法)
image API增强(支持多分辨率解析)
多版本jar支持(在不同环境运行不同jar包)
改进弃用注解使用@Deprecated
内置轻量级json API
弃用Applet API
Deprecation的弃用
2018年JDK10
JEP286,var 局部变量类型推断。
JEP296,将原来用 Mercurial 管理的众多 JDK 仓库代码,合并到一个仓库中,简化开发和管理过程。
JEP304,统一的垃圾回收接口。
JEP307,G1 垃圾回收器的并行完整垃圾回收,实现并行性来改善最坏情况下的延迟。
JEP310,应用程序类数据 (AppCDS) 共享,通过跨进程共享通用类元数据来减少内存占用空间,和减少启动时间。
JEP312,ThreadLocal 握手交互。在不进入到全局 JVM 安全点 (Safepoint) 的情况下,对线程执行回调。优化可以只停止单个线程,而不是停全部线程或一个都不停。
JEP313,移除 JDK 中附带的 javah 工具。可以使用 javac -h 代替。
JEP314,使用附加的 Unicode 语言标记扩展。
JEP317,能将堆内存占用分配给用户指定的备用内存设备。
JEP317,使用 Graal 基于 Java 的编译器,可以预先把 Java 代码编译成本地代码来提升效能。
JEP318,在 OpenJDK 中提供一组默认的根证书颁发机构证书。开源目前 Oracle 提供的的 Java SE 的根证书,这样 OpenJDK 对开发人员使用起来更方便。
JEP322,基于时间定义的发布版本,即上述提到的发布周期。版本号为$FEATURE.$INTERIM.$UPDATE.$PATCH,分j别是大版本,中间版本,升级包和补丁版本。
2018年JDK11
新特性及更新修改:
基于嵌套的访问控制
标准 HTTP Client 升级
Epsilon:低开销垃圾回收器
简化启动单个源代码文件的方法
用于 Lambda 参数的局部变量语法
低开销的 Heap Profiling
支持 TLS 1.3 协议
ZGC:可伸缩低延迟垃圾收集器
飞行记录器
动态类文件常量
2019年JDK12
Shenandoah: A Low-Pause-Time Garbage Collector (Experimental) 低暂停时间的GC
Microbenchmark Suite 微基准测试套件
Switch Expressions (Preview) Switch表达式
JVM Constants API JVM常量API
One AArch64 Port, Not Two 只保留一个AArch64实现
Default CDS Archives 默认类数据共享归档文件
Abortable Mixed Collections for G1 可中止的G1 Mixed GC
Promptly Return Unused Committed Memory from G1 G1及时返回未使用的已分配内存
2019年JDK13
JEP 350,Dynamic CDS Archives
扩展应用程序类-数据共享,以允许在 Java 应用程序执行结束时动态归档类。归档类将包括默认的基础层 CDS(class data-sharing)存档中不存在的所有已加载的应用程序类和库类。
JEP 351,ZGC: Uncommit Unused Memory
增强 ZGC 以将未使用的堆内存返回给操作系统。
JEP 353,Reimplement the Legacy Socket API
使用易于维护和调试的更简单、更现代的实现替换 java.net.Socket 和java.net.ServerSocket API 使用的底层实现。
JEP 354,Switch Expressions (Preview)
可在生产环境中使用的 switch 表达式,JDK 13 中将带来一个 beta 版本实现。switch 表达式扩展了 switch 语句,使其不仅可以作为语句(statement),还可以作为表达式(expression),并且两种写法都可以使用传统的 switch 语法,或者使用简化的“case L ->”模式匹配语法作用于不同范围并控制执行流。这些更改将简化日常编码工作,并为 switch 中的模式匹配(JEP 305)做好准备。
JEP 355,Text Blocks (Preview)
将文本块添加到 Java 语言。文本块是一个多行字符串文字,它避免了对大多数转义序列的需要,以可预测的方式自动格式化字符串,并在需要时让开发人员控制格式。
2020年发布JDK14
305:instanceof的模式匹配(预览)
343:包装工具(培养箱)
345:G1的NUMA感知内存分配
349:JFR事件流
352:非易失性映射字节缓冲区
358:有用的NullPointerExceptions
359:记录(预览)
361:开关表达式(标准)
362:弃用Solaris和SPARC端口
363:删除并发标记扫描(CMS)垃圾收集器
364:Mac OS上的ZGC
365:Windows上的ZGC
366:弃用ParallelScavenge + SerialOld GC组合
367:删除Pack200工具和API
368:文本块(第二预览)
370:外部存储器访问API(孵化器)
JVM运行机制
JVM模型
图解:
PC寄存器
每个线程拥有一个PC寄存器
在线程创建时 创建
指向下一条指令的地址
执行本地方法时,PC的值为undefined
方法区
保存装载的类信息
类型的常量池
字段,方法信息
方法字节码通常和永久区(Perm)关联在一起
Java堆(新生代,老年代,持久代)
和程序开发密切相关
应用系统对象都保存在Java堆中
所有线程共享Java堆
对分代GC来说,堆也是分代的
GC的主要工作区间
Matespace元数据区(jdk1.8新特性)
从JDK8开始,永久代(PermGen)的概念被废弃掉了,取而代之的是一个称为Metaspace的存储空间。Metaspace使用的是本地内存,而不是堆内存,也就是说在默认情况下Metaspace的大小只与本地内存大小有关。
-XX:MetaspaceSize=N
这个参数是初始化的Metaspace大小,该值越大触发Metaspace GC的时机就越晚。随着GC的到来,虚拟机会根据实际情况调控Metaspace的大小,可能增加上线也可能降低。在默认情况下,这个值大小根据不同的平台在12M到20M浮动。使用java -XX:+PrintFlagsInitial命令查看本机的初始化参数,-XX:Metaspacesize为21810376B(大约20.8M)。
-XX:MaxMetaspaceSize=N
这个参数用于限制Metaspace增长的上限,防止因为某些情况导致Metaspace无限的使用本地内存,影响到其他程序。在本机上该参数的默认值为4294967295B(大约4096MB)。
-XX:MinMetaspaceFreeRatio=N
当进行过Metaspace GC之后,会计算当前Metaspace的空闲空间比,如果空闲比小于这个参数,那么虚拟机将增长Metaspace的大小。在本机该参数的默认值为40,也就是40%。设置该参数可以控制Metaspace的增长的速度,太小的值会导致Metaspace增长的缓慢,Metaspace的使用逐渐趋于饱和,可能会影响之后类的加载。而太大的值会导致Metaspace增长的过快,浪费内存。
-XX:MaxMetasaceFreeRatio=N
当进行过Metaspace GC之后, 会计算当前Metaspace的空闲空间比,如果空闲比大于这个参数,那么虚拟机会释放Metaspace的部分空间。在本机该参数的默认值为70,也就是70%。
-XX:MaxMetaspaceExpansion=N
Metaspace增长时的最大幅度。在本机上该参数的默认值为5452592B(大约为5MB)。
-XX:MinMetaspaceExpansion=N
Metaspace增长时的最小幅度。在本机上该参数的默认值为340784B(大约330KB为)。
Java栈
线程私有
栈由一系列帧组成(因此Java栈也叫做帧栈)
帧保存一个方法的局部变量、操作数栈、常量池指针
每一次方法调用创建一个帧,并压栈
栈、堆、方法区交互
JVM参数
参数介绍
-Xss 设置每个线程可使用的内存大小,即栈的大小。
1,堆设置
-Xms 初始堆大小,默认为物理内存的1/64
-Xmx 最大堆大小,默认为物理内存的1/4
-Xmn 堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx减去-Xmn
-XX:NewSize=n 设置年轻代大小
-XX:NewRatio=n 设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n 年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n 设置持久代大小
-XX:MaxTenuringThreshold:设置转入老年代的存活次数。如果是0,则直接跳过新生代进入老年代
-XX:PermSize、-XX:MaxPermSize:分别设置永久代最小大小与最大大小(Java8以前,8以后被Matespace元数据区替代)
2,收集器设置
-XX:+UseSerialGC 设置串行收集器
-XX:+UseParallelGC 设置并行收集器
-XX:+UseParalledlOldGC 设置并行年老代收集器
-XX:+UseConcMarkSweepGC 设置并发收集器
3,垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
4,并行收集器设置
-XX:ParallelGCThreads=n 设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n 设置并行收集最大暂停时间
-XX:GCTimeRatio=n 设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
5,并发收集器设置
-XX:+CMSIncrementalMode 设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n 设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
参数设置(案例)
GC
GC的概念
Garbage Collection 垃圾收集
1960年 List 使用了GC
Java中,GC的对象是堆空间和永久区
GC参数
串行收集器
最古老,最稳定
效率高
可能会产生较长的停顿
-XX:+UseSerialGC
新生代、老年代使用串行回收
新生代复制算法
老年代标记-压缩
并行收集器
ParNew
-XX:+UseParNewGC
新生代并行
老年代串行Serial收集器新生代的并行版本
复制算法
多线程,需要多核支持
-XX:ParallelGCThreads 限制线程数量
Parallel收集器
类似ParNew
新生代复制算法
老年代 标记-压缩
更加关注吞吐量
-XX:+UseParallelGC
使用Parallel收集器 + 老年代串行-XX:+UseParallelOldGC
使用Parallel收集器 + 并行老年代
-XX:MaxGCPauseMills
最大停顿时间,单位毫秒
GC尽力保证回收时间不超过设定值
1
2
-XX:GCTimeRatio
0-100的取值范围
垃圾收集时间占总时间的比
默认99,即最大允许1%时间做GC
这两个参数是矛盾的。因为停顿时间和吞吐量不可能同时调优
CMS收集器
Concurrent Mark Sweep 并发(与用户线程一起执行 )标记清除
标记-清除算法
与标记-压缩相比
并发阶段会降低吞吐量
老年代收集器(新生代使用ParNew)
-XX:+UseConcMarkSweepGC
-XX:+ UseCMSCompactAtFullCollection Full GC后,进行一次整理
整理过程是独占的,会引起停顿时间变长
-XX:+CMSFullGCsBeforeCompaction
设置进行几次Full GC后,进行一次碎片整理
-XX:ParallelCMSThreads
设定CMS的线程数量
CMS运行过程比较复杂,着重实现了标记的过程,可分为
初始标记
根可以直接关联到的对象
速度快
并发标记(和用户线程一起)
主要标记过程,标记全部对象重新标记
由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正
并发清除(和用户线程一起)
基于标记结果,直接清理对象
特点
尽可能降低停顿
会影响系统整体吞吐量和性能
比如,在用户线程运行过程中,分一半CPU去做GC,系统性能在GC阶段,反应速度就下降一半
清理不彻底
因为在清理阶段,用户线程还在运行,会产生新的垃圾,无法清理
因为和用户线程一起运行,不能在空间快满时再清理
-XX:CMSInitiatingOccupancyFraction设置触发GC的阈值
如果不幸内存预留空间不够,就会引起concurrent mode failure
GC参数整理
-XX:+UseSerialGC:在新生代和老年代使用串行收集器
-XX:SurvivorRatio:设置eden区大小和survivior区大小的比例
-XX:NewRatio:新生代和老年代的比
-XX:+UseParNewGC:在新生代使用并行收集器
-XX:+UseParallelGC :新生代使用并行回收收集器
-XX:+UseParallelOldGC:老年代使用并行回收收集器
-XX:ParallelGCThreads:设置用于垃圾回收的线程数
-XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器
-XX:ParallelCMSThreads:设定CMS的线程数量
-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发
-XX:+UseSerialGC:在新生代和老年代使用串行收集器
-XX:SurvivorRatio:设置eden区大小和survivior区大小的比例
-XX:NewRatio:新生代和老年代的比
-XX:+UseParNewGC:在新生代使用并行收集器
-XX:+UseParallelGC :新生代使用并行回收收集器
-XX:+UseParallelOldGC:老年代使用并行回收收集器
-XX:ParallelGCThreads:设置用于垃圾回收的线程数
-XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器
-XX:ParallelCMSThreads:设定CMS的线程数量
-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发
GC算法与种类
新生代复制算法
老年代标记压缩
标记清除
如何判断对象是否可回收?
引用计数
引用计数算法是通过在对象头中分配一个空间来保存该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加一,如果删除对该对象的引用,那么它的引用计数就减一,当该对象的引用计数为0时,那么该对象就会被回收。
软引用,弱引用,虚引用
正向可达
可触及的
从根节点可以触及到这个对象
可复活的
一旦所有引用被释放,就是可复活状态
因为在finalize()中可能复活该对象不可触及的
在finalize()后,可能会进入不可触及状态
不可触及的对象不可能复活
可以回收
JVM监控工具
1.jdk自带jconsole,jvisualvm (jdk的bin目录)
2.Eclipse Memory Analyzer tool(MAT)
3.在线分析网址:https://fastthread.io
特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:
长按订阅更多精彩▼
如有收获,点个在看,诚挚感谢
免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!