Reference는 참조를 의미한다.
참조란, 변수나 객체의 실제 값을 직접적으로 다루지 않고 그 값이 저장된 메모리 주소를 가리키는 것이다.
참조는 강한 참조(Strong Reference)와 약한 참조(Weak Reference)가 존재한다.
강한 참조 (Strong Reference)
일반적으로 변수에 객체를 할당할 때 생성되는 기본적인 참조 방식
String str = new String("Hello");
그냥 이런 기본적인 방식이 강한 참조이다.
이 참조가 존재하면 가비지 컬렉터(Garbage Collector, GC)는 해당 객체를 절대 제거하지 않는다.
약한 참조(Weak Reference)
객체를 참조하되, 객체의 수명에는 영향을 주지 않는 보조적인 참조 방식
약한 참조만 남아있는 객체는 GC가 작동하는 순간 즉시 메모리에서 해제된다.
자바에는 WeakReference 클래스가 존재해 다음과 같이 선언하여 사용할 수 있다.
WeakReference<MyObject> weakRef = new WeakReference<>(new MyObject());
Garbage Collecter (GC)
메모리 관리를 담당하는 시스템 또는 프로그램의 구성 요소이며, 동적으로 할당된 힙 메모리에서 더 이상 사용되지 않는 객체를 찾아 제거하여 메모리를 회수하는 역할을 수행한다.
C/C++ 같은 언어에서는 프로그래머가 직접 free()나 delete를 호출해 메모리를 해제해야 하지만,
대부분의 언어에서는 GC가 자동으로 작동하여 메모리를 관리한다.
GC의 장점
- 메모리 누수 방지: 할당된 메모리가 방치되어 시스템 메모리가 부족해지는 현상을 막는다.
- 이중 해제 방지: 이미 해제된 메모리를 실수로 다시 해제할 때 발생하는 런타임 오류나 보안 허점을 차단한다.
- dangling pointer 예방: 이미 해제된 메모리 영역을 포인터가 가리키고 있을 때 발생하는 버그를 예방한다.
GC의 객체 회수 방법
GC는 객체가 메모리에서 더 이상 필요없는지 판단할 때 Reachability(도달 가능성) 개념을 사용한다.
그 기반에는 GC Root가 있는데, 여기에는 스레드 로컬 변수, 메서드 파라미터, 정적 변수 등이 있다.
GC Root에서 시작하여 참조 사슬을 따라 객체에 도달할 수 있다면 그 객체는 메모리에 남아있어야 한다. (Reachable)
그러나 GC Root에서 해당 객체까지 어떤 경로를 거쳐도 도달할 수 없다면, 그 객체는 GC의 회수 대상이 된다. (Unreachable)
예전에 성능 테스트를 하면서...
메모리 제한을 걸어 OOM을 발생시키고 강한 참조를 유지시켜 GC가 객체를 회수할 수 없게 했었다.
@RestController
@RequestMapping("/test")
public class MemoryTestController {
// GC가 회수하지 못하도록 참조를 유지하는 리스트
private static final List<byte[]> memoryHog = new ArrayList<>();
@GetMapping("/oom")
public String triggerOOM() {
// 10MB 크기의 바이트 배열을 무한히 생성하여 리스트에 추가
new Thread(() -> {
try {
while (true) {
// 10MB씩 할당
byte[] data = new byte[10 * 1024 * 1024];
memoryHog.add(data);
System.out.println("현재 할당된 총량: " + memoryHog.size() * 10 + "MB");
Thread.sleep(100); // 속도 조절 (필요시 제거)
}
} catch (OutOfMemoryError e) {
System.err.println("<OOM 발생>");
e.printStackTrace();
throw e;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
return "OOM 시뮬레이션 시작됨. 로그를 확인하세요.";
}
}
이렇게 정적 리스트를 선언하였고, 이 리스트는 애플리케이션이 종료될 때까지 메모리에 남아 GC Root 역할을 지속한다.
이때 memoryHog.add(data)를 통해 GC Root인 리스트가 byte배열을 강하게 참조하고 있으므로,
배열이 무한히 생성되어도 GC가 회수하지 못해 OOM(Out Of Memory) 에러가 발생하게 된다.

아랫줄에 보면
'java.lang.OutOfMemoryError: Java heap space' 를 볼 수 있다.
이에 대한 해결책으로 WeakReference를 사용했었다.
@GetMapping("/oom/improved2")
public String triggerOOMImproved2() {
new Thread(() -> {
try {
List<WeakReference<byte[]>> weakMemoryHog = new ArrayList<>();
int allocationCount = 0;
while (allocationCount < 200) { // 200개까지만 시도
byte[] data = new byte[10 * 1024 * 1024]; // 10MB
weakMemoryHog.add(new WeakReference<>(data)); // WeakReference 사용
allocationCount++;
System.out.println("할당 시도: " + allocationCount * 10 + "MB");
Thread.sleep(100);
// 주기적으로 GC 유도
if (allocationCount % 50 == 0) {
System.gc();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (OutOfMemoryError e) {
System.err.println("OOM 발생");
e.printStackTrace();
}
}).start();
return "WeakReference를 사용한 OOM 시뮬레이션 시작됨";
}
똑같이 byte 배열을 생성했지만, 리스트에 추가할 때 WeakReference<>로 한번 감싸서 넣어준 것을 확인할 수 있다.
WeakReference 객체 자체는 리스트에 강한 참조로 들어가있지만, 그 안의 data는 약한 참조가 유지되는 것이다.
따라서 주기적으로 System.gc();가 실행되는 지점에 GC가 바이트 배열을 수거할 수 있고, 에러가 발생하지 않는다.
추가적으로
WeakReference의 사용법은...
약한 참조로 감싸인 객체에 접근할 때는 .get()을 사용한다.
이때 GC에 의해 이미 회수되었을 가능성(null)을 항상 염두에 두어야 한다.
// 1단계: 힙 메모리에 1024바이트 크기의 바이트 배열 생성
// 2단계: 그 바이트 배열을 감싸는 WeakReference 객체 생성
// 3단계: 생성된 WeakReference 객체를 weakRef라는 변수에 할당
WeakReference<byte[]> weakRef = new WeakReference<>(new byte[1024]);
// 4단계: 약한 참조 내부에서 바이트 배열을 꺼내 data 변수에 담음 (강한 참조)
byte[] data = weakRef.get();
if (data != null) {
// 아직 GC에 의해 회수되지 않음
} else {
// GC에 의해 메모리 해제됨
}
WeakReference는 캐시 구현이나 옵저버 패턴에서 주로 사용된다고 한다.
캐시는 임시 저장소의 역할을 하기 때문에, 데이터를 계속 쌓아두지 않고 사용하지 않는 데이터는 비워야 한다.
옵저버 패턴에서는 등록 해제(unregister)를 깜빡했을 때 발생하는 메모리 누수 방지를 위해서 사용되는데,
이를 위해 이벤트 발행자가 리스너를 약하게 참조하도록 하고, 수명이 다하거나 참조를 끊으면 GC가 바로 회수하도록 한다.
레퍼런스(Reference), 참조에 대하여
자료구조와 알고리즘을 공부하다보니 레퍼런스(Reference)라는 단어를 듣게 되었습니다. 한글로는 참조라고도 합니다.예전 웹 개발 교육을 들었을때 참조에 대해서 공부를 했었던 기억이 나서 복
velog.io
[Java]가비지 컬렉터(Garbage Collector)란?
JVM GC가 Heap에서 사용하지 않는 객체를 메모리를 해제해준다. 하지만 어떻게 동작하고 어떤 알고리즘을 사용 하는지 알아보지 못해서 글로 작성해봤다. 단순히 암기가 아닌 시각적인 사진들을
velog.io
'▪️TIL' 카테고리의 다른 글
| [TIL] MSA 환경의 분산 트랜잭션 처리 패턴 - 2PC, SAGA (0) | 2026.08.14 |
|---|---|
| [TIL] 운영체제와 메모리 (0) | 2025.11.23 |
| [TIL] HTTPS와 SSL/TLS의 개념 (0) | 2025.11.12 |
| [TIL] IP 주소 체계 이해하기 (0) | 2025.11.01 |
| [TIL] 네트워크 토폴로지, 성능 분석 (0) | 2025.10.15 |