java编译程序和运行过程详解

blogtech blogtech     2023-01-23     585

关键词:

java整个编译以及运行的过程相当繁琐,我就举一个简单的例子说明:
编译原理简单过程:词法分析 --> 语法分析 --> 语义分析和中间代码生成 --> 优化 --> 目标代码生成
Java程序从源文件创建到程序运行要经过两大步骤:
1、Java文件会由编译器编译成class文件(字节码文件),会经过编译原理简单过程的前三步;
2、字节码由java虚拟机解释运行,解释执行即为目标代码生成并执行。因为java程序既要编译的同时也要经过JVM的解释运行,所以说Java被称为半解释语言!
( "semi-interpreted" language)
技术分享图片
 
public class Main 
 
    public static void main(String[] args) 
        Animal animal = new Animal("Tom");
        animal.printName();
    
 

 
class Animal
    private String name;
 
    public Animal(String name) 
        super();
        this.name = name;
    
     
    public void printName()
        System.out.println("Animal = " + this.name);
    
 
第一步(编译):创建完源文件之后,程序先要被JVM中的java编译器进行编译为.class文件。java编译一个类时,若这个类所依赖的类还没有被编译,编译器会自动的先编译这个所依赖的类,然后引用;若java编译器在指定的目录下找不到该类所依赖的类的 .class文件或者 .java源文件,就会报"Can‘t found sysbol"的异常错误。
 
编译后的字节码文件格式主要分为两部分:常量池和方法字节码。
  常量池记录的是代码出现过的字面量(文本字符串、八种基本类型的值、被声明为final的常量等)以及符号引用(类和方法的全限定名、字段的名称和描述符、方法的名称和描述符);
  https://www.cnblogs.com/blogtech/p/10000205.html
  方法字节码中放的是各个方法的字节码(依赖操作数栈和局部变量表,由JVM解释执行)
 
第二步(运行):java类运行的过程大概分为两个步骤:
(1)类的加载
  加载 --> 验证 --> 准备 --> 解析 --> 初始化(其中验证、准备、解析统称为类的连接);(参考《深入了解Java虚拟机》)
  加载:通过一个类的全限定名来获取定义此类的二进制字节流(Class文件);将这个二进制字节流所代表的静态存储结果转化为方法区的运行时数据结构;在内存中生成一个java.lang.Class对象,注意:存放在方法区!
  验证:验证目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全;使用纯粹的Java代码无法做到诸如访问数组边界意外的数据、将一个对象转型为它未实现的类型、跳转到不存在的代码之类的事情,如果这样做了,编译器将拒绝编译!
  准备:准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。首先这时候进行内存分配的仅包括类变量(static修饰的变量),而不是实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。
 
public static int value = 123; 
 
  变量value在准备阶段过后的初始值为0而不是123,因为这时候尚未开始执行任何Java方法,在类初始化的时候才会将value的值赋为123.
  解析:解析阶段是虚拟机将class常量池内的符号引用替换为直接引用的过程。
     符号引用:符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可;
       直接引用:是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。有了直接引用,那引用的目标必定已经在内存中存在。
  初始化:类初始化阶段是类加载过程的最后一步;在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源:初始化阶段是执行类构造器<clinit>( )方法的过程。
  <clinit>( )方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static 块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的。 
 
(2)类的执行
  需要说明的一点的是:JVM主要在程序第一次运行时主动使用类的时候,才会立即去加载,加载完毕就会生成一个java.lang.Class对象,并且存放在方法区。换言之,JVM并不是在运行时就会把所有使用到的类都加载到内存中,而是用到,不得不加载的时候,才加载进来,而且只加载一次,初始化类构造器<clinit>()方法也只执行一次,所以static 块,类变量赋值语句也就只执行一次,只生成一个java.lang.Class对象!
  由Java虚拟机的执行引擎来解释执行Java字节码,过程:输入字节码文件,字节码解析,输出执行完的结果!(不再赘述,请自行参考《深入了解Java虚拟机》)
 
