Ошибка в 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

Итак, следующие шаги происходят:

  1. Состояние карты: threshold = 786432; count=786432
  2. Новый элемент вставлен в карту: count = 786433; threshold = 786432
  3. так как новый счет будет больше, чем порог происходит rehash ()
  4. rehash () обнаруживает, что большинство объектов является сборщиком мусора, поэтому он не увеличивает размер сегмента, однако в любом случае копирует все объекты из одной таблицы в другую (System.arrayCopy ()).
  5. rehash () устанавливает внутренний счетчик в новое значение, которое меньше, потому что многие объекты были собраны сборщиком мусора (мягкие ссылки), скажем: count = 486 000
  6. Put () продолжается, игнорирует count = 486 000 и устанавливает счет в count = 786433
  7. Вставлен другой элемент, однако в этом состоянии число все еще превышает пороговое значение, поэтому перефразировка повторяется
  8. Отныне каждый элемент, добавленный на карту, будет вызывать 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
Другие вопросы по тегам