[Live-Study] JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.

Livenow·2020년 12월 29일
2
post-thumbnail

목표

자바 소스 파일(.java)을 JVM으로 실행하는 과정 이해하기


Overview

  • JVM이란 무엇인가
  • 컴파일 하는 방법
  • 실행하는 방법
  • 바이트코드란 무엇인가
  • JIT 컴파일러란 무엇이며 어떻게 동작하는지
  • JVM 구성 요소
  • JDK와 JRE의 차이

기선님이 주최하신 live-study 1주차입니다.

자바 개발자라고 떳떳히 말할 수 있을 기본기를 쌓아가는 과정입니다.


JVM이란 무엇인가

이부분은 이전에 정리한 포스트로 대체하겠습니다.

[Java] JVM이란?


컴파일 하는 방법

windows 기준으로 컴파일 하는 방법을 정리하겠습니다.

컴파일을 한다는 것은 .java파일.class 파일(바이트 코드)로 만드는 것을 의미합니다.

JDK(Java Developer Kit) 자바 개발 도구를 설치하면 bin디렉토리안에 아래와 같이 자바로 개발하는데 필요한 주요 파일들을 확인할 수 있습니다.

  • javac.exe : 자바 컴파일러, 자바로 작성된 소스코드를 바이트코드로 변환한다.
  • java.exe : 자바 인터프리터, 바이트코드를 실행한다.
  • javap.exe : 역어셈블러, 컴파일된 클래스 파일을 원래의 소스로 변환한다.

설치 후 환경변수 설정을 해야하는데 그것은 다른 개발자님의 블로그를 통해 전달하겠습니다.

자바 설치 확인

cmd를 켜고 아래와 같은 명령어를 타이핑합니다.

> java -version

https://user-images.githubusercontent.com/48986787/103259729-f3b11180-49dd-11eb-9e13-a717b22fcdf7.png

위의 사진과 같이 나온다면 자바가 잘 설치된 것입니다.

자바 소스코드 작성

저는 test 디렉토리에서 test용 자바 파일을 만들겠습니다.

#test/LiveStudy.java

public class LiveStudy {
    public static void main(String[] args) {
	System.out.println("Hello, Study World");
    }
}

만든 .java파일은 디렉토리에서 확인 가능합니다.

https://user-images.githubusercontent.com/48986787/103260459-07aa4280-49e1-11eb-9723-9b079839c5bc.png

.java 확장자를 사용해야 자바 바이트 코드를 만들 수 있는 것은 위에서 확인 했었죠?

컴파일 하기

지금까지 잘 따라왔다면 컴파일을 할 수 있는데요, 컴파일을 할 수 있는 이유는 위에서 환경변수라는 것을 설정했기 때문입니다.

환경변수는 운영체제가 실행할 수 있는 실행파일들이 위치한 디렉토리를 지정해두어 이를 운영체제가 참조해서 사용할 수 있도록 한 것입니다. (javac.exe, java.exe 등)

javac 명령어 사용

https://user-images.githubusercontent.com/48986787/103260721-104f4880-49e2-11eb-8162-5639cd4b0a4d.png

컴파일이 됐으면 디렉토리에서 .class파일(바이트코드)가 만들어 진 것을 확인할 수 있습니다.

https://user-images.githubusercontent.com/48986787/103260890-af744000-49e2-11eb-9448-9d6c0692a78d.png


실행하는 방법

컴파일이 됐으니 실행을 해야겠죠?

java 명령어 사용

https://user-images.githubusercontent.com/48986787/103261119-9b7d0e00-49e3-11eb-8ed8-4b435e998edd.png

출력문을 통해 위에서 작성한 코드가 잘 동작되는지 확인할 수 있었습니다.

명령문 작성시 (.class는 빼고 작성해야함)


바이트코드란 무엇인가

Java에서는 운영체제가 자바 소스코드를 직접 해석하여 사용하는 것이 아니기 때문에 JVM이라는 가상머신이 해당 자바코드를 사용하여, 실행에 대한 하드웨어, 운영체제 측면의 작업을 대신해줍니다.

컴파일시, 컴파일러(javac)에 의해 소스파일(.java)이 목적파일(.class)로 변환될때 컴퓨터가 바로 인식할 수 있는 바이너리코드가 아닌 바이트 코드로 변환되는데요, 그렇기 때문에 바이너리코드바이트 코드의 차이점을 우선 정리하려 합니다.

바이너리 코드 vs 바이트 코드

바이너리 코드

  • 컴퓨터가 인식할 수 있는 0과 1로 구성된 이진코드를 의미

기계어

  • 0과 1로 이루어진 바이너리 코드
  • 기계어가 이진코드로 이루어졌을 뿐이지 모든 이진코드가 기계어인 것은 아님 ( 바이너리 코드 != 기계어)
  • 기계어는 특정한 언어가 아니다.
  • CPU제조사에서 CPU를 만들 때 해당 CPU에서 사용하는 명령어 집합을 공개하는데, 이것을 '기계어'라고 부름
  • 때문에 CPU가 변경되면 기계어가 달라짐. 같은 동작을 하는 명령어지만 완전히 다른 0과 1의 나열이 될 수 있음
  • 아주 기본적인 연산자들은 서로 호환이 됨
  • 같은 회사의 CPU라도 버전 별로 다른 명령을 포함할 수 있으며 다른 회사라도 같은 명령어 집합을 공유할 수 있음

