내부 클래스와 외부 클래스의 관계, 특히 비정적(non-static) 내부 클래스가 어떻게 동작하는지에 대한 이해가 중요합니다. 이를 설명하면서 메모리 누수와 관련된 문제도 함께 다루겠습니다.
예를 들어:
class OuterClass {
private String data = "Outer class data";
class InnerClass {
public void printData() {
System.out.println(data); // 외부 클래스의 필드에 접근 가능
}
}
}
위 코드에서 InnerClass는 OuterClass의 인스턴스 필드인 data에 접근할 수 있습니다. 즉, InnerClass 객체는 항상 OuterClass 객체에 대한 참조를 암묵적으로 가지고 있게 됩니다.
만약 외부 클래스 객체가 더 이상 필요 없다고 해도, 내부 클래스 객체가 여전히 참조 중인 경우, 외부 클래스 인스턴스는 가비지 컬렉터에 의해 수거되지 않고 메모리에 남아 있을 수 있습니다. 이 경우, 메모리 누수가 발생할 수 있습니다.
1) 정적 내부 클래스(static inner class) 사용
정적 내부 클래스는 외부 클래스의 인스턴스에 대한 참조를 가지지 않기 때문에, 메모리 누수 문제가 발생하지 않습니다.
class OuterClass {
static class StaticInnerClass {
// 외부 클래스의 인스턴스 멤버에 접근 불가
}
}
정적 내부 클래스는 외부 클래스의 정적 멤버(static field, static method)에는 접근할 수 있지만, 인스턴스 필드나 메서드에는 접근할 수 없습니다. 외부 클래스의 인스턴스 참조가 필요 없을 때 유용합니다.
2) 외부 클래스의 약한 참조(Weak Reference) 사용
내부 클래스가 외부 클래스를 참조하지 않도록 하기 위해, 약한 참조(WeakReference)를 사용하여 외부 클래스 인스턴스에 대한 참조를 끊을 수 있습니다.
class OuterClass {
private String data = "Outer class data";
class InnerClass {
private WeakReference<OuterClass> outer;
InnerClass(OuterClass outerInstance) {
this.outer = new WeakReference<>(outerInstance);
}
public void printData() {
OuterClass outerInstance = outer.get();
if (outerInstance != null) {
System.out.println(outerInstance.data);
}
}
}
}
이 방법은 외부 클래스 객체에 대한 강한 참조를 사용하지 않으므로, GC가 외부 클래스 객체를 더 쉽게 수거할 수 있게 도와줍니다.
Q1: 비정적 내부 클래스가 왜 외부 클래스의 참조를 가지는지, 그 동작 원리를 더 구체적으로 설명할 수 있나요?
Q2: 정적 내부 클래스의 장단점은 무엇인가요? 언제 비정적 내부 클래스 대신 사용하는 것이 좋을까요?
Q3: 자바에서 약한 참조(WeakReference) 외에도 메모리 누수를 방지하기 위한 다른 기법들은 어떤 것들이 있을까요?
답:
Q1: 비정적 내부 클래스가 왜 외부 클래스의 참조를 가지는지, 그 동작 원리를 더 구체적으로 설명할 수 있나요?
비정적 내부 클래스는 외부 클래스의 인스턴스 필드와 메서드에 접근할 수 있도록 설계되어 있기 때문에, 내부 클래스는 외부 클래스의 인스턴스에 대한 암묵적인 참조를 가집니다. 이를 가능하게 하는 동작 원리는 다음과 같습니다:
비정적 내부 클래스가 생성될 때, 자바는 내부 클래스 객체에 외부 클래스의 인스턴스에 대한 참조를 함께 저장합니다. 이 참조는 내부 클래스의 모든 메서드나 필드에서 외부 클래스의 인스턴스 멤버에 접근할 수 있도록 해 줍니다.
예를 들어:
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
여기서 InnerClass는 OuterClass의 인스턴스 outer에 대한 참조를 암묵적으로 가지게 되어, InnerClass 객체는 언제든지 outer 객체의 필드와 메서드에 접근할 수 있습니다. 이 때문에, 내부 클래스 객체가 존재하는 한 외부 클래스의 인스턴스도 계속 메모리에 남아 있을 수 있습니다.
Q2: 정적 내부 클래스의 장단점은 무엇인가요? 언제 비정적 내부 클래스 대신 사용하는 것이 좋을까요?
장점:
- 외부 클래스 인스턴스 참조가 없음: 정적 내부 클래스는 외부 클래스의 인스턴스에 대한 참조를 가지지 않으므로, 메모리 누수 문제가 발생하지 않습니다. 이로 인해 메모리 관리가 더 효율적입니다.
- 캡슐화: 외부 클래스 내부에 정의됨으로써 외부 클래스와 연관된 코드를 더 가독성 있게 관리할 수 있습니다. 코드가 잘 구조화되고 명확해집니다.
- 독립성: 외부 클래스 인스턴스와 무관하게 정적 내부 클래스는 독립적으로 생성 및 사용될 수 있습니다.
단점:
- 외부 클래스의 인스턴스 멤버에 접근 불가: 정적 내부 클래스는 외부 클래스의 인스턴스 멤버에 접근할 수 없습니다. 만약 외부 클래스의 인스턴스 필드나 메서드에 접근해야 한다면, 비정적 내부 클래스를 사용해야 합니다.
- 언제 사용하는 것이 좋은가?
외부 클래스의 인스턴스와 독립적으로 동작할 수 있는 도우미(helper) 클래스를 정의할 때.
외부 클래스의 인스턴스 상태와 상관없이 내부 클래스가 특정 기능을 수행할 때.
메모리 관리가 중요한 경우, 특히 메모리 누수를 방지해야 하는 상황에서 정적 내부 클래스를 사용하는 것이 좋습니다.
Q3: 자바에서 약한 참조(WeakReference) 외에도 메모리 누수를 방지하기 위한 다른 기법들은 어떤 것들이 있을까요?
Map<Key, Value> cache = new WeakHashMap<>();
someComponent.removeListener(listener);
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
// 파일 처리
} // 자동으로 리소스가 닫힘