天天看点

读书笔记----Java垃圾回收:垃圾回收器和finalize()读书笔记—-Java垃圾回收:垃圾回收器和finalize()

读书笔记—-Java垃圾回收:垃圾回收器和finalize()

Java有一个垃圾回收器负责回收无用对象占据的内存资源。它会监视用new创建的所有对象,并将不会再引用的对象的内存资源释放(Java对象不具备和基本类型一样的生命周期,当用new创建Java对象时,它可以存活于作用域之外。例如,String s = new String(“string”),s 的生命周期会随着作用域的结束而结束,s指向的String对象仍然会占据内存空间,准确的说是堆)。但是Java垃圾回收器并没有完全避免“内存泄漏”,这就引入了finalize()方法。

finalize()方法

Java垃圾回收器负责回收无用对象占据的内存资源,但是也有例外。垃圾回收器只监视由new创建的对象,假定一个对象获得了一块特殊的内存区域,并没使用new,垃圾回收器就不知道如何释放这块特殊的内存区域。这时就需要调用finalize()方法。

这种情况主要发生在使用“本地方法”的情况下,本地方法是在Java中调用非Java代码。在这些非java代码中可能会分配而不释放存储空间(例如,在C语言中调用malloc()函数分配存储空间,而没有调用free()函数释放),从而造成内存泄漏。用《thinking in java》的话说,finalize()只能存在于程序员很难用到的一些晦涩的用法里了。

finalize()方法还有一个有趣的用法,验证对象终结条件,仅仅是有趣而已,也不常用到,感兴趣可以自行阅读《thinking in java》中对终结条件的描述以及finalize()的举例。

finalize()方法是Object类提供的,也就是说当你需要它时只需要将之重写(因为Java不提供“析构”方法,也没有相似的概念)。finalize()方法是在运行时被调用,这似乎是句废话。可是垃圾回收器回收垃圾可不是在运行时。

假设,java整个垃圾回收机制是这样的:一旦垃圾回收器准备释放无用对象的内存资源,将首先调用它们的finallize()方法,并在下一次垃圾回收动作之前才真正的释放内存资源。就不会存在“内存泄漏”了。没有选择这种机制的原因不仅仅是对象获得“特殊”内存资源几率的实在太小了,还有另一种考量。如果程序濒临结束,还有一部分资源一直没有被释放,那么随着执行结束,这些资源也会全部交还给OS。这种想法是恰当的,因为垃圾回收本身也是需要开销的,要是不用它,也算是省了233333333333333333

垃圾回收器

先奶一波垃圾回收器,其他语言在在堆上分配对象的代价是十分高昂的,Java同样是在堆上(基本数据类型在堆栈上)分配对象,开销却并没有想象中大。垃圾回收器对于对象的创建速度具有明显的提升,甚至可以媲美其他语言在堆栈上分配空间的速度。这是因为垃圾回收器在回收内存的同时,会紧凑堆中对象的排列。他对对象的重新排列实现了一种高速的,有大量(无限)空间可供分配的堆模型。而且它也会影响内存页面调度,具体是如何影响的我还不知道,要是哪位大神有空,还请指教233333333333

垃圾回收器具体是如何找到的存活对象,取决于不同JVM的实现。他们的思想是这样的:对于一个存活的对象,一定能追溯到它存活在堆栈或者静态存储区中的引用,反之如果从堆栈或者静态存储区开始,遍历所有的引用,就能找到所有存活的对象。

JVM会采用一种自适应的垃圾回收机制,自适应,顾名思义,根据情况选择最好的策略。那它都有哪些策略呢?

  • 停止-复制:显然,它会暂停程序的运行,将所有存活的对象从当前堆复制到另一个堆,没有被复制的当然就是垃圾。当然此时也不存在垃圾不垃圾了,直接清理原来的堆。当对象被复制到新堆时,自然是紧凑的。而且对象被复制到新堆之后,也要修正只向他们的引用。

    这种复制的回收方式效率不高,原因有二,其一,得有两个堆供JVM折腾,这就导致大量的内存复制行为,也得维护多一倍的内存空间。其二程序进入稳定状态后,会长生很少的垃圾,甚至不产生垃圾,要是在这么折腾就很zz,也很浪费。所以JVM会进行检查,要是不产生垃圾,就会切换到下一种模式。

  • 标记-清扫:它的思路是同样是从堆栈或者静态存储区开始,遍历所有的引用,并标记存活对象,当所有标记完成时(我不清楚以什么标准定义所有标记完成,请大神解惑),开始清理动作。它会清理未标记对象。

    它也必须是在程序暂停时才会进行。以这种策略清理之后的剩余堆空间不连续。需要垃圾回收器整理剩余对象。同样JVM会进行检查“标记-清理”的效果,如果堆空间出现很多碎片,就会切换到上一种模式。

  • 对“停止-复制”优化—代数:在有的JVM中,内存分配以较大的“块”为单位,如果对象较大,会占用单独的块。每个块都有相应的代数。如果块在某处被引用,增加其代数。在复制对象的时候,可以把对象复制到废弃块中。不复制大型对象,只增加其代数(意思就是,你占了一整块,我不复制你,你要是还被引用你就占着这整块,没被引用直接释放整块)。垃圾回收器只复制整理内涵小型对象的块。

    引用《thinking in java》中对垃圾回收器的描述:“自适应的,分代的,停止-复制,标记-清理”式垃圾回收器。

在啰嗦两句

垃圾回收不保证一定会发生,如果JVM面临内存资源耗尽的情况时,它是不会浪费时间去执行垃圾回收的。

还有看这种东西真的很枯燥啊啊啊啊啊啊啊。