[Java] Class Loader -2

하비·2024년 9월 29일
1

Java

목록 보기
6/13

앞 포스팅에서 Class Loader에 대해서 설명했다.

Class Loader는 간단히 얘기하면 class 파일들을 동적으로 로드하고, 메모리 영역(Runtime Data Areas)으로 올려주는 역할을 한다.

그러면 class loader가 자바 클래스들을 메모리에 언제 올리는지 알아보겠다.

언제 로딩이 될까?

JVM은 실행 될 때 모든 클래스를 메모리에 올려놓지 않는다.
그때마다 필요한 클래스를 메모리에 올려 효율적으로 관리한다.
그러면 static 멤버들은?
static 멤버들도 한번에 메모리에 올라가지 않는다. 언제 어디서 사용될지 모르는 static 멤버들을 처음에 전부 메모리에 올린다는건 비효율적이기 때문에, 클래스 내의 멤버를 호출하게 되면 그때서야 클래스가 동적으로 메모리에 로드된다.
각 상황에 따라 클래스 로드 시점을 알아보겠다.

클래스가 이렇다고 가정해보자

class Outer {
	static String value="> Outer 클래스의 static 필드입니다.";
    
    static final String VALUE="> Outer 클래스의 static final 필드입니다.";
    
    Outer() { System.out.println("> Outer 생성자 초기화");}
    
    static void getInstance() {
    	System.out.println("> Outer 클래스의 static 메서드 호출");
    
    class Inner {
    	Inner() { System.out.println("> Inner 생성자 초기화"); }
    
    static class Holder {
    	static String value="> Holder 클래스의 static 필드입니다.";
        
        static final String VALUE="> Holder 클래스의 static final 필드입니다.";
        
        Holder() { Sytem.out.println("> Holder 생성자 초기화"); }
    }
}

1. 아무것도 호출하지 않을 때

public class Main {
	public static void main(string[] args){
    }
}
  • Main 메소드를 실행했기 때문에 Main 클래스만 로드가 된다.
  • Outer 클래스에 static 멤버들이 있더라도 호출되지 않았기 때문에 클래스는 로드되지 않는다.

2. 인스턴스 생성

public class Main {
	public static void main(string[] args){
    	new Outer();
    }
}
  • 이번엔 Outer 인스턴스를 생성했기 때문에, Main과 Outer 둘다 로드된다.
  • 하지만 내부 클래스는 직접 인스턴스를 생성하지 않아 로드되지 않는다.

3. static 변수 호출

public class Main {
	public static void main(string[] args){
    	System.out.println(Outer.value);
    }
}
  • 클래스 내부의 static 멤버를 호출하면, 인스턴스화 하지 않아도 클래스가 로드된다.
  • 로드되는 클래스: Main, Outer

4. static final 상수 호출

public class Main {
	public static void main(string[] args){
    	System.out.println(Outer.VALUE);
    }
}
  • final은 JVM의 Method Area에 Constant Pool에 따로 저장되어 관리되기 때문에 Outer 클래스가 로드되지 않는다.
  • 로드되는 클래스: Main

5. static 메소드 호출

public class Main {
	public static void main(string[] args){
    	Outer.getInstance();
    }
}
  • static 변수를 호출한 것과 같이 Outer 클래스가 로드된다.
  • 로드되는 클래스: Main, Outer

6. 내부 클래스 호출

public class Main {
	public static void main(string[] args){
    	new Outer().new Inner();
    }
}
  • 내부 클래스를 생성하기 위해서는 외부 클래스를 먼저 생성하고 인스턴스화 해야하기 때문에 Outer 클래스와 Inner 클래스 둘다 로드 된다.
  • 이러한 특징 때문에 내부 클래스를 static으로 선언하지 않고 인스턴스 멤버 클래스로서 사용하면 메모리 누수가 발생하게 된다.
    • 내부 클래스를 static으로 선언하지 않을 경우, .class 파일을 보면 Inner class에서 Outer class를 매개 변수로 받아와 인스턴스 변수로 저장한다. (외부 참조)
    • 내부 클래스를 static으로 선언할 경우에는 Outer class를 인스턴스 변수로 저장하지 않는다.
    • 만약 외부 클래스가 필요없어지고 내부 클래스만 남아있을 경우, 필요없어진 외부 클래스를 GC 대상으로 삼아 메모리에서 제거해야 하지만 외부 참조로 내부 클래스와 연결되어 있기 때문에 메모리에서 제거가 안되고 잔존하게 되고 이는 곧 메모리 누수로 프로그램이 터지게 된다.
  • 로드되는 클래스: Main, Inner, Outer

7. static 내부 클래스 호출

public class Main {
	public static void main(string[] args){
    	new Outer().Holder();
    }
}
  • static으로 호출할 경우, 외부 클래스를 로드하지 않는다.
  • 로드되는 클래스: Main, Holder

8. static 내부 클래스의 static 변수 호출

public class Main {
	public static void main(string[] args){
    	System.out.println(Outer.Holder.value);
    }
}
  • 마찬가지로 클래스를 인스턴스화하지 않아도 static 멤버를 호출하면 Holder 클래스가 로드된다.
  • 외부 클래스는 로드되지 않는다.
  • 로드되는 클래스: Main, Holder

이렇게 class loader의 로드 시점에 대해 알아보았다.
다음 포스팅에서는 class loader 다음 단계인 Runtime Area에 대해 올리도록 하겠다.

profile
멋진 개발자가 될테야

0개의 댓글