「轻阅读」Java垃圾回收机制整理

fg电子和mg电子平台秤

69338d0023954cd182fa882342fe6c88

java垃圾收集器仅负责回收无用对象占用的内存资源。但是如果你的对象不是由new创建的(所有新对象都填充到堆中,在一个地方,易于清理/管理资源),它将不知道是否释放对象的特殊内存。为了处理这种情况,Object附带了一个finalize()方法。

finalize()的原理是:一旦垃圾收集器准备释放对象占用的存储空间,它将首先调用其继承/重写的fialize(),并在调用该方法后,它将不会立即执行回收,而是下次(当JVM时)感觉它需要更多的内存,它会在回收动作发生时自动回收对象占用的内存。所以通常自己重写fialize()方法,在回收的最后一刻做一些重要的清理工作。

java垃圾收集的几个特性:

1.对象可能不是垃圾回收

您创建的对象会执行某些操作,例如它会显示在计算机的屏幕上。然后除非你专门处理从屏幕上删除,否则它永远不会被清除。因此,如果您在finalize()方法中执行擦除屏幕的过程,则在进行垃圾收集时,调用finalize()并清除屏幕图像。注意:垃圾收集器仅在JVM认为需要更多内存时运行(尽管开销很小,但仍然有开销),因此大多数回收操作都会在存储空间结束时用完。强制JVM运行垃圾收集器。如果程序结束(或中断),那些资源将返回给操作系统。

2,垃圾收集并不意味着解构

这是C的概念,因为java和C之间的关系太深,所以经常用来比较。简单地说,C有一个叫做析构函数的东西,它必须在对象被销毁之前执行。这里的垃圾收集并不意味着解构。 Finalize()是一个类似的函数但不相等。

3,垃圾收集只与内存有关

在这里我们将讨论finalize()的实际用法。此方法内部执行的操作也应与内存及其循环有关,因此fialize()方法不是一般方法。您可能会认为当对象包含成员对象属性时,finalize()是否应该明确地清除这些对象?不正确。应该理解,垃圾收集器将负责释放对象占用的所有内存,而不管它是否被创建。因此,finalize()通常用于通过创建对象以外?亩韵罄创矶韵蟮拇娲⒖占浞峙洹U庥械懔钊死Щ螅梦颐蔷僖桓隼印?

当java跟踪源代码时,它经常遇到关键字本机修改的方法。这些方法也称为“本地方法”。使用这些本机方法时,内部调用是非Java代码(不是C是C ++)。在这些非代码中,C的malloc()函数系列可用于分配存储空间。这样,除非调用C的free()方法,否则不会释放存储空间。因此,您可以在finalize()中以纯模式调用free。

以上,建议尽可能少地重写finalize()的原则。

件”的验证。也就是说,它被标记,物体已经死亡并且可以被回收。例如:一个对象表示一个打开的文件,程序员应该在该对象被回收之前关闭该文件。只要对象具有未正确清理的部分,程序就有隐藏的缺陷。 Fialize()可用于最终找到这种情况。

//使用finalize()来检测

对象

//未正确清理up.class Book {

Boolean checkedOut=false;

Book(boolean checkOut){

checkedOut=checkOut;

}

Void checkIn(){

checkedOut=false;

}

受保护的void finalize(){

如果(签出)

System.out.println('错误:签出');

//通常,你也会这样做:

//super.finalize(); //调用基类版本

}

}

