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]
