jstat的小伙伴---找出system.gc的调用的小工具

简介: 场景分析现场环境中,造成gc频繁的可能性之一就是通过system.gc主动调用了gc。这种情况出现在开发人员业务代码,或者是jdk自身的代码中(例如nio)。

场景分析

现场环境中,造成gc频繁的可能性之一就是通过system.gc主动调用了gc。这种情况出现在开发人员业务代码,或者是jdk自身的代码中(例如nio)。我们可以通过jstat -gccause查看gc的原因,如果真的是system.gc,那么找出调用的代码就是继续解决问题的关键。

查看system.gc的调用

如果说查看代码调用,那么jstack就是首选,仔细想想,代码的触发时机不定,怎么才能去在合适的时候打印堆栈呢,最简单的想法就是定时的去调用,这个方法是有用的,只不过在频繁调用的时候,是可以捕获到的。可是万一不频繁调用呢。

当我们想知道自己的代码在什么时候被调用,那么最好的方式就是在自己的方法里去打印堆栈。这样就知道是谁调用的了,也不用担心是调用的时机等等。

解决方案也就出来了,那就是在system.gc调用的时候顺便打印一下堆栈。system是jdk的类,可以自己编写一个system类替换掉jdk的,只不过不想用的时候还得改回来。为了灵活我们选择动态修改字节码。

字节码改造

我们先想想打印堆栈的代码

        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement element : stackTrace) {
            System.out.println(element);
        }

现在呢,我们就在system.gc前调用这个方法。这里修改字节码的方式,我们使用asm。

            InsnList list = new InsnList();
            list.add(getLabelNode());
            list.add(new MethodInsnNode(INVOKESTATIC, "com/xp/agent/core/ThreadInfo", "getStack", "()V", false));
            insns.insert(list);

修改完就大功告成。

细节补充

字节码是改成了,但是我们的那个类路径得让加载system类的classloader(bootstrapclassloader)找到我们的类。否则会抛出classnotfind或者noclassdefineerror。这里我们通过增加配置文件的方式。

Agent-Class: com.xp.agent.main.Main
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Class-Path: trace-0.0.1-SNAPSHOT-common.jar asm-all-6.0_BETA.jar
Created-By: Apache Maven 3.3.9
Build-Jdk: 1.8.0_121
Boot-Class-Path: trace-0.0.1-SNAPSHOT-agentcore.jar

代码地址

大佬如果喜欢,可以点个星鼓励一下。

https://github.com/xpbob/lightTrace

目录
相关文章
|
6月前
|
运维 监控 数据可视化
从0开始回顾JVM---系列五
JVM调优 1、有哪些常用的命令行性能监控和故障处理工具? 操作系统工具 ● top:显示系统整体资源使用情况 ● vmstat:监控内存和CPU ● iostat:监控IO使用 ● netstat:监控网络使用 JDK性能监控工具 ● jps:虚拟机进程查看 ● jstat:虚拟机运行时信息查看 ● jinfo:虚拟机配置查看 ● jmap:内存映像(导出) ● jhat:堆转储快照分析 ● jstack:Java堆栈跟踪 ● jcmd:实现上面除了jstat外所有命令的功能 2、了解哪些可视化的性能监控和故障处理工具? 以下是一些JDK自带的可视化性能监控和故障处理工具: ● JCons
|
6月前
|
存储 算法 安全
从0开始回顾JVM---系列三
20、有哪几种垃圾回收器,各自的优缺点是什么? ⚡ 垃圾回收器主要分为以下几种:Serial,Parallel,CMS,G1。 1. Serial ● 单线程垃圾回收器,该线程运行时,其他线程都暂停,使用复制算法; ● 使用场景: 堆内存较小,适合个人电脑(CPU核数较小); a. 工作在老年区,回收算法:标记整理;工作在新生代,回收算法:复制; b. 单线程的垃圾回收器,只有一个垃圾回收线程在运行; c. 垃圾回收线程结束后,其他线程恢复运行; d. 触发垃圾回收时让所有线程在安全点停下【在垃圾回收过程中,对象的地址可能发生改变,为了保证安全使用对象地址,要
|
6月前
|
存储 安全 Java
从0开始回顾JVM---系列四
虚拟机执行 1、类的生命周期吗? 一个类从被加载到虚拟机内存中开始,到从内存中卸载,整个生命周期需要经过七个阶段:加载 、验证、准备、解析、初始化 、使用 和 卸载,其中验证、准备、解析三个部分统称为连接。 2、什么是类加载?类加载的过程? 类加载: 虚拟机把描述类的数据加载到内存里面,并对数据进行校验、解析和初始化,最终变成可以被虚拟机直接使用的class对象。 类加载过程如下: ● 加载:1、根据类的全限定类名获取二进制字节流;2、将字节流代表的静态存储结构转为方法区运行时存储数据结构;3、在堆中生成Class对象,作为方法区这个类数据访问入口; ● 验证: 检验加载的class文
|
6月前
|
存储 Java 编译器
从0开始回顾JVM---系列一
引言 1、什么是JVM? JVM——Java虚拟机,它是Java实现平台无关性的基石。 Java程序运行的时候,编译器将Java文件编译成平台无关的Java字节码文件(.class),接下来对应平台JVM对字节码文件进行解释,翻译成对应平台匹配的机器指令并运行。 同时JVM也是一个跨语言的平台,和语言无关,只和class的文件格式关联,任何语言,只要能翻译成符合规范的字节码文件,都能被JVM运行。 内存管理 1、什么是JVM内存结构? Java1.8 之后的内存结果图: JVM将虚拟机分为5大区域,程序计数器、虚拟机栈、本地方法栈、java堆、方法区,其中方法区和堆是线程共享区,虚拟
|
6月前
|
存储 缓存 算法
从0开始回顾JVM---系列二
11、什么情况下会发生内存泄露? 1. 静态集合类引起内存泄漏 ● 静态集合的生命周期和 JVM 一致,所以静态集合引用的对象不能被释放。 2. 单例模式 ● 单例对象在初始化后会以静态变量的方式在 JVM 的整个生命周期中存在。如果单例对象持有外部的引用,那么这个外部对象将不能被 GC 回收,导致内存泄漏。 3. 数据连接、IO、Socket等连接 ● 创建的连接不再使用时,需要调用 close 方法关闭连接,只有连接被关闭后,GC 才会回收对应的对象(Connection,Statement,ResultSet,Session)。忘记关闭这些资源会导致持续占有内存,无法被 GC 回收。 4
|
4月前
|
Java UED
Java面试题:描述JVM中垃圾收集的Stop-The-World现象及其影响
Java面试题:描述JVM中垃圾收集的Stop-The-World现象及其影响
52 1
|
5月前
|
Java
JVM打印GC信息
JVM打印GC信息
|
6月前
|
Web App开发 存储 数据可视化
VisualVM【实践 01】工具VisualVM下载使用及插件Visual GC示例说明HashMap初始化容量initialCapacity的影响(源码及visualvm_215.zip分享)
VisualVM【实践 01】工具VisualVM下载使用及插件Visual GC示例说明HashMap初始化容量initialCapacity的影响(源码及visualvm_215.zip分享)
93 0
|
Java
JVM学习笔记-如何在IDEA打印JVM的GC日志信息
若要在Idea上打印JVM相应GC日志,其实只需在Run/Debug Configurations上进行设置即可。
114 0