[讨论]垃圾回收器机制--质疑Think in java

goosman 2009-07-23 10:22:47
加精
程序员都知道“初始化”的重要性,但通常忘记清除的重要性。毕竟,谁需要来清除一个int 呢?但是对于
库来说,用完后简单地“释放”一个对象并非总是安全的。当然,Java 可用垃圾收集器回收由不再使用的对
象占据的内存。现在考虑一种非常特殊且不多见的情况。假定我们的对象分配了一个“特殊”内存区域,没
有使用new。垃圾收集器只知道释放那些由new 分配的内存,所以不知道如何释放对象的“特殊”内存。为
解决这个问题,Java 提供了一个名为finalize()的方法,可为我们的类定义它。在理想情况下,它的工作原
理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),而且只有在下
一次垃圾收集过程中,才会真正回收对象的内存。所以如果使用finalize(),就可以在垃圾收集期间进行一
些重要的清除或清扫工作。


上面这段文字是Think in java 4.3 清除:收尾和垃圾收集 一章中的第一段.
主要是想看看大牛们对垃圾回收器的看法, 因此, 没有循证原版Think in java中是怎么样描述这个问题的.
当然, 这个问题也可能是由于jdk的版本引起的误差, 因此, 题目只是想吸引大家进来看看...呵呵

请注意红字标明的部分, 我们可以看出, 作者(或者是翻译的误差? 这里就这么用用吧, 呵呵)认为: 垃圾回收器在运行时, 会首先调用对象的finalize方法, 在下一次垃圾回收器运行时, 释放该对象的内存.

为了照顾各位的情绪, 我还是把问题先在这里描述一下吧, 测试代码在Garbage类中的两个System.gc()的位置, 那里也有些注释的.
* 我对jdk6.0_update_10的GC的运行机制的推测.
* 1. 收集需要回收的对象, 并以某种方式记录下来.
* 2. 调用上次记录在案的需要回收的对象的finalize方法.
* 3. 回收已经调用过finalize方法的需要回收的对象.
* 4. 将已经释放的对象从那个"记录"中移除.
* 下一次运行, 继续1-4的步骤.


我下面引入程序代码(基本上是Think in java中的源代码, 对其中有些东西做了一点小的调整, 效果比较明显一点.):
1. Chair类, 我们就是用它来模拟垃圾的.

package selfimpr.ThinkInJava.gc;

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 == 35000)
System.out.println("Created 35000");
}
protected void finalize() {
//检测gc的第一次运行,gc第一次运行时调用finalize方法会触发并输出
if(!gcrun) {
gcrun = true;
System.out.println("Beginning to finalize after " +
created + " Chairs have been ced. ");
}
//监测收集编号i值为35000的Chair对象.
if(i==35000) {
System.out.println("Finalizing Chair #35000, " + "i" + i + " " + created +
"Setting flag to stop Chair creation");
//当i值为35000的Chair的对象被垃圾收集后, 将f设置为true, 结束创建.
f = true;
}
//记录销毁对象的数目
finalized ++;
if(finalized >= created-100)
System.out.println("All " + finalized + " finalized");
}
}


2. 主类, 控制程序监测GC运行

package selfimpr.ThinkInJava.gc;

public class Garbage {

public static void main(String[] args) {
if(args.length == 0) {
System.err.println("Usage: \n" +
"java Garbage before\n or:\n" +
"java Garbage after");
return ;
}
//1. Chair.f为假时不断的创建Chair和String对象.
while(!Chair.f) {
new Chair();
new String("To take up space");
}
//结束创建活动后, 打印总共被创建了多少Chair对象.
//在这个过程中, JVM自动运行的垃圾回收回收了多少个Chair对象.
System.out.println("After all Chairs have been created:\n" +
"total created = " + Chair.created +
", total finalized = " + Chair.finalized);
if(args[0].equals("before")) {
System.out.println("gc():");
System.gc();
System.out.println("runFinalization():");
System.runFinalization();
}
//这个地方进行了改动, 下面这两行代码是新加的.
//这里我做了两次测试
/*
* 第一次, 调用一次System.gc();
* 第二次, 调用两次System.gc();
* 然而, 和Think in java的讲法不同, 我调用第一System.gc()的时候,
* 程序中没有释放的其他对象的finalize方法并没有被调用.
* 而我调用两次System.gc();所有没有被释放的对象的finalize方法都被调用了.
*
* 因此, 感觉至少在jdk6.0_update_10中, GC的运行是在第二次的时候,
* 才调用finalize方法并释放内存的.
*/
System.gc();
System.gc();

//被改动的代码结束

System.out.println("bye!");
if(args[0].equals("after"))
System.runFinalizersOnExit(true);
}

}

