这里,我不用什么理论方式讲述垃圾回收机制原理.我只通过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();
- }
- }
java中finalize()方法的使用
类的Finalize方法,可以告诉垃圾回收器应该执行的操作,该方法从Object类继承而来。在从堆中永久删除对象之前,垃圾回收器调用该对象的Finalize方法。注意,无法确切地保证垃圾回收器何时调用该方法,也无法保证调用不同对象的方法的顺序。即使一个对象包含另一个对象的引用,或者在释放一个对象很久以前就释放了另一个对象,也可能会以任意的顺序调用这两个对象的Finalize方法。如果必须保证采用特定的顺序,则必须提供自己的特有清理方法。
public class ObjectGc {
public ObjectGc() {}
public static void main(String [] args) {
ObjectGc og = new ObjectGc();
//很明显,og没有存在的意义了
og = null;
//由于垃圾回收是不定时的,我们还是手动回收看看吧
System.gc();
}
//当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
protected void finalize() {
System.out.println("我已经被垃圾回收器回收了...");
}
}