算法 自动采集列表(Python使用计算法进行垃圾回收Python如何解决循环引用? )
优采云 发布时间: 2022-02-05 13:20算法 自动采集列表(Python使用计算法进行垃圾回收Python如何解决循环引用?
)
标记阶段:参考计数算法垃圾标记阶段:对象生存判断参考计算算法证明
/**
* -XX:+PrintGCDetails
* 证明:java使用的不是引用计数算法
*/
public class RefCountGC {
//这个成员属性唯一的作用就是占用一点内存
private byte[] bigSize = new byte[5 * 1024 * 1024];//5MB
Object reference = null;
public static void main(String[] args) {
RefCountGC obj1 = new RefCountGC();
RefCountGC obj2 = new RefCountGC();
obj1.reference = obj2;
obj2.reference = obj1;
obj1 = null;
obj2 = null;
//显式的执行垃圾回收行为
//这里发生GC,obj1和obj2能否被回收?
// System.gc();
// try {
// Thread.sleep(1000000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
这样创建对象会导致引用计数器为+2,我们来看看没有回收的内存。
让我们GC
显然这个GC被回收了。如果是引用计数算法,则不会采集它们。因此 Java 不使用引用计数算法。
Python 使用参考算法进行垃圾采集。Python如何解决循环引用?标记阶段:可达性分析算法(Java使用的算法) GCRoots 根集 GC Roots可以收录本地方法栈中JNI引用的那些元素 类的方法区中静态属性引用的对象 常量引用的对象在方法区All synchronized 锁同步持有的对象Java 虚拟机的内部引用反映了Java 虚拟机JMXBean 的内部情况,在JVMTI 中注册的回调,以及本地代码缓存。
特别注意辨别能力
因为Root使用栈来存储变量和指针,如果一个指针将对象保存在堆内存中,但不将自己保存在堆内存中,那么他就是一个Root
注意对象finalize()方法的终结机制注意,在功能上,finalize()和C++中的析构函数类似,但是Java使用了基于垃圾回收的自动内存管理机制,所以finalize()方法本质上是不同的 C++ 中的析构函数。由于finalize()方法的存在,虚拟机中的对象一般处于三种可能的状态。对象的三种状态
一个不可触及的对象在一定的条件下可能会自我复活,如果是这样,回收它是不合理的。为此,定义了虚拟机中的对象可能处于三种状态
具体过程对象复活
public class CanReliveObj {
public static CanReliveObj obj;//类变量,属于 GC Root
//此方法只能被调用一次
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("调用当前类重写的finalize()方法");
obj = this;//当前待回收的对象在finalize()方法中与引用链上的一个对象obj建立了联系
}
public static void main(String[] args) {
try {
obj = new CanReliveObj();
// 对象第一次成功拯救自己
obj = null;
System.gc();//调用垃圾回收器
System.out.println("第1次 gc");
// 因为Finalizer线程优先级很低,暂停2秒,以等待它
Thread.sleep(2000);
if (obj == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is still alive");
}
System.out.println("第2次 gc");
// 下面这段代码与上面的完全相同,但是这次自救却失败了
obj = null;
System.gc();
// 因为Finalizer线程优先级很低,暂停2秒,以等待它
Thread.sleep(2000);
if (obj == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is still alive");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果
MAT 和 JProfiler 的 GC Roots 可追溯性 MAT
MAT是Memory Analyzer的坚持,它是一个强大的Java堆内存分析器,用于查找内存泄漏和查看内存消耗。
MAT是基于Eclipse开发的,是一个性能分析工具。
获取转储文件方法一:使用jmap命令行
方法 2:使用 JVisualVM 将本地应用程序的堆转储导出为应用程序选项卡的子选项卡。同事堆转储对应左侧Application栏的一个timestamp节点,右键该节点选择save as 可以导出本地扫描阶段:Mark-Sweep算法(Mark-Sweep)
实施过程
当堆中可用内存耗尽时,整个进程(也称为 stw)停止,执行两个作业
缺点 什么是清算?
所谓清空,并不是真的清空,而是垃圾对象的地址存放在一个空闲列表中。
清算阶段:复制算法(Copying)
大意
将活动内存空间分成两块,一次只使用其中一个,垃圾回收时将正在使用的内存中的活动对象复制到未使用的内存块中,然后清除所有正在使用的内存块。对象,交换内存的作用,最后完成垃圾回收。
优点 缺点 特殊应用场景
在新生代中,常规应用的垃圾回收通常可以一次性回收70%-99%的内存空间,回收非常划算,所以虚拟机采用这种算法来回收新生代。
清除阶段:标记紧凑算法
基于老年垃圾回收的特点,需要使用其他算法
在标记压缩算法上实现过程标记扫描算法
mark-compression算法可视为mark-sweep-compression算法(只有在mark-sweep执行完成后,才进行内存碎片整理)
两者的本质区别在于
优缺点总结
分代采集算法
以上三种算法都不能完全替代其他方法。它们都有自己独特的优势和特点,手机算法的一代应运而生。
目前几乎所有的 GC 都使用分代采集算法来执行垃圾采集。
Tenured Gen 增量采集算法,分区算法增量采集算法
在上述算法的垃圾回收过程中,应用软件将处于STW(Stop The World)状态。在这种状态下,所有的线程都会被挂起,所有正常的工作都会被挂起,垃圾回收也就完成了。垃圾回收时间过长会影响系统稳定性和用户体验。为了解决这个问题,增量采集算法(Incremental Collecting)诞生了。
基本思想
如果一次采集和处理所有垃圾,需要长时间的停顿,那么垃圾采集线程和应用程序线程可以交替执行。每次垃圾采集线程只采集一小块内存空间,然后切换到应用程序。程序线程,如此反复,直到垃圾采集完成。
该算法的基础仍然是传统的mark-sweep算法和copy算法,而增量采集算法则妥善处理线程间的冲突,让垃圾采集线程分阶段完成标记、清理或复制工作。
缺点
线程切换和上下文转换的消耗会增加垃圾回收的整体成本,导致系统吞吐量下降。
分区算法
生成算法将对象的生命周期分为两部分(新生代、老生代)
分区算法将整个堆空间划分为连续不同和不同的小区间。每个细胞独立使用和独立回收。这种算法的优点是可以控制一次回收多少个cell。