JDK

진성대·2023년 3월 20일
0

JAVA

목록 보기
1/2

자바는 os에 독립적인 특징을 가지고 있다. 그게 가능한 이유는 JVM(Java Virtual Machine)덕분이다. 그렇다면 JVM(Java Virtual Machine)의 어떠한 기능 때문에, OS에 독립적으로 실행시킬 수 있는지 자바 컴파일 과정을 통해 알아보자.

Untitled

Untitled

자바 컴파일 순서

  1. 개발자가 자바 소스코드(.java)를 작성

  2. 자바 컴파일러(Java Compiler)가 자바 소스파일을 컴파일 합니다. 이때 나오는 파일은 자바 바이트 코드(.class)파일로 아직 컴퓨터가 읽을 수 없는 자바 가상 머신이 이해할 수 있는 코드입니다. 바이트 코드의 각 명령어는 1바이트 크기의 Opcode와 추가 피연산자로 이루어져 있습니다.

  3. 컴파일된 바이트 코드를 JVM의 클래스로더(Class Loader)에게 전달합니다.

  4. 클래스 로더는 동적로딩 (Dynamic Loading)을 통해 필요한 클래스들을 로딩 및 링크하여 런타임 데이터 영역(Runtime Data area), 즉 JVM의 메모리에 올립니다.

    • 클래스 로더 세부 동작
      1. 로드 : 클래스 파일을 가져와서 JVM의 메모리에 로드합니다.
      2. 검증 : 자바 언어 명세 (Java Language Specification) 및 JVM 명세에 명시된 대로 구성되어 있는지 검사합니다.
      3. 준비 : 클래스가 필요로 하는 메모리를 할당합니다. (필드, 메서드, 인터페이스 등등)
      4. 분석 : 클래스의 상수 풀 내 모든 심볼릭 래퍼런스를 다이렉트 레퍼런스로 변경합니다.
      5. 초기화 : 클래스 변수들을 적절한 값으로 초기화 합니다. (static 필드)
  5. 실행 엔진 (Execution Engine)은 JVM메모리에 올라온 바이트 코드들을 명령어 단위로 하나씩 가져와서 실행합니다. 이때, 실행 엔진은 두가지 방식으로 변경합니다.

    1. 인터프리터 : 바이트 코드 명령어를 하나씩 읽어서 해석하고 실행합니다. 하나하나의 실행은 빠르나, 전체적인 실행 속도가 느리다는 단점을 가집니다.
    2. JIT 컴파일러 (Just - In - Time Compiler) : 인터프리터의 단점을 보완하기 위해 도입된 방식으로 바이트 코드 전체를 컴파일 하여 바이너리 코드로 변경하고 이후에는 해당 메서드를 더이상 인터프리팅 하지 않고, 바이너리 코드로 직접 실행하는 방식입니다. 하나씩 인터프리팅하여 실행하는 것이 아니라 바이트 코드 전체가 컴파일된 바이너리 코드를 실행하는 것이기 때문에 전체적인 실행속도는 인터프리팅 방식보다 빠릅니다.

    Untitled

JVM Internal

NAVER D2

Untitled

클래스 로더(Class Loader)가 컴파일된 자바 바이트코드를 런타임 데이터 영역(Runtime Data Areas)에 로드하고, 실행 엔진(Execution Engine)이 자바 바이트코드를 실행한다.

클래스 로더

