当前位置:网站首页 > 今日头条 > 正文

大悲咒,JVM 调优-给你的java使用看治病,米奇妙妙屋

admin 0

java 运用

1 cpu 负载过高

1.1 剖析问题

  1. 首要咱们经过top 指令进行剖析,找出耗费最多cpu的java 进程id 。
  2. 找出对应的进程id 后,咱们能够经过 top 芭蕾舞少女-Hp 进程id 指令来找出该进程中占用cpu最多的前几个线程id。
  3. 咱们运用 jstack -l 进程pid > /tmp/java_pid.log 输出java的仓库日志到文件 /tmp/java_pid.log。
  4. 咱们将刚刚查询到的java进程中占用cpu最多的前几个线程id。进行转化为16进制。
printf "%X" 线程id 
  1. 咱们在java仓库日志文件中找到上面转化为16进制的线程的pid对应的 日志。
  2. 实践操作过程流程图:


  1. 弥补:有时可能是咱们代码创立线程过多导致的问题:
# 检查该进程有多少线程
ps p 9534 -L -o pcpu,pmem,pid,tid,time,tname,cmd|wc -l

1.2 解决方案

咱们把对应的线程id的日志拿给咱们的开发,进行定位过错,这儿简单定位出的过错是:
  1. 线程处于WAITING(等候状况)
  2. 线程BLOCKED(堵塞)

我能够把定位到代码方位,通知开发,让开发检查对应的代码是否有问题。

2 内存占用过多

​ Java 虚拟机在履行 Java 程序的过程中会把它办理的内存区分红若干个不同的数据区域。

这些组成部分一些是线程私有的,其他的则是线程同享的。

线程私有的:

  • 程序计数器
  • 虚拟机栈
  • 本地办法栈

线程同享的:

  • 办法区
  • 直接内存

2.1 从内存收回方面

​ Java 堆是废物搜集器办理的首要区域,因而也被称作榆绿毛萤叶甲GC堆(Garbage Collected Heap).从废物收回的视点,因为现在搜集器根本都选用分代废物搜集算法,所以Java堆还能够细分为:重生代和老时代:再详尽一点有:Eden空间、From Survivor、To Survivor空间等。进一步区分的意图是更好地收回内存,或许更快地分配内存。

在 JDK 1.8中移除整个永久代,取而代之的是一个叫元空间(Metaspace)的区域(永久代运用的是JVM的堆内存空间,而元空间运用的是物理内存,直接遭到本机的物理内存约束)。关于metaspace的具体解说看:JVM源码剖析之Metaspace解密大悲咒,JVM 调优-给你的java运用看看病,米奇妙妙屋

​ java 实践的内存运用是这样的,大多数状况下,目标在重生代中 eden 区分配。当 eden 区没有满足空间进行分配时,虚拟机将主张一次Minor GC(重生代GC).将eden 区的一些存活目标移动到Survivor 区,当Survivor区的巨细,不行贮存eden 区的存活目标时,那么就会将它移动到晚年区(Old Generation ),当晚年区满了时分将触发一次 Full GC .

​ 在实践工作中,咱们能够运用 jmap -heap pid 来检查当时的进程的 java 堆的散布状况。

