Java 垃圾回收

June 14, 2014

Java

首先,堆是内存里一块特殊的区域,但你创建对象的时候,虚拟机会根据当前的"堆指针"指向的位置开始,分配相应的空间,分配完后,堆指针同时移动到新的位置,这样保证堆指针指向的一直都是可用的内存地址。

为了避免堆指针一直无限的往后移动,直到超出了虚拟机分配的堆的最大值,就需要垃圾回收。把已经不需要在内存中存在的对象回收掉。"引用计数"是一个很简单的判断机制,每个对象都有一个引用计数器,当引用计数器值为0的时候,意味着该对象没有任何的引用者了,可以释放了。但是引用计数太过简单,在出现循环引用的时候,它没法识别出来,以至于内存一直没有被释放。

大部分虚拟机是这样做的,从当前的栈和静态存储区开始,遍历所有对象,遍历对象的引用,及其引用的引用,以此类推,所有被遍历到的,都是“活”的,然后就可以进行垃圾回收了。

第一种垃圾回收思路是,停止-复制 stop-and-copy 。首先暂停所有当前运行的程序,把所有“活”的对象复制到一个新的堆内存里,当然需要修改相应的引用的地址。复制完成后,原来的内存都可以被清空重复利用了,而新的堆里的对象都是活的对象,内存地址排列紧凑了。这样子解决了垃圾回收问题,但是效率不是很高,第一个原因是需要两个堆。解决这个问题的思路就是分块,把堆分成若干小块,这样每次复制不是复制整个堆内存了。第二个原因是,我们认为,当程序稳定运行的时候,并没有太多的垃圾需要回收,如果这个时候内存一直在那里来回倒腾的话,是很浪费的。

所以就有了第二种模式,标记-清扫 mark-and-sweep 。和停止-复制 一样,同样从栈和静态存储区出发,遍历所有活的引用,并且标记。遍历完成一遍之后,所有没有被标记的就被释放了。但是没有复制拷贝这个过程,所以当前堆内存是不连续的。当不连续的足够多的情况下,就来一遍复制,问题就解决了。

在Oracle ,也就是Sun 官方的Java 虚拟机规范里,定义了垃圾回收以低优先级在后台运行,但是实际上,在垃圾回收工作的时候,其他线程都是停止的。在遍历和复制的过程中,其他线程是暂停的。

在 Thinking Java 里,一直使用堆栈这个词,但是我的理解是栈。堆栈是堆和栈的总称,从上下文来看,它这里指的是栈。

--- EOF ---

评论

  1. 不曾离隐处,那得世人逢。

添加新评论