持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情
1.确定垃圾的算法:
-
引用计数法(无法解决循环引用,a指向b,b指向c,c指向a,但是a、b、c都不是根对象)
- 在java中我们不适用这个算法,使用的是根可达性算法。
- python中使用的是这个算法,这可能也是它的性能不如java的原因之一。
- 根可达性算法(三色标记算法,如上图)
-
三种颜色
- 白色:没有检查(或者检查过了,确实没有引用指向它了)
- 灰色:自身被检查了,成员没被检查完(可以认为访问到了,但是正在被检查,就是图的遍历里那些在队列中的节点)
- 黑色:自身和成员都被检查完了
-
假设现在有白、灰、黑三个集合(表示当前对象的颜色),其遍历访问过程为:
- 初始时,所有对象都在 【白色集合】中;
- 将GC Roots 直接引用到的对象 挪到 【灰色集合】中;
- 从灰色集合中获取对象:
- 将本对象 引用到的 其他对象 全部挪到 【灰色集合】中;
- 将本对象 挪到 【黑色集合】里面。
- 重复步骤3,直至【灰色集合】为空时结束。
- 结束后,仍在【白色集合】的对象即为GC Roots 不可达,可以进行回收。
-
什么是根?根就可以理解为
main方法
,你main方法就是根,你这这里new出来的对象
-
通过System Class Loader或者Boot Class Loader加载的class对象(通过自定义类加载器加载的class不一定是GC Root)
-
处于激活状态的线程
-
栈中的对象
-
JNI栈中的对象
-
JNI中的全局对象
-
正在被用于同步的各种锁对象
-
JVM自身持有的对象,比如系统类加载器等。
-
-
2垃圾回收算法
-
标记清除(Mark-Sweep)
- 缺点:
- 1.造成内存碎片化 ;
- 2 .分配效率较低,如果是一块连续的内存空间,那么我们可以通过指针加法(pointer bumping)来做分配。而对于空闲列表,Java 虚拟机则需要逐个访问列表中的项,来查找能够放入新建对象的空闲内存。
-
标记压缩(标记清除的升级)(Mark-Compact)
- 能够解决碎片化问题,但是压缩算法的性能开销是比较大的,因为需要挪动对象,然后改变对象引用的地址。
-
复制算法(Coping)
- 能够解决碎片化问题,但是空间使用效率变低了,毕竟有一块空间是用不着的。
- 复制算法是将内存划分为两个区间,在任意时间点,所有动态分配的对象都只能分配在其中的一个区间(称为活动区间),而另外一个区间(空闲区间)是空闲的。
- 当有效内存空间耗尽时,JVM将暂停程序运行,开启复制GC线程。接下来GC线程会将活动区的存活对象,全部复制到空闲区间,且严格按照内存地址依次排列,与此同时,GC线程将更新存活对象的内存引用地址指向新的内存地址。
- 此时,空闲区间已经与活动区间交换,而垃圾对象现在已经全部留在了原来的活动区间,也就是现在的空闲区间。事实上,在活动区间转换为空闲区间的同时,垃圾对象已经被一次性全部回收了。
三种算法在垃圾回收器中搭配使用。