언어/java

JVM의 Class Loader(클래스 로더)란?

sorecord 2026. 1. 19. 17:59

 

왜 알아야 할까?

클래스 로딩때 발생하는 이슈를 해결할 수 있고 코드 상에서 동적으로 클래스를 로딩하는 구문을 이해할 수 있으며, java의 동작 방식에 대해 한층 깊게 알수 있다.

 


java의 빌드 과정

"Build"는 우리가 작성한 코드를 실행 가능한 형태로 만드는 작업을 말한다. Java에서는 build하기 위해 아래의 과정을 거치게 된다.

  • 생성할 코드가 있는 경우 코드 생성 (Lombok, Object Mapping ... )
  • 소스 코드 컴파일 (.java -> .class 확장자 변경, cmd에서 javac 명령어와 같은 동작)
  • 테스트 코드 컴파일
  • 테스트 실행
  • 패키징 (jar, war ...)

 

 Java의 Class Loader는 이제 이 class들을 JVM에 로드하는 역할을 수행


@@JAR(JAVA Archive)이란??

.zip이랑 비교하면 이해하기 편하다. .zip은 하나의 파일 안에 여러 개의 파일이 들어 있는 파일이며, 이러한 형태를 아카이브(archive)라고 한다.

.jar은 java에서 사용되는 특정 유형의 아카이브이다.  이 또한 여러 파일이 포함된 단일 파일이며 가장 중요한 부분은 파일들은 컴파일된 .class 파일이다. 

활용할 수 있는 2가지 방식이 있다.

  1. Java 애플리케이션을 실행할 때 클래스 경로에 jar 파일을 추가하여 안에 포함된 모든 코드를 현재 애플리케이션에서 사용할 수 있다.
  2. jar 파일 자체를 실행 가능한 애플리케이션이되며 이를 실행할 수 있습니다.

jar파일은 java 코드의 편리한 컨테이너 역할을 하며 zip과 같이 동일한 방식으로 압축된다. 

 


ClassLoader의 주요 2가지 역할

 

1. 다양한 내장 클래스 로더와 사용자 정의 클래스 로더가 클래스를 로드합니다. java.lang.ClassLoader 추상 클래스를 확장하여 클래스 로더 구현을 만들 수 있다. = >(더 간단한 설명) 자바 프로그램이 클래스를 메모리에 로드하는 역할을 합니다. 기본적으로 자바에는 여러 가지 내장 클래스 로더가 있으며, 필요에 따라 사용자 정의 클래스 로더를 만들어 사용할 수도 있습니다.

 

2. 리소스는 .class 파일, 구성 정보 또는 이미지와 같은 데이터입니다. 일반적으로 리소스는 애플리케이션이나 라이브러리와 함께 패키징되어 쉽게 찾을 수 있도록 합니다. => (더 간단한 설명) 자바에서는 리소스를 패키징하여 애플리케이션과 함께 배포할 수 있으며, 이를 통해 프로그램이 실행될 때 필요한 데이터를 쉽게 찾고 사용할 수 있습니다. 리소스는 일반적으로 ClassLoader를 통해 접근됩니다.

 

더 자세하게 들어가보자.

 

자바는 컴파일 타임이 아니라 런타임(바이트 코드를 실행할때 = JVM이 동작되는 시점)에 클래스 로드하고 링크하는 특징이있다. 이것을 동적 로드라 부르며 담당하는 부분이 JVM의 클래스 로더이다. 

=>정리하자면 런타임 중에 JVM의 메소드 영역(저장되는 영역)에 동적으로 java 클래스를 로드하는 역할을 한다. 

