자바 스터디 1주차: JVM 이란? 자바코드는 어떻게 실행될까?

hwanse·2021년 1월 28일
0

Java

목록 보기
1/14
post-thumbnail

이번에 백기선님의 자바 라이브 스터디를 계기로 첫 블로그를 시작합니다.
더 나아가 개발관련해서 공부한 내용을 정리하여 블로그에 남겨보려고 합니다.

학습 목표

  1. JVM이란 무엇인가?
  2. 자바파일을 컴파일 및 실행하는 방법
  3. 바이트코드란 무엇인가?
  4. JIT 컴파일러란 무엇이며 어떻게 동작할까?
  5. JVM 구성요소
  6. JDK 와 JRE의 차이


1.JVM 이란 무엇인가?

JVM 이란 Java Vertual Machine, 자바 가상 머신의 약자를 따서 줄여 부르는 용어.
JVM 은 Java 와 OS 사이에서 중개자 역할을 수행하여 자바 바이트 코드(class파일) 를 OS에 특화된 코드로 변환하여 실행한다. 따라서 자바 바이트 코드는 OS 플랫폼으로 부터 독립적이며 모든 자바 가상 머신은 규격에 정의된 대로 자바 바이트 코드를 실행한다.


2.자바파일을 컴파일 및 실행하는 방법

  • 컴파일 : 사전적 의미로는 사람이 이해하는 언어를 컴퓨터가 이해할 수 있는 언어로 바꾸는 과정을 말함

자바에서의 컴파일은 .java 파일을 .class 파일로 만드는 과정이며, 자바 소스를 컴파일 및 실행하기 위해서는 자바 설치시 JDK에 포함된 java.exe, javac.exe 두 파일이 필요하다.

자바 설치가 설치되있으며 Hello.java라는 자바 파일이 생성되어 있다고 가정하여 진행한다.

자바 파일을 컴파일 하여 class 파일로 변환하기

$ javac Hello.class

자바파일을 실행하는 방법

$ java Hello



3.바이트 코드란 무엇인가?

프로그램을 실행하는 것은 컴퓨터이다, 사람이 프로그램을 개발을 할 수 있으나 해당 프로그램을 실행하는 주체인 컴퓨터가 이해할 수 있는 언어의 형태로 작성되어 있어야 한다. 예로 자바는 좀 더 사람에게 친화적인 언어의 형태이며 개발자가 작성한.java파일은 컴퓨터의 입장에서는 이해할 수 없는 코드이기 때문에 변환하는 과정을 통해 컴퓨터가 이해할 수 있는 형태로 만들어 주어야 한다. 여기서 변환을 해주는 중개자 역할은 JVM이 담당하고 있으며, 이때 JVM이 이해할 수 있는 형태의 코드가 바로 바이트 코드다.


4.JIT 컴파일러

JIT(Just-In-Time) 컴파일러는 런타임시 Java 애플리케이션의 성능을 향상시키는 Java Runtime Environment의 구성 요소이다. JVM의 어떤 것도 컴파일러보다 성능에 더 큰 영향을 미치지 않는다.

왜 성능을 향상시킬까?

먼저, JIT 컴파일(Just-In-Time Compilation) 방식은 프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일 기법으로 전통적인 컴퓨터 프로그램을 만드는 두 가지 방식인 인터프리트 방식과 정적 컴파일 방식을 혼합한 방식이다. JVM의 컴파일 실행(Runtime)시점에서 인터프리트 방식과 같이 네이티브 코드를 생성하고, 반복 호출된 동일한 바이트 코드를 네이티브 코드로 매번 번역하는 것이 아닌 캐싱을하여 해당 반복된 바이트 코드가 있을 때마다 캐싱된 네이티브 코드를 바로 호출한다. 매번 같은 기계어 코드를 생성하는 것을 방지함으로써 인터프리트 방식의 성능적인 단점을 보완하여 JVM의 실행 성능를 향상시키는 효과를 준다.

어떻게 동작할까?

