类的生命周期
加载阶段
加载阶段能够细分如下
- 加载类的二进制流
- 数据结构转换,将二进制流所代表的动态存储构造转化成办法区的运行时的数据结构
- 生成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,如有侵权,请分割删除。
发表回复