• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

JVM之类加载机制

互联网 diligentman 2个月前 (03-02) 19次浏览

JVM之类加载机制

JVM之类加载机制

加载

何时加载

《Java虚拟机规范》中没有强制约束类的加载时机,但class一般在使用的时候才会被加载,而加载之前必须初始化,《Java虚拟机规范》规定了有且只有以下六种情况必须立即对类进行初始化:

  1. 遇到new、getstatic、putstatic、invokestatic这四条字节码,能够产生这四条字节码的典型Java场景
    • 使用new关键字实例化对象(new)
    • 读取或设置一个类型的静态字段,被final修饰、已在编译器把结果放入常量池的静态字段除外(putstatic、getstatic)
    • 调用一个类型的静态方法(invokestatic)
  2. 使用java.lang.reflect包中的方法进行反射调用
  3. 当初始化子类时,先初始化父类
  4. 作为启动虚拟机,含有main()方法的类
  5. 当使用JDK 7新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句柄,并且这个方法句柄对应的类没有进行初始化,则需要先触发其初始化
  6. 当 一个接口中定义了JDK 8新加入的默认方法(被default关键字修饰的接口方法)时,如果这个接口的实例类发生了初始化,那么该接口需要在其之前被初始化

被动引用不会触发初始化

example1

/**
 * @Description 通过子类引用父类的静态字段,不会触发子类初始化
 **/
public class SuperClass {
    static {
        System.out.println("SuperClass init!");
    }
    public static int value = 123;
}

class SubClass extends SuperClass {
    static {
        System.out.println("SubClass init");
    }
}
/**
 * @Description 测试类
 **/
public class Client {
    public static void main(String[] args) {
        System.out.println(SubClass.value);
    }
}
/**
 * 运行结果:
 * SuperClass init!
 * 123
 **/

example2

/**
 * @Description 测试类
 * 通过数组定义来引用类,不会触发此类的初始化
 **/
public class Client {
    public static void main(String[] args) {
        SuperClass[] superClasses = new SuperClass[6];
    }
}

example3

/**
 * @Description 常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到
 * 定义常量的类,因此不会触发定义常量的类的初始化
 **/
public class ConstClass {
    static {
        System.out.println("ConstClass init!");
    }
    public static final String HELLO_WORLD ="hello world";
}
/**
 * @Description 测试类
 **/
public class Client {
    public static void main(String[] args) {
        System.out.println(ConstClass.HELLO_WORLD);
    }
}

加载阶段完成三件事

  1. 通过一个类的全限定名来获取定义此类的二进制字节流
  2. 将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构
  3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

获取二进制字节流

  • 从ZIP压缩包读取(JAR、WAR)
  • 从网络获取,Web Applet
  • 运行时计算生成,动态代理技术
  • 由其他文件生成,JSP
  • 数据库读取
  • 从加密文件中获取

类加载器

  • Bootstrap ClassLoader:负责加载jdk/jre/lib下,或被-Xbootclasspath参数指定的路径中
  • Extension ClassLoader:负责加载jdk/jre/lib/ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库
  • Application ClassLoader:负责加载用户类路径classPath所指定的类
  • 自定义类加载器

双亲委派

JVM之类加载机制

破坏双亲委派

Tomcat的类加载机制

如何对.class文件处理保证不被别人拿到以后反编译获取公司源码

编译时,可以采用一些小工具对字节码加密,类加载的时候,对加密的类考虑采用自定义的类加载器来解密文件即可。

验证

保证Class文件的字节流中的信息是符合《Java虚拟机规范》的

文件格式验证

验证字节流是否符合Class文件规范。基于二进制字节流进行,只有通过了字节流才被允许进入JVM内存的方法区中进行存储。

元数据验证

基于方法区的存储结构上进行,对字节码描述的信息进行语义分析。

字节码验证

基于方法区的存储结构上进行,验证过程中最复杂的一个阶段,通过数据流分析和控制流分析,确定程序语义是合法的、符合逻辑的

符号引用验证

确保解析行为能正常执行

准备

为类中定义的变量(静态变量)分配内存并设置类变量初始值,初始化阶段才会被赋值实际的值,如果是final修饰的静态变量,在准备阶段就会被设置为实际值。

解析

JVM将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄、调用点限定符这7类符号进行。

初始化

初始化阶段的重要工作就是执行类的初始化方法<clinit>,该方法是由编译器自动生成的,由类静态成员的赋值语句以及static语句块合并产生

展开阅读全文

jdkjava

© 著作权归作者所有

举报

打赏

0


0 收藏

微信
QQ
微博

分享

作者的其它热门文章

抽象工厂模式(Abstract Factory Pattern)
Redis之持久化
MySQL之基础架构
建造者模式(Builder Pattern)


程序员灯塔
转载请注明原文链接:JVM之类加载机制
喜欢 (0)