首页 > 试题广场 >

如何处理循环引用问题?

[问答题]
如何处理循环引用问题?
推荐
刚刚研究过这个问题。

何为循环引用

如果有两个或者以上的对象,它们彼此引用,就会造成循环引用。如下面的例子
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处理

什么是引用计数

引用计数是一种垃圾回收的形式,每一个对象都会有一个计数来记录有多少指向它的引用。其引用计数会变换如下面的场景

  • 当对象增加一个引用,比如赋值给变量,属性或者传入一个方法,引用计数执行加1运算。
  • 当对象减少一个引用,比如变量离开作用域,属性被赋值为另一个对象引用,属性所在的对象被回收或者之前传入参数的方法返回,引用计数执行减1操作。
  • 当引用计数变为0,代表该对象不被引用,可以标记成垃圾进行回收。

如何处理

实际上单纯的基于引用计数实现的计数器无法处理循环引用带来的问题。

CPython的垃圾回收就是采用引用计数,采用引用计数的主垃圾回收器会清理垃圾,对于那些因为循环引用无法清理的对象,CPython会不时启动一个辅助的基于引用遍历的垃圾回收器来清理它们。

引用遍历GC处理

什么是引用对象遍历

垃圾回收器从被称为GC Roots的点开始遍历遍历对象,凡是可以达到的点都会标记为存活,堆中不可到达的对象都会标记成垃圾,然后被清理掉。 GC Roots有哪些

  • 类,由系统类加载器加载的类。这些类从不会被卸载,它们可以通过静态属性的方式持有对象的引用。注意,一般情况下由自定义的类加载器加载的类不能成为GC Roots
  • 线程,存活的线程
  • Java方法中的局部变量或者参数
  • JNI方法栈中的局部变量或者参数
  • JNI全局引用
  • 用做同步监控的对象
  • 被JVM持有的对象,这些对象由于特殊的目的不被GC回收。这些对象可能是系统的类加载器,一些重要的异常处理类,一些为处理异常预留的对象,以及一些正在执行类加载的自定义的类加载器。但是具体有哪些前面提到的对象依赖于具体的JVM实现。

如何处理

基于引用对象遍历的垃圾回收器可以处理循环引用,只要是涉及到的对象不能从GC Roots强引用可到达,垃圾回收器都会进行清理来释放内存。

总结

基于引用计数的垃圾回收器无法处理循环引用导致的内存泄露问题,但是其在主流的JVM中很少,几乎所有的JVM都是采用引用对象遍历的方法,垃圾回收器都会处理循环引用潜在的问题。

具体可以参考这里的文章 http://droidyue.com/blog/2015/06/05/how-garbage-collector-handles-circular-references/

编辑于 2016-02-25 23:10:37 回复(1)
不知道题目是问头文件循环引用还是两个对象的循环引用!
如果是头文件的话:
循环引用会造成编译错误,如:
文件 a.h
#include "b,h"
class A
{
B b;
....
}
文件 b.h
#include "a,h"
class B
{
....
}
这样书写的话会造成互相引用,形成死循环,编译时通不过的
可以使用前置声明解决:
文件 a.h
class B;
class A
{
 B b;
....
}
文件 b.h
#include "a,h"
class B
{
....
}一起编译这两个类的cpp文件就能编译通过

如果是两个对象的循环引用:
多见于智能指针如share_ptr
如果两个对象A和B都持有对方的强引用,假若A对象需要释放,但是B对象持有强引用,不能释放,B也是如此,这就造成了两个对象的互相等待,永远得不到释放
我们可以使用weak_ptr,weak_ptr只是获得对象的使用权,没有获得所有权,当对象A强引用B,B弱引用A,当A需要释放时,有余没有其他的对象引用它了,所以能够释放,B对象也没有其他对象引用,也可以释放


发表于 2015-05-23 21:49:39 回复(0)
可以使用类似Java垃圾回收机制中的Mark and Sweep算法进行,但需要程序员主动去编写并调用检查程序;另外一个有效的办法是在知道存在循环引用的条件下,使用boost::weak_ptr,即弱引用来代替循环引用中的某个强引用,从而打破循环引用的环
编辑于 2015-05-18 13:08:06 回复(0)
循环引用:对象a引用了对象b,对象b也引用了对象a,形成循环引用。
如何处理:引用遍历GC。

发表于 2015-06-11 20:24:36 回复(0)

这些说的都是java的,C++是weak_ptr就可以了

发表于 2018-11-25 23:54:30 回复(0)
Bol)(xvf
发表于 2018-10-08 11:36:55 回复(0)
可达性分析
发表于 2018-02-28 19:12:53 回复(0)

标记对象是否存活一般有两种算法:引用计数算法,但是该算法不能处理对象之间相互循环引用的问题。另一种是可达性分析算法,通过GC Rooter对象作为起始结点,进行遍历搜索,如果一个对象没有任何引用链相连的话,则该对象不可用,可以判定为可回收的对象。GC Rooter的对象包括:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,本地方法栈中(Native方法)引用的对象。

发表于 2017-01-09 15:12:09 回复(0)
这里说的比较好:http://zhoumf1214.blog.163.com/blog/static/5241940201221942041379/
发表于 2016-03-11 13:39:40 回复(0)
基于引用计数的垃圾回收器无法处理循环引用导致的内存泄露问题,但是其在主流的JVM中很少,几乎所有的JVM都是采用引用对象遍历的方法,垃圾回收器都会处理循环引用潜在的问题。
发表于 2015-08-10 15:12:03 回复(0)
引用对象遍历GC处理
发表于 2015-07-31 17:10:54 回复(0)
无向图遍历
发表于 2015-07-28 16:28:35 回复(0)
怎么有点答非所问的感觉
发表于 2015-07-09 08:12:52 回复(0)
头文件循环引用:两个类互相引用,导致单纯互相引用头文件,从而无法通过编译。如类StrOp和类Other之间错误的互相引用如下:
1.在StrOp.h
#include"Others.h"
class StrOp{
...
private:
    Others oth;
};

2.在Others.h
#include"StrOp.h"
class Others{
...
private:
    StrOp strop;
};
结果:编译不通

改正之后的结果如下:

1.在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;
};



编辑于 2015-06-18 09:09:25 回复(0)
java采用可达性分析回收垃圾对象,循环引用并不会使对象无法被回收。
发表于 2015-06-17 14:12:25 回复(0)
两个对象的循环引用:循环引用的情况下,通常导致对象的引用计数无法归零,引入weak_ptr弱引用指针即可解决循环引用问题。weak_ptr不会修改引用计数。
发表于 2015-06-11 16:07:57 回复(0)
利用可达性分析。枚举根节点,看根节点与对象是否可达,不可达则判断其可被回收,即便有多个对象循环引用,如果不可达则一样要被回收,从此解决循环引用问题
发表于 2015-05-29 23:49:32 回复(0)
递归
发表于 2015-05-05 19:45:05 回复(0)