这里,我不用什么理论方式讲述垃圾回收机制原理.我只通过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();
}
}