Java的GC机制

  1.Java中finalize()的做用一主要是清理那些对象(并不是使用new)得到了一块“特殊”的内存区域。程序员能够用finalize()来操做。 程序员都了解初始化的重要性,但经常会忘记一样也重要的清理工做。毕竟,谁须要清理一个int呢?但在使用程序库时,把一个对象用完后就“弃之不顾”的作法并不是老是安全的。固然,Java有垃圾回收器负责回收无用对象占据的内存资源。但也有特殊状况:假定你的对象(并不是使用new)得到了一块“特殊”的内存区域,因为垃圾回收器只知道释放那些经由new分配的内存,因此它不知道该如何释放该对象的这块“特殊”内存区域,为了应对这种状况,java容许在类中定义一个名为finalize()的方法。它的工做原理“假定”是这样的:一旦垃圾回收器准备好释放对象占用的存储孔家,将首先调用其finalize()的方法。而且在下一次垃圾回收动做发生时,才会真正回收对象占用的内存。因此要是你打算用finalize(),就能在垃圾回收时刻作一些重要的清理工做。注意这里的finalize()并非C++里的析构.在C++中,对象必定会被销毁,而在Java里的对象却并不是老是被垃圾回收(1.对象可能不被垃圾回收;2.垃圾回收并并不等于“析构”)。 java

    2.垃圾回收只与内存有关。也就是说,使用垃圾回收器的惟一缘由是为了回收程序再也不使用的内存。因此对于与垃圾回收有关的任何行为来讲(尤为是finalize()方法),它们也必须同内存及其回收有关。但这是否意味着要是对象中含有其余对象,finalize()就应该明确释放那些对象呢?不,不管对象是如何建立的,垃圾回收器都会负责释放对象占据的全部内存。这就将对finalize()的需求限制到一种特殊状况,即经过某种建立对象方式之外的方式为对象分配了存储空间。不过,java中一切皆为对象,那这种特殊状况是怎么回事呢?因为在分配内存时可能采用了相似C语言中的作法,而非java中的一般作法。这种状况主要发生在使用“本地方法”的状况下,本地方法是一种在Java中调用非Java代码的方式。在非java代码中,也许会调用C的malloc()函数系列来分配存储空间,并且除非了free()函数 程序员

    3.垃圾回收如何工做 编程

