
Java规范定义标准结构如图3.1

Java代码的执行机制
Java源码编译机制
javac将Java源码编译为class文件的步骤如图3.2

1.分析和输入到符号表(Parse and Enter)
Parse过程所做的为词法和语法分析。词法分析(com.sun.tools.javac.parser.Scanner)要完成的是将代码字符串转变为token序列(例如Token.EQ(name:=));语法分析(com.sun.tools.javac.parser.Parser)要完成的是根据语法由token序列生成抽象语法树。
注解处理(Annotation Processing)
该步骤主要用于处理用户自定义的annotation。
语义分析和生成class文件(Analyse and Generate)
Analyse步骤基于抽象语法树进行一系列的语义分析。
类加载机制
类加载机制是指.class文件加载到JVM,并形成Class对象的机制,之后应用就可对Class 对象进行实例化并调用,类加载机制可在运行时动态加载外部的类、远程网络下载过来的class 文件等。除了该动态化的优点外,还可通过JVM的类加载机制来达到类隔离的效果。
JVM将类加载过程划分为三个步骤:装载、链接和初始化。装载和链接过程完成后,即将二进制的字节码转换为Class对象;初始化过程不是加载类时必须触发的,但最迟必须在初次主动使用对象前执行,其所作的动作为给静态变量赋值、调用<clinit>()等。

1.装载(Load)
装载过程负责找到二进制字节码并加载至JVM 中,JVM通过类的全限定名(com.bluedavy.HelloWorld)及类加载器(ClassLoaderA实例)完成类的加载。
2.链接(Link)
链接过程负责对二进制字节码的格式进行校验、初始化装载类中的静态变量及解析类中调用的接口、类。
3.初始化(Initizlize)
初始化过程即执行类中的静态初始化代码、构造器代码及静态属性的初始化,在以下四种情况初始化过程会被触发执行:
1)调用了new;
2)反射调用了类中的方法;
3)子类调用了初始化;
4)JVM启动过程中指定的初始化类。

类执行机制
字节码解释执行
在源码编译阶段将源码编译为JVM字节码,JVM字节码是一种中间代码的方式,要由JVM在运行期对其进行解释并执行,这种方式称为字节码解释执行方式。
指令解释执行
指令解释执行即获取下一条指令,解码并分派,然后执行。
栈顶缓存
栈顶缓存,即将本来位于操作数栈顶的值直接缓存在寄存器上,这对于大部分只需要一个值的操作而言,无须将数据放入操作数栈,可直接在寄存器计算,然后放回操作数栈。
部分栈帧共享
当调用方法时,后一方法可将前一方法的操作数栈作为当前方法的局部变量,从而节省数据copy带来的消耗。
编译执行
解释执行的效率较低,为提升代码的执行性能,Sun JDK提供将字节码编译为机器码的支持,编译在运行时进行,通常称为JIT编译器。Sun JDK在执行过程中对执行频率高的代码进行编译,对执行不频繁的代码则继续采用解释的方式,因此Sun JDK又称为Hotspot VM,在编译上Sun JDK提供了两种模式:client compiler (-client)和server compiler ( -server)。
client compiler又称为C1,较为轻量级,只做少量性能开销比高的优化,它占用内存较少,适合于桌面交互式应用。在寄存器分配策略上,JDK6以后采用的为线性扫描寄存器分配算法,在其他方面的优化主要有:方法内联、去虚拟化、冗余削除等。
方法内联
对于Java类面向对象的语言,通常要调用多个方法来完成功能。执行时,要经历多次参数传递、返回值传递及跳转等,于是C1采取了方法内联的方式,即把调用到的方法的指令直接植入当前方法中。
去虚拟化
去虚拟化是指在装载class文件后,进行类层次的分析,如发现类中的方法只提供一个实现类,那么对于调用了此方法的代码,也可进行方法内联,从而提升执行的性能。
冗余削除
冗余削除是指在编译时,根据运行时状况进行代码折叠或削除。
Server compiler又称为C2,较为重量级,C2采用了大量的传统编译优化技巧来进行优化,占用内存相对会多一些,适合于服务器端的应用。和C1不同的主要是寄存器分配策略及优化的范围,寄存器分配策略上C2采用的为传统的图着色寄存器分配算法“;由于C2会收集程序的运行信息,因此其优化的范围更多在于全局的优化,而不仅仅是一个方法块的优化。收集的信息主要有:分支的跳转/不跳转的频率、某条指令上出现过的类型、是否出现过空值、是否出现过异常。
逃逸分析是C2进行很多优化的基础,逃逸分析是指根据运行状况来判断方法中的变量是否会被外部读取。如不会则认为此变量是逃逸的,基于逃逸分析C2在编译时会做标量替换、栈上分配和同步削除等优化。除了逃逸分析以外,C2还会基于其拥有的运行信息来做其他的优化,例如编译分支频率执行高的代码。
标量替换
标量替换的意思简单来说就是用标量替换聚合量。
栈上分配
如果对象没有逃逸,那么C2会选择在栈上直接创建Point对象实例,而不是在JVM堆上。
同步削除
同步削除是指如果发现同步的对象未逃逸,那也没有同步的必要了,在C2编译时会去掉同步。
OSR (On Stack Replace)编译。OSR编译和C1、C2最主要的不同点在于OSR编译只替换循环代码体的入口,而C1、C2替换的是方法调用的入口,因此在OSR编译后会出现的现象是方法的整段代码被编译了,但只有在循环代码体部分才执行编译后的机器码,其他部分则仍然是解释执行方式。
反射执行
基于反射可动态调用某对象实例中对应的方法、访问查看对象的属性等,无需在编写代码时就确定要创建的对象。反射和直接创建对象实例,调用方法的最大不同在于创建的过程、方法调用的过程是动态的。
使用反射执行可以获取类的成员变量(包括私有成员变量)。
JVM内存状况查看方法和分析工具
几种常用的工具:输出GC日志、GC Port、JConsole、JVisuaIVM、JMap、JHat、JSat、Eclipse Memory Analyzer
2.JVM线程资源同步及交互机制
线程资源同步机制、线程资源交互机制
线程状态及分析
JVM把线程分为几种不同的状态,并根据状态放入不同的sets 中来进行调度。线程在创建完毕后进入new状态,调用了线程的start方法后线程进入Runnable状态,放入JVM的可运行线程队列中,等待获取CPU 的执行权;JVM按线程优先级及时间分片、轮循的方式来执行Runnable状态的线程。当线程进入start代码段,开始执行时,其线程状态转变为Running;线程在执行过程中如果执行了sleep,wait、join,或者进入了IO阻塞、锁等待时,则进入Wait或Blocked 状态,在这种状况下线程放弃CPU的使用权,进入 wait sets或锁sets中,直到wait结束、线程被唤醒或获取到锁,在这些情况下线程也再次进入Runnable状态;在线程执行完毕后,线程就从可运行线程队列中删除了,JVM线程的状态转变如图3.24所示。
