关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JAVA »ClassLoader, JavaAgent, Aspectj Weaving一站式扫盲帖

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

    2015-09-12 00:00:00 出处:ImportNew
    分享

    最近工作里复习的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装载的时机

    ClassLoader.loadClass() 前文所说的链接时触发的装载 Class.forName() 等java.lang.reflect反射包 new 构造对象 初始化子类时,会同时初始化父类 访问类的静态变量或静态方法(但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()的标准流程:

    findLoadedClass() 查看类是否已加载 如果不存在,则调用parent loader的loadClass() 如果不存在,调用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的字节码,现在还能不能跑就行。

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

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    Destoon 模板存放规则及语法参考

    Destoon系统常量与变量

    Destoon系统目录文件结构说明

    Destoon 系统安装指南

    Destoon会员公司主页模板风格添加方法

    Destoon 二次开发入门

    Microsoft 将于 2026 年 10 月终止对 Windows 11 SE 的支持

    Windows 11 存储感知如何设置?了解Windows 11 存储感知开启的好处

    Windows 11 24H2 更新灾难:系统升级了,SSD固态盘不见了...

    小米路由器买哪款?Miwifi热门路由器型号对比分析

    IT头条

    Synology 对 Office 套件进行重大 AI 更新,增强私有云的生产力和安全性

    01:43

    StorONE 的高效平台将 Storage Guardian 数据中心占用空间减少 80%

    11:03

    年赚千亿的印度能源巨头Nayara 云服务瘫痪,被微软卡了一下脖子

    12:54

    国产6nm GPU新突破!砺算科技官宣:自研TrueGPU架构7月26日发布

    01:57

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    技术热点

    最全面的前端开发指南

    Windows7任务栏桌面下角的一些正在运行的图标不见了

    sql server快速删除记录方法

    SQL Server 7移动数据的6种方法

    SQL Server 2008的新压缩特性

    每个Java程序员必须知道的5个JVM命令行标志

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。