使用过C++的人应该知道,使用new运算符在堆上创建对象的代价是非常高的——不但创建很麻烦,管理也很麻烦。Java也是通过new运算符在堆上创建对象,但是很明显有着较高的速度。这就要归功于垃圾回收器的存在了。
C++的堆分配和Java堆分配的不同之处
C++堆的管理是由程序员来负责的,这就意味着,对于一块分配出去的堆内存回收的时候可能已经碎片化了(有的回收,有的还在用),当再次申请空间时,以前空出来的可能不足以存放新创建的对象,这就使得在程序运行过程中内存块会出现这样的“空穴”,造成不必要的内存浪费。但是这种情况在Java中得到好转——首先,垃圾回收器会通过回收算法对内存空间进行回收和整理,使得堆内存局部保持紧凑并回收部分对象内存(因为编程问题)。
垃圾回收器的“自适应”技术
可能更多的人认识“自适应”这个词是在硬件层次上,如:CPU、显卡的驱动上。但是在Java中,它也有所体现。”自适应“主要体现在对算法的动态选择上。垃圾回收器有两种基础的算法:“停止-复制”和“标记-清扫”,和一种几乎不曾实现的算法:“引用计数法”。
引用计数法
引用计数法,就是通过引用的数量对对象是否存活进行判断。当所有的引用都不存在时,即引用数为0时,即可回收。但是,对于已经失去类似遍历引用的数据结构,如:双向链表,除了节点间的互相引用,已经没有遍历引用作用在上面,这样的链表已经不可能找到了。但是引用数不为0,又不能释放。所以,这个算法是种思想,但不是很实用。
停止-复制
暂停虚拟机中的程序,通过活对象进行向上查找,一直追溯到存活在堆栈或静态数据区中的引用,以此形成“活对象森林”,将这些对象重定向到新的堆上,剩下没遍历到的就是该回收的。新的堆上的对象排列紧密,从而进一步节省空间。当然,对象的引用也要随之修正。这种情况适用于回收对象较多,产生内存碎片较多时,全面的整理。
标记-清扫
同样是暂停虚拟机中的应用,只不过“停止-复制”是从下向上,追本溯源;而“标记-清扫”是由上而下,从祖到子。由堆栈或静态数据区出发,一道查找活的对象,并将其标记,等完成后,没标记的就是该回收的。注意,这里会收完后并不对堆进行紧凑优化,所以更适合于垃圾较少,碎片较少的情况。
这样,虚拟机在进行垃圾清理时,就会根据垃圾回收器的回收效率判断该使用哪种回收方式,显然这是“自适应”的体现。
原文地址:https://www.cnblogs.com/fusiji/p/11409880.html