zoukankan      html  css  js  c++  java
  • Java类编译、加载、和执行机制

    Java类编译、加载、和执行机制

    标签: java 类加载 类编译 类执行 机制


    0.前言

    个人认为,对于JVM的理解,主要是两大方面内容:

    • Java类的编译、加载和执行。
    • JVM的内存管理和垃圾回收机制。

    本文主要是以概要的形式学习第一点:类的编译、加载和执行。关于内存管理机制请看另外一篇文章JVM的内存管理和垃圾回收机制


    1. Java类的编译过程

    这是由*.java源码文件转为 .class二进制字节码文件的过程。
    我们编写好的源代码,就是
    .java文件。使用“javac test.java”就可以编译test.java文件。

    编译过程主要有三步:

    • 词法分析和输入到符号表
    • 注解处理
    • 语义分析和生成字节码

    详细过程:

    源代码文件*.java -> 词法分析器 -> tokens流 -> 语法分析器 -> 语法树/抽象语法树 -> 语义分析器 -> 注解抽象语法树 -> 字节码生成器 -> JVM字节码文件*.class 
    

    最后剩成的JVM字节码文件,使用命令“javap -c test”可以查看test.class的字节码信息,主要包含三项内容:

    • 结构信息:class文件相关信息。

    • 元数据:Java源码中的声明和常量信息。

    • 方法信息:Java源码语句和表达式对应的字节码。


    2. 类加载机制

    2.1 类加载器分类

    类加载器其实也是Java类。有四大类:

    • 根加载器Bootstrap Class Loader

    • 扩展加载器Extension Class Loader

    • 系统应用加载器APP Class Loader

    • 用户自定义加载器Customer Class Loader

    下面是各个类加载器的具体情况:

    类加载器分类

    Java程序在执行前先要检查类是否已经被加载。

    2.2检查过程

    检查类是否已经被加载,从底层往上层依次检查各个加载器已经加载的类,顺序是系统应用类加载器、扩展加载器、根加载器,一旦发现被某个加载器加载过,则马上使用该类。如果一直找到最顶层的根加载器,发现类还没有被加载进JVM运行数据区的方法区,则接下来就要加载该类。

    2.3类加载过程

    加载过程和检查过程顺序相反,从上层往下层的顺序进行加载。从加载器检查自己的加载路径,找要加载的类,一旦找到类就进行加载。

    注意:对每个加载器,最多只能加载一次系统绝对路径下的同一个类。对类而言,可以被不同加载器重复加载,只要你把类放到类加载器的加载路径下,就可以被那个加载器加载。


    3. 类执行机制

    类在被加载之后,接下来进行连接、初始化,然后才是使用,最后卸载。

    3.1 连接

    连接(linking)包括三个部分:

    • 验证verifying:验证类符合Java规范和JVM规范,和编译阶段的语法语义分析不同。

    • 准备preparing:为类的静态变量分配内存,初始化为系统的初始值。(不初始化静态代码块)。对于final static修饰的变量,直接赋值为用户的定义值。

    • 解析resolving:将符号引用(字面量描述)转为直接引用(对象和实例的地址指针、实例变量和方法的偏移量)

    3.2 类初始化

    初始化类的静态变量和静态代码块为用户自定义的值。非静态类在实例化类,在Java堆中创建对象的时候,才会进行初始化。初始化的顺序,和Java源码的从上到下顺序一致。注意:什么时候触发初始化?在类被Java程序“第一次主动使用”的时候,才会触发初始化操作(如果还没有加载,则会顺势触发类的加载过程)。

    类初始化

    3.3内存分配

    启动JVM后,操作系统就给JVM分配了内存空间,JVM自己由把得到的内存分为几块:
    JVM内存分配情况

    JVM是基于栈结构的体系结构来执行class字节码的,不同于windows和Linux基于寄存器结构。类的执行机制,主要是在Java栈上面完成。当一个线程被创建后,Java栈和PC寄存器就会被创建。Java栈由栈帧组成,调用一个方法,就会生成一个栈帧(可以理解为表示调用一个方法)。栈帧又由局部变量表、操作数栈和常量池引用组成。
    栈帧结构

    执行的时候,每个线程都有一个Java栈,当前执行的栈称为当前栈。一个Java栈调用多个方法,则会push很多个栈帧,当前活动的栈帧称为当前栈帧。当前栈帧执行完毕之后,会把执行结果(如果有)压入到调用它的那个栈帧的操作数栈中,作为上一个栈帧的一个中间处理结果被调用,然后就会被pop出去。当所有调用的方法执行结束后,栈帧也就都pop掉没有了。
    例如:执行代码
    操作数栈执行过程演示

    3.4 类具体的执行过程

    本步骤由执行引擎Execute Engine来完成。执行引擎把字节码转为机器码,然后操作系统才可以真正调用,在硬件环境上执行代码。执行引擎的通过Java字节码解释器(一行一行解释字节码)和JIT(Just In Time)即时编译器(对热代码整段编译)来完成机器码的翻译工作。

    JIT编译器的工作流程为:

    JVM字节码 -> 机器无关优化 -> 中间代码 -> 机器相关优化 -> 中间代码 -> 寄存器分配器 -> 中间代码 -> 目标机器码生成器 -> 目标机器码
    
  • 相关阅读:
    Java实现 LeetCode 413 等差数列划分
    Java实现 LeetCode 413 等差数列划分
    Java实现 LeetCode 412 Fizz Buzz
    简单字符设备驱动程序
    fork与vfork的区别
    进程创建函数fork()、vfork() ,以及excel()函数
    区分execl与system——应用程序中执行命令
    CMOS Sensor的调试经验分享
    嵌入式ARM-Linux开发工具下载地址合集
    嵌入式 linux 查看内存
  • 原文地址:https://www.cnblogs.com/fefjay/p/6305499.html
Copyright © 2011-2022 走看看