自己动手写写:Java GC

为什么要使用垃圾收集器?

 

类似类型为什么要收集一样(参考http://boy00fly.iteye.com/blog/1108420)。java程序运行的过程中,越来越多的对象在堆中生成,占用的内存越来越大,如果不收集的话,到最后结果可想而知;即使堆内存足够,请求分配新对象时可能不得不增大堆空间的大小,虽然可以使用的总空闲空间是足够的,这是因为堆中没有连续的空闲空间放得下新的对象,在一个虚拟内存系统中,增长的堆所需的额外分页(或交换)空间会影响运行程序的性能。

 

使用垃圾收集器的优缺点

 

优点1. 可以提高生产率。

     程序员可以从复杂的释放内存空间的重担中解救出来。

 2. 保持程序的完整性

     垃圾收集是Java安全策略的一个重要部分,java程序员不可能因失误(或者故意)错误地释放内存而导致Java虚拟机崩溃。

 

缺点使用java垃圾收集堆,有一个潜在缺陷是它加大了程序负担,可能影响程序性能。因为Java虚拟机必须追踪哪些对象被正在执行的程序所引用,并且动态地终结并释放不再被使用的对象。和明确释放不再被使用的内存比起来,这个活动会需要更多的CPU时间。并且,在垃圾收集环境下,程序员对安排CPU时间来释放无用对象缺乏控制。

 

哪些对象是垃圾对象?

 

      首先要检测出垃圾对象。垃圾检测通常通过建立一个根对象的集合并且检查从这些根对象开始的可触及性来实现。如果正在执行的程序可以访问到的根对象和某个对象之间存在引用路径,这个对象就是可触及的。对于程序来说,根对象总是可以访问的。从这些根对象开始,任何可以被触及的对象都被认为是"活动"的对象。无法被触及的对象被认为是垃圾。

 

      下面来谈谈什么是根对象集合?

 

      Java虚拟机的根对象集合根据实现不同而不同,总体分为以下几个来源:

      1. 局部变量中的对象引用和栈帧的操作数栈(以及类变量中的对象引用)。这个总是包含的。

      2. 被加载的类的常量池中的对象引用,比如字符串。被加载的类的常量池可能指向保存在堆中的字符串,比如类名字、超类的名字、超接口的名字、字段名、字段特征签名、方法名或者方法特征签名。

      3. 传递到本地方法中的、没有被本地方法"释放"的对象引用。(本地方法接口,本地方法通过简单地返回释放引用,或者显示地调用一个回调函数来释放传递来的引用,或者是这两者的结合。)

      4. Java虚拟机运行时数据区中从垃圾收集器的堆中分配的部分。举例来说,在某些实现中,方法区中的类数据本身可能被存放在使用垃圾收集器的堆中,以便使用和释放对象。同样的垃圾收集算法来检测和卸载不再被引用的类。

 

      任何被根对象引用的对象都是可触及的,从而是活动的。另外,任何被活动的对象引用的对象都是可触及的。程序可以访问任何可触及的的对象,所以这些对象必须保留在堆里面。任何不可触及的对象都可以被收集,因为程序没有办法来访问他们。

 

       在Java虚拟机实现中,有些垃圾收集器可以区别真正的对象引用和看上去像合法对象引用的基本类型(比如一个int)之间的差别。(例如一个int整形,如果被解释是一个本地指针,可能指向堆中的一个对象)可是某些垃圾收集器仍然选择不区分真正的对象引用和"伪装品",这种垃圾收集器被称为保守的(conservative),因为他们可能不能总是释放一个不再被引用的对象。对保守的垃圾收集器,有时候垃圾对象也被错误地判断为活动的,因为有一个看上去像是对象引用的基本类型"引用"了对象。保守的收集器使垃圾收集速度提高了,因为有一些垃圾被遗忘了。

 

        区分活动对象和垃圾对象的两个基本方法是引用计数和跟踪。引用计数垃圾收集器通过为堆中的每个对象保存一个计数来区分活动对象和垃圾对象。这个计数记录下了对那个对象的引用次数。跟踪垃圾收集器实际上追踪从根节点开始的引用图。在追踪中遇上的对象以某种方式打上标记,当追踪结束时,没有被打上标记的对象就被判定是不可触及的,可以被当做垃圾收集。

 

垃圾收集算法

 

垃圾收集算法主要有以下几种

1、引用计数收集器

2、跟踪收集器

3、压缩收集器

4、拷贝收集器

5、按代收集的收集器

6、自适应收集器

7、火车算法(Sun公司的HotSpot虚拟机就是使用的此算法)

 

终结

 

在Java语言里面,一个对象可以拥有终结方法:这个方法是垃圾收集器在释放对象钱必须运行的。这个可能存在的终结方法使得任何Java虚拟机的垃圾收集器要完成的工作更加复杂。

给一个类加上终结方法,只需要简单地再类中声明一个方法:

 

class Example {
    
      protected void finalize() throws Throwable {
             //*****
             super.finalize();

      }
}

 

垃圾收集器必须检查他所发现的不再被引用的对象是否存在finalize()方法。

 

因为,存在终结方法时,Java虚拟机的垃圾收集器必须每次在收集时执行一些额外的步骤。首先,垃圾收集器必须使用某种方法检测出不再被引用的对象(称作第一遍扫描)。然后,它必须检查它检测出的不再被引用的对象是否声明了终结方法。如果时间允许的话,可能在这个时候垃圾收集过程就着手处理这些存在的终结方法。

 

当执行了所有的终结方法之后,垃圾收集器必须从根节点开始再次检测不再被引用的对象(称为第二遍扫描)。这个步骤是必要的,因为终结方法可能"复活"了某些不再被引用的对象,使他们再次被引用了。最后垃圾收集器才释放那些在第一次和第二次扫描中发现的都没有被引用的对象。

 

为了减少释放内存的时间,在扫描到某些对象拥有终结方法和运行这些终结方法之间,垃圾收集器可以有选择地插入一个步骤。一旦垃圾收集器执行了第一遍扫描,并且找到一些不再被引用的对象需要执行终结,他可以运行一次小型的跟踪,从需要执行终结的对象开始(而非从根节点开始)。任何满足如下条件的对象-------->从根节点开始不可触及(在第一遍扫描中检测出),以及从将要被终结的对象开始不可触及---------这些对象不可能在执行终结方法时复活,他们可以立即被是释放。

 

如果一个带有终结方法的对象不再被引用,并且他的终结方法运行过了,垃圾收集器必须使用某种方法记住这一点,而不能再次执行这个对象的终结方法。如果这个对象被它自己的终结方法或者其他对象的终结方法复活了,稍后再次不再被引用,垃圾收集器必须像对待一个没有终结方法的对象一样对待它。

 

使用Java编程时,必须记住一点:是垃圾收集器运行对象的终结方法。因为总是无法预测何时对象会被垃圾收集,所以也无法预测对象的终结方法何时运行。应该避免编写这样的程序,即程序的正确性依赖于对象的终结方法所运行的时机。比如,如果不再被引用的对象的终结方法释放了一个以后程序中将用到的资源,这个资源将直到垃圾收集器运行了这个对象的中终结方法后才能够使用。如果程序在垃圾收集器有机会终结这个不再被引用的对象之前需要这个资源,这个程序将无法得到该资源。

 

对象可触及性的生命周期

 

在版本1.2之前,在垃圾收集器来看,堆中的没一个对象都有三种状态之一:可触及的,可复活的,以及不可触及的。

可触及的,不可触及的这两种装载大家都清楚的。那什么状态才是”可复活的“?它在从根节点开始的追踪图中不可及,但是有可能在垃圾收集器执行某些终结方法时触及。所以说可复活状态的对象或许转化为可触及的状态,或者前进到不可触及状态。但是不可触及状态标志着不但对象不再被触及,而且也不可能通过任何终结方法复活,不可触及的对象不再对程序传递执行产生影响,可以自由地回收他们所占据的内存。

 

在后续版本中,上述三种状态扩充的新的状态,例如:在1.2版本中扩充了三个新状态:软可触及,弱可触及,以及影子可触及。这里就不再详述了。

 

 

以上内容参考《深入Java虚拟机》。

 

 

补充:以上讲的是对象的回收问题,大家有没有想过对象可以怎么创建?即类的实例化!

 以下4种是显式实例化的途径:

 1. 明确地使用new操作符

 2. 调用Class或者java.lang.reflect.COnstructor对象的newInstance()方法

 3. 调用任意对象的clone()方法

 4. 通过java.io.ObjectInputStream类的getObject()方法反序列化

 

         隐式实例化的途径:

 1. 保存命令行参数的String对象。(main函数的参数)

 2. Java虚拟机装载每一个类型,它会暗中实例化一个Class对象来代表这个类型。

 3. 执行包含字符串连接操作符的表达式产生对象。

 4. ........

 5. ........

 6. ........

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值