...全文
1205 87 打赏 收藏 转发到动态 举报
写回复
用AI写文章
87 条回复
切换为时间正序
请发表友善的回复…
发表回复
ljs_1969 2009-07-27
  • 打赏
  • 举报
回复
谢谢分享!
yepeiwen520 2009-07-27
  • 打赏
  • 举报
回复
学习 学习
wyj1983 2009-07-27
  • 打赏
  • 举报
回复

持续关注。。。
yuchui 2009-07-27
  • 打赏
  • 举报
回复
支持10楼说的,楼主好好看看10楼的
The-Venus 2009-07-27
  • 打赏
  • 举报
回复
学习了。。。。。
charles_wang8888 2009-07-27
  • 打赏
  • 举报
回复
学习,收藏
zjny520 2009-07-27
  • 打赏
  • 举报
回复
学习了哦
hiboys 2009-07-27
  • 打赏
  • 举报
回复
想理解这个问题需要有多线程的知识
你写的finalize() 是被gc线程调用的

另,java中的变量仅占用栈内存或堆内存---
new 操作符 在程序堆内存上为对象分配内存资源
其它变量(包括指向对象的引用)占用栈内存,出栈操作自动释放
不存在其它类型的内存占用情况,即使存在双方相互引用的对象gc算法也有解决之道


finalize()的典型应用是在gc前给程序员个机会,来释放必要的资源。如数据库连接
assassin5616 2009-07-27
  • 打赏
  • 举报
回复
所有的书都写着finalize方法会被调用,但实际上finalize是不会保证被调用的。这个是在JVM上测试得到的结果。
cnmmbd 2009-07-27
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 fangjiaxing 的回复:]
先顶再看
[/Quote]
yuaa88 2009-07-27
  • 打赏
  • 举报
回复
学习学习~~
kaifaye 2009-07-27
  • 打赏
  • 举报
回复
学习来了~~~~
DeepNightTwo 2009-07-26
  • 打赏
  • 举报
回复
首先,finalize方法并不推荐普通编程的时候使用。
也就是说,别指着finalize方法释放资源,这是不安全的。当然,finalize方法肯定会在对象内存被释放之前调用。除了这个之外,还有以下几点需要注意的:
1)应该是只有一个类覆盖了Object类的finalize方法后,这个方法才会被调用。否则这个方法不会被调用(这是我的猜测,因为这样可以简化垃圾回收的过程,为什么看下面)
2)当一个对象满足被回收的条件后(不准确的说也就是没有任何引用指向它),当Java进行垃圾回收的时候,如果这个对象的类没有覆盖finalize方法,那么这个对象的内存将直接被释放。否则Java将调用finalize方法,并且不释放其占用的内存。

这时候使用有了变化,最简单的情况是:
3)下次垃圾回收的时候将已经调用过finalize方法的这个对象占用的内存释放,但是不会再次调用finalize方法。

比较复杂的情况是:

4)finalize方法可以让对象复活。就是说,在finalize方法中,如果又让某个可用的引用指向这个对象(比如加到一个队列中),那么这个对象就复活了,不会被下次垃圾回收而释放。
4)finalize方法只会调用一次。当这个复活对象再次满足垃圾回收的条件后,这时候Java不会再次调用finalize方法。就是说,一个复活后的对象,在垃圾回收之前其finalize方法不会再次被调用。这就是一个潜在的漏洞,也是为什么finalize方法不值得推荐的理由。
一个对象如果复活了,那么很可能会没有释放相应的资源,但是当这个对象再次没用的时候,其finalize方法将不会被再次调用。当然,用户可以确定说在finalize方法中不会造成对象复活,但是谁能保证在编写代码的时候时刻谨记这点呢?不要越雷池半步的诀窍就是不要靠近雷池。所以finalize方法不推荐使用。
zzmoutmans 2009-07-26
  • 打赏
  • 举报
回复
回帖是一种美德!
luckygino 2009-07-26
  • 打赏
  • 举报
回复
菜鸟 路过学习
marshalle 2009-07-25
  • 打赏
  • 举报
回复
mark
joedao 2009-07-25
  • 打赏
  • 举报
回复
接分就走
jdk_mo 2009-07-25
  • 打赏
  • 举报
回复
GC,又见GC
fengmy123 2009-07-25
  • 打赏
  • 举报
回复
太深奥了,我对GC的理解是,JVM遍历内存中所有对象,如果有释放标记的就释放,没有的就判断是否需要释放,如果需要释放则标记上下次释放,没有就不鸟它
udb2008 2009-07-25
  • 打赏
  • 举报
回复
http://www.leyis.cn/soft/?33772.htm
加载更多回复(60)

62,615

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