본문 바로가기
스터디/[white-ship] 자바 스터디(study hale)

1주차 과제: JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.

by doyoungKim 2021. 1. 2.

목표

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

학습할 것

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

 

1. JVM이란 무엇인가

프로그래밍 언어는 OS에 따른 제약을 받을 수 있다. 쉽게 예를 들면, IOS - Swift 를 들 수 있다.

하지만 JVM(Java Virtual Machine) 의 대표적인 특징은 java 확장자 파일, 프로그래밍 언어를 OS에 제약에서 해방 시켜 준다.

2. 컴파일 하는 방법

먼저, 컴파일이란 OS 가 읽을 수 있는 바이너리(binary) 코드로 만드는 것이다.

javac (Java Compiler)를 통해서 Java 언어를 즉 java라는 확장자 파일을 class 라는 확장자로 변환 한다. class 라는 파일은 바이트 코드(java byte code) 로 구성 되어 있다.

예시로 자바 파일을 만들어 보았다.

public class HelloWorld {
    public static void main(String[] args) {
        String greeting = "HelloWorld";
        System.out.println(greeting);
    }
}

javac 명령어를 이용해서 컴파일을 해보자.

javac ./HelloWorld.java

 

결과화면

.class 파일이 생긴 것을 볼 수 있다. 이는 바이트 코드로 되어있고 그 안을 javap 명령어를 통해 확인해보자.

javap -c HelloWorld //HelloWorld의 역어셈블된 코드 출력

 

다른 옵션은 다음과 같습니다.

?

도움말을 출력

-l

클래스 내 모든 라인 번호와 지역변수 테이블을 출력합니다

-public

클래스 내 public 접근 지정자로 선언된 클래스와 멤버를 출력합니다.

-protected

클래스 내 protected 접근 지정자로 선언된 클래스와 멤버를 출력합니다.

-private

클래스 내 private 접근 지정자로 선언된 클래스와 멤버를 출력합니다.

-p

모든 클래스와 멤버를 출력합니다.

-c

역어셈블된 코드를 출력합니다.

-verbose

스택 사이즈를 출력합니다

사실 바이트 코드를 본다고 좋은 면은 별로 없다고 한다.

최적화 작업은 JIT 부분에서 다 하기 때문이다. 하지만 우리는 자바가 어떻게 작동하는지 알아야할 필요가 있기 때문에 바이트 코드를 읽는 법을 알아야 하는 책임의식이 느껴진다.

자세한 내용은 밑의 링크를 참고해서 읽어보자.

 

이상현 IN 베를린 :: 자바 바이트코드 소개

자바 바이트코드 소개 Understanding bytecode and what bytecode is likely to be generated by a Java compiler helps the Java programmer in the same way that knowledge of assembly helps the C or C++ programmer. - IBM developerWorks journal 개발을

iamsang.com

 

3. 실행하는 방법

JVM은 바이트 코드를 OS가 실행 할 수 있도록 바이너리 코드(binary code)로 해석 한다.

여기서 process 는 추후 6. JVM 구성 요소를 보자

4. 바이트코드란 무엇인가

바이너리 코드 VS 바이트 코드

이름이 비슷하지만 바이트 코드는 JVM이 읽는 코드, 바이너리 코드는 OS가 읽는 코드이다.

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

Java 를 실행 시키려면 자바 컴파일러를 통해 class 확장자로 바꿔야 하고 JVM을 통해 바이너리 코드로 변경해야한다. 이 처럼 과정이 길기 때문에 과정을 최소화 하는 컴파일러이다.

인터프리터를 통해 전체 내용을 한 줄씩 읽어 해석을 하고 나면, JIT 컴파일러는 일정기간 동안 전체를 먼저 캐싱하고 나서 추 후에 변경된 부분이 있다면 해당 내용만 위의 과정을 걸치고 나머지 부분은 캐싱 되어 있는 내용을 가져다가 바로 실행 한다.

따라서 매번 인터프리터 방식으로 수행하는 것 보다 훨씬 효율 적이다.

6. JVM 구성 요소

JVM

 

Class Loader