[root@iz23nb5ujp69 ~]# jmap -heap 11764
Attaching to process ID 11764, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.73-b02
using thread-local object allocation.
Parallel GC with 2 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0 #GC后,假设发现闲暇堆内存占到整个预估堆内存的40%,则扩大堆内存的预估最大值,但不超越固定最大值。默许该值是40
MaxHeapFreeRatio = 100 #GC后,假设发现闲暇堆内存占到整个预估堆内存的100%,则缩短堆内存预估最大值。默许的是70
MaxHeapSize = 2147483648 (2048.0MB) # 最大的堆内存
NewSize = 715653120 (682.5MB) # 重生代初始巨细
MaxNewSize = 715653120 (682.5MB) # 重生代最大巨细
OldSize = 1431830528 (1365.5MB) #老时代
NewRatio = 2 # 重生代和老时代的 内存份额: 1:2 默许值
SurvivorRatio = 8 # Eden区与Survivor区的巨细比值,设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年青代的1/10,Eden区和Survivor区 的实践份额值是会变化的
MetaspaceSize = 21807104 (20.796875MB) # 元空间巨细
CompressedClassSpaceSize = 1073741824 (1024.0MB) # 紧缩时可用的最大的内存
MaxMetaspaceSize = 17592186044415 MB #元空间可用最大巨细
G1HeapRegionSize = 0 (0.0MB) #G1 搜集器的内存巨细
Heap Usage:
PS Young Generation # 重生代
Eden Space: # Eden
capacity = 372768768 (355.5MB)
used = 185979712 (177.36407470703125MB)
free = 186789056 (178.13592529296875MB)
49.89144154909459% used # eden 可用区运用率,该值满了将触发 young gc
From Space: # Survivor1
capacity = 175112192 (167.0MB)
used = 47983120 (45.76026916503906MB)
free = 127129072 (121.23973083496094MB)
27.401358781460516% used
To Space: # Survivor2
capacity = 167772160 (160.0MB)
used = 0 (0.0MB)
free = 167772160 (160.0MB)
0.0% used
PS Old Generation # 老时代
capacity = 1431830528 (1365.5MB)
used = 257274632 (245.35620880126953MB)
free = 1174555896 (1120.1437911987305MB)
17.9682320616089% used # 老时代 可用区运用率,该值满了将触发 full gc

恰当的young gc 能够让整理一些不存活的目标,可是短时刻许多的 young GC 是会导致 Full GC 的,那么Full gc 是尽量不要发作的,当一个运用,发作许多的full GC是不正常的, 过多的GC和Full GC是会占用许多的体系资源(首要是CPU),影响体系的吞吐量。

​ young gc:

Metadata GC

​ full gc :

GC 日志解析

对了,如安在日志中打印GC日志,咱们在后面的装备中会讲到。

[GC (Allocation Failure) [DefNew: 279616K->19156K(314560K), 0.0595827 secs] 279616K->19256K(1013632K), 0.0601044 secs] [Times: user=0.03 sys=0.02, real=0.06 secs] 
GC:
标明进行了一次废物收回,前面没有Full润饰,标明这是一次Minor GC。
Allocation Failure:
标明本次引起GC的原因是因为在年青代中没有满足的空间能够存储新的数据了。
279616K->19156K(314560K) 260460
三个参数别离为:GC前该内存区域(这儿是年青代)运用容量,GC后该内存区域运用容量,该内存区域总容量eroticax。
0.0595827 secs
表明GC耗时
279616K->19256K(1013632K)
堆区废物收回前的巨细,堆区废物收回后美津植秀泡泡氧气面膜的巨细,堆区总巨细。

0.0071945 secs
Times: user=女扮男装惑冷王0.01 sys=0.00, real=0.01 secs
别离表明用户态耗时,内核态耗时和总耗时
重生代整理的内存:279616 - 19156 = 260460k
堆区削减的内存:大悲咒,JVM 调优-给你的java运用看看病,米奇妙妙屋279616 - 19256 = 260360k
重生代存到老时代的 数据为 260460k - 260360k



那么怎么查询一个运用发作young Gc 和Full GC 的次数和耗时时刻。咱们能够运用jstat 。

jstat 查询GC 次数和Full Gc 次数

jstat -gcutil pid 2000 10 (每隔2秒输出一次成果,输出10次)
[root@iz23nb5ujp69 ~]# jstat -gcutil 3626 2000 10
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
56.01 0.00 8.21 88.19 98.44 96.92 27350 353.229 41 32.416 385.645
56.01 0.00 8.30 88.19 98.44 96.92 27350 353.229 41 32.416 385.645
56.01 0.00 8.41 88.19 98.44 96.92 27350 353.229 41 32.416 385.645
56.01 0.00 8.56 88.19 98.44 96.92 27350 353.229 41 32.416 385.645
56.01 0.00 9.00 88.19 98.44 96.92 27350 353.229 41 32.416 385.645
56.01 0.00 9.27 88.19 98.44 96.92 27350 353.229 41 32.416 385.645
56.01 0.00 9.34 88.19 98.44 96.92 27350 353.229 41 32.416 385.645
56.01 0.00 9.46 88.19 98.44 96.92 27350 353.229 41 32.416 385.645
56.01 0.00 9.57 88.19 98.44 96.92 27350 353.229 41 32.416 385.645
56.01 0.00 9.70 88.19 98.44 96.92 27350 353.229 41 32.416 385.645
S0 — Heap上的 Survivor space 0 区已运用空间的百分比
S1 — Heap上的 Survivor space 1 区已运用空间的百分比
E拓跋六修 — Heap上的 Eden space 区已运用空间的百分比
O — Heap上的 Old space 区已运用空间的百分比
M - 表明的是Klass Metaspace以及NoKlass Metaspace两者一共的运用率
CSS -表明的是NoKlass Metaspace的运用率
YGC — 从运用程序启动到采样时发作 Young GC 的次数
YGCT– 从运用程序启动到采样时 Young GC 所用的时刻(单位秒)
FGC — 从运用程序启动到采样时发作 Full GC 的次数
FGCT– 从运用程序启动到采样时 Full GC 所用的时刻(单位秒)
GCT — 从运用程序启动到采样时用于废物收回的总时刻(单位秒)
FGC Scavenge GC要慢,因而应该尽可能削减Full GC。

