类的生命周期
加载阶段
加载阶段能够细分如下
- 加载类的二进制流
- 数据结构转换,将二进制流所代表的动态存储构造转化成办法区的运行时的数据结构
- 生成 java.lang.Class 对象,作为办法区这个类的各种数据的拜访入口
加载类的二进制流的办法
- 从 zip 包中读取。咱们常见的 JAR、AAR 依赖
- 运行时动静生成。咱们常见的动静代理技术,在 java.reflect.Proxy 中就是用 ProxyGenerateProxyClass 来为特定的接口生成代理的二进制流
验证
验证是连贯阶段的第一步,这一阶段的目标是为了确保 Class 文件的字节流中蕴含的信息合乎以后虚拟机的要求,并且不会危害虚拟机本身的平安。
- 文件格式验证:如是否以魔数 0xCAFEBABE 结尾、主、次版本号是否在以后虚拟机解决范畴之内、常量合理性验证等。
此阶段保障输出的字节流能正确地解析并存储于办法区之内,格局上合乎形容一个 Java 类型信息的要求。 - 元数据验证:是否存在父类,父类的继承链是否正确,抽象类是否实现了其父类或接口之中要求实现的所有办法,字段、办法是否与父类产生矛盾等。
第二阶段,保障不存在不合乎 Java 语言标准的元数据信息。 - 字节码验证:通过数据流和控制流剖析,确定程序语义是非法的、合乎逻辑的。例如保障跳转指令不会跳转到办法体以外的字节码指令上。
- 符号援用验证:在解析阶段中产生,保障能够将符号援用转化为间接援用。
能够思考应用 -Xverify:none
参数来敞开大部分的类验证措施,以缩短虚拟机类加载的工夫。
筹备
为 类变量 分配内存并设置类变量初始值,这些变量所应用的内存都将在办法区中进行调配。
解析
虚拟机将常量池内的符号援用替换为间接援用的过程。
解析动作次要针对类或接口、字段、类办法、接口办法、办法类型、办法句柄和调用点限定符 7 类符号援用进行
初始化
到初始化阶段,才真正开始执行类中定义的 Java 程序代码,此阶段是执行 <clinit>()
办法的过程。
类加载的机会
虚拟机标准规定了有且只有 5 种状况必须立刻对类进行“初始化”(而加载、验证、筹备天然须要在此之前开始)
- 遇到 new、getstatic 和 putstatic 或 invokestatic 这 4 条字节码指令时,如果类没有进行过初始化,则须要先触发其初始化。对应场景是:应用 new 实例化对象、读取或设置一个类的动态字段(被 final 润饰、已在编译期把后果放入常量池的动态字段除外)、以及调用一个类的静态方法。
- 对类进行反射调用的时候,如果类没有进行过初始化,则须要先触发其初始化。
- 当初始化类的父类还没有进行过初始化,则须要先触发其父类的初始化。(而一个接口在初始化时,并不要求其父接口全副都实现了初始化)
- 虚拟机启动时,用户须要指定一个要执行的主类(蕴含 main() 办法的那个类),
虚构机会先初始化这个主类。 - 当应用 JDK 1.7 的动静语言反对时,如果一个 java.lang.invoke.MethodHandle 实例最初的解析后果 REF\_getStatic、REF\_putStatic、REF\_invokeStatic 的办法句柄,并且这个办法句柄所对应的类没有进行过初始化,则须要先触发其初始化。
留神:
- 通过子类援用父类的动态字段,不会导致子类初始化。
- 通过数组定义来援用类,不会触发此类的初始化。
MyClass[] cs = new MyClass[10];
- 常量在编译阶段会存入调用类的常量池中,实质上并没有间接援用到定义常量的类,因而不会触发定义常量的类的初始化。
类加载器
把实现类加载阶段中的“通过一个类的全限定名来获取形容此类的二进制字节流”这个动作的代码模块称为“类加载器”。
将 class 文件二进制数据放入办法区内,而后在堆内(heap)创立一个 java.lang.Class 对象,Class 对象封装了类在办法区内的数据结构,并且向开发者提供了拜访办法区内的数据结构的接口。
类的唯一性
对于任意一个类,都须要由加载它的类加载器和这个类自身一起确立其在 Java 虚拟机中的唯一性。
即便两个类来源于同一个 Class 文件,被同一个虚拟机加载,只有加载它们的类加载器不同,那这两个类也不相等。
这里所指的“相等”,包含代表类的 Class 对象的 equals() 办法、isAssignableFrom() 办法、isInstance() 办法的返回后果,也包含应用 instanceof 关键字做对象所属关系断定等状况
双亲委托机制
如果一个类加载器收到了类加载的申请,它首先不会本人去尝试加载这个类,而是把这个申请委派给父类加载器去实现,每一个档次的类加载器都是如此,因而所有的加载申请最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈本人无奈实现这个加载申请(它的搜寻范畴中没有找到所需的类)时,子加载器才会尝试本人去加载。
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
// 先从缓存中加没加载这个类
Class<?> c = findLoadedClass(name);
if (c == null) {
try {if (parent != null) {
// 从 parent 中加载
c = parent.loadClass(name, false);
} else {c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
// 加载不到,就本人加载
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
return c;
}
益处
- 防止反复加载,当父加载器曾经加载了该类的时候,就没有必要子 ClassLoader 再加载一次。
- 安全性思考,避免外围 API 库被随便篡改。
Android 中 ClassLoader
- ClassLoader 是一个抽象类,定义了 ClassLoader 的次要性能
- BootClassLoader 是 ClassLoader 的子类(留神不是外部类,有些资料上说是外部类,是不对的),用于加载一些零碎 Framework 层级须要的类,是 Android 平台上所有的 ClassLoader 的最终 parent
- SecureClassLoader 扩大了 ClassLoader 类,退出了权限方面的性能,增强了安全性
- URLClassLoader 继承 SecureClassLoader,用来通过 URI 门路从 jar 文件和文件夹中加载类和资源,在 Android 中根本无奈应用
- BaseDexClassLoader 是实现了 Android ClassLoader 的大部分性能
- PathClassLoader 加载应用程序的类,会加载 /data/app 目录下的 dex 文件以及蕴含 dex 的 apk 文件或者 java 文件(有些资料上说他也会加载零碎类,我没有找到,这里存疑)
- DexClassLoader 能够加载自定义 dex 文件以及蕴含 dex 的 apk 文件或 jar 文件,反对从 SD 卡进行加载。咱们应用插件化技术的时候会用到
- InMemoryDexClassLoader 用于加载内存中的 dex 文件
ClassLoader 的加载流程源码剖析
-> ClassLoader.java 类
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized(this.getClassLoadingLock(name)) {
// 先查找 class 是否曾经加载过,如果加载过间接返回
Class<?> c = this.findLoadedClass(name);
if (c == null) {long t0 = System.nanoTime();
try {if (this.parent != null) {
// 委托给 parent 加载器进行加载 ClassLoader parent;
c = this.parent.loadClass(name, false);
} else {
// 当执行到顶层的类加载器时,parent = null
c = this.findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException var10) { }
if (c == null) {long t1 = System.nanoTime();
c = this.findClass(name);
PerfCounter.getParentDelegationTime().addTime(t1 - t0);
PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
// 如果 parent 加载器中没有找到,PerfCounter.getFindClasses().increment();
}
}
if (resolve) {this.resolveClass(c);
}
return c;
}
}
由子类实现
protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);
}
BaseDexClassLoader 类中 findClass 办法
protected Class<?> findClass(String name) throws ClassNotFoundException {List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
// pathList 是 DexPathList,是具体寄存代码的中央。Class c = pathList.findClass(name, suppressedExceptions);
if (c == null) {
ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class ""+ name +"" on path: " + pathList);
for (Throwable t : suppressedExceptions) {cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
public Class<?> findClass(String name, List<Throwable> suppressed) {for (Element element : dexElements) {Class<?> clazz = element.findClass(name, definingContext, suppressed);
if (clazz != null) {return clazz;}
}
if (dexElementsSuppressedExceptions != null) {suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
public Class<?> findClass(String name, ClassLoader definingContext,
List<Throwable> suppressed) {return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed) : null;
}
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {return defineClass(name, loader, mCookie, this, suppressed);
}
private static Class defineClass(String name, ClassLoader loader, Object cookie,
DexFile dexFile, List<Throwable> suppressed) {
Class result = null;
try {result = defineClassNative(name, loader, cookie, dexFile);
} catch (NoClassDefFoundError e) {if (suppressed != null) {suppressed.add(e);
}
} catch (ClassNotFoundException e) {if (suppressed != null) {suppressed.add(e);
}
}
return result;
}
// 调用 Native 层代码
private static native Class defineClassNative(String name, ClassLoader loader, Object cookie, DexFile dexFile)
本文转自 https://juejin.cn/post/7038476576366788621,如有侵权,请分割删除。