Groovy AbstractConcurrentMap中的错误?

时间:2015-06-02 10:12:10

标签: groovy

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

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

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

  • last line of rehash()中,Segmen的内部计数器已更新count = newCount(这是"有效"无遗留引用的数量,可能比以前更小按上述计算)

  • rehash()完成后,put()方法会继续,但有缺陷的部分是,它忽略了内部count的先前设置,并设置了之前的计数在124143159

  • 行的每种情况下+1值

所以发生了以下步骤:

  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(),这会对性能产生巨大影响。
  9. 当在多线程环境中发生这种情况时,所有其他线程都在等待(停放)lock(),直到rehash()和put()完成(然后下一个线程再次执行rehash())。您可以想象这对性能有何影响...

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

    建议的解决方案:

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

    c = count + 1
    

1 个答案:

答案 0 :(得分:0)

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

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