zoukankan      html  css  js  c++  java
  • JVM-类加载原理

    写在前面

      我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了。那么字节码文件是怎样装载到JVM中的呢?中间经过了哪些步骤?常说的双亲委派模式又是怎么回事?本文主要搞清楚这些问题。

    类装载流程

    1、加载

      加载是类装载的第一步,首先通过class文件的路径读取到二进制流,解析二进制流将里面数据结构(类型、常量等)载入到方法区,在java堆中生成对应的java.lang.Class对象用类封装类在方法区中的数据结构。

    2.1、验证

      验证的主要目的就是判断class文件的合法性,比如class文件一定是以0xCAFEBABE开头的,另外对版本号也会做验证,例如如果使用java1.8编译后的class文件要再java1.6虚拟机上运行,因为版本问题就会验证不通过。除此之外还会对元数据、字节码进行验证,机构验证,语义验证,字节码验证。

    2.2、准备

      准备过程就是分配内存,给类的一些字段设置初始值,例如:public static int v=1;

        这段代码在准备阶段v的值就会被初始化为0,只有到后面类初始化阶段时才会被设置为1。

      但是对于static final(常量),在准备阶段就会被设置成指定的值,例如:public static final  int v=1;

        这段代码在准备阶段v的值就是1。

      对于int类型的静态变量分配4个字节的内存空间,并且默认值为0。long类型的静态变量分配8个字节的内存空间,默认值为0。布尔(false)

    2.3、解析

      解析过程就是将符号引用替换为直接引用,例如某个类继承java.lang.object,原来的符号引用记录的是“java.lang.object”这个符号,凭借这个符号并不能找到java.lang.object这个对象在哪里?而直接引用就是要找到java.lang.object所在的内存地址,建立直接引用关系,这样就方便查询到具体对象。或者A类中调用了B类对象的fun()方法,那么b.fun()就是符号引用,会转换为B类fun()的具体地址。

    3、初始化

      初始化过程,主要包括执行类构造方法、static变量赋值语句,staic{}语句块,需要注意的是如果一个子类进行初始化,那么它会事先初始化其父类,保证父类在子类之前被初始化。所以其实在java中初始化一个类,那么必然是先初始化java.lang.Object,因为所有的java类都继承自java.lang.Object。

    触发类初始化的场景

      1.创建类的实例。

      2:访问类或者接口的静态变量,或者给静态变量赋值。

      3.调用类的静态方法。(只有当出现访问的静态变量或者静态方法确实在当前类或者接口中定义时,才可以认为是对类或者接口的主动使用)

      4.反射(如 Class.forName("com.a.b.c.Test"))

      5.初始化一个类的子类。

      6.Java虚拟机启动时被标记为启动类的类

    系统中的ClassLoader

      BootStrap Classloader (启动ClassLoader) 只加载 jre/lib/下面的类

      Extension ClassLoader (扩展ClassLoader)只加载 jre/lib/ext/下面的类

      App ClassLoader(应用 ClassLoader) 加载环境变量Path

      Custom ClassLoader(自定义ClassLoader)

      每个ClassLoader都有另外一个ClassLoader作为父ClassLoader,BootStrap Classloader除外,它没有父Classloader。ClassLoader加载机制如下:

     

    类的加载

       类的加载并不需要等到某个类被“首次主动使用”时再加载它。

      JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果预先加载过程中遇到了.class文件缺失或者存在错误,类加载器必须在程序主动使用该类时报告错误(LinkageError错误),如果这个类一直没有被程序使用,那么类加载器就一直不会报告这个错误。

      调用ClassLoader类的loadClass方法加载一个类,并不是对一个类的主动使用,并不会导致类的初始化(仅仅是类的加载)。

     静态常量

      编译时静态常量 static final a = 6/3; //不会触发类的初始化

      允许时静态常量 static final a = Math.random(100); // 会触发类的初始化

  • 相关阅读:
    归一化与标准化的概念与区别
    深度学习中的优化器学习
    yolo3与yolo-tiny
    给tensorflow1.x estimator 添加timeline分析性能
    python 爬取百度图片
    TensorFlow 高性能数据输入管道设计指南
    TensorRT加速tensorflow模型(使用Tensorflow内置tensorRT,避免写自定义Plugin)
    21.Pod的limit和request和资源监控收集服务Heapster
    20.调度器,预选策略和优选策略
    8.docker的安全性
  • 原文地址:https://www.cnblogs.com/chihirotan/p/11516276.html
Copyright © 2011-2022 走看看