说到垃圾回收(垃圾收集,GC),很多人就会自然而然地把它和Java联系起来。在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM来处理。
顾名思义,垃圾回收就是释放垃圾占用的空间,那么在Java中,什么样的对象会被认定为“垃圾”?那么当一些对象被确定为垃圾之后,采用什么样的策略来进行回收(释放空间)?在目前的商业虚拟机中,有哪些典型的垃圾收集器?下面我们就来逐一探讨这些问题。以下是本文的目录大纲:
如何确定某个对象是“垃圾”?
典型的垃圾收集算法
典型的垃圾收集器
<强>一。如何确定某个对象是“垃圾”? 强>
在这一小节我们先了解一个最基本的问题:如果确定某个对象是“垃圾”?既然垃圾收集器的任务是回收垃圾对象所占的空间供新的对象使用,那么垃圾收集器如何确定某个对象是“垃圾”?即通过什么方法判断一个对象可以被回收了。
在java中是通过引用来和对象进行关联的,也就是说如果要操作对象,必须通过引用来进行。那么很显然一个简单的办法就是通过引用计数来判断一个对象是否可以被回收。不失一般性,如果一个对象没有任何引用与之关联,则说明该对象基本不太可能在其他地方被使用的到,那么这个对象就成为可被回收的对象了。这种方式成为。
这种方式的特点是实现简单,而且效率较高,但是它无法解决循环引用的问题,因此在Java中并没有采用这种方式(Python采用的是引用计数法)。看下面这段代码:
公开课主要{ 公共静态void main (String [] args) { MyObject中的object1=new MyObject (); MyObject object2=new MyObject (); 中的object1。对象=object2; object2。对象=中的object1; 中的object1=零; object2=零; } } 类MyObject { 公共对象对象=零; } >之前最后面两句将中的object1和object2赋值为空,也就是说中的object1和object2指向的对象已经不可能再被访问,但是由于它们互相引用对方,导致它们的引用计数都不为0,那么垃圾收集器就永远不会回收它们。
为了解决这个问题,在Java中采取了可达性分析法。该方法的基本思想是通过一系列的“GC根”对象作为起点进行搜索,如果在“GC根”和一个对象之间没有可达路径,则称该对象是不可达的,不过要注意的是被判定为不可达的对象不一定就会成为可回收对象。被判定为不可达的对象要成为可回收对象必须至少经历两次标记过程,如果在这两次标记过程中仍然没有逃脱成为可回收对象的可能性,则基本上就真的成为可回收对象了。
下面来看个例子:
对象aobj=新对象(); 对象bobj=新对象(); 对象cobj=新对象(); aobj=bobj; aobj=cobj; cobj=零; aobj=零; >之前第几行有可能会使得某个对象成为可回收对象?第7行的代码会导致有对象会成为可回收对象。至于为什么留给读者自己思考。
再看一个例子:
字符串str=新的字符串(“hello”); SoftReferencesr=new SoftReference (新的字符串(“java”)); WeakReference 或者说是=new WeakReference (新的字符串(“世界”)); >之前 这三句哪句会使得字符串对象成为可回收对象?第2句和第3句,第2句在内存不足的情况下会将字符串对象判定为可回收对象,第3句无论什么情况下字符串对象都会被判定为可回收对象。
最后总结一下平常遇到的比较常见的将对象判定为可回收对象的情况:
1)显示地将某个引用赋值为空或者将已经指向某个对象的引用指向新的对象,比如下面的代码:
对象obj=新对象(); obj=零; 其中obj1=新对象(); 对象methoda=新对象(); 其中obj1=methoda; >之前, 2)局部引用所指向的对象,比如下面这段代码:
空白有趣(){ … (int i=0; i<10;我+ +){ 对象obj=新对象(); System.out.println (obj.getClass ()); } } >之前循环每执行完一次,生成的对象对象都会成为可回收的对象。
Java垃圾回收机制简述