公共类TerminationCondition {

Public static void main(String [] args){

书小说=新书(真实);

//正确清理:

novel.checkIn();

//删除引用,忘记清理:

新书(真实);

//strong制垃圾收集&最终确定:

的System.gc();

}

}/*输出:

签出错误:

* ///:~

此示例的结尾是在收集垃圾之前应检查所有Book对象。但在主要内容中,第二本书没有checkIn。此时,通过finalize(),很明显某些对象在被销毁之前不会被清理。另外,代码也使用System.gc();这是强制垃圾收集机触发BOOK的finalize();当然,如果没有强制引起这种情况,当程序运行到大量分配的内存时(可以反复大量创建BOOK),强制垃圾收集器自动触发。如果BOOK继承了父类,要触发父类的finalize(),可以使用super.finalize()调用它;

总的来说,在堆中分配新资源会比?下暇顾榷颜豢臁5导噬希琂VM在这方面做了很多优化,垃圾收集器对提高对象创建速度有显着影响。也就是说,使用垃圾收集器释放存储空间有利于分配未使用的存储空间。流行的观点是垃圾收集器回收的内存越多,创建对象的速度就越快(仍然很关键),通常认为其他语言在堆栈上创建对象,例如C)。 C的堆就像一个堆场,里面的每个对象都有自己的存储空间。有些物体在一段时间后被摧毁。它的空间必须重复使用。在某些JVM中,堆的作用类似于传送带,分配新对象,并将其向前移动一个空间。这意味着空间分配将非常快(快速寻址)。 java的寻址指针只需要移动到尚未分配的区域,因此效率可以与C在堆栈上分配的速度相当。其中,记录对象空间地址“下标”方面,仍然有一些开销,但远远小于C需要查找堆开销。事实上,java中的堆可能不完全像传送带,因为这会导致频繁的内存分页(内存被分页,当页面被翻转时,它被从硬盘中移除并放置在虚拟内存上)。页面调度会显着影响性能。最终,在创建足够的对象之后,内存(大量虚拟内存泛滥)资源耗尽。

在这里,它出现了垃圾收集器的转向。当垃圾收集器工作时,它会回收空间,同时使堆中的对象紧凑。这样,“堆指针”可以很容易地移动到传送带的开头(java堆分配空间是高级的),并尽可能地避免页面错误。垃圾收集周期重新排列对象以实现可以分配的高速无限空间(?)堆模型。

以下是垃圾收集的三种常见设计方法(超过java):

1,引用计数

每个对象都包含应用程序计数。当存在连接到对象+1的引用时,引用将离开作用域或在-1时指定null。好吧,然后当你发现一个对象的引用为0时,它释放了它占用的空间(释放空间时会有一个0)。这里有一个缺陷。如果对象被循环引用,即A指向B,而B指向A,则存在“可以回收对象,但引用计数不为0”的情况。对于垃圾收集器,定位这样的参考对象组是非常昂贵的。此外,管理引用计数的开销很小,但这将在程序的整个生命周期中继续发生。使用引用计数的方式通常用于描述垃圾收集,但它不用于任何类型的JVM。

2,停止复制

此方法首先挂起程序(不是在后台,而是停止程序,执行垃圾收集),然后将幸存的对象从当前堆复制到另一个堆,其余的是垃圾。将对象复制到新主目录(堆)时,它们会彼此相邻放置,因此新堆仍然是紧凑的。这就是为什么前面提到的JVM虚拟机的垃圾收集周期可以使对象紧凑的原因。复制过程会产生新的开销,所有对旧对象的引用都指向新地址。可能存在来自非堆(非新堆)的引用,这些引用将在遍历旧堆的引用并重定向到新堆时找到。

这种回收方法效率低下。首先,因为有两个堆,然后要在两个堆中复制和传输对象,实际保持的空间是理论上的两倍。例如,一些JVM通过在堆中分配几个大块内存来执行此操作,并且在此内存块中执行复制操作。其次,当程序运行稳定时,产生的垃圾更少,甚至可能没有垃圾。此时来回复制是非常浪费的。为了避免浪费,如果没有生成新垃圾,JVM将检查并自动转换为下一个模式。

3,标记和扫描

这是Sun早期版本的虚拟机所使用的。这种方法的想法是从堆和静态存储开始,遍历所有引用,然后查找所有幸存的对象。每当您找到幸存的对象时,请为对象提供标记并进行记录。完成所有标记后,清理开始。清理后,将释放未标记的对象,不会发生复制。此时,堆看起来有点像C,因此如果您希望其余对象是连续的,则需要重新排列其余对象。

Sun的文献认为垃圾收集是一个低优先级的后台进程,指的是停止和复制:毕竟,暂停程序。但在早期版本中,JVM使用了标记和扫描。现在这两个集合方法由JVM监视。如果对象稳定且垃圾收集器效率较低,请切换到标记和扫描。类似地,如果标记和扫描的效果不好并且堆中有大量垃圾片段(没有引用对象),它将切换到停止和复制。当使用停止和复制时,因为内存分配在一个大的“块”中,如果对象很大,它将占用整个块。在停止和复制操作停止程序运行之前,所有幸存的对象将被复制到新块(堆)。此时,旧块将被丢弃,垃圾收集器可以被丢弃。块复制新对象并灵活使用资源。每个块都有一个相应的计数来记录它是否仍然存活。如果在某处引用块,则计数将增加。垃圾收集器将在最后一次回收操作后对新分配的块进行排序。这个短命的对象非常有用。垃圾收集周期将定期执行完全清理 - 仍然不会复制大对象,并且仍将复制和组织包含小对象的那些块。

JVM还有许多其他技术可以提高速度。例如,JIT(Just-In-Time)编译器技术。这会将全部或部分程序转换为机器代码,程序运行速度会更快。当需要加载类时(创建类的第一个对象,后续创建不再加载),编译器首先找到相应的类文件,然后将字节码的内容加载到内存中。这时,有两种方法可供选择。一种是让JIT将所有代码编译成机器代码。但是由于加载是无法控制的,它在程序的整个生命周期中都是分散的,需要花费大量的时间来加起来,并且它会增加可执行代码的长度(字节码远小于扩展的机器代码)在编译JIT之后)。 ),这可能导致内存分页,从而减慢程序。第二个是惰性求值,这意味着JIT仅在必要时编译代码,因此JIT不会编译从未执行(导入但未使用)的代码。 JDK中的Java HotSpot技术使用类似的方法,并且代码在每次执行时都会进行一些优化,因此执行得越多,它就越快。

原作者:VirLB

原文链接:

abb9967aeca9427590a0434bab1a6c8c