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


最初,虚拟机中的字节码是由解释器()完成编译的,当虚拟机发现某个方法或代码块的运行特别频繁的时候,就会把这些代码认定为“热点代码” 。为了提高热点代码的执行效率,在运行时,即时编译器(JIT)会把这些代码编译成与本地平台相关的机器码,并进行各层次的优化,然后保存到内存中,这样可以减少解释器的中间损耗,获得更高的执行效率 。如果没有即时编译,每次运行相同的代码都会使用解释器编译 。
五、类加载器 1、类加载器子系统
在Java虚拟机中,负责查找并装载类型的那部分被称为类加载器子系统 。类加载器子系统会负责整个类加载的过程:装载、验证、准备、解析、初始化 。
1)Java 虚拟机有两种类加载器,启动类加载器和用户自定义类加载器:
2)类唯一性:
对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性 。每一个类加载器,都拥有一个独立的类名称空间,由不同的类加载器加载的类将被放在虚拟机内部的不同命名空间中 。比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等 。这就是有时候我们测试代码时发现明明是同一个Class,却报强转失败之类的错误 。
2、双亲委派模型
Java 1.8 之前采用三层类加载器、双亲委派的类加载架构 。三层类加载器包括启动类加载器、扩展类加载器、应用程序类加载器 。
1)三层类加载器
2)双亲委派模型
除了启动类加载器之外,所有的类加载器都有一个父类加载器 。应用程序类加载器的父类加载器是扩展类加载器,扩展类加载器的父类加载器是启动类加载器 。一般来说,开发人员自定义的类加载器的父类加载器一般是应用程序类加载器 。
双亲委派模型:类加载器在尝试去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,如果父类加载器没有,继续寻找父类加载器,依次类推,如果到启动类加载器都没找到才从自身查找 。这个类加载过程就是双亲委派模型 。
首先要明白,Java 虚拟机判定两个 Java 类是否相同,不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样 。只有两个类来源于同一个Class文件,并且被同一个类加载器加载,这两个类才相等 。不同类加载器加载的类之间是不兼容的 。
双亲委派模型就是为了保证 Java 核心库的类型安全的 。所有 Java 应用都至少需要引用 java.lang. 类,也就是说在运行的时候,java.lang. 这个类需要被加载到 Java 虚拟机中 。如果这个加载过程由 Java 应用自己的类加载器来完成或者自己定义了一个 java.lang. 类的话,很可能就存在多个版本的 java.lang. 类,而这些类之间是不兼容的 。通过双亲委派模型,对于 Java 核心库的类加载工作由引导类加载器来统一完成,保证了 Java 应用所使用的都是同一个版本的 Java 核心库的类,是互相兼容的 。有了双亲委派模型,就算自己定义了一个 java.lang. 类,也不会被加载 。
3)
类加载器之间的父子关系一般不是以继承的关系来实现的,通常是使用组合、委托关系来复用父加载器的代码 。中有一个属性来表示父类加载器,如果为 null,就会调用本地方法直接使用启动类加载器来加载类 。类加载器在成功加载某个类之后,会把得到的 java.lang.Class 类的实例缓存起来 。下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,而不会尝试再次加载 。