深入理解java虚拟机之虚拟机执行子系统笔记

一个多月来浑浑噩噩,有时看电视打游戏,有时听听歌看看书逛逛街,有时闲谈扯淡,有时静坐思过,有时炒几个小菜还有时发呆半晌。或喜或忧,以思无益,不如学也。

每个java类经编译之后生成一个Class字节码文件,Class文件具有固定的文件存储格式。虚拟机把Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型。

Class文件结构(略)

C语言编程机制

C编程的基本机制:编译、链接。这使得程序便于模块化。您可以分别编译各个模块,然后用链接器将编译过的模块结合起来。

编译:将高级语言翻译成低级语言的过程。(C语言中生成目标代码)

链接:将目标代码、引用的库代码以及启动代码结合在一起,并把它们存放在单个文件中,即生成可执行文件。

库代码:C语言(标准)库函数

启动代码:程序和操作系统间的接口

类加载过程

加载(完成三件事):

①.通过类的全限定名来获取定义此类的二进制字节流

②.将这个字节流的静态储存结构转换为方法区的运行时数据结构

③.在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。

连接:

验证:检查Class字节码文件是否符合当前虚拟机规范

准备:为类变量分配内存并设置类变量的默认值(注意:常量在准备阶段将被赋予程序员指定初始值)

解析:将常量池中的符号引用替换为直接引用,即判断类和接口、属性、类方法和接口方法是否有效、是否具备访问权限等。

(类)初始化:执行类构造器<clinit>方法的过程。对类变量和静态语句块赋予程序员定制的值。

关于<clinit>与<init>

<clinit>:类构造器。初始化阶段,java编译器自动收集类变量与静态语句块的初始化语句(按先变量赋值再静态语句块的绝对顺序,因此在静态语句块中可以访问到类变量的初始值),放在<clinit>方法中。虚拟机保证子类的<clinit>方法执行之前父类的<clinit>方法已经执行完毕。因此虚拟机中第一个被执行的<clinit>()方法的类肯定是java.lang.Object

<init>:实例构造器。对象初始化时,jvm会调用<init>()方法为实例变量和初始化块进行初始化(然后才是构造方法)。<init>会显示的调用父类的构造器。

参考:http://www.ibm.com/developerworks/cn/java/j-lo-clobj-init/index.html

  http://penny.iteye.com/blog/83604

实例化一个类(生成对象)有四种途径:

调用new操作符;

调用Class或java.lang.reflect.Constructor对象的newInstance()方法;

调用任何现有对象的clone()方法;

通过java.io.ObjectInputStream类的getObject()方法反序列化。

(类)初始化时机:虚拟机规范严格规定有且只有四种情况必须立即对类进行初始化:

①.遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有初始化,则需要出发其初始化。(注意:特殊情况,全局常量在准备阶段初始化)

②.使用java.lang.reflect包的方法对类进行反射调用时

③.当初始化一个类,如果发现其父类没有初始化,则需要先触发其父类的初始化

④.当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类

类加载器

参考: http://www.ibm.com/developerworks/cn/java/j-lo-classloader/

虚拟机字节码执行引擎(基于栈的解释执行)

参考:

JVM工作原理和特点  http://developer.51cto.com/art/200907/135143.htm

《深入java虚拟机》sun公司核心技术丛书 (美)Bill Venners著 曹小钢 蒋靖 译

虚拟机栈是线程独享的,每当启动一个新线程时,JVM都会为它分配一个java栈。java栈以帧为单位保存线程的运行状态。虚拟机只会对java栈执行两种操作:以帧为单位的压栈和出栈。每一个栈帧都包括三部分:局部变量表、操作数栈、帧数据区。

局部变量表是一组变量值储存空间,用于存放方法参数和方法内部定义的局部变量。它被组织为一个以字长(32位)为单位、从0开始计数的数组,其中第0位索引默认是用于传递所属对象实例的引用,在方法中可以通过关键字“this”来访问该隐含的参数。

操作数栈被组织为一个以字长为单位(数组实现)的栈。

Java虚拟机没有寄存器,Java虚拟机的指令是从操作数栈中取得操作数而不是从寄存器中取得操作数,因此JVM是基于栈运行而不是基于寄存器运行的。

参考: http://blog.csdn.net/tuhuolong/article/details/6458815

如果读者有兴趣可以参考《深入理解Java虚拟机》 周志明:P224 配图说明基于栈的解释器质性过程,可以对比下汇编语言。

实战

书中提到:Java程序社区中流传着这么一个观点:“学习JavaEE规范,去看Jboss源码;学习类加载器,就去看OSGi源码”。感兴趣可以探究。

原文地址:https://www.cnblogs.com/zhangpeng201211/p/2642980.html