重点理解:根据上面的程序和概念解释,详解该程序运行的详细步骤
(1)在类路径下找到编译好的 java 程序中得到 Test.class 字节码文件后,在命令行上敲 java Test,系统就会启动一个 JVM 进程,JVM进程从classpath路径下找到一个名为Test.class的二进制文件,将Test.class文件中的 类信息加载到运行时数据区的方法区(JDK 8 方法区存在 堆区) 中,这一过程叫做类的加载。(只有类信息在方法区中,才能创建对象,使用类中的成员变量);
(2)JVM 找到main方法的主函数入口, 持有一个指向当前类(Test)常量池的指针,而常量池中的第一项发现是一个对Animal对象的符号引用,并且main方法中第一条指令是Animal animal = new Animal("Tom"),就是让JVM创建一个Animal对象,但是方法区中还没有Animal类的类信息,于是JVM就要马上的加载Animal类,将Animal类信息放入到方法区中,于是JVM 以一个直接指向方法区 Animal类的指针(直接引用)替换了常量池中第一项的符号引用。
(3)加载完Animal类的信息以后,JVM虚拟机就会在堆内存中为一个Animal类实例分配内存,然后调用其构造函数初始化Animal实例,这个实例持有指向方法区的Animal类的类型信息(其中包含有方法表,java动态绑定的底层实现)的引用。(animal指向了Animal对象的引用会自动的放在栈中,字符串常量"Tom"会自动的放在方法区的运行时常量池中,对象会自动的放入堆区)
(4)当使用 animal.pringName()的时候,JVM根据栈中animal引用找到Animal对象,然后根据Animal对象持有的引用定位到方法区中Animal类的类型信息方法表,获得pringName()函数的字节码地址,然后Java虚拟机执行引擎依赖局部变量表,操作数栈进行字节码解释执行,返回结果!
 
技术分享图片
大家可能对Java执行引擎,结合局部变量表和操作数栈执行字节码的理解不是很透彻,下来我简单介绍一下字节码的执行过程:
 
public int calc()
     int a = 100;
     int b = 200;
     int c = 300;
     return (a + b) * c;     
 
字节码指令展示:
  public int calc();
  Code:
  Stack=2, Locals=4, Args_size=1 //操作栈深度为2和4个Slot局部变量表
  0:bipush 100   //将100压入操作数栈
  2:istore_1   //将栈顶100数值存放到局变量Slot,index=1中
  3:sipush 200  //将200压入操作数栈
  6:istore_2      //将栈顶200数值存放到局部变量Slot,index=2中
  7:sipush 300  //将300压入操作数栈
  10:istore_3     //将栈顶200数值存放到局部变量Slot,index=3中
  11:iload_1       //将index=1的局部变量表数值压入操作数栈(100)
  12:iload_2      //将index=2的局部变量表数值压入操作数栈(200)
  13:iadd          //取栈顶两个数值相加,结果压入操作数栈(300)
  14:iload_3      //将index=3的局部变量表数值压入操作数栈(300)
  15:imul          //取栈顶两个数值相乘,结果压入操作数栈(90000)
  16:ireturn      //取栈顶数值返回调用者结果
技术分享图片
 
局部变量表 index = 0存储当前对象本身 this 
 
参考资料:
https://wenku.baidu.com/view/32208418650e52ea55189863.html
http://hxraid.iteye.com/blog/676235
http://hxraid.iteye.com/blog/428891
https://wenku.baidu.com/view/32208418650e52ea55189863.html


java多线程详解总结(代码片段)

...态对象。进程(process):是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期运行中的QQ,运行中的MP3播放器程序是静态的,进程是动态的进程作为资源分配的... 查看详情

程序由创建到得到运行结果的过程你知道吗?程序的环境和预处理爆肝总结画图详解(代码片段)

...译也分为三个阶段预处理阶段编译阶段汇编阶段链接阶段运行环境(执行环境)预处理详解预定义符号#define#define定义标识符#define定义宏#define替换规则#和##带副作用的宏参数宏和函数的对比命名约定#undef命令行定义条件编译常见的... 查看详情

java线程详解(代码片段)

...理一、进程与线程    进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,即进程空间或(虚空间)。进程不依赖于线程而独立存在,一个进程中可以启动多个线程。比如在Windows系统中... 查看详情

