Java语言具有比较独特的垃圾回收机制,相比于C语言和C++语言来说,它的垃圾回收机制更加健全,更加高效,使用更加方便。
垃圾回收机制一直是内存管理的重点内容。垃圾指的是一块没有实际用处的被占用的内存堆区域。堆是由动态内存分配器维护的某个进程的一块虚拟内存区域,分配器将堆视为一组大小不同的块的集合加以维护。但是,动态内存分配器只管分配,不管回收。因此,使用过的,没有实际用处的已分配区域就成了垃圾。我们都知道,如果我们(开发者或操作系统)长期对垃圾视而不见,那么,一旦堆被占满,内存会崩溃。不同的语言采用不同的回收策略,例如,C语言和C++语言需要编程人员自己写语句回收内存。而Java语言采用的是自动的垃圾回收机制,对于不再使用的内存,它可以自动地回收。
为了更好地理解垃圾回收机制,我们可以从两方面理解垃圾:1、从可达树的角度,我们可以把内存堆看成可达森林,在堆的外部有一个至多个根节点,堆的内部有很多节点,有些节点从外部的根节点可以到达,这样的节点称为可达节点,有些节点从外部不可到达,称为不可达节点。可达节点对应着被引用的内存空间,不可达节点对应着不可用的内存空间。我们用根节点表示引用,则可达节点可以被引用访问到。反之,不可达节点不能被引用访问到。所以,不可达节点就是垃圾。2、从Java语言本身角度,JAVA语言属于纯面向对象语言,所有成员都需要通过创建对象来访问(当然静态成员不需要人工创建,由Java虚拟机在编译时自动创建),如果我们改变了一个对象的引用,那么它就有可能成为垃圾。例如:
创建对象(假设类名为x):x obj=new x();
改变对象引用:x obj1=new x(); obj=obj1;
则老的obj对应的内存单元就失去了对它的引用,因此它就成为了垃圾。
Java的垃圾回收不需要开发人员手动编写语句进行回收,而是由Java虚拟机自动完成,Java虚拟机拥有一套自动搜索垃圾内存并进行回收的内置代码。受时钟的控制,Java虚拟机每隔一段固定的时间就会自动地对内存进行搜索,搜索不再使用的垃圾内存并将其释放进入可分配空间。事实上,这个过程的主要难点在于搜索不可用已分配内存。据此,Java根据前段所述的两种理解垃圾的机制,设计了两种算法用于搜索垃圾
1、引用计数:由第二角度分析,垃圾的产生是由于内存对象没有被引用引起的,因此我们只需要寻找没有被引用的对象即可。那么我们应该怎样寻找未被引用的对象就是一个问题。一个直观的想法是为对象增加标记引用量,标记引用量与对象被引用的次数相等。那么,我们只需要寻找标记引用量为0的对象即可
2、可达性分析:事实上,在上文中第一种分析角度中,内存块的可达与否并不是从整体上来看的。Java虚拟机可以搜索到每一个内存块,这里的可达性是指内存块对根节点的可达性。这样,垃圾回收可以有两种思路:(1)遍历根节点集合,然后对每一个根节点对应的可达树进行深度优先或广度优先搜索,得到所有可达内存块,并对可达内存块进行标记,然后按顺序遍历所有内存块,找到未被标记的内存块并删除。(2)遍历内存块,向上寻找是否可以到达根集合中的节点,寻找过程中进行标记,如果该节点与可达节点或根节点连通,则为非垃圾节点,反之为垃圾节点
标记出待回收内存块后,回收也有多种选择,在Java中存在着四种垃圾回收算法,标记清除算法、复制算法、标记整理算法以及分代回收算法。标记清除算法就是简单地回收标记清除的内存。复制算法就是将非垃圾对象复制到预留空闲内存,删除所有对象。标记整理算法在释放内存前进行移动,将所有非垃圾内存放在一起,减少外部碎片(无法被利用的未分配内存块)的产生。最广泛使用的分代收集算法实际上就是将垃圾内存分区,对不同的区采用不同的算法,规避缺陷,提升效率