참조란?
전통적인 의미의 '참조'는
참조 타입 데이터에 저장된 값이 다른 메모리 조각의 시작 주소를 뜻한다면, 이 참조 데이터를 통해 해당 메모리 조각이나 객체를 참조한다고 말한다.
- JDK1.2 이전에서 '참조'
위와 같다. 즉, 참조형 데이터에 다른 메모리 조각이나 객체가 저장된다면 이 참조데이터는 그 메모리 조각이나 객체를 참조한다고하는, '참조'와 '참조되지 않음'만을 다루는 전통적인 의미의 참조다.
현대의 참조
하지만 '메모리가 널널할 때는 굳이 버리지 않았다가 메모리가 부족해지면 GC의 대상이 되어야하는 경우'는 어떻게 표현할까? 그래서 JDK1.2 이후부터는 참조의 개념을 4가지로 구분했다.
강한 참조(strong reference)
가장 전통적인 의미의 참조. new 연산자를 사용해서 프로그램 코드에서 직접 객체를 생성하고 이를 변수에 대입하여 참조를 직접 할당한다. 절대 GC대상이 되지 않는다.
부드러운 참조(soft reference)
유용하지만 필수는 아닌 객체에 해당한다. GC의 대상이되지는 않으나 메모리 오버플로우가 발생하기 직전에 회수된다. 그래도 메모리가 부족한 경우 메모리 오버플로우에러가 발생한다. SoftReference<T>로 참조할 수 있다. 일반적으로 메모리 부담을 줄이고 캐시를 구현할 때 사용한다. 요즘은 Caffeine에 밀려서 캐시로는 잘 안쓰인다고.
약한 참조(weak reference)
부드러운 참조보다 연결 강도가 약하다. 약한 참조에 해당하는 객체는 다음 가비지 컬렉션까지만 '죽어있지 않다' GC가 시작되면 약한 참조에 해당하는 메모리들은 회수된다. WeakReference<T>로 참조할 수 있다.
People p = new People(28, "만치");
WeakReference<People> reference = new WeakReference<>(p);
사람의 나이와 이름을 저장하는 People 객체가 있다고치고, 이를 약한참조로 reference 객체에 연결해주었다고치자.
여기서 강한참조는 p → People 객체를, reference 변수 → WeakReference<People>로 참조 중이고, 약한참조로는 WeakReference<People> → People이 참조 중이다.
이 관계에서는 GC가 발생해도 딱히 소멸되는 객체가 없는데, 그 이유는 People 객체는 변수 p에게서, WeakReference<People> 객체는 reference 변수에서 참조 중이기 때문에 어느것도 GC의 대상이 되지 않기 때문이다.
즉, 약한참조더라도 객체들이 강한참조로 연결되기만한다면 GC 대상이되는 객체는 없다.
People p = new People(28, "만치");
WeakReference<People> reference = new WeakReference<>(p);
p = null;
여기서 People 객체의 참조를 해제해준다면
People 객체는 WeakReference<People>에게서 약한참조만 받게된다. 즉, 강한참조가 사라지고 약한참조만 남게된다. 이 경우에 GC가 동작하면 People 객체는 사라지게된다.
public static void main(String[] args) {
People p = new People(28, "만치");
WeakReference<People> reference = new WeakReference<>(p);
System.out.println(reference.get()); //이름: 만치, 나이: 28
p = null;
System.out.println(reference.get()); //이름: 만치, 나이: 28
System.gc();
System.out.println(reference.get()); //null
}
그래서 어느 한 객체가 있을 때, 그 객체를 강하게 참조하는 객체들이 모두 사라지고, 약하게 참조하는 객체들만 남게된다면 그 객체는 회수된다. 즉, 아무도 그 객체를 참조하지 않을 때 즉각적으로 메모리를 회수하는 역할을 수행한다.
주로 메모리관리, 캐시 등에 사용된다. 또다른 예시는 WeakHashMap으로 key값이 참조되지 않는다면 WeakHashMap 내부에서 key 값을 제거한다.
유령 참조(phantom reference)
유령참조는 객체 수명에 아무런 영향을 주지 않으며, 유령참조에서 객체 인스턴스를 가져오는 것도 불가능하다. 유령참조의 유일한 목적은 객체가 회수될 때 알림을 받기 위해서 뿐이다. PhantomReference<T>로 참조할 수 있다.
SoftReference<String> reference = new SoftReference<>("자바자바");
강한 참조를 제외하고는 위와같이 직접적으로 객체를 만들어주어야한다.
왜 이렇게 참조를 4가지로 구분했을까?
그 이유는
JDK, 그리고 자바가 발전하면서 수많은 기능들이 추가되고, 그에따라 메모리를 효율적으로 사용해야할 필요들이 생기기 때문이다.
자바의 기능들이 발전하면서 잠깐동안만 필요한 메모리, 캐시 같은 경우는 이전처럼 강한참조로 남겨두자니 메모리의 부족문제가 생기기 시작했고, '반드시 참조해야하는 객체'와 '참조하면 좋지만 메모리가 부족한 경우에는 버려도 되는 객체', 'GC 타이밍을 감지하거나 강한참조가 없을 경우 정리하고 싶은 객체' 등에 대한 요구가 발생했다.
그래서 자동으로 참조를 해제해 메모리를 관리할 수 있는 약한참조와 부드러운참조, 그리고 유령참조가 생겨난 것이다.
'언어공부 > Java | Kotlin' 카테고리의 다른 글
| [Java] 자바의 정석 독서 #30 - 열거형 (0) | 2025.12.31 |
|---|---|
| [Java] finalize()는 무엇이고, 왜 지양해야하는가? (0) | 2025.12.25 |
| [Java] 자바의 정석 독서 #29 - 제너릭 메서드, 제너릭 형변환 (0) | 2025.12.24 |
| [Java] 자바의 정석 독서 #28 - 제너릭 와일드카드, 공변성 (1) | 2025.12.23 |
| [Java] 자바의 정석 독서 #27 - 제너릭 기초 (1) | 2025.12.18 |
