为什么弱指针有用?

时间:2009-09-28 03:32:42

标签: pointers garbage-collection weak-references

我一直在阅读垃圾收集,寻找包含在我的编程语言中的功能,我遇到了“弱指针”。来自here

  

弱指针就像指针一样   除了来自弱者的引用   指针不会阻止垃圾   收集和弱指针必须   之前检查过它们的有效性   他们被使用了。

     

弱指针与之相互作用   垃圾收集器因为内存   他们所指的实际上可能仍然存在   是有效的,但包含不同的   对象比弱时做的对象   指针已创建。因此,每当一个   垃圾收集器回收内存,它   必须检查是否有任何   指向它的弱指针,和   将它们标记为无效(这不一定是   以这种天真的方式实施。)

我以前从未听说过弱点。我想支持我的语言中的许多功能,但在这种情况下,我不能为我的生活想到这将是有用的情况。对于什么会使用弱指针?

9 个答案:

答案 0 :(得分:9)

非常重要的是缓存。让我们考虑一下缓存的工作原理:

缓存背后的想法是将对象存储在内存中,直到内存压力变得如此之大以至于某些对象需要被推出(或者当然显然无效)。因此,您的缓存存储库对象必须以某种方式保留这些对象。通过弱引用保持它们,当垃圾收集器因为内存不足而寻找要消耗的东西时,仅由弱引用引用的项目将作为垃圾收集的候选者出现。缓存中当前正由其他代码使用的项目将具有仍处于活动状态的硬引用,因此这些项目将受到保护,不会被垃圾回收。

在大多数情况下,您不会使用自己的缓存机制,但通常使用缓存。假设您想拥有一个引用缓存中对象的属性,并且该属性长时间保留在范围内。您将 prefer 从缓存中获取对象,但如果它不可用,您可以从持久存储中获取它。如果压力过高,您也不希望强制该特定对象留在内存中。因此,您可以使用对该对象的弱引用,这将允许您获取(如果可用),但也允许它从缓存中删除。

答案 1 :(得分:8)

典型的用例是存储其他对象属性。假设您有一个具有固定成员集的类,并且从外部开始,您希望添加更多成员。所以你创建了一个字典对象 - >属性,其中键是弱引用。然后,字典不会阻止密钥被垃圾收集;删除对象也应该触发删除WeakKeyDictionary中的值(例如通过回调)。

答案 2 :(得分:5)

如果您的语言的垃圾收集器无法收集循环数据结构,那么您可以使用弱引用来启用它。通常,如果您有两个彼此引用的对象,但没有其他外部对象引用这两个对象,则它们将成为垃圾回收的候选对象。但是,一个天真的垃圾收集器不会收集它们,因为它们包含对彼此的引用。

要解决这个问题,你要做到这一点,这样一个对象对第二个对象有强引用,但第二个对第一个有弱引用。然后,当对第一个对象的最后一个外部引用消失时,第一个对象成为垃圾收集的候选对象,紧接着是第二个对象,因为现在它的唯一引用很弱。

答案 3 :(得分:3)

另一个例子......不完全缓存,但类似:假设一个I / O库提供了一个包装文件描述符并允许访问该文件的对象。收集对象时,将关闭文件描述符。希望能够列出所有当前打开的文件。如果对此列表使用强指针,则永远不会关闭文件。

答案 4 :(得分:1)

当您想要保留缓存的对象列表时使用它们,但如果对象的“真实”所有者使用它,则不会阻止这些对象进行垃圾回收。

Web浏览器可能具有一个历史记录对象,该对象保留对浏览器加载到其他位置并保存在历史记录/磁盘缓存中的图像对象的引用。 Web浏览器可能会使其中一个映像失效(用户清除缓存,缓存超时等),但页面仍然具有引用/指针。如果页面使用弱引用/指针,则对象将按预期消失,并且内存将被垃圾收集。

答案 5 :(得分:0)

弱引用的一个重要原因是处理对象可能充当将信息源或事件源连接到一个或多个侦听器的管道的可能性。如果没有任何监听器,则没有理由继续向管道发送信息。

例如,考虑一个可枚举的集合,它允许在枚举期间进行更新。该集合可能需要通知任何活动的枚举器它已被更改,因此这些枚举器可以相应地调整自己。如果某些枚举器被其创建者放弃,但该集合对其具有强引用,则只要该集合存在,这些枚举器将继续存在(并处理更新通知)。如果集合本身在应用程序的生命周期中存在,那么这些枚举器将有效地成为永久性内存泄漏。

如果集合中包含对枚举数的弱引用,则可以在很大程度上解决此问题。如果枚举器被放弃,它将有资格进行垃圾收集,即使该集合仍然存在弱引用。下次更改集合时,它可以查看其弱引用列表,向仍然有效的列表发送更新,并从列表中删除不存在的引用。

使用终结器和一些额外的对象可以实现弱引用的许多效果,并且可以使这样的实现比使用弱引用的那些更有效,但是存在许多缺陷并且很难避免错误。使用WeakReference制作正确的方法要容易得多。这种方法可能效率不高,但不会失败。

答案 6 :(得分:0)

