深入理解Java ClassLoader及在 JavaAgent 中的应用

转载自   深入理解Java ClassLoader及在 JavaAgent 中的应用

背景

众所周知, Java 或者其他运行在 JVM(java 虚拟机)上面的程序都需要最终便以为字节码,然后被 JVM加载运行,那么这个加载到虚拟机的过程就是 classloader 类加载器所干的事情.直白一点,就是 通过一个类的全限定类名称来获取描述此类的二进制字节流 的过程.

双亲委派模型

说到 Java 的类加载器,必不可少的就是它的双亲委派模型,从 Java 虚拟机的角度来看,只存在两种不同的类加载器:

  1. 启动类加载器(Bootstrap ClassLoader), 由 C++语言实现,是虚拟机自身的一部分.
  2. 其他的类加载器,都是由 Java 实现,在虚拟机的外部,并且全部继承自java.lang.ClassLoader

在 Java 内部,绝大部分的程序都会使用 Java 内部提供的默认加载器.

启动类加载器(Bootstrap ClassLoader)

负责将$JAVA_HOME/lib或者 -Xbootclasspath 参数指定路径下面的文件(按照文件名识别,如 rt.jar) 加载到虚拟机内存中.启动类加载器无法直接被 java 代码引用,如果需要把加载请求委派给启动类加载器,直接返回null即可.

扩展类加载器(Extension ClassLoader)

负责加载$JAVA_HOME/lib/ext 目录中的文件,或者java.ext.dirs 系统变量所指定的路径的类库.

应用程序类加载器(Application ClassLoader)

一般是系统的默认加载器,比如用 main 方法启动就是用此类加载器,也就是说如果没有自定义过类加载器,同时它也是getSystemClassLoader() 的返回值.

这几种类加载器的工作流程被抽象成一个模型,就是双亲委派模型.

 

工作流程:

  1. 收到类加载的请求
  2. 首先不会自己尝试加载此类,而是委托给父类的加载器去完成.
  3. 如果父类加载器没有,继续寻找父类加载器.
  4. 搜索了一圈,发现都找不到,然后才是自己尝试加载此类.

这基本就是双亲委派模型.

但是这种模型只是一种推荐的方式,并不是强制的,你也可以尝试打破这种规则.
自所以这样约定,还是有一定的好处的, Java 类随着它的类加载器一起具备了一种带有优先级的层次关系.
比如自己定义了java.lang.Object 对象,那么按照上面的流程,他永远都是被启动类加载器加载的rt.jar 中的那个类,而不是自己定义的这个类,这样就保证了兄运行的稳定,否则,可能变得非常混乱,可以随意改写任何类.

在 JavaAgent 中的应用

大多数情况下,其实我们并不需要知道这些,因为你的程序也会运行的非常正常,虽然像Tomcat,Spring Boot 都有自己定义的类加载器,但是我们在不用关心的情况下也会运行的好好地.

那么类加载器可以被运行在哪些地方呢?

  • 从远程(或者文件)加载类,有时候需要加载的类可能并不是在当前的 classpath, 可能需要自己定义类加载器去加载.
  • 自己想实现一个JavaAgent来增强字节码的时候.

JavaAgent 的使用后续文章补上.先上一张图.

 

  • 顶层是应用代码实际运行的 ClassLoader, 可能是Application ClassLoader, 也有可能是 tomcat 的webapp ClassLoader 或者其他容器自定义的类加载器,总是是真实 的用户编写的代码运行的 classloader.

  • 我们如果要在javaagent中增强用户或者用户使用的包进行增强的话,必须实现一个自定义的 classloader 来"继承"(委派)应用代码的类加载器.为什么?

  • javaagent 的代码永远都是被应用类加载器( Application ClassLoader)所加载,和应用代码的真实加载器无关,举个栗子,当前运行在 tomcat 中的代码是webapp ClassLoader 加载的,如果启动参数加上-javaagent, 这个 javaagent 还是在Application ClassLoader中加载的.

  • 按照上面的双亲委派模型,如果我们在 javaagent 中想要访问应用里面的 api 包或者类,这是不可能的,因为按照双亲委派模型,通俗来说就是,子加载器可以访问父加载器中的类,但是反过来就行不通.

那么这个时候有没有办法能够做到呢?

  • 我们可以自定义自己的类加载器继承应用代码类加载器(可以在 javaagent 中完成, javaagent 每加载一个类,就会回调传回真实的类加载器),然后我们在Application ClassLoader 中用自定义的类加载器去加载子类,并创建好实例(newInstance()), 将实例的引用保存 在变量中.

  • 真实运行的时候,就会通过这个变量,去访问我们自定义加载器的内容,又由于我们的自定义类加载器是继承自应用代码的类加载器的,所以自定义类加载器中的代码可以访问应用的代码.

总结一句就是,父类加载器无法加载子类加载器的类,但是可以持有子类加载器所加载类的实例,从而实现父类加载器的代码可以调用子类加载器的代码的形式

