| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- netty
- 백엔드개발
- NIO
- prometheus
- Kotlin
- Kubernetes
- SpringBoot
- 트랜잭션
- 동시성제어
- 데이터베이스
- spring boot
- webflux
- DevOps
- RDBMS
- 백엔드
- CloudNative
- docker
- grafana
- redis
- kafka
- mysql
- monitoring
- JPA
- selector
- GitOps
- jvm
- 성능최적화
- Java
- helm
- 성능 최적화
- Today
- Total
유성
JVM 메모리 참조 – GC와 참조 상태의 변화 흐름 본문
과거에 GC에 대한 내부 메모리영역과 최적의 GC 튜닝에 대해서 알아보았습니다.
https://youseong.tistory.com/30
자바의 Garbage Collection 이해와 성능 최적화 방법
1. Garbage Collection이란?Garbage Collection(GC)는 동적으로 할당된 메모리 중 필요하지 않은 부분을 해제하는 JVM의 기능입니다.더 이상 사용하지 않는 객체의 메모리 자원을 해제하여 메모리의 여유 공
youseong.tistory.com
이번 글에서는 GC 과정 중 객체의 참조 상태가 어떻게 변화하는지에 대해 다룹니다.
객체 상태에 대한 이해
객체 참조 상태는 총 4가지로 간단하게 설명하면 다음과 같습니다.
- Strong Reference : 직접 접근이 가능한 객체 참조 상태, GC에 의해 수거되지 않음
- Weak Reference : 직접 접근이 불가능하며 GC에 의해 수거되기 직전의 객체 참조 상태, GC에 의해 바로 수거
- Soft Reference : 메모리가 부족할 경우 GC에 의해 수거되는 객체 참조 상태
- Phantom Reference : GC에 의해 수거된 객체 참조 상태
그럼 각 참조 상태가 언제 변경되는지 알아보겠습니다.
Strong Reference
강한 참조 상태로 써 현재 사용하는 필드들이 이에 해당합니다.
아래 두가지 메서드를 순차적으로 실행합니다.
1| public void depth1() {
2| Object obj1 = new Object();
3| depth2();
4| }
5|
6| public void depth2() {
7| Object obj2 = new Object();
8| }
2번째 줄을 실행하게되면 객체가 생성되어 obj1 이라는 Reference에 할당됩니다.
obj1은 현재 callstack에서 접근 가능한 Reference이므로 GC의 영향을 받지 않습니다.
callstack을 그림으로 표현하면 다음과 같습니다.

이제 depth2 메서드를 실행하여 7번째 줄을 처리했다고 하겠습니다.
1| public void depth1() {
2| Object obj1 = new Object(); // reference 생성
3| depth2();
4| }
5|
6| public void depth2() {
7|-> Object obj2 = new Object(); // reference 생성
8| }
7번째 줄을 실행하게되면 객체가 생성되어 obj2 이라는 Reference에 할당됩니다.
callstack을 보면 다음과 같습니다.

obj1과 obj2가 모두 callstack에서 접근이 가능한 Reference로 GC 의 대상이 되지 않습니다.
프로그램을 실행하면서 값을 잘 불러와서 처리할 때 값이 사라지는 경우는 없으며,
이 경우가 객체 참조 상태가 Strong Reference이기 때문입니다.
추가로, Strong Reference는 callstack, static 변수, 기타 reachablility 경로를 통해 참조되고 있다는 의미입니다.
Weak Reference
약한 참조 상태로 써 현재 접근할 수 없는 필드이며, GC가 실행된 경우 Weak Reference 상태가 됩니다.
이제 depth2 메서드가 끝나고 dpeth1 4번째 줄로 넘어갔습니다.
1| public void depth1() {
2| Object obj1 = new Object(); // reference 생성
3| depth2();
4|-> }
5|
6| public void depth2() {
7| Object obj2 = new Object(); //
8| }
이렇게 되면 이전에 실행했었던 depth2 메서드 내부 local field obj2로의 접근 가능한 경로가 없어지게됩니다.
callstack을 그림으로 먼저 보겠습니다.