바이트 코드를 읽어오며 OS 로부터 메모리를 할당 받아 Runtime Data Area에 적재 시킵니다

1. 로딩:

.class 를 읽어서 바이너리 코드로 만들고 이를 "메소드" 영역에 저장한다.
저장하는 데이터는 다음과 같다.

    - Fully-Quailified Class Name
        - 클래스 로더, 클래스 패키지 경로, 패키지 이름, 클래스 이름을 모두 포함한 값
        - ex. java.lang.Character$Subset
    - Class | Interface | Enum 를 구분하여 저장
    - 메소드와 변수

로딩이 끝나면 해당 클래스 타입의 객체를 생성하여 "힙" 영역에 저장

BootStrap -> Extension -> Application Loader 순으로, 앞의 Loader 가 로딩할 수 없으면 그 다음 Loader 가 읽어내는식

2. 링크:

코드 내부의 레퍼런스를 연결함.

- Verify: .class 파일 형식이 유효한지 검사한다.
- Perpare : static변수와 기본 값에 필요한 메모리를 준비한다.
- Resolve (Optional)
    - 심볼릭 메모리 레퍼런스를 실제 메모리 레퍼런스로 교체한다.
    
- Optional인 이유는, 이 때 교체(binding) 될 수 있고, 이후 사용이 일어날 때에 동적으로 교체 될 수도 있다.

3. 초기화:

클래스에 있는 static 값들을 초기화 함.

 

 

Runtime Data Area

운영제체로 부터 할당 받은 메모리 공간

1. Method Area : 클래스 수준의 정보를 저장

  • 클래스 이름, 부모 클래스 이름, 메소드, 변수 등
  • static 변수, 일반 변수 등)

2. Heep Area : 객체(인스턴스) 수준의 정보를 저장

3. Stack Area : 인스턴스 및 지역 변수의 참조 주소 들을 저장

쓰레드마다 런타임 스택을 만들고, 스택 프레임(메소드 call)을 쌓는다.

  • 에러 났을 때, 에러 메시지를 보면, 런타임 스택에 메시지가 쌓여있는 걸 확인 할 수있다.

4. PC register :  쓰레드마다 가지고 있는 Program Counter

현재 실행할 부분을 가르키고 있다.

5. Native Method Stack : 네이티브(native) 메소드 호출할 때 사용하는 별도의 스택

네이티브 메소드는 java 가 아닌 c와 같은 언어(low-level)로 구현된 메소드이다.

  • 대표적인 예시로 Thread.currentThread() 이다. (public static native Thread currentThread()로 선언되어있음)

 

 

​Execution Engine

1. 인터프리터

바이트 코드를 한줄 한줄 읽어서 바이너리 코드로 변환

2. JIT(Just In Time) 컴파일러

바이트 코드에서 반복되는 코드 부분은 JIT 컴파일러가 미리 네이티브 코드로 변환 시켜놓음.
반복되는 코드가 읽힐 순서가 왔을 때, 인터프리터로 읽지 않고 바로 바이너리 코드를 사용한다.
인터프리터 읽을 때의 속도 효율성을 JIT 컴파일러가 보완하는 형태

3. GC (Garbage Collector)

더 이상 참조되지 않는 객체들을 모아서 메모리 정리를 한다.
경우에 따라 성능 효율을 위해 커스텀마이징을 해야한다.

7. JDK와 JRE의 차이

JRE (Java Runtime Environment):

실행 환경에서의 자바 즉 배포 할 때 사용 하는 형태 이다.

- 구성: JVM + 핵심 라이브러리

JDK (Java Development Kit)

개발 환경에서의 자바

- 구성: JRE + 개발 툴(java c)
* 자바 11부터는 JDK 단위로 배포된다.

8. javac 명령어 옵션

 

javac 명령어의 옵션 정리 | 커피한잔의 여유와 코딩

javac – 자바컴파일러로써, 자바코드를 작성한 소스파일(.java)을 자바 가상머신이 인식할수 있는 바이트 코드(.class) 타입으로 변환시켜주는 명령어 입니다. 사용법: javac 예를들어, Hello.java, Greetin

sjava.net

출처:

728x90

댓글