代码之家  ›  专栏  ›  技术社区  ›  Shamik

weakhashmap迭代和垃圾收集

  •  14
  • Shamik  · 技术社区  · 16 年前

    我正在使用 WeaekHashMap 实现缓存。我想知道我是否在迭代这个映射的键,同时垃圾收集器正在积极地从这个映射中删除键,我会收到一个 ConcurrentModificationException ? 我不这么认为,因为据我所知,ConcurrentModificationException是由于应用程序代码中的错误而发生的,在这种情况下,开发人员忘记了理解同一映射是由其他线程共享/使用的,在这种情况下,它不应该发生。但是想知道在weakhashmap不同步时,JVM如何处理这个问题?

    4 回复  |  直到 16 年前
        1
  •  12
  •   Kevin Brock    16 年前

    正如bkail所说,当gc从 WeakHashMap 它不会导致并发修改。实际上,GC通过对 WeakReference 对象(持有真正的键)本身。因此,不会收集映射直接引用的实际对象(引用对象),因此在您的某个线程调用此映射中的方法之前,映射不会更改。此时,映射检查GC中的引用队列,并查找已收集的所有键,并将它们从映射中删除,因此对映射结构的实际更改发生在某个线程上。

    考虑到这一点,可能会出现这样一种情况:在这样一个映射中,您可能会得到一个并发修改,而在另一种映射中,您可能不会得到这种修改——如果您放置了一个已经存在的键,或者调用了一个getter方法。但实际上,在并发应用程序中,无论如何您都应该锁定这些调用,这样您的程序中就会有一个真正的并发访问错误。

    在回答你的问题时说的话,你真的 不应使用 弱映射 对于缓存(即使您在谈论缓存密钥)。在缓存中,您不希望值在不再被引用时“神奇地”消失。通常情况下,您希望它们在达到某个最大值时消失(类似于Apache集合 LRUMap )或根据内存需求释放。

    稍后,您可以使用带有 SoftReference (Apache Collections提供了 ReferenceMap 这允许您指定键或值的引用类型)。软引用被指定为仅基于内存压力释放-另一方面,弱引用必须与GC做更多的工作,因为GC认识到对象没有剩余的硬引用,可以随时释放它。当然,软引用的实际工作方式也依赖于JVM实现。

    编辑 :我重读了你的问题,想解决另一个问题。因为实际修改发生在 弱映射 你自己的线的内部结构, 如果只在单个线程中使用此映射,则不需要同步任何方法调用。 . 这种行为与其他任何行为都没有区别 Map .

        2
  •  5
  •   Brett Kail    16 年前

    不,您将不会收到ConcurrentModificationException。weakhashmap在调用各种操作时使用referencequeue.poll。换句话说,每个调用者都会默默地负责从映射中清除过时的条目。但是,这意味着从多个线程调用weakhashmap上的方法是不安全的,否则这些线程似乎是“只读的”,因为对get()的任何调用都会破坏另一个线程试图迭代的条目链接列表。

        3
  •  0
  •   Ricardo Marimon    16 年前

    weakhashmap在键上很弱,而不是值,因此如果希望在不使用值时释放空间,则不适合缓存值。你可能想看看 MapMaker google collections .

        4
  •  -2
  •   luke    16 年前

    关于这一点,文件并不完全清楚,但它确实说明了这一点:

    weakhashmap类的行为 部分取决于 垃圾收集器,好几个 熟悉(但不需要)地图 不变量不适用于此类。 因为垃圾收集器可能 随时丢弃钥匙,A weakhashmap可能表现得像 未知线程正在静默删除 条目 . 尤其是,即使你 在weakhashmap实例上同步 不调用它的变元 方法,大小 返回较小值的方法 IsEmpty方法返回的时间 先假后真 containsKey方法返回true和 以后对于给定的键,对于 get方法返回 给定键,但稍后返回空,用于 要返回空值的Put方法和 移除为返回false的方法 以前出现的钥匙 地图,连续 检查密钥集,值 集合,输入设置为yield 依次减少 元素。- Java API

    我认为根据这种描述,你应该偶尔收到 ConcurrentModificationException 在迭代映射时。我将设计您的缓存,以便尽可能少地进行迭代。