Java Garbage Collection in a nutshell
Posted: 21 Sep 2018 07:02
Do you think your Java app like i2p sticks to the specified heap size and garbage collection occurs when this heap is filled with unused objects to some degree? Then you need to read on.
A short look at the physical memory occupied by i2p reveals that it allocates much more than the specified heap size visible to i2p and set in wrapper.config.
Basic facts:
- All garbage collectors have a stop-the-world pause that locks everything. Some collectors do some work like object marking before concurrently. The length of the stop-the-world pause statistically adds to all timer values.
- In old times - when Java was slow and throughput a top priority - the goal for this pause was set at 200ms. This goal is the default in all collectors until today (Java 11).
- Today with fast processors the JVM will accumulate lots of unused memory adding to the need for physical RAM because it can be collected so fast.
My insights: 200ms is too long for any network application and also i2p. We want lower latency and are willing to sacrifice some throughput. With the G1 collector (default in Java >=9, needs to be specified with -XX:+UseG1GC otherwise if available) you can specify the pause time goal with -XX:MaxGCPauseMillis=xxx.
Effect on my machine: 20ms as a pause time goal reduced the allocated RAM by 50% and doubled the GC overhead from about 1 to 2% CPU. Well worth trying.
A short look at the physical memory occupied by i2p reveals that it allocates much more than the specified heap size visible to i2p and set in wrapper.config.
Basic facts:
- All garbage collectors have a stop-the-world pause that locks everything. Some collectors do some work like object marking before concurrently. The length of the stop-the-world pause statistically adds to all timer values.
- In old times - when Java was slow and throughput a top priority - the goal for this pause was set at 200ms. This goal is the default in all collectors until today (Java 11).
- Today with fast processors the JVM will accumulate lots of unused memory adding to the need for physical RAM because it can be collected so fast.
My insights: 200ms is too long for any network application and also i2p. We want lower latency and are willing to sacrifice some throughput. With the G1 collector (default in Java >=9, needs to be specified with -XX:+UseG1GC otherwise if available) you can specify the pause time goal with -XX:MaxGCPauseMillis=xxx.
Effect on my machine: 20ms as a pause time goal reduced the allocated RAM by 50% and doubled the GC overhead from about 1 to 2% CPU. Well worth trying.