判断对象是都还活着:
- 引用计数算法(**已经淘汰**)
- 可达性分析算法,GCRoot
最基础的算法:标记-清除
分为标记和清除两步。
就像清理垃圾,要从头开始一个一个的贴上待清除的标签,贴完了再一个一个的清理掉。
这是很自然就能想到的一种清理方式。
在生活中也是这样,看看家里哪些东西不需要了,
盘点一下,不要的直接拿到外面。
这种方式有两个大问题:**从时间和空间两个角度来看**
-- 很费时间
-- 造成内存碎片
改进:复制算法
为了解决上面 标记清除 算法存在的劣势,一种叫做 复制 的收集算法出现了。
他将可用内存容量划分为大小相等的两块,每次只使用其中一块。
当这一块内存用完了,将其中还活着的对象复制到另一块内存上面,
然后再把使用过的内存空间一次清理掉。
这样每次清理都是把 整个半区 直接清空,
给新的对象分配空间的时候也不用再考虑空间碎片的情况。
这种算法的代价是将内存缩小为了原来的一半。
实际应用:新生代采用的垃圾回收算法思想
现在的商业虚拟机都采用这种收集算法来回收新生代
新生代中的对象98%是"朝生夕死"的,并不需要按照1:1的比例来划分内存空间
实际是将内存划分为一块较大的Eden 空间和两块较小的Survivor空间
每次使用Eden和一块Survivor。
当回收时,将二者中还活着的对象一次性复制到剩下的那一块Survivor空间中。
HotSpot 虚拟机默认的Eden:Survivor:Survivor 比例是8:1:1
也就是每次新生代中可用的内存空间为整个新生代容量的80%+10%
如果剩下那一块Survivor空间没有足够的空间存放上一次新生代收集下来的存活对象
这些对象将直接通过分配担保机制进入"老年代"
改进:标记–整理算法
复制收集算法在对象存活率较高时就要进行较多的复制操作,
效率就会变低,
更关键的是,如果不想浪费50%的空间,
就需要有额外的空间进行分配担保,
以应对被使用的内存中所有对象都100% 存活的极端情况,
所以在老年代一般不能直接选用复制收集算法。
根据老年代的特点,人们提出了一种”标记--整理“算法,
标记过程和之前一样,但之后的操作不是对已标记的对象进行回收,
而是让所有存活的对象都向一端移动,然后直接清理掉端边界之外的空间。
分代收集算法
当前商业虚拟机的垃圾回收,都采用分代回收的方法。
这种算法的核心思想是根据对象存活周期的不同将内存划分为几块。
一般是把java堆分为新生代和老年代。
这样就可以根据各个年代的特点选择最合适的垃圾回收算法。
在新生代中,每次垃圾回收时都发现有大批对象死去,只有少量存活,
那就选用复制算法,只需要付出少量存活对象的复制成本就能完成垃圾回收。
而老年代中因为对象存活率高、没有额外空间对他进行分配担保,
就要看情况选择 "标记-清除" 或者 "标记-整理” 算法。