JVM 동작 방식과 메모리 구조

정은영·2022년 10월 9일
0

CS

목록 보기
2/18

JVM(Java Virtual Machine)

자바 프로그램 실행 환경을 만들어 주는 소프트웨어이다.

Java는 OD에 종속적이지 않다는 특징을 가지고 있다. 따라서 OS에 종속 받지 않고 실행되기 위해서는 OS 위에서 JVM이 Java를 실행시킨다.

→  즉, OS에 종속 받지 않고 CPU가 Java를 인식, 실행할 수 있게 해주는 소프트웨어이다.

Java 소스코드(원시코드) *.java는 CPU가 인식을 하지 못하므로 기계어로 컴파일 해주어야 한다.

하지만 Java는 JVM이라는 가상머신을 거쳐서 OS에 도달하기 때문에 OS가 인식할 수 있는 기계어로 바로 컴파일되는 것이 아닌 JVM이 인식할 수 있는 Java bytecode *.class로 변환된다.

→ 즉, Java compiler가 .java파일을 .class라는 Java bytecode로 변환한다.

Java compiler 는 JDK를 설치하면 bin에 존재하는 javac.exe를 말한다. javac 명령어를 통해 .java를 .class로 컴파일 할 수 있다.

변환된 bytecode는 기계어가 아니기 때문에 OS에서 바로 실행될 수 없다. 이 때 JVM이 OS가 bytecode를 이해할 수 있도록 해석해 준다.

따라서 bytecode는 JVM 위에서 OS에 상관없이 실행될 수 있다. OS에 종속적이지 않고, Java 파일만 만들면 어느 디바이스든 JVM 위에서 실행할 수 있다.

→ 즉, JVM을 사용했을 때 장점은 하나의 바이트 코드.class로 모든 디바이스에서 동작하도록 할 수 있는 것이라고 할 수 있다.

  1. 개발자가 자바 소스코드(.java)를 작성한다.
  2. 자바 컴파일러가 자바 소스코드(.java)파일을 읽어 바이트코드(.class)코드로 컴파일 한다. 바이트코드(.class)파일은 아직 컴퓨터가 읽을 수 없는 JVM(자바 가상 머신)이 읽을 수 있는 코드이다. (java - > class)
  3. 컴파일된 바이트코드(.class)를 JVM의 클래스로더(Class Loader)에게 전달한다.
  4. 클래스 로더는 동적로딩(Dynamic Loading)을 통해 필요한 클래스들을 로딩 및 링크하여 런타임 데이터 영역(Runtime Data Area), 즉 JVM의 메모리에 올린다.
  5. 실행엔진(Execution Engine)은 JVM 메모리에 올라온 바이트 코드들을 명령어 단위로 하나씩 가져와서 실행한다. 이 때 실행 엔진은 두 가지 방식으로 분류된다.
  • 인터프리터 : 바이트 코드 명령어를 하나씩 읽어서 해석하고 실행한다. 하나하나의 실행은 빠르나, 전체적인 실행 속도가 느리다는 단점을 가진다.

  • JIT컴파일러 : 인터프리터의 단점을 보완하기 위해 도입된 방식으로 바이트 코드 전체를 컴파일하여 바이너리 코드로 변경하고 이후에는 해당 메서드를 더이상 인터프리팅 하지 않고, 바이너리 코드로 직접 실행하는 방식이다. 하나씩 인터프리팅하여 실행하는 것이 아니라 바이트 코드 전체가 컴파일된 바이너리 코드를 실행하는 것이기 때문에 전체적인 실행속도는 인터프리팅 방식보다 빠르다.


JVM 구조

  1. Java로 개발된 프로그램을 실행하면 JVM은 OS로부터 메모리를 할당받는다.

  2. Java compiler(javac)가 java source code(.java)를 java bytecode(.class)로 컴파일한다.

  3. Class Loader를 통해 JVM Runtime Data Area로 로딩한다.

  4. Runtime Data Area에 로딩된 .class들은 Execution Engine을 통해 해석된다.

  5. 해석된 bytecode는 Runtime Data Area의 각 영역에 배치되어 수행하며 이 과정에서 Execution Engine에 의해 GC의 작동과 스레드 동기화가 이루어진다.


JVM 메모리 구조

클래스 로더(Class Loader)

  • .class 바이트 코드를 읽어 들여 class 객체를 생성하는 역할을 한다. 즉, 클래스 로더는 클래스가 요청될 때 파일로부터 읽어 메모리로 로딩하는 역할을 한다.

  • Java bytecode(.class)를 묶어서 JVM이 운영체제로부터 할당받은 메모리 영역(Runtime Data Area)로 적재한다.

  • bytecode는 Runtime Data Area 중 Method Area에 배치된다.

    • 다음과 같은 정보를 다룬다.

      • Field 정보

      • Method 정보

      • Type 정보

      • Constant(상수) Pool

실행 엔진(Execution Engine)

  • JVM에 로드된 bytecode를 기계어로 변경하여 명령어(instruction) 단위로 실행gksek. bytecode를 운영체제에 맞게 해석해주는 역할을 한다.

가비지 컬렉터(GC; Garbage Collector)

  • 더이상 사용하지 않는 메모리를 자동으로 회수한다.
  • 개발자가 메모리를 따로 관리하지 않아도 되기 때문에 쉽게 프로그래밍할 수 있도록 도움을 준다.
  • Heap 메모리 영역에 생성된 객체들 중 참조하지 않는 객체를 제거한다.

런타임 데이터 영역(Runtime Data Area)

Java Application을 실행할 때 사용하는 데이터들을 로드하는 영역이다.

  • 모든 스레드가 공유해서 사용하는 영역

    • 힙 영역 (Heap Area)

    • 메서드 영역 (Method (Static) Area)

  • 각 스레드에서 별도로 사용하는 영역

    • 스택 영역 (Stack Area)
    • PC 레지스터 (PC Register)
    • 네이티브 메서드 스택 (Native Method Stack)

힙 영역

  • new 키워드로 생성된 객체와 배열이 생성되는 영역이다.
  • 주기적으로 GC가 제거하는 영역이다.

스택 영역

  • 지역 변수, 파라미터, 리턴 값, 연산에 사용하는 임시 값 등이 생성된다.

PC 레지스터

  • Thread가 생성될 때마다 생성되는 영역이다.
  • 프로그램 카운터를 저장하는 영역이다.
  • 현재 스레드가 실행되는 부분의 주소와 명령을 저장한다.

프로그램 카운터란, 프로세서 내부에 있는 레지스터 중의 하나로서 다음에 실행될 명령어의 주소를 가지고 있기 때문에 명령어 포인터라고도 한다. 프로그램 카운터는 각 명령어 사이클에 따라 자동으로 증가하여, 메모리에 있는 명령어들이 순차적으로 실행될 수 있도록 한다.

네이티브 메서드 스택

  • 자바 이외의 언어로 작성된 네이티브 코드를 실행할 때 사용하는 메모리 영역이다.

Reference

0개의 댓글