黑马程序员技术交流社区

标题: java中的垃圾回收机制 [打印本页]

作者: 吴扬    时间: 2012-6-22 14:51
标题: java中的垃圾回收机制
本帖最后由 吴扬 于 2012-6-23 00:04 编辑

问下大家java中如果一个实例对象如果没有被引用所指向的话,它到底是怎么被垃圾处理机制所回收的呢?GC这部分内容在毕老师的视频里面好像没有祥讲。
垃圾处理机制到底是如何在程序运行的过程中运行的呢?是不是它有属于自己的一个线程?望高人指点迷津,谢谢了!
作者: 孙飞    时间: 2012-6-22 15:22
本帖最后由 feigecal 于 2012-6-22 15:31 编辑

Java语言中,内存回收的任务由Java虚拟机来担当,而不是由Java程序来负责。在程序的运行时环境中,Java虚拟机提供了一个系统级的垃圾回收器
线程,它负责自动回收那些无用对象所占用的内存,这种内存回收的过程被称为垃圾回收(Garbage Collection)。

优点:
   把程序员从复杂的内存追踪、监测和释放等工作中解放出来,减轻程序员进行内存管理的负担。
   防止系统内存被非法释放,从而使系统更加健壮和稳定。
特点:
   只有当对象不再被程序中的任何引用变量引用时,它的内存才可能被回收。
   程序无法迫使垃圾回收器立即执行垃圾回收操作。
   当垃圾回收器将要回收无用对象的内存时,先调用该对象的finalize()方法,该方法有可能使对象复活,导致垃圾回收器取消回收该对象的内存。java中的对象转换:
    当一个对象(假定为Sample对象)被创建后,只要程序中还有引用变量引用它,那么它就始终处于可触及状态。
当Java虚拟机执行完所有可复活对象的finalize()方法后,假如这些方法都没有使Sample对象转到可触及状态,那么Sample对象就进入不可触及状态。只有当对象处于不可触及状态时,垃圾回收器才会真正回收它占用的内存。
                    
java中的对象转换
作者: 孙飞    时间: 2012-6-22 15:22
本帖最后由 feigecal 于 2012-6-22 15:26 编辑

Java语言中,内存回收的任务由Java虚拟机来担当,而不是由Java程序来负责。在程序的运行时环境中,Java虚拟机提供了一个系统级的垃圾回收器
线程,它负责自动回收那些无用对象所占用的内存,这种内存回收的过程被称为垃圾回收(Garbage Collection)。

优点:
   把程序员从复杂的内存追踪、监测和释放等工作中解放出来,减轻程序员进行内存管理的负担。
   防止系统内存被非法释放,从而使系统更加健壮和稳定。
特点:
   只有当对象不再被程序中的任何引用变量引用时,它的内存才可能被回收。
   程序无法迫使垃圾回收器立即执行垃圾回收操作。
   当垃圾回收器将要回收无用对象的内存时,先调用该对象的finalize()方法,该方法有可能使对象复活,导致垃圾回收器取消回收该对象的内存。java中的对象转换:
    当一个对象(假定为Sample对象)被创建后,只要程序中还有引用变量引用它,那么它就始终处于可触及状态。
当Java虚拟机执行完所有可复活对象的finalize()方法后,假如这些方法都没有使Sample对象转到可触及状态,那么Sample对象就进入不可触及状态。只有当对象处于不可触及状态时,垃圾回收器才会真正回收它占用的内存

                    java中的对象转换图

作者: oracleserver    时间: 2012-6-22 15:50
Java的堆是一个运行时数据区,类的实例(对象)从中分配空间。Java虚拟机(JVM)的堆中储存着正在运行的应用程序所建立的所有对象,这些对象通过new、newarray、anewarray和multianewarray等指令建立,但是它们不需要程序代码来显式地释放。

