从0开始回顾JVM---系列五

简介: JVM调优1、有哪些常用的命令行性能监控和故障处理工具?操作系统工具● top:显示系统整体资源使用情况● vmstat:监控内存和CPU● iostat:监控IO使用● netstat:监控网络使用JDK性能监控工具● jps:虚拟机进程查看● jstat:虚拟机运行时信息查看● jinfo:虚拟机配置查看● jmap:内存映像(导出)● jhat:堆转储快照分析● jstack:Java堆栈跟踪● jcmd:实现上面除了jstat外所有命令的功能2、了解哪些可视化的性能监控和故障处理工具?以下是一些JDK自带的可视化性能监控和故障处理工具:● JCons

JVM调优

1、有哪些常用的命令行性能监控和故障处理工具?


操作系统工具

  • top:显示系统整体资源使用情况
  • vmstat:监控内存和CPU
  • iostat:监控IO使用
  • netstat:监控网络使用

JDK性能监控工具

  • jps:虚拟机进程查看
  • jstat:虚拟机运行时信息查看
  • jinfo:虚拟机配置查看
  • jmap:内存映像(导出)
  • jhat:堆转储快照分析
  • jstack:Java堆栈跟踪
  • jcmd:实现上面除了jstat外所有命令的功能

2、了解哪些可视化的性能监控和故障处理工具?

以下是一些JDK自带的可视化性能监控和故障处理工具:

  • JConsole

  • VisualVM

  • Java Mission Control

JMC主要界面

除此之外,还有一些第三方的工具:

  • MAT

Java 堆内存分析工具。

  • GChisto

GC 日志分析工具。

  • GCViewer

GC 日志分析工具。

  • JProfiler

商用的性能分析利器。

  • arthas

阿里开源诊断工具。

  • async-profiler

Java 应用性能分析工具,开源、火焰图、跨平台。

3、讲一下常见的JVM参数?


堆配置:

  • -Xmx3550: 设置堆最大值
  • -Xms3660m: 设置初始堆大小
  • -Xss128k: 设置线程栈大小
  • -Xmn2g: 设置年轻代大小
  • -XX:NewSize=1024m: 设置年轻代初始值
  • -XX:MaxNewSize=1024m: 设置年轻代最大值
  • -XX:SurvivorRatio=4: 设置Survivor区与Eden区比值
  • -XX:MaxTenuringThreshold=15:  设置分代年龄阈值,满15就进入老年代。
  • -XX:PretenureSizeThreshold: 大对象直接进入老年代

收集器设置:

  • -XX:+UseSerialGC:设置串行收集器
  • -XX:+UseParallelGC:设置并行收集器
  • -XX:+UseParalledlOldGC:设置并行年老代收集器
  • -XX:+UseConcMarkSweepGC:设置并发收集器

打印GC回收的过程日志信息

  • -XX:+PrintGC
  • -XX:+PrintGCDetails
  • -XX:+PrintGCTimeStamps
  • -Xloggc:filename

4、有做过JVM调优吗?

实际上,JVM调优是不得已而为之,代码重构比JVM调优会更好,调优流程图大致如下:

真实案例:

电商公司的运营后台系统,偶发性的引发OOM异常,堆内存溢出。

  1. 因为是偶发性的,所以第一次简单的认为就是堆内存不足导致,单方面的加大了堆内存从4G调整到8G   -Xms8g。
  2. 但是问题依然没有解决,只能从堆内存信息下手,通过开启了-XX:+HeapDumpOnOutOfMemoryError参数 获得堆内存的dump文件。
  3. 用JProfiler 对  堆dump文件进行分析,通过JProfiler查看到占用内存最大的对象是String对象,本来想跟踪着String对象找到其引用的地方,但dump文件太大,跟踪进去的时候总是卡死,而String对象占用比较多也比较正常,最开始也没有认定就是这里的问题,于是就从线程信息里面找突破点。
  4. 对线程进行分析,先找到了几个正在运行的业务线程,然后逐一跟进业务线程看了下代码,有个方法引起了我的注意: 导出订单信息
  5. 因为订单信息导出这个方法可能会有几万的数据量,首先要从数据库里面查询出来订单信息,然后把订单信息生成excel,这个过程会产生大量的String对象。
  6. 为了验证自己的猜想,于是准备登录后台去测试下,结果在测试的过程中发现导出订单的按钮前端居然没有做点击后按钮置灰交互事件,后端也没有做防止重复提交,因为导出订单数据本来就非常慢,使用的人员可能发现点击后很久后页面都没反应,然后就一直点,结果就大量的请求进入到后台,堆内存产生了大量的订单对象和EXCEL对象,而且方法执行非常慢,导致这一段时间内这些对象都无法被回收,所以最终导致内存溢出。
  7. 知道了问题就容易解决了,最终没有调整任何JVM参数,只是做了两个处理:
  • 在前端的导出订单按钮上加上了置灰状态,等后端响应之后按钮才可以进行点击
  • 后端代码加分布式锁,做防重处理;