导致young Gc 和 full GC 的原因有哪些:

  1. young 可用区 设置的太小 ,young gc 设置的太小就会导致 ,屡次young gc,屡次young gc 也就导致 oldGeneration 不断增大,终究导致full gc
  2. Old Generation 设置的太小, 当 Old 救君缘Generation 太小的话就会导致 常常占满,然后会进行full GC 。
  3. System.gc()被显现调用 , 废物收回不要手动触发,尽量依托JVM本身的机制。
  4. Meta(元数据)区可用内存设置的太少。

jvm 默许运用的配大悲咒,JVM 调优-给你的java运用看看病,米奇妙妙屋置

​ 咱们拿咱们的tomcat 运用来说,咱们假设运用默许的装备,咱们运用jmap -heap 线程 检查

[root@www apache-tomcat-8.5.38]# jmap -heap 7568
Attaching to process ID 7568, please wait...
Debugger attached successfxxtubeully.
Server compiler detected.
JVM version is 25.131-b11
using thread-local object allocation.
Mark Sweep Compact大悲咒,JVM 调优-给你的java运用看看病,米奇妙妙屋 GC
Heap Configuration:
MinHeapFreeRatio = 40 # 这个默许值在上面现已提到了,关于heap,咱们尽量不要让它主动调整
MaxHeapFreeRatio = 70 # 这个默许值在上面现已提到了,关于heap,爱是蓝色的咱们尽量不要让它主动调整
MaxHeapSize = 480247smartdeblur808 (458.0MB)
NewSize = 10485760 (10.0MB)
MaxNewSize = 160038912 (152.625MB)
OldSize = 20971520 (20.0MB) # 默许的值分配不合理
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)

上面的装备很大一部分是不合理的,关于线上运用来说。

JVM装备参数(依据自己的项目状况调整)