“引用记数(reference counting)”是一种简单但速度很慢的垃圾回收技术。每一个对象都含有一个引用记数器,当有引用链接至对象时,引用计数加1。当引用离开做用域或被置为null时,引用计数减1。虽然管理引用记数的开销不大,但须要在整个程序生命周期中持续地开销。垃圾回收器会在含有所有对象的列表上遍历,当发现某个对象的引用计数为0时,就释放其占用的空间。这种方法有个缺陷,若是对象之间存在循环引用,可能会出现“对象应该被回收,但引用计数却不为零”的状况。对垃圾回收器而言,定位这样存在交互引用的对象组所需的工做量极大。引用记数经常使用来讲明垃圾收集的工做方式,彷佛从未被应用于任何一种Java虚拟机实现中。 安全


  在一些更快的模式中,垃圾回收器并不是基于引用记数技术。它们依据的思想是:对任何“活”的对象,必定能最终追溯到其存活在堆栈或静态存储区之中的引用。这个引用链条可能会穿过数个对象层次。由此,若是你从堆栈和静态存储区开始,遍历全部的引用,就能找到全部“活”的对象。对于发现的每一个引用,你必须追踪它所引用的对象,而后是此对象包含的全部引用,如此反复进行,直到“根源于堆栈和静态存储区的引用”所造成的网络所有被访问为止。你所访问过的对象必须都是“活”的。注意,这就解决了“存在交互引用的总体对象”的问题,这些对象根本不会被发现,所以也就被自动回收了。 网络


  在这种方式下,Java虚拟机将采用一种“自适应”的垃圾回收技术。至于如何处理找到的存活对象,取决于不一样的Java虚拟机实现。有一种做法名为“中止——复制”(stop-and-copy)。这意味着,先暂停程序的运行,(因此它不属于后台回收模式),而后将全部存活的对象从当前堆复制到另外一个堆,没有被复制的所有都是垃圾。当对象被复制到新堆时,它们是一个挨着一个的,因此新堆保持紧凑排列,而后就能够按前述方法简单、直接地分配新空间了。 函数

  “标记——清扫”所依据的思路一样是从堆栈和静态存储区出发,遍历全部的引用,进而找出全部存活的对象。每当它找到一个存活对象,就会给对象设一个标记,这个过程当中不会回收任何对象。只有所有标记工做完成的时候,清除动做才会开始。在清处过程当中,没有标记的对象将被释放,不会发生任何复制动做。因此剩下的堆空间是不连续的,垃圾回收器要是但愿获得连续空间的话,就得从新整理剩下的对象。 优化


  “中止——复制”的意思是这种垃圾回收方式不是在后台进行的;相反,垃圾回收动做发生的同时,程序将会被暂停。在Sun 公司的文档中你会发现,许多参考文献将垃圾回收视为低优先级的后台进程,但事实上垃圾回收器并不是以这种方式实现——至少Sun公司早期版本的Java虚拟机中并不是如此。当可用内存数量较低时,Sun版中的垃圾回收器才会被激活,一样,“标记——清扫”工做也必须在程序暂停的状况下才能进行。 翻译


  如前文所述,这里讨论的Java虚拟机,内存分配单位是较大的“块”。若是对象较大,它会占用单独的块。严格来讲,“中止——复制”要求你在释放旧有对象以前,必须先把全部存活对象从旧堆复制到新堆,这将致使大量内存复制行为。有了块以后,垃圾回收器在回收的时候就能够往废弃的块里拷贝对象了。每一个块都用相应的“代数(generation count)”记录它是否还存活。一般,若是块在某处被引用,其代数会增长;垃圾回收器将对上次回收动做以后新分配的块进行整理。这对处理大量短命的临时对象颇有帮助。垃圾回收器会按期进行完整的清除动做——大型对象仍然不会被复制(只是其代数会增长),内含小型对象的那些块则被复制并整理。Java虚拟机会进行监视,若是全部对象都很稳定,垃圾回收器的效率下降的话,就切换到“标记——清扫”方式;一样, Java虚拟机会注意“标记——清扫”的效果,要是堆空间出现不少碎片,就会切换回“中止——复制”方式。这就是“自适应”技术。你能够给它个罗嗦的称呼:“自适应的、分代的、中止——复制、标记——清扫”式垃圾回收器。 对象


  Java虚拟机中有许多附加技术用以提高速度。尤为是与加载器操做有关的,被称为“即时”(Just-In-Time,JIT)编译的技术。这种技术能够把程序所有或部分翻译成本地机器码(这原本是Java虚拟机的工做),程序运行速度所以得以提高。当须要装载某个类(一般是在你为该类建立第一个对象)时,编译器会先找到其 .class 文件,而后将该类的字节码装入内存。此时,有两种方案可供选择。一种是就让即时编译器编译全部代码。但这种作法有两个缺陷:这种加载动做散落在整个程序生命周期内,累加起来要花更多时间;而且会增长可执行代码的长度(字节码要比即时编译器展开后的本地机器码小不少),这将致使页面调度,从而下降程序速度。另外一种作法称为“惰性编译(lazy uation)”,意思是即时编译器只在必要的时候才编译代码。这样,从不会被执行的代码也许就压根不会被JIT所编译。新版JDK中的Java HotSpot技术就采用了相似方法,代码每次被执行的时候都会作一些优化,因此执行的次数越多,它的速度就越快。 生命周期

参考资料:《Java编程思想第四版》,《深刻Java虚拟机》

相关文章
相关标签/搜索