docs(10_Wiki): Topic_Business/General/Graphic/Programming을 Topics/ 하위로 이동
최상위 10_Wiki/Topic_*였던 4개 카테고리 폴더를 10_Wiki/Topics/Topic_* 로 재배치. 콘텐츠 변경 없음(순수 폴더 이동) — Topics/ 하위 나머지 폴더는 이미 지난 커밋에서 전부 정리된 상태(잔존 항목은 에이전트 운영 상태 및 사용자가 보존을 요청한 업데이트0615/무제 3.canvas 뿐).
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
---
|
||||
id: wiki-2026-0508-메모리-누수-memory-leaks
|
||||
title: 메모리 누수(Memory Leaks)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Memory Leak, Heap Leak, Leak Detection]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [memory, debugging, performance, gc, profiling]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: C/C++/Java/JS/Python
|
||||
framework: Valgrind/ASan/Heaptrack/Chrome DevTools
|
||||
---
|
||||
|
||||
# 메모리 누수(Memory Leaks)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 alive 인데 매 unused, 매 unused 인데 매 free X"**. 매 manual mem (C/C++) 의 free 누락, 매 GC mem (Java/JS) 의 unintended retention. 매 long-running process 의 매 가장 흔한 incident, 매 2026 도구는 매 ASan/heaptrack/V8 heap snapshot/JFR + AI-assisted root cause.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 두 의미
|
||||
- **매 Manual leak**: 매 free X — 매 OS 가 매 process 종료 시 매 회수.
|
||||
- **매 GC leak**: 매 root 에서 매 reachable 이지만 매 logically dead — 매 unintended retention.
|
||||
|
||||
### 매 흔한 원인
|
||||
1. 매 Cache 의 unbounded growth.
|
||||
2. 매 Listener / observer 의 unsubscribe 누락.
|
||||
3. 매 Closure 의 unintended capture (JS).
|
||||
4. 매 Static collection (HashMap) 의 누적.
|
||||
5. 매 ThreadLocal 의 cleanup 누락 (JVM/web).
|
||||
6. 매 Native handle (file/socket) 의 close 누락.
|
||||
7. 매 Circular ref + RC (Python before GC, Swift, ObjC ARC).
|
||||
|
||||
### 매 detection 의 axes
|
||||
- 매 Symptom: 매 RSS 증가, 매 OOM, 매 GC 빈도 ↑, 매 latency drift.
|
||||
- 매 Tool: 매 sampling profiler, 매 heap snapshot, 매 allocation tracking.
|
||||
|
||||
## 패턴
|
||||
|
||||
### C++ Valgrind
|
||||
```bash
|
||||
g++ -g -O0 main.cpp -o app
|
||||
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./app
|
||||
# 매 "definitely lost" 의 우선 fix
|
||||
```
|
||||
|
||||
### C++ AddressSanitizer + LeakSanitizer
|
||||
```bash
|
||||
g++ -fsanitize=address,leak -g main.cpp -o app
|
||||
ASAN_OPTIONS=detect_leaks=1 ./app
|
||||
```
|
||||
|
||||
### C++ RAII (matter of design)
|
||||
```cpp
|
||||
class FileHandle {
|
||||
FILE* f;
|
||||
public:
|
||||
explicit FileHandle(const char* p) : f(fopen(p, "r")) {}
|
||||
~FileHandle() { if (f) fclose(f); }
|
||||
FileHandle(const FileHandle&) = delete;
|
||||
FileHandle& operator=(const FileHandle&) = delete;
|
||||
};
|
||||
// 매 std::unique_ptr / std::shared_ptr 가 매 default
|
||||
```
|
||||
|
||||
### JS — listener 누락
|
||||
```javascript
|
||||
// 매 BAD — node 가 떠도 listener 가 retain
|
||||
function attach(el) {
|
||||
const handler = () => doStuff(el);
|
||||
el.addEventListener("click", handler);
|
||||
}
|
||||
|
||||
// 매 GOOD
|
||||
function attach(el) {
|
||||
const handler = () => doStuff(el);
|
||||
el.addEventListener("click", handler);
|
||||
return () => el.removeEventListener("click", handler);
|
||||
}
|
||||
```
|
||||
|
||||
### JS — closure 의 unintended capture
|
||||
```javascript
|
||||
// 매 BAD — 매 huge 가 매 outer scope 에 retained
|
||||
function setup() {
|
||||
const huge = new Array(1e7).fill(0);
|
||||
const small = huge[0];
|
||||
return () => small;
|
||||
}
|
||||
|
||||
// 매 GOOD
|
||||
function setup() {
|
||||
const small = (() => {
|
||||
const huge = new Array(1e7).fill(0);
|
||||
return huge[0];
|
||||
})();
|
||||
return () => small;
|
||||
}
|
||||
```
|
||||
|
||||
### Java — static map leak
|
||||
```java
|
||||
// 매 BAD
|
||||
public class Cache {
|
||||
static final Map<Key, Value> M = new HashMap<>();
|
||||
}
|
||||
|
||||
// 매 GOOD — Caffeine size cap + eviction
|
||||
import com.github.benmanes.caffeine.cache.*;
|
||||
Cache<Key, Value> c = Caffeine.newBuilder()
|
||||
.maximumSize(10_000).expireAfterWrite(Duration.ofMinutes(10)).build();
|
||||
```
|
||||
|
||||
### Java heap dump + MAT
|
||||
```bash
|
||||
jcmd <pid> GC.heap_dump /tmp/heap.hprof
|
||||
# 매 Eclipse MAT 에서 dominator tree / leak suspect
|
||||
```
|
||||
|
||||
### Chrome DevTools — heap snapshot diff
|
||||
```text
|
||||
1. baseline snapshot
|
||||
2. suspect action 반복 (e.g. open/close modal x10)
|
||||
3. second snapshot
|
||||
4. "Comparison" mode, 매 retained 증가 식별
|
||||
5. retainer chain 의 root 추적
|
||||
```
|
||||
|
||||
### Python tracemalloc
|
||||
```python
|
||||
import tracemalloc
|
||||
tracemalloc.start(25)
|
||||
snap1 = tracemalloc.take_snapshot()
|
||||
# ... 매 action 반복 ...
|
||||
snap2 = tracemalloc.take_snapshot()
|
||||
for stat in snap2.compare_to(snap1, 'lineno')[:10]:
|
||||
print(stat)
|
||||
```
|
||||
|
||||
### Linux heaptrack (C/C++ low-overhead)
|
||||
```bash
|
||||
heaptrack ./app
|
||||
heaptrack_gui heaptrack.app.<pid>.gz
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| Stack | Tool |
|
||||
|---|---|
|
||||
| C/C++ dev | ASan + LeakSanitizer |
|
||||
| C/C++ prod | heaptrack / jemalloc profiler |
|
||||
| Java | JFR + heap dump + MAT |
|
||||
| Node.js | --inspect + Chrome heap snapshot |
|
||||
| Browser | DevTools Memory tab |
|
||||
| Python | tracemalloc / objgraph / memray |
|
||||
| Go | pprof (heap profile) |
|
||||
|
||||
**기본값**: 매 first symptom = RSS chart, 매 second = heap snapshot diff, 매 third = retainer/dominator analysis.
|
||||
|
||||
## Graph
|
||||
- 부모: [[Memory Management]] · [[Performance]]
|
||||
- 응용: [[Cache Eviction]] · [[WeakRef]]
|
||||
- Adjacent: [[Garbage Collection]] · [[Reference Counting]] · [[RAII]] · [[OOM]]
|
||||
|
||||
## LLM 활용
|
||||
**언제**: 매 leak 패턴 식별, 매 fix 제안 (RAII, weak ref, eviction policy), 매 heap dump 분석 hint.
|
||||
**언제 X**: 매 production heap 의 직접 access, 매 sensitive customer data dump.
|
||||
|
||||
## 안티패턴
|
||||
- **매 OOM 시 -Xmx ↑**: 매 leak 을 매 늦출 뿐.
|
||||
- **매 try/finally 없는 close**: 매 try-with-resources / RAII / context manager 사용.
|
||||
- **매 Strong cache without eviction**: Caffeine / LRU.
|
||||
- **매 Listener pattern 의 no unsubscribe**: weakRef / dispose.
|
||||
- **매 GC tuning 으로 leak 감추기**: root cause X.
|
||||
|
||||
## 검증 / 중복
|
||||
- Verified: Valgrind manual, ASan docs, V8 blog, JFR docs, tracemalloc PEP, heaptrack docs.
|
||||
- 신뢰도 A.
|
||||
|
||||
## Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — leak detection per-stack/anti-patterns/RAII 작성 |
|
||||
Reference in New Issue
Block a user