자바는 javac라는 컴파일러를 사용한다. javac는 .java파일의 소스 코드를 .class파일의 바이트 코드로 변환한다. 이 바이트 코드는 JVM에 있는 인터프리터를 통해 읽혀져 머신이 실행되고 있는 플랫폼에서 실행 가능한 native 코드로 변환되어 수행된다. 인터프리터는 한 줄씩 한 줄씩 바이트 코드을 읽어들이는데, 이 때 특정 반복된 코드가 자주 읽히게 되면 그 바이트 코드 영역을 hot code영역이라고 부른다. JIT 컴파일러는 이 hot code영역을 좀 더 효율적으로 다루기 위해 등장했고 인터프리터를 보완해주는 컴파일러다. 인터프리터가 반복된 바이트 코드를 읽는 도중에 hot code영역이라고 판단을 내리면 JIT 컴파일러에게 컴파일을 위임한다. JIT 컴파일러는 이 요청을 받고 바이트 코드를 최적화해서 native 코드로 컴파일을 하고 캐싱한다. 이렇게 캐싱된 native 코드를 통해서 인터프리터가 추후 같은 hot code 영역을 읽으려 할 때 인터프리터를 사용하지 않고 캐싱되어진 최적화 native 코드를 호출하기 때문에 결과적으로 JVM이 좀 더 향상된 실행 성능을 보여주는 것이다.

JIT 컴파일 과정
<이미지 출처 : https://aboullaite.me/understanding-jit-compiler-just-in-time-compiler >


5.JVM 구성요소

JVM의 구성요소는 아래와 같다
JIT 컴파일 과정
<이미지 출처 : 백기선님 더 자바, 코드를 조작하는 다양한 방법>

클래스 로더 시스템

.class파일에서 바이트 코드를 읽고 메모리에 저장하는 역할

  • 로딩
    • 클래스를 읽어오는 과정
  • 링크
    • 클래스의 레퍼런스를 연결하는 과정
  • 초기화
    • static 값들의 초기화 및 변수 할당

메모리

  • 메소드 영역
    • 클래스 수준의 정보(클래스 이름, 부모 클래스 이름, 메소드, 변수)를 저장(전체 공유 자원)
  • 힙 영역
    • 실제 new 생성자를 통해 생성된 객체를 저장(전체 공유 자원)
  • 스택 영역
    • 각 쓰레드별로 비례하여 런타임 스택을 만들고, 그 스택 안에 메소드 호출할 때 마다 스택 프레임이라 불리는 블럭으로 쌓는다. 쓰레드가 종료되면 런타임 스택도 사라짐.
  • PC 레지스터
    • 쓰레드마다 쓰레드 내 현재 실행알 instruction의 위치를 가리키는 포인터가 생성됨.
  • 네이티브 메소드 스택
    • 네이티브 메소드를 호출할 때 사용하는 별도의 스택, 여기서 네이티브란 java가 아닌 c와 같은 언어로 구현된 메소드를 말함.
    • 예) Thread.currentThread 를 예시로 들 수 있다. 해당 메소드의 선언부가 아래와 같음
      public static native Thread currentThread()

실행 엔진

  • 인터프리터
    • 바이트 코드를 한 줄씩 읽어서 네이티브 코드로 변환
  • JIT 컴파일러
    • 바이트 코드에서 반복되는 코드를 미리 네이티브 코드로 변환함
    • 반복되는 바이트 코드가 있을 경우 인터프리터로 컴파일하지 않고 미리 변환된 네이티브 코드를 바로 사용
    • 인터프리터의 효율성에 대한 단점을 JIT 컴파일러가 보완하는 역할
  • GC (Garbage Collector)
    • 더 이상 참조되지 않는 객체를 모아서 메모리를 정리하는 역할
    • 경우에 따라 성능 향상을 위해 개발자가 GC 사이즈를 커스터 마이징을 해야하는 경우가 있음 (대규모 프로젝트일 경우)


6.JDK 와 JRE의 차이

JIT 컴파일 과정
<이미지 출처 : 백기선님 더 자바, 코드를 조작하는 다양한 방법>

JDK (Java Development Kit)

  • JRE + 개발용Tool을 합친 Kit
  • 오라클은 자바 11부터는 JDK만 제공하여 JRE를 따로 제공하지 않음

JRE (Java Runtime Environment)

  • 자바 애플리케이션을 실행할때 필요한 요소들이 구성되어 있음
  • JVM과 핵심 라이브러리 및 자바 런타임 환경에서 구동시 필요한 리소스 파일을 가지고 있음
  • JDK와 달리 개발 관련 도구는 포함되어 있지 않다



참고

profile
만사가 귀찮은 ISFP가 쓰는 학습 블로그

0개의 댓글