先来看一段代码:
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