弱指针使得它们不会成为指针指向的对象的“生命支持”形式。

假设你有一个Viewport类,2个UI类和一些Widget类。您希望UI控制其创建的窗口小部件的生命周期,因此您的UI将SharedPtrs保存到它控制的所有窗口小部件。只要您的UI对象处于活动状态,它所引用的任何Widgets都不会被垃圾收集(感谢SharedPtr)。

但是,Viewport实际上是您的类,因此您的UI需要将Viewport指向Widgets,以便它可以绘制它们。无论出于何种原因,您希望将活动UI类更改为另一个。让我们考虑两种情况,一种是UI传递Viewport WeakPtrs,另一种是传递SharedPtrs(指向Widgets)。

如果您已将所有窗口小部件作为WeakPoin传递给Viewport,则只要删除了UI类,就不会有更多的共享块到窗口小部件,因此它们将被垃圾收集,视口对对象的引用将无法保留他们处于“生命支持”,这正是你想要的,因为你甚至不再使用那个UI了,更不用说它创建的小部件了。

现在,考虑您已经将Viewport传递给了SharedPointer,删除了UI,并且不会对Widgets进行垃圾回收!为什么?因为仍处于活动状态的视口有一个数组(向量或列表,无论如何),其中包含对小部件的SharedPtrs。即使你已经删除了控制另一个UI对象的小部件的UI,Viewport实际上已成为他们的“生命支持”形式。

通常,语言/系统/框架会垃圾收集任何内容,除非在内存中某处有“强”引用。想象一下,如果一切都有强烈的引用,那么任何东西都不会被垃圾收集!有时候你想要那种行为,有时你却不这样做。如果您使用WeakPtr,并且没有指向对象的Shared / StrongPtrs(仅WeakPtrs),那么尽管WeakPtr引用,对象将被垃圾收集,并且WeakPtrs(应该)设置为NULL(或删除,或者东西)。

同样,当你使用WeakPtr时,你基本上允许你给它的对象也能够访问数据,但是WeakPtr不会阻止它指向的对象的垃圾收集,就像SharedPtr那样。当你想到SharedPtr时,想想“生命支持”,WeakPtr,没有“生命支持”。在对象没有生命支持之前,通常不会发生垃圾收集。

答案 7 :(得分:-1)

例如,在缓存方案中可以使用弱引用 - 您可以通过弱引用访问数据,但如果长时间不访问数据或内存压力很高,GC可以释放它。

答案 8 :(得分:-1)

垃圾收集的原因在于像C这样的语言,其中内存管理完全由程序员明确控制,当对象所有权传递时,特别是在线程之间,或者甚至更难,在共享内存的进程之间,避免内存泄漏和悬空指针可能变得非常困难。如果这还不够难,你还必须处理需要访问更多的对象,而不是一次适合内存 - 你需要有一种方法可以释放一些对象一段时间,以便其他对象可以在记忆中。

因此,某些语言(例如,Perl,Lisp,Java)提供了一种机制,您可以停止“使用”一个对象,垃圾收集器最终会发现这个并释放用于该对象的内存。如果没有程序员担心他们错误的所有方法(尽管程序员可以通过很多方式搞砸了它),它就能正确地做到这一点。

如果您在概念上将访问对象的次数乘以计算对象值所花费的时间,并且可能再次乘以不具有对象的成本或对象的大小的成本因为在内存中保留一个大对象可以防止保留几个较小的对象,你可以将对象分为三类。

有些对象非常重要,您希望明确管理它们的存在 - 它们不会被垃圾收集器管理,或者在显式释放之前不得收集它们。有些对象计算成本低,体积小,不经常访问或具有类似的特性,可以随时进行垃圾收集。

第三类,重新计算成本昂贵但可以重新计算的对象,可以经常访问(可能是短暂的一段时间),大尺寸,等等是第三类。您希望尽可能长时间地将它们保存在内存中,因为它们可能会再次被重用,但您不希望关键对象所需的内存不足。这些是弱参考的候选者。

如果这些对象与关键资源没有冲突,您希望这些对象尽可能长时间保留,但如果关键资源需要内存,则应删除它们,因为可以在需要时再次重新计算它们。这些都是帽子弱点指针。

这方面的一个例子可能是图片。假设您有一个包含数千张图片的照片网页。您需要知道要布置多少图片,并且您可能需要进行数据库查询才能获得列表。保存几千个项目列表的内存可能非常小。你想做一次查询并保留它。

但是,您只能在网页的一个窗格中一次实际显示几十张图片。您无需获取用户无法查看的图片的位。当用户滚动页面时,您将收集可见图片的实际位。这些图片可能需要很多兆字节来显示它们。如果用户在几个滚动位置之间来回滚动,则您不希望一遍又一遍地重新获取这些兆字节。但是你无法一直将所有图片保存在内存中。所以你使用弱指针。

如果用户只是一遍又一遍地看几张图片,他们可能会留在缓存中而您不必重新获取它们。但是如果它们足够滚动,你需要释放一些内存,以便可以获取可见的图片。如果使用弱引用,则在使用之前检查引用。如果它仍然有效,您可以使用它。如果不是,则进行昂贵的计算(获取)以获得它。