Language/Java

[JVM] 내부 클래스를 static으로 선언해주는 이유

Uykm 2023. 11. 9. 00:11

▼ Why ?

이번에 '싱글톤 패턴' 에 대해 공부하다가 싱글톤 패턴을 구현하는 기법들에 대해서도 공부하게 됐다. 그 여러 기법들 중 'LazyHolder' 기법은 워낙 중요한 기법이기도 하고 클래스 로더 매커니즘 클래스가 로드되는 시점을 활용한 기법이기 때문에, 그 부분들을 이해할 필요성을 느껴서 공부하게 되었다.


▼ What ?

우선 JVM(Java Virtual Machine)의 클래스 로더(Class Loader)가 어떤 방식으로 작업을 수행하는지를 공부해보고, 이를 통해 자바(Java)의 클래스들이 어떻게 메모리에 올라가고 클래스 멤버들이 언제 초기화되는지를 알아보려고 한다. 


내부(Inner) 클래스를 static으로 선언해주는 이유

 

내부 클래스를 static으로 선언해주는 이유는 ?

  • 결론을 먼저 말하자면, 메모리를 효율적으로 사용하고 외부(outer) 클래스 'GC(Garbage Collector' 의 대상에서 제외되는 경우를 방지하기 위함이다 !

 


 

내부 클래스를 static으로 선언해주지 않는다면..?

  • 내부(Inner) 클래스는 외부(outer) 클래스의 인스턴스에 대한 '외부 참조'  반드시 갖게 된다. 따라서 내부 클래스가 외부의 멤버를 사용하지 않아도, 숨겨진(hidden) 외부 참조가 생겨난다.
     쉽게 말하면,  일단 아래의 'Inner_Class' 처럼 static으로 선언되지 않은 내부(inner) 클래스 "비정적(non-static) 멤버 클래스" 라고 한다. 이런 비정적(non-static) 멤버 클래스의 인스턴스 외부(outer) 클래스의 인스턴스와 '내부적으로 연결'되어 있다고 할 수 있다.
     그렇기 때문에 아래의 코드처럼 내부 클래스에서 외부 클래스의 메서드 'this' 로 호출 가능한 것이다.

    ( 단순하게 생각하면, 내부 클래스가 클래스 기능을 할 수 있다고 해도 결국 외부 클래스의 멤버인데, 멤버가 자신을 담고 있는 클래스와 연결되어있지 않다는 사실이 더 이상한 것이다. )

  • 외부(outer) 클래스의 인스턴스에 대한 '외부 참조'가 왜 문제가 될까 ?
    예를 들어, Main에서 내부(innter) 클래스의 객체를 여러 번 생성하고 배열에 저장해줄 상황이 있다고 가정해보자.
    1. 내부 클래스의 객체를 생성해주려면 외부 클래스를 '인스턴스화(instantiate)', 즉 외부 클래스를 new 연산자를 이용해 객체를 생성해줘야 한다.
    2. 그렇게 생성한 외부(outer) 클래스의 인스턴스 변수로 내부 클래스의 객체를 생성하고 반환해주는 'getInnerObject' 메서드를 호출하여 원하는 만큼 내부 클래스의 객체를 생성하여 저장했다고 해보자.
    3. 그럼 메서드 호출을 위한 용도만으로 생성된 외부(outer) 클래스의 객체는 이제 필요가 없어져 할당된 메모리가  'GC' 에 의해 수거, 즉 힙 메모리에서 삭제되어야 하는 것이 일반적인 메모리 관리의 흐름인데, 수거되지 않고 메모리에 그대로 남아있게 된다 !
    4. 그 이유가 바로 내부 클래스에서 외부 클래스를 참조, 즉 앞서 말한 '외부 참조' 현상 때문이다.
    5. 만약, 외부(outer) 클래스의 객체가 메모리를 엄청나게 많이 차지하고 있는 경우라면 메모리가 터져 'OutOfMemoryError' 가 발생하게 된다. (메모리 누수)
public class Outer_Class {
    int field = 10;
    int getField() {
        return field;
    }

    class Inner_Class {
        int inner_field = 20;
        int getOuterfield() {
            return Outer_Class.this.getField(); // 숨은 외부 참조가 있기 때문에 가능
        }
    }
}

 


 

내부 클래스를 static으로 지정해주는 경우는 ?

  • 내부(inner) 클래스가 외부(outer) 클래스의 멤버를 가져와 사용하는 경우가 아니라면 내부 클래스는 반드시 static으로 선언해주자 !