Java垃圾回收对应的是堆内存的灵活分配,因为java不要求程序员手动释放申请的堆内存,而这部分大块内存的回收工作就有JVM自己来完成。
Java垃圾回收的工作方式:
一、“引用-计数”。这是一种速度很慢的垃圾回收技术。每个对象都有一个引用计数器,当有引用连接至对象时,计数加1;当引用离开作用域或置为null时,计数减1。在之后的垃圾回收器遍历对象列表时,回收计数为0的对象所占内存。这种方法有一个缺陷,需要为循环引用的对象做很大的工作,才能区分是否计数应置为0。这种方式通常用于解释GC的工作原理,而未被应用于任何JVM中。
针对“引用-计数”方式的缺陷,考虑到任何一个“活”的对象,一定能追溯到其存活在堆栈或静态存储区的引用。因此如果从堆栈和静态存储区开始,遍历所有的引用,我们将会得到所有有效的对象,而“引用-计数”方式中的循环引用的对象自然不会被遍历到。由此,有了以下两种方式。
二、“停止-复制”。这种方式会暂停程序,重新申请一块已分配堆内存大小的内存,将被遍历到的对象拷贝到新申请的堆内存中,并将引用指向新的地址。拷贝结束后,原对内被释放,有用的对象被保留,到达不到的对用被回收,完成垃圾回收。它的缺点是,需要内存比实际需要大一倍的空间,而且全部复制在程序进入稳定状态后是不明智的选择,因为此时产生的垃圾可能会较少。
三、“标记-清扫”。这种方式也会暂停程序。不同于“停止-复制”,标记清扫是为每个被遍历到的对象做上标记,在标记完成后,清除所有未被标记的对象。这种方式的缺点是会产生内存碎片,堆内存空间不连续。
“自适应的、分代的、停止-复制、标记-清扫”是一种折中式的GC工作方式。既可以解决循环引用,又可以在程序运行稳定状态较快工作,又可以有效的解决堆内存碎片的问题。
提到垃圾回收,就不能不说finalize()方法。这个方法在GC回收对象所占用的内存时被GC调用,且最多被调用一次,而且不会抛出其中的异常,所以这个方法可以用来检测可能未被关闭的IO流、未断开的SOCKET、未释放的持有对象的内存空间,并作出相应的处理。这个方法不应被程序员过多的调用,事实上,它近似于多余。