c/c++程序编译过程详解

...把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作系统的启动代码和用到的... 查看详情

c/c++程序编译过程详解

...把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作系统的启动代码和用到的... 查看详情

tomcat启动过程原理详解(代码片段)

...面、类和其他资源的集合,它们可以用标准方式打包,并运行在来自多个供应商的多个容器。Web应用程序存在于结构化层次结构的目录中,该层次结构是由JavaServlet规范定义的。Web应用程序的根目录包含直接存储或存储在子文件... 查看详情

java异常详解(代码片段)

...辑错误不是异常,此时无法通过编译,程序无法运行,异常是指程序运行过程中的问题。示例:publicclassTestpublicstaticvoidmain(String[]args)inta=10;intb=0;intc=a/b;System.out.println(a+"/"+b+"="+c)... 查看详情

详解java类加载机制

JAVA源码编译由三个过程组成:1、源码编译机制。2、类加载机制3、类执行机制我们这里主要介绍编译和类加载这两种机制。一、源码编译代码编译由JAVA源码编译器来完成。主要是将源码编译成字节码文件(class文件)。字节码... 查看详情

linux开启过程详解

...打开电源开关,结束于内核初始化完成和systemd进程成功运行。启动阶段接管了剩余工作,直到操作系统进入可操作状态。??总体来说,Linux的开机引导和启动过程是相当容易理解,下文将分节对于不同步骤进行详细说明。BIOS上电... 查看详情

instrumentation接口详解(代码片段)

...立于应用程序之外的代理程序,可以用来监控和扩展JVM上运行的应用程序,相当于是JVM层面的AOP功能:监控和扩展JVM上的运行程序,替换和修改java类定义,提供一套代理机制,支持独立于JVM应用程序之外的程序以代理的方式连... 查看详情

简述java程序的编辑编译和运行过程

...文件的话,编译器话报“cantfindsymbol”的错误。第二步(运行):java类运行的过程大概可分为两个过程:1、类的加载2、类的执行。需要说明的是:JVM主要在程序第一次主动使用类的时候,才会去加载该类。也就是说,JVM并不是... 查看详情

jsp执行过程详解

....Servlet在Web应用程序中的位置 图1  3.Servlet工作过程   图2 3.Servlet和JSP之间的关系(图2中最后一个蓝色的Servlet容器指的是tomcat服务器,JSP页面在tomcat服务器中的解析过程如图3所示)JSP本质上也是Servlet的... 查看详情

1.3—一个典型的java程序的编写和运行过程

JAVA语言应用范围桌面应用编程WEB客户端编程WEB服务器编程手机编程机器人编程 第一个JAVA程序JAVA开发环境搭建下载:DownloadJ2SDK(Java 2Software DevelopmentKit) from http://java.sun.com安装:runtheexecutable(跟普通软件安装... 查看详情

存储过程详解(代码片段)

...库服务器上进行了编译并存储在数据库中,所以存储过程运行要比单个的SQL语句块要快。同时由于在调用时只需用提供存储过程名和必要的参数信息,所以在一定程度上也可以减少网络流量、简单网络负担。    1... 查看详情

java反射超详解✌(代码片段)

...用法Java反射超详解✌1.反射基础Java反射机制是在程序的运行过程中,对于任何一个类,都能够知道它的所有属性和方法;对于任意一个对象,都能够知 查看详情

jvm笔记—java内存区域详解(代码片段)

...内存区域-常见面试题1.基本问题介绍下Java内存区域(运行时数据区)Java对象的创建过程(五步,必须能默写出来并且要知道每一步虚拟机做了什么)对象的访问定位的两种方式(句柄和直接指针两种方式&... 查看详情

详解java中的异常和处理

简介程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?或者用C语言风格:用函数返回值作为执行状态?。Java... 查看详情

java内存管理-程序运行过程

勿在流沙住高台,出来混迟早要还的。做一个积极的人编码、改bug、提升自己我有一个乐园,面向编程,春暖花开!相信在做Java开发的伙伴一定知道JVM(JavaVirtualMachine(Java虚拟机)!本系列会开启对JVM相关的知识的探索和总结... 查看详情