JVM垃圾收集算法分析及选择策略

  • 格式:pdf
  • 大小:256.52 KB
  • 文档页数:4

竺三竺/

0引言JVM垃圾收集算法分析及选择策略

李振汕

(广西政法管理干部学院信息工程系,南宁530023)

摘要:JVM的垃圾收集算法对应用程序的性能有着非常重要的影响,有些时候甚至成为应用

程序的性能瓶颈,因此Java学习者有必要了解这一内部机制。阐述ⅣM垃圾收集的机

制、原理。着重分析几种常用的垃圾收集算法。给出垃圾收集算法的选择策略。

关键词:JvM;垃圾收集;收集算法

Java语言的一个非常重要的特点就是与平台的

无关性。而使用Java虚拟机(J、,M)是实现这一特点的

关键,Java虚拟机有自己想象中的硬件。例如处理器、

堆栈、寄存器等,还具有相应的指令系统。一般的高级

语言如果要在不同的平台上运行.至少需要编译成不

同的目标代码。而引入Java语言虚拟机后.Java语言

在不同平台上运行时不需要重新编译。Java语言使用

模式Java虚拟机屏蔽了与具体平台相关的信息.使

得Java语言编译程序只需生成在Java虚拟机上运行

的目标代码(字节码)。就可以在多种平台上不加修改

地运行。Java虚拟机在执行字节码时.把字节码解释

成具体平台上的机器指令执行.

1JVM的垃圾收集机制

垃圾收集是一种动态存储管理技术.自动地释放

不再被程序引用的对象.按照特定的垃圾收集算法来

实现资源自动回收的功能。垃圾收集器是Java虚拟

机的核心组成部分之一.对Java虚拟机的性能有非

常重要的影响。垃圾收集意味着程序不再需要的对象

是无用信息.这些信息将被丢弃。当一个对象不再被

引用的时候.内存回收它占领的空间。以便空间被后

来的新对象使用。除了释放没用的对象.垃圾收集也

可以清除内存记录碎片。内存记录碎片在正常的程序

执行期间产生。新的对象被分派,而且没用的对象被

释放.以致于内存的空闲部分处于被有用的对象占据

的空间之间。要分派新的对象请求空间必须有充足的

空的内存空间。在一个虚拟内存系统中.额外的分页

(或对换1需要会降低运行程序的性能。在小容量存储器的系统中,碎片会引起虚拟机用尽内存。垃圾收集

能自动释放内存空间,减轻编程的负担,这使Java虚

拟机具有一些优点。它能使编程效率提高,在没有垃

圾收集机制的时候.程序员得花许多时间来解决一个

难懂的存储器问题。

2JVM垃圾收集原理

Java语言的一个重要优点就是通过垃圾收集器

(GarbageC01lection,Gc)自动管理内存的回收,程序

员不需要手动释放内存。因此.很多程序员认为Java

不存在内存泄漏问题.或者认为即使有内存泄漏也不

是程序的责任。而是GC或J、rM的问题。其实,这种想

法是不正确的,因为Java也存在内存泄漏,只是它的

表现与C++等语言不同而已。

在Java中.内存的分配是由程序完成的。而内存

的释放则是由GC完成的.这种收支两条线的方法确

实简化了程序员的工作,但同时也加重了JvM的工

作。这也是Java程序运行速度较慢的原因之一。因

为.GC为了能够正确释放对象.GC必须监控每一个

对象的运行状态,包括对象的申请、引用、被引用、赋

值等。监视对象状态是为了更加准确、及时地释放对

象.而一个对象允许被释放的根本原则就是该对象不

再被引用。

为了更好地理解GC的工作原理.我们可以举一

个例子说明如何用有向图表示内存管理.首先来看一

段简单的代码:

Cl船8

TestGC

0bject

0l=new0bject0;

收稿日期:2009—07—21修稿日期:20019一08—25

作者简介:李振汕(1972一),男,广西天等人,硕士,副教授,研究方向为计算机应用、电子商务、网络安全

55MODERNCOMPUTER

2009.9现

-_

 万方数据现

__

v\、竺竺

Object

02=new0bject0;

02=01:

在这段代码中,我们先后创建了两个Object类

型的对象.分别用变量01和02来表示.然后将Ol

变量赋值给02.这意味着从此01和02指向同一个

对象了。

用有向图来表示主进程、01,02变量、先后创建

的两个Object类型的对象所占用的内存块,如图l

所示。

图1用有向图表示对象的创建和内存的分配

通过上面的有向图可以清晰地看到.第二次创建

的对象属于不可到达节点.这正是内存垃圾收集器回

收的目标.

Java使用有向图的方式进行内存管理.可以消除

引用循环的问题,例如有3个对象,相互引用.只要它

们和根进程是不可达的.那么GC也是可以回收它们

的。这种方式的优点是管理内存的精度很高.但是效

率较低。

从上面对Java内存管理机制的分析可知.我们

只需要确保无用对象所占用的内存块在有向图中不

可到达,Java的垃圾收集器最终都将回收这些内存,

只是时间上的问题,这也是Java程序代码看起来更

简洁高效、Java语言的生产效率更高的原因。

即使Java语言内置了非常有效的垃圾回收机

制,但也并非说Java程序便永远摆脱了内存泄漏的

阴影。我们来看下面一段代码:

VectorV=newVecto“10);

