前面三步:加载 连接 初始化 将Class文件加载到虚拟机中
加载:
1、通过全类名定义获取此类的二进制字节流
2、将字节流所代表的静态存储结构转换为方法区的运行时数据结构
3、在内存中生成一个代表该类的Class对象,作为方法区这些数据的访问入口
每个java类都有一个引用指向加载它的ClassLoder
准备:
为类变量分配内存并设置类变量初始值
解析:
虚拟机将常量池的符号引用替换为直接引用的过程
什么是符号引用:
符号引用以一组符号来描述所引用的目标
什么是直接引用:
直接引用是可以直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄
初始化:
初始化阶段是执行初始化方法的过程,是类加载的最后一步
类卸载:卸载类即该类的Class对象被GC
加载java类的字节码(.class文件)到JVM中
字节码可以是.java文件经过javac编译得到 也可以通过工具动态生成、通过网络下载得来
加载规则:先判断该类是否被加载过,已经加载的类直接返回
内置加载器:
1、BootstrapClassLoader
– 启动类加载器
加载JDK内部的核心类库
2、ExtensionClassLoader
– 扩展类加载器
加载ext目录下的jar包和类
3、AppClassLoader
– 应用程序类加载器
负责加载当前应用classPath下的所有jar包和类
自定义类加载器
需要继承java.lang.ClassLoader
关键方法:
1、proteceted Class loadClass(String name,boolen resolve)
加载指定二进制名称的类,实现双亲委派机制
2、protected Class findClass(String name)
根据类的二进制名称来查找类
什么是双亲委派模型?
每个ClassLoader实例都有一个相关的父类加载器,ClassLoader实例会在试图亲自查找类或者资源之前,将搜索类或资源的任务委托给其父类加载器
ClassLoader的loadClass()方法
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
//首先,检查该类是否已经加载过
Class c = findLoadedClass(name);
if (c == null) {
//如果 c 为 null,则说明该类没有被加载过
long t0 = System.nanoTime();
try {
if (parent != null) {
//当父类的加载器不为空,则通过父类的loadClass来加载该类
c = parent.loadClass(name, false);
} else {
//当父类的加载器为空,则调用启动类加载器来加载该类
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
//非空父类的类加载器无法找到相应的类,则抛出异常
}
if (c == null) {
//当父类加载器无法加载时,则调用findClass方法来加载该类
//用户可通过覆写该方法,来自定义类加载器
long t1 = System.nanoTime();
c = findClass(name);
//用于统计类加载器相关的信息
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
//对类进行link操作
resolveClass(c);
}
return c;
}
}
双亲委派模型的好处:避免类的重复加载