必须看过Java-JVM基础(调优必须知道)
知道JVM大概的情况,这篇文章我博客里有
堆区(Java堆:所有的线程共享该区域)
通过new的方式创建的对象(一个类的实例)、数组所占的空间。
非堆区: (代码、常量、外部访问, 比如: 流在传输数据时所占用的资源等)
调优注意事项:
以下都是我工作中遇到的并使用的一些参数,没写的就是还没有用到过
设置堆的最小空间大小。 建议和-Xmx(最大堆内存)设置一样
设置方式
-Xms4096M
设置堆的最大空间大小。
设置方式
-Xmx4096M
堆内新生代的大小。剩下的就是老年代的了: -Xmx减去-Xmn
设置方式
-Xmn1024M
置每个线程可使用的内存大小,即栈的大小。在相同物理内存下,减小这个值能生成更多的线程,当然操作系统对一个进程内的线程数还是有限制的,不能无限生成。线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会出现内存溢出的错误。 JVM默认是1024KB
设置方式
-Xss256k
设置永久代最小空间大小。 。建议和-XX:MaxPermSize设置一样
设置方式
-XX:PermSize=524m
设置永久代最小空间大小。
设置方式
-XX:MaxPermSize=524m
1.8之前叫永久代,1.8之后叫元空间
初始原空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:
规则: 如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
建议设置此值,JVM默认20.8m 防止超过此值,扩充时候会不断触发Fuil GC ,我们给他默认调整为524m
设置方式
-XX:MetaspaceSize=524m
设置永久代最大空间大小。默认是没有限制的。 本地物理内存多大那么就能多大
建议设置此值防止,无限制占用物理内存,和MetaspaceSize保持一致,这样就不会因为元空间满导致不断的Fuil GC ,而是直接报错java.lang.OutOfMemoryError: Metaspace,
这样我们就不会误解是堆内存不够的原因,而是元空间不够的原因,这样就能很好定位到问题
设置方式
-XX:MaxMetaspaceSize=524m
设置年轻代和年老代的比值 ( 项目不同设置也会不同需要分析问题,反正尽量让老年代尽量少full gc)
例如: 3表示新生代占年老代的1/3,占整个堆内存的1/4,因为还有一个元空间
设置方式
-XX:NewRatio=3
年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。
如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
设置方式
-XX:SurvivorRatio=6
设置转入老年代的存活次数。如果是0,则直接跳过新生代进入老年代 JVM默认15
设置方式
-XX:MaxTenuringThreshold=5
设置字符串去重
1.java应用内存里面的字符串占比大概是25%。
2.java应用内存里面的字符串有13.5%是重复的。
3.字符串的平均长度是45。
由于存在重复字符串导致高达13.5%的内存被浪费了!你可以用分析工具来分析看下你的应用中有多少内存是因为重复字符串被浪费掉的
注意只有:
设置方式
-XX:+UseStringDeduplication
手动指定对象大小,当对象达到指定大小时直接存放到老年代中,由于新生代大多使用复制算法,为了节省复制算法耗时
设置方式
XX:PretenureSizeThreshold=10M
System.gc()默认会触发一次Full GC,如果在代码中不小心调用了System.gc()会导致JVM间歇性的暂停 ,开启后,会导致System.gc()调用变成一个空调用
如果发生这样的错误的话 :java.lang.OutOfMemoryError: Direct buffer memory.
那么就是我们项目中使用了Netty框架或者NIO 导致无法申请到足够的堆外内存,从而产生的问题
这时候我们就不能开启这个参数了,一般情况下是建议开启的
设置方式
-XX:+DisableExplicitGC
需要分析GC日志的话需要添加下面这些参数
-XX:+PrintGCDateStamps:打印 GC 发生的时间戳。
-XX:+PrintTenuringDistribution:打印 GC 发生时的代龄信息。
-XX:+PrintGCApplicationStoppedTime:打印 GC 停顿时长
-XX:+PrintGCApplicationConcurrentTime:打印 GC 间隔的服务运行时长
-XX:+PrintGCDetails:打印 GC 详情,包括 GC 前/内存等。
-Xloggc:/home/log/gc.log:指定 GC log 的路径
一般设置下面这些就够了
-XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc:./gclogs/gc.log
支持1.8和之前 jdk9之后不一样了自行百度
内容大概是这样的:
看不懂没关系有
在线分析工具
GCEasy: https://gceasy.io/gc-index.jsp
离线版的分析工具:
GCViewer
下载地址: 但是需要自己解压然后进入到java目录启动gcviewer.xx.jar
本人以提取好的gcviewer-1.37-SNAPSHOT.jar
链接:https://pan.baidu.com/s/1i_F9FQRJwaxKylHR-CtQ-Q
提取码:1234
gcviewer具体使用教程百度自行查找
GChisto:
gitHub下载: https://github.com/jewes/gchisto
需要使用IDEA 自行打包或者直接在项目里运行也行
本人以打包好的gc-gchisto.jar
链接:https://pan.baidu.com/s/1j3uI3T7XbMd5h2jxOwJGoQ
提取码:1234
需要使用java -jar gc-gchisto.jar 启动 然后把日子放进去就行
GChisto具体使用教程百度自行查找
是堆栈溢出时候打印出日志文件
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/home/log/java_dump.hprof
手动导出dump文件命令:
ps -ef | grep java (左边第二个就是 PID)
jmap -dump:format=b,file=heapDump.hprof PID 进程号
注:后缀名必须hprof结尾,然后下载此文件到本地
使用 MAT Java 堆内存分析工具。
http://www.eclipse.org/mat/downloads.php 官网下载地址(新版需要JDK11+才能打开)
我这有旧版JDK1.8能打开的MAT
链接:https://pan.baidu.com/s/1x_oLOR7v6j9kW5wSELXOew
提取码:1234
工具下载好后解压文件夹,然后打开软件
之后new—>open heap dump选择下载的堆文件
具体使用方式查百度这里就不多啰嗦了
java程序遇到致命错误打印日志 比如:java线程莫名其妙死掉了
-XX:ErrorFile=/home/hd/java_error%p.log
使用 CrashAnalysis工具
https://github.com/xpbob/CrashAnalysis gitHua 下载地址
百度网盘CrashAnalysis-1.0
链接:https://pan.baidu.com/s/1oob9dAplWo73livzDj0MbA
提取码:1234
下载下来是jar 需要使用 java -jar CrashAnalysis-1.0-SNAPSHOT.jar 启动
然后使用工具把错误文件打开就行,就会自动分析问题
cms 和G1 的选择上 我觉得G1是永远的神
-XX:+UseG1GC (开启G1收集器)
不要手动设置新生代和老年代的大小,我们只要设置整个堆的大小, 如果手动设置了新生代和老年代的大小大小就意味着放弃了G1的自动调优。
-XX:MaxGCPauseMillis=400 (设置GC最大暂停时间200~500都行)
一般情况下这个值设置到200ms或者500ms都是可以的(不同情况下会不一样),但如果设置成50ms就不太合理。暂停时间设置的太短,就会导致出现G1跟不上垃圾产生的速度。最终退化成Full GC。所以对这个参数的调优是一个持续的过程,逐步调整到最佳状态。暂停时间只是一个目标,并不能总是得到满足。
我公司之前遇到的问题:
项目总是莫名其妙的宕机,经过不断的排查,通过,java宕机后自动生成的,hs_err_pid.log日志,发现是java解析字节码时候出错了.具体为啥解析错误,这个是JIT的问题了,我们也不知道
解决方案是:
混合模式
换成解释模式
或者编译模式
报错信息
java.lang.OutOfMemoryError: Java heap space
报错原因
1.无法在 Java 堆中分配对象
2.吞吐量爆增一瞬间把堆给充满了,而且这些对象还无法一次收回
3.应用程序无意中保存了对象引用,对象无法被 GC 回收
4.应用程序过度使用 finalizer。finalizer 对象不能被 GC 立刻回收。finalizer 由结束队列服务的守5.护线程调用,有时 finalizer 线程的处理能力无法跟上结束队列的增长
解决办法
1.将堆内存 dump 下来,使用 堆文件分析工具,分析一下,解决内存泄漏;
2.如果没有内存泄漏,使用 -Xmx 增大堆内存;
3.自定义的 finalizer对象,考虑其存在的必要性。
报错信息
java.lang.OutOfMemoryError:GC overhead limit exceeded
报错原因
垃圾回收器超过98%的时间用来做垃圾回收,但回收了不到2%的堆内存。
解决办法
1.添加 -XX:-UseGCOverheadLimit 这个参数去掉报警,但这只是一种掩耳盗铃的方式,一般出现 GC overhead limit exceeded 说明离真正的 OOM 也不远了;
2.将堆内存 dump 下来,使用堆文件分析工具,分析一下,解决内存泄漏;
3.如果没有内存泄漏,使用 -Xmx 增大堆内存;
报错信息
java.lang.OutOfMemoryError: PermGen space 或者
java.lang.OutOfMemoryError: Metaspace(Java8及以上)
报错原因
永久代是 HotSot 虚拟机对 方法区的具体实现,存放了已被虚拟机加载的类信息、常量、静态变量、JIT编译后的代码等。需要注意的是,在Java8后,永久代有了一个新名字:元空间,元空间使用的是本地内存。
永久代里存在的信息也有了若干变化:
解决办法
永久代/元空间 溢出的原因比较简单,解决方法有如下几种:
1.Java8前的应用:使用 -XX:MaxPermSize 增加永久代的大小;
2.Java8及以后的应用:如果设置了 -XX:MaxMetaSpaceSize,调整其大小或者移除掉该参数。
3.尝试重启JVM。
报错信息
java.lang.OutOfMemoryError : unable to create new native Thread
报错原因
虚拟机在拓展栈空间时,无法申请到足够的内存空间。一般出现在内存空间过小,但是又创建了大量的线程的场景。(就是没有内存创建线程了)
解决办法
配置每个用户的最大线程数方法如下:
修改/etc/security/limits.conf文件, 在文件末尾添加注意*不要少了
* soft nofile 204800
* hard nofile 204800
* soft nproc 204800
* hard nproc 204800
将以上保存好,然后重启服务器,再使用 ulimit -n
报错信息
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
报错原因
这种情况一般是由于不合理的数组分配请求导致的
解决办法
1.消除代码逻辑错误或者调整堆大小。
2.修复应用程序中分配巨大数组的 bug
报错信息
java.lang.OutOfMemoryError: Out of swap space
报错原因
这种情况一般是操作系统导致的,可能的原因有:
解决办法
报错信息
java.lang.OutOfMemoryError: stack_trace_with_native_method
报错原因
这种情况表明,本地方法在运行时出现了内存分配失败。
和java.lang.OutOfMemoryError : unable to create new native Thread 保存不同,
方法栈溢出出现在 JVM 的代码层面,而本地方法溢出发生在JNI代码或本地方法处。
解决办法
先检查下本地内存是否满了,在检查jvm堆的使用情况,如果都没啥问题那么,直接换JDK就行了 这是JVM底层的问题
可以参照下面方式添加jvm参数调优
nohup java -Xmx2048m -Xms2048m -jar target/demo-0.0.1-SNAPSHOT.jar>demo.log 2>&1 &
设置applicaton.yml
tomcat最大线程数,默认为200 : server.tomcat.max-threads: 800
进入tomcat/bin目录catalina.bat 中加入下面代码,位置cygwin=false前(文件搜索定位)
JAVA_OPTS="-Xms4096m -Xmx4096m "
修改setDomainEnv.cmd文件
我们只需要调整 64BIT的就行 32BIT不用管,这代表是32位机器,现在基本服务都是64位的了
if和else里的都需要调整,
启动服务必须使用 startWebLogic.sh 才有效 其他方式启动无效…具体怎么调整JVM自行百度搜索
ps -ef | grep java
只找到自己进程的pid (左边第二个就是)然后
jinfo -flags pid
就能看到此程序生效的JVM参数,如果看不到就没有生效或者其他…自己找原因
别一遇到问题就找JVM的事情,他跟你有仇???
下面将展示linux-java问题排查正确思路,建议收藏起来以后遇到问题好排查
检查命令: top 查看程序进程的cpu是否太高
16核的cpu最高可以达到1600% 正常在1000%左右都是正常) 而1核cpu最高只能100%
查看当前linux 是多少核的 top 然后按下键盘1
cpu过高最终可能发生宕机,或者cpu强制杀死占用过高的进程
我们还可以使用top查看指定程序的内存使用情况
PID:进程的ID
USER:进程所有者
PR:进程的优先级别,越小越优先被执行
NInice:值
VIRT:进程占用的虚拟内存
RES:进程占用的物理内存
SHR:进程使用的共享内存
S:进程的状态。S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值为负数
%CPU:进程占用CPU的使用率
%MEM:进程使用的物理内存和总内存的百分比
TIME+:该进程启动后占用的总的CPU时间,即占用CPU使用时间的累加值。
COMMAND:进程启动命令名称
如果网络不通畅或者波动较大的话会导致,用户访问失败,
命令: ping -s 65507 ip -c 1000
如果在监控过程中突然有一个time耗时特别长的,比如:
平均是3~4ms(毫秒)如果中途出现 30多或者300多那么就是网络不稳定
如果直接ping不通那么就是网络异常(导致系统访问失败)
微服项目需要将当前项目 , 相关联的服务器也ping下
命令: df -hT
主要看硬盘内存是否满了
主要涉及: 系统缓存,文件下载,内存交互… 最终可能发生宕机,死机…异常情况
命令 free -m (单位是mb)
total 是内存总量
used 是当前以使用多少
free 是剩余空闲内存
Shared:多个进程共享的内存总额。
buff/cache 是系统缓存
available 还可以被 应用程序 使用的物理内存大小
available = free + buffer + cache (注:只是大概的计算方法)
对于应用程序来说,buffers/cached 是等于可用的,因为buffer/cached是为了提高文件读取的性能,当应用程序需在用到内存的时候,buffer/cached会很快地被回收。
提醒必须使用jdk1.8+的才行,1.7的话下面有些命令或者工作可能你使用不了
使用Arthas工具或者JProfiler 工具 ,这两种工具使用教程我在博客里有自行搜索,
我建议使用Arthas因为可用不用重启项目对项目影响最小
使用jstat命令,比可视化软件好用多了,前提是用数量了
命令如下:
ps -ef | grep java 找到PID(左边第二个)
jstat -gcutil PID 5000
效果图(单位秒):
注意: 别误会了,图中的时间都是叠加的 比如YGC是17.929秒 ,是295次Young GC 所有总和,可以看到295~296之间差55毫秒而这55毫秒,就是1次Young GC的时间 ,其他以此类推
参数介绍:
显示列名 | 具体描述 |
---|---|
S0C | 年轻代中第一个survivor(幸存区)的容量 (字节) |
S1C | 年轻代中第二个survivor(幸存区)的容量 (字节) |
S0U | 年轻代中第一个survivor(幸存区)目前已使用空间 (字节) |
S1U | 年轻代中第二个survivor(幸存区)目前已使用空间 (字节) |
EC | 年轻代中Eden(伊甸园)的容量 (字节) |
EU | 年轻代中Eden(伊甸园)目前已使用空间 (字节) |
OC | Old代的容量 (字节) |
OU | Old代目前已使用空间 (字节) |
PC | Perm(持久代)的容量 (字节) |
PU | Perm(持久代)目前已使用空间 (字节) |
YGC | 从应用程序启动到采样时年轻代中gc次数 |
YGCT | 从应用程序启动到采样时年轻代中gc所用时间(秒) |
FGC | 从应用程序启动到采样时old代(全gc)gc次数 |
FGCT | 从应用程序启动到采样时old代(全gc)gc所用时间(秒) |
GCT | 从应用程序启动到采样时gc用的总时间(秒) |
JVM在深度点的排查就需要 jstat的其他命令了,以上基本能解决日常百分之90以上的jvm问题了,在深入的话,其实就算发现问题,可能你也可能没啥办法去解决,因为都是java内部本身的问题,基本都是直接换JDK版本
比如: 类的加载情况, 类的编译情况,已经各堆之间更细节的分布情况…这个我博客里有jstat的细节方面的命令自行查找
如果按照上面都没找到问题所在,那么这就棘手了,我之前也遇到过找了1个多星期的问题,最后别人告诉我,拟生产环境统一配置
中没有分别给各自,配置各自应用的IP地址,导致每次访问时候都要把他所有前面的服务器都走一遍,所以访问一直卡10秒,很固定的那种,我叫哪一个气啊…
所以很多时候都是人为的问题,你可以找到一个对项目全部配置都很了解的人,让他全部检查一遍确认没问题才行
以上都没问题了那么,就只能从代码入手了,如果是性能方面的话,
java进程突然挂了无外乎三种情况:
先检查是不是linux的OOM killer杀死的java
cd /var/log
grep -C 5 "java" messages
这就有问题了,
dumping core 这是程序崩溃了 - 我们可以看jvm生成的hs_err_pid.log 日志(系统自动生成)
或者
如果出现 kernel: Out of memory: Kill process意味着整个系统的内存已经不足,如果不杀死进程的话,就会导致系统的崩溃.
具体问题这个可以看 jvm 配置的 各种错误日志
在上面都有配置教程,3大日志(堆日志,致命日志,gc日志)
点赞 -收藏-关注-便于以后复习和收到最新内容有其他问题在评论区讨论-或者私信我-收到会在第一时间回复如有侵权,请私信联系我感谢,配合,希望我的努力对你有帮助^_^
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_45203607/article/details/120379758
内容来源于网络,如有侵权,请联系作者删除!