이전 포스팅에서 간단한 이미지로 설명했듯이 3가지 단계로 나뉜다.

  • 로딩
    • 자바 바이트 코드(.class)(=컴파일된 코드)를 메소드 영역에 저장한다.
    • 각 자바 바이트 코드(.class)는 JVM에 의해 메소드 영역에 다음 정보들을 저장한다.
      • 로드된 클래스를 비롯한 그의 부모 클래스의 정보
      • 클래스 파일과 Class, Interface, Enum의 관련 여부
      • 변수나 메소드 등의 정보
  • 링크
    • 검증: 읽어 들인 클래스가 자바 언어 명세 및 JVM 명세에 명시된 대로 잘 구성되어 있는지 검사한다.
    • 준비: 클래스가 필요로 하는 메모리를 할당하고, 클래스에서 정의된 필드, 메소드, 인터페이스를 나타내는 데이터 구조를 준비한다.
    • 분석: 심볼릭 메모리 레퍼런스를 메소드 영역에 있는 실제 레퍼런스로 교체한다.
  • 초기화
    • 클래스 변수들을 적절한 값으로 초기화 한다. 즉, static 필드들이 설정된 값으로 초기화한다.

 

 


3가지 Class Loader

 

간단한 예제

public void printClassLoaders() throws ClassNotFoundException {
    System.out.println("Classloader of ArrayList:"
        + ArrayList.class.getClassLoader());
 
    System.out.println("Classloader of Logging:"
        + Logging.class.getClassLoader());
 
    System.out.println("Classloader of this class:"
        + PrintClassLoader.class.getClassLoader());
 
}

출력값:
Class loader of ArrayList:null
Class loader of Logging:sun.misc.Launcher$ExtClassLoader@3caeaf62
Class loader of this class:sun.misc.Launcher$AppClassLoader@18b4aac2

 

결과를 잘 살펴보면 글자가 다르다. null, Ext, App 이런 형태로 나뉜다. 이렇게 3가지의 클래스 로더로 나뉘게된다.


 

Bootstrap Class Loader

첫 번째는 Bootstrap Class Loader로 3가지 기본 클래스로더 중 최상위 클래스로더다.

 

JVM 시작 시 가장 최초로 실행되는 클래스 로더이다. 부트스트랩 클래스 로더는 자바 클래스를 로드하는 것이 아닌, 자바 클래스를 로드할 수 있는 자바 자체의 클래스 로더와 최소한의 자바 클래스(java.lang.Object, Class, ClassLoader)만을 로드함.

 

클래스 로더들도 결국 Java의 클래스로 최 상위 클래스 로더인 Bootstrap Class Loader에 의해 로드된다. 이는 주로 JDK 내부 클래스, 일반적으로 jre/lib/rt.jar에 담긴 JVM을 실행시키기 위한 핵심 클래스들을 로딩합니다.(rt는 RunTime를 의미)

 그런데 위에서 JDK에 있어야하는 ArrayList의 클래스 로더는 null이 나왔었는데, 이러한 이유는 Bootstrap Class Loader는 자바가 아닌 네이티브 코드로 작성되어 있어서 자바 클래스로 표시되지 않기 때문입니다.

Extension Class Loader(= Platform Loader로 변경되었다.)

확장 클래스 로더는 부트스트랩 클래스 로더를 부모로 갖는 클래스 로더로서, 확장 자바 클래스들을 로드한다. java.ext.dirs 환경 변수에 설정된 디렉토리의 클래스 파일을 로드하고, 이 값이 설정되어 있지 않은 경우 ${JAVA_HOME}/jre/lib/ext 에 있는 클래스 파일을 로드한다.

 

Java가 기본적으로 제공하는 클래스도 아니고, 내가 직접 작성하지도 않은 외부 라이브러리들을 가져올 때, 해당 위치에 jar를 넣게되고, Extension Class Loader가 거기에 있는 클래스를 로드해줍니다.

Application Class Loader(= System Loader)

 classpath나 실행시킨 jar 파일 안에 있는 manifest 파일에 지정된 경로에 있는 클래스들을 로딩합니다. 쉽게 말해 내가 작성한 class 확장자 파일을 로드해주는 역할.

 

 


