java垃圾回收机制

一、回收什么?

1、当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾

2、不是回收对象,而是回收对象所占的内存

在回收之余,也会清除内存碎片

引用:

垃圾收集器的主要功能有

(1)  定期发现那些对象不再被引用,并把这些对象占据的堆空间释放出来。

(2)  类似于操作系统的内存管理,垃圾收集器还需要处理由于对象动态生成与销毁产生的堆碎块,以便更有效的利用虚拟机内存。

二、怎么判断是垃圾

(1)引用计数法(Reference Counting Collector)

        堆中每一个对象都有一个引用计数。当新创建一个对象,或者有变量被赋值为这个对象的引用,则这个对象的引用计数加1;当一个对象的引用超过生存期或者被设置一个新的值时,这个对象的引用计数减1。当对象的引用计数变为0时,就可以被当作垃圾收集。

        这种方法的好处是垃圾收集较快,适用于实时环境。缺点是这种方法无法监测出循环引用。例如对象A引用对象B,对象B也引用对象A,则这两个对象可能无法被垃圾收集器收集。引用计数器也增加了程序执行的开销。因此这种方法是垃圾收集的早期策略,现在很少使用。 

2)跟踪法(Tracing Collector)

     这种方法把每个对象看作图中一个节点,对象之间的引用关系为图中各节点的邻接关系。垃圾收集器从一个或数个根结点遍历对象图,如果有些对象节点永远无法到达,则这个对象可以被当作垃圾回收。

     容易发现,这种方法可以检测出循环引用,避免了引用计数法的缺点,较为常用。

(在对象遍历阶段,GC必须记住哪些对象可以到达,以便删除不可到达的对象,这称为标记(marking)对象。然后,GC要删除不可到达的对象。删除时,有些GC只是简单的扫描堆栈,删除未标记的未标记的对象,并释放它们的内存以生成新的对象,这叫做清除(sweeping)。这种方法的问题在于内存会分成好多小段,而它们不足以用于新的对象,但是组合起来却很大。因此,许多GC可以重新组织内存中的对象,并进行压缩(compact),形成可利用的空间。)

三、怎么 回收---一些常用的垃圾收集器

(1)标记-清除收集器

    这种收集器首先遍历对象图并标记可到达的对象,然后扫描堆栈以寻找未标记对象并释放它们的内存。这种收集器一般使用单线程工作并停止其他操作。并且,由于它只是清除了那些未标记的对象,而并没有对标记对象进行压缩,导致会产生大量内存碎片,从而浪费内存。

(2)标记-压缩收集器

    有时也叫标记-清除-压缩收集器,与标记-清除收集器有相同的标记阶段。在第二阶段,则把标记对象复制到堆栈的新域中以便压缩堆栈。这种收集器也停止其他操作。

(3)复制收集器

    该算法的提出是为了克服句柄的开销和解决堆碎片的垃圾回收。它开始时把堆分成一个对象区和多个空闲区,程序从对象区为对象分配空间,当对象满了,基于coping算法的垃圾回收就从根集中扫描活动对象,并将每个活动对象复制到空闲区(使得活动对象所占的内存之间没有空闲间隔),这样空闲区变成了对象区,原来的对象区变成了空闲区,程序会在新的对象区中分配内存。
  一种典型的基于coping算法的垃圾回收是stop-and-copy算法,它将堆分成对象区和空闲区域区,在对象区与空闲区域的切换过程中,程序暂停执行。
这种收集器将堆栈分为两个域,常称为半空间。每次仅使用一半的空间,JVM生成的新对象则放在另一半空间中。GC运行时,它把可到达对象复制到另一半空间,从而压缩了堆栈。这种方法适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。并且对于指定大小堆来说,需要两倍大小的内存,因为任何时候都只使用其中的一半。

 (4) 增量收集器

    增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾,也可理解为把堆栈分成一小块一小块,每次仅对某一个块进行垃圾收集。这会造成较小的应用程序中断时间,使得用户一般不能觉察到垃圾收集器正在工作。

火车算法:

1)首先检查序号最小的整列火车,如果已经不存在任何引用(成熟对象区之内的&之外的)指向这列火车中任何车厢中的对象,则直接回收整列火车的内存;

2)如果1)的检查结果为false,则注意力集中到序号最小火车的序号最小的车厢上,如果整节车厢的对象都不再被引用,则直接回收;

  

3)如果序号最小的车厢中的对象还有被引用的,则进行对象转移。如果引用来自其他火车的某个车厢,则把当前对象转移到引用它的那节车厢,如果那节车厢空间不足,则增加新的车厢,并在新的车厢中存放转移来的对象。如果引用来自成熟对象空间之外,则对象被转移到其他火车。如果引用来自本火车的其他车厢,则对象被转移到火车最末尾的车厢。


