垃圾收集器具有处理引用循环的功能。据我了解,这对于所有具有 GC 的语言都是必要的。
但我不明白,为什么不能创建避免引用循环的语言,必要时使用一些弱引用。
编程中出现的不可避免的引用循环的现实例子有哪些?
最佳答案
您无法创建避免引用循环的编程语言,因为应用程序员的责任是创建循环,而不是创建循环。您只能创建一种要求程序员始终承担这一责任的语言。
这是数据结构的基本设计,可能允许循环,也可能不允许循环。例如。在Java中,List
是一个引用列表,因此,直接或间接存储List
本身没有问题。但举一个更直接的例子,在双向链表中,每个节点都有一个指向其下一个节点和前一个节点的指针。这已经足以形成引用循环:
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│ │ -next-----> │ │ -next-----> │ │ -next-----> │ │
│ Node │ │ Node │ │ Node │ │ Node │
│ │ <-previous- │ │ <-previous- │ │ <-previous- │ │
└──────┘ └──────┘ └──────┘ └──────┘
这已经形成了多个循环,两个相邻节点之间通过其 previous
和 next
引用形成了短循环,而且还间接在其他节点之间形成了循环。
要通过弱引用切断这些循环,Node
类的设计者必须决定是使用 next
还是 previous
引用虚弱的。它们中的任何一个都会破坏基本功能之一:
- 如果您有对第一个节点的引用,则可以通过
next
引用链到达并遍历所有节点 - 如果您有对最后一个节点的引用,则可以通过
先前
引用链到达并遍历所有节点 - 事实上,对任何链节点的引用就足以到达所有节点
- 如果所有节点均无法访问,则所有节点都会被垃圾回收
如果将两个引用之一声明为弱,则不能假设对一个节点的引用使所有节点都保持事件状态。如果 next
很弱,您需要始终保留对最后一个节点的引用,以防止突然删除下一个节点。如果 previous
很弱,则必须始终保留对第一个节点的引用。
因此,要求开发人员始终通过弱引用来切断循环将会对软件的设计方式造成根本性的限制。作为另一个示例,考虑您注册了监听器的组件将在事件发生时修改该组件(或引用前者的另一个组件),从而形成循环。使监听器引用变弱意味着它可能会无缘无故地突然消失。
<小时/> 也就是说,即使是弱引用本身也是垃圾收集器进行图遍历时自然提供的一个功能,因为只有那些在发现它们的存在时才能廉价地采取行动。当引用计数系统发现最后一个/唯一存在的强引用已被删除时,它可以轻松地释放对象,但是当对象被删除时,它需要对所有现有的弱引用进行额外的簿记以清除它们获释。这就是引用计数不再有意义的地方。实现起来不再简单(这是唯一的优点),同时效率低下,因为当遍历对象图时,就像迭代链接列表一样,您必须永久更新引用计数器(在线程中)如果您的语言支持多线程,则这是安全的方式),而遍历垃圾收集器只需在必须检查可回收内存时才需要执行某些操作。而且它只需要遍历存活的对象,因此它不做任何事情的时间越长,下一个周期的工作量就越少。
关于reference - 引用循环的实际例子有哪些?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51458072/