5、线上服务CPU占用过高怎么排查?

  1. 首先需要找出哪个进程占用CPU高
  • top:  列出系统各个进程的资源占用情况;
  1. 然后找到对应进程里哪个线程占用CPU高
  • top -Hp 进程ID:   列出对应进程里面的线程占用资源情况
  1. 找到对应线程ID后,再打印出对应线程的堆栈信息
  • printf "%x\n"  PID:    把线程ID转换为16进制。
  • jstack PID: 打印出进程的所有线程信息,从打印出来的线程信息中找到上一步转换为16进制的线程ID对应的线程信息。
  1. 最后根据线程的堆栈信息定位到具体业务方法,从代码逻辑中找到问题所在。


6、内存持续上升,我该如何处理?【


分析:内存飚高如果是发生在java进程上,一般是因为创建了大量对象所导致,持续飚高说明垃圾回收跟不上对象创建的速度,或者内存泄露导致对象无法回收

处理过程如下:

  1. 先观察垃圾回收的情况
  • jstat -gc PID 1000: 查看GC次数,时间等信息,每隔一秒打印一次。
  • jmap -histo PID | head -20:   查看堆内存占用空间最大的前20个对象类型,可初步查看是哪个对象占用了内存。
  • 分析:如果每次GC次数频繁,而且每次回收的内存空间也正常,那说明是因为对象创建速度快导致内存一直占用很高;如果每次回收的内存非常少,那么很可能是因为内存泄露导致内存一直无法被回收。
  1. 导出堆内存文件快照
  • jmap -dump:live,format=b,file=/home/myheapdump.hprof PID  dump堆内存信息到文件。
  1. 使用visualVM对dump文件进行离线分析,找到占用内存高的对象,再找到创建该对象的业务代码位置,从代码和业务场景中定位具体问题。

7、说一下JVM调优的命令?


  1. jps:查看当前系统有哪些Java进程。
  2. jmap:查看堆内存占用情况,只能查看某一时刻的堆内存占用情况。
  • 使用方式:  jamp -heap 进程id;
  • 举例:jmap -heap 7988  (查看当前7988进程的堆内存占用情况)。
  1. jstack:jstack用于生成java虚拟机当前时刻的线程快照,用来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。


8、频繁 minor gc 和频繁Full GC怎么办?


频繁 minor gc

  • 通常情况下,由于新生代空间较小,Eden区很快被填满,就会导致频繁Minor  GC,因此可以通过增大新生代空间-Xmn来降低Minor GC的频率。

频繁 Full GC

有哪些原因导致FGC?

  1. 大对象:系统一次性加载了过多数据到内存中,导致大对象进入了老年代;
  2. 内存泄漏:频繁创建了大量对象,但是无法被回收;
  3. 程序频繁生成一些长生命周期的对象,当这些对象的存活年龄超过分代年龄时便会进入老年代,最后引发FGC;
  4. 代码中显式调用了gc方法,包括自己的代码甚至框架中的代码。
  5. JVM参数设置问题:包括总内存大小、新生代和老年代的大小、Eden区和S区的大小、元空间大小、垃圾回收算法等等。

排查问题时可以使用的工具:

  1. 公司的监控系统
  2. JDK的自带工具,包括jmap、jstat等常用命令;
  3. 可视化的堆内存分析工具:JVisualVM、MAT等。

如何排查?

  1. 查看监控,了解出现问题的时间点以及当前FGC的频率;
  2. 了解JVM的参数设置,包括:堆空间各个区域的大小设置,新生代和老年代分别采用了哪些垃圾收集器,然后分析JVM参数设置是否合理。
  3. 针对大对象或者长生命周期对象导致的FGC,可通过 jmap -histo 命令并结合dump堆内存文件作进一步分析,需要先定位到可疑对象。
  4. 通过可疑对象定位到具体代码再次分析,这时候要结合GC原理和JVM参数设置,弄清楚可疑对象是否满足了进入到老年代的条件才能下结论。

9、有没有处理过内存泄漏问题?是如何定位的?


内存泄漏是内在病源,外在病症表现可能有:

  1. 应用程序长时间连续运行时性能严重下降
  2. CPU 使用率飙升,甚至到 100%
  3. 频繁 Full GC,各种报警,例如接口超时报警等
  4. 应用程序抛出 OutOfMemoryError 错误
  5. 应用程序偶尔会耗尽连接对象

主要有以下排查操作步骤

  1. 使用 jps 查看运行的 Java 进程 ID;
  2. 使用top -p [pid] 查看进程使用 CPU 和 内存 的情况;
  3. 使用 top -Hp [pid] 查看进程下的所有线程占 CPU 和内存 的情况
  4. 线程 ID 转换为 16 进制:printf "%x\n" [pid],输出的值就是线程栈信息中的 nid
  5. 抓取线程栈:jstack 29452 > 29452.txt,可以多抓几次做个对比。
  6. 使用jstat -gcutil [pid] 5000 10 每隔 5 秒输出 GC 信息,输出 10 次,查看 YGCFull GC 次数。通常会出现 YGC 不增加或增加缓慢,而 Full GC 增加很快。
  7. 如果发现 Full GC 次数太多,就很大概率存在内存泄漏了
  8. 使用 jmap -histo:live [pid] 输出每个类的对象数量,内存大小(字节单位)及全限定类名。
  9. 生成 dump 文件,借助工具分析哪个对象非常多,基本就能定位到问题在哪了。
相关文章
|
16小时前
|
存储 安全 Java
从0开始回顾JVM---系列四
虚拟机执行 1、类的生命周期吗? 一个类从被加载到虚拟机内存中开始,到从内存中卸载,整个生命周期需要经过七个阶段:加载 、验证、准备、解析、初始化 、使用 和 卸载,其中验证、准备、解析三个部分统称为连接。 2、什么是类加载?类加载的过程? 类加载: 虚拟机把描述类的数据加载到内存里面,并对数据进行校验、解析和初始化,最终变成可以被虚拟机直接使用的class对象。 类加载过程如下: ● 加载:1、根据类的全限定类名获取二进制字节流;2、将字节流代表的静态存储结构转为方法区运行时存储数据结构;3、在堆中生成Class对象,作为方法区这个类数据访问入口; ● 验证: 检验加载的class文
|
16小时前
|
存储 算法 安全
从0开始回顾JVM---系列三
20、有哪几种垃圾回收器,各自的优缺点是什么? ⚡ 垃圾回收器主要分为以下几种:Serial,Parallel,CMS,G1。 1. Serial ● 单线程垃圾回收器,该线程运行时,其他线程都暂停,使用复制算法; ● 使用场景: 堆内存较小,适合个人电脑(CPU核数较小); a. 工作在老年区,回收算法:标记整理;工作在新生代,回收算法:复制; b. 单线程的垃圾回收器,只有一个垃圾回收线程在运行; c. 垃圾回收线程结束后,其他线程恢复运行; d. 触发垃圾回收时让所有线程在安全点停下【在垃圾回收过程中,对象的地址可能发生改变,为了保证安全使用对象地址,要
|
16小时前
|
Java
jvm---类加载器(1)
jvm---类加载器(1)
|
16小时前
|
存储 Java 编译器
从0开始回顾JVM---系列一
引言 1、什么是JVM? JVM——Java虚拟机,它是Java实现平台无关性的基石。 Java程序运行的时候,编译器将Java文件编译成平台无关的Java字节码文件(.class),接下来对应平台JVM对字节码文件进行解释,翻译成对应平台匹配的机器指令并运行。 同时JVM也是一个跨语言的平台,和语言无关,只和class的文件格式关联,任何语言,只要能翻译成符合规范的字节码文件,都能被JVM运行。 内存管理 1、什么是JVM内存结构? Java1.8 之后的内存结果图: JVM将虚拟机分为5大区域,程序计数器、虚拟机栈、本地方法栈、java堆、方法区,其中方法区和堆是线程共享区,虚拟
|
16小时前
|
存储 缓存 算法
从0开始回顾JVM---系列二
11、什么情况下会发生内存泄露? 1. 静态集合类引起内存泄漏 ● 静态集合的生命周期和 JVM 一致,所以静态集合引用的对象不能被释放。 2. 单例模式 ● 单例对象在初始化后会以静态变量的方式在 JVM 的整个生命周期中存在。如果单例对象持有外部的引用,那么这个外部对象将不能被 GC 回收,导致内存泄漏。 3. 数据连接、IO、Socket等连接 ● 创建的连接不再使用时,需要调用 close 方法关闭连接,只有连接被关闭后,GC 才会回收对应的对象(Connection,Statement,ResultSet,Session)。忘记关闭这些资源会导致持续占有内存,无法被 GC 回收。 4
|
16小时前
|
安全 Java
《深入理解java虚拟机》学习笔记-----郑雨迪
《深入理解java虚拟机》学习笔记-----郑雨迪
45 0
|
10月前
|
算法 Java API
Java虚拟机System.gc()解析
对于Java语言来说是不用刻意手动去释放内存,同时,也尽可能不需要手动去干预Java虚拟机的GC行为。在本篇文章中,我们试图从多个方面去解析有关System.gc()API调用的最常见问题。希望对需要了解这块技术的朋友有所帮助。
142 0
|
存储 缓存 监控
JVM详解 --- 垃圾回收机制
JVM详解 --- 垃圾回收机制
JVM详解 --- 垃圾回收机制
|
算法 Java 数据格式
JVM:Java运行时数据区域----程序计数器
JVM:Java运行时数据区域----程序计数器
77 0
JVM:Java运行时数据区域----程序计数器
|
存储 Java 索引
JVM:Java运行时数据区域----Java虚拟机栈
JVM:Java运行时数据区域----Java虚拟机栈
85 0
JVM:Java运行时数据区域----Java虚拟机栈