[{"data":1,"prerenderedAt":1078},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjava-garbage-collection":3},{"id":4,"title":5,"body":6,"description":1063,"difficulty":1064,"extension":1065,"framework":1066,"frameworkSlug":223,"meta":1067,"navigation":1068,"order":714,"path":1069,"qaPath":1070,"seo":1071,"stem":1072,"subtopic":1073,"topic":1074,"topicSlug":1075,"updated":1076,"__hash__":1077},"blog\u002Fblog\u002Fjava-garbage-collection.md","Java Garbage Collection Deep Dive — G1, ZGC, Tuning, and Avoiding GC Pauses",{"type":7,"value":8,"toc":1033},"minimark",[9,14,27,31,47,63,70,74,81,102,113,120,124,131,142,145,151,165,171,174,178,187,194,201,208,239,247,266,290,293,302,306,313,321,328,348,366,369,373,380,457,461,472,479,486,490,493,515,518,524,527,533,536,540,646,651,655,659,669,673,718,728,732,751,755,761,765,779,789,882,889,893,985,992,996,1029],[10,11,13],"h2",{"id":12},"why-garbage-collection-is-an-interview-staple","Why garbage collection is an interview staple",[15,16,17,18,22,23,26],"p",{},"GC problems — long pauses, OOM errors, throughput degradation — are among the hardest\nissues to diagnose in production Java systems, and they often surface as latency spikes\nor cascading failures under load. Interviewers ask about GC to probe whether you understand\n",[19,20,21],"em",{},"how"," automatic memory management works, not just ",[19,24,25],{},"that"," it exists. This article gives you\na thorough mental model of the full GC landscape.",[10,28,30],{"id":29},"the-core-idea-reachability-not-reference-counting","The core idea: reachability, not reference counting",[15,32,33,34,38,39,42,43,46],{},"Java does not use reference counting (the way CPython does). Instead, an object is\nconsidered ",[35,36,37],"strong",{},"live"," if and only if it is ",[35,40,41],{},"reachable"," from a ",[35,44,45],{},"GC root"," — a locally\ndetermined set of objects guaranteed to be in use:",[48,49,50,54,57,60],"ul",{},[51,52,53],"li",{},"Local variables in active stack frames",[51,55,56],{},"Static fields of loaded classes",[51,58,59],{},"JNI references held by native code",[51,61,62],{},"Active thread objects",[15,64,65,66,69],{},"The GC traces the object graph from every root. Any object not found during that traversal\nis ",[35,67,68],{},"garbage"," and its memory can be reclaimed. This approach handles cyclic references\nautomatically (two objects pointing at each other but unreachable from any root are both\ncollected), which reference counting famously cannot do without extra machinery.",[10,71,73],{"id":72},"mark-and-sweep-the-foundational-algorithm","Mark-and-sweep — the foundational algorithm",[15,75,76,77,80],{},"All production JVM collectors are variations on ",[35,78,79],{},"mark-and-sweep",":",[82,83,84,90,96],"ol",{},[51,85,86,89],{},[35,87,88],{},"Mark"," — traverse the live object graph from GC roots; mark each live object.",[51,91,92,95],{},[35,93,94],{},"Sweep"," — scan the heap; reclaim any unmarked object's memory.",[51,97,98,101],{},[35,99,100],{},"Compact"," (in most modern collectors) — slide live objects together to eliminate\nfragmentation and reset the allocation pointer.",[103,104,109],"pre",{"className":105,"code":107,"language":108},[106],"language-text","Before GC:  [A live][B dead][C live][D dead][E live]\nAfter mark: [A ✓]   [B    ] [C ✓]   [D    ] [E ✓]\nAfter sweep:[A    ] [free  ] [C    ] [free  ] [E    ]\nAfter compact: [A][C][E][         free         ]\n","text",[110,111,107],"code",{"__ignoreMap":112},"",[15,114,115,116,119],{},"Compaction is what makes bump-pointer allocation (just increment a pointer for each\n",[110,117,118],{},"new",") possible after collection — it's why Java allocation is so fast.",[10,121,123],{"id":122},"generational-collection-the-big-performance-win","Generational collection — the big performance win",[15,125,126,127,130],{},"Empirically, ",[35,128,129],{},"most objects die young"," (the weak generational hypothesis). A typical\nproduction allocation profile looks like:",[48,132,133,136,139],{},[51,134,135],{},"~90 % of objects become garbage before their first GC",[51,137,138],{},"~9 % survive a few GC cycles",[51,140,141],{},"~1 % live for the lifetime of the application (singletons, caches)",[15,143,144],{},"Generational GC exploits this by splitting the heap:",[103,146,149],{"className":147,"code":148,"language":108},[106],"┌─────────────────────────────────────────────────────┐\n│                     Java Heap                       │\n│  ┌────────────────────────────┐ ┌─────────────────┐ │\n│  │      Young Generation      │ │  Old Generation  │ │\n│  │  ┌──────────┬────┬──────┐  │ │  (Tenured)       │ │\n│  │  │  Eden    │ S0 │  S1  │  │ │                  │ │\n│  │  └──────────┴────┴──────┘  │ │                  │ │\n│  └────────────────────────────┘ └─────────────────┘ │\n└─────────────────────────────────────────────────────┘\n",[110,150,148],{"__ignoreMap":112},[15,152,153,156,157,160,161,164],{},[35,154,155],{},"Minor GC"," (Young Gen only): objects that survive are copied between Survivor spaces\nand age-counted. After reaching ",[110,158,159],{},"-XX:MaxTenuringThreshold"," (default 15) they are\n",[35,162,163],{},"promoted"," to Old Gen.",[15,166,167,170],{},[35,168,169],{},"Full GC"," (entire heap): triggered when Old Gen fills up. It is expensive — sometimes\nmany seconds — because the collector processes far more data.",[15,172,173],{},"The insight: by collecting the small Young Gen frequently, most garbage is reclaimed\ncheaply before it ever pollutes the expensive-to-collect Old Gen.",[10,175,177],{"id":176},"the-gc-collector-lineup","The GC collector lineup",[179,180,182,183,186],"h3",{"id":181},"serial-gc-xxuseserialgc","Serial GC (",[110,184,185],{},"-XX:+UseSerialGC",")",[15,188,189,190,193],{},"Single-threaded, stop-the-world for all phases. Fastest ",[19,191,192],{},"per-collection"," on a single\ncore; slowest absolute pause when the heap is large. Right for tiny heaps (\u003C 256 MB),\nCLIs, or single-core containers.",[179,195,197,198,186],{"id":196},"parallel-gc-xxuseparallelgc","Parallel GC (",[110,199,200],{},"-XX:+UseParallelGC",[15,202,203,204,207],{},"Multi-threaded stop-the-world. Maximises ",[35,205,206],{},"throughput"," — the fraction of CPU time your\napplication runs rather than GC. All threads pause while GC threads work in parallel.\nThe default before Java 9; still ideal for batch jobs that care only about total\nprocessing speed.",[103,209,213],{"className":210,"code":211,"language":212,"meta":112,"style":112},"language-bash shiki shiki-themes github-light github-dark","java -XX:+UseParallelGC -XX:GCTimeRatio=19 BatchJob  # target 95 % app \u002F 5 % GC\n","bash",[110,214,215],{"__ignoreMap":112},[216,217,220,224,228,231,235],"span",{"class":218,"line":219},"line",1,[216,221,223],{"class":222},"sScJk","java",[216,225,227],{"class":226},"sj4cs"," -XX:+UseParallelGC",[216,229,230],{"class":226}," -XX:GCTimeRatio=19",[216,232,234],{"class":233},"sZZnC"," BatchJob",[216,236,238],{"class":237},"sJ8bj","  # target 95 % app \u002F 5 % GC\n",[179,240,242,243,246],{"id":241},"g1-gc-xxuseg1gc-default-since-java-9","G1 GC (",[110,244,245],{},"-XX:+UseG1GC",") — default since Java 9",[15,248,249,250,253,254,257,258,261,262,265],{},"G1 breaks the heap into equal-sized ",[35,251,252],{},"regions"," (1–32 MB, configured with\n",[110,255,256],{},"-XX:G1HeapRegionSize","). Each region is labelled Eden, Survivor, or Old dynamically.\nG1 collects the regions with the most garbage first (hence \"Garbage First\"), meeting a\n",[35,259,260],{},"configurable pause target"," (",[110,263,264],{},"-XX:MaxGCPauseMillis=200"," by default).",[103,267,269],{"className":210,"code":268,"language":212,"meta":112,"style":112},"java -XX:+UseG1GC -Xms4g -Xmx4g -XX:MaxGCPauseMillis=100 ApiServer\n",[110,270,271],{"__ignoreMap":112},[216,272,273,275,278,281,284,287],{"class":218,"line":219},[216,274,223],{"class":222},[216,276,277],{"class":226}," -XX:+UseG1GC",[216,279,280],{"class":226}," -Xms4g",[216,282,283],{"class":226}," -Xmx4g",[216,285,286],{"class":226}," -XX:MaxGCPauseMillis=100",[216,288,289],{"class":233}," ApiServer\n",[15,291,292],{},"G1 has short STW phases (initial mark, remark) and long concurrent phases, keeping\npauses in the tens of milliseconds range for heaps up to several GBs. It is the right\ndefault for most Java applications.",[15,294,295,298,299,301],{},[35,296,297],{},"Humongous objects"," (> 50 % of region size) bypass Young Gen and land in dedicated\ncontiguous regions. Frequent large short-lived allocations can pressure G1 significantly\n— increase ",[110,300,256],{}," to raise the humongous threshold.",[179,303,305],{"id":304},"cms-deprecated-and-removed","CMS — deprecated and removed",[15,307,308,309,312],{},"Concurrent Mark-Sweep was the first low-latency collector. It had two problems: no\ncompaction (leading to fragmentation and eventual \"concurrent mode failure\" full GCs)\nand high CPU overhead. Deprecated in Java 9, ",[35,310,311],{},"removed in Java 14",". Use G1 instead.",[179,314,316,317,320],{"id":315},"zgc-xxusezgc-production-since-java-15","ZGC (",[110,318,319],{},"-XX:+UseZGC",") — production since Java 15",[15,322,323,324,327],{},"ZGC targets ",[35,325,326],{},"sub-millisecond pauses"," regardless of heap size (tested at terabytes).\nIt achieves this with:",[48,329,330,336,342],{},[51,331,332,335],{},[35,333,334],{},"Colored pointers"," — GC metadata encoded in the unused upper bits of 64-bit\nreferences, avoiding per-object GC state.",[51,337,338,341],{},[35,339,340],{},"Load barriers"," — every pointer dereference checks and fixes up the reference;\nrelocation happens while the app runs.",[51,343,344,347],{},[35,345,346],{},"Concurrent relocation"," — objects are moved to new locations concurrently, with\nthe load barrier transparently forwarding stale references.",[103,349,351],{"className":210,"code":350,"language":212,"meta":112,"style":112},"java -XX:+UseZGC -Xmx16g LowLatencyService\n",[110,352,353],{"__ignoreMap":112},[216,354,355,357,360,363],{"class":218,"line":219},[216,356,223],{"class":222},[216,358,359],{"class":226}," -XX:+UseZGC",[216,361,362],{"class":226}," -Xmx16g",[216,364,365],{"class":233}," LowLatencyService\n",[15,367,368],{},"ZGC trades some throughput (load barriers add overhead per dereference) for extremely\nconsistent pause times. Use it when tail-latency SLAs are measured in single-digit\nmilliseconds.",[179,370,372],{"id":371},"shenandoah","Shenandoah",[15,374,375,376,379],{},"Red Hat's low-pause alternative to ZGC. Also relocates concurrently, using ",[35,377,378],{},"Brooks\npointers"," (an extra forwarding word in the object header). Pause targets of \u003C 10 ms.\nMore compatible with 32-bit and compressed-oops configurations than ZGC.",[381,382,383,399],"table",{},[384,385,386],"thead",{},[387,388,389,393,396],"tr",{},[390,391,392],"th",{},"Collector",[390,394,395],{},"Pause target",[390,397,398],{},"Best for",[400,401,402,414,425,436,447],"tbody",{},[387,403,404,408,411],{},[405,406,407],"td",{},"Serial",[405,409,410],{},"Lowest memory overhead",[405,412,413],{},"Tiny heaps \u002F single-core",[387,415,416,419,422],{},[405,417,418],{},"Parallel",[405,420,421],{},"Maximum throughput",[405,423,424],{},"Batch processing",[387,426,427,430,433],{},[405,428,429],{},"G1",[405,431,432],{},"Balanced (~10–200 ms)",[405,434,435],{},"General-purpose servers",[387,437,438,441,444],{},[405,439,440],{},"ZGC",[405,442,443],{},"\u003C 1 ms",[405,445,446],{},"Ultra-low latency APIs",[387,448,449,451,454],{},[405,450,372],{},[405,452,453],{},"\u003C 10 ms",[405,455,456],{},"Low-latency, more portable",[10,458,460],{"id":459},"stop-the-world-why-it-cant-be-fully-eliminated","Stop-the-world — why it can't be fully eliminated",[15,462,463,464,467,468,471],{},"All JVM GCs require at least brief ",[35,465,466],{},"stop-the-world (STW)"," phases where every\napplication thread is paused. The reason: safely inspecting and modifying the object\ngraph requires all threads to be at a ",[35,469,470],{},"safepoint"," — a known, consistent execution\nstate where no thread holds an inconsistent view of the heap.",[15,473,474,475,478],{},"Even ZGC has two tiny STW phases (initial mark root scan and final mark). The goal of\nmodern collectors is to make STW ",[35,476,477],{},"short and predictable",", not to eliminate it.",[15,480,481,482,485],{},"Long \"time-to-safepoint\" entries in GC logs (the time between the JVM requesting a\nsafepoint and threads arriving at one) often point to long counted loops in hot paths.\nJava 10+ added ",[110,483,484],{},"-XX:+UseCountedLoopSafepoints"," to insert safepoint checks inside\ncounted loops.",[10,487,489],{"id":488},"reading-gc-logs","Reading GC logs",[15,491,492],{},"Enable structured GC logging in production (it has negligible overhead):",[103,494,496],{"className":210,"code":495,"language":212,"meta":112,"style":112},"java -Xlog:gc*:file=\u002Fvar\u002Flog\u002Fgc.log:time,uptime,level,tags:filecount=10,filesize=20m App\n",[110,497,498],{"__ignoreMap":112},[216,499,500,502,505,509,512],{"class":218,"line":219},[216,501,223],{"class":222},[216,503,504],{"class":226}," -Xlog:gc",[216,506,508],{"class":507},"szBVR","*",[216,510,511],{"class":226},":file=\u002Fvar\u002Flog\u002Fgc.log:time,uptime,level,tags:filecount=10,filesize=20m",[216,513,514],{"class":233}," App\n",[15,516,517],{},"A typical G1 Young GC event:",[103,519,522],{"className":520,"code":521,"language":108},[106],"[1.234s][info][gc] GC(5) Pause Young (Normal) (G1 Evacuation Pause) 256M->128M(1024M) 8.432ms\n                         │              │                              │     │    │       │\n                         type           reason                        before after cap  pause\n",[110,523,521],{"__ignoreMap":112},[15,525,526],{},"A Full GC triggered by evacuation failure:",[103,528,531],{"className":529,"code":530,"language":108},[106],"[12.3s][info][gc] GC(42) To-space exhausted\n[12.4s][info][gc] GC(43) Pause Full (G1 Compaction Pause) 2048M->1200M(2048M) 9831.456ms\n",[110,532,530],{"__ignoreMap":112},[15,534,535],{},"GC logs are the first thing you need when diagnosing a latency spike. Always enable them\nin production with rolling files — you can't replay what you didn't capture.",[10,537,539],{"id":538},"the-most-important-tuning-flags","The most important tuning flags",[381,541,542,552],{},[384,543,544],{},[387,545,546,549],{},[390,547,548],{},"Flag",[390,550,551],{},"Effect",[400,553,554,568,577,587,597,607,617,626,636],{},[387,555,556,565],{},[405,557,558,561,562],{},[110,559,560],{},"-Xms"," \u002F ",[110,563,564],{},"-Xmx",[405,566,567],{},"Initial \u002F maximum heap size",[387,569,570,574],{},[405,571,572],{},[110,573,245],{},[405,575,576],{},"Select G1 (default Java 9+)",[387,578,579,584],{},[405,580,581],{},[110,582,583],{},"-XX:MaxGCPauseMillis=N",[405,585,586],{},"G1 pause time goal (soft, default 200 ms)",[387,588,589,594],{},[405,590,591],{},[110,592,593],{},"-XX:G1HeapRegionSize=N",[405,595,596],{},"Region size (1–32 MB, power of 2)",[387,598,599,604],{},[405,600,601],{},[110,602,603],{},"-XX:MaxTenuringThreshold=N",[405,605,606],{},"GC cycles before Old Gen promotion",[387,608,609,614],{},[405,610,611],{},[110,612,613],{},"-XX:+HeapDumpOnOutOfMemoryError",[405,615,616],{},"Auto heap dump on OOM",[387,618,619,623],{},[405,620,621],{},[110,622,319],{},[405,624,625],{},"Select ZGC",[387,627,628,633],{},[405,629,630],{},[110,631,632],{},"-XX:ConcGCThreads=N",[405,634,635],{},"Concurrent GC threads (G1 \u002F ZGC)",[387,637,638,643],{},[405,639,640],{},[110,641,642],{},"-XX:NativeMemoryTracking=summary",[405,644,645],{},"Track off-heap memory",[15,647,648],{},[35,649,650],{},"Start with heap size and the pause target; add other flags only when data from GC logs\nshows a specific problem.",[10,652,654],{"id":653},"common-gc-anti-patterns","Common GC anti-patterns",[179,656,658],{"id":657},"calling-systemgc","Calling System.gc()",[15,660,661,664,665,668],{},[110,662,663],{},"System.gc()"," is a hint to run a Full GC. The JVM may ignore it (or is suppressed by\n",[110,666,667],{},"-XX:+DisableExplicitGC","). When honoured, it triggers the most expensive GC event at\nan arbitrary moment. Remove it immediately from any production code.",[179,670,672],{"id":671},"static-caches-without-eviction","Static caches without eviction",[103,674,677],{"className":675,"code":676,"language":223,"meta":112,"style":112},"language-java shiki shiki-themes github-light github-dark","static Map\u003CString, Object> cache = new HashMap\u003C>(); \u002F\u002F static = GC root\n\u002F\u002F Every value added here lives forever → Old Gen grows without bound\n",[110,678,679,712],{"__ignoreMap":112},[216,680,681,684,688,691,694,697,700,703,706,709],{"class":218,"line":219},[216,682,683],{"class":507},"static",[216,685,687],{"class":686},"sVt8B"," Map\u003C",[216,689,690],{"class":507},"String",[216,692,693],{"class":686},", ",[216,695,696],{"class":507},"Object",[216,698,699],{"class":686},"> cache ",[216,701,702],{"class":507},"=",[216,704,705],{"class":507}," new",[216,707,708],{"class":686}," HashMap\u003C>(); ",[216,710,711],{"class":237},"\u002F\u002F static = GC root\n",[216,713,715],{"class":218,"line":714},2,[216,716,717],{"class":237},"\u002F\u002F Every value added here lives forever → Old Gen grows without bound\n",[15,719,720,721,693,724,727],{},"Use a proper cache (",[110,722,723],{},"Caffeine",[110,725,726],{},"Guava Cache",") with size and time-based eviction.",[179,729,731],{"id":730},"excessive-object-churn","Excessive object churn",[15,733,734,735,738,739,742,743,746,747,750],{},"Creating millions of short-lived objects per second (e.g., boxing primitives in a tight\nloop, concatenating strings with ",[110,736,737],{},"+"," in a loop) drives rapid Minor GC cycles. Use\nprimitive collections (",[110,740,741],{},"int[]"," vs ",[110,744,745],{},"List\u003CInteger>","), ",[110,748,749],{},"StringBuilder",", or streams for\nbulk operations.",[179,752,754],{"id":753},"humongous-object-abuse-g1","Humongous object abuse (G1)",[15,756,757,758,760],{},"Objects larger than half a G1 region bypass Young Gen entirely. Frequent humongous\nallocations fragment Old Gen and can cause evacuation failures. Increase\n",[110,759,256],{}," or restructure the allocation.",[10,762,764],{"id":763},"finalization-avoid-it-entirely","Finalization — avoid it entirely",[15,766,767,770,771,774,775,778],{},[110,768,769],{},"Object.finalize()"," was Java's attempt at destructor semantics. It introduces two GC\ncycles of delay, unpredictable timing, and the possibility of object resurrection. It\nis ",[35,772,773],{},"deprecated in Java 9"," and ",[35,776,777],{},"scheduled for removal",".",[15,780,781,782,774,785,788],{},"Always use ",[110,783,784],{},"AutoCloseable",[110,786,787],{},"try-with-resources"," for deterministic resource cleanup:",[103,790,792],{"className":675,"code":791,"language":223,"meta":112,"style":112},"class Resource implements AutoCloseable {\n    @Override public void close() { \u002F* release underlying resource *\u002F }\n}\ntry (Resource r = new Resource()) {\n    r.use();\n} \u002F\u002F close() called deterministically here, even on exception\n",[110,793,794,811,837,843,861,873],{"__ignoreMap":112},[216,795,796,799,802,805,808],{"class":218,"line":219},[216,797,798],{"class":507},"class",[216,800,801],{"class":222}," Resource",[216,803,804],{"class":507}," implements",[216,806,807],{"class":222}," AutoCloseable",[216,809,810],{"class":686}," {\n",[216,812,813,816,819,822,825,828,831,834],{"class":218,"line":714},[216,814,815],{"class":686},"    @",[216,817,818],{"class":507},"Override",[216,820,821],{"class":507}," public",[216,823,824],{"class":507}," void",[216,826,827],{"class":222}," close",[216,829,830],{"class":686},"() { ",[216,832,833],{"class":237},"\u002F* release underlying resource *\u002F",[216,835,836],{"class":686}," }\n",[216,838,840],{"class":218,"line":839},3,[216,841,842],{"class":686},"}\n",[216,844,846,849,852,854,856,858],{"class":218,"line":845},4,[216,847,848],{"class":507},"try",[216,850,851],{"class":686}," (Resource r ",[216,853,702],{"class":507},[216,855,705],{"class":507},[216,857,801],{"class":222},[216,859,860],{"class":686},"()) {\n",[216,862,864,867,870],{"class":218,"line":863},5,[216,865,866],{"class":686},"    r.",[216,868,869],{"class":222},"use",[216,871,872],{"class":686},"();\n",[216,874,876,879],{"class":218,"line":875},6,[216,877,878],{"class":686},"} ",[216,880,881],{"class":237},"\u002F\u002F close() called deterministically here, even on exception\n",[15,883,884,885,888],{},"For post-GC notification without finalizers, use ",[110,886,887],{},"java.lang.ref.Cleaner"," (Java 9+),\nwhich runs cleanup actions in a dedicated thread without the resurrection hazard.",[10,890,892],{"id":891},"diagnosing-gc-related-production-problems","Diagnosing GC-related production problems",[381,894,895,908],{},[384,896,897],{},[387,898,899,902,905],{},[390,900,901],{},"Symptom",[390,903,904],{},"Likely cause",[390,906,907],{},"Investigation",[400,909,910,921,932,943,957,970],{},[387,911,912,915,918],{},[405,913,914],{},"Long periodic pauses",[405,916,917],{},"Full GC \u002F Old Gen collection",[405,919,920],{},"GC logs, heap dump",[387,922,923,926,929],{},[405,924,925],{},"Steadily growing Old Gen",[405,927,928],{},"Memory leak",[405,930,931],{},"Heap dump + Eclipse MAT",[387,933,934,937,940],{},[405,935,936],{},"Frequent Minor GCs",[405,938,939],{},"High allocation rate",[405,941,942],{},"GC logs, allocation profiler",[387,944,945,948,951],{},[405,946,947],{},"OOM: Java heap space",[405,949,950],{},"Heap too small or leak",[405,952,953,956],{},[110,954,955],{},"jmap"," heap dump",[387,958,959,962,965],{},[405,960,961],{},"OOM: Metaspace",[405,963,964],{},"Class loader leak",[405,966,967],{},[110,968,969],{},"-XX:NativeMemoryTracking",[387,971,972,977,980],{},[405,973,974,975],{},"High process RSS vs ",[110,976,564],{},[405,978,979],{},"Native memory growth",[405,981,982],{},[110,983,984],{},"jcmd VM.native_memory",[15,986,987,988,991],{},"The workflow is always: ",[35,989,990],{},"measure (GC logs) → hypothesis → verify (heap dump \u002F profiler) →\nfix → measure again."," Never tune flags without first understanding what the data says.",[10,993,995],{"id":994},"recap","Recap",[15,997,998,999,1002,1003,1006,1007,1010,1011,1013,1014,774,1016,1018,1019,1022,1023,774,1025,1028],{},"Java's GC automatically reclaims ",[35,1000,1001],{},"unreachable"," objects — anything not reachable from a\nGC root. The foundational algorithm is ",[35,1004,1005],{},"mark-and-sweep-compact",". ",[35,1008,1009],{},"Generational\ncollection"," (Eden → Survivor → Old Gen) exploits the observation that most objects die\nyoung, keeping Minor GC pauses short. ",[35,1012,429],{}," is the right default for most servers,\noffering balanced throughput and ~10–200 ms pauses. ",[35,1015,440],{},[35,1017,372],{}," push\npauses below 1–10 ms for latency-critical workloads. Always ",[35,1020,1021],{},"enable GC logging"," in\nproduction, avoid ",[110,1024,663],{},[110,1026,1027],{},"finalize()",", and diagnose with heap dumps and\nGC-log analysis before reaching for tuning flags.",[1030,1031,1032],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":112,"searchDepth":714,"depth":714,"links":1034},[1035,1036,1037,1038,1039,1051,1052,1053,1054,1060,1061,1062],{"id":12,"depth":714,"text":13},{"id":29,"depth":714,"text":30},{"id":72,"depth":714,"text":73},{"id":122,"depth":714,"text":123},{"id":176,"depth":714,"text":177,"children":1040},[1041,1043,1045,1047,1048,1050],{"id":181,"depth":839,"text":1042},"Serial GC (-XX:+UseSerialGC)",{"id":196,"depth":839,"text":1044},"Parallel GC (-XX:+UseParallelGC)",{"id":241,"depth":839,"text":1046},"G1 GC (-XX:+UseG1GC) — default since Java 9",{"id":304,"depth":839,"text":305},{"id":315,"depth":839,"text":1049},"ZGC (-XX:+UseZGC) — production since Java 15",{"id":371,"depth":839,"text":372},{"id":459,"depth":714,"text":460},{"id":488,"depth":714,"text":489},{"id":538,"depth":714,"text":539},{"id":653,"depth":714,"text":654,"children":1055},[1056,1057,1058,1059],{"id":657,"depth":839,"text":658},{"id":671,"depth":839,"text":672},{"id":730,"depth":839,"text":731},{"id":753,"depth":839,"text":754},{"id":763,"depth":714,"text":764},{"id":891,"depth":714,"text":892},{"id":994,"depth":714,"text":995},"Everything you need to know about Java GC — mark-and-sweep, generational collection, Serial\u002FParallel\u002FG1\u002FZGC\u002FShenandoah collectors, stop-the-world pauses, GC logs, key tuning flags, finalization pitfalls, and diagnosing GC-related production incidents.","hard","md","Java",{},true,"\u002Fblog\u002Fjava-garbage-collection","\u002Fjava\u002Fjvm-internals\u002Fgarbage-collection",{"title":5,"description":1063},"blog\u002Fjava-garbage-collection","Garbage Collection","JVM Internals","jvm-internals","2026-06-20","FatW13ZuV00AwqhDyHFl9QcvmcYBxETClNQoZ3acRbc",1782244089490]