JVM(五) 生产环境内存溢出调优
1.gc配置参数
1.1 控制台打印gc日志
-verbose:gc
-XX:+PrintGCDetails
-XX:+PrintHeapAtGC(详细的gc信息)
1.2 输出gc日志到指定文件 -Xloggc:
(例如: -Xloggc:C:\logs\gc.log)
1.3 Gc日志分块
-XX:-UseGCLogFileRotation
-XX:GCLogFileSize = 8M
1.4 指定最小堆内存 -Xms
(例如-Xms20M指定最小堆内存为20M)
1.5 指定最大堆内存 -Xmx
(例如-Xms20M指定最大堆内存为20M)
1.6 指定新生代内存大小 -Xmn
(例如-Xmn10M指定新生代内存为10M)
1.7 指定eden区在新生代的占比 -XX:SurvivorRatio=8
(eden比S0,S1区比例为8:1:1)
1.8元空间设置大小 -XX:MetaspaceSize
初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
-XX:MaxMetaspaceSize 最大空间,默认是没有限制的。
1.9 指定创建的对象超过多少会直接创建在老年代 -XX:PretenureSizeThreshold
此参数只能在serial收集器和parnew收集器才有效 (比如 -XX:PretenureSizeThreshold=1M)
1.10 指定多大年龄的对象进入老年代 -XX:MaxTenuringThreshold
(比如 -XX:MaxTenuringThreshold=15 默认也是15次)
1.11 内存溢出时候打印堆内存快照 -XX:+HeapDumpOnOutOfMemoryError
-XX:+HeapDumpOnOutOfMemoryError
该配置会把快照保存在用户目录或者tomcat目录下,也可以通过 -XX:HeapDumpPath=/tmp/heapdump.dump来显示指定路径
1.12 使用serialGC收集器 -XX:+UseSerialGC
1.13 使用ParNew收集器 -XX:+UseParNewGC
1.14使用cms收集器 -XX:+UseConcMarkSweepGC
该标志首先是激活CMS收集器。默认HotSpot JVM使用的是并行收集器。
启动CMS多线程执行
-XX:+CMSConcurrentMTEnabled
指定CMS启动线程个数
-XX:ConcGCThreads
标志-XX:ConcGCThreads=<value>(例如:-XX:ConcGCThreads=4)
指定老年代内存达到多少的百分比进行垃圾收集
-XX:CMSInitiatingOccupancyFraction=70 和 -XX:+UseCMSInitiatingOccupancyOnly
执行多少次fullgc后执行一次full gc的标记压缩算法
默认参数场景是:-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0。这意味着每次full gc(标记清除)后,都会压缩
开启在CMS重新标记阶段之前的清除minor gc
-XX:+CMSScavengeBeforeRemark
2. GC日志详情
2.1 minor gc
1.178: [GC (Allocation Failure) [PSYoungGen: 32768K->2688K(37888K)] 32768K->2696K(123904K), 0.0048091 secs] [Times: user=0.05 sys=0.02, real=0.00 secs]
1.178: # 虚拟机启动以来的秒数
[GC (Allocation Failure)
[PSYoungGen: 32768K->2688K(37888K)] # gc类型:新生代gc前占用大小 -> 新生代gc后占用大小(新生代总容量)
32768K->2696K(123904K), # 堆空间gc前占用大小 -> 堆空间gc后占用大小(堆空间总容量)
0.0048091 secs #gc总耗时
]
[
Times:
user=0.05 #用户态耗费时间
sys=0.02, #内核态耗费时间
real=0.00 secs #整个过程实际耗费时间
]
# user+sys是CPU时间,每个CPU core单独计算,所以这个时间可能会是real的好几倍。
2.2 full gc / major gc
7.740: [Full GC (Metadata GC Threshold) [PSYoungGen: 6612K->0K(333824K)] [ParOldGen: 10378K->15906K(95744K)] 16990K->15906K(429568K), [Metaspace: 34026K->34026K(1079296K)], 0.1300535 secs] [Times: user=0.38 sys=0.00, real=0.13 secs]
7.740: #虚拟机启动以来的秒数
[
Full GC (Metadata GC Threshold) #gc类型
[PSYoungGen: 6612K->0K(333824K)] # 新生代gc前占用大小 -> 新生代gc后占用大小(新生代总容量)
[ParOldGen: 10378K->15906K(95744K)] # 老年代代gc前占用大小 -> 老年代gc后占用大小(老年代总容量)
16990K->15906K(429568K), # 堆空间gc前占用大小 -> 堆空间gc后占用大小(堆空间总容量)
[Metaspace: 34026K->34026K(1079296K)], # 元空间gc前占用大小 -> 元空间gc后占用大小(元空间总容量)
0.1300535 secs #gc总耗时
]
[
Times:
user=0.05 #用户态耗费时间
sys=0.02, #内核态耗费时间
real=0.00 secs #整个过程实际耗费时间
]
# user+sys是CPU时间,每个CPU core单独计算,所以这个时间可能会是real的好几倍。
3. 生产环境死锁定位
3.1 来一段死锁代码
package com.kawa.xuduocloud.zuul; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; /**
* @program: xuduocloud
* @author: Brian Huang
* @create: 2019-12-07 15
**/
@RestController
public class JvmController { @GetMapping("/deadLock")
public ResponseEntity<String> deadLock(){ Object lockA = new Object();
Object lockB = new Object();
new Thread(new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (lockA) {
System.out.println(name + " got lockA, want LockB");
try {
Thread.sleep(100);
} catch (InterruptedException e) { e.printStackTrace();
}
synchronized (lockB) {
System.out.println(name + " got lockB");
System.out.println(name + ": say Hello!");
}
}
}
}, "thread-lock-A").start(); new Thread(new Runnable() {
@Override
public void run() { String name = Thread.currentThread().getName();
synchronized (lockB) {
System.out.println(name + " got lockB, want LockA");
try {
Thread.sleep(100);
} catch (InterruptedException e) { e.printStackTrace();
}
synchronized (lockA) {
System.out.println(name + " got lockA");
System.out.println(name + ": say Hello!");
}
} }
}, "thread-lock-B").start();
return new ResponseEntity<>("deadLock", HttpStatus.OK);
} }
3.2 jps 查看所有的Java进程及其pid
我这边启动是XuduocloudZuulApplication这个项目
3.3 然后通过jstack <pid> > <path> 导出jstack信息
$ jstack 18640 > C:\\Users\\LiangHuang\\Desktop\\lock.txt
打开lock.txt文件,搜索关键字“deadlock”,可以很清晰的看到锁与等待锁的信息
Found one Java-level deadlock:
=============================
"thread-lock-B":
waiting to lock monitor 0x000000001e9975b8 (object 0x00000000e8cc87d8, a java.lang.Object),
which is held by "thread-lock-A"
"thread-lock-A":
waiting to lock monitor 0x0000000017e26338 (object 0x00000000e8cc87c8, a java.lang.Object),
which is held by "thread-lock-B" Java stack information for the threads listed above:
===================================================
"thread-lock-B":
at com.kawa.xuduocloud.zuul.JvmController$2.run(JvmController.java:55)
- waiting to lock <0x00000000e8cc87d8> (a java.lang.Object)
- locked <0x00000000e8cc87c8> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"thread-lock-A":
at com.kawa.xuduocloud.zuul.JvmController$1.run(JvmController.java:34)
- waiting to lock <0x00000000e8cc87c8> (a java.lang.Object)
- locked <0x00000000e8cc87d8> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748) Found 1 deadlock.
其实定位线上死锁比较简单两个指令就可以可了 ==> jps+ jstack
4. 生产环境CPU100%定位
4.1 来一个cpu100%代码
4.2 通过top指令找到cpu占用率高的程序 top
可以看到pid为7977java进程占用最高
4.3 通过ps指令查找最展cpu的子线程 ps -mp pid -o THREAD,tid,time
可以看到tid为8072的子线程占用率最高
4.4 通过jstack导出日志 分析日志 jstack pid >a.txt
查找方式最将刚才tid从十进制转为16进制 8072 => 1f88
在日志文件搜索关键字 ‘1f88’ 查找nid包含这个就是对应cpu100%的日志信息 , 如下
生产环境cpu飙高三个指令就可以了 => top + ps + jstack
5.生产环境堆内存溢出定位
环境配置 -verbose:gc -XX:+PrintGCDetails -Xloggc:C:\Users\LiangHuang\Desktop\up\gc.log -XX:-UseGCLogFileRotation -XX:GCLogFileSize=5M -XX:HeapDumpPath=C:\Users\LiangHuang\Desktop\heapdump.dump -XX:+HeapDumpOnOutOfMemoryError
可以在oom时候生产快照,方便我们使用工具分析
5.1 内存溢出的定位
5.1.1 上代码 内存溢出
5.1.2 jps查看程序pid
5.1.3 jstat 查看jvm内存使用情况 jstat -gctuil pid
5.1.4 oracle jdk 工具 jvisualvm.exe 分析快照
jvisualvm.exe导入dump文件
可以看到异常的线程,打开Show Threads 详细信息,搜索关键字“http-nio-82-exec-1”
5.2 内存泄漏的定位
5.2.1 上代码内存泄漏
5.2.2 jps 查看程序pid
5.2.3 jstat查看内存情况 jstat -gcutil pid
可以看到eden区和老年代 没有被回收,内存泄漏的导致不能被回收
5.2.4 oracle jdk 工具 jvisualvm.exe 分析快照
jvisualvm.exe导入dump文件
6.总结JVM
6.1. jvm常用的调优参数
-Xmx 堆内存最大值 -Xmx2g
-Xms 堆内存最小值 -Xms2g
-Xmn 新生代大小 默认时堆的1/3
-xss 线程栈空间大小 -Xss256k
6.2. jvm运行时数据区域由哪几个部分组成,各自作用
线程共享
堆: new出来的对象放在堆中,对象可能会栈上分配(内存逃逸分析)
元空间/方法区:class对象,常量,静态变量,运行时常量池 内存独占
栈:栈内部由栈帧组成,先进后出,栈帧(局部变量表,操作数栈,动态链接,返回地址)
PC寄存器(程序计数器):指向当前线程执行到多少行代码
本地方法栈: native修饰的方法
6.3. gc算法有哪些,gc收集器有哪些
gc算法==
分代算法
复制算法
标记清除
标记压缩
引用计数
可达性分析法 gc收集器==
young区 (Serial,ParNew,Parallel Scavenge)
old区(SerialOld,ParallelOld,CMS)
G1
6.4. GC Roots的对象有哪些
局部变量
静态变量
本地方法栈
静态常量 static final
6.5. 垃圾收集器各自优缺点
Serial:单线程收集 非单核服务器 stw比较长
Parnew: 多线程收集 多核线程比较快
PS: 可控吞吐量 (用户线程执行时间)/(用户线程执行时间+gc执行时间)
CMS: 初始标记(标记和gc roots相关的对象 有swt),
并发标记(标记被初始标记的对象关联的对象 和用户线程一起执行),
重新标记(新生代可达引用到老年代的对象,刚进入老年代的对象 stw),
并发清除 (和用户线程一起执行,清除老年代没有被标记的对象)
6.6. full gc,minor gc,major gc,stw
minor gc新生代gc
major gc老年代gc
full gc= minor gc + major gc
stw: stop the word 停止其他所有的用户线程
6.7. jvm中一次完整的gc流程 ygc => fgc ,对象如何晋升到老年代
正常流程===
经过15次ygc(复制算法)晋升到老年代
大对象直接进入到老年代 非正常===
动态年龄 (s区 50%以上对象年龄 > s区平均年龄则进阶老年代,如果老年代空间不足则发生full gc)
空间担保 (s0或s1放不下这些对象,会进行于此空间担保(老年代剩余空间大于历代s区进阶的平均值则担保成功))如果担保
失败则发生full gc 元空间/方法区不足也会发生full gc,但不会被垃圾收集器回收
6.8. 内存泄漏判断
对象不能被gc回收就会导致内存泄漏
jstack 发生gull gc后,新生代和老年代的占用情况,如果占用的空间没有降低则可以判断放生内存泄漏
如果fgc放生频率远远高于ygc则发生了内存泄漏
6.9. 内存溢出判断
后台没写分页,大数据量,内存溢出报错后,对象会被回收,整个服务任然可用
内存泄漏导致的内存溢出,泄漏的对象不会被回收,知道我们的整个堆内存被占满,导致整个服务不可用 打印dump文件,分析快照,查找大对象
6.10. java中的引用类型有哪些
强引用:Object o = new Objetc(); gc不会回收强引用对象
软引用:SoftReference 对内存占满时就会这里面的对象(这个一般用来做缓存)
弱引用:WearkReference 只能存在下一次gc之前 (minor gc,major gc发生就会被回收)
虚引用:Object o = new Object(); o = null; 提醒gc来回收这个对象
JVM(五) 生产环境内存溢出调优的更多相关文章
- (转)centos 7 Tomcat 8.5 的安装及生产环境的搭建调优
原文:https://www.cnblogs.com/linhankbl/articles/9149804.html#top JVM菜鸟进阶高手之路七(tomcat调优以及tomcat7.8性能对比) ...
- 中间件及tomcat的内存溢出调优
主要是这三个选项的调整需要根据主机的内存配置 以及业务量的使用情况调节 -Xmx4g -Xms4g -Xmn2g xmx 与xms一般设置为一样 xmn大致设置为xmx xms的三分之一 可以使用 ...
- <JVM下篇:性能监控与调优篇>补充:浅堆深堆与内存泄露
笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...
- <JVM下篇:性能监控与调优篇>01-概述篇-02-JVM监控及诊断工具-命令行篇
笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...
- java jvm 参数 -Xms -Xmx -Xmn -Xss 调优总结
常见配置举例 堆大小设置 JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制.32位系统 下,一般限制在1.5 ...
- <JVM下篇:性能监控与调优篇>03-JVM监控及诊断工具-GUI篇
笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...
- Linux下jetty报java.lang.OutOfMemoryError: PermGen space及Jetty内存配置调优解决方案
Linux下的jetty报java.lang.OutOfMemoryError: PermGen space及Jetty内存配置调优解决方案问题linux的jetty下发布程序后再启动jetty服务时 ...
- <JVM下篇:性能监控与调优篇>补充:使用OQL语言查询对象信息
笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...
- JVM垃圾回收机制总结:调优方法
转载: JVM垃圾回收机制总结:调优方法 JVM 优化经验总结 JVM 垃圾回收器工作原理及使用实例介绍
随机推荐
- centos vim 配置
centos 6.5使用源码安装vim 7.41.下载vim的源码.vim官网是www.vimunix.com/vim使用链接下载7.4源码包:ftp://ftp.vim.org/pub/vim/un ...
- UOJ46 【清华集训2014】玄学 【时间线段树】
题目链接:UOJ 这题的时间线段树非常的妙. 对时间建立线段树,修改的时候在后面加,每当填满一个节点之后就合并进它的父亲. 对于一个节点维护序列,发现这是一个分段函数,合并就是归并排序.于是就形成了差 ...
- [SDOI2009][BZOJ 1876]SuperGCD
Description Sheng bill有着惊人的心算能力,甚至能用大脑计算出两个巨大的数的GCD(最大公约 数)!因此他经常和别人比 赛计算GCD.有一天Sheng bill很嚣张地找到了你,并 ...
- Tkinter 之TopLevel顶级窗口
一.参数说明 width 设置宽度 height 设置高度 background(bg) 设置背景颜色默认值由系统指定为了防止更新,可以将颜色值设置为空字符串 borderwidth(bd) 设置 ...
- [Linux] 内核通知链 notifier
Linux 内核中每个模块之间都是独立的,如果模块需要感知其他模块的事件,就需要用到内核通知链. 最典型的通知链应用就是 LCD 和 TP 之间,TP 需要根据 LCD 的亮灭来控制是否打开关闭触摸功 ...
- springboot前端传参date类型后台处理方式
转载:https://blog.csdn.net/xiangluer/article/details/81913137?utm_source=copy 参考如下博客: https://www.jian ...
- java的集合类【Map(映射)、List(列表)与Set(集)比较】
https://baike.baidu.com/item/java%E9%9B%86%E5%90%88%E7%B1%BB/4758922?fr=aladdin https://www.cnblogs. ...
- visual studio python快捷键
打开交互式窗口: alt + i 智能提示: ctrl + j, or alt + ->
- Vue动态路由 Get传值
<template> <!-- 所有的内容要被根节点包含起来 --> <div id="home"> 我是首页组件 <ul> < ...
- C/C++ socket编程教程之九:TCP的粘包问题以及数据的无边界性
C/C++ socket编程教程之九:TCP的粘包问题以及数据的无边界性 上节我们讲到了socket缓冲区和数据的传递过程,可以看到数据的接收和发送是无关的,read()/recv() 函数不管数据发 ...