admin 管理员组

文章数量: 887006

java自动回收机制

无论何种计算机语言,在堆上分配对象的代价是十分昂贵的。垃圾回收器对于提高对象的创建速度,却具有明显的效果。

听起来很奇怪——存储空间的释放会影响到存储空间的分配,但这确实是某些JVM的工作方式。这也意味着,Java从堆中

分配空间的速度,可以与其他语言从堆栈上分配空间相匹配的。摘自《Thinking in Java 4》

如何理解Java垃圾回收机制的工作方式呢?打个比方说:如果把C++的堆想象成一个院子,那么里面每个对象都负责

管理自己的地盘,对象可能被销毁,但地盘必须得被重用;在某些Java虚拟机中,堆的实现截然不同,它就像一个传送

带,每分配一个新对象,它就往前移动一格。这就意味着对象的分配空间素速度非常快。Java的“堆指针”只是简单地移动

到位分配的区域,其效率比得上C++在堆栈上分配空间的效率。当然在实际过程中还有其他的开销,但比不上查找要用的

空间开销大。

到这里,大家就会有个疑问了,Java中堆并不一定就像传送带那样工作呀。要真是这样的话,势必会导致频繁的内存

页面调度——将其移进移出硬盘,因此会显得需要拥有比实际需要更多的内存。页面调度会显著的影响性能,最终,在

创建足够多的对象后,内存资源会被耗尽。其中的秘密在于垃圾回收器的加入,当它工作时,将一面回收空间,一面使

堆中的对象紧凑排列,这样“堆指针”就可以很容易靠近传送带的开始处,也就尽量避免了页面错误。通过垃圾回收器对

对象重新排列,实现了一种高速的,无限内存空间可供分配的堆模型。

想要更好的理解Java的垃圾回收,可以参考其他系统中的垃圾回收方法。

1、引用计数

这是一个简单但是速度很慢的垃圾回收技术。每个对象都含有一个引用记数器,当有引用连接对象是,引用计数+1。

当引用离开作用域或设置为null时,引用计数-1。虽然管理引用计数开销不大,但这项开销在整个程序生命周期中持续

发生。垃圾回收器会在含有全部对象的列表上遍历,当发现引用计数为0的时候,就释放其内存空间。这种方法有个缺陷,

如果两个对象互相引用的话,那么这个对象的内存就不会被释放掉(查找这种情况需要的工作量很大)。所以这种方法

一般不会被运用到实际开发中,仅仅作为垃圾回收的工作机制的说明。

2、停止-复制

这种方法依据的思想是:对于任何“活”的对象,一定能最终追溯到堆栈或静态存储区之中的引用。这个引用条可能会

穿过数个对象层次。由堆栈或静态存储区开始,遍历所有引用,找到所有的“活”的对象。注意:这种“交互自引用的对象组”

的问题就不会出现,所以就被自动回收了。

在这种方式下,java虚拟机采用了一种自适应的垃圾回收技术。显然这意味着,先暂停程序的运行,然后将所有存活的

对象从当前的堆复制到另一个堆,没有被复制的全部是垃圾。当对象被复制进新堆时,他们是一个紧挨着一个的,所以新

堆保持紧凑排列,就可以按前述方法简单,直接的分配新空间了。

当把对象从一个堆搬到另一个堆,所有指向他的引用也必须得修正。

所以对于这种“复制式回收器”而言,效率会降低。还有两个原因,一是有两个堆,实际维护起来需要一倍多的时间;二是

当程序稳定了,产生的垃圾没有或很少,所以再从一个堆复制到另一个堆,这很浪费。

3、标记-清扫

为了避免这种情况,一些虚拟机会进行检查:要是没有新垃圾产生,就会转换到另一种工作模式(即自适应)。对于一般

用途而言,“标记-清扫”是很慢的,但是你如果知道有很少垃圾活没有垃圾的时候,它的速度就很快了。它的思路是这样的:从

堆栈和静态区出发,遍历所有引用,找出存活的对象,并且为每个存活的对象打上一个标记,这过程中不会回收任何的对象。

只有全部标记工作完成了,清理动作才会开始。在清理的过程中,没有标记的对象就是垃圾,会被释放空间,这时候,内存空间

是不连续的,垃圾回收器想要得到连续的空间的话,只能重新整理剩下的对象。同样的,这些方法也必须在程序暂停之后,才能

工作。

本文标签: java自动回收机制