对于基本类型变量来说,因为都被分配在内存栈中,因此释放不是问题,而且都会被及时地释放.但对于引用类型来说,就不一样了.
Java提供了基本的对象内存回收机制-垃圾回收器.下面看看如何工作:
首先我们必须了解一点,引用类型的变量和基本类型的变量一样,都是被分配到内存栈里的,只是引用类型的变量在栈中保存的是一个引用地址(指针),该引用地址所指的堆内存就是实际的对象存放区。
Java的垃圾回收器对于Java程序来说是外部程序,是独立运行的,因此必须有一种机制,让垃圾回收器知道哪些对象(堆中)是不再被用的,最简单的方法当然是对对象引用计数:
一个对象被一个对象变量引用,其对象引用计数就加1,如果一个对象的引用变量(在栈中)超出生存期,被释放时,对象引用计数就减1,这样,垃圾回收器就可以根据对象引用计数来判断对象是否可以被收回。看起来这种方法确实比较简单,但实际上,这种方式还是有很多问题的:
1)如果对象变量都在栈中,上述机制当然没问题。但实际上对象变量可以是另一个对象的成员,而且对象本身也可以包含其它对象类型的成员变量,这样就会形成一个复杂的引用关系网,这个引用关系网还可能会包含循环引用的情况,就会很容易导致一些对象在整个程序运行期间都不会被回收,从而产生大量的内存泄露;
2)同步问题。垃圾回收器工作时,如果程序也在工作,也在分配或操作对象,比如一个对象的引用计数虽然为0,垃圾回收器回收该对象,但同时,这个对象又被引用到一个新的对象变量,就会产生不可预知的错误。为了防止这种冲突,垃圾回收器工作时程序就必须停止工作。这就会带来另外一个问题,程序运行效率降低。
现在这种利用对象引用计数方式的垃圾回收方式已经很少单独使用。
另外一种方法是采用对象跟踪技术的方法:
垃圾回收器工作时,从程序栈内存中的引用变量开始,遍历对象引用,标记对象可达。那么不可达的对象都可以回收掉。
这种方法解决了循环引用的问题,回收效率比较高,但也同样有以下问题:
1)没有解决垃圾回收器和程序之间的同步问题,垃圾回收器工作期间程序必须停止运行;
2) 每次垃圾回收时,都需要遍历所有的对象,用时会比较长;
当然还有很多垃圾回收策略,大家可以搜一下,虽然垃圾回收对于程序员来说,大部分时间是透明的,但理解这些机制,其实也可以对我们处理实际问题时提供一种借鉴。
无论是那种回收机制,对于程序员来说,有两个地方是必须关注的:
1)我们的对象什么时候被回收(垃圾回收器运行的不可控);
2)Java产生的对象没问题,但我们自己分配的内存(主要是调用其它语言代码库,比如C++)怎么释放。
垃圾回收器工作的触发是根据其对内存使用的监控来确定的,当内存空间小于某个阀值时才触发垃圾回收。对于大部分的情况,这应该不是问题,但如果我们的对象使用了第三方稀缺资源,比如数据库链接,或者我们需要在对象释放的时候做一些清除(比如擦除在界面上的绘图)工作,我们该怎么办?一种办法当然是主动出击,比如数据库链接,打印机释放等,但有些情况下,我们还是无法主动出击的,比如一个对象使用了数据库链接,但这个对象被多个其它对象共享,你就很难主动出击释放链接了。对于这种情况,Java提供了另外一种机制,就是为每个对象提供一个finalize()方法,该方法由垃圾回收器调用(虽然你自己也可以调用,但不建议这样做,原因同无法主动出击类似)。
这样对于对象释放后需要做的清理工作就可以在这个方法中进行。但要注意垃圾回收器对finalize()方法的调用是有限制的,比如每个对象只调用一次这个方法,因此,我们必须留意对象"复活"所带来的问题。
对于什么时候我们的对象会被回收的问题,虽然我们可以调用System.gc()来建议(注意,只是建议)垃圾回收器进行回收工作,但实际上对我们来说,还是不可控的,因此:
1)对于资源,特别是稀缺资源的释放,我们还是应该尽量主动出击来完成;
2)对于调用其它语言代码库,也尽量主动出击进行对象释放,如果不行,至少也要在finalize()方法中完成;
下面的例子,是抄书本上的,用来演示垃圾回收的不确定性(我稍微做了修改):
public class Chair {
static boolean gcrun = false;
static boolean f = false;
static int created = 0;
static int finalized = 0;
int i;
Chair() {
i = ++created;
if(created == 47)
System.out.println("Created 47");
}
protected void finalize() {
if(!gcrun) {
gcrun = true;
System.out.println(
"Beginning to finalize after " +
created + " Chairs have been created=>"+i);
}
if(i == 47) {
System.out.println(
"Finalizing Chair #47, " +
"Setting flag to stop Chair creation");
f = true;
}
finalized++;
if(finalized >= created)
System.out.println("All " + finalized + " finalized");
}
}