ClassLoader의 동작 방식

  1. JVM의 메소드 영역에 클래스가 로드되어 있는지 확인한다. 만일 로드되어 있는 경우 해당 클래스를 사용한다.
  2. 메소드 영역에 클래스가 로드되어 있지 않을 경우, 시스템 클래스 로더에 클래스 로드를 요청한다.
  3. 시스템 클래스 로더는 확장 클래스 로더에 요청을 위임한다.
  4. 확장 클래스 로더는 부트스트랩 클래스 로더에 요청을 위임한다.
  5. 부트스트랩 클래스 로더는 부트스트랩 Classpath(JDK/JRE/LIB)에 해당 클래스가 있는지 확인한다. 클래스가 존재하지 않는 경우 확장 클래스 로더에게 요청을 넘긴다.
  6. 확장 클래스 로더는 확장 Classpath(JDK/JRE/LIB/EXT)에 해당 클래스가 있는지 확인한다. 클래스가 존재하지 않을 경우 시스템 클래스 로더에게 요청을 넘긴다.
  7. 시스템 클래스 로더는 시스템 Classpath에 해당 클래스가 있는지 확인한다. 클래스가 존재하지 않는 경우 ClassNotFoundException을 발생시킨다.

클래스 로더는 Java Runtime Environment의 일부분이다. JVM이 클래스를 요청하면 클래스 로더는 클래스 이름을 사용하여 클래스를 찾고 클래스의 정의를 런타임에 로드하려고 시도한다. Java.Lang.ClassLoader.loadClass() 메소드를 이용하여 해당 클래스가 있는지 찾고, 만약 찾지 못한다면 상위 클래스 로더로 위임하게 됩니다. 결국 부모도 못찾게되면, java.net.URLClassLoader.findClass() 메소드를 이용하여 파일 시스템에서 찾게 됩니다. 이 마저도 실패하면 ClassNotFoundException 또는 NoClassDefFoundError를 throw하게 됩니다. 

이미지로 표현한 클래스 로더 동작 순서

ClassLoaderRunner는 자기 자신을 로딩한 Application Class Loader에게 Internal 클래스 로딩을 요청합니다. 클래스 로딩 요청을 받은 Application Class Loader는 스스로 직접 로딩하지 않고 상위 클래스로더인 Extension Class Loader에게 위임하게 됩니다. Extension Class Loader는 최상위 클래스 로더인 Bootstrap Class Loader에게 다시 위임하게 되고, 최상위 클래스 로더에 도달하면 rt.jar에 있는지 확인하고 있으면 반환, 없으면 다시 하위 클래스로 보내는 로직을 반복하게 되고, 마지막에도 못찾게되면 ClassNotFoundException을 throw하게 됩니다. (이미지 진행과정 설명+ 아래의 위임원칙을 따르는 것을 확인할 수 있다.)

 

 

위 과정은 3가지 원칙을 준수하며 진행된다. 

 


 

지켜야 할 3가지 규칙

 

    • 위임 원칙(Delegation Model)
    • 위에서 설명한 클래스 로더 동작 방식을 보면, 위임 법칙을 따른다는 것을 확인할 수 있다.
    • 하위 클래스 로더는 클래스 또는 리소스 검색을 상위 클래스 로더에 위임한다는 원칙. 그래서 최상위 클래스 로더인 Bootstrap ClassLoader 부터 자신이 해당 클래스를 가지고 있는지 확인한 후 결과를 돌려주는 방식으로 진행
  • 가시 범위 원칙(visibility)
    • 하위 클래스로더는 상위 클래스로더가 로딩한 클래스를 볼 수 있지만, 상위 클래스로더는 하위 클래스로더가 로딩한 클래스를 볼 수 없다는 원칙
    • 이로 인해 java.lang.Object 클래스 등 상위 클래스 로더에서 로드한 클래스도 하위 클래스 로더인 시스템 클래스 로더 등에서 사용할 수 있다.
    • 예를 들자면, Application Class Loader는 Extension Class Loader, Bootstrap Class Loader가 로딩한 클래스는 볼 수 있으나, 반대로 Extension Class Loader, Bootstrap Class Loader는 Application Class Loader가 로딩한 클래스는 볼 수 없다는 원칙
  • 유일성의 원칙(unique classes)
    • 하위 클래스로더는 상위 클래스로더가 로딩한 클래스를 다시 로딩하지 않게 해서 로딩된 클래스의 유일성을 보장
    • 위임 원칙에 의해서 위쪽으로 책임을 위임하기 때문에 고유한 클래스를 보장할 수 있다.

 


