一 创建对象的步骤虚拟机遇到一条new指令
检查指令的参数能否在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载、解析和初始化过
若没有,则必须首先执行相应的类加载过程
类加载完成,虚拟机为新生对象分配内存。分配内存相当于从Java堆中划分出一块内存大小确定的块,分两种情况
(1)Java堆内存,属于绝对规整的那种。只需指针向空闲空间挪动一段举例即可
(2)不规整。空闲区和已分配区交错,需要一张“空闲列表”记录哪些区域是分配了的
虚拟机对对象进行必要的设置。如,这个对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码。此步骤为止虚拟机步骤完成
Java程序开始:init方法还没有执行,所有字段仍然为0,new指令后,执行 init 方法。
二 垃圾回收的目标
Java运行期间的各个部分:程序计数器、虚拟机栈、本地方法栈,这三个区域随着线程而存亡。栈中的线帧随着方法的进入和退出而入栈、出栈。每个线帧分配多少内存在类的结构确定时也确定。这几个区域的内存分配和回收都是确定的,方法结束或线程结束时,内存就回收了。
而Java堆和方法区就不一样了,一个接口的多个实现类所需要的内存都不一样,一个方法的各个分支所需内存也不一样,程序运行期间才能动态确认。垃圾收集器关注的也是此部分
三 判断对象是否存活(堆中)
有以下方法:
引用记数法:给对象添加一个引育弄个计数器,有一个地方引用时该计数器加1,一个引用失效时,减一
缺点:无法应对循环引用,例如【objA.instance=objB和obj.instance=objA】
可达性分析算法:以一个称为”GC Root”的对象作为起始点,从这些节点往下搜索。当一个对象到”GC Root”的引用路径不可达时,证明该对象不可用。java采用此方法
四 回收方法区
主要有两部分:(1)废弃的常量(2)无用的类
废弃常量:没有任何对象引用它
无用的类:
(1)该类所有的实例都已被回收,也即Java堆中不存在该类的任何实例
(2)加载该类的classLoader已被回收
(3)该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法
五 对象的访问定位
Java程序,需要通过栈上的reference数据来操作堆行的具体对象。reference类型在Java中只规定了一个指向该对象的引用,如何去定位,访问堆中的对象的具体对象。不同的虚拟机实现,分为使用句柄和直接指针两种。
5.1 句柄访问
Java堆中划分一块内存作为句柄池,reference中存储的即对象的句柄地址,而句柄包含了对象实例数据与类型数据各自的具体地址信息。
优点:reference中存储的是稳定的句柄地址,对象被移动(垃圾回收时),只会改变句柄中的实例数据指针,而reference本身不需要移动
5.2 直接指针访问
Java堆中对象的布局中就必须考虑如何放置访问类型数据相关信息,reference中存储的直接是对象地址
优点:速度更快,节省了一次指针定位的时间开销。虚拟机Sun HotSpot即采用此方法。