在第一代面向对象语言C++中,最让人头疼以及影响敏捷开发的无疑是内存的申请与回收
在程序运行时,使用享元设计使用的一些代码复用时,可能会导致对象常驻内存久之导致内存溢出,而后工程师们便萌发了自动回收机制,提出了JVM内存模型
(注:JVM并不是Java专用虚拟机的意思,而是一种内存模型规范)
JVM经过多年的不断迭代以及程序要求不断地变更,涌现出许多优秀的垃圾回收算法:
- 标记清除算法
- 标记整理算法
- 复制算法
标记清除算法
思想如名,进行的是内存对象块的标记与清除,首先进行的是内存对象块的标记,早期的JVM使用的是引用计数算法,对象被引用计算+1
释放引用计数-1,当进行标记时,JVM会去寻找引用计数为0的对象但是请看下面一段代码
public class ReferenceNumberGC {
public ReferenceNumberGC instance = null;
public static void testGC(){
ReferenceNumberGC a = new ReferenceNumberGC();
ReferenceNumberGC b = new ReferenceNumberGC();
a.instance = b;
b.instance = a;
System.gc();
}
}
这段代码可以看出a和b对象进行了相互的引用,而导致两个对象的引用计数都为1,而两个对象又未被其他线程引用,常驻内存进而导致内存泄漏。
目前JVM中标记算法采用的是根搜索算法,寻找到线程发起的root对象,再进行进行引用多叉树的遍历,便能标记出真正未被引用的对象。
言归正题标记算法将内存堆中所有未引用的对象标记完成后进行回收,但是对于这种古老的算法,效率与内存碎片是当时未解决的难题
存在内存碎片的主要问题是在于当申请大对象时,由于不存在连续的内存空间,使得虚拟机需要使用链表的方式进行不连续的内存管理,降低的内存读写的效率,以及虚拟机申请内存的速度。
复制算法
为解决效率与碎片问题,采用的复制算法,将内存区域分为两大块,当无法申请连续的空间时,便将一块内存上的对象块复制到另外一块内存中,并且连续排列,再收已被复制的对象块,但是代价是将内存可使用区域缩小了一般,只是特定场景的权益之计
标记整理算法
在JVM虚拟机中老年代内存区100%占用的极端情况,这样复制算法就不适应了,有工程师就提出了标记整理算法,当对对象进行标记时将仍然存活的对象从内存区复制到内存的一端,并将边界以外的内存回收掉 变完成了一次标记整理算法
*下一个章节将介绍JVMGC回收器的简析