先来看一段代码:
import java.util.Arrays; import java.util.EmptyStackException; /** * 2014年6月28日09:31:59 * @author 阳光小强 * */ public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITAL_CAPACITY = 15; public Stack(){ elements = new Object[DEFAULT_INITAL_CAPACITY]; } public void push(Object obj){ ensureCapacity(); elements[size++] = obj; } public Object pop(){ if(size == 0){ throw new EmptyStackException(); } return elements[--size]; } /** * 如果长度超出了默认长度则加倍 */ private void ensureCapacity(){ if(elements.length == size){ elements = Arrays.copyOf(elements, 2 * size + 1); } } }这段程序表面上看是没有任何错误的,但是它隐藏着一个“内存泄露”问题,随然每次都有pop()从栈里弹出对象,但是栈中的对象还是被引用着,所以不能够及时释放。将上面代码修改如下:
public Object pop(){ if(size == 0){ throw new EmptyStackException(); } Object result = elements[--size]; elements[size] = null; return result; }再来看一段代码:
import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; public class IOTest { public static void main(String[] args) { try { FileInputStream fis = new FileInputStream("test.xml"); InputStreamReader isr = new InputStreamReader(fis); BufferedReader br = new BufferedReader(isr); if(br.ready()){ System.out.println(br.readLine()); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }成功输出结果如下:
这段代码看似没有任何问题,但是却存在着内存泄露问题,我们没有关闭相应的资源
再来思考一个问题,代码如下:
public class IOTest { public static void main(String[] args) { IOTest test = new IOTest(); IOTest.MyThread myThread = test.new MyThread(); myThread.start(); test = null; //这个对象能释放吗? myThread = null; //线程能自动结束吗?为什么? } class MyThread extends Thread{ private int i = 0; @Override public void run() { while(true){ try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i++); } } } }
上面对象能释放吗?线程能自动结束吗?
在搞清楚上面问题之前,我们将上面代码进行修改,先看如下代码:
public class IOTest { private String data = "阳光小强"; public static void main(String[] args) { IOTest test = new IOTest(); /*IOTest.MyThread myThread = test.new MyThread(); myThread.start();*/ test = null; //这个对象能释放吗? //myThread = null; //线程能自动结束吗?为什么? System.gc(); //启动垃圾回收器 while(true){ } } class MyThread extends Thread{ private int i = 0; @Override public void run() { while(true){ try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i++); System.out.println(data); } } } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("对象销毁了"); } }在上面代码中我们重写了IOTest对象的finalize()方法,该方法是Object类的方法(protected),作用是当对象的垃圾回收器执行的时候回调该方法。
上面我们使用了System.gc()方法来通知垃圾回收器进行垃圾回收,运行的结果是输出了“对象销毁了".下面我们将上面代码中的注释去掉,启动线程后再来运行一次。
public class IOTest { private String data = "阳光小强"; public static void main(String[] args) { IOTest test = new IOTest(); IOTest.MyThread myThread = test.new MyThread(); myThread.start(); test = null; //这个对象能释放吗? myThread = null; //线程能自动结束吗?为什么? System.gc(); //启动垃圾回收器 while(true){ } } class MyThread extends Thread{ private int i = 0; @Override public void run() { while(true){ try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i++); System.out.println(data); } } } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("对象销毁了"); } }可以看到”对象销毁了“这句话没有被输出到控制台,说明我们创建的IOTest对象没有被销毁,在很多书上都会说,给一个对象赋null值,这个对象就会成为无主对象,就会被垃圾回收器回收,但是为什么这个线程和IOTest对象还是存在的?如果是这样的话,我们应该考虑如何节省我们的内存?再来看一段代码:
public class IOTest { private String data = "阳光小强"; public static void main(String[] args) { IOTest test = new IOTest(); IOTest.MyThread myThread = test.new MyThread(); myThread.start(); test = null; //这个对象能释放吗? myThread = null; //线程能自动结束吗?为什么? while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.gc(); //启动垃圾回收器 } } class MyThread extends Thread{ private int i = 0; @Override public void run() { while(i<5){ try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i++); System.out.println(data); } } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("线程对象销毁了"); } } public void testUsed(){ while(true){ } } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("外部类对象销毁了"); } }
输出结果:
从上面结果中我们可以看到,只有当线程结束后,线程对象和启动线程的对象才能真正的被垃圾回收器回收,所以在内部类中定义线程类,启动线程,会一直持有外部类的引用。现在我们又会产生一个疑问,是不是所有的内部类都持有外部类的引用呢?下面我们再来做个试验:
public class IOTest { private String data = "阳光小强"; public static void main(String[] args) { IOTest test = new IOTest(); IOTest.MyThread myThread = test.new MyThread(); //myThread.start(); test = null; //这个对象能释放吗? //myThread = null; //线程能自动结束吗?为什么? while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.gc(); //启动垃圾回收器 } } class MyThread extends Thread{ private int i = 0; @Override public void run() { while(i<5){ try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i++); System.out.println(data); } } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("线程对象销毁了"); } } public void testUsed(){ while(true){ } } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("外部类对象销毁了"); } }在上面的代码中我没有启动线程,所以此时的MyThread就可以当成一个普通的内部类了,我将外部类的引用置为空,会发现没有任何结果输出(说明外部类没有被销毁)
public class IOTest { private String data = "阳光小强"; public static void main(String[] args) { IOTest test = new IOTest(); IOTest.MyThread myThread = test.new MyThread(); //myThread.start(); test = null; //这个对象能释放吗? myThread = null; //线程能自动结束吗?为什么? while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.gc(); //启动垃圾回收器 } } class MyThread extends Thread{ private int i = 0; @Override public void run() { while(i<5){ try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i++); System.out.println(data); } } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("线程对象销毁了"); } } public void testUsed(){ while(true){ } } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("外部类对象销毁了"); } }我再将内部类的引用和外部类的引用都置为空,则输出了以下结果:
至少我可以从上面现象中这样认为,所有内部类都持有外部类的引用。这个结论还有待进一步的验证。。。
感谢你对“阳光小强"的关注,我的另一篇博文很荣幸参加了CSDN举办的博文大赛,如果你觉的小强的博文对你有帮助,请为小强投上你宝贵的一票,投票地址:http://vote.blog.csdn.net/Article/Details?articleid=30101091
Java的结构之美【2】——销毁对象,布布扣,bubuko.com
原文地址:http://blog.csdn.net/dawanganban/article/details/35550399