자바는 동적 로드, 즉 컴파일 타임이 아니라 런타임에 클래스를 처음으로 참조할 때 해당 클래스를 로드하고 링크하는 특징이 있다. 이 동적 로드를 담당하는 부분이 JVM의 클래스 로더이다. 자바 클래스 로더의 특징은 다음과 같다.

  • 계층 구조 : 클래스 로더끼리 부모-자식 관계를 이루어 계층 구조로 생성된다. 최상위 클래스 로더는 부트스트랩 클래스 로더(Bootstrap Class Loader)이다.
  • 위임 모델 : 계층 구조를 바탕으로 클래스 로더끼리 로드를 위임하는 구조로 동작한다. 클래스를 로드할 때 먼저 상위 클래스 로더를 확인하여 상위 클래스 로더에 있다면 해당 클래스를 사용하고, 없다면 로드를 요청받는 클래스 로더가 클래스를 로드한다.
  • 가시성(visibility)제한 : 하위 클래스 로더는 상위 클래스 로더의 클래스를 찾을 수 있지만, 상위 클래스 로더는 하위 클래스 로더의 클래스를 찾을 수 없다.
  • 언로드 불가 : 클래스 로더는 클래스를 로드할 수는 있지만 언로드할 수 는 없다. 언로드 대신, 현재 클래스 로더를 삭제하고 아예 새로운 클래스 로더를 생성하는 방법을 사용할 수 있다.

각 클래스 로더는 로드된 클래스들을 보관하는 네임스페이스(namespace)를 갖는다. 클래스를 로드할 때 이미 로드된 클래스인지 확인하기 위해서 네임스페이스에 보관된 FQCN(Fully Qualified Class Name)을 기준으로 클래스를 찾는다. 비록 FQCN이 같더라도 네임스페이스가 다르면, 즉 다른 클래스 로더가 로드한 클래스이면 다른 클래스로 간주된다.

다음 그림은 클래스 로더 위임 모델을 표현한 것이다.

Untitled

클래스 로더가 클래스 로드를 요청받으면, 클래스 로더 캐시, 상위 클래스 로더, 자기 자신의 순서로 해당 클래스가 있는지 확인한다. 즉, 이전에 로드된 클래스인지 클래스 로더 캐시를 확인하고, 없으면 상위 클래스 로더를 거슬러 올라가며 확인한다. 부트스트랩 클래스 로더까지 확인해도 없으면 요청받은 클래스 로더가 파일 시스템에서 해당 클래스를 찾는다.

  • 부트스트랩 클래스 로더 : JVM을 기동할 때 생성되며, Object 클래스들을 비롯하여 자바 API들을 로드한다. 다른 클래스 로더와 달리 자바가 아니라 네이티브 코드로 구현되어 있다.
  • 익스텐션 클래스 로더(Extenstion Class Loader) : 기본 자바 API를 제외한 확장 클래스들을 로드한다. 다양한 보안 확장 기능 들을 여기에서 로드하게 된다.
  • 시스템 클래스 로더 (System Class Loader) : 부트스트랩 클래스 로더와 익스텐션 클래스 로더가 JVM 자체의 구성 요소들을 로드하는 것이라 한다면, 시스템 클래스 로더는 애플리케이션의 클래스들을 로드한다고 할 수 있다. 사용자가 지정한 $CLASSPATH 내의 클래스들을 로드한다.
  • 사용자 정의 클래스 로더(User-Defined Class Loader) : 애플리케이션 사용자가 직접 코드상에서 생성해서 사용하는 클래스 로더이다.

웹 애플리케이션 서버(WAS)와 같은 프레임워크는 웹 애플리케이션들, 엔터프라이즈 애플리케이션들이 서로 독립적으로 동작하게 하기 위해 사용자 정의 클래스 로더를 사용한다. 즉, 클래스 로더의 위임 모델을 통해 애플리케이션의 독립성을 보장하는 것이다. 이와 같은 WAS의 클래스 로더 구조는 WAS 벤더마다 조금씩 다른 계층 구조를 사용하고 있다.

클래스 로더가 아직 로드되지 않은 클래스를 찾으면, 다음 그림과 같은 과정을 거쳐 클래스를 로드하고 링크하고 초기화 한다.

Untitled

각 단계를 간단히 설명하면 다음과 같다.

  • 로드 : 클래스를 파일에서 가져와서 JVM의 메모리에 로드한다.
  • 검증 (Verifying) : 읽어 들인 클래스가 자바 언어 명세 (Java Language Specification) a
profile
신입 개발자

0개의 댓글