-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGarbageCollection.java
More file actions
182 lines (166 loc) · 7.48 KB
/
Copy pathGarbageCollection.java
File metadata and controls
182 lines (166 loc) · 7.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package Phase6_RuntimeMemoryRegexReflection.MemoryAllocation;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.ref.Cleaner;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.WeakHashMap;
/**
* Garbage Collection (GC)
* -----------------------
* Java's garbage collector reclaims memory occupied by objects that are no
* longer "reachable" from the program. You never `free` or `delete`
* yourself - the JVM does it for you, on a schedule it controls.
* <p>
*
* Reachability - The Core Rule
* ----------------------------
* An object is REACHABLE if some chain of references can be traced to it
* from a GC ROOT. GC roots include:
* <p>
*
* - Local variables in active stack frames (stack roots).
* - Static fields of loaded classes.
* - Active JNI references from native code.
* - Currently executing threads.
* <p>
*
* Anything else - no path back to a root - is UNREACHABLE and may be
* collected at any time.
* <p>
*
* The Classic Mark-and-Sweep
* --------------------------
* 1. MARK - traverse the object graph from every root and mark every
* reachable object.
* 2. SWEEP - walk the heap and reclaim space occupied by anything not
* marked. (Modern collectors also COMPACT to keep the heap
* un-fragmented.)
* <p>
*
* The Generational Hypothesis
* ---------------------------
* - Most objects DIE YOUNG.
* - Long-lived objects rarely reference young ones.
* <p>
*
* Modern collectors exploit this by splitting the heap into a YOUNG
* generation (cheap, frequent "minor" GCs) and an OLD generation
* (expensive "major" GCs, but rarely needed).
* <p>
*
* Three Kinds of References (java.lang.ref)
* -----------------------------------------
* STRONG - the normal kind. As long as one strong reference exists,
* the GC cannot reclaim the object.
* SOFT - may be cleared at the GC's discretion when memory is
* tight. Good for memory-sensitive caches.
* WEAK - cleared on the next GC. The classic example is
* WeakHashMap, where map ENTRIES disappear when no other
* code holds the KEY.
* PHANTOM - never returns the referent; useful for resource cleanup
* via java.lang.ref.Cleaner.
* <p>
*
* Cleaner (Java 9+) - the Modern Replacement for finalize()
* ----------------------------------------------------------
* `Object.finalize()` is unreliable, deprecated, and slow. Use Cleaner for
* deterministic, predictable cleanup of off-heap or native resources.
* <p>
*
* What System.gc() Does
* ---------------------
* It is only a HINT. The JVM is free to ignore it. Useful in
* micro-benchmarks; almost never useful in real applications.
*/
public class GarbageCollection {
public static void main(String[] args) throws Exception {
section("1) The collectors registered in this JVM");
for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
System.out.println(" " + gc.getName() + " (collections so far: "
+ gc.getCollectionCount() + ")");
}
section("2) An unreachable object becomes eligible for GC");
Object o = new Object();
System.out.println("o = " + o);
WeakReference<Object> watcher = new WeakReference<>(o);
o = null; // drop the strong ref
encourageGc();
System.out.println("after o=null + gc, watcher.get() = " + watcher.get());
section("3) Strong reference keeps the object alive");
Object strong = new Object();
WeakReference<Object> w2 = new WeakReference<>(strong);
encourageGc();
System.out.println("with strong ref alive, w2.get() = " + w2.get());
section("4) WeakReference vs SoftReference");
SoftReference<byte[]> soft = new SoftReference<>(new byte[10 * 1024 * 1024]);
WeakReference<byte[]> weak = new WeakReference<>(new byte[10 * 1024 * 1024]);
encourageGc();
// Weak refs are cleared on the next GC.
// Soft refs survive normal GC; only collected under memory pressure.
System.out.println("weak.get() after gc = " + (weak.get() == null ? "null (cleared)" : "kept"));
System.out.println("soft.get() after gc = " + (soft.get() == null ? "null (cleared)" : "kept"));
section("5) WeakHashMap - automatic eviction when keys go away");
java.util.Map<Object, String> map = new WeakHashMap<>();
Object key = new Object();
map.put(key, "the value");
System.out.println("size before = " + map.size());
key = null; // remove the strong ref
encourageGc();
System.out.println("size after = " + map.size() + " (entry was auto-removed)");
section("6) Cleaner - modern replacement for finalize()");
Cleaner cleaner = Cleaner.create();
Object owner = new Object();
cleaner.register(owner, () -> System.out.println(" cleanup task ran"));
owner = null;
encourageGc();
Thread.sleep(100); // give Cleaner's daemon thread a moment
section("7) Counting GC cycles before and after a big allocation burst");
long start = totalCollections();
for (int i = 0; i < 200; i++) {
byte[] junk = new byte[1024 * 1024];
junk[0] = 1;
}
long end = totalCollections();
System.out.println("collections triggered by burst: " + (end - start));
// OUTPUT (representative)
// ====== 1) The collectors registered in this JVM ======
// G1 Young Generation (collections so far: 0)
// G1 Old Generation (collections so far: 0)
// ====== 2) An unreachable object becomes eligible for GC ======
// o = java.lang.Object@1540e19d
// after o=null + gc, watcher.get() = null
// ====== 3) Strong reference keeps the object alive ======
// with strong ref alive, w2.get() = java.lang.Object@<addr>
// ====== 4) WeakReference vs SoftReference ======
// weak.get() after gc = null (cleared)
// soft.get() after gc = kept (under low memory, would be null)
// ====== 5) WeakHashMap - automatic eviction when keys go away ======
// size before = 1
// size after = 0 (entry was auto-removed)
// ====== 6) Cleaner - modern replacement for finalize() ======
// cleanup task ran
// ====== 7) Counting GC cycles before and after a big allocation burst ======
// collections triggered by burst: 7
}
/** Nudge the GC. Calls + small wait - still only a hint. */
private static void encourageGc() {
System.gc();
try { Thread.sleep(50); } catch (InterruptedException ignored) {}
}
/** Total GC collections across all registered collectors. */
private static long totalCollections() {
long total = 0;
for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
total += Math.max(0, gc.getCollectionCount());
}
return total;
}
private static void section(String title) {
System.out.println("\n====== " + title + " ======");
}
// Suppress unused warning for the import we keep for documentation
@SuppressWarnings("unused")
private static HashMap<?, ?> ignore;
}