https://gitee.com/vectorx/NOT...
https://codechina.csdn.net/qq...
https://github.com/uxiahnan/N...
[TOC]
1. 概述篇
1.1. 大厂面试题
<mark>支付宝:</mark>
支付宝三面:JVM性能调优都做了什么?
<mark>小米:</mark>
有做过JVM内存优化吗?
从SQL、JVM、架构、数据库四个方面讲讲优化思路
<mark>蚂蚁金服:</mark>
JVM的编译优化
jvm性能调优都做了什么
JVM诊断调优工具用过哪些?
二面:jvm怎么调优,堆内存、栈空间设置多少适合
三面:JVM相干的剖析工具应用过的有哪些?具体的性能调优步骤如何
<mark>阿里:</mark>
如何进行JVM调优?有哪些办法?
如何了解内存透露问题?有哪些状况会导致内存透露?如何解决?
<mark>字节跳动:</mark>
三面:JVM如何调优、参数怎么调?
<mark>拼多多:</mark>
从SQL、JVM、架构、数据库四个方面讲讲优化思路
<mark>京东:</mark>
JVM诊断调优工具用过哪些?
每秒几十万并发的秒杀零碎为什么会频繁产生GC?
日均百万级交易系统如何优化JVM?
线上生产零碎OOM如何监控及定位与解决?
高并发零碎如何基于G1垃圾回收器优化性能?
1.2. 背景阐明
生产环境中的问题
- 生产环境产生了内存溢出该如何解决?
- 生产环境应该给服务器调配多少内存适合?
- 如何对垃圾回收器的性能进行调优?
- 生产环境CPU负载飙高该如何解决?
- 生产环境应该给利用调配多少线程适合?
- 不加log,如何确定申请是否执行了某一行代码?
- 不加log,如何实时查看某个办法的入参加返回值?
为什么要调优
- 防止出现OOM
- 解决OOM
- 缩小Full GC呈现的频率
不同阶段的思考
- 上线前
- 我的项目运行阶段
- 线上呈现OOM
1.3. 调优概述
监控的根据
- 运行日志
- 异样堆栈
- GC日志
- 线程快照
- 堆转储快照
调优的大方向
- 正当地编写代码
- 充沛并正当的应用硬件资源
- 正当地进行JVM调优
1.4. 性能优化的步骤
第1步:性能监控
- GC频繁
- cpu load过高
- OOM
- 内存泄露
- 死锁
- 程序响应工夫较长
第2步:性能剖析
- 打印GC日志,通过GCviewer或者 http://gceasy.io 来剖析异样信息
- 灵活运用命令行工具、jstack、jmap、jinfo等
- dump出堆文件,应用内存剖析工具剖析文件
- 应用阿里Arthas、jconsole、JVisualVM来实时查看JVM状态
- jstack查看堆栈信息
第3步:性能调优
- 适当减少内存,依据业务背景抉择垃圾回收器
- 优化代码,管制内存应用
- 减少机器,扩散节点压力
- 正当设置线程池线程数量
- 应用中间件进步程序效率,比方缓存、音讯队列等
- 其余……
1.5. 性能评估/测试指标
进展工夫(或响应工夫)
提交申请和返回该申请的响应之间应用的工夫,个别比拟关注均匀响应工夫。罕用操作的响应工夫列表:
操作 | 响应工夫 |
---|---|
关上一个站点 | 几秒 |
数据库查问一条记录(有索引) | 十几毫秒 |
机械磁盘一次寻址定位 | 4毫秒 |
从机械磁盘程序读取1M数据 | 2毫秒 |
从SSD磁盘程序读取1M数据 | 0.3毫秒 |
从近程分布式换成Redis 读取一个数据 | 0.5毫秒 |
从内存读取 1M数据 | 十几奥妙 |
Java程序本地办法调用 | 几奥妙 |
网络传输2Kb数据 | 1 奥妙 |
在垃圾回收环节中:
- 暂停工夫:执行垃圾收集时,程序的工作线程被暂停的工夫。
- -XX:MaxGCPauseMillis
吞吐量
- 对单位工夫内实现的工作量(申请)的量度
- 在GC中:运行用户代码的事件占总运行工夫的比例(总运行工夫:程序的运行工夫+内存回收的工夫)
- 吞吐量为1-1/(1+n),其中-XX::GCTimeRatio=n
并发数
- 同一时刻,对服务器有理论交互的申请数
内存占用
- Java堆区所占的内存大小
相互间的关系
以高速公路通行情况为例
- 吞吐量:每天通过高速公路收费站的车辆的数据
- 并发数:高速公路上正在行驶的车辆的数目
- 响应工夫:车速
<hr/>
2. JVM监控及诊断工具-命令行篇
2.1. 概述
性能诊断是软件工程师在日常工作中须要常常面对和解决的问题,在用户体验至上的明天,解决好利用的性能问题能带来十分大的收益。
Java 作为最风行的编程语言之一,其利用性能诊断始终受到业界宽泛关注。可能造成 Java 利用呈现性能问题的因素十分多,例如线程管制、磁盘读写、数据库拜访、网络I/O、垃圾收集等。想要定位这些问题,一款优良的性能诊断工具必不可少。
领会1:应用数据阐明问题,应用常识剖析问题,应用工具解决问题。
领会2:无监控、不调优!
简略命令行工具
在咱们刚接触java学习的时候,大家必定最先理解的两个命令就是javac,java,那么除此之外,还有没有其余的命令能够供咱们应用呢?
咱们进入到装置jdk的bin目录,发现还有一系列辅助工具。这些辅助工具用来获取指标 JVM 不同方面、不同档次的信息,帮忙开发人员很好地解决Java应用程序的一些疑难杂症。
官网源码地址:http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/jdk.jcmd/share/classes/sun/tools
2.2. jps:查看正在运行的Java过程
jps(Java Process Status):显示指定零碎内所有的HotSpot虚拟机过程(查看虚拟机过程信息),可用于查问正在运行的虚拟机过程。
阐明:对于本地虚拟机过程来说,过程的本地虚拟机ID与操作系统的过程ID是统一的,是惟一的。
根本应用语法为:jps [options] [hostid]
咱们还能够通过追加参数,来打印额定的信息。
options参数
- -q:仅仅显示LVMID(local virtual machine id),即本地虚拟机惟一id。不显示主类的名称等
- -l:输入应用程序主类的全类名 或 如果过程执行的是jar包,则输入jar残缺门路
- -m:输入虚拟机过程启动时传递给主类main()的参数
- -v:列出虚拟机过程启动时的JVM参数。比方:-Xms20m -Xmx50m是启动程序指定的jvm参数。
阐明:以上参数能够综合应用。
补充:如果某 Java 过程敞开了默认开启的UsePerfData参数(即应用参数-XX:-UsePerfData),那么jps命令(以及上面介绍的jstat)将无奈探知该Java 过程。
hostid参数
RMI注册表中注册的主机名。如果想要近程监控主机上的 java 程序,须要装置 jstatd。
对于具备更严格的平安实际的网络场合而言,可能应用一个自定义的策略文件来显示对特定的可信主机或网络的拜访,只管这种技术容易受到IP地址欺诈攻打。
如果平安问题无奈应用一个定制的策略文件来解决,那么最平安的操作是不运行jstatd服务器,而是在本地应用jstat和jps工具。
2.3. jstat:查看JVM统计信息
jstat(JVM Statistics Monitoring Tool):用于监督虚拟机各种运行状态信息的命令行工具。它能够显示本地或者近程虚拟机过程中的类装载、内存、垃圾收集、JIT编译等运行数据。在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。罕用于检测垃圾回收问题以及内存透露问题。
官网文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html
根本应用语法为:jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
查看命令相干参数:jstat-h 或 jstat-help
其中vmid是过程id号,也就是jps之后看到的后面的号码,如下:
option参数
选项option能够由以下值形成。
<mark>类装载相干的:</mark>
- -class:显示ClassLoader的相干信息:类的装载、卸载数量、总空间、类装载所耗费的工夫等
<mark>垃圾回收相干的:</mark>
- -gc:显示与GC相干的堆信息。包含Eden区、两个Survivor区、老年代、永恒代等的容量、已用空间、GC工夫共计等信息。
- -gccapacity:显示内容与-gc基本相同,但输入次要关注Java堆各个区域应用到的最大、最小空间。
- -gcutil:显示内容与-gc基本相同,但输入次要关注已应用空间占总空间的百分比。
- -gccause:与-gcutil性能一样,然而会额定输入导致最初一次或以后正在产生的GC产生的起因。
- -gcnew:显示新生代GC情况
- -gcnewcapacity:显示内容与-gcnew基本相同,输入次要关注应用到的最大、最小空间
- -geold:显示老年代GC情况
- -gcoldcapacity:显示内容与-gcold基本相同,输入次要关注应用到的最大、最小空间
- -gcpermcapacity:显示永恒代应用到的最大、最小空间。
<mark>JIT相干的:</mark>
- -compiler:显示JIT编译器编译过的办法、耗时等信息
- -printcompilation:输入曾经被JIT编译的办法
jstat -class
jstat -compiler
jstat -printcompilation
jstat -gc
jstat -gccapacity
jstat -gcutil
jstat -gccause
jstat -gcnew
jstat -gcnewcapacity
jstat -gcold
jstat -gcoldcapacity
jstat -t
jstat -t -h
表头 | 含意(字节) |
---|---|
EC | Eden区的大小 |
EU | Eden区已应用的大小 |
S0C | 幸存者0区的大小 |
S1C | 幸存者1区的大小 |
S0U | 幸存者0区已应用的大小 |
S1U | 幸存者1区已应用的大小 |
MC | 元空间的大小 |
MU | 元空间已应用的大小 |
OC | 老年代的大小 |
OU | 老年代已应用的大小 |
CCSC | 压缩类空间的大小 |
CCSU | 压缩类空间已应用的大小 |
YGC | 从应用程序启动到采样时young gc的次数 |
YGCT | 从应用程序启动到采样时young gc耗费工夫(秒) |
FGC | 从应用程序启动到采样时full gc的次数 |
FGCT | 从应用程序启动到采样时的full gc的耗费工夫(秒) |
GCT | 从应用程序启动到采样时gc的总工夫 |
interval参数: 用于指定输入统计数据的周期,单位为毫秒。即:查问距离
count参数: 用于指定查问的总次数
-t参数: 能够在输入信息前加上一个Timestamp列,显示程序的运行工夫。单位:秒
-h参数: 能够在周期性数据输入时,输入多少行数据后输入一个表头信息
补充: jstat还能够用来判断是否呈现内存透露。
第1步:在长时间运行的 Java 程序中,咱们能够运行jstat命令间断获取多行性能数据,并取这几行数据中 OU 列(即已占用的老年代内存)的最小值。
第2步:而后,咱们每隔一段较长的工夫反复一次上述操作,来取得多组 OU 最小值。如果这些值呈上涨趋势,则阐明该 Java 程序的老年代内存已使用量在一直上涨,这意味着无奈回收的对象在一直减少,因而很有可能存在内存透露。
2.4. jinfo:实时查看和批改JVM配置参数
jinfo(Configuration Info for Java):查看虚拟机配置参数信息,也可用于调整虚拟机的配置参数。在很多状况卡,Java应用程序不会指定所有的Java虚拟机参数。而此时,开发人员可能不晓得某一个具体的Java虚拟机参数的默认值。在这种状况下,可能须要通过查找文档获取某个参数的默认值。这个查找过程可能是十分艰巨的。但有了jinfo工具,开发人员能够很不便地找到Java虚拟机参数的以后值。
根本应用语法为:jinfo [options] pid
阐明:java 过程ID必须要加上
选项 | 选项阐明 |
---|---|
no option | 输入全副的参数和零碎属性 |
-flag name | 输入对应名称的参数 |
-flag [+-]name | 开启或者敞开对应名称的参数 只有被标记为manageable的参数才能够被动静批改 |
-flag name=value | 设定对应名称的参数 |
-flags | 输入全副的参数 |
-sysprops | 输入零碎属性 |
jinfo -sysprops
> jinfo -syspropsjboss.modules.system.pkgs = com.intellij.rtjava.vendor = Oracle Corporationsun.java.launcher = SUN_STANDARDsun.management.compiler = HotSpot 64-Bit Tiered Compilerscatalina.useNaming = trueos.name = Windows 10...
jinfo -flags
> jinfo -flags 25592Non-default VM flags: -XX:CICompilerCount=4 -XX:InitialHeapSize=333447168 -XX:MaxHeapSize=5324668928 -XX:MaxNewSize=1774714880 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=111149056 -XX:OldSize=222298112 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGCCommand line: -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:8040,suspend=y,server=n -Drebel.base=C:\Users\Vector\.jrebel -Drebel.env.ide.plugin.version=2021.1.2 -Drebel.env.ide.version=2020.3.3 -Drebel.env.ide.product=IU -Drebel.env.ide=intellij -Drebel.notification.url=http://localhost:7976 -agentpath:C:\Users\Vector\AppData\Roaming\JetBrains\IntelliJIdea2020.3\plugins\jr-ide-idea\lib\jrebel6\lib\jrebel64.dll -Dmaven.home=D:\eclipse\env\maven -Didea.modules.paths.file=C:\Users\Vector\AppData\Local\JetBrains\IntelliJIdea2020.3\Maven\idea-projects-state-596682c7.properties -Dclassworlds.conf=C:\Users\Vector\AppData\Local\Temp\idea-6755-mvn.conf -Dmaven.ext.class.path=D:\IDEA\plugins\maven\lib\maven-event-listener.jar -javaagent:D:\IDEA\plugins\java\lib\rt\debugger-agent.jar -Dfile.encoding=UTF-8
jinfo -flag
> jinfo -flag UseParallelGC 25592-XX:+UseParallelGC> jinfo -flag UseG1GC 25592-XX:-UseG1GC
jinfo -flag name
> jinfo -flag UseParallelGC 25592-XX:+UseParallelGC> jinfo -flag UseG1GC 25592-XX:-UseG1GC
jinfo -flag [+-]name
> jinfo -flag +PrintGCDetails 25592> jinfo -flag PrintGCDetails 25592-XX:+PrintGCDetails> jinfo -flag -PrintGCDetails 25592> jinfo -flag PrintGCDetails 25592-XX:-PrintGCDetails
拓展:
java -XX:+PrintFlagsInitial 查看所有JVM参数启动的初始值
[Global flags] intx ActiveProcessorCount = -1 {product} uintx AdaptiveSizeDecrementScaleFactor = 4 {product} uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product} uintx AdaptiveSizePausePolicy = 0 {product}...
java -XX:+PrintFlagsFinal 查看所有JVM参数的最终值
[Global flags] intx ActiveProcessorCount = -1 {product}... intx CICompilerCount := 4 {product} uintx InitialHeapSize := 333447168 {product} uintx MaxHeapSize := 1029701632 {product} uintx MaxNewSize := 1774714880 {product}
java -XX:+PrintCommandLineFlags 查看哪些曾经被用户或者JVM设置过的具体的XX参数的名称和值
-XX:InitialHeapSize=332790016 -XX:MaxHeapSize=5324640256 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
2.5. jmap:导出内存映像文件&内存应用状况
jmap(JVM Memory Map):作用一方面是获取dump文件(堆转储快照文件,二进制文件),它还能够获取指标Java过程的内存相干信息,包含Java堆各区域的应用状况、堆中对象的统计信息、类加载信息等。开发人员能够在控制台中输出命令“jmap -help”查阅jmap工具的具体应用形式和一些规范选项配置。
官网帮忙文档:https://docs.oracle.com/en/java/javase/11/tools/jmap.html
根本应用语法为:
- jmap [option] <pid>
- jmap [option] <executable <core>
- jmap [option] [server_id@] <remote server IP or hostname>
选项 | 作用 |
---|---|
-dump | 生成dump文件(Java堆转储快照),-dump:live只保留堆中的存活对象 |
-heap | 输入整个堆空间的详细信息,包含GC的应用、堆配置信息,以及内存的应用信息等 |
-histo | 输入堆空间中对象的统计信息,包含类、实例数量和共计容量,-histo:live只统计堆中的存活对象 |
-J <flag> | 传递参数给jmap启动的jvm |
-finalizerinfo | 显示在F-Queue中期待Finalizer线程执行finalize办法的对象,仅linux/solaris平台无效 |
-permstat | 以ClassLoader为统计口径输入永恒代的内存状态信息,仅linux/solaris平台无效 |
-F | 当虚拟机过程对-dump选项没有任何响应时,强制执行生成dump文件,仅linux/solaris平台无效 |
阐明:这些参数和linux下输出显示的命令多少会有不同,包含也受jdk版本的影响。
> jmap -dump:format=b,file=<filename.hprof> <pid>> jmap -dump:live,format=b,file=<filename.hprof> <pid>
因为jmap将拜访堆中的所有对象,为了保障在此过程中不被利用线程烦扰,jmap须要借助平安点机制,让所有线程停留在不扭转堆中数据的状态。也就是说,由jmap导出的堆快照必然是平安点地位的。这可能导致基于该堆快照的剖析后果存在偏差。
举个例子,假如在编译生成的机器码中,某些对象的生命周期在两个平安点之间,那么:live选项将无奈探知到这些对象。
另外,如果某个线程长时间无奈跑到平安点,jmap将始终等上来。与后面讲的jstat则不同,垃圾回收器会被动将jstat所须要的摘要数据保留至固定地位之中,而jstat只需间接读取即可。
2.6. jhat:JDK自带堆剖析工具
jhat(JVM Heap Analysis Tool):Sun JDK提供的jhat命令与jmap命令搭配应用,用于剖析jmap生成的heap dump文件(堆转储快照)。jhat内置了一个微型的HTTP/HTML服务器,生成dump文件的剖析后果后,用户能够在浏览器中查看剖析后果(剖析虚拟机转储快照信息)。
应用了jhat命令,就启动了一个http服务,端口是7000,即http://localhost:7000/,就可...
阐明:jhat命令在JDK9、JDK10中曾经被删除,官网倡议用VisualVM代替。
根本实用语法:jhat <option> <dumpfile>
option参数 | 作用 |
---|---|
-stack false|true | 敞开|关上对象调配调用栈跟踪 |
-refs false|true | 敞开|关上对象援用跟踪 |
-port port-number | 设置jhat HTTP Server的端口号,默认7000 |
-exclude exclude-file | 执行对象查问时须要排除的数据成员 |
-baseline exclude-file | 指定一个基准堆转储 |
-debug int | 设置debug级别 |
-version | 启动后显示版本信息就退出 |
-J <flag> | 传入启动参数,比方-J-Xmx512m |
2.7. jstack:打印JVM中线程快照
jstack(JVM Stack Trace):用于生成虚拟机指定过程以后时刻的线程快照(虚拟机堆栈跟踪)。线程快照就是以后虚拟机内指定过程的每一条线程正在执行的办法堆栈的汇合。
生成线程快照的作用:可用于定位线程呈现长时间进展的起因,如线程间死锁、死循环、申请内部资源导致的长时间期待等问题。这些都是导致线程长时间进展的常见起因。当线程呈现进展时,就能够用jstack显示各个线程调用的堆栈状况。
官网帮忙文档:https://docs.oracle.com/en/java/javase/11/tools/jstack.html
在thread dump中,要注意上面几种状态
- <mark>死锁,Deadlock(重点关注)</mark>
- <mark>期待资源,Waiting on condition(重点关注)</mark>
- <mark>期待获取监视器,Waiting on monitor entry(重点关注)</mark>
- <mark>阻塞,Blocked(重点关注)</mark>
- 执行中,Runnable
- 暂停,Suspended
- 对象期待中,Object.wait() 或 TIMED_WAITING
- 进行,Parked
option参数 | 作用 |
---|---|
-F | 当失常输入的申请不被响应时,强制输入线程堆栈 |
-l | 除堆栈外,显示对于锁的附加信息 |
-m | 如果调用本地办法的话,能够显示C/C++的堆栈 |
2.8. jcmd:多功能命令行
在JDK 1.7当前,新增了一个命令行工具jcmd。它是一个多功能的工具,能够用来实现后面除了jstat之外所有命令的性能。比方:用它来导出堆、内存应用、查看Java过程、导出线程信息、执行GC、JVM运行工夫等。
官网帮忙文档:https://docs.oracle.com/en/java/javase/11/tools/jcmd.html
jcmd领有jmap的大部分性能,并且在Oracle的官方网站上也举荐应用jcmd命令代jmap命令
jcmd -l:列出所有的JVM过程
jcmd 过程号 help:针对指定的过程,列出反对的所有具体命令
jcmd 过程号 具体命令:显示指定过程的指令命令的数据
- Thread.print 能够替换 jstack指令
- GC.class_histogram 能够替换 jmap中的-histo操作
- GC.heap_dump 能够替换 jmap中的-dump操作
- GC.run 能够查看GC的执行状况
- VM.uptime 能够查看程序的总执行工夫,能够替换jstat指令中的-t操作
- VM.system_properties 能够替换 jinfo -sysprops 过程id
- VM.flags 能够获取JVM的配置参数信息
2.9. jstatd:近程主机信息收集
之前的指令只波及到监控本机的Java应用程序,而在这些工具中,一些监控工具也反对对近程计算机的监控(如jps、jstat)。为了启用近程监控,则须要配合应用jstatd 工具。命令jstatd是一个RMI服务端程序,它的作用相当于代理服务器,建设本地计算机与近程监控工具的通信。jstatd服务器将本机的Java应用程序信息传递到近程计算机。