作者:和伟
- QQ:46219905
转贴请注名作者和出处
java对象及内存的回收
提到java,很多人会将其同C++进行比较,其中一点最明显的莫过于以下几点:
java的垃圾回收GC(Garbage Collection )创建
取消类的多层继承
指针的取消
在这里,我们主要看下前两点(BTW:类的继承我会在下次有空时提到^_^)
1、GC
在C++中,内存的回收是由开发者自己来回收,而在java中,java的创始者提出了GC这一概念并将其应用到了JVM中,即开发中申请了内存后由
JVM 的GC来负责内存的回收
一种对于一般开发者的困惑就是对象和内存的混淆,接下来我们来看下这两者的区别
我们认为java同C++一样,是属于面向对象的语言,Bruce在 Thinking in Java 书中提到,在java中,一切皆为对象,这句话我认为应这样理解,其包括以下两层含义:
1>java中一切等均为继承Object而来
2>java中包括内存的分配等均使用对象来完成
在java中,我们不用直接来进行内存等的操作,这一切均是通过对象的创建和销毁来完成的,例
Object object = new Object();
object.do();
另外一个好处是,对象的完成后,JVM GC 会帮我们自己来进行对象的摧毁(Destory)来达到内存的释放
但是,注意到这里边有个问题,在阐明这个问题之前,我们首先来看下java JVM中的内存管理,进而理解和发现这个问题的焦点.
我们知道,java是移植性高的一种面向对象语言,如Sun所说,"一次编写、到处运行"的概念,基于目前操作系统等核心的不同,Sun
创建了JVM这样一种容器(我们暂且称为容器吧),用来运行java语言,这样无论在哪种操作系统上,java始终是运行在自己的JVM容器上,
运行的机制是由jvm来完成的,所以,这样一来跨平台、移植性的问题就解决了.
在JVM中,内存的一般体现在静态申请(Static)、堆(Heap)和栈(Stack)三者上,我们主要来看下后两者
内存的管理通常是由Heap和Stack来完成的,Heap是用来存放对象的实际内容,Stack则是用来存放对象的地址链接的,即我们通常所
说的对象引用
通常我们通过对象的申请new来实现的对象内容实际是存储在Heap中的,而我们对对象的引用是在Stack上进行的,看以下例子你会更明白些
Object object = new Object(); //对象的内容是存储在Heap中的,而对于对象的引用是在Stack上来完成的
...
对象在完成之后,通常GC会根据实际情况,将Stack中不再引用的对象从Heap 中收回
当需要内存申请时,JVM会通过从Heap中分配内存,并将引用保存在Stack中,当JVM发现Heap 中设置的内存不够时,会通过GC来回收,
而当GC发现Heap中内存不够时,并且无法再分配更多内存时,就会出现了常见的内存溢出(Memory leak),随之扔出OutMemory异常.
顺便提一下,目前的JVM有几种版本,但对于GC的回收无外乎通过对象的可到达或对象的累计增减来完成回收,大多数JVM采用前者来实现GC
的回收操作.
问题的焦点终于出现了,当GC发现对象引用不可到达时,可以及时进行内存的回收,但在有些时候,GC却无法实现对象的摧毁,请看以下
ObjectA a = new ObjectA();
ObjectB b = new ObjectB();
ObjectC c = new ObjectC();
//a 引用了b对象
//b引用了c
//a对象结束
这时候GC发现a引用不可到达,a对象引用随之被摧毁,但这时GC却发现b和c对象引用是可到达的,所以暂只能对a对象进行内存的回收.
... //程序经过多次反复运行后
OutMemory Error问题发生了! //很遗憾,谁都不喜欢看到这种现象(当然也包括我^_^),但是它毕竟还是出现了。
反之按照如下进行规范操作,就会避免这种现象的发生
Object a = new Object();
a.do();
a = null; //解除引用
虽然JVM有时候会帮我们完成这一步,但它毕竟不是那么万能,所以我们不能太过于依赖GC
综上所述,限于目前算法的有限性,JVM GC并非万能,有些情况下也无法对对象进行摧毁和内存的回收,我们不可过多依赖于
JVM GC的回收机制,所以在实际的程序编写中,如果一个对象完成时,我们尽量解除相关的对象引用,从而达到内存的释放,这时候既
排除了Memory leak的隐患,也减少了GC频繁运作,提高了JVM的运行效率,又何乐而不为呢?