matrei commented on code in PR #16545:
URL: https://github.com/apache/grails-core/pull/16545#discussion_r4422656173


##########
grails-doc/src/en/guide/deployment/deploymentTasks.adoc:
##########
@@ -80,6 +80,46 @@ 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

Review Comment:
   With that sentence gone, the only Java 25-specific part of this section is 
the `UseCompressedClassPointers` note. Everything else applies to Java 21 too, 
and someone on 21 who reads "Recommended settings for Java 25:" (line 89) will 
assume it doesn't apply to them. I'd drop "Java 25" here and on line 89, and 
change the link text in `deploymentStandalone.adoc:48` to match. The 
`java25-memory` anchor can stay.
   
   ```suggestion
   === Metaspace and Off-Heap Memory
   ```



##########
grails-doc/src/en/guide/upgrading/upgrading80x.adoc:
##########
@@ -4837,3 +4837,39 @@ With `DISCRIMINATOR` multi-tenancy, GORM for MongoDB, 
GORM for Neo4j and GORM's
 * Without a current tenant, these methods throw `TenantNotFoundException`, as 
a query does. `getAll` without ids still returns an empty list.
 * Inside `Tenants.withoutId { }` a lookup by id is still not restricted to a 
tenant, and neither is a proxy returned by `load` or `proxy` that is 
initialized there. As the previous note says, only `withoutId` lifts the 
restriction: a tenant id of `default` restricts a lookup by id to that tenant 
as it restricts a query.
 * A lookup by id still finds an instance that the session holds for the 
current tenant, including one that has been saved but not flushed, and it does 
not flush the session.
+
+
+[[java25-memory-upgrade]]
+==== 88. Java 25 Memory Requirements

Review Comment:
   8.0.x now has `==== 88. Lookups by id are restricted to the current tenant 
with DISCRIMINATOR multi-tenancy`. Git merges the two without a conflict, so 
it's easy to miss, but after a rebase on 8.0.x this one needs to be 89.
   
   ```suggestion
   ==== 89. Java 25 Memory Requirements
   ```
   
   Optional: "Requirements" reads as if Java 25 needs more memory, which the 
section itself says isn't the case. Something like "Metaspace and Compressed 
Class Pointers on Java 25" would describe what's in it.



##########
grails-doc/src/en/guide/upgrading/upgrading80x.adoc:
##########
@@ -4837,3 +4837,39 @@ With `DISCRIMINATOR` multi-tenancy, GORM for MongoDB, 
GORM for Neo4j and GORM's
 * Without a current tenant, these methods throw `TenantNotFoundException`, as 
a query does. `getAll` without ids still returns an empty list.
 * Inside `Tenants.withoutId { }` a lookup by id is still not restricted to a 
tenant, and neither is a proxy returned by `load` or `proxy` that is 
initialized there. As the previous note says, only `withoutId` lifts the 
restriction: a tenant id of `default` restricts a lookup by id to that tenant 
as it restricts a query.
 * A lookup by id still finds an instance that the session holds for the 
current tenant, including one that has been saved but not flushed, and it does 
not flush the session.
+
+
+[[java25-memory-upgrade]]
+==== 88. Java 25 Memory Requirements
+
+Grails 8 supports Java 25 (the next LTS after Java 21).

Review Comment:
   Nit: section 1 says this now (line 57), so it can go here. Also on line 57: 
the rest of the file puts one sentence per line.
   
   ```suggestion
   ```



##########
grails-doc/src/en/guide/upgrading/upgrading80x.adoc:
##########
@@ -4837,3 +4837,39 @@ With `DISCRIMINATOR` multi-tenancy, GORM for MongoDB, 
GORM for Neo4j and GORM's
 * Without a current tenant, these methods throw `TenantNotFoundException`, as 
a query does. `getAll` without ids still returns an empty list.
 * Inside `Tenants.withoutId { }` a lookup by id is still not restricted to a 
tenant, and neither is a proxy returned by `load` or `proxy` that is 
initialized there. As the previous note says, only `withoutId` lifts the 
restriction: a tenant id of `default` restricts a lookup by id to that tenant 
as it restricts a query.
 * A lookup by id still finds an instance that the session holds for the 
current tenant, including one that has been saved but not flushed, and it does 
not flush the session.
+
+
+[[java25-memory-upgrade]]
+==== 88. Java 25 Memory Requirements
+
+Grails 8 supports Java 25 (the next LTS after Java 21).
+Both Java 21 and Java 25 leave metaspace unlimited by default unless you set 
an explicit ceiling with `-XX:MaxMetaspaceSize`; this default has been 
unchanged since JDK 8.
+On both versions, `-XX:MetaspaceSize` sets the initial high-water mark that 
triggers garbage collection, not a limit on metaspace growth. The JVM adjusts 
this threshold according to metadata usage.
+
+A Grails application loads a large number of classes — plugins, Hibernate 
entity mappings, GSP-compiled views, Spring beans — so metaspace can account 
for a significant part of the process's native memory on either JDK.
+In a container environment this is especially important: the 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.
+
+**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.
+
+The `-XX:+/-UseCompressedClassPointers` flag is deprecated in Java 25 and will 
be removed in a future release.
+
+* If your startup scripts pass `-XX:+UseCompressedClassPointers`, remove it 
because it is the default.
+* If they pass `-XX:-UseCompressedClassPointers` to disable compressed class 
pointers, note that dropping it changes the memory layout and has to be checked 
against the measured class-space use.

