java GC - 垃圾回收器原理

java从堆分配空间的速度,可以喝其他语言从堆栈上分配空间的速度相比。

C++语言的堆看做一个院子,每个对象都有自己的一块地盘。一段时间后,对象可以被销毁,但是地盘当然需要重用。JVM中不同:它更像一个传送带,每分配一个新的对象,就往前移动一格,这样对象分配速度快。java“堆指针”只是简单的移动到尚未分配的区域,与C++在堆栈上的分配速度。当然,在实际的过程中,簿记工作方面会有商量额外的开销,但是相比查找可用空间这样很小了。

你可能已经意识到,其实java中的堆未必完全是像传送带那样工作。因为这样的话,会导致频繁的内存页面调度——将其移进、出硬盘,并因此显得需要拥有比实际需要更多的内存。页面调度会显著的影响性能,最终在创建足够多的对象后,内存资源耗尽。因此,这里就多了jvm的核心关键:垃圾回收器。

垃圾回收器工作时候,将一面回收空间,一面使堆中的对象紧凑排列。这样的“堆指针”就很容易移动到更接近“传送带”的开始处,也就尽量避免了页面错误。通过垃圾回收器对对象的重新排列,实现了一种高速的、有足够空间可供分配的堆模型。

我们先看看其他语言的垃圾回收机制:引用计数是一种简单但是速度很慢的垃圾回收技术。对个对象都含有一个引用计数器,当有引用连接对象时,引用计数+1,,当引用离开作用于或者被设置成为null时,引用计数-1,。虽然管理引用计数的开销不大,但是这是一个在整个程序的生命周期中一直持续着。垃圾回收器会在所有的对象列表上遍历,当发现某个对象的引用计数0,就释放其占用的空间(这样的机制,导致一旦计数为0就会立即释放对象资源),这样的方法有个明显的缺陷,就是如果对象之间存在循环引用,可能出现“对象英爱被回收但是引用计数不为0”。对垃圾回收期而言,要定位这样的交互自引用的对象组,又是一个大量的开销。引用计数常用来说明垃圾回收器的垃圾收集方式,但却未被任何垃圾回收器使用。

在更通用的模式中,垃圾回收器并非基于引用计数技术。中心思想:对任何“活”的对象。一定能够追溯到让其存活的在堆栈或者静态存储区的引用。这个引用链条可能会穿过数个对象层次。由此,如果从堆栈或者静态存储区开始,便利所有引用,就能找到所有“活”着的对象。对于发现的引用,必须追踪它所引用的对象,然后是此对象包含的所有的引用,如此迭代下去,直到找到所有“根源于堆栈和静态存储区的引用”所形成的网络被全部访问为止。你所访问过的对象,理所当然应该都是“活”的。这样就解决了“交互自引用的对象组”的问题——这种现象根本不会被发现,因此自动回收。

在这种思想下,Java虚拟机将采用一种“自适应”回收技术。至于如何处理找到的这些存活对象,就取决于java虚拟机的实现。

1.停止-复制(stop-and-copy)

这意味着先暂停程序的运行(不属于后台回收模式),然后将所有的“活”对象从当前堆复制到另一个新堆。没有被复制的就是垃圾了,回收。当对象被复制到新堆后,它是一个接着一个紧凑的直接按照前面方法,简单直接分配内存。

当堆乡从一处搬到另一处,所有指向它的引用必须修正,位于堆或者静态储存区的可以自行修正,但可能还有其他指向这些对象的引用,他们在遍历使用时,才能动态的被找到。(旧地址-新地址的映射)

缺点:“复制式”GC效率会降低,1)首先要得到两个堆,2)在这两个堆间的分离来回操作,得维护比实际需要更多一倍的时间,3)复制:程序进入隐性状态后,可能只有很少的垃圾,甚至没有垃圾产生,但是GC仍然会将所有的内存从一处-另一处新堆,浪费。

Java处理:1)按照需要从堆中分配几块较大的内存,复制动作发生在这些较大内存间,减少创建新堆。2)

JVM检查:要是没有新垃圾,就会转换到新模式(自适应),这种模式称为标记-清扫(mark-and-sweep)

3.标记-清扫(mark-and-sweep)

依据思路仍然是从堆栈或者静态存储区出发,遍历所有引用,找出所有“活”对象,每当它找到或对象,就会给对象标记,这个过程不会回收任何对象。

全部标记完成后,清理才开始,没有被标记的对象就是GC,北释放内存,没有复制操作。

缺点:这样做后,剩下的内存空间显然不是连续的,要想连接,需要重新整理。而且这种模式速度慢。

Java GC theory:

以上两种思路,都是在暂定程序情况下进行的。

前文所述,当前Java VM中,内存分配以较大”块“为单位,如果对象较大,会占用单独的块。严格地说,定制-复制要求释放就对象之前,必须把所有存活的对象从旧堆中复制到新堆,这会导致大量的copy块,GC回收时候就可以网废弃块中COPY。

每个块都有相应代数(generation count)记录它是否存活,通常,块在某处被引用,就会代数+1,GC将上次回收动作之后新分配的块进行管理,这对处理大量短命临时的对象尤其有效。

GC定期进行完整清理动作,大型对象仍然不会被复制(代数+1),内含小对象的那些块被复制,整理。JVM会监视:所有对象都很稳定,GC效率低,切换”标记-清扫“模式,同样的,JVM跟踪该模式的效果,如果发现大量碎片,重回“停止-复制”模式。

自适应技术:自适应的、分代的,停止-复制,标记-清扫式GC。

猜你喜欢

转载自flycw.iteye.com/blog/2387685