-Xms2048m # 堆的最小内存,主张和最大内存设置的共同,以避免每次废物收回完成后JVM重新分配内存,进步GC运转的功率。
-Xmx2048m # 堆的最大内存,主张和最小内存设置的共同,以避免每次废物收回完成后JVM重新分配内存,进步GC运转的功率。
-XX:MaxHeapFreeRatio=100 #GC后,假设发现闲暇堆内存占到整个预估堆内存的100%,则缩短堆内存预估最大值。默许的是70
-XX:MinHeapFreeRatio=0 #GC后,假设发现闲暇堆内存占到整个预估堆内存的0%,则扩大堆内存的预估最大值,但不超越固定最大值。默许该值是40
-Xmn900m #设置重生代的内存,假设咱们设置了Xmx 和Xms为共同的话,那么该值的默许值为Xmx值的1/3。
-XX:MetaspaceSize=64M # 初始化的Metaspace巨细,也是最小巨细 java 8 后,用Meta替代了永久代,默许该值为20M(因体系而异),假设日志中呈现了Meta GC,那么能够进步该值。
-XX:MaxMetaspaceSize= # 这个参数用于约束Metaspace添加的上限,避免因为某些状况导致Metaspace无限的运用本地内存,影响到其他程序。
-XX:MinMetaspaceFreeRatio=40 #GC后,假设发现闲暇Meta内存占到整个预估Meta内存的40%,则扩大Meta内存的预估最大值,但不超越固定最大值。默许该值是40
-XX:MaxMetaspaceFreeRatio=70 #GC后,假设发现闲暇Meta内存占到整个预估Meta内存的70%,则缩短Meta内存预估最大值。默许的是7叶瑞财回忆学0
-XX:MaxNewSize= #设置重生代的最大值,一般默许为整个堆的1/3
-XX:NewRatio=N #设置重生大悲咒,JVM 调优-给你的java运用看看病,米奇妙妙屋代和老时代的比值,默许为2 表明 重生代占用1/3
-XX:SurvivorRatio=N #设置重生代中的 Eden 和两个Survivor 的比值,默许为8表明,eden占用 8/10,时刻中jvm会主动调整Eden 和Survivor 的值的。
-XX:MaxTenuringT大悲咒,JVM 调优-给你的java运用看看病,米奇妙妙屋hreshold=N #重生代的目标的年纪(年纪计数器)到达N值后移动到老时代,默许值为15
-XX:ParallelGCThreads=n #设置废物搜集器在并行阶段运用的线程数,主张设置为与处理器数目持平
-XX:+DisableExplicitGC #封闭System.gc() 看你的程序是否需求System.gc(),再来决议
-XX:MaxDirectMemorySize # 来指定最大的堆外内存
选用GC 收回器,不同的收回器,对应的推迟和内存不共同
## 针对Meta 细化设置
-XX:CompressedClassSpaceSize #这个参数首要是设置Klass Metaspace的巨细,不过这个参数设置了也不一定起作用,条件是能敞开紧缩指针,假设-Xmx超越了32G,紧缩指针是敞开不来的。假设有Klass Metaspace,那这块内存是和Heap连着的。
## 废物搜集器的挑选
Parallel Scavenge搜集器重视点是吞吐量(高功率的运用CPU)。CMS等废物搜集器的重视点更多的是用户线程的中止时刻(进步用户体会,削减收回的中止时刻),CMS GC算法首要是针对老生代,耐久代。所谓吞吐量便是CPU中用于运转用户代码的时刻与CPU总耗费时刻的比值。还有G1 搜集器是(java1.9的默许搜集器)。jdk1.8 默许废物搜集器Parallel Scavenge(重生代)+Parallel Old(老时代)
假设要运用CMS搜集器的话,
-XX:+UseConcMarkSweepGC # 敞开CMS GC 废物搜集器
-XX:+UseCMSInitiatingOccupancyOnly #只要敞开了这个参数,CMSInitiatingOccupancyFraction这个参数才会收效
-XX:CMSInitiatingOccupancyFraction= #触发cms gc的老生代运用率,当老生代运用到达该阈值之后,就将触发GC,该参数有必要合作UseCMSInitiatingOccupanc放学后福不福2yOnly运用才有用
-XX:+CMSClassUnloadingEnabled/-XX:-CMSClassUnloadingEnabled # 在运用CMS时,是否敞开类卸载 假设敞开 在full gc是会顺带扫描metaSpace/PermGen
-XX:+ParallelRefProcEnabled # 尽量敞开并行处理在任何当地
-XX:+CMSScavengeBeforeRemark #敞开在cms gc remark之前做一次ygc,削减gc roots扫描的目标数,然后进步remark的功率

其它参数

-Xss256k # 每个线程的仓库巨细,Xss越大,每个线程的巨细就越大,占用的内存越多,能包容的线程就越少
#Xss越小,则递归的深度越小,简单呈现栈溢出 java.lang.StackOverflowError,削减局部变量的声明,能够节约栈帧巨细,添加调用深度
-XX:+PrintGCDetails # 日志中输入GC概况日志。
-XX:+PrintHeapAtGC # 打印GC前后的具体仓库信息
-XX:+PrintGCTimeStamps # 打印GC发作的时pearlblanc间戳
-XX:+PrintGCDateStamps # 输出GC的时刻戳(以日期的方式,如 2013-05-04T21:53:59.234+0800)
# 指定GC 日志和过错日志,OOM
-XX:ErrorFile=/tmp/gc/hs_err_pid%p.log # 发作过错时过错日志保存的当地
-Xloggc:/tmp/gc/gc.log # gc 日志记载的当地
-XX:+HeapDumpOnOutOfMemoryError #启用当抛出OutOfMemoryError反常时,将堆转储到文件
-XX:HeapDumpPath=/tmp/gc #当启用 Heap葛森疗法李开复驳斥谣言DumpOnOutOfMemoryError 时,贮存dump文件的途径
-XX:+PrintGCApplicationStoppedTime # 启用打印运用暂停的时刻
-XX:+TraceClassLo梁慧贤ading # 记载你的类到底是从哪个文件加载进来的
还有一些参数见文章:https://blog.csdn.net/see__you__again/article/details/51998038

