본문 바로가기
Programming Language/Java

Java의 동작 방식에 대하여 - JVM Architecture, ClassLoader

by Ahngyuho 2023. 7. 19.

Java 동작 방식

항상 Java는 어떤 식으로 실행되는지 궁금할 때가 많았습니다. 현재 이 글을 작성하는 시점에서도 그렇게 완벽한 이해라고는 생각하지 않지만 이번 글에서는 Java는 어떤 식으로 실행되는지 JVM과 ClassLoader를 중점으로 글을 작성해 보려고 합니다.

 

What is JVM?

JVM이란 Java Virtual Machine의 약자로 자바 바이트 코드를 실행시키기 위한 가상의 기계를 말합니다.

자바 프로그램을 실행하기 위해서는 반드시 JVM이 설치되어 있어야 합니다.

 

JVM에는 메모리 영역이 할당되는데 Java 동작 방식 이해에 아주 중요한 개념입니다.

 

 

Java Runtime Sytem

JVM에 할당되는 메모리 영역은 5 영역으로 나뉩니다.

 

PC (pc register)

CPU에 PC register가 존재하듯이 JVM에서도 PC register라는 개념이 존재합니다. Java는 다중 스레딩을 지원하는 언어로

새 스레드가 생성될 때마다 PC register가 생성됩니다. 스레드는 코드를 실행하는 역할을 합니다. 프로그래머가 작성한 코드는 자바 바이트 코드로 변형되어 스레드에 의해 실행됩니다. 실행해야 할 바이트 코드 명령어를 이 PC register에 저장됩니다. 그렇게 되면서 자바 바이트 코드가 실행될 수 있는 겁니다.

 

현재 스레드에서 메서드가 호출이 된다면 실행 완료 후 다시 원래의 명령문으로 돌아올 수 있는 이유도 이 PC register의 존재 때문입니다. CPU의 PC register의 존재 이유가 매우 흡사합니다.

 

제어 흐름(for, while, do-while), 스위치 문 등등 사용되는 부분이 굉장히 많습니다.

 

Stack

JVM은 메서드가 실행되면 필요한 정보(매겨변수, 로컬변수, 및 기타 데이터)를 포함하는 프레이라는 것을 만들어 Stack이라는 영역에서 보관합니다. 

 

 

Native Method Stack

이 영역은 Java 코드 실행에 대한 것이 아닌 운영체제 및 Native Library 실행을 관리하는 영역입니다. 이는 Java 코드 실행이 아닌 C,C++ 와 같은 코드를 실행하는 것과 연관됩니다. 그래서 Native Stack 이라는 별도의 공간이 마련된 겁니다.

 

 

Method

ClassLoader가 프로그램 실행 시 필요한 클래스를 로딩하는 곳이 Method Area 입니다. 이 Method Area는 모든 스레드가 접근할 수 있습니다.

저장되는 데이터는 클래스의 메타데이터로 클래스를 실행하기 위해 필요한 정보입니다. 클래스의 인스턴스를 저장하는 것이 아닌 클래스 자체 정의를 저장한다고 생각하시면 될 거 같습니다.

 

 

Heap

JVm에는 모든 JVM 스레드 간에 공유되는 힙이라는 영역이 존재합니다. 힙에는 static을 제외한 Java 코드에서 객체를 정의 생성하면 힙 메모리에 할당되어 여러 스레드가 접근하여 사용할 수 있습니다.

그리고 여기 Heap 영역이 가비지 컬렉터가 활동하는 영역입니다.

 

Javanew 키워드를 사용하면 Heap 영역에 해당 개체가 메모리에 적재됩니다. 그리고 이 개체가 더 이상 사용되지 않는다고 판단되면 가비지 컬렉터가 메모리를 회수합니다.

Java에 모든 변수들이 힙에 저장되는 것은 아닙니다. 지역 변수나 메서드의 매개변수는 메서드가 실행될 때 Frame이 만들어져 Stack에 저장될 때 결국 이 Stack 영역에 저장되게 됩니다.

그리고 클래스 메타 데이터 및 정적 변수는 special area에 저장됩니다.

 

이렇게 heap 에 저장되지 않는 것들도 존재하지만 시스템의 메모리에 영향을 주는 것은 확실히 Heap 임에는 분명합니다.

 

ClassLoader

  • ClassLoader는 Java 프로그램에서 실행되는 메서드를 찾아 JVM이 관리하는 메모리 영역에 적재하는 역할을 합니다.
  • 로딩, 링크, 초기화라는 실행 순서를 가집니다.

실행 순서

  • 로딩
  • ClassLoader는 Java 클래스를 JVM으로 로드하는 역할을 합니다. 코드에서 처음 클래스가 참조되면 ClassLoader는 클래스 경로(class path)에서 해당 클래스 파일을 검색합니다.
  • 클래스 경로는 JVM이 클래스 파일을 찾는 디렉토리 및 JAR 파일 목록입니다. 이 경로는 ClassLoader의 종류인 Bootstrap, Extension, 시스템 및 사용자 정의 각각 존재합니다.
  • ClassLoader는 계층 구조로 이루어져 있어 상위 계층의 ClassLoader에 해당 클래스가 존재하지 않다면 하위 계층에 클래스 탐색을 위임합니다.
  • 링킹
  • Verify, Prepare, Resolve의 3개의 하위 단계가 존재합니다.
  • Verify - JVM은 로드된 클래스 파일이 JVM 사양을 준수하고 보안 규칙을 위반하지 않았는지 확인하기 위해 해당 단게에서 클래스 파일의 정확성을 확인합니다.
  • Prepare - 클래스 변수(static 변수)와 기본값이 필요한 메모리를 준비합니다.
  • 초기화
  • Static 변수의 값을 할당합니다.

 

ClassLoader 계층구조

ClassLoader는 계층구조로 이루어져 있습니다.

 

  • Bootstrap class loader Jre\lib\rt.jar 라는 파일에 Java Standart Edition 의 모든 클래스 파일들이 있고 이 파일들을 로드 합니다.

 

  • Extention classloader - Extention classloaderBootstrap ClassLoader의 자식 클래스로더이자 System/appliction classloader의 부모 클래스로더입니다Jre\lib\ext 폴더에 존재하는 클래스들을 로딩하는 역할을 합니다.

 

    • System/appliction classloader - 종속성 관리 도구인 Gradle/Maven을 통해 클래스 경로 관리. 종속성 관리 도구가 classpath 관리를 해주기 때문에 Spring 혹은 Spring boot 같은 프레임워크를  Java 애플리케이션에서 사용할  수 있습니다.
    • Gradle,Maven 은 클래스 경로를 관리하는 빌드 자동화 및 종속성 관리 시스템 입니다. Gradle은 프로젝트를 빌드할 때 build.gradle 파일에 정의된 종속성을 확인하고 다운받습니다. 그리고 이 종속성들은 Gradle이 클래스 경로에 추가합니다.

 

이렇게 Java 바이트 코드가 실행되기 위해 필요한 것들을 살펴보았는데 아직 저의 머리에 모든 구조와 실행 프로세스가 완전히 녹아든게 아닌 느낌이 듭니다. 현재 포스팅 글은 저도 두고두고 살펴보면서 수정해 나갈 예정입니다.