Java Agent的隔离实现以及卸载时一些坑

转载自   Java Agent的隔离实现以及卸载时一些坑

在《一文带你了解Java Agent》中,让大家了解了Java Agent的来龙去脉,当通过attach方式去动态加载一个Java Agent时,Agent中的类会被加载到业务的虚拟机中,在使用完Agent的之后,如果想卸载这些无用的类,怎么实现?

这里就涉及到如何回收Perm区、或者Metaspace中已经加载的类了,如果一个类的类加载器对象没有GC Root关联,那么可以通过FGC的方式回收这些类。不过,如果通过JVM内部的类加载器比如AppClassLoader去加载这些类的话,可能永远也不能回收了,所以得通过自定义的类加载器去实现Agent类的加载动作,因为自定义的类加载器对象,我们可以自己控制。

下面是自定义类加载器的实现

public class AgentClassLoader extends URLClassLoader {public AgentClassLoader(URL[] urls) {super(urls, ClassLoader.getSystemClassLoader().getParent());}@Overrideprotected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {final Class<?> loadedClass = findLoadedClass(name);if (loadedClass != null) {if (resolve) {resolveClass(loadedClass);}return loadedClass;}// 优先从parent(SystemClassLoader)里加载系统类,避免抛出ClassNotFoundExceptionif (name != null && (name.startsWith("sun.") || name.startsWith("java."))) {return super.loadClass(name, resolve);}// 先从agent中加载try {Class<?> aClass = findClass(name);if (resolve) {resolveClass(aClass);}return aClass;} catch (Exception e) {// ignore}return super.loadClass(name, resolve);}
}

这样,通过AgentClassLoader加载的类,就可以和业务的类完全隔离开,在需要回收这些类的时候,只要把AgentClassLoader对象和GC root的关联完全掐断就行。

不过用了AgentClassLoader之后,还是遇到了一些坑,比如在Agent中使用Cat的时候,因为Cat是单例模式,都是通过 Cat.logEvent这种方式使用,所以在第一次使用Cat的时候,Cat内部会进行初始化,比如系统信息上报逻辑。因为业务逻辑在使用Cat的时候,已经初始化过了一次,在Agent内部使用时,因为是通过AgentClassLoader加载的,又是一个全新的Cat,相当于那些上报逻辑又初始化了一次,这这种明显是不行的,那如何在Agent中可以使用业务加载的那个Cat对象呢?

后来想到了一个解决方案,通过一个CatAdapt封装了一下Cat

public class CatAdapter {private static final Logger logger = LoggerFactory.getLogger(CatAdapter.class);private static Method logEvent;public static void init(ClassLoader classLoader) {try {Class catClazz = Class.forName("com.dianping.cat.Cat", true, classLoader);logEvent = catClazz.getMethod("logEvent", String.class, String.class);} catch (Exception e) {logger.error("cat adapter init failed", e);}}public static void logEvent(String type, String name) {if (logEvent != null) {try {logEvent.invoke(null, type, name);} catch (Exception e) {// ignore}}}
}

在Agent初始化入口的agentmain方法中,获取当前线程的classLoader

ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
Class catAdapter = agentLoader.loadClass("com.**.**.CatAdapter");
Method catAdapterInit = catAdapter.getMethod("init", ClassLoader.class);
catAdapterInit.invoke(null, currentClassLoader);

又通过agentLoader去加载CatAdapter类,在init方法中,通过当前线程的classLoader去加载真正的Cat类,这时拿到的Cat的class对象和业务的Cat class对象是同一个,从而避免了上述问题,在Agent内部就可以通过CatAdapter实现Cat方法的代理调用,从而实现数据的埋点。

卸载时的一些坑

为了验证执行FGC时,是否可以把无用的类回收,遇到了下面这些坑。 1、很单纯的以为把agentLoader设置为null,我就可以快乐的回收了,执行了 jmap-histo:live pid之后,惊喜的发现,Agent的类还在。 2、为了看下为什么没有回收,把堆对象dump下来,通过mat工具进行分析,找了一个Agent的类,发现其对象正被agentLoader对象拽着,顺腾摸瓜,发现agentLoader被线程池的线程拽着,这下明白了,需要把这些线程池给shutdown掉 3、因为在Agent初始化的时候,创建了几个线程池处理一些内部逻辑,所以要卸载Agent的时候,这些线程池必须shutdown。 4、把线程池shutdown之后,继续使用 jmap-histo:live pid,发现这些类特么还在,真是顽固啊。dump下来,继续分析,发现agentLoader还被一个 Finalizer对象给勾着!这是为啥,为什么有Finalizer对象勾着它?按照我的理解,只有重写了finalize方法的类才会有Finalizer对象,一瞬间,我怀疑是不是线程池的类重写了finalize方法,一查还真是,在 ThreadPoolExecutor类中重写了finalize方法。

5、重写了finalize方法,这种情况理论上要经过两次GC才会被回收,执行了两次 jmap-histo:live pid,Agent的类果然没了!!!那个开心。 6、后面又一次不经意的发现又无法回收了,又只能dump下来,继续分析,这次agentLoader对象被业务线程的threadLocal对象给拽着了,死都不放手。

这一次真的查了好久,因为不好复现,前前后后验证了多次,最终发现在使用了Agent的Mock功能之后,就会出现这个问题,Mock功能会根据业务配置的String字符串,通过jackson框架反序列化成一个对象并返回。

jackson在序列化的时候,需要开辟一块内存空间,为了能够重复利用这块空间,jackson默认把这个内存空间封装成一个SoftReference保存在ThreadLocal中。

 

这样每个线程都有一块内存可以重复使用,这原本是好事,但是在我们这,变成了一只暗搓搓的手,死死抓着agentLoader不放,导致了所有类都不能回收。

JsonFactory f = new JsonFactory();f.disable(JsonFactory.Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING);

最终取消这个特性,每次序列化都去创建一块内存,这样就可以避免这个问题,又可以快乐的回收了。

后面还有更多的坑等着去填,越填越开心...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/324013.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

KTV阶段项目告一段落,让我们张开双臂迎接新阶段吧!!

经过两周的周期&#xff0c;KTV项目终于告一段落&#xff0c;今天进行了项目答辩&#xff0c;大家表现的都很棒哦&#xff0c;以下是对KTV项目的一个简要总结。各位老师&#xff0c;各位4班的同学们&#xff0c;以及各位其他班的同学&#xff0c;大家下午好&#xff0c;首先需要…

Orleans稍微复杂的例子—互动

我费力费心的翻译过官方的教程,但是本人英语词汇量不高,可是架不住电子词典啊…只要肯花时间,我这些内容谁都可以做出来.所以这个事例告诉我们一个道理,那就是码农有三好,钱多话少死得早.我也许只有后两好. 当初阿尔法狗在围棋上战胜人类的时候,人工智能一时大热,不管老小,都大…

(链表 栈 队列 递归)

文章目录链表反转链表删除点链表中给定值的结点栈和队列双向链表实现栈和队列数组实现队列获取栈的最小值用两个栈实现一个队列用两个队列实现一个栈递归链表 反转链表 &#xff08;反转单链表 反转双向链表&#xff09; public class Code01_ReverseList {public static cl…

一文带你了解Java Agent

转载自 一文带你了解Java Agent Java Agent这个技术&#xff0c;对于大多数同学来说都比较陌生&#xff0c;像个黑盒子。但是多多少少又接触过&#xff0c;实际上&#xff0c;我们平时用的很多工具&#xff0c;都是基于Java Agent实现的&#xff0c;例如常见的热部署JRebel&am…

P3834-【模板】可持久化线段树 1(主席树)

正题 评测记录&#xff1a;https://www.luogu.org/recordnew/lists?uid52918&pidP3834 题意 给定一个长度为n的序列&#xff0c;有m个询问&#xff0c;求一个区间内的第k小的树。 解题思路 我们先思考用线段树快速询问第k小的树 我们可以用权值线段树来处理第k小的树&…

点滴小组KTV点歌系统简介

‍‍20级青鸟四班 点滴小组指导老师&#xff1a;穆老师 班主任&#xff1a;佟老师小组成员&#xff1a;组长&#xff1a;路鑫 副组长&#xff1a;戴洁 王硕组员&#xff1a;马蓥芳 组员&#xff1a;徐圣乾组员&#xff1a;徐圣坤 组员&#xff1a;赵昌杰制作周期&#xff1a;…

迁移.net framework 工程到.net core

在迁移.net core的过程中&#xff0c;第一步就是要把.net framework 工程的目标框架改为.net core2.0&#xff0c;但是官网却没有提供转换工具&#xff0c;需要我们自己动手完成了。.net framework 工程迁移为.net core工程大体上有两种方案&#xff1a; 1.创建一个.net core的…

(归并排序 快排 堆)

文章目录归并排序递归方法实现非递归方法实现求数组的小和求数组的降序对个数快排荷兰国旗问题&#xff08;Partition过程&#xff09;快排1.0快排2.0快排3.0堆大根堆堆排序使用堆排序归并排序 递归方法实现 public class Code01_MergeSort {// 递归方法实现public static vo…

亦云小组KTV点歌系统简介

20级青鸟四班 亦云小组指导老师&#xff1a;穆老师 班主任&#xff1a;佟老师小组成员&#xff1a;组长&#xff1a;靳天宇组员&#xff1a;王晓丹 谢佳泽 王睿志 蒲璐颖 张铨政目录&#xff1a;1.首页 2.项目前台 3..项目后台总结&#xff1a;本次KTV项目总结。总体来说&…

学习手记(2018/7/14~2018/7/18)——快乐纪中

2018/7/14&#xff1a;普通的纪中一天 儿子兄弟表示法 将一颗多叉树转换为二叉树的方法&#xff0c;左子节点连原树的第一个儿子&#xff0c;右子节点连原树的右边的兄弟 适用范围&#xff1a;树形dp 数位dp常见方法 状态压缩 分类讨论记忆法&#xff08;记忆化搜索&#x…

Entity Framework Core 2.0 特性介绍和使用指南

前言 这是.Net Core 2.0生态生态介绍的最后一篇&#xff0c;EF一直是我喜欢的一个ORM框架&#xff0c;随着版本升级EF也发展到EF6.x&#xff0c;Entity Framework Core是一个支持跨平台的全新版本&#xff0c;可以用三个词来概况EF Core的特点&#xff1a;轻量级、可扩展、跨平…

图解elasticsearch原理转载自

转载自 图解elasticsearch原理 版本 elasticsearch版本: elasticsearch-2.x 内容 图解ElasticSearch 云上的集群 集群里的盒子 云里面的每个白色正方形的盒子代表一个节点——Node。 节点之间 在一个或者多个节点直接&#xff0c;多个绿色小方块组合在一起形成一个Elas…

零云九歌小组KTV点歌系统简介

指导老师&#xff1a;穆老师 班主任&#xff1a;佟老师小组成员&#xff1a;组长&#xff1a;张炜林 副组长&#xff1a;李钰组员&#xff1a;郑宪佳 宋翔 李兆勋 杜庆霖零云九歌目录&#xff1a;1.首页 2.项目前台 3..项目后台总结&#xff1a;本次KTV项目总结。总体来说&am…

Orleans简单配置

话说曾几何时,我第一次看到xml文件,心中闪过一念想:"这<>是什么鬼?"…用ini或者json多简单易懂,现在发觉作为配置文件,json有赶超xml的趋势.不过xml用多了,也觉得顺眼多了.不觉得<>难看了,反而它能折叠让我觉得组织起来也更方便了.这说明一个道理:不是你…

say小组KTV点歌系统简介

指导老师&#xff1a;穆老师 班主任&#xff1a;佟老师小组成员&#xff1a;组长&#xff1a;焦文宇 组员&#xff1a;窦倩 王晓凤 巩固 石虹蔓 田锋目录&#xff1a;1.首页 2.项目前台 3..项目后台总结&#xff1a;从5月25号开始的到6月10号结束&#xff0c;这期间我们遇…

Java虚拟机必学之四大知识要点,附学习资料

转载自 Java虚拟机必学之四大知识要点&#xff0c;附学习资料 作为一位 Java 程序员&#xff0c;在尽情享受 Java 虚拟机带来好处的同时&#xff0c;我们还应该去了解和思考“这些技术特性是如何实现的”&#xff0c;去了解最底层的原理。只有熟悉 JVM&#xff0c;你才能在遇…

程序配置amp;amp;ConfigurationManager

配置组件是.net framework中非常常用的功能。在创建.net framework 工程时&#xff0c;系统不仅会自动生成app.config文件&#xff0c;而且还提供了非常强大的访问类库。但是这些好东西&#xff0c;在.net core 2.0中已经不复存在&#xff0c;至少说没有.net framework 中那么完…

既然参与,那就做好,我相信我们是最棒的!!!

‍‍大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。今天是3班的KTV项目答辩&#xff0c;也是20级的最后一个班级&#xff0c;连续6天的KTV项目答辩终于告一段落。回顾各班所做的项目&#xff0c;通过我自己参与点评的以及从班内参观同学…

又发生频繁FGC,这次是谁的锅

转载自 又发生频繁FGC&#xff0c;这次是谁的锅 这是笨神JVMPocket群里一位名为"云何*住"的同学提出来的问题&#xff0c;问题现象是CPU飙高并且频繁FullGC。 重现问题 这位同学的业务代码比较复杂&#xff0c;为了简化业务场景&#xff0c;笔者将其代码压缩成如…

三个剩两个,两个剩一个,最后一个都没剩下。

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。今天文章的标题本来是&#xff1a;“从未见过如此不要脸耍无赖的人&#xff01;&#xff01;&#xff01;”&#xff0c;后来想了想&#xff0c;毕竟公众号里面还有那么多不同身份的粉丝&a…