在我正在修改的幻灯片上,它说以下内容:
可以通过维护对每个对象的引用数的计数,或通过跟踪从根开始的引用链来识别活动对象。
引用计数很昂贵——每次引用发生变化时都需要采取行动,而且它不会发现循环结构,但它可以逐步回收空间。
跟踪涉及仅在需要回收空间时识别活动对象——将成本从一般访问转移到 GC 运行时间,通常仅在内存不足时。
我理解为什么引用计数很昂贵的原理,但不明白什么是“不能发现循环结构,但它可以逐步回收空间”。方法。任何人都可以帮我一点吗?
谢谢
在我正在修改的幻灯片上,它说以下内容:
可以通过维护对每个对象的引用数的计数,或通过跟踪从根开始的引用链来识别活动对象。
引用计数很昂贵——每次引用发生变化时都需要采取行动,而且它不会发现循环结构,但它可以逐步回收空间。
跟踪涉及仅在需要回收空间时识别活动对象——将成本从一般访问转移到 GC 运行时间,通常仅在内存不足时。
我理解为什么引用计数很昂贵的原理,但不明白什么是“不能发现循环结构,但它可以逐步回收空间”。方法。任何人都可以帮我一点吗?
谢谢
引用计数没有发现周期性结构......
假设您有两个对象 O1 和 O2。它们相互引用:O1 -> O2 和 O2 -> O1,并且没有其他对象引用它们。它们都将具有引用计数 1(一个引用者)。
如果 O1 或 O2 都无法从 GC 根访问,则可以安全地对它们进行垃圾收集。但是,这并不能通过计数引用来检测,因为它们的引用计数 > 0。
0 引用是对象符合垃圾回收条件的充分但非必要要求。
...但它可以逐步回收空间。
增量部分是指您可以快速垃圾收集一些 0 引用的对象,被中断并在另一个时间继续而不会出现问题。
如果跟踪算法被中断,它将需要在下一次调度时从头开始。(参考树可能从一开始就发生了变化!)
假设我有一个对象A
。A
需要另一个被调用的对象B
来完成它的工作。但是B
需要调用另一个对象C
来完成它的工作。但出于某种原因C
需要一个指向的指针。A
所以依赖图看起来像:
A -> B -> C -> A
对象的引用计数应该是指向它的箭头的数量。在这个例子中,每个对象都有一个引用计数。
假设我们的主程序在执行过程中创建了一个这样的结构,并且主程序有一个指向 的指针A
,使A
' 的计数等于 2。当这个结构超出范围时会发生什么?A
的引用计数减一。
但请注意!现在A
,B
和C
所有的引用计数都是 1 ,即使它们无法从主程序访问。所以这是内存泄漏。维基百科有关于如何解决这个问题的详细信息。
大多数垃圾收集器都有一个收集周期,在此期间它们会暂停执行并释放不再使用的对象。在标记和扫描系统中,这是扫描步骤。不利的一面是,在两次扫描之间的时间段内,内存不断增长。一个对象可能在创建后几乎立即停止使用,但在下一次扫描之前它永远不会被回收。
在引用计数系统中,对象的引用计数一达到零就会被释放。没有大的停顿或任何大的扫描步骤,不再使用的对象不会只是坐在那里等待收集,它们会立即被释放。因此,收集是增量的,因为它增量收集不再使用的任何对象,而不是批量收集自上次收集以来已不再使用的所有对象。
Of course, this incrementalism may come with its own pitfalls, namely that it might be less expensive to do a bulk GC rather than a lot of little ones, but that is determined by the exact implementation.
假设您有三个对象(A、B、C):A 对 B 有引用,B 对 C 有引用,C 对 A 有引用。但没有其他对象对其中一个有任何引用。是一个独立的循环结构。使用传统的引用计数会阻止垃圾收集器删除循环,因为每个对象仍然被引用。但只要没有人对这三个中的一个有参考,他们就可以/应该被删除。我猜想增量回收空间意味着在查找未引用实例、无循环等时引用计数的工作方式。
如果引用计数达到 0,则可以释放对象以进行垃圾收集。
对于循环引用,这永远不会发生,因为圆中的每个对象都保持对另一个对象的引用,因此它们都至少为 1。
为此,需要一些图论来检测不再附加到任何东西的引用,比如堆海中的小岛。为了将它们保存在内存中,它们必须在某处对静态变量有一些“附件”。
这就是追踪的作用。它确定堆的哪些部分是孤岛并且可以被释放,哪些仍然连接到大陆,即静态变量smewhere。