Created
April 23, 2026 15:18
-
-
Save lrytz/c210446b9c49a17a2c93606dd4fafb6b to your computer and use it in GitHub Desktop.
Nested object locking
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Minimal reproducer for the unbounded native-memory growth exposed by | |
| // scala/scala3#25777. Each iteration holds 100 `synchronized` blocks nested on | |
| // distinct objects, then drops them. | |
| // | |
| // * On JDK <= 22 (`-XX:LockingMode=1` default, legacy stack locking): each | |
| // lock's bookkeeping lives in a Java stack slot. Nothing is allocated in | |
| // native memory; the loop runs in steady state. | |
| // | |
| // * On JDK 23+ (`-XX:LockingMode=2` default, new lightweight locking): each | |
| // thread has a fixed 8-slot LockStack. Taking a 9th or deeper lock allocates | |
| // a heavyweight `ObjectMonitor` in native memory. These are eligible for | |
| // async deflation after release, but this loop produces new monitors faster | |
| // than the deflater can reclaim, so the "Object Monitors" category of | |
| // Native Memory Tracking grows without bound. | |
| // | |
| // Compile & run: | |
| // javac LockNesting.java | |
| // java -XX:NativeMemoryTracking=summary LockNesting & | |
| // PID=$! | |
| // # watch the "Object Monitors" line grow on JDK 23+, stay at 0 on <= 22 | |
| // while true; do jcmd $PID VM.native_memory summary | grep -E 'Object Monitors'; sleep 2; done | |
| // | |
| // Or see inflation messages directly: | |
| // java -Xlog:monitorinflation=debug LockNesting | head -20 | |
| public class LockNesting { | |
| public static void main(String[] args) { | |
| while (true) { | |
| Object[] objs = new Object[100]; | |
| for (int i = 0; i < objs.length; i++) objs[i] = new Object(); | |
| nested(objs, 0); | |
| } | |
| } | |
| static void nested(Object[] objs, int i) { | |
| if (i == objs.length) return; | |
| synchronized (objs[i]) { | |
| nested(objs, i + 1); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment