抽象ConcurrentMap http://grepcode.sadcom/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMap.java是Groovy中的核心类,它用于存储在运行时添加到Groovy类中的动态属性。我正在使用 Grails 2.1.2 和 Groovy 1.8.8,但我认为所有 Groovy 版本中都存在该问题(链接的源代码适用于 Groovy 版本 2.4.3)。
问题发生在内部类Segment中put() http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMap.java#105方法(第 105 行):
当当前计数大于map的阈值a时rehash() http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMapBase.java#213发生。现在棘手的部分是,Map 保存对对象的软引用,并且rehash() http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMapBase.java#213验证这些引用。因此,当 GC 丢弃软引用时,生成的段不会扩展(正如在put() http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMap.java#100方法)。
in the rehash() 的最后一行 http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMapBase.java#283段内部计数器已更新count = newCount
(这是“活动”未丢弃引用的数量,可以小于如上所述的先前计数)
after rehash() http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMapBase.java#213完成后,put() http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMap.java#100方法继续,但是有问题的部分是,它忽略了内部的先前设置count
并在行中的每种情况下设置先前的 count+1 值124 http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMap.java#124, 143 http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMap.java#143 and 159 http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMap.java#159
因此正在发生以下步骤:
- 地图状态:
threshold = 786432; count=786432
- 新元素被插入到地图中:
count = 786433; threshold = 786432
- 因为新计数将大于阈值rehash() http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMapBase.java#213 happens
- rehash() 发现,大多数对象都是垃圾收集的,因此它不会增加 Segment 的大小,但无论如何它会将所有对象从一个表复制到另一个表(System.arrayCopy())。
- rehash() 将内部计数设置为新值,该值较小,因为许多对象被垃圾收集(软引用),可以说:
count = 486 000
- The put() http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMap.java#105继续,忽略
count = 486 000
并将计数设置为count = 786433
- 插入了另一个元素,但是在这种状态下,计数仍然大于阈值,因此再次进行重新哈希
- 从现在开始,添加到地图的每个元素都会触发rehash() http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.3/org/codehaus/groovy/util/AbstractConcurrentMap.java#105,这对性能有巨大的影响。
当这种情况发生在多线程环境中时,所有其他线程都在等待(停放)lock(),直到 rehash() 和 put() 完成(然后下一个线程再次执行 rehash())。您可以想象这对性能有何影响......
我不明白这个错误如何能够在这么多版本中存在而没有人注意到,尽管该类被广泛使用。也许我错过了什么?
建议的解决方案:
重新哈希完成后更新 c 变量。
在第 105 行和第 106 行之间添加:
c = count + 1
该错误已在 Groovy JIRA 上报告https://issues.apache.org/jira/browse/GROOVY-7448 https://issues.apache.org/jira/browse/GROOVY-7448现在已修复。
Fix Version/s:
2.4.4, 2.5.0-beta-1
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)