貌似比较抽象,后面会补上详细的例子供参考.

例子

针对上面的情形,我们定义一个例子,可以详细解释 ClassLoader 的加载使用,

  1. 假如我们有如下的 ClassLoader,FooClassLoader:
package com.example.test;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;/*** @author lican*/
public class FooClassLoader extends ClassLoader {private static final String NAME = "/Users/lican/git/test/foo/";@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {Class<?> loadedClass = findLoadedClass(name);if (loadedClass == null) {String s = name.substring(name.lastIndexOf(".") + 1) + ".class";File file = new File(NAME + s);try (FileInputStream fileInputStream = new FileInputStream(file)) {byte[] b = new byte[fileInputStream.available()];fileInputStream.read(b);return defineClass(name, b, 0, b.length);} catch (IOException e) {e.printStackTrace();}}return loadedClass;}}
  1. 被加载的类定义,然后我们将这个类放到不是源代码的路径比如我放到
    /Users/lican/git/test/foo/这里的,主要是方便测试.

package com.example.test;public class FooTest {public String getFoo() {return "foo";}
}

然后测试程序为:

package com.example.test;import java.lang.reflect.Method;/*** @author lican*/
public class ClassLoaderTest {private Object fooTestInstance;private FooClassLoader fooClassLoader = new FooClassLoader();public static void main(String[] args) throws Exception {ClassLoaderTest classLoaderTest = new ClassLoaderTest();classLoaderTest.initAndLoad();Object fooTestInstance = classLoaderTest.getFooTestInstance();System.out.println(fooTestInstance.getClass().getClassLoader());Method getFoo = fooTestInstance.getClass().getMethod("getFoo");System.out.println(getFoo.invoke(fooTestInstance));System.out.println(classLoaderTest.getClass().getClassLoader());}private void initAndLoad() throws Exception {Class<?> aClass = Class.forName("com.example.test.FooTest", true, fooClassLoader);fooTestInstance = aClass.newInstance();}public Object getFooTestInstance() {return fooTestInstance;}
}

我们用FooClassLoader来加载com.example.test.FooTest, 然后在 AppClassLoader中持有引用.被后续使用.

引用

  • 深入理解 Java 虚拟机(第二版)

 

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

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

相关文章

如何从Gitee中拉取项目到HBuilder中?

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。今天&#xff0c;给大家分享一下&#xff0c;如何使用HBuilder连接gitee&#xff0c;进行代码的提交&#xff08;明天在看&#xff09;与拉取。1首先&#xff0c;在HBuilder中下载Git的插件…

ASP.NET Core Razor页面 vs MVC

作为.NET Core 2.0发行版的一部分&#xff0c;还有一些ASP.NET的更新。其中之一是添加了一个新的Web框架来创建“页面”&#xff0c;而不需要复杂的ASP.NET MVC。新的Razor页面是一个比较简单的MVC框架版本&#xff0c;在某些方面是老的“.aspx” WebForms的演变。 在本文中&a…

Gradle 简单使用

文章目录创建Gradle项目dependencies.gradlegradle.propertiesbuild.gradleGradle配置文件详解dependency-management 插件SpringBootPlugin 插件多模块热部署创建Gradle项目 dependencies.gradle ext.versions [ // 定义所有要使用的版本号springboot: 2.4.1 // Spri…

如何将HBuilder中的项目Push至Gitee中!

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。昨天&#xff0c;给大家介绍了下HBuilder中怎么从gitee中拉取项目至本地&#xff0c;需要的小伙伴们可以点击链接查看&#xff1a;如何从Gitee中拉取项目到HBuilder中&#xff1f;今天&…

PLEASE小组KTV点歌系统简要说明

PLEASE小组KTV项目指导老师&#xff1a;穆老师 班主任&#xff1a;佟老师小组成员&#xff1a;组长&#xff1a;徐祥 副组长&#xff1a;孟祥帅组员&#xff1a;董经纬 组员&#xff1a;孙晓文 周航 组员&#xff1a;徐龙一 张远远制作周期&#xff1a;2021年6月25日…

C#使用Xamarin开发可移植移动应用(5.进阶篇显示弹出窗口与通讯中心)附源码

源码地址:https://github.com/l2999019/DemoApp 可以Star一下,随意 - - 说点什么.. 没啥好说的.开干吧. 今天的学习内容? 今天的内容比较简单. 就几个弹出框和内置的消息通讯中心. 如图: 正文 1.xamarin中的弹出窗 xamarin帮我们把各个系统的弹出窗做了2个统一的类. 1.…

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

转载自 Java Agent的隔离实现以及卸载时一些坑 在《一文带你了解Java Agent》中&#xff0c;让大家了解了Java Agent的来龙去脉&#xff0c;当通过attach方式去动态加载一个Java Agent时&#xff0c;Agent中的类会被加载到业务的虚拟机中&#xff0c;在使用完Agent的之后&am…

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…