Review Comment:
   > will be removed in a future release
   
   The deployment note already says when: JDK-8363996 makes the flag obsolete 
in JDK 27, where it is accepted with a warning and ignored. That matters most 
for the minus form. From 27 the VM always uses compressed class pointers, so 
the class-space check in the second bullet is needed whether or not someone 
drops the flag. Something like:
   
   ```suggestion
   The `-XX:+/-UseCompressedClassPointers` flag is deprecated in Java 25 and 
becomes obsolete in JDK 27 
(https://github.com/openjdk/jdk/commit/da296cbea1[JDK-8363996]), where it is 
accepted with a warning and ignored.
   
   * If your startup scripts pass `-XX:+UseCompressedClassPointers`, remove it 
because it is the default.
   * If they pass `-XX:-UseCompressedClassPointers` to disable compressed class 
pointers, check the class-space use that `jcmd <pid> VM.metaspace` reports 
before you drop it.
   Dropping it changes the memory layout, and from JDK 27 compressed class 
pointers are always on, whether or not the flag is there.
   ```



##########
grails-doc/src/en/guide/upgrading/upgrading80x.adoc:
##########
@@ -4837,3 +4837,39 @@ With `DISCRIMINATOR` multi-tenancy, GORM for MongoDB, 
GORM for Neo4j and GORM's
 * Without a current tenant, these methods throw `TenantNotFoundException`, as 
a query does. `getAll` without ids still returns an empty list.
 * Inside `Tenants.withoutId { }` a lookup by id is still not restricted to a 
tenant, and neither is a proxy returned by `load` or `proxy` that is 
initialized there. As the previous note says, only `withoutId` lifts the 
restriction: a tenant id of `default` restricts a lookup by id to that tenant 
as it restricts a query.
 * A lookup by id still finds an instance that the session holds for the 
current tenant, including one that has been saved but not flushed, and it does 
not flush the session.
+
+
+[[java25-memory-upgrade]]
+==== 88. Java 25 Memory Requirements
+
+Grails 8 supports Java 25 (the next LTS after Java 21).
+Both Java 21 and Java 25 leave metaspace unlimited by default unless you set 
an explicit ceiling with `-XX:MaxMetaspaceSize`; this default has been 
unchanged since JDK 8.
+On both versions, `-XX:MetaspaceSize` sets the initial high-water mark that 
triggers garbage collection, not a limit on metaspace growth. The JVM adjusts 
this threshold according to metadata usage.
+
+A Grails application loads a large number of classes — plugins, Hibernate 
entity mappings, GSP-compiled views, Spring beans — so metaspace can account 
for a significant part of the process's native memory on either JDK.
+In a container environment this is especially important: the 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.
+
+**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.
+
+The `-XX:+/-UseCompressedClassPointers` flag is deprecated in Java 25 and will 
be removed in a future release.
+
+* If your startup scripts pass `-XX:+UseCompressedClassPointers`, remove it 
because it is the default.
+* If they pass `-XX:-UseCompressedClassPointers` to disable compressed class 
pointers, note that dropping it changes the memory layout and has to be checked 
against the measured class-space use.
+
+See <<java25-memory,Java 25 Memory Considerations>> 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 in the first round (I mentioned this 
line last time too), and the link text is the heading from two rounds ago.
   
   On my question from the first round: `<<java25-memory,…>>` does resolve on 
the chapter pages, because `DocPublisher.linkAcrossChapters` rewrites 
cross-chapter fragment links, so this form is fine.
   
   ```suggestion
   See <<java25-memory,Metaspace and Off-Heap Memory>> in the deployment guide 
for full details, including a systemd service unit example.
   ```
   
   (If the heading keeps "Java 25", use that as the link text instead.)



##########
grails-doc/src/en/guide/deployment/deploymentTasks.adoc:
##########
@@ -80,6 +80,46 @@ 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. 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.

Review Comment:
   > Java 25 uses more off-heap memory than earlier LTS releases.
   
   I asked about a source for this in both earlier rounds and haven't seen one. 
The upgrade note in this PR now says Java 21 and Java 25 behave the same for 
metaspace, so the two pages contradict each other. Unless there's a measurement 
or a JDK release note behind it, please remove it. The suggestion also adds 
backticks around `MaxMetaspaceSize` and puts one sentence per line, like the 
rest of the file:
   
   ```suggestion
   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.
   ```



-- 
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