联系方式

咨询热线:400-998-6158

点击此处免费预约试听课程»

常见问题
学习资讯
学习资讯

java程序的诞生与死亡

java程序一生简史一:编译

Java语言的编译期其实是一段“不确定 ”的过程,因为可能是一个前端编译器把.java文件转变为.class文件的过程;也可能是指JVM的后端运行期编译器(JIT编译器)把字节码转变为机器码的过程;还可能是指使用静态提前编译器(AOT编译器)直接把.java文件编译成本地机器码的过程。

java程序一生简史二:词法、语法分析

词法分析是将源代码的字符流转变为Token集合,而语法分析则是根据Token序列抽象构造语法树(AST)的过程,AST是一种用来描述程序代码语法结构的树形表示形式,语法树的每个节点都代表着程序代码中的一个语法结构,例如包、类型、修饰符、运算符、接口、返回值甚至代码注释都可以是一个语法结构。

java程序一生简史三:填充符号表

完成了语法和词法分析之后,下一步就是填充符号表的过程,符号表中所登记的信息在编译的不同阶段都要用到。在这里延伸一下符号表的概念。符号是由一组符号地址和符号信息构成的表格,较简单的可以理解为哈希表的K-V值对的形式。符号表较早期的应用之*就是组织程序代码的信息。较初,计算机程序只是一串简单的数字,但程序猿们很快发现使用符号来表示操作和内存地址(变量名)要方便得多。将名称和数字关联起来就需要一张符号表。

java程序一生简史周期四:语义分析

经过上两步之后,我们获得了程序代码的抽象语法树表示,语法树能表示一个正确的源代码抽象,但无法增加源程序是符合逻辑的,这时候语义分析登场了,它的主要任务就是对结构上正确的源程序进行上下文有关性质的审查。标注检查、数据及控制流分析、解语法糖是语义分析阶段的几个步骤,在这具体说下语法糖的概念。语法糖是指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但更方便程序猿使用。Java中较常用的语法糖主要是泛型、变长参数、自从装箱/拆箱、遍历循环,JVM在运行时不支持这些语法,它们在编译阶段还原回简单的基础语法结构,这个过程也就是解语法糖。举个泛型擦除的例子,List<Integer>和List<String>在编译之后会进行泛型擦除,变成一样的原生类型List<E>。

java程序一生简史五:字节码生成

字节码生成是Javac编译过程的较后一个阶段,在这个阶段会把前面各步骤生成的信息转化成字节码写到磁盘中,还会进行了少量代码添加和转换的工作。实例构造器<init>()方法和类构造器<clinit>()方法(这里的实例构造器并不是指默认构造函数,如果用户代码没有提供任何构造函数,那编译器将会添加一个没有参数的、访问性与当前类一致的默认构造函数,这个工作在填充符号表阶段已经完成,而类构造器<clinit>()方法指的是编译器自动收集类中的所有类变量赋值动作和静态语句块中的语句合并产生的)就是在这个阶段添加到语法树中的。到此为止整个编译过程结束。

java程序一生简史六:类加载

编译将程序编译成字节码之后,下一步就是类加载到内存的过程。

类加载的过程是在虚拟机内存的方法区进行,这地方涉及到虚拟机内存,所以在这首先简单介绍下程序在内存区域分布的概念。虚拟机内存区域划分为:程序计数器、栈、本地方法栈、堆、方法区(部分区域为运行时常量池)、直接内存。

java程序一生简史七:程序计数器

程序计数器是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。在JVM概念模型中,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

java程序一生简史八:栈

栈用于存储局部变量表、操作数栈、动态链接、方法出口等信息。其中局部变量表存放了编译期克制的各种基本数据类型、对象引用。它与程序计数器一样都是线程私有的。

java程序一生简史九:本地方法栈

