⚡ make Skiko's periodic System.gc() a concurrent cycle - #4913
Open
guiyanakuang wants to merge 1 commit into
Open
⚡ make Skiko's periodic System.gc() a concurrent cycle#4913guiyanakuang wants to merge 1 commit into
guiyanakuang wants to merge 1 commit into
Conversation
Skiko's FrameWatcher calls System.gc() every 30 s whenever more than 1000 frames were rendered in the interval, to release native Skia peers held by tiny Java wrappers. With G1 that is a stop-the-world full collection: on a 2-core VM it measured 54-200 ms per call (p50 83 ms, one outlier of 2.3 s), so any animating window stalls twice a minute. -XX:+ExplicitGCInvokesConcurrent turns those calls into a concurrent cycle. The reference processing Skiko relies on still happens at remark, but the stop-the-world part drops to a 3-8 ms concurrent-start pause. In the A/B run the number of pauses above 50 ms went from 24 to 3 and none exceeded 100 ms, with the lowest total pause time of all variants. The flag is ignored by collectors other than G1, so single-core machines that default to Serial GC see no change. Follow-up to #4908.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #4908 and the GC flags withdrawn from #4912. This PR adds exactly one JVM flag,
-XX:+ExplicitGCInvokesConcurrent, backed by an A/B run on a 2-core VM. The other candidates were measured too and are documented below so they do not get re-proposed without data.What the flag fixes
Skiko's
FrameWatcher(org.jetbrains.skiko.Setup,skiko.gc.auto) callsSystem.gc()every 30 s whenever more than 1000 frames were rendered in that interval, to release native Skia peers held by small Java wrappers. Under G1 an explicitSystem.gc()is a stop-the-world full collection. In the baseline run it fired 20 times in 23 minutes at 54-200 ms each (p50 83 ms), and one run hit a 2.3 s full GC, so any window that is animating stalls twice a minute.With
-XX:+ExplicitGCInvokesConcurrentthe same calls start a concurrent cycle instead. The reference processing Skiko relies on still happens at remark; the stop-the-world part becomes a 3-8 ms concurrent-start pause plus a remark pause (p50 20 ms on the test VM). The flag is ignored by collectors other than G1, so single-core machines that default to Serial GC are unaffected. It reaches the packaged app throughinitJvmArgs(verified ingenerated.conveyor.conf).A/B setup
~/.crosspaste, 1 min warm-up, 6 min clipboard load (180 items: 1600x1000 random bitmaps and 200-line texts viaSet-Clipboard), 16 min idle. Samples every 30 s (working set, private bytes, threads, CPU),-Xlog:gc*for pauses.System.gc()fires every 30 s); "tray" = second launch, no window, the reporter's scenario.Idle CPU in the "shown" rows is dominated by the window rendering continuously (B, C and D rendered for the whole idle phase, A stopped after 12 min), not by GC; compare A2/C2 for the GC-only picture.
What was rejected, with data
System.gc()resets the 5-minute idle clock. In the tray scenario it fired twice, returned an expanded heap (268 MB committed to 114 MB, private bytes 568 to 417 MB) at the cost of two ~50-65 ms pauses per cycle, but ended at the same private bytes as the baseline, whose heap had simply not expanded that far. Real but conditional benefit; left out of this PR so it can be decided separately.-XX:+UseG1GC/-XX:ParallelGCThreads=4: not measured, dropped for the reasons in the 🐛 fix Coil memory cache limit being overridden to 435 MiB #4912 review (fixed count, forces G1 on single-core machines). The 2-core VM confirms JBR's own ergonomics already pick 2 parallel / 1 concurrent worker there.Side findings
JAVA_TOOL_OPTIONS; the A/B injected flags by starting the bundled runtime directly with the launcher's option list.Test plan
writeConveyorConfig -PappEnv=PRODUCTIONemits the flag intoapp.jvm.options