동적 클래스 로딩

자바의 클래스 로딩은 클래스 참조 시점에 JVM에 코드가 링크되고, 실제 런타임 시점에 로딩되는 동적 로딩을 거친다. 런타임에 동적으로 클래스를 로딩한다는 것은 JVM이 미리 모든 클래스에 대한 정보를 메소드 영역에 로딩하지 않는다는 것(=실행될때 모든 클래스를 메모리에 올려두지 않고 그때마다 필요한 클래스를  메모리에 올려 효율적으로 관리)을 의미한다.

동적으로 클래스를 로딩하는 방식은 두 가지가 있다.

 

로드 타임 동적 로딩 (Load-time Dynamic Loading)

public class HelloWorld { 
        public static void main(String[] args) { 
                System.out.println("안녕하세요!"); 
        } 
}

위 코드의 경우 다음과 같이 동작한다.

 

  1. JVM이 시작되고 부트스트랩 클래스 로더가 생성된 후에 모든 클래스가 상속받고 있는 Object 클래스를 읽어온다.
  2. 클래스 로더는 명령 행에서 지정한 HelloWorld 클래스를 로딩하기 위해, HelloWorld.class 파일을 읽는다.
  3. HelloWorld 클래스를 로딩하는 과정에서 필요한 클래스인 java.lang.String과 java.lang.System을 로딩한다.

이처럼 하나의 클래스를 로딩하는 과정에서 동적으로 다른 클래스를 로딩하는 것을 로드 타임 동적 로딩이라고 한다


런타임 동적 로딩 (Run-time Dynamic Loading)

public class RuntimeLoading { 
        public static void main(String[] args) { 
                try { 
                        Class cls = Class.forName(args[0]); 
                        Object obj = cls.newInstance(); 
                        Runnable r = (Runnable) obj; 
                        r.run(); 
                } catch (Exception e) {
                        e.printStackTrace();
                }
        }
}

위 코드에서 Class.forName(className) 은 파라미터로 받은 className에 해당하는 클래스를 로딩한 후에 객체를 반환하며, 다음과 같이 동작한다.

 

  1. Class.forName() 메소드가 실행되기 전까지는 RuntimeLoading 클래스에서 어떤 클래스를 참조하는지 알 수 없다.
  2. 따라서 RuntimeLoading 클래스를 로딩할 때는 어떤 클래스도 읽어오지 않고, RuntimeLoading 클래스의 main() 메소드가 실행되고 Class.forName(args[0]) 를 호출하는 순간에 비로소 args[0] 에 해당하는 클래스를 로딩한다.

 

이처럼 클래스를 로딩할 때가 아닌, 코드를 실행하는 순간에 클래스를 로딩하는 것을 런타임 동적 로딩이라고 한다.


Impa dev님의 글을 목차로 정리했다. 

https://inpa.tistory.com/entry/JAVA-%E2%98%95-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%8A%94-%EC%96%B8%EC%A0%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%97%90-%EB%A1%9C%EB%94%A9-%EC%B4%88%EA%B8%B0%ED%99%94-%EB%90%98%EB%8A%94%EA%B0%80-%E2%9D%93

 

☕ 클래스는 언제 메모리에 로딩 & 초기화 되는가 ❓

JVM의 클래스 로더 (Class Loader) 자바의 클래스들이 언제 어디서 메모리에 올라가고 클래스 멤버들이 초기화되는지, 원리를 알기위해선 우선 JVM(자바 가상 머신)의 클래스 로더(Class Loader)의 진행

inpa.tistory.com

 

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

 

