这里,我不用什么理论方式讲述垃圾回收机制原理.我只通过2个简单的程序,探讨我对Java 垃圾回收机制的理解.毕竟很多东西,理论的术语可能没有直观的测试更好.
package cn.vicky.chapt14; /** * * @author Vicky.H */ public class FinalizeTest { public static void main(String[] args) throws InterruptedException { Session session = new Session(); Object[] arr1 = new Object[5]; Object[] arr2 = new Object[10]; System.out.println(session.hashCode()); arr1[3] = session; System.out.println(arr1[3].hashCode()); arr2[7] = session; System.out.println(arr2[7].hashCode()); System.out.println("--------------"); session = null; System.out.println(arr1[3].hashCode()); System.out.println(arr2[7].hashCode()); System.gc(); System.out.println("第一次销毁session"); System.out.println("--------------"); arr1[3] = null; System.gc(); System.out.println("第二次销毁session"); System.out.println(arr2[7].hashCode()); System.out.println("--------------"); arr2[7] = null; System.gc(); System.out.println("第三次销毁session.可见,当内存中的对象在Java程序没有任何一个指向的时候," + "通过垃圾回收机制才能正在销毁对象.请注意,该条打印语句虽然执行顺序在System.gc()之后" + "被执行的,但在打印\"销毁对象\"之前已经被打印出来,表示System.gc()是异步的." + "它的执行不并影响主程序的执行,它将交与JVM虚拟机执行!"); Thread.sleep(30000); System.out.println("总结:Java无法像C或C++那样通过free() delete 销毁对象,但Java可以通过" + "取消对\"对象\"的所有引用并且调用System.gc()的方式来进行销毁."); System.out.println("Test Over"); } } class Session { int id; String name; @Override protected void finalize() throws Throwable { System.out.println("销毁对象"); super.finalize(); } }
run-single:
33263331
33263331
33263331
--------------
33263331
33263331
第一次销毁session
--------------
第二次销毁session
33263331
--------------
第三次销毁session.可见,当内存中的对象在Java程序没有任何一个指向的时候,通过垃圾回收机制才能正在销毁对象.请注意,该条打印语句虽然执行顺序在System.gc()之后被执行的,但在打印"销毁对象"之前已经被打印出来,表示System.gc()是异步的.它的执行不并影响主程序的执行,它将交与JVM虚拟机执行!
销毁对象
总结:Java无法像C或C++那样通过free() delete 销毁对象,但Java可以通过取消对"对象"的所有引用并且调用System.gc()的方式来进行销毁.
Test Over
package cn.vicky.chapt14; /** * * @author Vicky.H */ public class FinalizeTest2 { public static void sayHello1(){ System.out.println("hello world".hashCode()); } public static void sayHello2(){ System.out.println("hello world".hashCode()); System.gc(); } public static void main(String[] args) { // sayHello1(); // 多次执行该程序.打印结果都相同:1794106052 sayHello2(); // 即便调用了System.gc(),但多次执行该程序.打印结果都相同:1794106052,垃圾回收是不是没用? System.out.println("总结:简单的说,JVM 垃圾回收机制适用于通过 new 创建的对象,深入理解可以查阅\"堆\"与\"栈\"!"); } }
通过上面的例子,我们了解到,System.gc()将内存中的对象,在Java程序中没有任何引用(指针指向)的数据销毁.那么如何取消引用呢?
常规情况下,对象的引用如下
Session s; // 空指针
s = new Session() // s变为指向引用的指针
取消对new Session()的引用,可以直接将s再次变为空指针,也就是 s = null;这样JVM变清楚new Session()所分配的内存为垃圾数据,可以通过System.gc()销毁.
但通过情况下,对一个对象的引用是保存在数组或容器中,或其他对象中引用的.我们可以通过如下实例达到效果.
package cn.vicky.chapt14; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; /** * * @author Vicky.H */ public class FinalizeTest1 { public static void test1() { Player[] players = new Player[10]; Arrays.fill(players, new Player()); System.out.println("-------"); System.out.println(players.hashCode()); System.out.println(Arrays.hashCode(players)); System.out.println("-------"); for (Player player : players) { System.out.println(player.hashCode()); } System.out.println("-------"); for (int i = 0; i < players.length; i++) { players[i] = new Player(); } System.gc(); // 回收第一次创建,但没有引用的Player对象. for (Player player : players) { System.out.println(player.hashCode()); } System.out.println("-------"); System.gc(); // 尝试回收players中的所有对象.[不会被回收] System.out.println("不会被回收"); System.out.println("-------"); Arrays.fill(players, null); System.gc(); // 尝试回收players中的所有对象.[回收成功] } public static void test2() throws InterruptedException { List<Player> players = new ArrayList<Player>(); System.out.println(players.hashCode()); // 1 for (int i = 0; i < 10; i++) { players.add(new Player()); } System.out.println(players.hashCode()); System.out.println("-------"); for (Player player : players) { System.out.println(player.hashCode()); } System.out.println("-------"); System.gc(); // 回收集合中的对象失败. Arrays.fill(players.toArray(), null); System.gc(); // 回收集合中的对象失败.因为Collection.toArray() 是通过"拷贝"方式实现的. System.out.println("-------"); Thread.sleep(5000); System.out.println("清空部分数据"); // 清空部分数据 List<Player> subList = players.subList(2, 4); // 数据指向的地址与原来数据是相同的 for (Player player : subList) { System.out.println(player.hashCode()); } // players.removeAll(subList); // 不能通过这样的方式,抛出java.util.ConcurrentModificationException subList.clear(); // 通过这样的方式,能直接修改players. System.gc(); Thread.sleep(5000); System.out.println("清空所有的数据"); players.clear(); // 清空所有的数据 System.gc(); } public static void main(String[] args) throws InterruptedException { // FinalizeTest1.test1(); FinalizeTest1.test2(); } } class Player { int id; String name; @Override protected void finalize() throws Throwable { System.out.println("销毁对象"); super.finalize(); } }
package cn.vicky.chapt14; /** * * @author Vicky.H */ public class FinalizeTest3 { public static void main(String[] args) throws InterruptedException { /* A a = new A(new B()); System.gc(); System.out.println("还有引用,无法销毁!"); System.out.println("--------"); Thread.sleep(5000); a = null; System.gc(); System.out.println("没有引用,可以销毁销毁,由于B的引用是基于A的,所以,当A没有引用自动关联到B也没有引用,那么B也将会被销毁!"); System.out.println("--------"); Thread.sleep(5000); */ B b = new B(); A a1 = new A(b); A a2 = new A(b); // a1,a2同时引用b b = null; // 这里只是将b设置为空指针,但无法修改a1,a2中对已经创建的内存的指向. System.out.println(a1.b.hashCode()); System.out.println(a2.b.hashCode()); a1 = null; System.gc(); // 销毁a1.但不会销毁b对象,因为a2还保留b的引用 Thread.sleep(5000); a2 = null; System.gc(); // 销毁a2.也会销毁b对象,因为a1,a2都被销毁,b在程序中没有任何引用. } } class A { int id; String name; B b; public A() { } public A(B b) { this.b = b; } @Override protected void finalize() throws Throwable { System.out.println("销毁一个A对象"); super.finalize(); } } class B { int id; String name; @Override protected void finalize() throws Throwable { System.out.println("销毁一个B对象"); super.finalize(); } }