zoukankan      html  css  js  c++  java
  • Java的结构之美【2】——销毁对象

    先来看一段代码:

    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

  • 相关阅读:
    功能测试-用例设计
    性能测试网址
    怎么调整磁盘分区的大小
    NIKKEI Programming Contest 2019-2 Task E. Non-triangular Triplets
    AtCoder Beginner Contest 131 Task F. Must Be Rectangular
    CF1244C The Football Season
    NIKKEI Programming Contest 2019-2 Task D. Shortest Path on a Line
    常见组合计数问题汇总
    【B2B】01-BFS
    【C++】A trick I learned:put boilerplate code into constructor of a struct
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6468885.html
Copyright © 2011-2022 走看看