六、终结处理 finalize() 与垃圾回收机制 GC
finalize() 与析构函数
C++ 中由于没有垃圾回收器,所以,每个对象需要考虑被销毁的情况,当一个对象中包含有其他对象,在销毁这个对象之前需要先将其包含的其他对象销毁,再执行这个对象的销毁动作。
这里,销毁对象内部其他对象,或者在销毁当前对象前需要做的额外工作,都可以放在一个叫做析构函数的内部。
#include<iostream> using namespace std; class App { int id; public: App(int i) { cout << "构造函数" << endl; id = i; } ~App() { cout<<"ID: "<<id<<"析构函数"<<endl; }; }; int main() { App t0(0); //栈中分配 App t1[3] {1,1,1}; //栈中分配,数组型对象 App *t2 = new App(2); //堆中分配 delete t2; App *t3 = new App[3] {3,3,3}; //堆中分配 delete []t3; cout << "main函数结束,释放所有对象内存" << endl; return 0; }
输出结果及分析:
可以发现,不管是在栈中的对象还是在堆中 new 的对象,在被销毁时均会执行析构函数,并且开发者可以精确知道什么时候销毁对象。
回到 Java 中的
finalize()函数,它的作用其实与 C++ 中的析构函数无异,同样是在对象被销毁前会执行这个函数。
问题的关键是 Java 中存在垃圾回收器,不需要开发者手动进行清除无用的对象,所以开发者并不知道垃圾回收器会在何时对这个对象进行清除(通常垃圾回收器以内存作为标准进行回收垃圾,当内存不足时,垃圾回收器便会执行清理操作,当内存充足时,即便有大量无用的对象,也不一定会执行清理操作)。
因此,我们不能以 C++ 中的析构函数的用法来使用
finalize()函数,举个反例:
假设某个对象在创建时会往屏幕上绘制图案,如果不是明确地进行擦除,它可能永远都不会被擦除。
但是如果在 finalize 函数中加入擦除的动作,那么只有等到垃圾回收器回收这个对象时,才会被擦除,只要垃圾回收器一天没有回收它,那么它画在屏幕上的东西就存在一天。
finalize 的用途
我们知道,Java 中一切都是对象,而只要是对象均可以被垃圾回收器回收,那么,
finalize()函数存在的意义是什么呢?
因为 Java 中可能存在采用 C 语言中类似的分配内存的做法,而不是用 Java 中常见的
new,这种做法通常发生于调用本地方法的时候,本地方法只支持 C/C++,因此可能存在使用
malloc()函数分配内存的方式,所以必须使用
free()函数才能释放这部分内存。
总结上面的话就是
finalize()函数通常用于释放 Java 调用 C/C++ 方法产生的对象的内存。实际开发中大部分情况不会用到它。
最后,如果希望在 Java 中进行除释放空间外的清理操作,需要明确调用某个 Java 方法(不能直接调用 finalize() 方法),这便等同于析构函数了,但却没有析构函数方便。
利用 finalize() 发现程序缺陷
下面代码中,模拟货物签收的场景,当未签收的货物被销毁时(货物都必须被签收),通过
finalize()方法可以发现这个缺陷。
class Goods{ Boolean signIn = false; Goods(Boolean signIn){ this.signIn = signIn; } void checkSignIn(){ this.signIn = true; } protected void finalize(){ if(!this.signIn){ System.out.println("ERROR:Goods is not sign in!"); } } } public static void main(String [] args){ Goods goods = new Goods(false); goods.checkSignIn();//货物签收 new Goods(true);//未签收的货物 System.gc();//强制执行垃圾回收 } /* Output: ERROR:Goods is not sign in! */
垃圾回收机制
C++ 中,在堆上分配内存的代价是很高的,因此,初学者很可能以为 Java 中在堆上分配内存代价同样很高。但是实际上,Java 中的垃圾回收器可以提高对象的创建速度。
首先来理解一下 C++ 中的堆,可以假设堆是一个院子,每个对象都在这个院子中,并且各自都管辖着自己的地盘,当某些对象被销毁后,必须要考虑这个空出来的地盘的重用,因此在堆上分配的代价主要在于对对象销毁后的地盘的重用(每次查找可用空间开销很大)。
在 Java 中的堆,更类似一个传送带,凡是有对象要被分配内存,那么传送带往前移动一格,Java 中的堆指针,则只需要移动到未分配的区域。因此效率与 C++ 中在堆栈上分配空间相当(尽管实际情况下在簿记工作方面有一定开销,但远远小于查找可用空间的开销)
此时,假设上面的堆模型中内存空间已经枯竭,那么即便“传送带”上存在空闲空间,也会导致内存耗尽的结局。
这个时候就需要垃圾回收器的介入了,它工作时,一边回收空间,一边将堆中的对象排列紧凑。
垃圾回收技术
一、引用计数
引用计数是一种简单,但速度很慢的垃圾回收技术。
原理是给每个对象一个引用计数器,当有引用连接至对象时,引用计数器加 1,当引用离开作用域或置为 null 时,引用计数器减 1,最后,垃圾回收器遍历所有对象,将引用计数器为0的对象销毁释放空间。
缺陷:当存在两个对象中均存在对方的引用,并且这两个对象无其他引用时,理论上它们都要被销毁,但它们的引用计数器不为 0。定位这种对象间存在循环引用的情况,所需工作量极大。
引用计数法常用于说明垃圾收集的工作方式。目前从未真正应用于任何一种虚拟机中。
二、引用链追溯法
这种技术的核心思想是:对于任何活的对象,一定可以追溯到堆栈或者静态区域中的引用。这个过程可能会穿过多个对象层次。
由此可知,从堆栈或者静态区域出发,遍历所有引用,就能找到所有活着的对象,当然,对于找到的对象中的其他对象的引用,同样要追溯。最后,所有访问过的对象都是活的,其他的都可以被销毁。
注意:这个方式可以完全解决交互自引用对象组的问题。
自适应垃圾回收技术
那么,该如何处理找到的这些活对象呢?
方法一:停止-复制(stop and copy),即先暂停程序(因此,它不是后台回收模式),将活对象复制到另一个堆,没有被复制的均为垃圾,在新堆中,对象与对象之间非常紧凑。
在复制到新堆后,所有指向新堆中的对象的引用都必须修正,包括堆栈或静态区域的引用,和对象内的引用(在遍历的过程中才能被找到并加以修正)。
缺陷:复制式回收器效率并不高,首先需要有两个堆,然后利用这两个堆来回倒腾。其次,程序进入稳定状态后可能产生的垃圾很少,甚至没有,但是复制式回收器依然会进行两个堆之间的复制。
方法二:标记-清扫(mark-and-sweep),这个方法同样是依据引用链追溯法扫描所有活着的对象,在扫描到活的对象时,将这个对象标记一下,扫描完成后,再将未标记的对象进行清理,这将导致堆空间是不连续的,如果希望得到连续的空间,则需要重新整理剩余对象。
这个方法一样需要暂停程序进行。
自适应垃圾回收技术就是 Java 虚拟机可以通过检查,如果没有新垃圾产生或者产生很少的垃圾,就会从停止-复制模式转换到标记-清扫模式。
最后,上面所讨论的 Java 虚拟机中,内存均是以较大的
“块”为单位的,每个块都有一个代数表示其是否存活。有了块的概念后,停止-复制方法就不必设置两个堆了,只要把存活的对象往废弃的块中复制即可。
大型的对象可能占用一整个块,对于大型对象,不会被复制,但是块的代数会增加,而内含小型对象的块则被复制并整理。
这对处理大量短命的临时对象很有帮助。
Java中其他用于提升速度的技术
即时编译技术(Just-In-Time)
这种技术可以把程序全部或部分翻译成机器码,这原本是 Java 虚拟机的工作,可以使得程序运行速度更快。
在程序运行时,首次加载一个类,首先需要找到它的 .class 字节码文件,此时可以选择让即时编译器编译所有代码,但是这样做的话,编译都分散在整个程序生命周期内,更加耗时,其次,编译后的机器码比字节码长。
惰性评估(lazy evaluation)
意思是即时编译器只会在必要时进行编译,这样一来,从不会被执行的代码可能压根不会被JIT编译。
例如 Java HotSpot 技术采用了类似的方法,代码每次执行都会进行优化,因此代码执行越多,速度越快
- Java中终结处理和垃圾回收(finalize方法介绍)
- Java终结处理和垃圾回收机制
- 垃圾回收机制(五)-System.gc()与Object.finalize()的区别
- oc中没有gc(垃圾回收机制),苹果是如何处理的?
- java 垃圾回收机制 finalize System.gc
- java: system.gc()和垃圾回收机制finalize
- Android内存优化4 了解java GC 垃圾回收机制2 GC执行finalize的过程
- JAVA gc垃圾回收机制
- 步步为营 C# 技术漫谈 四、垃圾回收机制(GC)
- JVM 垃圾回收机制与GC性能调优
- C#技术漫谈之垃圾回收机制(GC)
- 【Java】finalize()和垃圾回收机制
- JVM垃圾回收机制GC
- 成为Java GC专家(3)—如何优化Java垃圾回收机制
- C#的GC,也就是垃圾回收机制
- Java的JVM GC(Garbage Collection)垃圾回收原理机制及算法
- 成为Java GC专家(1):深入浅出Java垃圾回收机制
- java jvm gc 垃圾回收机制 总结
- Android内存优化5 了解java GC 垃圾回收机制3
- 终结处理和垃圾回收(1)