Java虚拟机----垃圾回收与内存分配
一、垃圾回收的对象:
在Java的运行时数据区中,程序计数器和虚拟机栈、本地方法栈是随着线程的生灭而生灭,栈当中栈帧的大小在编译的时候已知,在方法结束之后栈帧出栈,这部分的垃圾回收是明确的,因此需要讨论的垃圾回收的区域是堆,以及方法区。
二、怎么判断一个对象是否存活:
1、引用计数算法:给对象添加一个引用计数器,当有一个地方引用它时加1,引用失效的时候减1,当引用计数器值为0的时候对象就不再被使用,判定对象已死。
---- 事实上java虚拟机不是使用这样子的算法来判断对象是否存活的,因为这样很难解决对象之间相互循环引用而又没有被其他地方调用(这种情况是可以被回收)的问题。
2、可达性分析算法:
通过一系列的“GC ROOTS”对象作为起始点,从这些节点往下搜索它的引用链,一个对象只要在任何的引用链上能连接上“GC ROOTS”对象,则这个对象是存活的,否则对象则已死。
这些“GC ROOTS”对象包括:
- 虚拟机栈(栈帧)中引用的对象,
- 方法区中类的静态属性引用的对象,
- 方法区中常量引用的对象
三、引用:
传统的引用定义:
如果reference数据当中存储的数值代表的是另外一个内存的起始地址,就称这块内存代表着一个引用。
扩充后的引用:
强引用、软引用、弱引用、虚引用。
- 强引用:类似使用new指令创建的引用---------只要强引用还在,垃圾收集器就不会回收掉被引用的对象;
- 软引用:描述一些有用但是非必需的对象-----在系统将要发生内存溢出异常之前会软引用的对象进行一个尝试回收,如果还无法回收到足够的空间才会抛出内存溢出异常;
- 弱引用:也是描述非必需对象的-----------------在下一次的垃圾回收时一定会对这些弱引用对象进行回收;
- 虚引用:对象的虚引用不会对对象的生存时间产生影响,唯一目的是在对象被垃圾收集器回收的时候能收到一个系统通知。
四、对象具体的生存和死亡:
在可达算法中的对象,会根据四个引用类型进行相应的垃圾回收操作,而对于“”不可达“”的对象,也并非是直接进行回收,需要进行两次标记:
第一次标记:发现对象没有到“GC ROOTS”对象的引用链,则将进行第一次标记,并且进行第一次筛选,筛选的条件是此对象是否有必要执行finalize()方法;当对象没有finalize()方法,或者是finalize()方法已经被调用过,则判定该对象没有必要执行finalize方法。如果判定对象需要执行finalize方法则把对象放入F-Queue队列中(虚拟机会有一个低优先级的线程去执行它,至于执行结果并不去管,不管它是成功还是失败)
第二次标记:对放在F-Queue队列的对象进行第二次标记,如果它还是不可达的,那么只能被回收了。
进行两次标记是给了对象一个自我拯救的机会,第一次标记之后会调用finalize()方法,如果在这个方法中这个对象又被引用了,那么这个对象就可以自救了,需要注意的是,一个对象的finalize方法只能被执行一次,也就是说它只能自救一次,这也是可以理解的,否则一个对象在finalize中进行无限次自救,它将永远不会被回收。