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. form单提交后按钮变成失效变灰

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  2. Markdown基础知识

    一 Markdown简介 Markdown是⼀种可以使⽤普通⽂本编辑器编写的标记语⾔,通过简单的标记语法,它可以使普通⽂本内容具有⼀定的格式,可以简单理解为纯⽂本格式的word. 软件⼀般⽤vscod ...

  3. 编写C语言的两种方法----Visual Studio/CodeBlocks

    1.CodeBlock(安装简单) 参考这个博客的:https://blog.csdn.net/jjjjkkjkk/article/details/80331625?utm_medium=distri ...

  4. 在Linux上成功启动Jenkins却无法访问的问题

    本鸟最近打算学习Jenkins,正准备在Linux上面鼓捣一番,,却没想被入门级别问题当头一棒 下载完jenkins.war,使用java -jar命令在8088端口开启服务:java -jar je ...

  5. App在后台运行时如何保存数据到sqlite数据库

    iOS程序进入后台后,是不允许读写任何文件和数据库(sqlite),但是允许读写NSUserDefault中的数据. 因此在后台时如果想存储数据,则可使用NSUserDefault(偏好设置)临时保存 ...

  6. jumpserver部署使用

    一.简介 前面我们聊到了openvpn的部署和使用,它能够实现从互联网通过openvpn连接到公司内网服务器,从而进行远程管理:但openvpn有一个缺点它不能记录哪些用户在内网服务器上操作了什么,拥 ...

  7. 使用微创联合M5S空气检测仪、树莓派3b+、prometheus、grafana实现空气质量持续监控告警WEB可视化

    1.简介 使用微创联合M5S空气检测仪.树莓派3b+.prometheus.grafana实现空气质量持续监控告警WEB可视化 grafana dashboard效果: 2.背景 2.1 需求: 1. ...

  8. MySql基础(常用)

    MySQL常用语句 1.查看当前所有数据库 show databases; 2.打开指定的库 use 库名; 3.查看当前库中的所有表 show tables; 4.查看其他库的表 show tabl ...

  9. glog修改

    在写代码的过程中,打log肯定是少不了的,毕竟不能总靠调试来发现问题.log库的选用就很纠结了,成熟的log库非常多,log4cpp.log4cxx.poco.log.boost.log.glog等等 ...

  10. Flink的DataSource三部曲之二:内置connector

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...