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的常量等)以及符号引用(类和方法的全限定名、字段的名称和描述符、方法的名称和描述符);
  方法字节码中放的是各个方法的字节码(依赖操作数栈和局部变量表,由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 
 
参考资料:

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

  1. hive UDAF开发入门和运行过程详解(转)

    介绍 hive的用户自定义聚合函数(UDAF)是一个很好的功能,集成了先进的数据处理.hive有两种UDAF:简单和通用.顾名思义,简单的UDAF,写的相当简单的,但因为使用Java反射导致性能损失, ...

  2. 高性能Java解析器实现过程详解

    如果你没有指定数据或语言标准的或开源的Java解析器, 可能经常要用Java实现你自己的数据或语言解析器.或者,可能有很多解析器可选,但是要么太慢,要么太耗内存,或者没有你需要的特定功能.或者开源解析 ...

  3. 一个 java 文件的执行过程详解

    平时我们都使用 idea.eclipse 等软件来编写代码,在编写完之后直接点击运行就可以启动程序了,那么这个过程是怎么样的? 总体过程 我们编写的 java 文件在由编译器编译后会生成对应的 cla ...

  4. C++——代码运行过程详解

    #include <iostream> using namespace std; ;//初始化的全局变量:保存在数据段 char *p1;//未初始化的全局变量:保存在BSS段 int m ...

  5. Servlet运行过程详解

    比如,在浏览器地址栏输入http://ip:port/web01/hello step1,浏览器依据ip,port建立与servlet容器(容器同时也是一个简单的web服务器)之间的连接. step2 ...

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

    对于ASP.NET开发,排在前五的话题离不开请求生命周期.像什么Cache.身份认证.Role管理.Routing映射,微软到底在请求过程中干了哪些隐秘的事,现在是时候揭晓了.抛开乌云见晴天,接下来就 ...

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

    https://my.oschina.net/itblog/blog/275294 摘要: 本文通过一个例子,详细介绍Hadoop 的 MapReduce过程. 分析MapReduce执行过程 Map ...

  8. Cordova 打包 Android release app 过程详解

    Cordova 打包 Android release app 过程详解 时间 -- :: SegmentFault 原文 https://segmentfault.com/a/119000000517 ...

  9. 转载:C/C++源代码到可执行程序的过程详解

    C/C++源代码到可执行程序的过程详解 编译,编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格 ...

随机推荐

  1. CentOS安装MySQL详解 转

      引言 最近某云搞活动,买了个服务器作为平时学习和测试用,新机器啥也没有,一些常用软件的安装是免不了的,于是乎想着把安装过程都详细记录下来,一是做个备忘,二是给有需要的同学作个参考. Linux上安 ...

  2. selenium-滚动

    移动到指定的坐标(相对当前的坐标移动) driver.execute_script("window.scrollBy(0, 700)"); 移动到窗口绝对位置坐标,如下移动到纵坐标 ...

  3. TimeSpan时间间隔详解

    1 public string GetShiXian(string fadanshijian) 2 { 3 string result,chulishijian; 4 5 DateTime fdTim ...

  4. spring-boot-route(二十)Spring Task实现简单定时任务

    Spring Task是Spring 3.0自带的定时任务,可以将它看作成一个轻量级的Quartz,功能虽然没有Quartz那样强大,但是使用起来非常简单,无需增加额外的依赖,可直接上手使用. 一 如 ...

  5. git学习(六) git reset操作

    git reset 操作 git reset git reset HEAD 文件名 移除不必要的添加到暂存区的文件 git reset HEAD^ 或者 commitid 去掉上一次的提交 git r ...

  6. Java Web核心组件之Servlet的使用介绍

    Servlet是Java Servlet的简称,称为小程序或服务连接器,用Java编写的服务端程序,主要功能在于交互式地浏览和修改数据,生成动态的Web内容:Servlet运行于支持Java的应用服务 ...

  7. pyqt5安装后 pyqt-tools却无法安装解决方法!

    逛了逛国外论坛 这哥们跟我一样 我一晚上没睡 就为了这个 原来 我的py版本太高级了 我把py3.9卸载了 换上了老旧的3.76版本 成功了

  8. dd 在度娘上看到的一个大牛的《背包九讲》 (:

    P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最 ...

  9. Linux常用系统文件目录结构

    Linux常用系统文件目录结构 bin:全称binary,含义是二进制.该目录中存储的都是一些二进制文件,文件都是可以被运行的. dev:该目录主要存放的是外接设备,例如硬盘.其他的光盘等.在其中的外 ...

  10. APP后台架构20191205

    1.架构,架构与业务紧密相关,是有业务驱动的. 2.APP后台演进原则. App后台的架构是由业务规模驱动而演进的,App后台是为业务服务的,App后台的价值在于能为业务提供其所需要的功能,不应过度设 ...