??xml version="1.0" encoding="utf-8" standalone="yes"?>
GC的基本原?/strong>
Java的内存管理实际上是对象的管理,其中包括对象的分配和释放?/span>
对于E序员来_分配对象使用new关键字;释放对象Ӟ只要对象所有引用赋gؓnullQ让E序不能够再讉K到这个对象,我们U该对象?不可辄"。GC负责回收所?不可?对象的内存空间?/span>
对于GC来说Q当E序员创建对象时QGC开始监控这个对象的地址、大以及用情c通常QGC采用有向囄方式记录和管理堆(heap)中的所有对象。通过q种方式定哪些对象?可达?Q哪些对象是"不可辄"。当GC定一些对象ؓ"不可?ӞGC有责Q回收q些内存I间。但是,Z保证GC能够在不同^台实现的问题QJava规范对GC的很多行为都没有q行严格的规定。例如,对于采用什么类型的回收法、什么时候进行回收等重要问题都没有明的规定。因此,不同的JVM的实现者往往有不同的实现法。这也给JavaE序员的开发带来行多不定性。本文研I了几个与GC工作相关的问题,努力减少q种不确定性给JavaE序带来的负面媄响?
增量式GC( Incremental GC )
GC在JVM中通常是由一个或一l进E来实现的,它本w也和用L序一样占用heapI间Q运行时也占用CPU。当GCq程q行Ӟ应用E序停止q行。因此,当GCq行旉较长Ӟ用户能够感到JavaE序的停,另外一斚wQ如果GCq行旉太短Q则可能对象回收率太低,q意味着q有很多应该回收的对象没有被回收Q仍然占用大量内存。因此,在设计GC的时候,必d停顿旉和回收率之间q行权衡。一个好的GC实现允许用户定义自己所需要的讄Q例如有些内存有限有讑֤Q对内存的用量非常敏感Q希望GC能够准确的回收内存,它ƈ不在意程序速度的放慢。另外一些实时网l游戏,׃能够允许E序有长旉的中断。增量式GC是通过一定的回收法Q把一个长旉的中断,划分为很多个的中断Q通过q种方式减少GC对用L序的影响。虽Ӟ增量式GC在整体性能上可能不如普通GC的效率高Q但是它能够减少E序的最长停时间?/span>
下图pCZQ增量式GC和普通GC的比较。其中灰色部分表C线E占用CPU的时间?/span>
Sun JDK提供的HotSpot JVMp支持增量式GC。HotSpot JVM~省GC方式Z使用增量GCQؓ了启动增量GCQ我们必dq行JavaE序时增?Xincgc的参数。HotSpot JVM增量式GC的实现是采用Train GC法。它的基本想法就是,堆中的所有对象按照创建和使用情况q行分组(分层)Q将使用频繁高和h相关性的对象攑֜一队中Q随着E序的运行,不断对组q行调整。当GCq行Ӟ它L先回收最老的(最q很访问的)的对象,如果整组都ؓ可回收对象,GC整l回收。这P每次GCq行只回收一定比例的不可辑֯象,保证E序的顺畅运行。Train GC法是一个非常好的算法?br />
详解finalize函数
finalize是位于Objectcȝ一个方法,该方法的讉K修饰WؓprotectedQ由于所有类为Object的子c,因此用户cdҎ讉K到这个方法。由于,finalize函数没有自动实现铑ּ调用Q我们必L动的实现Q因此finalize函数的最后一个语句通常是super.finalize()。通过q种方式Q我们可以实C下到上实现finalize的调用,卛_释放自己的资源,然后再释攄cȝ资源?/span>
ҎJava语言规范QJVM保证调用finalize函数之前Q这个对象是不可辄Q但是JVM不保证这个函C定会被调用。另外,规范q保证finalize函数最多运行一ơ?/span>
很多Java初学者会认ؓq个ҎcM与C++中的析构函数Q将很多对象、资源的释放都放在这一函数里面。其实,q不是一U很好的方式。原因有三,其一QGCZ能够支持finalize函数Q要对覆盖这个函数的对象作很多附加的工作。其二,在finalizeq行完成之后Q该对象可能变成可达的,GCq要再检查一ơ该对象是否是可辄。因此,使用finalize会降低GC的运行性能。其三,׃GC调用finalize的时间是不确定的Q因此通过q种方式释放资源也是不确定的?/span>
通常Qfinalize用于一些不Ҏ控制、ƈ且非帔R要资源的释放Q例如一些I/O的操作,数据的连接。这些资源的释放Ҏ个应用程序是非常关键的。在q种情况下,E序员应该以通过E序本n理(包括释放)q些资源ZQ以finalize函数释放资源方式QŞ成一U双保险的管理机Ӟ而不应该仅仅依靠finalize来释放资源?/span>
下面l出一个例子说明,finalize函数被调用以后,仍然可能是可辄Q同时也可说明一个对象的finalize只可能运行一ơ?/span>
class MyObject{
Test main; //记录Test对象Q在finalize中时用于恢复可达?
public MyObject(Test t)
{
main=t; //保存Test 对象
}
protected void finalize()
{
main.ref=this;// 恢复本对象,让本对象可达
System.out.println("This is finalize");//用于试finalize只运行一?
}
}
class Test {
MyObject ref;
public static void main(String[] args) {
Test test=new Test();
test.ref=new MyObject(test);
test.ref=null; //MyObject对象Z可达对象Qfinalize被调用
System.gc();
if (test.ref!=null) System.out.println("My Objectq活着");
}
}
|
q行l果Q?
This is finalize
MyObjectq活着
此例子中Q需要注意的是虽然MyObject对象在finalize中变成可辑֯象,但是下次回收时候,finalize却不再被调用Q因为finalize函数最多只调用一ơ?/span>
Java2增强了内存管理功能, 增加了一个java.lang.ref包,其中定义了三U引用类。这三种引用cd别ؓSoftReference、WeakReference和PhantomReference。通过使用q些引用c,E序员可以在一定程度与GCq行交互Q以便改善GC的工作效率。这些引用类的引用强度介于可辑֯象和不可辑֯象之间。它们的引用强度如下图所C:
创徏一个引用对象也非常ҎQ例如如果你需要创Z个Soft Reference对象Q那么首先创Z个对象,q用普通引用方?可达对象)Q然后再创徏一个SoftReference引用该对象;最后将普通引用设|ؓnull。通过q种方式Q这个对象就只有一个Soft Reference引用。同Ӟ我们U这个对象ؓSoft Reference 对象?/span>
Soft Reference的主要特Ҏ据有较强的引用功能。只有当内存不够的时候,才进行回收这cd存,因此在内存够的时候,它们通常不被回收。另外,q些引用对象q能保证在Java抛出OutOfMemory 异常之前Q被讄为null。它可以用于实现一些常用图片的~存Q实现Cache的功能,保证最大限度的使用内存而不引vOutOfMemory。以下给U引用类型的使用伪代码;
//甌一个图像对?
Image image=new Image();//创徏Image对象
…
//使用 image
…
//使用完了imageQ将它设|ؓsoft 引用cdQƈ且释攑ּ引用Q?
SoftReference sr=new SoftReference(image);
image=null;
…
//下次使用?
if (sr!=null) image=sr.get();
else{
//׃GC׃低内存,已释放imageQ因此需要重新装载;
image=new Image();
sr=new SoftReference(image);
}
|
Weak引用对象与Soft引用对象的最大不同就在于QGC在进行回收时Q需要通过法查是否回收Soft引用对象Q而对于Weak引用对象QGCLq行回收。Weak引用对象更容易、更快被GC回收。虽ӞGC在运行时一定回收Weak对象Q但是复杂关pȝWeak对象常帔R要好几次GC的运行才能完成。Weak引用对象常常用于Mapl构中,引用数据量较大的对象Q一旦该对象的强引用为nullӞGC能够快速地回收该对象空间?
Phantom引用的用途较,主要用于辅助finalize函数的用。Phantom对象指一些对象,它们执行完了finalize函数QƈZ可达对象Q但是它们还没有被GC回收。这U对象可以辅助finalizeq行一些后期的回收工作Q我们通过覆盖Reference的clear()ҎQ增源回收机制的灉|性?br />
一些Java~码的徏?/strong>
ҎGC的工作原理,我们可以通过一些技巧和方式Q让GCq行更加有效率,更加W合应用E序的要求。以下就是一些程序设计的几点?/span>