2. 인스턴스 생성( =  메인 클래스에서 외부클래스를 인스턴스화 했을 경우) => 외부 클래스 로드됨, 하지만 외부클래스의 Inner클래스는 직접 인스턴스 호출하지 않으니 호출되지 않음.

 

3. Static 변수 호출( = 메인 클래스에서 외부 클래스 내부의 Static을 호출 했을 경우,인스턴스 생성하지 않음) => 외부 클래스 로드됨

 

4. Static final 상수 호출( = 메인 클래스에서 외부 클래스 내부의 Static final을 호출한 경우) => 외부 클래스가 로드 되지 않는다. 상수는 JVM의 Method Area에 Constant Pool에 따로 저장되기 때문이다.

 

5. Static 메소드 호출(= 3번과 마찬가지 클래스의 인스턴스 생성하지 않고 외부 클래스 내부의 static 메소드 호출) => 외부클래스 로드됨.

 

6. 내부 클래스 호출( = 외부 클래스 내부의 Inner클래스(static없음) 호출) => Inner클래스를 위해선 외부클래스를 먼저 생성하고 인스턴스화 해야 한다. 외부, Inner클래스 둘다 호출  

@ 이런 특징 떄문에 Inner클래스를 static으로 선언하지 않고 인스턴스 멤버클래스로 사용하면 메모리 누수가 발생함.

 

7. static 내부 클래스 호출( = 외부클래스 내부의 Holder클래스(static있음) 호출) => Holder클래스는 외부클래스를 생성하지 않고 바로 직접 인스턴스화 가능 즉, 외부 클래스 호출하지 않고 Holder클래스만 호출 가능. 6번과 차이점을 비교해라.

@static이 붙었다고 해서 Holder(=static inner) 클래스를 static 멤버나 static 메서드처럼 취급해서 생각하면 안된다.
Inner(=inner) 클래스와 Holder(=static inner) 클래스의 차이는 외부클래스를 생성해야 내부 클래스를 인스턴스를 할수있느냐 없느냐의 차이일뿐, 클래스를 초기화해서 사용하는 것은 같다.

 

8. Static 내부 클래스의 Static 변수 호출( = 외부 클래스 내부의 Holder클래스 안에 있는 Static 변수 호출) => 마찬가지로 클래스를 인스턴스화 하지 않아도 static 멤버를 호출하면 값이 출력된다. 즉, 외부 클래스를 호출하지 않고 Holder클래스만 호출.


위의 과정을 통해, 클래스 초기화 시점을 알아보자.

클래스 초기화는 static 블록과 static 멤버 변수의 값을 할당하는 것을 의미한다. 꼭 new 생성자로 클래스를 인스턴스화 해야 클래스가 초기화 되는 것이 아니다. 

 

위의 클래스 로더 설명에서 클래스 로더의 초기화 과정을 3단계로 나누어 설명했지만, 사실 클래스 초기화는 클래스 로드 시점과 거의 동시에 일어나기 때문에 같다고 볼 수있다

  • 클래스의 인스턴스 생성, 2번
  • 클래스의 정적 메소드 호출, 5번
  • 클래스의 정적 변수 할당, 3번
  • 클래스의 정적 변수 사용(final 제외),3번

6번은 제외해야 한다. 저렇게 하면 안된다는 것이다. 


@static 블록 - 클래스가 로딩되고 클래스 변수가 준비된 후 자동으로 실행도는 블록

아래 예시를 봐서 new 생성자로 인스턴스화 하지 않아도 클래스가 메모리에 로드되어 초기화 되는 것을 알 수 있다. 

 

class Outer {
	// static 블록
    static {
        System.out.println("> Initializing class Outer");
    }
	
    // 생성자
    Outer() {
        System.out.println("> Outer 생성자 호출");
    }
	
    // 정적 메소드
    static void getInstance() {
        System.out.println("> Outer 클래스의 static 메서드 호출");
    }
}

public class Main {
    public static void main(String[] args) {
        Outer.getInstance(); // 정적 메소드 호출
    }
}

