说说学习一门语言的“会”的三个层次,这里我说一下我理解的“会”的三个层次:第一层次:理解这门语言我称之为你好世界的语法和写作水平;第二层:了解这门语言的优缺点和它的生态,了解这门语言的能力,我称之为应用层;第三层次:理解这门语言的底层运行机制,有利于程序调优,当程序遇到比较少见的问题时,能够从根源上分析解决。我称之为精通水平。Java类加载机制首先,我们需要思考一件事,我们写的Java代码是如何在各种操作系统上运行的。Java文件被Javac编译成类文件,这种中间代码称为字节码。然后字节码由JVM加载。这个过程称为类加载。在运行时,解释器将字节码解释成一行机器码来执行。在程序运行过程中,即时编译器会将部分字节码编译成机器码供热代码使用,以获得更高的执行效率。在整个运行期间,解释器和即时编译器相互配合,使程序几乎可以达到与编译语言相同的执行速度。这部分留给专业的编译器开发者,本文不做深入讲解。上图到此结束,不要问我右上角的两个表情是怎么回事。我刚刚发现你可以在编辑时添加表情符号。如果觉得好玩,不妨试试。在详细解释类的生命周期之前,我们先明确一下类加载过程的目的。从高处看,就是加载一个javac编译的文件,生成某种形式的class文件的数据结构到内存中。程序可以调用这个数据结构来构造Java对象。这个过程是在运行时进行的,也是Java动态可伸缩性的基础。上图展示了一个类的整个生命周期。至于类加载,只包括加载、链接和初始化三个阶段。加载只是类加载的第一个环节,两者要有所区别。解析部分比较灵活,可以在初始化链接之前或者之后完成,实现后期绑定。类加载的其他阶段的顺序是不可变的。加载加载是读取一个类文件,将其转化为静态数据结构存放在方法区,并在堆中生成一个方便用户调用的Java对象的过程。这里值得注意的是,这个Java文件不一定是本地文件,一般是指来自各种来源的二进制流,比如网络、数据库,或者动态代理技术等即时生成的类文件。验证验证步骤较多,上图并不完全准确。文件格式的验证实际上发生在加载阶段。通过加载成功。加载成功并不代表JVM完全认可这个类,还需要通过语法和语义分析来保证这个类不会对JVM造成伤害,这是对元数据和字节码的验证。在解析阶段,还会验证符号引用。随着JVM版本的增加,验证过程也在不断丰富。PreparationPreparation就是给静态变量赋初值。注意这里的初始值是JVM默认的初始值,是固定的,不是我们写代码时的初始值。这里有一个比较容易混淆的概念。Java内存规范定义了方法区的抽象概念。HotSpot等主流JVM实现在JDK8之前采用永久代方式在堆中开辟特殊空间,JDK8之后采用元空间作为直接内存替换。HotSpot的实现:类的元信息、常量池、静态变量等在JDK8之前都存在于永久代方法区的具体实现中。JDK8以后,常量池和静态变量从方法区移出,转移到堆中。元信息仍然保留在方法区,具体的存储方式改为元空间。解析解析是用直接引用代替符号引用。静态解析符号引用是指如果类A引用了类B,则加载阶段为静态解析。此时B还没有放入JVM内存中。此时A只引用代表B的符号,是符号引用。直接引用是指类A在解析阶段发现自己引用了B,如果此时B还没有加载的话。就是直接触发B的类加载,然后将B的符号引用替换为实际地址。这称为直接引用。动态解析本文的类生命周期部分介绍了后期绑定的概念。后期绑定其实就是动态解析。如果代码使用了多态性。B是一个抽象类或接口,所以A无法知道用哪一个来替换它,只能等到真正的传输发生时再替换实际地址。这就是为什么一些解析发生在初始化之后。总结一下类加载的过程,今天就讲到这里。让我们回顾一下类加载的五个阶段。从JVM的角度来看,加载读取二进制流和初始化阶段向用户开放了主导权。用户可以在这个阶段使用动态代理等方式选择是否加载。也可以使用多态的手段来选择这个阶段是否初始化。其余的都在JVM内部完成。此时,你可以闭上眼睛回顾一下classloading的五个阶段,不死记硬背能不能看懂。文章来自编程生活