4)经过3)的对象转移,还剩下的对象就可以进行垃圾回收了。

        通过上面的描述,我们会注意到,火车算法在进行垃圾回收时要扫描目标对象,确保没有对象引用指向他们,这是比较耗时的工作,因此火车算法采用了记忆集合来记录所有对一节车厢或者一列火车所有的外部引用。所有这些引用的集合称为内存块的”记忆集合”(remembered set).如果发现记忆集合为空,则说明没有引用,直接可以回收车厢或者火车。

    火车算法最大的好处是它可以保证大的循环结构可以被完全收集,因为成为垃圾的循环结构中的对象,无论多大,都会被移入同一列火车,最终一起被收集。还有一个好处是这种算法在大多数情况下可以保证一次垃圾收集所耗时间在一定限度之内,因为一次垃圾回收只收集一个车厢,而车厢的大小是有限度的。

(5)分代收集器

    复制收集器的缺点是:每次收集时,所有的标记对象都要被拷贝,从而导致一些生命周期很长的对象被来回拷贝多次,消耗大量的时间。而分代收集器则可解决这个问题,分代收集器把堆栈分为两个或多个域,用以存放不同寿命的对象。JVM生成的新对象一般放在其中的某个域中。过一段时间,继续存在的对象(非短命对象)将获得使用期并转入更长寿命的域中。分代收集器对不同的域使用不同的算法以优化性能。

具体介绍:http://jefferent.iteye.com/blog/1123677

7)并行收集器

    并行收集器使用某种传统的算法并使用多线程并行的执行它们的工作。在多CPU机器上使用多线程技术可以显著的提高java应用程序的可扩展性。

8)自适应收集器

         根据程序运行状况以及堆的使用状况,自动选一种合适的垃圾回收算法。这样可以不局限与一种垃圾回收算法。

四、Java GC实现机制

有些面向对象的程序设计语言,特别是C++,有显式的析构器方法,其中放置一些当对象不在使用时需要执行的清理代码。在析构器中,最常见的操作是回收分配给对象的存储空间。由于Java有自动的垃圾回收器,不需要人工回收内存,所以Java不支持析构器。

finalize方法在垃圾回收器 清除对象之前调用。在实际应用中,不要依赖于使用finalize方法回收任何资源,这是因为很难知道这个方法什么时候才能够调用。

在JVM垃圾回收器收集一个对象之前,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,Java提供了缺省机制来终止该对象心释放资源,这个方法就是finalize()。它的原型为:
  protected void finalize() throws Throwable
  在finalize()方法返回之后,对象消失,垃圾收集开始执行。原型中的throws Throwable表示它可以抛出任何类型的异常。
  之所以要使用finalize(),是存在着垃圾回收器不能处理的特殊情况。假定你的对象(并非使用new方法)获得了一块“特殊”的内存区域,由于垃圾回收器只知道那些显示地经由new分配的内存空间,所以它不知道该如何释放这块“特殊”的内存区域,那么这个时候java允许在类中定义一个由finalize()方法。

      特殊的区域例如:1)由于在分配内存的时候可能采用了类似 C语言的做法,而非JAVA的通常new做法。这种情况主要发生在native method中,比如native method调用了C/C++方法malloc()函数系列来分配存储空间,但是除非调用free()函数,否则这些内存空间将不会得到释放,那么这个时候就可能造成内存泄漏。但是由于free()方法是在C/C++中的函数,所以finalize()中可以用本地方法来调用它。以释放这些“特殊”的内存空间。2)又或者打开的文件资源,这些资源不属于垃圾回收器的回收范围。
      换言之,finalize()的主要用途是释放一些其他做法开辟的内存空间,以及做一些清理工作。因为在JAVA中并没有提够像“析构”函数或者类似概念的函数,要做一些类似清理工作的时候,必须自己动手创建一个执行清理工作的普通方法,也就是override Object这个类中的finalize()方法。

一旦垃圾回收器准备好释放对象占用的存储空间,首先会去调用finalize()方法进行一些必要的清理工作。只有到下一次再进行垃圾回收动作的时候,才会真正释放这个对象所占用的内存空间。

五、触发主GC(Garbage Collector)的条件

  JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。总的来说,有两个条件会触发主GC:

  1)当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。

  2)Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。

六、显式调用垃圾回收器

	public static void gc()
运行垃圾回收器。

调用 gc 方法暗示着 Java 虚拟机做了一些努力来回收未用对象,以便能够快速地重用这些对象当前占用的内存。当控制权从方法调用中返回时,虚拟机已经尽最大努力从所有丢弃的对象中回收了空间。

调用 System.gc() 实际上等效于调用:

 Runtime.getRuntime().gc()


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值