Launcher: 启动器,发射器
Launcher
类下的getLauncher()
,返回Launcher
对象,这个对象是个单例
–》launcher.getClassLoader()
获取对应的classLoader
–》classLoader.loadClass("类全限定类名")
加载要运行的类
–》 加载完后jvm会执行类中的main方法
–》 程序运行结束,jvm销毁
类加载过程:
- 加载 :在硬盘上查找并通过IO读取字节码文件,在加载阶段会在内存中生成一个代表这个类的
java.lang.Class对象
,作为方法区这个类各种数据的访问入口(字节码加载到jvm内存中) - 验证:校验字节码文件格式是否正确,是否符合jvm规范
- 准备:将静态变量按照jvm规定赋予默认的初始值,整形:0,布尔:false
- 解析:将符号引用变成直接引用
符号引用: 就是每一个符号
直接引用:相当于在jvm中的内存地址
静态链接:把一些静态方法(符号引用)
替换成指向数据所存内存的指针或句柄(直接引用)
,在类加载期间完成
(静态方法,main方法,为了提高效率,因为这些方法在加载之后就不会变了)
动态链接:程序在运行期间
将符号引用替换成直接引用(运行期间多态,相同的符号引用可能动态到不同的内存地址) - 初始化: 对类的静态变量初始化为指定的值,并执行静态代码块
other
类被加载到方法区中后主要包含: 运行时常量池
,,类型信息
,,字段信息
,,方法信息
,类加载器实例的引用
,,对应class实例的引用
Launcher
jar包中的类不是一次性全部加载的,是使用到时才加载
Launcher.getLauncher()
返回的 Launcher对象 ,,在构造的时候会 创建 ExtClassLoader
和 AppClassLoader
,,并且将ExtClassLoader
实例传入AppClassLoader
中,并设置parent
属性为ExtClassLoader
实例。。
Launcher默认
返回的类加载器是AppClassLoader
双亲委派机制:
ClassLoader#loadClass()
: 实现了双亲委派机制
- 引导类加载器:加载jre的lib目录下核心类库
- 扩展类加载器:加载jre的lib目录下ext扩展目录
- 应用类加载器: 加载自己写的那些类
- 自定义加载器: 加载自己定义的路径下的那些类
应用类加载器加载路径
:包括了引导类加载器和扩展类加载器的路径
默认先使用应用类加载器加载,,加载类调用
loadClass()
,最终调用的是父类ClassLoader
的loadClass()
双亲委派解释:
这个
loadClass()
会先检查是否加载过这个类,如果加载过了,就不用加载,直接返回
如果没有加载过,委托给父加载器加载
如果父加载器和bootstrap加载器都没有找到指定的类,那么调用当前类的findClass()
来完成类加载
问题:
-
为什么不直接从bootstrapClassLoader 到 AppClassLoader ,,而要双亲委派:
95%的代码都是 AppClassLoader,如果一个类会被使用很多次,如果是从BootstrapClassLoader --> AppClassLoader ,每次加载都会从上到下委托,,如果是双亲委派,他会先通过findLoadedClass()
查找是否加载过当前类,如果加载过了,直接使用 -
为什么要设计双亲委派
- 沙箱安全机制: 确保核心api库被随意更改
- 避免类的重复加载: 当父亲已经加载了该类,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性
全盘负责委托机制
当一个ClassLoader装载一个类时,除非显示的使用另外一个ClassLoader,该类所依赖及引用的类也由这个ClassLoader载入
自定义类加载器
ClassLoader类 有两个核心方法:
- loadClass() : 实现了双亲委派
- findClass() : 空方法,,子类实现,加载自己的类
- 继承
ClassLoader
类, - 重写
loadClass()
打破双亲委派机制, - 重写
findClass()
,如果父类加载器找不到最终会调用自己的 findClass()
public class MyClassLoaderTest {
static class MyClassLoader extends ClassLoader{
/**
* 要加载的路径
*/
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
/**
* 读取类文件字节
* @param name 类的名字
* @return
* @throws Exception
*/
private byte[] loadByte(String name) throws Exception{
name = name.replaceAll("\\.", "/");
FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
return data;
}
/**
* 双亲委派会委托给父类加载器加载,如果父类没有,最终会调用自己的 findClass()
* @param name
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
return defineClass(name,data,0,data.length);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 重写 loadClass() 打破双亲委派机制
* @param name
* @param resolve
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)){
// 是否加载过这个类 ,,加载过直接使用
Class<?> c = findLoadedClass(name);
if(c == null){
if(name.startsWith("com.cj")){
// 自定义classLoader 加载
c = findClass(name);
}else{
// 调用appClassLoader去加载核心类库
c = this.getParent().loadClass(name);
}
}
if(resolve){
resolveClass(c);
}
return c;
}
}
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
MyClassLoader classLoader = new MyClassLoader("I:/test");
Class<?> claz = classLoader.loadClass("com.cj.User");
Object o = claz.newInstance();
// claz.getDeclaredMethod()
}
}
import com.cj.User;
import jdk.nashorn.internal.ir.CallNode;
import java.io.FileInputStream;
import java.io.IOException;
public class MyClassLoader extends ClassLoader {
// classLoader加载路径
private String classPath;
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
if (name.startsWith("com.cj")){
// com.cj包 调用自己的类加载器
c = findClass(name);
}else{
// 其他使用双亲委派
c = super.loadClass(name,resolve);
}
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 加载data
byte[] data = new byte[0];
try {
String path = name.replaceAll("\\.", "/").concat(".class");
FileInputStream fis = new FileInputStream(classPath + "/" + path);
// 返回有多少字节
int len = fis.available();
data = new byte[len];
fis.read(data);
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
// defineClass() 将一个字节数组转换为Class对象
return defineClass(name,data,0,data.length);
}
public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
MyClassLoader classLoader = new MyClassLoader("D:/test");
Class<?> clazz = classLoader.loadClass("com.cj.User");
Object obj = clazz.newInstance();
System.out.println(clazz.getClassLoader().getClass().getName());
System.out.println(User.class.getClassLoader().getClass().getName());
}
}
Tomcat 也就是通过打破双亲委派机制,实现不同war包的独立运行。。每个war包都有自己的ClassLoader实例,加载自己的war中的类。。。 共用的一个ClassLoader类,只是不同的实例。。实例中传入自己的要加载的路径
jsp热加载实现: 创建一个线程去定时监听这个文件是否修改(文件都有modify time,最后更新时间),如果修改了,创建一个新的
lassLoader
去加载修改后的文件