垃圾收集器与内存分配策略( 三 )


属性表集合 -属性
属性的作用是通知虚拟机自动为静态变量赋值(提前赋值,不需要运行时赋值,加快运行时的获取速度) 。只有被关键字修饰的变量(类变量)才可以使用这项属性 。类似“int x=123”和“ int x=123”这样的变量定义在Java程序里面是非常常见的事情,但虚拟机对这两种变量赋值的方式和时刻都有所不同 。对非类型的变量(也就是实例变量)的赋值是在实例构造器()方法中进行的;而对于类变量,则有两种方式可以选择:在类构造器()方法中或者使用属性 。
目前公司实现的Javac编译器的选择是,如果同时使用final和来修饰一个变量(按照习惯,这里称“常量”更贴切),并且这个变量的数据类型是基本类型或者java.lang.的话,就将会生成属性来进行初始化;如果这个变量没有被final修饰,或者并非基本类型及字符串,则将会选择在()方法中进行初始化 。
类加载子系统 类加载过程
Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制 。与那些在编译时需要进行连接的语言不同,在Java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的,这种策略让Java语言进行提前编译会面临额外的困难,也会让类加载时稍微增加一些性能开销,但是却为Java应用提供了极高的扩展性和灵活性,Java天生可以动态扩展的语言特性就是依赖运行期动态加载和动态连接这个特点实现的 。
一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载()、验证()、准备()、解析()、初始化()、使用(Using)和卸载()七个阶段,其中验证、准备、解析三个部分统称为连接()
加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类型的加载过程必须按照这种顺序按部就班地开始,而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运行时绑定特性(也称为动态绑定或晚期绑定) 。请注意,这里笔者写的是按部就班地“开始”,而不是按部就班地“进行”或按部就班地“完成”,强调这点是因为这些阶段通常都是互相交叉地混合进行的,会在一个阶段执行的过程中调用、激活另一个阶段 。
加载
“加载”()阶段是整个“类加载”(Class )过程中的一个阶段,在加载阶段,Java虚拟机需要完成以下三件事情:
1)通过一个类的全限定名来获取定义此类的二进制字节流 。
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构 。
3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口 。
相对于类加载过程的其他阶段,非数组类型的加载阶段(准确地说,是加载阶段中获取类的二进制字节流的动作)是开发人员可控性最强的阶段 。加载阶段既可以使用Java虚拟机里内置的引导类加载器来完成,也可以由用户自定义的类加载器去完成,开发人员通过定义自己的类加载器去控制字节流的获取方式(重写一个类加载器的()或()方法),实现根据自己的想法来赋予应用程序获取运行代码的动态性 。
对于数组类而言,情况就有所不同,数组类本身不通过类加载器创建,它是由Java虚拟机直接在内存中动态构造出来的 。但数组类与类加载器仍然有很密切的关系,因为数组类的元素类型( Type,指的是数组去掉所有维度的类型)最终还是要靠类加载器来完成加载,一个数组类(下面简称为C)创建过程遵循以下规则: