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

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

原文地址:https://www.cnblogs.com/blogtech/p/10000246.html

时间: 2024-10-12 21:04:58

Java编译程序和运行过程详解的相关文章

OC学习小结之ios运行过程详解

1)ios核心类 UIView 视图,屏幕上能看得见的东西都是视图,例如:按钮.文本标签.和表格等 UIViewController:内部默认有个视图(UIView),负责管理UIView的生命周期.装配数据到UIView上显示.处理其事件 ios采用MVC模式:model -view -controller 2)运行过程 ios运行原理 1.先执行main函数 2.main函数调用UIApplicationMain函数 创建一个UIApplication实例,这个是一个单例对象,一个ios程序

C++ 编译,运行过程 详解。

要更深入了解C++, 必须要知道一个程序从开始到结束都干了些什么, 怎么干的. 所以我从C++编译到运行过程,解析下程序是怎么跑的. 首先,初略的说一下之前C++的编译过程,C++编译过程包括预编译->汇编->编译->链接.称为一个可执行文件.(Windows平台下为.exe文件). 预编译主要展开包含的头文件,宏定义等操作.例如一个简单的main程序,编译预编译后,的文件对比.   可以看到里面的宏已经被去掉了.如果定了那个宏,那么宏里面的内容也会显示出来.头文件也是,如果你包含了你一

Servlet运行过程详解

比如,在浏览器地址栏输入http://ip:port/web01/hello step1,浏览器依据ip,port建立与servlet容器(容器同时也是一个简单的web服务器)之间的连接. step2,浏览器将请求数据打包(按照http协议,该数据包也称为请求数据包). step3,浏览器发送请求数据包给容器. step4,容器收到请求数据包之后,解析该数据包,并将解析之后得到的数据封装到request对象上,同时,容器还要创建response对象. step5,容器要依据请求资源路径("/we

ASP.NET 运行时详解 揭开请求过程神秘面纱

对于ASP.NET开发,排在前五的话题离不开请求生命周期.像什么Cache.身份认证.Role管理.Routing映射,微软到底在请求过程中干了哪些隐秘的事,现在是时候揭晓了.抛开乌云见晴天,接下来就一步步揭开请求管道神秘面纱. 上篇回顾 在介绍本篇内容之前,让我们先回顾下上一篇<ASP.NET运行时详解 集成模式和经典模式>的主要内容.在上一篇随笔中,我们提到ASP.NET运行时通过Application的InitInternal方法初始化运行管道.ASP.NET运行时提供了两种初始化管道模

Java下static关键字用法详解

Java下static关键字用法详解 本文章介绍了java下static关键字的用法,大部分内容摘自原作者,在此学习并分享给大家. Static关键字可以修饰什么? 从以下测试可以看出, static 可以修饰: 1. 语句块 2. 成员变量(但是不能修饰局部变量) 3. 方法 4. 接口(内部接口) 5. 类(只能修饰在类中的类, 即静态内部类) 6. jdk 1.5 中新增的静态导入 那么static 修饰的表示什么呢? 当创建一个类时,就是在创建一个新类型,描述这个类的对象的外观和行为,除

Hadoop MapReduce执行过程详解(带hadoop例子)

https://my.oschina.net/itblog/blog/275294 摘要: 本文通过一个例子,详细介绍Hadoop 的 MapReduce过程. 分析MapReduce执行过程 MapReduce运行的时候,会通过Mapper运行的任务读取HDFS中的数据文件,然后调用自己的方法,处理数据,最后输出.Reducer任务会接收Mapper任务输出的数据,作为自己的输入数据,调用自己的方法,最后输出到HDFS的文件中.整个流程如图: Mapper任务的执行过程详解 每个Mapper任

Java虚拟机之垃圾回收详解一

Java虚拟机之垃圾回收详解一 Java技术和JVM(Java虚拟机) 一.Java技术概述: Java是一门编程语言,是一种计算平台,是SUN公司于1995年首次发布.它是Java程序的技术基础,这些程序包括:实用程序.游戏.商业应用程序.在全世界范围内,Java运行在超过数十亿台个人计算机上,数十亿台设备上,还包括手机和电视设备.Java由一系列的关键组件作为一个整体构建出了Java平台. Java Runtime Edition 当你下载Java,你就得到了Java运行环境(JRE).JR

【java项目实战】Servlet详解以及Servlet编写登陆页面(二)

Servlet是Sun公司提供的一门用于开发动态web网页的技术.Sun公司在API中提供了一个servlet接口,我们如果想使用java程序开发一个动态的web网页,只需要实现servelet接口,并把类部署到web服务器上就可以运行了. 到底什么是Servlet呢? 通俗一点,只要是实现了servlet接口的java程序,均称Servlet.Servlet是由sun公司命名的,Servlet = Server + Applet(Applet表示小应用程序),Servlet是在服务器端运行的小

Java移位运算符 “&lt;&lt;” 作用及详解

左移运算符(<<) 基本用法 将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0). 例:a = a << 2 将a的二进制位左移2位,右补0, 左移1位后a = a *2; 若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2. 举例以及困惑 给出下面的程序,大家可以猜一猜结果是什么? public class MainClass { public static void main(String[] args) { long i = 1L <<