瓯北网站制作报价关键词排名客服
垃圾回收
- 垃圾回收
- 什么是垃圾
- 引用计数法
- 可达性分析算法
- 垃圾回收算法
- 标记清除法
- 标记复制法
- 标记整理法
- 分代
- 常用的垃圾回收器
垃圾回收
如果你学过C++,你肯定知道,我们没申请一块内存,都要自己写回收内存的方法。而Java不需要我们管理内存,因为Java有垃圾回收器。那么说到垃圾回收器,就牵扯到三个问题:
- 什么是垃圾
- 什么时候回收垃圾
- 怎么回收
什么是垃圾
首先我们要了解Java运行时内存,我们关注的点主要在堆区与方法区。 栈区的内存基本是类结构确定的时候就确定下来了。那么我们说的垃圾回收,是回收这两个区域的不再使用的对象。
那么如何确定对象是不再使用的,或者说是死对象?
引用计数法
引用计数法就是:在对象中增加一个引用计数器,每当有其他对象引用这个对象的时候,计数器就加一;当不再引用这个对象的时候,计数器减一;计数器为0的时候就代表这个对象不再被使用了。
这个方法是比较简单的方法,其实很多时候我们遇到问题,想到的最朴素的思想,有的时候效率其实也不是不错的。因为原理简单,所以效率其实也是很高的。但是这个方法其实没有在Java的实际的垃圾回收器中大范围使用,主要原因是,引用计数法解决不了循环引用的问题。
循环引用就是A对象引用B对象,B对象也引用A对象,两个对象互相持有对方的实例引用。
可达性分析算法
可达性分析算法:这个算法的核心是通过一系列GC root根对象开始进行深度搜索,每一个root对象深度搜索都会拉出来一个引用链条,在这些链条上的对象属于活对象,不在这些引用链条的对象就是死对象,也就是我们垃圾回收的对象。
垃圾回收算法
标记清除法
最基础的垃圾收集算法是标记清楚算法,这个算法由List之父John McCarthy提出,首先标记出需要回收的对象,然后在标记完成之后,统一回收所有的被标记的对象。
算法特点:
- 实现简单
- 执行效率不稳定,因为算法分为两个步骤,一个标记一个清楚,都是跟对象的数量相关的;
- 最大的缺点是会产生内存碎片,而碎片太多会导致之后程序需要一个连续的大内存的时候找不到连续的内存地址,又得触发一次新的内存回收
标记复制法
标记复制法是由1969年Fenichel提出的一种半区复制的垃圾收集算法,它将可用内存分为大小相等的两块,每次只使用一块。当这一块的内存使用完,就把还存活的对象复制到另一半内存上,然后把这一半内存清理掉。
算法特点:
- 可以有效地避免标记清除法带来地内存碎片的问题
- 浪费了一般的空间,适用于新生代对象朝生夕死的情况
标记整理法
标记复制法在对象存活率比较高的时候,需要进行比较多的复制操作,所以一般在老年代一般不采用这种算法。1974年Edward Lueders提出一种标记整理法的算法。标记的过程跟标记清楚法一样,但是后续为了避免出现内存碎片,这个算法要求让所有存活的对象都向内存空间的一端移动,最后直接清楚调边界外的内存。
算法特点:
- 避免产生内存碎片
- 移动对象的时候,需要更新所有指向改对象的引用,增加了复杂度
分代
基于对象的生命周期不同,将垃圾回收空间分为多个区域,不同区域采用不同的回收策略
- 新生代: 通常包含最新的对象,默认采用标记复制算法回收这种短命的对象
- 老年代: 存活时间比较长的对象,一般采用标记整理法或者标记清楚法
常用的垃圾回收器
根据不同的垃圾回收算法和应用场景,JVM 提供了多种垃圾回收器供选择:
- Serial 收集器
特点:单线程工作,适合客户端模式下的小型应用程序。
适用场景:桌面应用、嵌入式设备。 - Parallel 收集器
特点:多线程并行工作,专注于提高吞吐量。
适用场景:服务器端应用,对响应时间要求不高但希望最大化 CPU 利用率。 - CMS(Concurrent Mark-Sweep)收集器
特点:并发工作,尽量减少停顿时间。
适用场景:Web 应用,对响应时间敏感的应用程序。 - G1(Garbage First)收集器
特点:分区收集,综合考虑吞吐量和延迟,支持细粒度的垃圾回收。
适用场景:大型应用,具有较大的堆内存配置。