hyeonga_code

JVM_10_정적 로딩과 동적 로딩 본문

JVM

JVM_10_정적 로딩과 동적 로딩

hyeonga 2024. 5. 8. 14:59
반응형

동적 로딩 Dynamic Loading

프로그램이 실행 중일 때 필요한 클래스나 라이브러리, 객체를 로드하는 방식입니다.
지연 로딩 Lazy Loading 이라고도 합니다.

  • 장점
    사용하지 않는 모듈은 로드하지 않아 메모리 효율성이 높습니다.
    필요한 컴포넌트만 로드하여 확장에 유연합니다.
    개발 모듈이 독립적으로 업데이트가 가능하여 유지보수가 용이합니다.
  • 단점
    로딩 코드를 관리하고 의존성에 문제가 발생할 수 있습니다.
    실행 시점에 로드해야 하므로 초기 로딩 시간이 필요합니다.



정적 로딩 Static Loading

프로그램 실행 전에 필요한 모든 컴포넌트가 메모리에 로드되는 방식입니다.
컴파일 타임에 코드와 라이브러리가 결정되어 실행 파일에 포함됩니다.
시스템 소프트웨어에서 전체 시스템을 하나의 실행 파일로 컴파일합니다.

  • 장점
    추가적인 외부 코드의 로딩을 요구하지 않아 런타임 성능 향상에 도움이 됩니다.
    실행 시 외부 참조가 적어 속도가 빠르고 의존성을 관리할 필요가 없습니다.
    시작 시간을 감소할 수 있습니다.
    모든 종속성을 실행 파일에 포함시켜 종속성 관리의 단순화가 가능합니다.
    메모리 접근에 최적화 되어 있어 추가적인 객체 생성/제거가 없어 GC의 부하가 감소합니다.
    프로그램의 모든 스레드가 동일한 코드와 데이터에 접근하므로 멀티스레딩 환경에서의 안전성이 보장됩니다.
  • 단점
    사용하지 않는 코드도 로드되어 메모리 사용이 많습니다.
    프로그램 수정 시 전체를 재컴파일하여 다시 배포해야 합니다.
    예시
  • 시스템 라이브러리 링크
    운영체제가 제공하는 시스템 라이브러리이지만 공통 라이브러리에 정적 로딩을 사용하는 대표적인 사례입니다.
    프로그램이 실행될 때 필수적이고 기본적인 기능을 제공하며 실행 파일에 직접 링크되어 배포됩니다.
    정적 링크된 라이브러리는 실행 파일의 크기를 증가시키지만 버전에 따른 종속성 문제나 충돌을 방지할 수 있습니다.
  • 임베디드 시스템과 마이크로컨트롤러
    리소스가 제한된 환경에서 자주 사용됩니다.
    이러한 시스템들은 일반적으로 메모리나 저장공간이 제한적이기 때문에 실행 중에 추가적인 코드를 로드하고 관리하는 것이 실용적이지 않습니다.

    애플리케이션의 시작 시간을 최소화하고자 하는 경우 사용합니다.

 

+ 실행 과정

class A {
  public static String str = "...";
  private static int i = -1;

  priavte final List<String> list = new ArrayList<>();
  private final Long l = 100L;

  public static void main(...) {
    A a = new A();
    B b = new B();
  }
}

class B extends A {
}

 

JVM을 실행하면 클래스 A가 로딩되는 이유

 

JVM이 실행되면 클래스 로더가 META-INF/MANIFEST.MF 파일을 읽어옵니다.
이 파일 내의 Main-Class 에는 진입점을 제공하는 main() 메소드를 가지고 있는 클래스 이름을 명시하고 있습니다.
클래스 A는 main() 메소드를 가지고 있으므로 JVM 실행 시 초기에 로딩이 됩니다.

클래스가 로딩되면 초기화 과정이 수행됩니다. 정적 필드 할당 초기화

 

public static String str = "...";

 

String str :
str 변수는 String 타입의 참조 변수입니다.
정적 필드 는 클래스가 메모리에 로드될 때 메소드 영역 에 할당됩니다.

"..." :
문자열 리터럴은 재사용을 위해 상수 풀 에 저장됩니다.
상수 풀메소드 영역의 일부분입니다.
같은 문자열 리터럴에 대한 여러 참조가 단일 객체를 가리키도록 하기 위함입니다.

str : 메소드 영역에 저장된 "..." 의 주소를 저장합니다.

private static int i = -1;

private :
i 변수가 선언된 클래스 내부에서만 접근할 수 있음을 의미합니다.

int i :
정수형 자료형은 정적 필드메소드 영역 에 할당됩니다.

-1 :
변수 i가 가리키는 메소드 영역 에 저장됩니다.

 

 

 

 

main() 메소드가 실행됩니다.

A a = new A();

A a :
a 는 클래스 A의 인스턴스를 참조하는 변수로 스택 영역 에 할당됩니다.
스택에 저장되는 것은 힙 영역 에 생성된 객체의 주소입니다.

new A() :
실제 클래스 A의 객체는 힙 영역에 생성됩니다.
listArrayList<String> 객체를 가리킵니다.
lLong 객체를 참조하는데 캐싱 매커니즘에 의해 이미 존재하는 인스턴스를 참조할 수 있습니다.
A 객체에 할당되는 메모리는 list 의 참조 주소를 저장할 크기와 l 의 참조 주소를 저장할 크기만큼 할당됩니다.

priavte final List<String> list = new ArrayList<>();

List<String\> list :
참조 타입의 변수 list는 객체의 주소를 가지고 있는 인스턴스 변수이므로 힙 영역 에 할당됩니다.
list 변수에는 객체의 실제 주소 크기만큼만 할당됩니다.

new ArrayList<>() :
새 인스턴스를 생성하게되며 List<String> 을 저장할 수 있는 공간을 힙 영역 에 할당됩니다.

private final Long l = 100L;

Long l :
l힙 영역 에 생성됩니다.
l 은 Long 객체의 실제 주소를 저장합니다.

100L :
Long 타입의 리터럴입니다.

final 변수가 클래스 인스턴스 간에 변하지 않고 공유될 수 있는 값인 경우 static 으로 선언하는 것이 메모리 사용 측면에서 효율적입니다.

B b = new B();

B b :
b 는 클래스 B의 인스턴스를 참조하는 변수로 스택 영역 에 할당됩니다.

new B() :
실제 클래스 B의 객체는 힙 영역에 생성됩니다.
클래스 B는 클래스 A를 상속받고 있기 때문에 A의 모든 인스턴스 변수(list, l)를 포함하여 힙 영역 에 할당됩니다.
클래스 B를 로드할 때, 클래스 로더는 부모 클래스인 클래스 A를 로드합니다.
클래스 로딩은 메소드 영역 에서 발생합니다.

반응형