首页 >Java >java教程 >JVM内存管理------杂谈(借此也论一论obj=null)

JVM内存管理------杂谈(借此也论一论obj=null)

黄舟
黄舟原创
2016-12-28 15:29:041294浏览

作为一个程序猿,修炼的过程就犹如玄幻小说中的主角,不仅需要练习各种武技,内气的修炼的一样重要。虽然武技可以迅速的提升主角的实力,但是在内气太差的情况下,根本发挥不出武技的十之一二。
因此,在介绍过设计模式这一类外功之后,LZ就直接转战内气修炼,和各位猿友探讨一下JVM的内容。
本来这一章应该是介绍与GC相关的内容,不过在此之前,LZ准备先和各位探讨一下一个编程的小技巧。当然,这个小技巧其实也是与GC密切相关的。
不知道各位猿友有没看过一些JAVA内存相关的文章,里面在罗列建议的时候,经常会写出这样一条建议。
第XX条、请在使用完对象之后,显示的将对象设置为null。
原话不一定如此,但意思是一样的。话里所描述的意思,就是说我们以后写代码应该这么写。

Object obj = new Object();  
//to do something   
obj = null;

这段代码有点C/C++的风格,obj=null代替了C/C++中的delete obj或者是free(obj),相当于在提示我们,就算有了GC,我们也要像没有GC一样,使用完对象就得将其赋为空值。
首先,LZ这里要说明的是,将obj赋为空值,与C/C++中的delete其实有着天壤之别,LZ之所以说它们代替了delete和free,仅仅是因为它们出现在代码中的位置类似而已。
obj=null只做了一件事,就是将obj这个引用变量与new Object()创造的实例的关联断开,实际上,实例所占用的内存空间依然没有释放。
在提出这个建议的时候,很多博主或者图书的作者(因为有不少博主估计也是从书上看的)的本意,是想尽快消除引用与实例的关联,从而诱发GC在进行垃圾回收时,将实例所占用的内存释放。在某些时候,这么做也是为了消除内存泄露。
LZ个人觉得,许多博主或者是图书作者,提出这个建议的初衷,主要是针对一些没有接触过GC原理以及内存管理的程序猿而建议的,因为在不明白相关知识的前提下,很容易掌握不好变量的作用域,从而导致不必要的内存浪费(个人觉得这个并不是主要目的,因为只要没有发生内存泄露,内存终究是要被GC释放的),甚至是内存泄露。
所以为了安全起见,有些高人们就提出了这么一个建议。
有鉴于此,LZ个人觉得,在各位掌握了相关知识之后,完全可以忽略这个建议,而且这么做明显会降低代码的清晰度以及增加编码的负担,然而所换来的好处,却只是为了避免那根本不一定存在的内存泄露。
这里解释一下为何在有些时候不将对象赋为空值会造成内存泄露,我们考虑下面一段代码。

import java.util.Arrays;  
public class Stack {  
      
    private static final int INIT_SIZE = 10;  
    private Object[] datas;  
      
    private int size;  
    public Stack() {  
        super();  
        datas = new Object[INIT_SIZE];  
    }  
      
    public void push(Object data){  
        if (size == datas.length) {  
            extend();  
        }  
        datas[size++] = data;  
    }  
      
    public Object pop(){  
        if (size == 0) {  
            throw new IndexOutOfBoundsException("size is zero");  
        }  
        return datas[--size];  
    }  
      
    private void extend(){  
        datas = Arrays.copyOf(datas, 2 * size + 1);  
    }  
      
}

这段代码是一个简单的长度可伸缩的栈实现,在你写一段测试代码去测试它的时候,会发现它毫无问题。但是不好意思,这里面就有一个明显的“内存泄露”,这就是因为一些对象或者说引用没有设置为空值所造成的。 
如果你还没看出来,LZ稍微提示一下,各位就会意识到了。这段代码里面,数组里的对象只会无限增长,而不会随着栈中元素的弹出而减少,减小的仅仅是对外展示的栈的大小size。考虑一个极端的场景,假设你曾经往栈中放入了100万个对象,最后使用了99万9千9百9十9个,从外部看来,栈中还只剩一个可用对象了,但是我们的栈依然持有着100万个对象的引用。如果JVM可以活过来的话,想必一定会把你蹂躏到死的。
我们缺少的就是将对象赋为null值的那一步,所以pop方法应该改为下面的方式,而且各位可以去看一下ArrayList中remove方法的源码,也是类似的思路。

public Object pop(){  
        if (size == 0) {  
            throw new IndexOutOfBoundsException("size is zero");  
        }  
        Object data = datas[--size];  
        datas[size] = null;  
        return data;  
    }

这个内存泄露是非常隐蔽的,而且实际使用当中不一定就能发现,因为随着Stack对象的回收,整个数组也会被回收,到时内存泄露就被掩盖了。
所以个人觉得上述的那个建议是完全没有必要的,就算遵从了上面的建议,如果对内存管理不熟悉的话,也不会想到上面这个代码中的问题。如果想彻底的避免内存泄露的发生,机械式的主动将对象赋为空值,并不是一个可以从根本上解决问题的办法。
真正能够解决问题的办法,就是掌握好GC的策略与原理,定义一个变量时多注意变量的作用域,如此才可以更好的避免内存泄露。
所以学好GC原理还是很有必要的,希望准备走JAVA之路的朋友,尤其是从培训机构出来的猿友们(此处没有鄙视培训机构出来的猿友们的意思,因为LZ本人也是半路出家,从培训机构走上编程之路的程序猿之一),一定不要只专注于各个框架的使用以及业务的深入,虽然这些事很有必要,但并不是全部。
follow me and go the memory world 吧(语法都是浮云)。

 以上就是JVM内存管理------杂谈(借此也论一论obj=null)的内容,更多相关内容请关注PHP中文网(www.php.cn)!


声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn