关于Java内存溢出的一些思考

来源:互联网 发布:android锁机源码 编辑:程序博客网 时间:2024/05/22 06:22


      Java中是否有内存溢出的问题?


       我们都知道,JVM中提供了GC的机制来帮助内存的管理,使得Java不必像C++般必须由程序员主动销毁对象,释放被占用空间。GC具有主动性,也可以被调用。但GC的这种主动回收内存垃圾的“主动性”如何?效率如何?

       主动性的问题:垃圾回收器“主动”回收内存垃圾的时候,必是濒临内存资源用完的时刻。为什么呢?因为垃圾回收器本身也是占用资源的“一个线程”,它的启动必然带来资源的开销,所以一般情况下,垃圾回收器不被愿意带有“主动性”。

       通过调用(System.gc()、Runtime.getRuntime().gc()):程序员主动调用垃圾回收。


       那么,在上述愿景下,是否可以防止内存溢出或泄露的问题呢?

       考虑两种情况:1.一次加载过多的对象;2.内存中存在”不可回收的“对象或内存区域。


       第一种情况呢,是可能的(是一个”out  of menory exception“的错误)。

       第二种情况呢,需要了解一些东西。


       java不像C或C++,有指针这东西。而是靠”引用“,来维持栈与内存堆中的联系(如,Object o = new Object(),在内存堆中分配了部分空间给Object对象(new Object()),然后在栈中添加该Object对象的引用o(Object o),并指向该内存空间(= )。(至于,“栈是运行时单位,而堆是存储的单位”这句话,与内存堆的回收有关。)”引用“是有分类的:强引用、软引用、弱引用、虚引用。不同的引用类型,在GC下,垃圾回收器的对待是不一致的。

      (1)强引用下,对象不可回收。

2软引用下,视乎当前内存的使用状况,相当有余则不回收。另外,在OutOfMomery的情况下,必定被回收。

3弱引用下,GC启动则回收。

4虚引用下,与弱引用类似,虚引用与弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。使用的唯一目的是:对象被回收时返回一个系统通知。

      不同的引用特性,决定了对象的使用及生命周期。所以,在当前引用对象均为强引用的情况下,及时GC启动,也无济于事。

      今天翻看《Java编程思想(第四版)》,看到这3句话(第四章第五节):

          1.对象可能不被垃圾回收。

          2.垃圾回收并不等于“析构”(C++的说法)。

          3.垃圾回收只与内存有关。

       还有一个问题:垃圾回收器只知道释放经由new分配的内存,而如果某些内存块不是由new产生,那这种“特殊”的内存区域,该如何释放呢? 

       new是java中创建对象的唯一方式。java也宣称”一切都是对象“。但,真的一切都是对象吗?基本数据类型就不是(如int、char等)。当然,为了把基本数据类型也包含进”对象“的范畴,java推出了基本数据类型的包装类(如Integer、Char等)。那么该如何对这些非new的该如何回收?要引出这一点,new出来的对象是存放在内存堆中的。那么非new的,会放置在哪呢?栈!就好像栈中存放对象的引用一般,对象的引用是对内存堆真实对象的联系维护。更真实的是,这个引用,实际上就是一个值,一个内存堆中对象的存储空间值。是个基本类型的数据?我相信是。所以说,非new的存放于栈中就很自然了。那这个栈会怎么样回收呢?其实也很简单,就是判断是否有相关“引用”指向这个常量,无则清除掉。

       另外,有说明的一点是,为什么会选取在栈中存放基本类型的数据。这样说吧,基本类型数据的可占用空间大小是固定的(如int占用4字节等),并且占用不大。而对象呢,光是纯Object对象就占用8byte,一个Integer就占用12byte...并且大多数对象的可占用大小是未知而庞大的,故必须放置在内存堆中,进行动态管理。


       真正关于垃圾回收的话题、方式也很多,不过这里就先只讨论下”溢出“的问题,感兴趣的也可以看看下面的参考资料,有话及的相关内容。该文主要在于对过往的整理,不足处望指正。


参考:《Java编程思想(第四版)》

            《JVM调优总结.pdf》——作者:和你在一起(http://pengjiaheng.javaeye.com)

           《疯狂Java:突破程序员基本功的16课》

原创粉丝点击