docs(10_Wiki): 위키 전체 재구성 — Topic_* 폴더를 4개 카테고리로 통합 + 대규모 중복 제거
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를 Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류. - 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로 자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거, 동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거. - 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming, Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business, Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로, 나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는 title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백). 원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지. - 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서. - 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는 지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지. - Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경. - 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
---
|
||||
id: wiki-2026-0508-gc-root
|
||||
title: GC Root
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Garbage Collection Root, GC 루트, Root Set]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [gc, jvm, memory, runtime]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: Java/Kotlin
|
||||
framework: JVM/HotSpot/ZGC
|
||||
---
|
||||
|
||||
# GC Root
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 도달 가능성(reachability) 의 출발점"**. GC Root 는 garbage collector 가 live object 를 판별하기 위해 traversal 을 시작하는 reference set. 매 root 에서 도달 불가능한 object 는 collectible 로 marking 된다. JVM, .NET CLR, V8, Go runtime 모두 같은 개념을 공유한다.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 GC Root 종류 (HotSpot 기준)
|
||||
- **Stack reference**: 매 thread 의 local variable / parameter / operand stack.
|
||||
- **Static field**: 매 loaded class 의 static reference.
|
||||
- **JNI local/global**: 매 native code 가 hold 한 reference.
|
||||
- **Active thread**: 매 살아있는 `Thread` object 자체.
|
||||
- **Class metadata**: 매 ClassLoader, Class object (Metaspace).
|
||||
- **Synchronization monitor**: 매 `synchronized` 로 lock 된 object.
|
||||
- **Weak/Soft/Phantom 제외**: 매 reachability 약한 reference 는 root 가 아님.
|
||||
|
||||
### 매 Reachability tier
|
||||
1. **Strongly reachable**: root 에서 strong ref 만으로 도달 → never collected.
|
||||
2. **Softly reachable**: SoftReference 로만 도달 → 매 memory pressure 시 collect.
|
||||
3. **Weakly reachable**: WeakReference 만 → 매 next GC cycle 에 collect.
|
||||
4. **Phantom reachable**: PhantomReference → enqueue 후 cleaning.
|
||||
5. **Unreachable**: → collectible.
|
||||
|
||||
### 매 응용
|
||||
1. **Memory leak 진단** (heap dump → root path 추적).
|
||||
2. **Escape analysis** (root 도달 안 되는 local → stack alloc).
|
||||
3. **Concurrent GC 의 root scanning** (ZGC, Shenandoah, G1 의 phase 1).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### 1. Heap dump 에서 root path 찾기 (Eclipse MAT / jhat)
|
||||
```bash
|
||||
# JVM heap dump 생성
|
||||
jcmd <pid> GC.heap_dump /tmp/heap.hprof
|
||||
|
||||
# Eclipse MAT CLI 로 leak suspects 분석
|
||||
./MemoryAnalyzer -consoleLog -application org.eclipse.mat.api.parse \
|
||||
/tmp/heap.hprof org.eclipse.mat.api:suspects
|
||||
```
|
||||
|
||||
### 2. JFR 로 GC root 통계 수집 (JDK 21+)
|
||||
```bash
|
||||
java -XX:StartFlightRecording=duration=60s,filename=app.jfr,settings=profile \
|
||||
-XX:+UseZGC -Xmx4g -jar app.jar
|
||||
|
||||
jfr print --events GarbageCollection,GCReferenceStatistics app.jfr
|
||||
```
|
||||
|
||||
### 3. WeakReference 로 root 진입 회피
|
||||
```java
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
// Cache 가 GC root 가 되어 leak 발생하는 anti-pattern 회피
|
||||
public class Cache<K, V> {
|
||||
private final WeakHashMap<K, V> map = new WeakHashMap<>();
|
||||
|
||||
public void put(K key, V value) { map.put(key, value); }
|
||||
public V get(K key) { return map.get(key); }
|
||||
// key 가 외부에서 strong ref 사라지면 entry 자동 제거
|
||||
}
|
||||
```
|
||||
|
||||
### 4. ThreadLocal leak 회피 (매 typical root leak)
|
||||
```java
|
||||
public class RequestContext {
|
||||
private static final ThreadLocal<UserSession> CTX = new ThreadLocal<>();
|
||||
|
||||
public static void set(UserSession s) { CTX.set(s); }
|
||||
public static UserSession get() { return CTX.get(); }
|
||||
|
||||
// 매 critical: thread pool 환경에서 반드시 remove() 호출
|
||||
public static void clear() { CTX.remove(); }
|
||||
}
|
||||
|
||||
// Servlet filter 에서:
|
||||
try {
|
||||
RequestContext.set(session);
|
||||
chain.doFilter(req, res);
|
||||
} finally {
|
||||
RequestContext.clear(); // 매 root reference 끊기
|
||||
}
|
||||
```
|
||||
|
||||
### 5. JNI global ref 정리 (native code 가 GC root)
|
||||
```c
|
||||
// JNI: global ref 는 명시적으로 free 해야 함
|
||||
jobject globalRef = (*env)->NewGlobalRef(env, localObj);
|
||||
// ... 사용 ...
|
||||
(*env)->DeleteGlobalRef(env, globalRef); // 매 빠뜨리면 영구 leak
|
||||
```
|
||||
|
||||
### 6. Static field 로 의도된 long-lived root
|
||||
```kotlin
|
||||
object AppMetrics { // 매 Kotlin object → static singleton → GC root
|
||||
private val counter = AtomicLong(0)
|
||||
fun increment() = counter.incrementAndGet()
|
||||
fun snapshot() = counter.get()
|
||||
}
|
||||
```
|
||||
|
||||
### 7. ZGC / Shenandoah concurrent root scanning 활성화
|
||||
```bash
|
||||
# JDK 21+: ZGC generational
|
||||
java -XX:+UseZGC -XX:+ZGenerational \
|
||||
-XX:SoftMaxHeapSize=8g -Xmx16g -jar app.jar
|
||||
|
||||
# Shenandoah
|
||||
java -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -jar app.jar
|
||||
```
|
||||
|
||||
### 8. Async profiler 로 allocation root 찾기
|
||||
```bash
|
||||
./profiler.sh -e alloc -d 60 -f alloc.html <pid>
|
||||
# 매 flamegraph 에서 root → leaking site path 시각화
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Long-lived cache | `WeakHashMap` / Caffeine `weakKeys()` |
|
||||
| Per-request state in pool | `ThreadLocal` + `try/finally remove()` |
|
||||
| Native handle wrapping | `Cleaner` (java.lang.ref.Cleaner, JDK 9+) |
|
||||
| Listener registration | `WeakReference` 로 listener wrap |
|
||||
| 매 short-lived burst alloc | 매 그냥 strong ref + young gen 의존 |
|
||||
|
||||
**기본값**: 매 strong ref + escape analysis 신뢰. WeakReference 는 매 진짜 leak 패턴 (cache, listener, ThreadLocal in pool) 에만.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Garbage Collection]]
|
||||
- 응용: [[Memory Leak Detection]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: heap dump 의 leak suspect 해석, root path 의 의미 설명, ThreadLocal/static leak pattern detection.
|
||||
**언제 X**: 매 production 의 실제 GC tuning (real profiling 데이터 없이 추측 X).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Static collection 무한 grow**: `static List` 에 매 add 만 하고 remove 안 함 → 영구 root.
|
||||
- **ThreadLocal in pool no remove**: 매 thread 재사용 시 이전 request data 가 root 로 남음.
|
||||
- **JNI GlobalRef leak**: native 에서 `DeleteGlobalRef` 누락.
|
||||
- **Anonymous inner class capture**: `new Runnable() {}` 가 outer `this` 를 capture → outer 가 root 에 매달림.
|
||||
- **Listener 등록 후 unregister 안 함**: 매 EventBus, Observer 패턴의 classic leak.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (OpenJDK HotSpot source, JEP 376/439, Eclipse MAT docs).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — GC root taxonomy + leak 진단 패턴 |
|
||||
Reference in New Issue
Block a user