• 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

  • 相关阅读:
    Oracle DUL/AUL/ODU 工具说明
    Mysql 不同版本 说明
    Oracle RAC ASM disk header 备份 恢复 与 重建 示例说明
    Oracle checkpoint 说明
    Oracle RBA(Redo Byte Address) 说明
    Oracle wrap 和 unwrap( 加密与解密) 说明
    Oracle 10.2.0.5.4 Patch Set Update (PSU) – Patch No: p12419392
    Oracle RAC CSS 超时计算 及 参数 misscount, Disktimeout 说明
    Oracle 估算数据库大小的方法
    Oracle 11g 11.2.0.2 Bug 10082277 – Excessive allocation in PCUR or KGLH0 heap of “kkscsAddChildNo”
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4315457.html
Copyright © 2020-2023  润新知