期望到达的: young gc 频率适中,假设young gc 次数较少的话,一次young gc 的耗锦门医娇时就会比较长,那么最求的平衡便是: young gc 频率和 young gc 耗时 到达两者的平衡值。Fu2004辣妹奸细之危机四伏ll Gc 尽量不要有。

参阅装备

-Xms2048m -Xmx2048m -XX:MaxHeapFreeRatio=100 -XX:MinHeapFreeRatio=0 -Xmn900m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -XX:MinMetaspaceFreeRatio=0 -XX:SurvivorRatio=7 -XX:MaxMetaspaceFreeRatio=100 -XX:MaxTenuringThreshold=14 -XX:ParallelGCThreads=2 -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:+PrintGCDetails

调优完之后的作用是 ,20-30分钟发作一次Minor GC(young gc ) 每次的GC 耗时,无Full GC 和Meta 导致的GC。

事例剖析:

GC 日志

 [GC (Metadata GC Threshold) [PSYoungGen: 282234K->101389K(523264K)] 589068K->410894K(1921536K), 0.1611903 secs] [Times: user=0.18 sys=0.00, real=0.16 secs]

[Full GC (Metadata GC Threshold) [PSYoungGen大悲咒,JVM 调优-给你的java运用看看病,米奇妙妙屋: 101389K->0K(523264K)] [ParOldGen: 309505K->258194K(1398272K)] 410894K->258194K(1921536K), [Metaspace: 268611K->268101K(1294336K)], 1.8562117 secs] [Times: user=1.80 sys=0.08, real=1.86 secs]

咱们能够在日志中看到触发了一次一般的GC 和一次 Full GC ,两次GC的原因都是Meta区GC导致的,咱们看Full Gc 的日志, young 区的内存没有运用完,old区的内存也没有占用满,只要Meta 区的内存占用满了,那么导致这个问题的便是Meta 区设置的太小。

扩展:

检查 java 的一些默许装备

java -XX:+PrintFlagsInitial
示例: 检查Meta(元空间的默许巨细)
java -XX:+PrintFlagsInitial |grep MetaspaceSize

2.2 从代码层面

从代码层面的话,咱们就需求剖分出 每个程序的内存散布状况,每个类的实例数,占用内存最大的类,和他们活动的时长,是否有内存走漏啊,内存走漏的疑点。

内存走漏:目标现已死了,无法经过废物搜集器进行主动收回,无法开释内存。

内存溢出:程序在请求内存时,没有满足的内存空间供其运用。

1 生成堆

剖析的条件是咱们需求拿到剖析的数据:

jmap -dump:live,format=b,file=/tmp/dump1628.dat pid 

2 下载并装置MAT

然后咱们能够运用MAT剖析东西进行剖析堆文件。

MAT下载链接 :http://www.eclipse.org/mat/(需求java环境)

3 将堆信息导入到MAT 进行剖析

内容借鉴于:https://www.cnblogs.com/AloneSword/p/3821569.html

Histogram: 列出内存中的目标,目标的个数以及巨细。

Dominator Tree: 能够列出那个线程,以及线程下面的那些目标占用的空间.

Top Consumers: 以图形的方式列出最大的object

Duplicate Classes: 列出一个类易泽睿被多个类加载引证的类名

Leak Suspects: 包括疑似内存走漏和体系概述的陈述

Top Components:列出占用超越总堆1%的组件

关于每一个分类的概况见博客:http赖银燕微博s://www.cnblogs.com/AloneSword/p/3821569.html

关于内存的调优,咱们还能够运用东西 JConsole和Java VisualVM。 等等、

END

提到最终给我们免费共享一波福利吧!我自己搜集了一些Java材料,里边就包容了一些BAT面试材料,以及一些 Java 高并发、散布式、微效劳、高性能、源码剖析、JVM等技术材料

因为渠道规矩约束,需求获取材料的朋友们能够重视小编,后台私信“Java”获取。