ClassLoader, JavaAgent, Aspectj Weaving一站式扫盲帖

最近工作里复习的Class Loader基础知识集锦,写下来希望对别人有帮助,而且不止是为了撂倒面试官。

为了尽量简单明了容易背,有些部分写得比较干。

0. 参考资料:

  • 书:《深入了解Java虚拟机》、《实战Java虚拟机》
  • 规范: Java语言规范 第12章
  • 源码: OpenJDK 7 的Java及C代码( class.c , classloader.c,jvm.cpp)

1. Class装载的三个阶段

1.1 载入 (Load)

从Class文件或别的什么地方载入一段二进制流字节流,把它解释成永久代里的运行时数据结构,生成一个Class对象。

1.2 链接 (Resolve)

将之前载入的数据结构里的符号引用表,解析成直接引用。

中间如果遇到引用的类还没被加载,就会触发该类的加载。

可能JDK会很懒惰的在运行某个函数实际使用到该引用时才发生链接,也可能在类加载时就解析全部引用。

1.3 初始化 (Initniazle)

初始化静态变量,并执行静态初始化语句。

2. Class装载的时机

  1. ClassLoader.loadClass()
  2. 前文所说的链接时触发的装载
  3. Class.forName() 等java.lang.reflect反射包
  4. new 构造对象
  5. 初始化子类时,会同时初始化父类
  6. 访问类的静态变量或静态方法(但static final的常量除外,此君在常量池里)

本质上,也是很懒惰的按需加载的,由于类装载的Lazy和前面解释引用的Lazy,所以Jar包里有时候有些类用到的了没在Class Path里的其他类,也能人品爆发的照跑不误。

除了1,其他几种方式默认都到达类装载的初始化阶段。

3. ClassLoader.loadClass() 与 Class.forName()

ClassLoader.loadClass(String name, boolean resolve),其中resolve默认为false,即只执行类装载的第一个阶段。

Class.forName(String name, boolean initialize, ClassLoader loader), 其中initialize默认为true,即执行到类装载的第三个阶段。

4. ClassNotFoundException 和 NoClassDefFoundError

ClassLoader.loadClass() 与 Class.forName() 找不到类定义的二进制流时抛出ClassNotFoundException。

链接阶段解释引用失败,找不到引用的类时抛出NoClassDefFoundError。

5. ClassLoader及双亲委派机制

ClassLoader.loadClass()的标准流程:

  1. findLoadedClass() 查看类是否已加载
  2. 如果不存在,则调用parent loader的loadClass()
  3. 如果不存在,调用findClass() 在本ClassLoader的ClassPath里加载该类

所谓双亲委派机制,就是先从parent loader开始查找,找不到了才用自己的findClass()函数去查找,兼顾了效率:避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次,和安全,避免子类乱加载。

而OSGI或SPI或热替换方案,则需要破坏这个双亲委托,先调用自己的findClass()。

findClass() 是各个ClassLoader各自实现,各显神通的地方,从各种奇葩地方载入Class二进制字节流。

但最后都会调用defineClass(),传入二进制字节流,返回Class对象。留意此处,呆会AspectJ的时候会回到这里。

在JDK6,loadClass()很过分的定义了方法级的synchronized ,在JDK7改成一个以Class Name作Key的 parallelLockMap,增强了并行加载不同Class的能力。

6. System ClassLoader 与 Thread Context Classloader

有时候,看到错误日志说张三不是张三,包名类名一样但instanceof 死活返回 false,唯一原因是它们由两个不同的ClassLoader加载。

默认的Bootstrap(加载jdk的lib目录),Extension(加载jdk的lib/ext目录),Application(加载启动时定义的classpath)三层ClassLoader机制不再重复。

平时用ClassLoader.getSystemClassLoader()就可以得到sun.misc.Launcher$ApplicationClassLoader 这个Application ClassLoader。

在类A里加载类B,默认使用加载了类A的Loader。但,也有特殊情况,比如JDBC加载driver时的机制,需要在父 ClassLoader(JDBC属于JDK一部分)里根据配置反射创建jdbc driver的数据实现类,Sun设计了一个特殊方案 --Thread Context Class Loader。