위의 코드를 설명 new로 인스턴스를 생성하지 않고 static으로 클래스 초기화가 가능.

 


클래스 초기화 진행순서

 

클래스 초기화 시점을 알았으니, 이번에 클래스가 초기화 되면 클래스 내부에서 어떤 멤버들이 어떤 순서로 초기화가 순차적으로 진행되는지 알아보자.

 

class Outer {
    public static Object obj = new Print(); // "1. 정적 변수"

    static {
        System.out.println("2. 정적 블록");
    }

    Outer() {
        System.out.println("3. 생성자");
    }
}

// 정적 변수가 초기화 됬음을 출력해주는 용도
class Print {
    Print() {
        System.out.println("1. 정적 변수");
    }
}

public class Main {
    public static void main(String[] args) {
        new Outer();
    }
}

 

정적 변수 -> 정적 블록 -> 생성자 순서대로 나온다. 하지만 중요한 점은 정적 변수와 정적 블록의 순서는 코드 선언의 순서에 따라 다르다. 한 마디로 위의 코드에서 정적 블록이 위에 있었다면 정적 블록 -> 정적 변수 순서 였을 것이다.

생성자는 항상 마지막이다. 

 


클래스 초기화는 오직 한번 수행

 

만일 멀티 쓰레드 환경에서 여러개의 쓰레드가 동시에 클래스를 인스턴스화 하여도 클래스 초기화는 오직 한번만 수행된다. 정확히 말하면 클래스 로딩이 최초로 될때, 그때 한번만 초기화를 수행하고 그 이후에는 초기화를 스킵한다고 보면 된다.

 

즉, 멀티 스레드 환경에서 클래스 초기화 동적 자체는 스레드 세이프함을 의미한다. 

 

위의 내용이 중요한 이유를 설명하겠다.

 

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Main {
    public static void main(String args[]) {
        // 1. 스레드 풀 생성
        ExecutorService service = Executors.newCachedThreadPool();

        // 2. 반복문을 통해 - 10개의 스레드가 동시에 인스턴스 생성
        for (int i = 0; i < 10; i++) {
            service.submit(() -> {
                BadSingleton.getInstance();
            });
        }
        // 3. 종료
        service.shutdown();
    }
}
class BadSingleton {
    private BadSingleton() {
        System.out.println("싱글톤 인스턴스 생성");
    }

    private static BadSingleton instance;

    public static BadSingleton getInstance() {
        // 여러 스레드가 동시에 if문을 통과하는 경우
        if(instance == null) {
            // 여러개의 인스턴스가 만들어질 수 있음
            instance = new BadSingleton();
        }
        return instance;
    }
}

 

이 코드에서 중요한 착각이 하나 있다..

if (instance == null) 과
instance = new BadSingleton()
이게 한 덩어리로 실행된다고 착각한다.

스레드 스케줄링 때문에 중간에 끼어들 수 있다.


AI를 활용한 예시.

⏱️ 타임라인 시작

🔵 1단계 – 스레드 A 실행

 
if (instance == null) // true
  • 아직 객체 없음
  • 조건 통과
  • ⚠️ 아직 생성은 안 함

👉 여기서 CPU를 빼앗긴다 (컨텍스트 스위치)


🔴 2단계 – 스레드 B 실행

 
if (instance == null) // true
  • 여전히 null
  • 조건 통과
 
instance = new BadSingleton();
  • 객체 #1 생성
  • instance → 객체 #1

🔵 3단계 – 다시 스레드 A 실행

 
instance = new BadSingleton();
  • ⚠️ 스레드 A는 이미 조건을 통과했기 때문에
  • 다시 null 검사 안 함
  • 객체 #2 생성
  • instance → 객체 #2 (덮어씀)

4️⃣ 결과를 정리하면 이렇게 된다

스레드반환받은 객체
스레드 B 객체 #1
스레드 A 객체 #2

👉 객체가 2개 만들어짐
👉 싱글톤 완전히 깨짐


5️⃣ “아니, instance에 하나만 남잖아?” 라는 착각

