|
60 | 60 |
|
61 | 61 | 加载的类在JVM中创建相应的类结构,类结构会存储在方法区(JDK1.8之前:永久代;JDK1.8及之后:元空间)。 |
62 | 62 |
|
| 63 | +### Class实例的位置在哪 |
| 64 | + |
| 65 | +**Class实例的位置** |
| 66 | + |
| 67 | +类将.class文件加载至元空间后,会在堆中创建一个Java.lang.Class对象,用来封装类位于方法区内的数据结构,该Class对象是在加载类的过程中创建的,每个类都对应有一个Class类型的对象。(instanceKlass --> mirror :Class的实例) |
| 68 | + |
| 69 | +图示 |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | +外部可以通过访问代表Order类的Class对象来获取Order的类数据结构。 |
| 74 | + |
| 75 | +**再说明** |
| 76 | + |
| 77 | +Class类的构造方法是私有的,只有JVM能够创建。 |
| 78 | + |
| 79 | + java.lang.Class实例是访问类型元数据的接口,也是实现反射的关键数据、入口。通过Class类提供的接口,可以获得目标类所关联的.class文件中具体的数据结构:方法、字段等信息。 |
| 80 | + |
| 81 | + ## 过程二:Linking(链接)阶段 |
| 82 | + |
| 83 | +### 环节1:链接阶段之Verification(验证) |
| 84 | + |
| 85 | +当类加载到系统后,就开始链接操作,验证是链接操作的第一步。 |
| 86 | + |
| 87 | +<font color = 'red'>它的目的是保证加载的字节码是合法、合理并符合规范的。</font> |
| 88 | + |
| 89 | +验证的步骤比较复杂,实际要验证的项目也很繁多,大体上Java虚拟机需要做以下检査,如图所示。 |
| 90 | + |
| 91 | +.jpeg) |
| 92 | + |
| 93 | +**整体说明:** |
| 94 | + |
| 95 | +验证的内容则涵盖了类数据信息的格式验证、语义检查、字节码验证,以及符号引用验证等。 |
| 96 | + |
| 97 | +- 其<font color = 'red'>中格式验证会和装载阶段一起执行。</font>验证通过之后,类加载器才会成功将类的二进制数据信息加载到方法区中。 |
| 98 | +- <font color = 'red'>格式验证之外的验证操作将会在方法区中进行。</font> |
| 99 | + |
| 100 | +具体说明: |
| 101 | +1. 格式验证:是否以魔数 OxCAFEBABE开头,主版本和副版本号是否在当前Java虚拟机的支持范围内,数据中每一个项是否都拥有正确的长度等。 |
| 102 | + |
| 103 | +2. Java虚拟机会进行字节码的语义检查,但凡在语义上不符合规范的,虚拟机也不会给予验证通过。比如: |
| 104 | + |
| 105 | + - 是否所有的类都有父类的存在(在Java里,除了Object外,其他类都应该有父类) |
| 106 | + - 是否一些被定义为final的方法或者类被重写或继承了 |
| 107 | + - 非抽象类是否实现了所有抽象方法或者接口方法 |
| 108 | + - 是否存在不兼容的方法(比如方法的签名除了返回值不同,其他都一样,这种方法会让虚拟机无从下手调度;abstract情况下的方法,就不能是final的了) |
| 109 | + |
| 110 | +3. Java虚拟机还会进行字节码验证,字节码验证也是验证过程中最为复杂的一个过程。它试图通过对字节码流的分析,判断字节码是否可以被正确地执行。比如: |
| 111 | + |
| 112 | + - 在字节码的执行过程中,是否会跳转到一条不存在的指令 |
| 113 | + - 函数的调用是否传递了正确类型的参数 |
| 114 | + - 变量的赋值是不是给了正确的数据类型等 |
| 115 | + |
| 116 | + 栈映射帧(StackMapTable)就是在这个阶段,用于检测在特定的字节码处,其局部变量表和操作数栈是否有着正确的数据类型。但遗憾的是,100%准确地判断一段字节码是否可以被安全执行是无法实现的,因此,该过程只是尽可能地检査出可以预知的明显的问题。<font color = 'red'>如果在这个阶段无法通过检查,虚拟机也不会正确装载这个类。但是,如果通过了这个阶段的检查,也不能说明这个类是完全没有问题的。</font> |
| 117 | + |
| 118 | + 在前面3次检查中,已经排除了文件格式错误、语义错误以及字节码的不正确性。但是依然不能确保类是没有问题的。 |
| 119 | + |
| 120 | +4. 校验器还将进行符号引用的验证。Class文件在其常量池会通过字符串记录自己将要使用的其他类或者方法。因此,在验证阶段,虚拟机就会检查这些类或者方法确实是存在的,并且当前类有权限访问这些数据,如果一个需要使用类无法在系统中找到,则会抛出NoClassDefFoundError,如果一个方法无法被找到,则会抛出NoSuchMethodError。 |
| 121 | + |
| 122 | + <font color = 'red'>此阶段在解析环节才会执行。</font> |
| 123 | + |
| 124 | +### 环节2:链接阶段之Preparation(准备) |
| 125 | + |
| 126 | +<font color = 'red'>简言之,为类的静态变量分配内存,并将其初始化为</font><font color = 'orange'>默认值。</font> |
| 127 | + |
| 128 | +在这个阶段,虚拟机就会为这个类分配相应的内存空间,并设置默认初始值。Java虚拟机为各类型变量默认的初始值如表所示。 |
| 129 | + |
| 130 | + |
| 131 | + |
| 132 | +注意:Java并不支持boolean类型,对于boolean类型,内部实现是int,由于int的默认值是0,故对应的,boolean的默认值就是false。 |
| 133 | + |
| 134 | +注意: |
| 135 | +1. <font color = 'red'>这里不包含基本数据类型的字段用static final修饰的情况,因为final在编译的时候就会分配了,准备阶段会显式赋值。</font> |
| 136 | +2. 注意这里不会为实例变量分配初始化,实例变量是会随着对象一起分配到Java堆中。 |
| 137 | +3. <font color = 'red'>在这个阶段并不会像初始化阶段中那样会有初始化或者代码被执行。</font> |
| 138 | + |
| 139 | +### 环节3:链接阶段之Resolution(解析) |
| 140 | + |
| 141 | +<font color = 'red'>简言之,将类、接口、字段和方法的符号引用转为直接引用。</font> |
| 142 | + |
| 143 | +所谓解析就是将符号引用转为直接引用,也就是得到类、字段、方法在内存中的指针或者偏移量。因此,可以说,如果直接引用存在,那么可以肯定系统中存在该类、方法或者字段。但只存在符号引用,不能确定系统中一定存在该结构。 |
| 144 | + |
| 145 | + |
| 146 | + |
| 147 | + |
| 148 | + |
| 149 | + |
| 150 | + |
63 | 151 |
|
| 152 | + |
| 153 | + |
| 154 | + |
| 155 | + |
| 156 | + |
| 157 | + |
| 158 | + |
| 159 | + |
| 160 | + |
| 161 | + |
| 162 | + |
| 163 | + |
| 164 | + |
0 commit comments