个人主页:五敷有你
系列专栏:JVM
⛺️稳中求进,晒太阳
ClassLoader包含了四个核心方法
//由类加载器子类实现,获取二进制数据调用defineClass,比如URLClassLoader会根据文件路径去获取类文件中的二进制文件
(不想打破就重写它)
public Class> findClass(String name)
//类加载的入口,提供了双亲委派机制,内部会调用findClass(打破双亲委派,就重写它)
protected Class> loadClass(String name)
//做一些类名的校验,然后调用虚拟机底层方法将字节码信息加载到虚拟机内存中
protected final Class> defineClass(String name,byte[] b,int off,int len)
//执行类生命周期的连接阶段
protected final void resolveClass(Class> c)
阅读双亲委派机制的核心代码,分析如何通过自定义类加载器打破双亲委派机制。
打破双亲委派机制的核心就是讲下面的代码重写
自定义类加载器的父类怎么是AppClassLoader呢?
以Jdk8为例,ClassLoader类中提供了构造方法设置parent的内容:
这个构造方法由另外一个构造方法调用,其中父类加载器由getSystemClassLoader方法设置,该方法返回的是AppClassLoader
两个自定义类加载器加载相同限定名的类,不会冲突吗?
不会冲突,在同一个Java虚拟机中,只有相同类加载器+相同的类限定名才会被认为是同一个类。
在Arthas中使用sc –d 类名的方式查看具体的情况
JDBC中使用了DriverManager来管理项目中的不同数据库的驱动,比如mysql驱动,oracle驱动
DrvierManager类位于rt.jar包中,由启动类加载器加载
依赖中的mysql驱动对应的类,由应用程序类加载器来加载。
DriverManager属于rt.jar是启动类加载器的,而用户jar包中的驱动需要由应用程序类加载器加载,这就违反了双亲委派机制。
DriverManager怎么知道jar包中要加载的驱动在哪里?
DriverManager使用SPI机制,最终加载jar包中的对应驱动类
SPI全称(Service Provider Interface)是JDK内置的服务提供发现机制。
历史上,OSGi模块化框架,它存在同级之间的类加载器的委托加载,OSGi还使用类加载器实现了热部署功能
热部署指的是在服务器不停止的情况下,动态的更新字节码文件到内存中
使用arthas不停机解决线上问题
背景:小李的团队将代码上线后,发现bug,但用户着急使用,如果重新打包在发布需要一个多小时的时间,所以希望使用arthas尽快解决问题
思路:
- 在出问题的服务上部署一个arthas并启动。
- jad(反编译) --source-only 类全限定名 > 目录/文件名.java
- jad 命令 反编译,然后可以用其他编辑器修改源码(比如Vim)
- mc -c 类加载器的hashcode 目录/文件名.java -d 输出目录
- mc 命令用来编译修改过的代码。
- retransform class 文件所在目录/xxx.class
- retransform命令加载新的字节码
注意:
JKD8及之前的版本,扩展类加载器和应用程序类加载器的源码位于rt.jar包中的sun.misc.Launcher.java中
JDK9引入了module的概念,类加载器在设计上发生很大变化
平台类加载器遵循模块化方式加载字节码文件,所以继承关系从URLClassLoader变成了BuiltinClassLoader,BuiltinClassLoader实现了从模块中加载字节码文件。平台类加载器的存在更多的是为了与老版本的设计方案兼容,自身没有特殊的逻辑。
类加载器(ClassLoader)负责在类加载过程中的字节码获取并加载到内存这一部分。通过加载字节码数据放入内存转换成byte[],接下来调用虚拟机底层方法将byte[]转换成方法区和堆中的数据
1.启动类加载器(Bootstrap ClassLoader)加载核心类
2.扩展类加载器(Extension ClassLoader)加载扩展类
3.应用程序类加载器(Application ClassLoader)加载应用classpath中的类
4.自定义类加载器,重写findClass方法。JDK9及之后扩展类加载器(Extension ClassLoader)变成了平台类加载器(PlatformClassLoader)
每个Java实现的类加载器中保存了一个成员变量叫“父”(Parent)类加载器。自底向上查找是否加载过,再由顶向下进行加载。避免了核心类被应用程序重写并覆盖的问题,提升了安全性。
1、重写loadClass方法,不再实现双亲委派机制。
2、JNDI、JDBC、JCE、JAXB和JBI等框架使用了SPI机制+线程上下文类加载器。
3、OSGi实现了一整套类加载机制,允许同级类加载器之间互相调用。