이 부분에서 많은 사람들이 헷갈려.

어쨌든 마지막엔 instance 하나만 남는 거 아냐?

중요한 포인트 👇

  • “객체가 몇 개 생성되었는가” 가 핵심
  • 참조가 덮어씌워졌을 뿐
  • 이미 다른 스레드는 다른 객체를 사용 중
 
BadSingleton a = BadSingleton.getInstance(); // 객체 #1 BadSingleton b = BadSingleton.getInstance(); // 객체 #2

👉 a != b

싱글톤의 본질인

“애플리케이션 전체에서 단 하나의 인스턴스”

이 조건을 깨버린 거야.

 


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Main {
    public static void main(String args[]) {
        // 1. 스레드 풀 생성
        ExecutorService service = Executors.newCachedThreadPool();

        // 2. 반복문을 통해 - 10개의 스레드가 동시에 인스턴스 생성
        for (int i = 0; i < 10; i++) {
            service.submit(() -> {
                SingleTon.getInstance();
            });
        }
        // 3. 종료
        service.shutdown();
    }
}

// 싱글톤 클래스
class SingleTon {
    // private 생성자 new 를 통한 인스턴스 생성 방지
    private SingleTon() {
        System.out.println("싱글톤 인스턴스 생성");
    }

    // 정적 메서드를 통해 내부 클래스 로딩
    public static SingleTon getInstance() {
        return LazyHolder.INSTANCE;
    }

    // 내부 클래스가 로딩될때 초기화 수행 - 싱글톤 인스턴스 생성
    private static class LazyHolder {
        private static final SingleTon INSTANCE = new SingleTon();
    }
}

 

싱글톤 LazyHolder는 클래스 로딩 및 초기화 과정이 스레드 세이프함을 이용하는 방법이다.

Spring프레임 워크를 이용하면 직접 싱글톤을 만들어 사용할 일은 없다.

하지만 어떻게 동작하는지는 알아둬야 한다.

 


https://steady-coding.tistory.com/593

 

[Java] JVM의 클래스 로더란?

java-study에서 스터디를 진행하고 있습니다. 클래스 로더란? 자바는 동적 로드, 즉 컴파일 타임이 아니라 런타임(바이트 코드를 실행할 때)에 클래스 로드하고 링크하는 특징이 있다. 이 동적 로드

steady-coding.tistory.com

https://velog.io/@skyepodium/%ED%81%B4%EB%9E%98%EC%8A%A4%EB%8A%94-%EC%96%B8%EC%A0%9C-%EB%A1%9C%EB%94%A9%EB%90%98%EA%B3%A0-%EC%B4%88%EA%B8%B0%ED%99%94%EB%90%98%EB%8A%94%EA%B0%80

 

클래스는 언제 로딩되고 초기화되는가? (feat. 싱글톤)

클래스는 언제 로딩되고 초기화되는가?

velog.io

https://velog.io/@sincewhen/Java-Class-Load-Process

 

Java Class Load 과정

Java로 프로그래밍을 하는 사람이라면 우리가 .java 파일로 작성한 소스 코드가 빌드하면서 어떤 과정을 거치는지, 어떻게 JVM에 올라가는지 아는 것은 중요합니다. 그래야 클래스 로딩 때 발생하

velog.io

https://www.baeldung.com/java-classloaders

 

https://velog.io/@wpdlzhf159/Java-jar%EB%9E%80

 

[Java] jar 파일이란?

간단한 jar 파일 생성을 통해 자바 애플리케이션 배포시 사용되는 jar파일에 대해 알아보고 zip과의 차이점은 무엇이 있을지 알아봅시다!

velog.io

 

공부용 작성글입니다. 위의 사이트를 참고했습니다. 문제시 삭제하겠습니다.

'언어 > java' 카테고리의 다른 글

객체의 메모리 레이아웃과 Heap(JVM)  (1) 2026.01.23
Runtime Data Areas(JVM)이란?  (0) 2026.01.21
JVM이란? 메모리 구조!  (0) 2026.01.14