文章目录
- 快速回忆
- 一、gc日志和dump快照
- 二、gc日志和dump快照实战
- 三、java命令行工具(核心,重点)
- 四、面试必问(异常类)
-
-
- 内存泄露和内存溢出的关系?
- 内存泄露的几种情况
- JVM中常见的两种Error
- StackOverflowError栈内存溢出
- JVMOOM问题如何排查和解决?
- java.lang.OutOfMemoryError: Java heap space堆内存溢出问题如何排查和解决?
- 如果JVM出现频繁FullGC该如何解决
- 我实际遇到的内存泄露:
- 线上CPU预警、内存预警问题排查的步骤?(宏观谈)
- CPU预警、内存预警之类的问题具体排查步骤:
- 线上的 API 接口响应比较慢,该如何快速排查和定位问题?
- 线程诊断_CPU占用过高
- 线程诊断_迟迟得不到结果
- MySQL 数据库cpu 飙升的话,要怎么处理呢?
- 数据库出现死锁如何排查?
-
- 五、面试必学(调优类)
- 六、JVM性能优化案例
快速回忆
gc日志和dump快照
当服务发生堆内存溢出时,你可以从系统里面拿到两类日志:一个是gc日志,另一个是dump快照
gc日志可以直接看(不推荐),但是直接看晦涩难懂,建议用GCEasy去分析
dump快照是二进制文件,你想直接看也看不懂,所以你需要借助JvisualVm、MAT、Jprofile等专业工具分析
jinfo、jstat、jmap这些命令行工具有啥用?我们能用JvisualVm、MAT、Jprofile去图像分析,那为啥要学这些命令行?
JvisualVm、MAT、Jprofile是分析dump快照的,但是我如果想知道①生产环境堆内存大小是多少?②生产环境老年代使用了多少了?是否快发生堆内存溢出了?③生产环境出现死锁了④生产环境cpu过高 等等问题,你是只能用命令行去解决的
JvisualVm、MAT里面能看到啥?
①从概要里面可以看到出现问题的代码行数、堆文件的概览情况
②从类里面可以看到每一个类实例的个数和占用的大小(如果又大量的People对象那就说明那块代码有问题)
③从线程里面可以看到哪个线程拥有的堆内存过大或者持有了大量的某个对象
命令行工具
jps查看正在运行的java进程
jps -l 查看正在运行的java进程(输出的内容更long、更具体了)
jstat -gcutil 6384 1000 30 是看老年代使用占比
jstack 10086 是看10086号进程下的所有线程情况
jinfo -flags 10086 是看10086号进程的jvm参数
jmap是打印/导出JVM中堆的快照
jmap -dump:live,format=b,file=heapdump.hprof 1234 导出堆内存日志
jmap -heap 1234 这会显示当前堆内存信息
[root@idm1 dump]# jmap -heap 15737
Attaching to process ID 15737, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.172-b11
using thread-local object allocation.
Parallel GC with 13 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 6442450944 (6144.0MB)
NewSize = 1073741824 (1024.0MB)
MaxNewSize = 1073741824 (1024.0MB)
OldSize = 5368709120 (5120.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 1012924416 (966.0MB)
used = 386504832 (368.5997314453125MB)
free = 626419584 (597.4002685546875MB)
38.15732209578805% used
From Space:
capacity = 30408704 (29.0MB)
used = 21497224 (20.50135040283203MB)
free = 8911480 (8.498649597167969MB)
70.69431173390356% used
To Space:
capacity = 30408704 (29.0MB)
used = 0 (0.0MB)
free = 30408704 (29.0MB)
0.0% used
PS Old Generation
capacity = 5368709120 (5120.0MB)
used = 1980373120 (1888.6309814453125MB)
free = 3388336000 (3231.3690185546875MB)
36.88732385635376% used
98726 interned Strings occupying 10912288 bytes.
内存泄露和内存溢出的关系?
内存泄露:当前对象已经不需要被使用了,但是由于代码等原因让程序误以为该对象还被使用导致该对象不能被回收,这就是内存泄露
内存溢出:内存不够了,就溢出了。(内存泄漏会慢慢导致内存溢出)
内存泄露的几种情况
1、静态集合类 :用一个static类型的list去存放大量数据,且用完还没有回收
2、各种连接(数据库连接、网络连接、IO连接)
3、本地缓存:你使用一个map作为本地缓存,服务启动时把数据加载到map里面
…
反正让一个生命周期很长的对象持有生命周期很短的,这就可能导致内存泄漏
堆内存溢出问题如何排查和解决?
①分析系统的debug日志,看有没有指向某行代码的报错
②分析gc日志,交给GCEasy去分析
③分析dump快照借助JvisualVM、MAT、Jprofile来分析
当应用发生 oom 后,最佳的分析载体就是 heap dump。通过添加-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/app.dump
参数,可以实现当应用发生 oom 时,全自动导出 heap dump。有了 heap dump,我们就可以借助JvisualVM、MAT、Jprofile等工具来分析heap dump 了
oom 发生的原因有两种:
1. 应用正常,但是堆设置过小,无法支持正常的对象分配
2. 应用发生了内存泄漏,导致应该被清理的对象没有清理,无限增长
线上CPU预警、内存预警问题排查的步骤?(宏观谈)
1、是否需要查看硬件的资源:
- 分析cpu:top等命令
- 内存分析 free -h
- 磁盘分析 df -h
- ⽹络分析:使⽤dstat、vmstat 等命令查看⽹络流量、TCP 连接等情况,分析异常流量
- 数据库资源:查看后台是否有长时间执行的慢sql、是否有死锁
2、实时分析
如果这类问题能实时监控、能稳定复现:
比如说是一个接口很慢,那我们可以重新点一下,然后借助arthas等工具,还有jstack、jinfo、jstat等命令实时监控性能(生产环境用不了图形化界面,就只能使用这些命令行了)
3、离线分析
如果这类问题不能稳定复现,是宕机类问题,分析日志:
①查看业务的debug日志
②到GCeasy上面分析gc.log(GC日志)
③使用jvisualVM、MAT等工具分析dump堆快照
如何进行GC调优?
1. 新生代调优
-
新生代内存越大越好么?
- 不是:
- 新生代内存太小:频繁触发Minor GC,新生代内存紧张,幸存者区空间紧张了,导致对象的晋升阈值降低,很多生命周期很短的对象也被晋升到老年代,导致Full GC 也频繁。所以要调大新生代内存
- 新生代内存太大:老年代内存占比有所降低,会更频繁地触发Full GC。而且触发Minor GC时,清理新生代所花费的时间会更长。
- 通常新生代内存占整个堆的25%—50%最好
- 不是:
2. 老年代调优
以CMS为例:
-
如果没有 Full GC 那么说明当前系统暂时不需要优化,否则,就先尝试调优新生代。
-
如果频繁Full GC:
①Xmx和Xms设置为老年代存活对象(FuIIGC之后的老年代内存占用)的3-4倍。
②方法区(永久代 或 元空间)设置为老年代存活对象的1.2-1.5倍。
③年轻代Xmn的设置为老年代存活对象的1-1.5倍:
④老年代的内存大小设置为老年代存活对象的2-3倍,
3、调整垃圾回收器,提高服务吞吐量
Java中一共有7大垃圾收集器:
- SerialGC是串行垃圾收集器(新生代:串行 ,老年代:串行),会暂停所有的用户线程
- ParNewGC新生代的并行垃圾回收器(新生代:并行 ,老年代:串行)
- ParallelOldGC老年代的并行垃圾收集器(新生代:并行 ,老年代:并行)
- ParallelGC并行垃圾收集器(新生代:并行 ,老年代:并行)
- ConcMarkSweepGC(CMS)并发标记清除(新生代:ParNewGC ,老年代:CMS)
- G1GC:G1垃圾收集器(新生代:G1 ,老年代:G1)
如何选择垃圾收集器?
- 单CPU或者小内存,单机程序
- -XX:+UseSerialGC
- 多CPU,需要最大的吞吐量,如后台计算型应用
- -XX:+UseParallelGC
- -XX:+UseParallelOldGC
- 多CPU,追求低延迟(响应时间),需要快速响应如互联网应用
- -XX:+UseConcMarkSweepGC
- 同时注重低延迟(响应时间) + 碎片少的用G1
- -XX:+UseG1GC
一、gc日志和dump快照
当服务发生堆内存溢出时,你可以从系统里面拿到两类日志:一个是gc日志,另一个是dump快照
gc日志可以直接看(不推荐),但是直接看晦涩难懂,建议用GCEasy去分析
dump快照是二进制文件,你想直接看也看不懂,所以你需要借助JvisualVm、MAT、Jprofile等专业工具分析
上面都是离线分析。
我们可以用JvisualVm、MAT、Jprofile连上我们的idea进行实时监控堆内存然后实时分析,但是这个在生产环境中不适用;
生产环境发生堆内存溢出时我们拿到gc日志和dump快照然后进行离线分析;
也就是说,对于dump快照和gc日志而言,我们生产环境主打的就是离线分析。
jinfo、jstat、jmap这些命令行工具有啥用?我们能用JvisualVm、MAT、Jprofile去图像分析,那为啥要学这些命令行?
JvisualVm、MAT、Jprofile是分析dump快照的,但是我如果想知道①生产环境堆内存大小是多少?②生产环境老年代使用了多少了?是否快发生堆内存溢出了?③生产环境出现死锁了④生产环境cpu过高 等等问题,你是只能用命令行去解决的
GC日志是什么,要怎么看?
添加如下的启动参数-XX:+PrintGCDetails -XX:+PrintGCDataStamps -XLoggc:./logs/gc.log
-XX:+PrintGCDetails 在发生垃圾回收时打印内存回收详细的日志,并在进程退出时输出当前内存各区域分配的情况
-XX:+PrintGCDataStamps GC发生时输出时间戳
-XLoggc:./logs/gc.log 把GC日志写入到一个文件里面去
上面的gc.log可以到GcEasy官网去分析
Minor GC日志该怎么看?(了解)
以下内容来自别的视频
Full GC日志该怎么看?(了解)
以下内容来自别的视频
Full GC由于包括了新生代、老年代、元空间等,所以它的日志很长
dump快照是什么?要怎么看?
dump快照是二进制文件,你想直接看也看不懂,所以你需要借助JvisualVm、MAT、Jprofile等专业工具分析
Jconsole
略,这个工具不如JvisualVm、MAT、Jprofile
JvisualVM
JvisualVM怎么分析我们后面慢慢讲,我们先说一下它怎么安装插件
MAT
MAT可以分析heap dump文件。在进行内存分析时,只要获得了反映当前设备内存映像的hprof文件,通过MAT打开就可以直观地看到当前的内存信息。
面试问答:MAT里面能看到哪些内存信息?
- 所有的对象信息,包括对象实例、成员变量、存储于栈中的基本类型值和存储于堆中的其他对象的
引用值。 - 所有的类信息,包括classloader、类名称、父类、静态变量等.
- GCRoot到所有的这些对象的引用路径
- 线程信息,包括线程的调用栈及此线程的线程局部变量(TLS)
Jprofile
Jprofile是收费的,所以Jprofile更强大,它支持实时分析正在运行的程序,也支持离线对dump文件的分析
这个工具就先不介绍了,老师课程里面都是实时分析,但是我们真实生产环境要的是离线分析,而且JvisualVm、MAT使用的更多一些
其它工具
还有JMC、Flame Graphs 、Tprofiler 、 Btrace 、YouKit、JProbe、Spring Insight等工具
二、gc日志和dump快照实战
对于gc日志和dump快照怎么看,我们必须依赖案例来去了解JvisualVm、MAT、GCEasy这些工具的使用,离开案例都是耍流氓。
java.lang.OutOfMemoryError:Java heap space
jvm参数配置:
-Xms50M -Xmx50M -XX:MetaspaceSize=64M
-XX:+PrintGCDetails -XX:+PrintGCDataStamps -XLoggc:./logs/gc.log
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=heap/heapdump.hprof
-Xms50M -Xmx50M 这两个参数保证你的堆内存足够小能够发生堆内存溢出
-XX:+PrintGCDetails 在发生垃圾回收时打印内存回收详细的日志,并在进程退出时输出当前内存各区域分配的情况
-XX:+PrintGCDataStamps GC发生时输出时间戳
-XLoggc:./logs/gc.log 把GC日志写入到一个文件里面去,方便后面分析
-XX:+HeapDumpOnOutOfMemoryError 保证当服务器出现堆内存溢出时能够把当前的堆使用情况记录下来(这个很关键,你要是没有这个参数就不会记录当时的堆内存情况。这就好比是一个人被杀了,临死之前拍了一段录像一样。)
-XX:HeapDumpPath=heap/heapdump.hprof 保证当服务器出现堆内存溢出时能够把当前的堆使用情况记录到heap目录下的heapdump.hprof 文件中(这就好比是一个人被杀了,临死之前拍了一段录像,然后把录像放到某个柜子里)
经过上面堆内存参数的配置,发生堆内存溢出后你本地会有两个文件gc.log和heapdump.hprof
gc.log记录了gc情况,可以到GcEasy官网去分析
heapdump.hprof记录了堆内存情况,借助jvisualvm工具或MAT工具分析
1、gc.log怎么看
gc.log怎么看?
GcEasy
下面为其它视频讲解的内容
其它GC日志分析的工具
GCViewer
还有GChisto
2、heapdump.hprof怎么看?
①jvisualvm查看
备注:下面的截图来自另一个视频
>
②使用MAT查看
MAT是Eclipse自带的工具,可以单独安装,而且MAT是专门做堆内存分析的,功能比JvisualVM更强大
说明
分析
备注:下面的图来自其它视频
Histogram直方图: