JVM监控及诊断工具(GUI)
环境
jdk11
jconsole
- 官方文档:https://docs.oracle.com/javase/7/docs/technotes/guides/management/jconsole.html
- 从Java5开始,在JDK中自带的Java监控和管理控制台
- 用于对JVM中内存、线程和类等的监控,是一个基于JXM(java management extensions)的GUI性能监控工具
- 连接方式
4.1 Local:使用JConsole连接一个正在本地系统运行的JVM,并且执行程序的和运行JConsole的需要是同一个用户。JConsole使用文件系统的授权通过RMl连接器连接到平台的MBean服务器上。这种从本地连接的监控能力只有Sun的JDK具有。
4.2 Remote:使用下面的URL通过RMI连接器连接到一个JMX代理,servicejmx.rmi:///jndi/rmi://hostName:portNum/jmxrmi。JConsole为建立连接,需要在环境变量中设置mx.remote.credentials来指定用户名和密码,从而进行授权。
4.3 Advanced:使用一个特殊的URL连接JMX代理。一般情况使用自己定制的连接器而不是RMI提供的连接器来连接JMX代理,或者是一个使用JDK1.4的实现了JMX和JMX Rmote的应用。
Visual VM
- Visual vM是一个功能强大的多合一故障诊断和性能监控的可视化工具。
- 它集成了多个JDK命令行工具,使用visual VM可用于显示虚拟机进程及进程的配置和环境信息(jps,jinfo),监视应用程序的CPU、Gc、堆、方法区及线程的信息(jstat、jstack)等,甚至代替Jconsole。
- VisualVM 也已在 Oracle JDK 6~8 中作为Java VisualVM发布。它已在 Oracle JDK 9 中停止使用。
- 下载地址:https://visualvm.github.io/download.html
- 可能的错误
- 安装过程
6.1 修改etc下的配置文件:visualvm.conf
6.2 如果jdk的目录中有jre目录,将其删除或移出jdk目录
- 插件:https://visualvm.github.io/plugins.html
- 连接方式
8.1 本地连接:监控本地Java进程的CPU、类、线程等
8.2 远程连接
8.2.1 确定远程服务器的ip地址
8.2.2 添加JMX(通过JMX技术具体监控远端服务器哪个Java进程)
8.2.3 修改bin/catalina.sh文件,连接远程的tomcat
8.2.4 在.../conf中添加jmxremote.access和jmxremote.password文件
8.2.5 将服务器地址改为公网ip地址
8.2.6 设置阿里云安全策略和防火墙策略
8.2.7 启动tomcat,查看tomcat启动日志和端口监听
8.2.8 JMX中输入端口号、用户名、密码登录 - 主要功能
9.1 生成/读取堆内存快照
9.2 查看JVM参数和系统属性
9.3 查看运行中的虚拟机进程
9.4 生成/读取线程快照
9.5 程序资源的实时监控
9.6 其它功能:1. JMX代理连接;2. 远程环境监控;3. CPU分析和内存分析 - 添加插件
Tool=>Plugins
public static void main(String[] args) {
List<String> list = new ArrayList<>();
while (true) {
list.add(new String());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
eclipse MAT
- MAT(Memory Analyzer Tool)工具是一款功能强大的Java堆内存分析器。可以用于查找内存泄漏以及查看内存消耗情况。
- 下载地址:https://www.eclipse.org/mat/downloads.php
- 对正在运行的Java进程生成dump文件
File=>Acquire Heap Dump
- histogram:显示各个类的实例数目以及这些实例的浅堆(Shallow heap)或深堆(retained heap)
4.1 显示对象的强引用
4.2 两个dump文件对比
- thread overview:查看系统中的Java线程、查看局部变量的信息
5.1 出引用和入引用
深堆和浅堆
浅堆(shallow heap)
- 浅堆(shallow heap)是指一个对象所消耗的内存。在32位操作系统中,一个对象引用会占据4个字节,一个int类型会占据4个字节,long型变量会占据8个字节,每个对象头需要占用8个字节。根据堆快照格式不同,对象的大小可能会向8字节进行对齐。
- 以String为例: 2个int值共占8字节,对象引用占用4字节,对象头8字节,合计20字节,向8字节对齐,故占24字节。(jdk7中)。
- 这24字节为String对象的浅堆大小。它与String的value实际取值无关,无论字符串长度如何,浅堆大小始终是24字节。
深堆(retained heap)
- 保留集(retained set):对象A的保留集指当对象A被垃圾回收后,可以被释放的所有的对象集合(包括对象A本身),即对象A的保留集可以被认为是只能通过对象A被直接或间接访问到的所有对象的集合。通俗地说,就是指仅被对象A所持有的对象的集合。
- 深堆(retained heap):深堆是指对象的保留集中所有的对象的浅堆大小之和。
- 浅堆指对象本身占用的内存,不包括其内部引用对象的大小。一个对象的深堆指只能通过该对象访问到的(直接或间接)所有对象的浅堆之和,即对象被回收后,可以释放的真实空间。
支配树
- MAT提供了一个称为支配树(Dominator Tree)的对象图。支配树体现了对象实例间的支配关系。在对象引用图中,所有指向对象B的路径都经过对象A,则认为对象A支配对象B。如果对象A是离对象B最近的一个支配对象,则认为对象A为对象B的直接支配者。
- 支配树是基于对象间的引用图所建立的,它有以下基本性质:
2.1 对象A的子树(所有被对象A支配的对象集合)表示对象A的保留集(retained set),即深堆。
2.2 如果对象A支配对象B,那么对象A的直接支配者也支配对象B。
2.3 支配树的边与对象引用图的边不直接对应。
内存泄漏
- Java中内存泄漏的8中情况:静态集合类、单例模式、内部类持有外部类、各种连接,如数据库连接等、变量不合理的作用域、改变哈希值、缓存泄漏、监听器和回调
- 静态集合类:如HashMap、LinkedList等。如果这些容器为静态的,那么她们的生命周期与JVM程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。即长生命周期的对象持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收。
static List list = new ArrayList();
public void method1() {
Object o = new Object();
list.add(o);
}
- 单例模式:单例模式,和静态集合导致内存泄露的原因类似,因为单例的静态特性,它的生命周期和JVM 的生命周期一样长,所以如果单例对象如果持有外部对象的引用,那么这个外部对象也不会被回收,那么就会造成内存泄漏。
- 内部类持有外部类:如果一个外部类的实例对象的方法返回了一个内部类的实例对象。这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄漏。
- 各种连接:在对数据库进行操作的过程中,首先需要建立与数据库的连接,当不再使用时,需要调用close方法来释放与数据库的连接。只有连接被关闭后,垃圾回收器才会回收对应的对象。否则,如果在访问数据库的过程中,对Connection、Statement或ResultSet不显性地关闭,将会造成大量的对象无法被回收,从而引起内存泄漏。
- 变量不合理的作用域:一个变量的定义的作用范围大于其使用范围,很有可能会造成内存泄漏。另一方面,如果没有及时地把对象设置为null,很有可能导致内存泄漏的发生。
- 改变哈希值:
7.1 当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了。
7.2 否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄漏。
7.3 这也是String为什么被设置成了不可变型 - 缓存泄漏:内存泄漏的另一个常见来源是缓存,一旦你把对象引用放入到缓存中,他就很容易遗忘。对于这个问题,可以使用WeakHashMap代表缓存,此种Map的特点是,当除了自身有对key的引用外,此key没有其他引用那么此map会自动丢弃此值。
- 监听器和回调:内存泄漏另一个常见来源是监听器和其他回调,如果客户端在你实现的API中注册回调,却没有显示的取消,那么就会积聚。需要确保回调立即被当作垃圾回收的最佳方法是只保存它的弱引用,例如将他们保存成为WeakHashMap中的键。
JProfiler
- Profiler是由 ej-technologies公司开发的一款Java应用性能诊断工具。
- 下载地址:https://www.ej-technologies.com/download/jprofiler/files
- 主要功能:方法调用、内存分配、线程和锁、高级子系统
- 数据采集方式
4.1 Instrumentation:全功能模式,在Class加载之前,JProfiler把相关功能代码写入到需要分析的class的bytecode中,对正在运行的JVM有一定影响
4.1.1 优点:功能强大,调用堆栈信息正确
4.1.2 缺点:若分析的class较多,对应用的性能影响较大,CPU开销可能很高。因此此模式一般配合Filter使用,只对特定的类或包进行分析
4.2 Sampling:类似于样本统计,每隔一定时间(5ms)将每个线程栈中方法栈中的信息统计出来
4.2.1 优点:对CPU的开销很低,对应用影响小
4.2.2 缺点:一些数据/特性不能提供(例如:方法的调用次数)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)