目录
一、理论篇
GC全称Garbage Collection,垃圾收集,是一种自动管理堆内存的机制,负责管理堆内存上对象的释放。在没有GC时,需要开发者手动管理内存,想要保证完全正确的管理内存需要开发者花费相当大的精力。所以为了让程序员把更多的精力集中在实际问题上,GC诞生了。
而GC世界里的对象,与我们常规理解的对象不同,它由头和域组成:
简单的理解,GC将原本对象使用的内存空间包装成了域,又添加了一块头的区域用来存储对象本身的信息和GC所需要的信息,不同的GC算法它使用的信息也不同。一般来说,头中的信息是无法被对象直接访问的,开发者也无需知道,仅仅是为了语言处理程序的一些操作所存在。
常见的GC算法:
1.1 引用计数
引用计数会在对象头中维护一个计数器,当对象被引用时候,计数器就会+1,当被移除引用时候,计数器就-1,在引用减少的同时判断计数器值如果为0, 表该对象是一个Garbage,就会被回收掉。
优点:
1.即刻回收。当计数为0时立刻就将垃圾回收掉了,内存空间不会被垃圾占领。很多GC算法是在内存空间不够用了以后才开始进行垃圾回收。
2.最大暂停时长短。最大暂停时长指的是因GC执行而导致其他程序暂停执行的最大时长。这里由于垃圾的回收并不是在某一个时刻统一去回收的,所以最大暂停时长短。
3.不需要去查找。很多GC算法是在内存不够时查找所有对象从而分析出哪些是可回收的垃圾对象,相比之下,引用计数无需查找,只需要在更新对象计数的同时就可以直接回收了。
缺点:
1.计数增减频繁,每次有引用更新时都伴随着计数值的增减。
2.保存计数带来的空间开销。由于要考虑到引用计数可能引用的最大数量,而每个对象都需要留有这样的空间,就降低了内存空间的使用率。
3.碎片化。对象回收之后导致已用内存和空闲内存交替存在,出现内存碎片化问题,有可能导致这些空洞区域再也无法被使用到,这种内存也叫僵尸内存。
4.分配速度慢。由于其已用内存和空闲内存交替存在,在内存分配上采用空闲列表的机制
5.循环引用。比如两个对象互相引用的情况下,两者都无法回收。每次内存分配时通过遍历空闲列表查找合适大小的内存块,和操作系统动态分区管理内存思路相似相似。
尽管引用计数法有很多问题,但是只要做些改良依然是一个很实用的GC算法,所以现在也有很多地方都在使用。
1.2 Mark&Sweep(标记清除)
Mark&Sweep和它的名字一样,分两个阶段:标记阶段和清除阶段。
标记阶段是通过一系列的GCRoot作为起始点,从这些节点开始向下搜索,搜索到的对象为存活对象,在对象头中打上标记。这里的GCRoot可以理解为指向堆内对象的引用,GC通过查找一个个引用来搜索所有存活对象,这种算法称为根搜索算法(或者可达性分析算法),而可能成为GCRoot的有全局变量、静态变量、函数参数、栈上的局部变量、寄存器中的变量。
清除阶段将存活对象的标记重置,非存活对象回收。这里的回收是把这块内存归还到一个空闲列表里。跟引用计数算法是一样的。
优点:
1.易与其他算法相结合,后面介绍的几种算法均是基于此算法。
缺点:
1.碎片化,与引用计数一样,这种方式依然会有碎片化问题。
2.分配速度慢。
1.3 Copying GC(GC复制算法)
复制算法的思路是把内存分为两个空间,这里暂时称为From空间和To空间。在为对象分配内存时,分配大小为size的内存就通过指针移动的方式移动size个距离:
一开始只在From空间分配,当From空间内存使用完以后开始执行GC操作:
首先设置一个指针在To区的起始位,然后把From区按照可达性分析算法找到存活的对象复制到To区(递归复制其子对象),每复制一个对象obj将指针移动objSize个距离,以此保证连续分配。之后再把From区内存清空,From区和To区交换,以此类推。
优点:
1.优秀的吞吐量。吞吐量意思就是单位时间内GC的处理能力,可以简单理解为效率更好的算法吞吐量更优秀。对比一下,Mark&Sweep算法的消耗是根搜索和遍历整个堆花费的时间之和,GC复制算法则是根搜索和复制存活对象。一般来说GC复制算法的吞吐量会更优秀,堆越大差距越明显。
2.高速分配。GC复制算法只需移动指针便可以进行分配了,无需每次遍历空闲列表。<