Ошибка в Groovy AbstractConcurrentMap?
AbstractConcurrentMap является базовым классом в Groovy, он используется для хранения динамических свойств, добавляемых в классы Groovy во время выполнения. Я использую Grails 2.1.2 с Groovy 1.8.8, но я думаю, что проблема присутствует во всех версиях Groovy (связанный исходный код предназначен для Groovy версии 2.4.3).
Проблема возникает во внутреннем классе метода put () сегмента (строка 105):
Когда текущий счетчик превышает порог карты, происходит перефразировка (). Теперь самое сложное в том, что Map содержит мягкие ссылки на объекты, а rehash () проверяет эти ссылки. Поэтому, когда GC отбрасывает мягкие ссылки, результирующий сегмент не расширяется (как это предполагается в методе put ()).
в последней строке rehash () обновляется внутренний счетчик сегмента
count = newCount
(которое является количеством "живых" неразброшенных ссылок и может быть меньше, чем предыдущее количество, как описано выше)после выполнения rehash () метод put () продолжается, однако ошибочная часть заключается в том, что он игнорирует предыдущий параметр внутреннего
count
и он устанавливает предыдущее значение count+1 в каждом случае в строках 124, 143 и 159
Итак, следующие шаги происходят:
- Состояние карты:
threshold = 786432; count=786432
- Новый элемент вставлен в карту:
count = 786433; threshold = 786432
- так как новый счет будет больше, чем порог происходит rehash ()
- rehash () обнаруживает, что большинство объектов является сборщиком мусора, поэтому он не увеличивает размер сегмента, однако в любом случае копирует все объекты из одной таблицы в другую (System.arrayCopy ()).
- rehash () устанавливает внутренний счетчик в новое значение, которое меньше, потому что многие объекты были собраны сборщиком мусора (мягкие ссылки), скажем:
count = 486 000
- Put () продолжается, игнорирует
count = 486 000
и устанавливает счет вcount = 786433
- Вставлен другой элемент, однако в этом состоянии число все еще превышает пороговое значение, поэтому перефразировка повторяется
- Отныне каждый элемент, добавленный на карту, будет вызывать rehash (), который оказывает огромное влияние на производительность.
Когда это происходит в многопоточной среде, все другие потоки ожидают (припаркованы) для lock (), пока не будут выполнены rehash () и put () (а затем следующий снова выполнит rehash ()). Вы можете себе представить, как это влияет на производительность...
Я не понимаю, как эта ошибка могла пережить так много версий, о которых никто не заметил, несмотря на то, что класс широко используется. Может я что-то упустил?
Предложенное решение:
Обновите переменную c после завершения перефразировки. Между строками 105 и 106 добавьте:
c = count + 1
1 ответ
Об ошибке сообщили в Groovy JIRA https://issues.apache.org/jira/browse/GROOVY-7448 и теперь она исправлена.
Fix Version/s:
2.4.4, 2.5.0-beta-1