Repository navigation
Fix robots staying in memory on Java 24+, and the client spinning when CLEARDATA cannot delete - #120
Conversation
…s in memory with Java 24+ ThreadManager adds the thread to outputStreamThreads before it opens a robot's file or creates its data directory, and only RobocodeSecurityPolicy removes it again, when the SecurityManager asks about the write. Java 24+ has no SecurityManager, so the thread stays in the list, and through it the whole robot: its proxy, peer, class loader and static data. This hits every robot that writes a file, so a long run runs out of memory. Three RoboRumble clients on Java 25, with the -Xmx512M of roborumble.bat, ran out of memory after 59, 86 and 362 battles. The battle thread died and the clients hung. A heap dump showed 164 RobotPeers, of which 162 were only held through this list. Version 1.11.2 does the same. Java 8 and 21 do not, as their SecurityManager removes the thread. With this change, a client on Java 25 ran 333 battles without running out of memory, and held only the 2 robots of the running battle when checked after 120 battles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…TA, and wait if it still fails closeOpenStreams only knows the files that robots open for writing through Robocode. A file a robot opened for reading and did not close stays open until the stream is garbage collected, and until then it cannot be deleted on Windows, so the client skips battles. Skipping a battle allocates next to nothing, so a collection can be a long time coming. With a test robot that does this, in a 3-robot list on Windows, clients skipped 69% of their battles on Java 25 and 82% on Java 21 over two minutes. No real robot did it in about 26,000 battles, but only about 3,200 of those were on Windows. So when the data cannot be deleted, collect the garbage and try again. The same clients then fought 20 of 20 battles on each of Java 8, 21 and 25. If that fails too, e.g. as another program holds the file, the client had nothing to slow it down. It went through its battles at full speed and asked the server for the ratings on each iteration: 348 iterations, 17,362 skipped battles and 4 MB of log in a minute with ITERATE=YES. Waiting a minute before going on brought this down to 1 iteration and 2 skipped battles in 130 seconds. Each battle tries again, so the client fights as soon as the data can be deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Interrupted pauses do not stop the runner, so the restored interrupt flag bypasses subsequent backoff and permits a tight skip/download loop with undeletable data and ITERATE=YES.
1 open issue
What changed in this PR
This PR addresses robot retention on Java 24+ and prevents persistent CLEARDATA deletion failures from making RoboRumble repeatedly skip battles at full speed.
Changes:
- Removes robot threads from file-access tracking in
finallyblocks. - Retries data deletion after garbage collection and waits one minute if deletion still fails.
- Adds regression tests and updates the release notes.
| File | Description |
|---|---|
versions.md |
Documents the memory fix and revised CLEARDATA behavior. |
robocode.roborumble/src/test/java/net/sf/robocode/roborumble/battlesengine/BattlesRunnerTest.java |
Adds deletion-retry and persistent-failure tests. |
robocode.roborumble/src/main/java/net/sf/robocode/roborumble/battlesengine/BattlesRunner.java |
Adds garbage-collection retry and failure backoff. |
robocode.host/src/test/java/net/sf/robocode/host/security/ThreadManagerTest.java |
Tests that opening a robot file does not retain its thread. |
robocode.host/src/main/java/net/sf/robocode/host/security/ThreadManager.java |
Ensures thread tracking is cleared after directory creation and file opening. |
Review effort: Balanced
|
Thanks, these are great finds, and both got past my follow-up. Merged. I reran the full suite on Java 8 and 25 and it passes, and And no problem about the name. I'd read that too broadly. When the server is up, we'll add the link to AutoRumble without that kind of credit in the README. |
|
Heads up: we're moving the tests from JUnit 4 to JUnit 6 (#121 and #122). New tests need the Jupiter API, and any open branch with JUnit 4 tests will need updating, such as your batch upload code if it has tests. Assertions take the message last, and |

This was largely written by Claude Opus 5.5
Two bugs found while testing 1.12.0 against AutoRumble with real robots, on Linux (Java 21) and Windows (Java 8, 21 and 25).
Robots stay in memory on Java 24+
ThreadManageradds a robot's thread tooutputStreamThreadsbefore it opens a file or creates the data directory, and onlyRobocodeSecurityPolicyremoves it again. Java 24+ has no SecurityManager, so the thread stays in the list, and through it the whole robot: its proxy, peer, class loader and static data.Every robot that writes a file is affected, so a long run runs out of memory. Three clients on Java 25, with the
-Xmx512Mofroborumble.bat, ran out of memory after 59, 86 and 362 battles. The battle thread died and the clients hung. A heap dump showed 164RobotPeers, of which 162 were held through this list.This is not from #117 or #118: 1.11.2 does the same, keeping 80 robots after 41 battles. Java 8 and 21 are not affected, as their SecurityManager removes the thread.
The fix removes the thread once the file is open or the directory is created.
CLEARDATA can make the client skip battles or spin
closeOpenStreamsonly knows files opened for writing through Robocode. A file a robot opened for reading and did not close cannot be deleted on Windows until the stream is garbage collected, and skipping a battle allocates next to nothing, so a collection can be a long time coming. With a test robot that does this, in a 3-robot list, clients skipped 69% of their battles on Java 25 and 82% on Java 21 over two minutes. No real robot did it in about 26,000 battles, though only about 3,200 of those were on Windows.If the file stays undeletable, e.g. as another program holds it, the client goes through its battles at full speed and asks the server for the ratings on each iteration: 348 iterations, 17,362 skipped battles and 4 MB of log in a minute with
ITERATE=YES.The fix collects the garbage and tries again, and if that fails too, waits a minute before going on. A battle is still skipped when the data cannot be deleted, so results never claim otherwise.
Testing
ThreadManagerTest,BattlesRunnerTeston Java 8, 21 and 25 on WindowsThe full test suite has not been run on this branch.
🤖 Generated with Claude Code