一、基本概述

1.1、介绍

在运行Java的时候有时候想测试运行时占用内存情况,这时候就需要使用测试工具查看了。在eclipse里面有 Eclipse Memory Analyzer tool(MAT)插件可以测试,而在IDEA中也有这么一个插件,就是JProfiler。

3Profiler是由 ej-technologies 公司开发的一款Java应用性能诊断工具。功能强大,但是收费。

官网下载地址: https://www.ej-technologies.com/products/jprofiler/overview.html

1.2、特点

  • 使用方便、界面操作友好(简单且强大)
  • 对被分析的应用影响小(提供模板)
  • CPU, Thread ,Memory分析功能尤其强大
  • 支持对jdbc ,noSql, jsp, servlet,socket等进行分析
  • 支持多种模式(离线,在线)的分析
  • 支持监控本地、远程的JVM
  • 跨平台,拥有多种操作系统的安装版本

image.png

1.3、主要功能

1.3.1-方法调用

对方法调用的分析可以帮助您了解应用程序正在做什么,并找到提高其性能的方法

1.3.2-内存分配

通过分析堆上对象、引用链和垃圾收集能帮您修复内存泄漏问题,优化内存使用

1.3.3-线程和锁

JProfiler提供多种针对线程和锁的分析视图助您发现多线程问题

1.3.4-高级子系统

许多性能问题都发生在更高的语义级别上。例如,对于JDBC调用,您可能希望找出
执行最慢的SQL语句。JProfiler支持对这些子系统进行集成分析

二、安装与配置

2.1、下载与安装

https://www.ej-technologies.com/download/jprofiler/version_100
image.png
image.png

2.2、JProfiler中配置IDEA

image.png

2.3、IDEA集成JProfiler

方式一:在idea插件中安装
image.png
image.png
方式二:从官方下载
https://plugins.jetbrains.com/plugin/253-jprofiler
image.png

三、具体试用

3.1、数据采集方式

JProfier数据采集方式分为两种:Sampling(样本采集)和Instrumentation(重构模式)

  • Instrumentation:这是Profiler全功能模式。在class加载之前,JProfier把相关功能代码写入到需要分析的class的bytecode中,对正在运行的jvm有一定影响。
    • 优点:功能强大。在此设置中,调用堆栈信息是准确的。
    • 缺点:若要分析的class较多,则对应用的性能影响较大, CPU开销可能很高(取决于Filter的控制)。因此使用此模式一般配合Filter使用,只对特定的类或包进行分析。
  • Sampling:类似于样本统计,每隔一定时间(5ms)将每个线程栈中方法栈中的信息统计出来。
    • 优点:对CPU的开销非常低,对应用影响小(即使你不配置任何Filter)
    • 缺点:一些数据/特性不能提供(例如:方法的调用次数、执行时间)

注:JProfiler本身没有指出数据的采集类型,这里的采集类型是针对方法调用的采集类型。因为JProfiler的绝大多数核心功能都依赖方法调用采集的数据,所以可以直接认为是JProfiler的数据采集类型。

3.2、遥感监测Telemetries

遥感监测Telemetries(查看VM的运行信息)

  • 整体视图overview:显示堆内存、cpu、线程以及GC等活动视图
  • 内存Memory:显示一张关于内存变化的活动时间表。
  • 记录的对象 Recorded objects:显示一张关于活动对象与数组的图表的活动时间表。
  • 记录吞吐量 Record Throughput:显示一段时间累计的JVM生产和释放的活动时间表。
  • 垃圾回收活动GC Activity:显示一张关于垃圾回收活动的活动时间表。
  • 类classes:显示一个与已装载类的图表的活动时间表。
  • 线程Threads:显示一个与动态线程图表的活动时间表
  • CPU负载 CPU Load:显示一段时间中CPU的负载图表。

    3.3、内存视图Live Memory

    Live memory内存剖析: class/class instance的相关信息。例如对象的个数,大小,对象创建的方法执行栈,对象创建的热点。

  • 所有对象All 0bjects

显示所有加载的类的列表和在堆上分配的实例数。只有Java 1.5 (JVMTI)才会显示此视图。
image.png

  • 记录对象 Record 0bjects

查看特定时间段对象的分配,并记录分配的调用堆栈。

  • 分配访问树Allocation Call Tree

显示一棵请求树或者方法、类、包或对已选择类有带注释的分配信息的J2EE组件。

  • 分配热点Allocation Hot Spots

显示一个列表,包括方法、类、包或分配已选类的32EE组件。你可以标注当前值并且显示差异值。对于每个热点都可以显示它的跟踪记录树。

  • 类追踪器Class Tracker

类跟踪视图可以包含任意数量的图表,显示选定的类和包的实例与时间。

分析:内存中的对象的情况
>频繁创建的Java对象:死循环、循环次数过多
>存在大的对象:读取文件时,byte[]应该边读边写。—>如果长时间不写出的话,导致byte[]过大
>存在内存泄漏