이 때 obj2 객체 Reference에 대한 직접적인 접근은 불가능 하기에 GC의 대상이 되게 됩니다.
아직 GC mark가 실행되지 않았다면, Strong Reference로 남아있습니다.
(Reference의 상태를 바꾸는 주체는 GC입니다.)
다시 말해서 GC가 실행되면 바로 Weak Reference가 될 객체 참조이나, GC가 아직 실행되지 않았다면, 해당 객체는 여전히 Strong Reference 상태로 유지됩니다.
직접 코드를 실행해서 obj2의 객체 Reference가 WeakReference로 변경되었는지 확인해보겠습니다.
(jvm, os마다 값이 다를 수 있습니다.)

ReferenceQueue에 값이 있으면, obj2의 객체 참조가 WeakReference에 들어갔었다 라는 뜻입니다.
꼭 "현재 상태가 WeakReference 이다." 를 보장하지는 않습니다.
Soft Reference
Soft Reference는 조금 특수한 기능을 하는 Reference입니다.
SoftReference는 명시적으로 선언해 사용해야 하며, JVM이 메모리 부족 상태일 때만 수거되도록 합니다.

보통의 경우에는 SoftReference가 사용되지 않고, 데이터가 제거되어도 상관없는 캐시메모리로 사용할 수 있으며
사용할 경우 꼭 StrongReference는 null로 초기화 하여 제거하는 것이 바람직 합니다.
아래 코드는 SoftReference를 사용하여 캐시를 만드는 예제입니다.
public static Map<Integer, SoftReference<Object>> softCache = new HashMap<>();
public void init() {
for (int i = 0; i < 100; i++) {
Object obj = new Object();
SoftReference<Object> softRef = new SoftReference<>(obj);
softCache.put(i, softRef);
obj = null; // StrongReference 제거: 이제 SoftReference만 남음
}
}
public Optional<Object> get(int i) {
SoftReference<Object> softRef = softCache.get(i);
Object obj = softRef.get(); // << GC에 의해 수거되었을 경우 null이 반환됨
return Optional.ofNullable(obj);
}
위 캐시는 JVM 메모리가 부족해질 경우 데이터가 제거되기에, 꼭 넣은 데이터가 나온다고 보장하지 않습니다.
있으면 사용하고, 없어도 괜찮은 경우 사용해볼 수 있습니다.
Phantom Reference
마지막 객체 참조 상태로 GC로 해제된 상태를 뜻합니다.
GC의 mark and sweep 과정이 끝난 후, 객체는 참조 불가능한 상태가 되고,
이 시점에 PhantomReference는 ReferenceQueue에 등록됩니다.
이미 메모리에 대한 연결이 끊어졌기 때문에 접근 자체가 불가능하므로 직접적인 활용은 불가능합니다.
그렇기에 PhantomReference는 객체가 GC될 때 후처리 작업을 위해서 사용해 볼 수 있습니다.

참고로 PhantomReference 상태가 메모리가 정리됐다는 것을 보장하지 않습니다. 연결이 끊겼다는 뜻입니다
끊어진 메모리들을 치우는 작업이 이후 수행됩니다.
'테스트코드 & 정적분석' 카테고리의 다른 글
| 문제 발생시 커널을 조사하는 방법 (0) | 2025.12.08 |
|---|---|
| 캐시와 메모리 모델: 자바 멀티스레드 핵심 3가지 이슈 (3) | 2025.07.21 |
| OutOfMemoryError 해결하기: IntelliJ로 메모리 누수 분석하는 방법 (0) | 2024.08.15 |
| TDD로 코드 품질과 효율성 높이는 방법: 장단점과 실전 활용 (0) | 2024.01.04 |
| JVM 메모리 덤프 생성 및 분석 (0) | 2023.08.13 |