异常将上下文初始化事件发送到类的侦听器实例_深入浅出JVM性能调优——JVM内存( 八 )


常量池的项目类型:
再理解下符号引用和直接应用:
比如看这个方法,首先要调用 super.,即父类的方法,那么第三个指令就会在常量池寻找 [#16] 这个索引,然后可以从常量池找到这个方法的相关信息,再通过 [#29] 找到类信息 。
3、类索引、父类索引与接口索引
Class文件中由这三项数据来确定该类型的继承关系 。类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名 。
它们各自指向一个类型为的常量表,通过常量中的索引值可以找到定义在类型的常量中的全限定名字符串 。

异常将上下文初始化事件发送到类的侦听器实例_深入浅出JVM性能调优——JVM内存

文章插图
4、字段表
字段表用于描述接口或者类中声明的变量 。Java语言中的“字段”包括类级变量以及实例级变量,但不包括在方法内部声明的局部变量 。
描述符:
描述符标识字符含义:
比如从构造方法的描述符 可以看出,方法的参数包括对象类型 java.lang.、基本类型 int,返回值为 void 。
5、方法表
方法表与字段表类似,方发表用于描述方法的访问标志、名称索引、描述符索引、属性表集合、代码指令等
1)异常表:
如果方法表有异常捕获的话,还会有异常表 。当方法抛出异常时,就会从异常表查找能处理的异常处理器 。
2)重载多出的方法:
如果父类方法在子类中被重写,那方法表中就会包含父类方法的信息,如果重写泛型方法,还会出现编译器自动添加的桥接方法 。
因为泛型编译后的实际类型为 ,如果子类泛型不是 ,那么编译器会自动在子类中生成一个类型的桥接方法 。桥接方法的内部会先做类型转换检查,然后调用重载的方法 。因为我们在声明变量时一般是声明的超类,实际类型为子类,而超类方法的参数是类型的,因此就会调用到桥接方法,进而调用子类重载后的方法 。
而且,当我们通过反射根据方法名获取方法时,要注意泛型重载可能获取到桥接方法,此时可以通过 .() 方法判断是否是桥接方法 。
3)类构造器和实例构造器:
方法表还包括实例构造方法 和 类构造方法。就是对应的实例构造器 。是编译时将类初始化的代码搜集在一起形成的类初始化方法,如静态变量赋值、静态代码块 。
初始化阶段会调用类构造器 来初始化类,因此其一定是线程安全的,是由虚拟机来保证的 。这种机制我们可以用来实现安全的单例模式,枚举类的初始化也是在 方法中初始化的 。
6、属性表
属性表集合主要是为了正确识别Class文件而定义的一些属性,如 Code、、、、 等等 。
每一个属性,它的名称都要从常量池中引用一个类型的常量来表示 。
四、类加载 1、类初始化的时机
类和接口被加载的时机因不同的虚拟机可能不同,但类初始化的触发时机有且仅有六种情况:
这六种情况称为对一个类型进行主动引用 。除此之外,所有引用类型的方式都不会触发初始化,称为被动引用 。
触发接口初始化的情况:
1)主动初始化:
从输出可以看出,对 final 常量的引用不会触发类的初始化,调用静态方法时触发了类的初始化,同时,一定会先触发父类的初始化,而且类只会被初始化一次 。
注意初始化的顺序是按代码的顺序从上到下初始化:
2)被动初始化,如下被动引用不会触发类的初始化:
3)不难判断,例子中定义的类的加载顺序如下:
2、加载
在加载阶段,Java虚拟机必须完成以下三件事情:
这个二进制流可以从 Class 文件中获取,从JAR包、WAR包中获取,从网络中获取,实时生成、还可以从加密文件中获取,在加载时再解密(防止Class文件被反编译) 。这个加载是由类加载器加载进虚拟机的,非数组类型可以使用内置的引导类加载器来加载,也可以使用开发人员自定义的类加载器来加载,我们可以自己控制字节流的获取方式 。而数组类型本身不通过类加载器加载,它是由虚拟机直接在内存中构造出来的 。