{"id":1977,"date":"2021-09-15T10:49:22","date_gmt":"2021-09-15T10:49:22","guid":{"rendered":"https:\/\/smartfoxserver.com\/blog\/?p=1977"},"modified":"2021-09-15T10:49:22","modified_gmt":"2021-09-15T10:49:22","slug":"sfs2x-memory-settings-and-garbage-collection-part-2","status":"publish","type":"post","link":"https:\/\/smartfoxserver.com\/blog\/sfs2x-memory-settings-and-garbage-collection-part-2\/","title":{"rendered":"SFS2X memory settings and garbage collection (part 2)"},"content":{"rendered":"\n<p>In our <a rel=\"noreferrer noopener\" aria-label=\"previous chapter (opens in a new tab)\" href=\"https:\/\/smartfoxserver.com\/blog\/sfs2x-memory-settings-and-garbage-collection-part-1\/\" target=\"_blank\">previous chapter<\/a> we have talked about the JVM memory configuration: when to use the defaults and when it might be necessary to fine tune the settings. We also touched on potential side effects of manual tuning and how it can sometimes backfire, for example, forcing the Garbage Collector (GC) to work harder.<\/p>\n\n\n\n<p>In the second part of this series we are taking a look at the different Garbage Collectors available, which is the best for a specific use case, and when it&#8217;s useful to switch to a different implementation.<\/p>\n\n\n\n<!--more-->\n\n\n\n<figure class=\"wp-block-image size-large is-resized noShadow\"><img loading=\"lazy\" src=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/gc-choices-1024x502.png\" alt=\"\" class=\"wp-image-1980\" width=\"512\" height=\"251\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/gc-choices-1024x502.png 1024w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/gc-choices-300x147.png 300w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/gc-choices-768x376.png 768w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/gc-choices-1536x753.png 1536w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/gc-choices-624x306.png 624w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/gc-choices.png 2016w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/figure>\n\n\n\n<h2>\u00bb The default Garbage Collector<\/h2>\n\n\n\n<p>At the time of writing this article (Sept. 2021) <strong>SmartFoxServer 2X<\/strong> runs on <strong>Java 8 <\/strong>by default, which in turn uses the <strong>Parallel GC<\/strong>. This is a multi-threaded collector that manages the heap in parallel while pausing the application threads, so it can momentarily stop the execution of our code while sweeping away the garbage.<\/p>\n\n\n\n<p>These pauses are usually very short, in the order of a few milliseconds for ordinary heap sizes but they can increase with very large memory settings (i.e. 8GB+ of allocated heap) and high server activity.<\/p>\n\n\n\n<p>The Parallel GC can also be fine tuned to control the number of threads used and the ideal pause length for each collection cycle, to avoid latency.<\/p>\n\n\n\n<p>If you&#8217;re interested to learn more about these settings you can take a look at the <a rel=\"noreferrer noopener\" aria-label=\"details on Oracle's website (opens in a new tab)\" href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/guides\/vm\/gctuning\/parallel.html\" target=\"_blank\">details on Oracle&#8217;s website<\/a>.<\/p>\n\n\n\n<h2>\u00bb More Garbage Collectors<\/h2>\n\n\n\n<p>Beyond the default GC the JVM offers <strong>other garbage collection options<\/strong> that can be used in different scenarios, depending on the use case, hardware available and Java Runtime (JRE) version.<\/p>\n\n\n\n<p>As of Q3 2021 the main options available in the JVM are:<\/p>\n\n\n\n<p><strong>CMS GC (Concurrent Mark Sweep)<\/strong>: uses a similar multi-thread approach to the <strong>Parallel GC<\/strong> but doesn&#8217;t stop your application. Pauses are overall shorter and your application will share some CPU resources with the GC while the everything is running. It&#8217;s a tradeoff where you pay a bit of the CPU budget for a less intrusive GC cycles and virtually no pauses.<\/p>\n\n\n\n<p><strong>G1GC (Garbage First)<\/strong>: is a garbage collector designed for multi-processor machines using large heap sizes (<a rel=\"noreferrer noopener\" aria-label=\"tens of GBs according to the docs (opens in a new tab)\" href=\"https:\/\/docs.oracle.com\/javase\/9\/gctuning\/garbage-first-garbage-collector.htm#JSGCT-GUID-0394E76A-1A8F-425E-A0D0-B48A3DC82B42\" target=\"_blank\">tens of GBs according to the docs<\/a>) aiming at consistent, small GC times. This is typically not recommended for SFS2X as it occupies a very small heap and the need to switch to G1GC would be warranted only by very memory hungry Extensions.<\/p>\n\n\n\n<p><strong>ZGC: <\/strong>this is a more recent implementation, released as production-ready since JDK15. It uses a low-latency, concurrent algorithm and guarantees pauses &lt; 10ms for application threads. This is particularly good for low-latency environments such as game servers and it can also replace the G1GC. It could be a consideration to switch to ZGC for developers running on JRE 15 or higher, for a high traffic applications.<\/p>\n\n\n\n<p>This is not meant as a comprehensive list of all possibile GC options and in fact there are more, but these represent the most commonly used on the server side these days. <\/p>\n\n\n\n<h2>Which GC should I use?<\/h2>\n\n\n\n<p>We will answer the question by taking a look at a real-life production example where we tested different JREs and GC implementations under the same testing conditions. <\/p>\n\n\n\n<p>We tested a real-time, memory-intensive SFS2X Extension where users are grouped in Rooms of 8-10 players and sending 20 positional updates per second. For the tests we used an Intel Xeon (quad-core 2.0Ghz) with 32GB RAM, and Ubuntu 20.04 Server installed without any custom JVM or Linux settings. <\/p>\n\n\n\n<p>Here&#8217;s the example running on vanilla SFS2X 2.17.0 \/ JRE 8 \/ default GC (Parallel)<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"674\" src=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-1024x674.png\" alt=\"\" class=\"wp-image-1995\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-1024x674.png 1024w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-300x197.png 300w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-768x505.png 768w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-1536x1010.png 1536w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-2048x1347.png 2048w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-624x411.png 624w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Next is the same test running under the same SFS2X \/ JRE 8 \/ using the G1GC collector:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"670\" src=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-G1GC-1024x670.png\" alt=\"\" class=\"wp-image-1996\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-G1GC-1024x670.png 1024w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-G1GC-300x196.png 300w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-G1GC-768x502.png 768w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-G1GC-1536x1005.png 1536w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-G1GC-2048x1340.png 2048w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK8-MemHeavy-2.6K-G1GC-624x408.png 624w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Finally the same test once again, this time under JRE 15, using the ZGC:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"643\" src=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK15-MemHeavy-2.6K-ZGC-1024x643.png\" alt=\"\" class=\"wp-image-1997\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK15-MemHeavy-2.6K-ZGC-1024x643.png 1024w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK15-MemHeavy-2.6K-ZGC-300x188.png 300w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK15-MemHeavy-2.6K-ZGC-768x482.png 768w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK15-MemHeavy-2.6K-ZGC-1536x965.png 1536w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK15-MemHeavy-2.6K-ZGC-2048x1286.png 2048w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/09\/JDK15-MemHeavy-2.6K-ZGC-624x392.png 624w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>It can be noted that these GCs work in fairly different ways and use different strategies for memory management. The <strong>default Parallel GC<\/strong> tends to be more conservative, often reducing the allocated heap, when possible. Instead <strong>G1GC and ZGC<\/strong> (designed for memory hungry apps) tend to leave more allocated heap even when it&#8217;s not strictly needed.<\/p>\n\n\n\n<p>Generally speaking we recommend using the standard <strong>Parallel GC found in JDK8<\/strong> as it is more than enough for a multitude of turn-based and real-time games that don&#8217;t use very large amounts of heap (e.g. tens of GBs). <\/p>\n\n\n\n<p>However if your application does fall in the &#8220;memory-hungry&#8221; category with heap sizes that easily reach the 10-20GB mark (and higher) you will likely benefit from using the <strong>G1GC<\/strong> or, even better, the <strong>ZGC<\/strong> (which requires switching to a different JDK).<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3>\u00bb Useful resources<\/h3>\n\n\n\n<p>If you want to dive deeper into this subject and learn more about the various Garbage Collection options we recommend these external articles:<\/p>\n\n\n\n<ul><li><a rel=\"noreferrer noopener\" aria-label=\"Tuning Garbage Collection (opens in a new tab)\" href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/guides\/vm\/gctuning\/toc.html\" target=\"_blank\">Tuning Garbage Collection<\/a> (Oracle)<\/li><li><a rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\" href=\"https:\/\/www.baeldung.com\/jvm-garbage-collectors\" target=\"_blank\">JVM Garbage Collectors<\/a> (Baeldung)<\/li><li><a rel=\"noreferrer noopener\" aria-label=\"Seven Types of Garbage Collectors (opens in a new tab)\" href=\"https:\/\/medium.com\/@hasithalgamge\/seven-types-of-java-garbage-collectors-6297a1418e82\" target=\"_blank\">Seven Types of Garbage Collectors<\/a> (Medium)<\/li><\/ul>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In our previous chapter we have talked about the JVM memory configuration: when to use the defaults and when it might be necessary to fine tune the settings. We also touched on potential side effects of manual tuning and how it can sometimes backfire, for example, forcing the Garbage Collector (GC) to work harder. In [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[23],"tags":[141,70,139,105,140,68,34,7],"_links":{"self":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/1977"}],"collection":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/comments?post=1977"}],"version-history":[{"count":20,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/1977\/revisions"}],"predecessor-version":[{"id":2047,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/1977\/revisions\/2047"}],"wp:attachment":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/media?parent=1977"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/categories?post=1977"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/tags?post=1977"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}