最近刷题刚刚研究过这个问题。
何为循环引用
如果有两个或者以上的对象,它们彼此引用,就会造成循环引用。如下面的例子
class Node {
Node next ;
}
Node a = new Node ();
Node b = new Node ();
a . next = b ;
b . next = a ;
代码中,a对象引用了b对象,b对象也引用了a对象,这种情况下a对象和b对象就形成了循环引用。
引用计数GC(java)
- 什么是引用计数
引用计数是一种垃圾回收的形式,每一个对象都会有一个计数来记录有多少指向它的引用。其引用计数会变换如下面的场景
当对象增加一个引用,比如赋值给变量,属性或者传入一个方法,引用计数执行加1运算。
当对象减少一个引用,比如变量离开作用域,属性被赋值为另一个对象引用,属性所在的对象被回收或者之前传入参数的方法返回,引用计数执行减1操作。
当引用计数变为0,代表该对象不被引用,可以标记成垃圾进行回收。
- 如何处理
实际上单纯的基于引用计数实现的计数器无法处理循环引用带来的问题。
CPython的垃圾回收就是采用引用计数,采用引用计数的主垃圾回收器会清理垃圾,对于那些因为循环引用无法清理的对象,CPython会不时启动一个辅助的基于引用遍历的垃圾回收器来清理它们。
引用遍历GC处理
什么事是引用对象遍历
垃圾回收器从被称为GC Roots的点开始遍历遍历对象,凡是可以达到的点都会标记为存活,堆中不可到达的对象都会标记成垃圾,然后被清理掉。 GC Roots有哪
类,由系统类加载器加载的类。这些类从不会被卸载,它们可以通过静态属性的方式持有对象的引用。注意,一般情况下由自定义的类加载器加载的类不能成为GC Roots
线程,存活的线程
Java方法栈中的局部变量或者参数
JNI方法栈中的局部变量或者参数
JNI全局引用
用做同步监控的对象
被JVM持有的对象,这些对象由于特殊的目的不被GC回收。这些对象可能是系统的类加载器,一些重要的异常处理类,一些为处理异常预留的对象,以及一些正在执行类加载的自定义的类加载器。但是具体有哪些前面提到的对象依赖于具体的JVM实现。- r如何处理
基于引用对象遍历的垃圾回收器可以处理循环引用,只要是涉及到的对象不能从GC Roots强引用可到达,垃圾回收器都会进行清理来释放内存
- r如何处理
- 对于C++而言
头文件循环引用:两个类互相引用,导致单纯互相引用头文件,从而无法通过编译。如类StrOp和类Other之间错误的互相引用如下:
1).在StrOp.h
#include"Others.h"
class StrOp{
...
private:
Others oth;
};
2)在Others.h
#include"StrOp.h"
class Others{
...
private:
StrOp strop;
};
结果:编译不通
改正之后的结果如下
链接:https://www.nowcoder.com/questionTerminal/15be363c213f437081abf0b31b727951
来源:牛客网
.在StrOp.h
class Others;
class StrOp{
...
private:
Others* oth;//在此不用指针,编译不通,原因请看最后解释
};
//解释:这个声明,有时候被称为前向声明(forward declaration),在程序中引入了类类型的Others.在声明之后,定义之前,类Others是一个不完全类型(incompete type),即已知Others是一个类型,但不知道包含哪些成员. 不完全类型只能以有限方式使用,不能定义该类型的对象,不完全类型只能用于定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数.
2.在Other.h
#include"StrOp.h"
class{
...
private:
StrOp strop;
};