matrei commented on code in PR #16545: URL: https://github.com/apache/grails-core/pull/16545#discussion_r4203194992
########## grails-doc/src/en/guide/deployment/deploymentTasks.adoc: ########## @@ -80,6 +80,49 @@ Configure writable paths explicitly when using a read-only root filesystem. Heap dumps and other diagnostics need a writable destination with sufficient space and controlled access. Keep JVM debugging and management interfaces private; they should not be exposed through the application's public ingress. +[[java25-memory]] +=== Java 25 Metaspace and Off-Heap Memory + +Java 25 uses more off-heap memory than earlier LTS releases, primarily because newer JVM features, optimized garbage collectors, and container-aware runtimes proactively allocate non-heap space (such as Metaspace, thread stacks, and JFR buffers) to improve performance and reduce GC frequency. +On earlier JDKs the initial metaspace high-water mark was approximately 12-21 MB; on Java 25 there is no upper limit unless you set one explicitly. +A Grails application that loads many classes (plugins, Hibernate mappings, GSP-compiled views) can therefore consume substantially more native memory than the same application on Java 21. + +In a container environment this matters most: the JVM heap limit (`-Xmx` or `-XX:MaxRAMPercentage`) does not cover metaspace, so a container sized only for the heap can be killed by the container runtime's OOM killer when metaspace grows beyond the remaining native memory budget. + +**Recommended settings for Java 25:** + +Set an explicit metaspace ceiling so the JVM raises `OutOfMemoryError: Metaspace` before the container runtime kills the process: + +[source,bash] +---- +java -Xmx512m -XX:MaxMetaspaceSize=256m -Dgrails.env=prod -jar application.jar +---- + +Or, using percentage-based heap sizing in a container: + +[source,bash] +---- +java -XX:MaxRAMPercentage=60.0 -XX:MaxMetaspaceSize=256m -Dgrails.env=prod -jar application.jar +---- + +Tune `-XX:MaxMetaspaceSize` from a measured baseline: run the application under representative load, observe the metaspace usage with `jcmd <pid> VM.metaspace` or a monitoring agent, and add a safety margin. + +For a systemd service unit, add the flags to `ExecStart`: + +[source,ini] +---- +ExecStart=/usr/bin/java \ + -Xmx512m \ + -XX:MaxMetaspaceSize=256m \ + -Dgrails.env=prod \ + -jar /opt/myapp/application.jar \ + --spring.config.additional-location=file:/etc/myapp/ +---- + +NOTE: The `-XX:+/-UseCompressedClassPointers` flag is deprecated in Java 25 and will be removed in a future release. +If your startup scripts pass `-XX:+UseCompressedClassPointers` (the default), remove it. +If they pass `-XX:-UseCompressedClassPointers` to disable compressed class pointers, remove it and, if you encounter class-space exhaustion, increase the class space with `-XX:CompressedClassSpaceSize=4g` instead. Review Comment: The deprecation note is still correct, but the removal is already scheduled: [JDK-8363996](https://github.com/openjdk/jdk/commit/da296cbea1) makes `UseCompressedClassPointers` obsolete in JDK 27, which means the flag is accepted with a warning and ignored. Could you say that here? Anyone who relies on `-XX:-UseCompressedClassPointers` will be using compressed class space on 27 whether or not they remove the flag. ########## grails-doc/src/en/guide/deployment/deploymentTasks.adoc: ########## @@ -80,6 +80,49 @@ Configure writable paths explicitly when using a read-only root filesystem. Heap dumps and other diagnostics need a writable destination with sufficient space and controlled access. Keep JVM debugging and management interfaces private; they should not be exposed through the application's public ingress. +[[java25-memory]] +=== Java 25 Metaspace and Off-Heap Memory + +Java 25 uses more off-heap memory than earlier LTS releases, primarily because newer JVM features, optimized garbage collectors, and container-aware runtimes proactively allocate non-heap space (such as Metaspace, thread stacks, and JFR buffers) to improve performance and reduce GC frequency. +On earlier JDKs the initial metaspace high-water mark was approximately 12-21 MB; on Java 25 there is no upper limit unless you set one explicitly. +A Grails application that loads many classes (plugins, Hibernate mappings, GSP-compiled views) can therefore consume substantially more native memory than the same application on Java 21. Review Comment: > On earlier JDKs the initial metaspace high-water mark was approximately 12-21 MB; on Java 25 there is no upper limit unless you set one explicitly. This still says earlier JDKs had a limit and Java 25 doesn't. The high-water mark (`MetaspaceSize`) only decides when the first metaspace-triggered GC happens, and `MaxMetaspaceSize` has had no limit by default since Java 8, so nothing changed between 21 and 25. The "therefore … substantially more native memory than the same application on Java 21" on the next line depends on that claim, so it goes too. > primarily because newer JVM features, optimized garbage collectors, and container-aware runtimes proactively allocate non-heap space (such as Metaspace, thread stacks, and JFR buffers) to improve performance and reduce GC frequency Do you have a source for this? Metaspace isn't allocated ahead of time to reduce GC frequency, JFR buffers only exist when JFR is enabled, and container awareness has been in the JVM since JDK 10. Unless there's a measurement or a JDK release note behind it, I'd drop the explanation and state only what we know. Something like: > Metaspace has no upper limit by default (`MaxMetaspaceSize` is unlimited on every JDK since 8). Grails applications load many classes (plugins, Hibernate mappings, GSP-compiled views), so metaspace can make up a large part of the process's native memory. If a heading without "Java 25" reads better after that, "Metaspace and Off-Heap Memory" would fit. ########## grails-doc/src/en/guide/upgrading/upgrading80x.adoc: ########## @@ -4716,3 +4716,26 @@ for template renders: Objects passed with `bean` or `collection` are not part of `model`. See link:theWebLayer.html#definingInterceptors[Defining Interceptors]. + +[[java25-memory-upgrade]] +==== 85. Java 25 Memory Requirements + +**What to do when running on Java 25:** + +Set an explicit metaspace ceiling with `-XX:MaxMetaspaceSize` so the JVM raises `OutOfMemoryError: Metaspace` before the container runtime kills the process: + +[source,bash] +---- +java -Xmx512m -XX:MaxMetaspaceSize=256m -Dgrails.env=prod -jar application.jar +---- + +Or, using percentage-based heap sizing in a container: + +[source,bash] +---- +java -XX:MaxRAMPercentage=60.0 -XX:MaxMetaspaceSize=256m -Dgrails.env=prod -jar application.jar +---- + +Tune `-XX:MaxMetaspaceSize` from a measured baseline: run the application under representative load, observe metaspace usage with `jcmd <pid> VM.metaspace`, and add a safety margin. + +See link:deployment.html#java25-memory[Java 25 Metaspace and Off-Heap Memory] in the deployment guide for full details, including systemd service unit examples and guidance on the metaspace free-ratio flags. Review Comment: > including systemd service unit examples and guidance on the metaspace free-ratio flags The free-ratio guidance was removed, so this should only mention the systemd example. ########## grails-doc/src/en/guide/deployment/deploymentStandalone.adoc: ########## @@ -45,6 +45,7 @@ java -Xmx768m -Dgrails.env=prod -jar build/libs/myapp-0.1.jar \ This example requires `/etc/myapp/` to exist. Place external `application.yml` or `application.properties` there. Size the heap for your workload and leave memory for metaspace, thread stacks, direct buffers, native libraries, and the operating system. +On Java 25 (and since Java 8), metaspace is unbounded by default; see <<java25-memory,Java 25 Memory Considerations>> for recommended settings. Review Comment: > On Java 25 (and since Java 8), metaspace is unbounded by default; see <<java25-memory,Java 25 Memory Considerations>> The link text still uses the old heading. Since this is true on every JDK, I'd simplify it to "Metaspace is unbounded by default; see <<java25-memory,…>> for recommended settings." ########## grails-doc/src/en/guide/upgrading/upgrading80x.adoc: ########## @@ -4716,3 +4716,26 @@ for template renders: Objects passed with `bean` or `collection` are not part of `model`. See link:theWebLayer.html#definingInterceptors[Defining Interceptors]. + +[[java25-memory-upgrade]] +==== 85. Java 25 Memory Requirements + +**What to do when running on Java 25:** Review Comment: Section 85 now starts straight with "What to do when running on Java 25" without saying why. Could you add one sentence of context (metaspace has no default limit, and a container sized only for the heap can be OOM-killed)? Since this is the upgrade guide, I'd also keep a short bullet telling people to remove `-XX:+/-UseCompressedClassPointers` from their startup scripts. That's something they have to do when moving to 25, and the upgrade guide is where they'll look for it. -- 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]
