垃圾回收机制
1、什么是GC
GC(GarbageCollection)是垃圾自动回收机制,将对象中不再被使用的对象进行回收,主要作用于堆内存。在Java中开发人员无法使用指针来自由的管理内存,GC是JVM对内存(实际上就是对象)进行管理的方式。GC使得Java开发人员摆脱了繁琐的内存管理工作,让程序的开发更有效率。
Java中GC的主要任务是:
- 分配内存
- 确保被引用对象的内存不被错误地回收
- 回收不再被引用的对象的内存空间
凡事都有两面性。垃圾回收器在把程序员从释放内存的复杂工作中解放出来的同时,为了实现垃圾回收,garbage collector必须跟踪内存的使用情况,释放没用的对象,在完成内存的释放之后还需要处理堆中的碎片, 这样做必定会增加JVM的负担,这就需要我们更深入的了解GC。
深入了解GC机制让我们的代码更有效率,尤其是在构建大型程序时,GC直接影响着内存优化和运行速度。
2、JVM对象的引用分为了四种类型:
- 强引用:默认情况下,对象采用的均为强引用(这个对象的实例没有其他对象引用,GC时才会被回收)
- 软引用:软引用是Java中提供的一种比较适合于缓存场景的应用(只有在内存不够用的情况下才会被GC)
- 弱引用:在GC时一定会被GC回收
- 虚引用:由于虚引用只是用来得知对象是否被GC
3、如何判断一个对象是可回收的?
- 引用计数方式(当对象引用一次,就加一)
给对象添加一个引用计数器,每当一个地方引用了对象,引用计数器加1,当引用失效,引用计数器减1当对象的引用计数器为0表示对象已死,可回收缺点(引导同学回答)无法检测"循环引用":当两个对象互相引用,即使他两都不被外接任何东西引用,他两的计数器都不为零,因此永远不会被回收而对实际开发者而言,这两个对象已经完全没有用处了,因此,Java未采用该方案来判定对象存活性内存泄漏案例:
循环引用
class A{
public B _b;
}
class B{
public A _a;
}
public class TestDemo {
public void func() {
A a = new A();
B b = new B();
a._b = b;
b._a = a;
}
}
当func 函数执行时,A 和 B 两个对象分别有两个引用,因此它们的引用计数都是 2, func 执行完毕后,a和 b 两个引用变量失效并修改 A 和 B 的引用计数, 但是由于 A 和 B 的引用计数现在还是 1,所以 GC 无法回收这两个对象, 造成资源泄露,所以该方法不可取。
2. 进行可达性分析
该方案是对象判活的主要方案基本思想就是将所引用的对象想象成一棵树,从树的根节点GC Roots出发持续遍历找出所有的树枝对象,这些对象被称之为"可达"对象或者"存活对象",其余的对象则被视为"死亡"的不可达对象,或称"垃圾",GC Roots本身一定是可达的,这样从他们出发遍历到的对象才能保证一致可达,那么Java中哪一些对象一定是可达的呢?或者是哪一些可以作为GC Roots呢问题:可以作为GC Roots的对象虚拟机栈(帧栈中的本地变量表)中的引用的对象方法区中静态属性引用的对象方法区中常量引用的对象本地方法栈中JNI引用的对象
4、垃圾回收器的作用:
垃圾回收器是具体执行垃圾回收算法的。它会根据不同的需求调用不同的垃圾收集器,而不同垃圾收集器采用不同的垃圾收集算法
垃圾回收机制主要是检测并回收堆上的垃圾
堆 : 根据每块区域上对象的不同性质具体分为以下3块区域
(1)新生代 GC new 特点:对象 朝生夕灭
使用算法:复制算法
2 个相等的区域
(2)老年代
a.新生代的对象 移动过来的
b.大对象
特点:生命周期都很长
所以,老年代是标记-清除 或者 标记-整理算法
(3)永久代(元数据区) 是不会被回收的一部分
5、常见的垃圾回收算法
(1)标记-清除算法:
分为标记和清除两阶段:首先标记出所有需要回收的对象,然后统一回收所有被标记的对象。这是最基础的算法,后续的收集算法都是基于这个算法扩展的。
不足:效率低;标记清除之后会产生大量碎片 效果图如下:
(2)复制算法:
将可用内存按容量划分为大小相等的两块,每次只用其中一块。当这块内存用完了,就将还存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间,只能使用一部分内存,导致性能降低,而新生代就大概解决了这个问题。
效果图如下:
(3)标记-整理算法
此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。效果图如下:
(4)分代收集算法
算法的思想是按对象的存活周期不同将内存划分为几块一般是把 Java 堆分为新生代和老年代(还有一个永久代,是 HotSpot 特有的实现,其他的虚拟机实现没有这一概念,永久代的收集效果很差,一般很少对永久代进行垃圾回收),这样就可以根据各个年代的特点采用最合适的收集算法。
这是当前商业虚拟机常用的垃圾收集算法。分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。