바이트 코드

  • 가상 머신이 이해할 수 있는 언어
  • CPU가 아닌 가상 머신에서 이해할 수 있는 코드를 위한 이진 표현법. 즉, 가상 머신이 이해할 수 있는 0과 1로 구성된 이진코드를 의미.
  • 어떤 플렛폼에도 종속되지 않고 실행될 수 있는 가상 머신용 기계어 코드
  • CPU가 텍스트(.c)를 이해하지 못하듯이 가상 머신 또한 텍스트(.java)를 이해하지 못함.
  • Java의 가상 머신을 JVM이라고 하며 JVM을 위한 바이트 코드를 자바 바이트 코드라고 함.
  • 바이트 코드는 저스트 인 타임(just-in-time, JIT) 컴파일러에 의해 바이너리 코드로 변환됨.

바이트 코드 과정

프로그램을 실행하는것은 컴퓨터입니다. 그렇기 때문에 프로그램은 컴퓨터가 이해할 수 있는 형태로 작성되어 있어야 합니다.

  1. javac로 컴파일
    • 가상머신은 텍스트파일(.java)을 이해하지 못하기 때문에 가상 머신이 인식하기 쉬운 코드로 변환합니다.
    • 이 과정에서 .class 확장자를 가지는 바이트 코드가 만들어집니다.
  2. java로 실행
    • JVM이 컴파일된 코드(바이트 코드)를 실행합니다.

전체과정은 아래 그림과 같습니다.

https://user-images.githubusercontent.com/48986787/101020230-844c2b80-35b1-11eb-8626-c573e30167ae.png


JIT 컴파일러란 무엇이며 어떻게 동작하는지

실행 엔진

실행 엔진은 클래스 로더를 통해 런타임 데이터 영역에 배치된 바이트 코드를 명령어 단위로 읽어서 실행합니다.

즉, 바이트코드를 기계어로 번역하여 실행합니다. 이때 사용하는 방식이 2가지인데 이를, 인터프리터JIT 컴파일러라 부릅니다.

인터프리터

  • 바이트 코드 명령어를 하나씩 읽어서 해석하고 실행. 하나하나의 해석은 빠르지만 전체적인 실행 속도는 느리다는 단점을 가짐. JVM안에서 바이트코드는 기본적으로 인터프리터 방식으로 동작.

JIT(Just In Time) 컴파일러

https://user-images.githubusercontent.com/48986787/101051104-72c24e00-35c8-11eb-87f6-6fda70476740.png

  • 인터프리터의 단점을 보완하기 위해 도입된 방식으로 바이트 코드 전체를 컴파일하여 네이티브 코드로 변경하고 이후에는 해당 메서드를 더 이상 인터프리팅 하지 않고 네이티브 코드로 직접 실행하는 방식. 하나씩 인터프리팅하여 실행하는것이 아니라 바이트 코드 전체가 컴파일된 네이티브 코드를 실행하는 것이기 때문에 전체적인 실행 속도는 인터프리팅 방식보다 빠름.
  • 네이티브 코드는 캐시에 보관하기 때문에 한 번 컴파일된 코드는 캐시에서 바로 꺼내어 실행하기 때문에 빠르게 수행됩니다. 하지만 JIT 컴파일러가 컴파일하는 과정은 바이트 코드를 하나씩 인터프리팅 하는 것보다 훨씬 오래 걸리기 때문에 JIT 컴파일러를 사용하는 JVM은 내부적으로 해당 메서드가 얼마나 자주 호출되고 실행되는지 체크하고, 일정 기준을 넘었을 때에만 JIT 컴파일러를 통해 컴파일하여 네이티브 코드를 생성합니다.
  • 즉, JIT 컴파일러는 같은 코드를 매번 해석하지 않고 실행할 때 컴파일을 하면서 해당 코드를 캐싱해버립니다. 이후엔, 바뀐 부분만 컴파일 하고 나머지는 캐싱된 코드를 사용 합니다. 결론적으로, 인터프리터의 속도를 개선할 수 있습니다.

JVM 구성 요소

이또한 이전에 정리했던 글(JVM 구조)에서 확인할 수 있습니다.

간단히 말하자면 크게

  1. 클래스 로더 시스템 (Class Loader)
  2. 메모리(Runtime Data Area)
  3. 실행엔진(Execution Engine)
  4. 네이티브 메소드 인터페이스 (JNI)

로 이루어져 있습니다.


JDK와 JRE의 차이

JRE (Java Runtime Environment): JVM + 라이브러리

  • 자바 실행 환경
  • 자바 애플리케이션을 실행할 수 있도록 구성된 배포판.
  • JVM과 핵심 라이브러리 및 자바 런타임 환경에서 사용하는 프로퍼티 세팅이나 리소스파일을 가지고 있다.
  • 개발 관련 도구는 포함하지 않는다. (그건 JDK에서 제공)
  • 자바 11부터는 제공하지않고, 이것만 써서하는건 흔치않다.

JDK (Java Development Kit): JRE + 개발 툴

  • 자바 개발 도구
  • JRE + 개발에 필요할 툴
  • 소스 코드를 작성할 때 사용하는 자바 언어는 플랫폼에 독립적(JVM은 플랫폼에 종속적).
  • 오라클은 자바 11부터는 JDK만 제공하며 JRE를 따로 제공하지 않는다.
  • Write Once Run Anywhere
profile
경험의 연장선

0개의 댓글