jdaugherty opened a new pull request, #16834:
URL: https://github.com/apache/grails-core/pull/16834

   ## Description
   
   The `grails-data-hibernate7-core` suite runs out of its default test fork 
heap on 9.0.x on every push since `5f2d6acde8` (2026-10-29), the first tested 
commit after the untested merge-ups of the 8.0.x specs of 2026-10-28/30, and 
#16624, which adds domain classes to the TCK, tips the Java 21 lanes over as 
well. On 8.0.x and 8.1.x the suite ends a few megabytes under the heap, with a 
full GC every few tests.
   
   A heap dump at the OOM (`-XX:+HeapDumpOnOutOfMemoryError`, so soft 
references are cleared) shows one dominator: `class 
java.beans.ThreadGroupContext`, the JDK Introspector's BeanInfo cache, retains 
203 MB, 41% of the heap, for 6,700 classes with 370,000 `MethodDescriptor`s. 
Groovy reads the BeanInfo of a class when it builds its MetaClass, and the 
cache is a `WeakHashMap` keyed by the class, so an entry is released only when 
the class is unloaded, which never happens in a test JVM. Over the suite the 
cache fills with the domain classes of the TCK and of each spec, at about 330 
method descriptors each (49% of the descriptors), and with every closure of 
every spec, each a class with an entry of its own (32%). Nothing GORM-specific 
leaks: after each spec the Hibernate `SessionFactoryRegistry`, 
`GormRegistry.allDatastores` and the connection pool threads are all at zero, 
as #16535 left them.
   
   `GrailsDataTckManager.cleanupSpec()` now flushes the Introspector's caches, 
as `cleanRegistry()` already removes the MetaClasses of the registered domain 
classes. The live set stops growing with the number of specs that have run; a 
class a later spec builds a MetaClass for is introspected again, which costs 
less than the full GCs it replaces. The Mongo and Neo4j managers override 
`cleanupSpec()` and call `super`, so every TCK-based suite gets the flush.
   
   ### Measurements
   
   Local, `:grails-data-hibernate7-core:test` at the default fork heap (512 
MB), Docker present so the container rows run:
   
   | Run | Result | Time | Full GCs | Live after the last full GC |
   |---|---|---|---|---|
   | 9.0.x `6b52c74ea9` | passed | 11m 33s | 1,042 | 480-505 MB from test 
~2,900 on |
   | #16624 head `af5d40aa04` | OOM at test 4,338 | 19m 55s | 1,200+ | 512 MB |
   | #16624 head, cache flushed after each spec | passed, 4,393 tests | 7m 10s 
| 904 | 458 MB peak, not growing |
   
   A per-spec probe (a Spock global extension reading the cache through 
reflection, not committed) shows the cache at 6,812 classes and 662,000 method 
descriptors at the end of the unflushed 9.0.x run, and under 200 classes at any 
point with the flush.
   
   ### Tests
   
   `GrailsDataTckManagerSpec` (in `grails-data-hibernate5-core`, where the TCK 
base classes are tested) gets a feature asserting that the BeanInfo the 
Introspector returns for a class is a different instance after `cleanupSpec()`. 
`:grails-data-hibernate5-core:test --tests GrailsDataTckManagerSpec` and 
`:grails-datamapping-tck:codeStyle` pass.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to