本地方法栈与上面介绍的虚拟机栈作用相似,它们的区别*是虚拟机栈为虚拟机执行Java方法(字节码)服务,而本地方法栈则为虚拟机使用的Native方法服务,甚至有的虚拟机会把这两块合二为一。

java程序一生简史十:堆

堆是JVM管理内存较大的一块。它是被所有线程共享的一块区域,它的目的是存放对象实例,几乎所有的对象实例都在这里分配内存(像特殊的类对象会在方法区分配内存)。这地方也是垃圾收集管理的主要区域,从内存回收角度看,现在垃圾收集器都采用分代收集算法(后面会详细介绍),所以Java堆还可以进一步细分:新生代和老年代,而新生代进一步细分:Eden空间、From Survivor空间、To Survivor空间。为了效率考虑,堆还可能划分为多个线程私有的分配缓冲区(TLAB)。无论如何划分,都与存放内容无关,无论哪个区域,存放的依然是对象实例,它们存在的目的只是为了更好的回收和分配内存而已。

java程序一生简史十一:方法区

方法区与堆一样,都是线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。而运行时常量池是方法区的一部分,它主要用于存放编译期声明各种字面量和符号引用。

java程序一生简史十二:直接内存

直接内存并不是虚拟机运行时数据区的一部分,也是不Java规范中定义的内存区域,你可以简单理解为堆外内存,内存分配不受Java堆大小的限制但受整个内存大小的限制。

说完了虚拟机内存区域的概念,我们回到正题,类加载的流程到底是什么呢?加载、验证、准备、解析、初始化五步。其中加载、验证、准备、初始化是顺序执行的,而解析则不一定,它有可能会在初始化之后执行。

java程序一生简史十三:加载

在加载阶段,JVM需要完成三个步骤:首先通过类的全限定名来获取定义此类的二进制字节流,然后将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构,较后在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据入口。

在*一步获取二进制字节流中并没有明确指出从一个*.class文件中获取,规定的灵活性导致我们可以从ZIP(为JAR、EAR/WAR格式提供基础)包中获取,从网络获取(Applet),运行时计算生成(动态代理),其他文件产生(JSP文件生成的Class类),从数据库获取。

java程序一生简史十四:验证

验证,顾名思义,其实就是为了确保Class文件字节流中包含信息符合JVM的要求,因为Class文件的来源途径不一定中规中矩的从编译器产生,也有可能用十六进制编辑器直接编写Class文件。校验流程为文件格式校验、元数据验证、字节码验证,这地方的具体安全校验方式不再细说。

java程序一生简史十五:准备

准备阶段正式为类变量分配内存并设置初始值的阶段,这些变量所使用的内存都在方法区进行分配。

java程序一生简史十六:解析

解析阶段是JVM将常量池内的符号引用替换为直接引用(指向目标的指针、相对偏移量或句柄)的过程,前面我们谈到的编译填充符号表的价值在这地方体现出来了。解析过程无非就是对类或接口、字段、接口方法进行解析。

java程序一生简史十七:初始化

类初始化阶段是类加载过程的较后一步,在准备阶段,变量已经赋过一次初始值,而在这一步,则会根据程序猿定制的要求进行初始化类变量和其他资源。在这个阶段就是执行前面编译字节码生成流程提到的<clinit>()方法的过程。虚拟机也增加在多线程环境下这个方法被同时调用时被正确的加锁、同步,增加只有一个线程去执行这个方法而其他线程阻塞等待,笔者以前写的一篇文章《从一个简单的Java单例示例谈谈并发》中,基于类初始化的单例线程安全的写法涉及到的就是这块,有兴趣的可以结合起来一起看看。这地方还涉及到另一个我们比较关心的知识点,Java何时触发对类的初始化操作呢?

学校联系方式

更多培训课程,学习资讯,课程优惠等学校信息,请进入 石家庄裕华区IT培训石家庄鹿泉区php培训石家庄藁城区java培训 网站详细了解,免费咨询电话:400-998-6158

相关课程