JAXB(比如要在Jar包里找xsd schema文件的时候)也使用了它,所以用到它们时就要注意Thread Context ClassLoader的设置,可以用代码随时设置current thread的loader,也可以用自定义的ThreadFactory在创建线程时设置,它默认是父线程的loader,如果都没设置就是 System ClassLoader。

7. Java Agent机制与AspectJ的LoadTime Weaving

在JDK5开始,在启动JVM时可增加-javaagent参数,在装载Class时对类进行动态的修改。

AspectJ的Load Time Weaving机制,需要配置 -javaagent: [path to aspectj-weaver.jar] 。

打开aspectj-weaver.jar,可以看到META-INF/MANIFEST里定义了 Premain-Class: org.aspectj.weaver.loadtime.Agent

再打开这个Agent类,简化后的代码大概这个样子:

ClassFileTransformer s_transformer = new ClassPreProcessorAgentAdapter();
public static void premain(String options, Instrumentation instrumentation) {
instrumentation.addTransformer(s_transformer);
}

可见它的主要作用是将自己的类转换器注册到JDK所传入的Instrumentation。

再看ClassFileTransformer的定义:ClassLoader会在前面defineClass()的过程中,在把二进制字节流转换为Class对象之前,先把二进制流和当前ClassLoader传给Transformer,由Transformer加工为另一段二进制字节流返回。

AspectJ就是利用传入的ClassLoader,找出其Class Path里的META-INF/aop.xml,然后根据aop.xml里的配置进行代码植入。

测试显示,加了LoadTime Weaving,类加载的速度明显变慢,如果是100ms就调用超时的服务,需要做类的预加载。

8. Jar包的预加载

比如有个有趣的需求是加载某个Class A所在的Jar里的全部的Class (怎么好像一点都不有趣)

URL jarUrl = ClassA.getProtectionDomain().getCodeSource().getLocation();
JarFile jarfile = new JarFile(jarUrl.getPath());
Enumeration entries = jarfile.entries();

然后遍历JarEntry,过滤出后缀为.class的文件,按类名进行装载就可以了。

9.Class的二进制兼容性

如果Class A 依赖 spring-1.0.jar编译,当spring升级到spring-2.0.jar,Class A不需要修改代码也不需要重新编译,可以直接运行的,spring-2.0.jar就满足二进制兼容性。

在 Java语言规范的第13章 有详细的描述 ,不想直接睡着最好可以找个中文版来看,感谢那些翻译的同学。

虽然规范的这章看着比较长比较吓人,但其实二进制兼容性还是很容易做到的,只要你不做把接口改为抽象类之类奇怪的事情,其他一些看起来很大的改动,比如改throws定义,其实都没有问题。

真的遇到问题,设身处地想想自己是那段Class A的字节码,现在还能不能跑就行。

感谢你看到这里,希望你只在工作里用到这些知识,祝工作愉快。

时间: 2024-12-20 01:13:26

ClassLoader, JavaAgent, Aspectj Weaving一站式扫盲帖的相关文章

hadoop快速扫盲帖,从零了解hadoop

1.MapReduce理论简介 1.1 MapReduce编程模型 MapReduce采用"分而治之"的思想,把对大规模数据集的操作,分发给一个主节点管理下的各个分节点共同完成,然后通过整合各个节点的中间结果,得到最终结果.简单地说,MapReduce就是"任务的分解与结果的汇总". 在Hadoop中,用于执行MapReduce任务的机器角色有两个:一个是JobTracker:另一个是TaskTracker,JobTracker是用于调度工作的,TaskTracke

什么叫响应式网页,扫盲帖

摘自百度 响应式Web设计(Responsive Web design)的理念是: 页面的设计与开发应当根据用户行为以及设备环境(系统平台.屏幕尺寸.屏幕定向等)进行相应的响应和调整.具体的实践方式由多方面组成,包括弹性网格和布局.图片.CSS media query的使用等.无论用户正在使用笔记本还是iPad,我们的页面都应该能够自动切换分辨率.图片尺寸及相关脚本功能等,以适应不同设备:换句话说,页面应该有能力去自动响应用户的设备环境.响应式网页设计就是一个网站能够兼容多个终端——而不是为每个

