文章目录
一、什么是垃圾回收
1.1 C/C++语言的垃圾回收
1.2 Java语言的垃圾回收
二、垃圾回收的常见算法
2.1 引用计数法
2.1.1 原理
2.1.2 优缺点
2.2 标记清除法
2.2.1 原理:
2.2.2 优缺点:
2.3 标记压缩算法
2.3.1 原理:
2.3.2 优缺点:
2.4 复制算法
2.4.1 JVM中年轻代内存空间:
2.4.2 优缺点
2.4 分代算法
三、垃圾收集器以及内存分配
3.1、串行垃圾收集器
3.1.1 编写测试代码
3.1.2 设置垃圾回收为串行收集器
3.2、并行垃圾收集器
3.2.1 parNew垃圾收集器
3.2.2 ParallelGC垃圾收集器
3.3、CMS垃圾收集器
四、结束语
一、什么是垃圾回收
程序的运行必然需要申请内存资源,无效的对象资源如果不及时处理就会一直占用内存资源,最终将导致内存溢出,所以对内存资源的管理是非常重要了。
1.1 C/C++语言的垃圾回收
在C/C++语言中,没有自动垃圾回收机制,是通过new关键字申请内存资源,通过delete 关键字释放内存资源,如果程序员在某些位置没有写delete进行释放,那么申请的对象将一直占用内存资源,最终可能会导致内存溢出。
1.2 Java语言的垃圾回收
为了让程序员更专注于代码的实现,而不用过多的考虑内存释放的问题,所以在Java语言中,有了自动的垃圾回收机制,也就是我们熟悉的GC。
有了垃圾回收机制后,程序员只需要关心内存的申请即可,内存的释放由系统自动识别完成。
换句话说,自动的垃圾回收的算法会变得非常重要了,如果因为算法的不合理,导致内存一直没有释放,同样也可能会导致内存溢出的。
除了Java语言,C#、Python等语言也都有自动的垃圾回收机制。
二、垃圾回收的常见算法
自动化的管理内存资源,垃圾回收机制必须要有一套算法来进行计算,哪些是有效的对象,哪些是无效的对象,对于无效的对象就要进行回收处理。
常见的垃圾回收算法有:引用计数法、标记清除法、标记压缩法、复制算法、分代算法等。
2.1 引用计数法
引用计数是历史最悠久的一种算法,最早George E.Collins在1960的时候首次提出,50年后的今天,该算法依然被很多编程语言使用。
2.1.1 原理
假设有一个对象A,任何一个对象对A的引用,那么对象A的引用计数器+1,当引用失败时,对象A的引用计数器就-1,如果对象A的计数器的值为0,就说明对象A没有引用了,可以被回收。
如图所示:
2.1.2 优缺点
优点:
实时性较高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收。
在垃圾回收过程中,应用无需挂起,如果申请内存时,内存不足,则立刻报 outofmember 错误。
区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象。
缺点:
每次对象被引用时,都需要去更新计数器,有一点时间开销。
浪费CPU资源,即使内存够用,仍然在运行时进行计数器的统计。
无法解决循环引用问题。(最大缺点)
什么是循环引用:
class TestA{ public TestB b; } class TestB{ public TestA a; } public class TestCycle { public static void main(String[] args) { TestA a = new TestA(); TestB b = new TestB(); a.b = b; b.a = a; a = null; b = null; } }
2.2 标记清除法
标记清除算法,是将垃圾回收分为2个阶段,分别是 标记和清除
- 标记:从根节点开始标记引用的对象
- 清除:未被标记引用的对象就是垃圾对象,可以被清理
2.2.2 优缺点:
可以看到,标记清楚算法解决了引用计数算法中的循环引用的问题,没有从root节点引用的对象都会被回收。
同样标记清楚算法也是有缺点的:
效率较低,标记和清除两个动作都需要遍历所有的对象,并且在GC时,需要停止应用程序,对于交互性要求比较高的应用而言这个体验是非常差的。
通过标记清楚算法清理出来的内存,碎片化较为严重,因为被回收的对象可能存在于内存的各个角落,所以清理出来的内存时不连贯的。
2.3 标记压缩算法
标记压缩算法是在标记清除算法的基础之上,做了优化改进的算法,和标记清除算法一样,也是从根节点开始,对对象的引用进行标记,在清理阶段,并不是简单的清理未标记的对象,而是将存活的对象压缩到内存的一端,然后清理边界以外的垃圾,从而解决了碎片化的问题。
2.3.1 原理:
2.3.2 优缺点:
优缺点同标记算法,解决了标记清除算法的碎片化的问题,同时,标记压缩算法多了一步,对象移动内存位置的步骤,其效率也是有一定的影响。