网站首页/技术开发列表/内容

对于垃圾回收的一些知识

技术开发2024-04-11阅读
为什么要进行垃圾回收?
因为JVM本身就实现内存堆里的,它不可能有象C++的自动变量(临时变量),所以所有的对象都要被会收.我们先来看一下C++:

ClassType varname;//自动变量,在内存的栈中创建,随作用域的消失而自动消失.
ClassType varname = new ClassType();//在内存堆中创建,要程序员手工释放,如:
delete varname;相当于C中的free


由于JVM本身就是在内存堆中实现,所以它不可能创建自动变量,所有的对象都有手工释放,这个\\"手工"是指要程序实现,并不一定非要程序员编程,JVM自己就实现了"常规\\"对象的释放,这就是JVM的垃圾回收器.

但对于非"常规\\"的对象比如直接或间接调用本地资源.(有人把图象擦除认为不是本地方法调用,其实它只是AWT在间接调用本地方法而已),就是程序员自己编程来释放资源.

一个对象是如何被标记垃圾(可回收对象)?以前有些实现采用"引用计数",就是一个对象在被引用的时候,引用计数加1,当引用句柄消失时引用计数减速1,当引用计数为0,就可被回收了.

那有人说刚new的对象不也被回收了吗?

一个对象的生存期分为新生代,旧生代,新生代又分为Eden 和两片生存空间其中保证有一片空间在任何时间是空的,对象刚被new的时候在Eden 中,垃圾回收器不会对Eden中对象回收,只有Eden中对象满的时候,被复制到下一片生存空间.当生存空间满的时候,才会发生一次小回收.当对象在生存空间太久达到一个\\"老化"的值时就被复制到了旧生代,旧生代满的时候就发生大回收了.

但java并不是采用引用计数来标记一个对象是否可以回收的.因为JAVA中对象很可能被直接或间接循环引用,就是对象A中引用到对象B的一个属性,而对象B又引用了A中一个属性,这样可能造成引用记数永远不会为0.

JAVA中是采用"由根遍历\\"来标记对象,就是从句柄开始对一个句柄引用的对象进行查找,然后对这个对象引用的对象再进行查找,这样递归好象从树根往沿枝条找到叶子,如果一个对象没有被这样查找到说明它已经没有对象对它引用了.

如何释放非JAVA对象?我们已经说过常规的JAVA对象会被JVM的垃圾回收器来回收,但对于本地资源(一般来说本地方法大都调用了本地资源),就要我们手工释放了.


一般来说我们可以在finalize() 中定义释放本地资源的代码,但是这个方法不是肯定会被执行的,finalize() 不是在对象退出的时候运行,而是在对象被作为垃圾回收时才调用.有可能JVM一直不需要回收,所以这个方法就一直不会被调用,同样如果要求对象在退出前一定要稍许对象,你一定要把释放对象的代码写在try{}finally{}的finally块中,这就是我再三强调释放数据数连结一定要写在这儿的原因,在java1.1中还有个方法是System.runFinalizersOnExit(),但它不如finally来得更有效.

同样System.gc()方法并不能保证垃圾回收的发生,它只是"建议\\",而垃圾回到到底什么时候发生?小回收是生存空间满,大回收是旧生代满,这只是前提.因为垃圾回收是低优先级的方式运行,只有当其他线程都挂起等待内存释放的情况出现时,它才开始释放对象的内存.

相关阅读