1、由健忘症引发的问题
今天闲来没事在日志中瞟见了个OutOfMemoryError错误,不禁得想到前一段时间看到一篇面经里问到Java中是否有内存泄露,这个好久之前是留意过的,大致记得内存溢出和内存泄露是不一样的,至于各自都有哪些状况,那个...额....忘了...。好吧,记忆力一贯很差,忘就忘了,那就再总结一遍吧。翻了下收藏的博客,回顾了下即是想起了了~.~。看起来一切很美好,可是其中的一个例子忽然使我困惑了: java
public class TestDemo {
static Test[] tests = new Test[3];
public static void main(String[] args) {
Test t = new Test("test1");
tests[0] = t;
//将t置为null,看起来彷佛咱们已经释放建立的对象,当下次gc时其将被回收
t = null;
//那么咱们打印下test[0]看看
System.out.println(tests[0]);
}
}
这是个示例内存泄露的例子,该例子十分典型,几乎全部内存泄露的示例都与此相似,做为javaer每每以为理所应当。然而做为一个学习C++入行(学的很烂),并一直把引用当指针看的javaer难免以为有些疑惑:t是对象的引用,这里能够看作指向对象的指针,那么test[0]=t,按理说应该是把t指针赋值给test[0],算是地址传递吧,那个t指向null以后,test[0]应该也指向null了啊。
看起来彷佛有点道理,然而当了解了java的引用以后,发现吧指针等同于引用是有一些问题的。
学习
2、引用究竟是什么
java中的引用究竟是什么呢,简单点说,引用就是存在栈区的一种特定类型的数据,其存储着对象实例在堆区的地址,其特色以下:
- 自己是一种数据类型,存储在栈区
- 其值存储着实例对象在堆区的虚拟地址(注意,是虚拟地址,并非实际内存地址,就如同图书馆里的索引号,不通过转换你并不知道书的实际位置)
- 对象在建立未赋值时(无实例),引用会指向null
- java中参数传递只有值传递一种,所谓的引用传递传递的是引用中存储的值
从定义看起来彷佛仍是区分不出来引用到底和指针有什么区别,那么请注意上边红字,java为了屏蔽对内存直接操做,对对象的实际内存地址进行了包装,从而使引用中的值只能用来找对象,而没法操做内存。这一点正是和指最大不一样,C++中的指针就是一个真实的内存地址,能够经过该地址把内存玩出十八般花样。这点也说明了咱们经常把引用传递当成地址传递是错误的(虽然说实际效果差很少)。
3、上边的例子到底发生了什么
好吧,看了上边一坨也许你并看不出个什么,也许自己这块有点绕,也许我说的不清楚,那么咱们不如直接画图说明上边那个例子到底发生了什么(图示画的不必定和实际彻底一致,只为说明问题),说不定你就明白了:
tests因为是静态变量,在类加载完就已经实例化了,其在堆内存中分配了长度为3的空间,不过值都为null。在建立t以后,t指向了堆内存中的对象:
tests[0]=t,这就是咱们理解错误的地方,这一步test[0]并非指向t,而是t把Test实例的地址直接赋值给了tests[0],所以tests[0]一样指向Test实例,这和t已经没有任何关系了。
其实从上图咱们就应该理解了,t=null以后,其实只是斩断了t和Test实例的关系,并无改变Test到tests的依赖,从而gc并不会回收Test,这样就形成了逻辑上的内存泄露(为啥说逻辑上,由于明明就是你让tests还存着Test呢,只是你自觉得是的觉得释放了,固然,这种意义的泄露和C++所说的内存泄露很不一样)。
4、只为告终尾不突兀
原本是想整理篇内存溢出和内存泄露的,顺便写下这块,可是发现并非太好说清楚,就单写了。好了,就这样吧,原来JVM学习总结系列写了一半扔那了,如今发现仍是挺有意思的,有空接着写吧。