一般来说,堆的是由垃圾回收 来负责的,尽管JVM规范并不要求特殊的垃圾回收技术,甚至根本就不需要垃圾回收,但是由于内存的有限性,JVM在实现的时候都有一个由垃圾回收所管理的堆。垃圾回收是一种动态存储管理技术,它自动地释放不再被程序引用的对象,按照特定的垃圾收集算法来实现资源自动回收的功能。

关键字是GC

GC 调用 System.gc; Garbage collection。

每个类里面都一个finalize方法


这个方法负责回收
作者: 黑马_张佳超    时间: 2012-6-22 16:49
垃圾回收机制分为两种:垃圾回收和终结处理。
垃圾回收:自动回收内存中无用的对象。这时候java虚拟机采用一种自适应的垃圾回收技术。根据不同虚拟机实现,垃圾处理的方式也有所不同。
有一种做法是:停止-复制(stop-and-copy)。简单的描述下处理过程:先暂停程序的运行,然后将所有存活的对象从当前的堆复制到另一个堆,没有被复制的全部当做垃圾处理了。
另一种模式为:标记-清扫(mark-and-sweep)。过程:从堆栈和静态存储区出发,便利所有的引用,找到所有存活的对象。每找到一个存活对象就会给对象设一个标记,这个过程不做回收处理。当所有对象都标记完成之后,清理动作才会开始。清理过程中,没被标记的对象将被释放,不会发生任何复制动作。
终结处理:用来释放非new创建的对象占用的"特殊"的内存区域,因为垃圾回收器只知道释放那些经由new分配的内存,所以它不知道该如何释放该对象的这块“特殊”内存。这时候就需要程序员手动来处理无用的垃圾对象。
要清理一个对象,必须在需要清理的时刻调用执行清理动作的方法。这时候就需要重写finalize()方法。
何为finalize():当垃圾回收器准备要释放一个对象的内存空间的时先调用其finalize()方法。
下面的实例演示finalize()用法:
  1. class Book {
  2.         boolean checkedOut = false;
  3.         Book(boolean checkOut){
  4.                 checkedOut = checkOut;
  5.         }
  6.         void checkIn(){
  7.                 checkedOut = false;
  8.         }
  9.         protected void finalize(){
  10.                 if(checkedOut)
  11.                         System.out.println("Error:checked out");
  12.         }
  13.        
  14. }
  15. public class TerminationCondition {
  16.         public static void main(String[] args) {
  17.                 Book novel = new Book(true);
  18.                 novel.checkIn();
  19.                 new Book(true);
  20.                 System.gc();
  21.         }
  22. }
复制代码
实例演示了所有的Book对象在被当做垃圾回收前都应该被签入(checkIn())。本实例中有一个Book对象没用执行签入动作,被视为是无效对象,这时候就能被finalize()终结掉该对象。
作者: 张文强    时间: 2012-6-22 16:53
垃圾回收机制(GC)通过确定对象是否被活动对象引用来确定是否为垃圾。GC首先要判断该对象此时是否可以收集。两种常用的方法是引用计数和对象引用遍历。
引用计数:是一种简单但速度很慢的垃圾回收技术.所有对象都有一个引用计数器,当有引用连接时计数器加1,当引用离开作用域时或者被置于NULL时,计数器-1,垃圾回收器会在所以包含对象引用的列表上进行遍历,当发现某个对象的引用计数为0时,就释放占用的空间.
程序可以用System.gc() 或Runtime.getRuntime().gc()    请求垃圾回收,但并不保证立即执行垃圾回收.

垃圾回收器通常是作为一个单独的低级别的线程运行,至于什么时候进行自动垃圾回收,按照Java官方的解释,有两个时机,会进行垃圾的自动回收。
1.当平台没有其他的线程运行的时候,也就是说,现在java平台很闲,它得找点事做,这个时候,可能会执行垃圾回收操作。
2.当平台系统可用内存量过低,无法保证基本的内存空间分配操作时候,它可能会突发地执行来挽救内存资源。当然其执行与否也是不可预知的。也就是说你的纸篓已经满了,无法再向里面扔垃圾了,这时候,就需要保洁人员来处理你才能继续扔垃圾啊,但是保洁员来不来,什么时候来,你是做不了主的。
作者: 韦念欣    时间: 2012-6-22 19:14
内存中的垃圾,指的是无效但又无法自动释放的内控空间。
在Java 语言中,一般来说,没有引用句柄指向的类对象最容易成为垃圾。

产生垃圾的情况有很多,主要有以下3 种:
1、超出对象的引用句柄的作用域时,这个引用句柄引用的对象就变成垃圾。
{  
    Person p1 = new Person();
    ……
}
引用句柄p1 的作用域是从定义到“}”处,执行完这对大括号中的所有代码后,产生的Person 对象就会变成垃圾,因为引用这个对象的句柄p1 已超过其作用域,p1 已经无效,Person 对象不再被任何句柄引用了。   

2、没有超出对象的引用句柄的作用域时,给这个引用句柄赋值为空时,这个引用句柄引用的对象就变成垃圾。
{  
    Person p1 = new Person();
    …..
    p1 = null;
    ….
}
在执行完“p1=null;”后,即使句柄p1 还没有超出其作用域,仍然有效,但它已被赋值为空,不再指向任何对象,则这个Person 对象不再被任何句柄引用,变成了垃圾。此后p1 还可以指向其它Person 对象,因为还没有超出它的作
用域。

3、创建匿名对象时,匿名对象用完以后即成垃圾。
{
   new Person();            // 因为是匿名对象,没有引用句柄指向它,即为垃圾
   new Person().print(); //当运行完匿名对象的print()方法,这个对象也变成了垃圾
    ……
}
因此,在程序中应尽量少用匿名对象。


       在 Java 程序运行过程中,JVM会单独启动一个线程来运行一个垃圾回收器,它将会不定时地被唤起检查是否有不再被使用的对象,并释放它们占用的内存空间。垃圾回收器的回收无规律可循,可能在程序的运行的过程中,一次也没有启动,也可能启动很多次。因此,并不会因为程序代码一产生垃圾,垃圾回收器就马上被唤起而自动回收垃圾,很可能到程序结束时垃圾回收器都没有启动。所以垃圾回收器并不能完全避免内存泄漏的问题。
       另一方面,垃圾回收会给系统资源带来额外的负担和时空开销。它被启动的几率越小,带来的负担的几率就越小。因此,垃圾的回收策略也很重要。

       不同厂商、不同版本的Java 虚拟机中的内存垃圾回收机制并不完全一样,通常越新版本的内存回收机制越快。而不同的Java 虚拟机采用不同的回收策略,常用的有两种:复制式回收策略和自省式回收策略。
        复制式回收策略:先将正在运行中的程序暂停,然后把正在被使用的所有对象从它们所在的堆内存A 里复制到另一块堆内存B,再释放堆内存A 中的所有空间,这些不再使用的对象所占用的内存空间就会被释放掉。这种方式需要维护所需内存数量的至少两倍的内存空间,适合垃圾比较多的情况。当程序只产生了少量垃圾或者没有垃圾时,这种回收策略的效率就非常低。
        自省式回收策略:首先检测所有正在使用的对象,并为它们标注,比如用1 来标注正在使用的对象,用0 来标注不再被使用的对象,然后将所有标注为 0 的内存空间一次释放。因为标注会增大系统的开销,因此这种方式的速度仍然很慢,尤其是在垃圾比较多的情况下,效率会很低。这种方法适合垃圾比较少的情况。
        这两种方式具有互补性,因此在一些Java 虚拟机中两种方式被有机的结合运用。
作者: 吴扬    时间: 2012-6-22 23:43
谢谢大家,看了大家的回复,弄明白了!




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2