HTTPS科普扫盲帖

---恢复内容开始--- 为什么需要https HTTP是明文传输的,也就意味着,介于发送端.接收端中间的任意节点都可以知道你们传输的内容是什么.这些节点可能是路由器.代理等. 举个最常见的例子,用户登陆.用户输入账号,密码,采用HTTP的话,只要在代理服务器上做点手脚就可以拿到你的密码了. 用户登陆 --> 代理服务器(做手脚)--> 实际授权服务器 在发送端对密码进行加密?没用的,虽然别人不知道你原始密码是多少,但能够拿到加密后的账号密码,照样能登陆. HTTPS是如何保障安全的 HTTP

【腾讯bugly干货分享】HTML 5 视频直播一站式扫盲

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1277 视频直播这么火,再不学就 out 了. 为了紧跟潮流,本文将向大家介绍一下视频直播中的基本流程和主要的技术点,包括但不限于前端技术. 1. H5 到底能不能做视频直播? 当然可以, H5 火了这么久,涵盖了各个方面的技术. 对于视频录制,可以使用强大的 webRTC(Web Real-Time Communica

八卦扫盲帖

2014 10 29 2:48 PM 反贼全部无条件投降,因为反贼门都来自3,这个世界属于-11(混沌) 2014 10 29 10:01PM Teachers 全部投降 在9月初他们一致认为你会输 所以和学生签订了攻守同盟(只有少数学生知道) 只可惜其他参与者误认为老师一直和你一边 逆着来 老师越是帮 他们他们越认为老师不怀好意 更加深了对老师的敌意 以至于形成了一个搞笑(高效)的局面 逼着老师要帮你他们才心安 老师最后形成了这样一个delimma 帮你要 承担痛苦和后果 不帮你学生逼老师帮你

visul studio 使用git扫盲帖。

写给和一样的菜B 有必要知道的命令: git rm --cached (文章底部有git命令大全) 创建.gitignore文件 windows版: 在项目根目录下面创建gitignore.txt文件 把你需要排除的文件名保存到gitignore.txt文件 在项目根目录下面按住Shift键并邮件然后选择“在此处打开命令窗口”(有的是powershell) 执行命令 ren gitignore.txt .gitignore linux: touch .gitignore #创建gitignore

生产者消费者模式(转)

本文转载自博文系列架构设计:生产者/消费者模式.文中对原文格式进行了稍加整理. 概述 今天打算来介绍一下“生产者/消费者模式”,这玩意儿在很多开发领域都能派上用场.由于该模式很重要,打算分几个帖子来介绍.今天这个帖子先来扫盲一把.如果你对这个模式已经比较了解,请跳过本扫盲帖,直接看下一个帖子(关于该模式的具体应用) . 看到这里,可能有同学心中犯嘀咕了:在四人帮(GOF)的23种模式里面似乎没听说过这种嘛!其实GOF那经典的23种模式主要是基于OO的(从书名<Design Patterns: E

IT知识收藏.2015年1月、2月

--个人收藏的近两个月的最新网文,经过整理IT类知识网址100个左右,今天共享一下希望大家喜欢-- 按来源分组 程序员杂志 你为什么不是史蒂夫·乔布斯 http://www.csdn.net/article/2015-01-29/2823765 造成IT项目失败的五个原因 http://www.csdn.net/article/2015-01-30/2823781 Esri ArcGIS 10.3 惊艳登场,打造新一代Web GIS最强"芯" http://blog.csdn.net/

2017年2月笔记

[阿里巴巴JAVA开发手册] https://segmentfault.com/p/1210000008272934 一个复杂系统的拆分改造实践 http://www.cnblogs.com/LBSer/p/6195309.html 搭建vue+webpack+mock脚手架(一) https://segmentfault.com/a/1190000008279215 微信LazyMan笔试题的深入解析和实现 http://www.jianshu.com/p/f1b7cb456d37 蚂蚁开源的