Error in Groovy AbstractConcurrentMap?

AbstractConcurrentMap is the main class in Groovy and is used to store dynamic properties added to Groovy classes at runtime. I'm using Grails 2.1.2 with Groovy 1.8.8, but I think the problem is present in all versions of Groovy (linked source for Groovy version 2.4.3).

The problem occurs in the inner class Segment put () method (line 105):

  • When the current counter is greater then the map threshold rehash () appears . Now the tricky part is that the Map contains soft references to objects and rehash () validates those references. Therefore, when the GC drops soft references, the resulting segment is not expanded (as the put () method suggests ).

  • the last line of rehash () updated the internal Segmen't counter count = newCount

    (which is the number of "live" non-standard links and may be less than the previous one, as described above)

  • after rehash () the put () is executed continues, however, the buggy is that it ignores the previous setting of the internal count

    and sets each time to the previous value + 1 on lines 124 , 143 and 159

So, the following steps are performed:

  • Card state: threshold = 786432; count=786432

  • A new element is inserted into the map: count = 786433; threshold = 786432

  • since the new count will be greater than the threshold rehash () occurs
  • rehash () detects that most of the objects are garbage collected, so it doesn't increase the segment size, however, it does copy all objects from one table to another anyway (System.arrayCopy ()).
  • rehash () sets the internal count to a new value, which is less because many objects have been garbage collected (soft links), say: count = 486 000

  • put () continues, ignores count = 486 000

    and sets the countercount = 786433

  • Another element is inserted, but in this state the counter is even greater than the threshold, so the repetition is repeated again
  • Now every item added to the map will call rehash () which has a huge performance impact.

When this happens in a multithreaded environment, all other threads wait (parked) to block () until the rehash () and put () operations are done (and then the next one will retry rehash ()). You can imagine the performance impact this ...

I don't understand how this error can survive so many versions, no one notices, despite the class being widely used. Maybe I missed something?

Suggested solution:

Update the c variable after renaming is complete. Add between lines 105 and 106:

c = count + 1

      

+3


source to share


1 answer


The bug has been reported in Groovy JIRA https://issues.apache.org/jira/browse/GROOVY-7448 and has now been fixed.



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

      

0


source







All Articles