3.4、堆遍历heap walker

类classes
显示所有类和它们的实例,可以右击具体的类”Used Selected Instance”实现进一步跟踪。
分配 Allocations
为所有记录对象显示分配树和分配热点。
索引 References
为单个对象和“显示到垃圾回收根目录的路径”提供索引图的显示功能。还能提供合并输入视图和输出视图的功能。
时间 Time
显示一个对已记录对象的解决时间的柱状图。
检查Inspections
显示了一个数量的操作,将分析当前对象集在某种条件下的子集,实质是一个筛选的过程。
图表Graph
你需要在references视图和biggest视图手动添加对象到图表,它可以显示对象的传入和传出引用,能方便的找到垃圾收集器根源。

Ps :在工具栏点击”Go To Start”可以使堆内存重新计数,也就是回到初始状态。

3.5、cpu视图cpu views

Profiler提供不同的方法来记录访问树以优化性能和细节。线程或者线程组以及线程状况可以被所有的视图选择。所有的视图都可以聚集到方法、类、包或J2EE组件等不同层上。
访问树 Call Tree
显示一个积累的自顶向下的树,树中包含所有在JVM中已记录的访问队列。JDBC, JMS和NDI服务请求都被注释在请求树中。请求树可以根据Servlet和3SP对URL的不同需要进行拆分。
热点Hot Spots
显示消耗时间最多的方法的列表。对每个热点都能够显示回溯树。该热点可以按照方法请求,3DBC,MS和JNDI服务请求以及按照URL请求来进行计算。
访问图 Call Graph
显示一个从已选方法、类、包或32EE组件开始的访问队列的图。
方法统计Method Statistis
显示一段时间内记录的方法的调用时间细节。

3.6、线程视图threads团

了Profiler通过对线程历史的监控判断其运行状态,并监控是否有线程阻塞产生,还能将一个线程所管理的方法以树状形式呈现。对线程剖析。
线程历史 Thread History
显示一个与线程活动和线程状态在一起的活动时间表。
线程监控Thread Monitor
显示一个列表,包括所有的活动线程以及它们目前的活动状况。
线程转储 Thread Dumps
显示所有线程的堆栈跟踪。

线程分析主要关心三个方面:
1. web容器的线程最大数。比如:Tomcat的线程容量应该略大于最大并发数。
2.线程阻塞
3.线程死锁

3.7、监视器&锁Monitors&locks

监控和锁Monitors &Locks所有线程持有锁的情况以及锁的信息。
观察VM的内部线程并查看状态:

  • 死锁探测图表Current Locking Graph :显示JVM中的当前死锁图表。
  • 目前使用的监测器 Current Monitors :显示目前使用的监测器并且包括它们的关联线程。
  • 锁定历史图表 Locking History Graph :显示记录在JVM中的锁定历史。
  • 历史检测记录 Monitor History :显示重大的等待事件和阻塞事件的历史记录。
  • 监控器使用统计 Monitor Usage Statistics :显示分组监测,线程和监测类的统计监测数据。

    四、案例分析

    案例1
    良性的案例
    1. package com.study.jprofiler;
    2. import java.util.ArrayList;
    3. import java.util.concurrent.TimeUnit;
    4. public class JProfilerTest {
    5. public static void main(String[] args) {
    6. while (true){
    7. ArrayList list = new ArrayList();
    8. for (int i = 0; i < 500; i++){
    9. Data data = new Data();
    10. list.add(data);
    11. }
    12. try {
    13. TimeUnit.MILLISECONDS.sleep(500);
    14. } catch (InterruptedException e) {
    15. e.printStackTrace();
    16. }
    17. }
    18. }
    19. }
    20. class Data{
    21. private int size = 10;
    22. private byte[] buffer = new byte[1024 * 1024];
    23. private String info = "hello,chang";
    24. }
    案例2
    内存泄漏 ```java package com.study.jprofiler; import java.util.ArrayList; import java.util.concurrent.TimeUnit; public class MemoryLeak { public static void main(String[] args) {
    1. while (true){
    2. ArrayList beanList = new ArrayList();
    3. for (int i = 0; i < 500; i++){
    4. Bean data = new Bean();
    5. data.list.add(new byte[1024 * 10]);
    6. beanList.add(data);
    7. }
    8. try {
    9. TimeUnit.MILLISECONDS.sleep(500);
    10. } catch (InterruptedException e) {
    11. e.printStackTrace();
    12. }
    13. }
    } } class Bean{ int size = 10; String info = “hello,chang”; static ArrayList list = new ArrayList(); }

``` 一直不断的在占据内存,虽然进行了垃圾回收,但内存占用率还是在不断的增加
image.png
image.png
image.png
image.png
因为静态是跟着JVM的结束而结束,所以将Bean类下的ArrayList改成非静态的,JVM就能进行正常的垃圾回收的。