【问题标题】:Bug in Groovy AbstractConcurrentMap?Groovy Abstract ConcurrentMap 中的错误?
【发布时间】:2017-02-03 20:54:52
【问题描述】:

AbstractConcurrentMap 是 Groovy 中的核心类,用于存储在运行时添加到 Groovy 类的动态属性。我将 Grails 2.1.2 与 Groovy 1.8.8 一起使用,但我认为该问题存在于所有 Groovy 版本中(链接的源代码适用于 Groovy 版本 2.4.3)。

问题发生在内部类Segment的put()方法(第105行):

  • 当当前计数大于映射的阈值时,会发生rehash()。现在棘手的部分是,Map 包含对对象的软引用,rehash() 验证这些引用。因此,当 GC 丢弃软引用时,生成的段不会扩展(正如 put() 方法中所假设的那样)。

  • last line of rehash() 中,Segmen't 内部计数器已更新count = newCount(这是“活动”未丢弃引用的数量,并且可以小于之前的计数,如上所述)

    李>
  • rehash() 完成后,put() 方法继续,但有问题的部分是,它忽略了内部 count 的先前设置,并且在每种情况下都设置了先前的 count+1 值在线124143159

所以下面的步骤正在发生:

  1. 地图状态:threshold = 786432; count=786432
  2. 新元素被插入到地图中:count = 786433; threshold = 786432
  3. 因为新计数将大于阈值rehash() 发生
  4. rehash() 发现大多数对象都被垃圾回收,因此它不会增加 Segment 的大小,但无论如何它会将所有对象从一个表复制到另一个表 (System.arrayCopy())。
  5. rehash() 将内部计数设置为新值,该值更小,因为许多对象被垃圾收集(软引用),可以说:count = 486 000
  6. put() 继续,忽略count = 486 000 并将计数设置为count = 786433
  7. 插入了另一个元素,但在此状态下,计数仍大于阈值,因此重新哈希再次发生
  8. 从现在开始,添加到地图的每个元素都会触发rehash(),这会对性能产生巨大影响。

当这种情况发生在多线程环境中时,所有其他线程都在等待(停放)lock(),直到 rehash() 和 put() 完成(然后下一个线程再次执行 rehash())。你可以想象这是什么性能影响......

我不明白这个错误是如何在这么多版本中存活下来的,尽管该类被广泛使用,但没有人注意到。也许我错过了什么?

建议的解决方案:

在 rehash 完成后更新 c 变量。 在第 105 和 106 行之间添加:

c = count + 1

【问题讨论】:

标签: groovy


【解决方案1】:

该错误已在 Groovy JIRA https://issues.apache.org/jira/browse/GROOVY-7448 上报告,现已修复。

Fix Version/s:
2.4.4, 2.5.0-beta-1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-11
    • 1970-01-01
    • 1970-01-01
    • 2013-05-26
    • 2012-07-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多