Java语言提供了对象终止(finalzation)机制来允许开发人员提供对象被销毁之前的自定义处理逻辑。
当垃圾回收其发现没有引用指向一个对象,即垃圾回收此对象之前,总会先调用这个对象的finalize方法。
finalize方法位于Object类中,允许在子类中被重写,**用于在对象被回收时进行资源释放,**通常在这个方法中进行一些资源释放和清理的工作,比如关闭文件,套接字和数据库连接等。
永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。里有包括以下三点:
由于finalize方法的存在,虚拟机中对象一般处于三中可能的状态。
如果从所有的根节点都无法访问到某个独享,说明对象已经不再使用了。一般来说,此对象需要回收。但事实上,也并非是非死不可,这时候它们暂时处于缓刑阶段。一个无法触及的对象有可能在某一个条件下复活自己,如果这样,那么对它的回收就是不合理的,为此,定义虚拟机中的对象可能的三种状态:
以上三中状态中,是由于finalize方法的存在进行的区分,只有在对象不可触及时才可以被回收。
判定一个对象是否可回收,至少姚经理两次标记过程:
当成功区分出内存中存活对象和死亡对象后,GC接下来的任务就是执行垃圾回收,释放掉无用对象所占用的内存空间,一遍有足够的的可用内存空间为新对象分配内存。
目前在JVM中比较常见的三种垃圾收集算法是标记-清除算法(Mark-Sweep),复制算法(Copying),标记-压缩算法(Mark-Compact)。
标记-清除算法(Mark-Sweep)是一种非常基础和常见的垃圾收集算法。
当堆中的有效内存空间被耗尽的时候,就会停止整个程序(也被称为stop the world ,STW),然后进行两项工作,第一项是标记,第二项是清除。
注意:这里所谓的清楚并不是真的置空,而是把需要清除的对象地址保存在一个空闲的地址列表中。下次有新对象需要加载时,判断垃圾的位置空间是否够,如果够,就存放。
缺点:
为了解决标记-清除算法在垃圾收集效率方面的缺点,之后诞生了复制算法(Copying)。
核心思想: 将活着的内存空间分为两块,每次只使用其中一块,在垃圾回收时将正在是用的呢村中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的所有对象,交换两个内存的角色,最后完成垃圾回收。
优点
缺点
如果系统中的垃圾对象很多,复制算法就不会很理想,因为复制算法需要复制的存活对象数量并不会太大,或者说非常低才行。
应用场景:
在新生代,对常规应用的垃圾回收,一次通常可以回收70%-9%的内存空间。而因为复制算法适合存活对象数量少的场景,所以现在的商业虚拟机都是用这种手机算法回收新生代。
复制算法的高效性是建立在存活对象少,垃圾对象多的前提下的。这种情况在新生代经常发生,但是在老难带,耿长江的情况是大部分对象都是存活对象。如果依然使用复制算法,由于存活对象较多,复制的成本也将提高。因此,基于老年代垃圾回收的特性,需要使用其他的算法。
标记清除算法的确可以应用在老年代中,但是该算法不仅执行效率低下,而且在执行完内存回收后还会产生内存碎片,所以JVM的设计者需要在此基础上进行改进。标记-压缩算法诞生。
第一阶段和标记清除算法一样,从根节点开始标记所有被引用的对象。
第二阶段将所有的存活对象压缩到内存一端,按顺序排放。
之后清理便捷外所有空间。
标记压缩算法的最终效果等同于标记-清除算法执行完成后,在进行一次内存碎片整理。因此也成为了标记-清除-压缩算法。
二者的本质差异在于标记清除算法是一种非移动时的回收算法,标记压缩是移动式的,是否移动回收后的存活对象是一项优缺点并存的风险决策。
可以看到,标记的存回对象将会被整理,按照内存地址一次排列,而未被标记的内存会被清理掉。如此依赖,当我们需要给新对象分配内存时,JVM只需要迟永一个内存的起始地址即可,这比维护一个空闲列表显然少了很多开销。
优点
缺点