For(inti=l;i<100;i++)

Object

O=new0bjectO;

V.add(0);

0=null:

这一段代码的用途是将对象迭加到一个容器中。

程序代码的编写者虽然刻意地在每次循环末尾将O

变量置为null,但是O变量所指向的0biect对象(内

存块)却处于被vector对象v引用的状态。也就是

说,在有向图中,这些0biect对象并不是不可到达节

点,也就是说,在有向图中,这些Obiect对象并不是不

MODERNCoMPUTER

2009.9

56可到达节点。也就不属于被垃圾收集器回收的目标。

如果变量V不再有用.而且主进程运行很长时

间的话.那么上面的例子在局部上便造成了“事实上”

的内存泄漏。其实,内存泄漏是一个相对的概念.在内

存资源紧张的场合,无用的内存块没有及时回收.便

意味着系统面临内存溢出的风险。对于上面的例子.

最简单的做法是,在变量V使用完毕之后,执行“V=

mlll”即可。

3JVM垃圾收集算法和分析

根据所采取的垃圾回收策略的不同.可分为以下

几种:

(1)引用计数(ReferenceCo咖曲力算法

引用计数法是在每个被动态分配的对象中使用

额外的空间来存放引用计数值.从而来表示对该对象

的活跃引用的数量。当增加一个新的引用时.引用计

数值加1;当一个原有的引用被丢弃时。引用计数值

减l。一旦引用计数值为0.该对象就满足了垃圾收集

的条件.这时立即触发垃圾收集器对该对象占用的内

存进行回收。

引用计数算法的优点是在执行垃圾收集任务时

速度较快.它将存储管理的开销自然分摊到正常执行

中。存储回收通常无需占用大段时问.可以较好地支

持需要“实时”响应的应用。缺点是简单的引用计数可

能导致程序执行中出现无法确定时间的停顿.算法对

程序中每一次内存分配和指针操作提出了额外的要

求(增加或减少内存块的引用计数1。更重要的是,引用

计数算法无法回收循环引用的内存块。

(2)标记一清扫(Mark—Sweep,MS)算法

标记一清扫算法的基本思想是:弄清了哪些东西

有用,剩下的就是应收集的垃圾结点。其执行过程分

为标记和清扫两大阶段。在标记阶段.收集器从静态

区和栈区的所有根指针出发.沿指针链访问所有可达

结点.并在它们的特殊位置记录达到信息f如设置一

个标记位1。在清扫阶段里,收集器需要对整个动态区

做一次遍历.在遍历中将所有未加标记的结点送回自

由空间。这种分步执行的思路奠定了现代垃圾收集算

法的思想基础。与引用计数算法不同的是.标记一清扫

算法不需要运行环境监测每一次内存分配和指针操

作.而只要在标记阶段中跟踪每一个指针变量的指

向——用类似思路实现的垃圾收集器也常被统称为

跟踪收集器(-I'mcingCoⅡector)。这种方式的优点是MS

收集器可以在存储耗尽时启动.在它的工作期间要求

工作例程挂起等待。直到一遍Gc完成.实现容易。缺

点是工作例程需要等待较长时问.回收完成后造成老

 万方数据竺竺

,//

结点和新结点交错分布或同一数据结构的有关结点

的非局部化分布.造成较大的效率损失。

(3)结点复制(NodeCopyin曲算法

结点复制算法(也称为结点搬迁算法1的提出是为

了解决标记一清扫算法在垃圾收集效率方面的缺陷。

其基本思想是:将一个动态存储块里的所有结点搬

走,整个块就自由了,其中的废料也回收了。它别出心

裁地将堆空间一分为二.收集器的工作就是逐个将活

结点从一个半区搬到另一个半区.即使用简单的复制

操作来完成垃圾收集工作。结点复制算法的优点是回

收后的空间是连续的.其工作开销正比于工作空间中

活结点的数量,大幅提高了垃圾收集的效率.将原本复

杂的内存分配算法变得前所未有地简明和扼要f既然每

次内存回收都是对整个半区的回收.内存分配时也就

不用考虑内存碎片等复杂情况,只要移动堆顶指针.按

顺序分配内存就可以了1。不过,结点复制算法提高效率

的代价是人为地将可用内存空间缩小了一半.

(4)标记一缩并(Mark—Compact)算法

标记一缩并算法是标记一清扫算法和结点复制算

法的有机结合。标记一缩并算法分成两阶段:在标记阶

段,访问并标记每个活动对象;在缩并阶段.依次对标

记的对象进行移动.使之与相邻的活动对象在堆上分

布连续,自由空间也能连成一体。这样,克服了标记一

清扫法的内存碎片问题.也使生命周期长的对象趋于

集中在堆的一边.就不会像在节点复制算法中那样反

复地复制它们。

因此.标记一缩并算法把标记一清扫算法在内存占

用上的优点和结点复制算法在执行效率上的特长综

合起来,其总体执行效率高于标记一清扫算法.又不像

复制算法那样需要牺牲一半的存储空间.在许多现代

的垃圾收集器中,人们都使用了标记一整理算法或其

改进版本.

(5)增量收集(IncrementalCouectin曲算法

对实时垃圾收集算法的研究直接导致了增量收

集算法的诞生。最初.人们为了进行实时的垃圾收集,

可以设计一个多进程的运行环境.例如用一个进程执

行垃圾收集工作。另一个进程执行程序代码。这样一

来.垃圾收集工作看上去就仿佛是在后台悄悄完成

的,不会打断程序代码的运行。

增量收集算法的基础仍是传统的标记一清扫和复

制算法。增量收集算法通过对进程间冲突的妥善处

理,允许垃圾收集进程以分阶段的方式完成标记、清

理或复制工作。

(6)分代收集(GenerationalCoUectin蓟算法

研究表明,在程序设计中有这样的规律:多数对

57象存在的时间比较短.少数对象存在的时间比较长:

新生成的对象,其生存期短,生成时间越长的对象。其

生存期也越长。因此.分代收集算法通常将堆中的内

存块按寿命分为两类:年老的和年轻的。垃圾收集器

使用不同的收集算法或收集策略.分别处理这两类内

存块.并特别地把主要工作时间花在年轻的内存块上

做GC。这种算法的优点是搬动的结点少.回收的空间

多.避免了大量有用结点被反复搬来搬去的时间开

销:通过将空间划分为多个块.降低了整个动态空间

的脉动幅度.实际上也减少了总的空间需求量。分代

收集算法使垃圾收集器在有限的资源条件下.可以更

有效地工作.

4垃圾收集算法的选择策略

每种垃圾收集算法具有各自的优缺点.在实际应

用时要根据具体情况进行选择。通常,在选择垃圾收

集算法时所需考虑的因素有:

(1)垃圾收集给程序执行带来的时间开销。其中

一个是垃圾收集所花费的总时间.另一个是挂起程序

的中断时间。因垃圾收集而造成的延时是许多程序.

特别是执行关键任务的程序无法容忍的。

(2)垃圾收集时的内存占用。这一问题主要出现