Class类文件结构
class文件是一组以8字节为单位的二进制流,只有两种数据类型:无符号数(基本数据类型),表(复合数据类型)
魔数
版本号
常量池(占class空间最大的数据之一,从1开始计数)
1.字面量 :接近于java层面的常量概念,如字符串,声明为final的常量
2.符号引用:类和接口的全限定名,字段和方法的描述符
字段描述符:描述字段数据类型
方法描述符:描述方法参数列表和返回值
访问标志
类索引,父类索引,接口索引集合
字段表集合(描述接口或类中声名的变量,不包括方法中的局部变量)
方法表集合
属性表集合(如,方法表,属性表都可以带有自己的属性表,如code属性存方法的代码信息)
对象的创建(非Class类对象)
0.准备工作,这个指令的参数是否可以在常量池中定位到一个类的符号引用,检查这个类是否已经被加载,解析初始化,如果没有,先执行相应的类加载过程
1.分配内存(堆中)
2.分配的内存空间都初始化为零值
3.init根据程序员的意愿初始化
分配内存
对象所需内存的大小在类加载完成后便完全确定(JVM可以通过普通Java对象的类元数据信息确定对象大小);
为对象分配内存相当于把一块确定大小的内存从Java堆里划分出来
1. 指针碰撞
如果Java堆是绝对规整的:一边是用过的内存,一边是空闲的内存,中间一个指针作为边界指示器;
分配内存只需向空闲那边移动指针,这种分配方式称为"指针碰撞"(Bump the Pointer);
2. 空闲列表
如果Java堆不是规整的:用过的和空闲的内存相互交错;
需要维护一个列表,记录哪些内存可用;
分配内存时查表找到一个足够大的内存,并更新列表,这种分配方式称为"空闲列表"(Free List);
Java堆是否规整由JVM采用的垃圾收集器是否带有压缩功能决定的;
所以,使用Serial、ParNew等带Compact过程的收集器时,JVM采用指针碰撞方式分配内存;而使用CMS这种基于标记-清除(Mark-Sweep)算法的收集器时,采用空闲列表方式;
对象的访问定位
1.通过句柄访问对象
好处:reference中存的是稳定的句柄地址,在GC时对象被移动只会改变实例数据指针,而reference本身不需要改变
2.通过直接指针访问对象
好处:访问速度快
对象创建的时机
使用new关键字创建对象
使用Class类的newInstance方法(反射机制)
用Constructor类的newInstance方法(反射机制)
使用Clone方法创建对象
用(反)序列化机制创建对象
对象的初始化顺序
父类静态代码块,静态成员变量
子类静态代码块,静态成员变量
父类普通成员变量,非static代码块
父类构造函数
子类普通成员变量,非static代码块
子类构造函数
类加载的时机
new关键字实例化对象,读取或设置一个类的静态字段(被final修饰除外)、调用一个类的静态方法;
使用java.lang.reflect包的方法对类进行反射调用
初始化一个类,若其父类没有被初始,先初始化起父类
虚拟机启动时,执行的主类(含main方法的类)先初始化
比如,new一个对象,首先检查Class类是否被加载,没有,先进行类加载过程;
然后进行对象的创建,分配内存,按照顺序初始化;
再次new一个对象,检查Class类被加载过了,直接进行对象的创建;
原文地址:http://blog.51cto.com/13580976/2148191