前面在学习 JVM 的知识的时候, 一般都需要利用相关参数进行分析, 而分析一般都需要用到一些分析的工具, 因为一般使用 IDEA, 而 VisualVM 对于 IDEA 也不错, 所以就选择 VisualVM 来分析 JVM 性能, 这篇文章就介绍一下如何利用 VisualVM 进行性能分析, 以及在分析之前需要知道一些 GC 优化的原则, GC 优化的目的, 以及遇到问题时怎么去解决问题的方法.
1 为什么需要
开发大型 Java 应用程序的过程中难免遇到内存泄露, 性能瓶颈等问题, 比如文件, 网络, 数据库的连接未释放, 未优化的算法等. 随着应用程序的持续运行, 可能会造成整个系统运行效率下降, 严重的则会造成系统崩溃. 为了找出程序中隐藏的这些问题, 在项目开发后期往往会使用性能分析工具来对应用程序的性能进行分析和优化.
VisualVM 是一款免费的性能分析工具. 它通过 jvmstat,JMX,SA(Serviceability Agent) 以及 Attach API 等多种方式从程序运行时获得实时数据, 从而进行动态的性能分析. 同时, 它能自动选择更快更轻量级的技术尽量减少性能分析对应用程序造成的影响, 提高性能分析的精度.
2 如何安装
这里有两种方式:
没有按照 IDEA 插件
如果没有按照 IDEA 插件的话, 我们需要找到 JDK 的按照目录 bin 下找到如下执行程序.
然后双击执行, 就会出现界面, 如下;
但是, 我们一般使用 IDEA, 所以会使用插件, 就是下面这种方式.
按照 IDEA 插件
先在插件中找到 VisualVM 安装;
安装了之后, 在运行的地方就会多出现两个 VisualVM 的运行按钮;
这样运行程序之后, 就可以自动打开 VisualVM 程序了.
3 基本介绍
这一部分先对这个工具做一个简要的介绍, 看看基本有哪些我们会用到的功能.
在没有添加其他插件的时候, 是只有下面几个功能的.
3.1 概述
如上图所示, 概述基本上都是我们的系统属性, 运行程序时设置的 JVM 参数等信息的展示, 所以, 这一部分可以让我们查看这些信息.
3.2 监视
监视这个界面的功能还是很有作用的, 可以看到 cup 运行情况, 堆的使用情况, 类的情况以及线程的动态情况.
因此, 我们可以利用这个界面查看 CPU 情况好不好, 更重要的是, 我们可以查看堆的使用情况, 这对于我们分析 JVM 还是非常重要的.
3.3 线程
如上图所以, 可以看到所有的线程的情况, 是运行, 休眠, 等待, 驻留, 监视等情况.
注意, 以上这些都不是关键, 关键是 VisualVM 中还有一个很重要的功能, 可以添加插件获取更多的功能.
3.4 插件添加
正是因为有了插件的扩展功能, 所以这个工具才如此强大, VisualVM 可以做到以下:
显示虚拟机进程以及进程的配置, 环境信息, jps,jinfo.
监视应用程序的 CPU,GC, 堆, 方法区以及线程的信息 (jstat,jstack).
dump 以及分析堆转存储快照 (jmap,jhat).
还有很多其他的功能.
在工具 -> 找到可用插件, 安装即可.
下一部分我们就利用已经安装的插件 Visual GC 进行分析.
4 利用 Visual GC 分析虚拟机内存区域
这部分会用到一些 Java 虚拟机的一些基础知识, 所以, 查看这部分之前, 请先查看这篇文章:.
在这个界面分为以下几个部分.
space(Metaspace(元数据),Old 老年代, 新生代 (Eden,S0,S1))
- Graphs(Compile Time(编译时间),Class Loader Time(类加载时间),GC Time(垃圾收集时间),Eden Space,Survivor 0,Survivor 1,Old Gen,Metaspace)
- Histogram(Parameters 参数设置)
那么知道这些参数之后, 怎么去分析虚拟机到底运行是好是坏呢, 这个时候, 我们需要了解一些 Java 虚拟机基础的优化知识.
首先, 需要了解一些 GC 优化的原则.
多数的 Java 应用不需要在服务器上进行 GC 优化;
多数导致 GC 问题的 Java 应用, 都不是因为我们参数设置错误, 而是代码问题;
在应用上线之前, 先考虑将机器的 JVM 参数设置到最优 (最适合);
减少创建对象的数量;
减少使用全局变量和大对象;
GC 优化是到最后不得已才采用的手段;
在实际使用中, 分析 GC 情况优化代码比优化 GC 参数要多得多;
另外, 我们需要知道我们 GC 优化的目的.
将转移到老年代的对象数量降低到最小;
减少 full GC 的执行时间;
一般, 我们需要执行的有以下几点;
减少使用全局变量和大对象;
调整新生代的大小到最合适;
设置老年代的大小为最合适;
选择合适的 GC 收集器;
至于怎么算合适, 后面我会通过一个实例讲解.
其实, 如果想要知道更多 JVM 内存分配和回收策略的原理, 可以查看这篇文章: JVM 内存分配和回收策略的原理.
一般我们执行了我们的程序之后, 接下来就是需要查看 GC 的状态了, 接着分析结果, 判断是否需要进行优化.
一般如果达到以下的指标, 就不需要进行 GC 了.
Minor GC 执行时间不到 50ms,Minor GC 执行不频繁, 约
10
秒一次;
Full GC 执行时间不到 1s,Full GC 执行频率不算频繁, 不低于
10
分钟 1 次;
实例 1
我们先看一个 GC 状态需要优化的例子, 在这个实例中, 我们给堆分配的最大最小的值都是 64M(很小的堆大小).
GC 状态差情况分析
- /**
- * VM Args:-Xms64m -Xmx64m -XX:+HeapDumpOnOutOfMemoryError
- * @author 欧阳思海
- */
- public class HeapTest {
- static class StaticObject {
- }
- public static void main(String[] args) {
- List<StaticObject> list = new ArrayList<StaticObject>();
- int i = 1;
- // 不断的向堆中添加对象
- while (true) {
- list.add(new StaticObject());
- i++;
- System.out.println(i);
- System.out.println(list.size());
- }
- }
- }
由于分配的堆内存太小, 所以导致, 堆溢出.
接着我们查看一下 Visual GC 的监视情况.
监视界面情况
我们可以从堆的使用情况看出, 基本已经使用完.
Visual GC 监视情况
从上图可知, 在短短的运行时间中, Eden 进行了 49 次 GC, 虽然时间短, 但是能说明一个问题, 新生代堆内存分配的空间太小, 导致频繁 GC.
同时, Old 老年代也进行了 33 次 GC, 虽然运行时间也在不需要优化的范围内, 而且从 Survivor 可以看出, 基本没有 GC, 说明这些都是大对象, 直接进入到了 Old 老年代, 导致 GC 频繁.
所以, 我们需要进行的优化就是加大新生代和老年代堆内存的大小, 同时减少大对象的产生.
参数优化分析
我们将 VM 参数改为:-Xms512m -Xmx512m -Xmn128m -XX:+HeapDumpOnOutOfMemoryError, 运行大概 5 分钟再次查看结果.
首先没有出现堆内存溢出.
监视情况
加大了堆内存, 所以堆内存没有出现问题.
Visual GC 监视情况
加大了堆内存之后, Eden 新生代进行了 66 次 GC, 使用时间 3.381s 基本满足要求 (执行时间不到 50ms,Minor GC 执行不频繁, 约 10 秒一次), 同时老年代 old 进行了 2 次 GC, 使用时间 4.127s, 这里还是有待优化的, 不太满足优化要求.
dump 文件分析
点击堆 dump 这个按钮就会生成 dump 文件, 我们可以分析类及对象的一些情况.
分析之后发现, StaticObject 对象大多, 没有进行 GC, 问题主要在这里, 所以, 下一步需要解决这个问题.
通过以上分析可以说明一个问题, 加大了堆内存之后, 新生代和老年代的 GC 情况大大的改善了, 但是还有大对象的问题, 所以还有待优化.
修改大对象, 进行 GC
修改程序, 如下:
- /**
- * VM Args:-Xms512m -Xmx512m -Xmn128m -XX:+HeapDumpOnOutOfMemoryError
- *
- * @author 欧阳思海
- */
- public class HeapTest {
- /* static class StaticObject {
- }*/
- public static void main(String[] args) {
- int i = 1;
- while (true) {
- i++;
- System.out.println(i);
- }
- }
- }
监视情况
堆一直运行良好.
Visual GC 监视情况
这次相对于上次相比, 老年代的情况已经改善了, 没有 GC, 说明大对象不存在了.
通过上面的分析跟优化, 就满足 GC 的需求了, 不需要再优化了.
Java 虚拟机深入理解系列全部文章更新中...
深入理解 Java 虚拟机 - Java 内存区域透彻分析
深入理解 Java 虚拟机 - 常用 vm 参数分析
深入理解 Java 虚拟机 - JVM 内存分配与回收策略原理, 从此告别 JVM 内存分配文盲
深入理解 Java 虚拟机 - 如何利用 JDK 自带的命令行工具监控上百万的高并发的虚拟机性能
来源: https://segmentfault.com/a/1190000021442637