Java의 컴파일

wnajsldkf·2022년 10월 16일
0

Java

목록 보기
12/19
post-thumbnail

자바 프로그램 실행 과정은 크게 컴파일 환경과 런타임 환경으로 구분된다.
이번 시간에는 컴파일 환경에 대해 알아보겠다.
컴파일 환경에서는 Java Compiler에 의해 Java Code(.java)Byte Code(.class)로 변환된다.

이번 시간에는 Java Compiler가 어떻가 Java Code(.java)Byte Code(.class)로 변환하는지 알아보겠다.

컴파일 언어는 무엇일까?

한번쯤 'Python으로 하면 실행 속도가 느리다' 라는 말을 들어본적이 있을 것이다. 왜 그럴까?
Python은 인터프리어 언어이기 때문이다.

인터프리터 언어

ex. R, Python, Ruby
인터프리터 언어는 원시코드(개발자가 작성한 코드)를 기계언어로 변환하지 않고 한줄한줄 해석해 바로 명령어를 실행한다.
우리가 처음보는 새로운 언어로 적힌 문서를 만날 때 번역이 필요한 것처럼 컴퓨터도 마찬가지이다.
우리가 통역이 없으면 하나하나 사전을 찾아가며 의미를 찾는 것처럼 개발자가 작성한 코드가 기계언어로 변환되지 않았기 때문에 컴퓨터는 코드를 이해하는데 시간이 걸린다.

  • 코드 변경시 빌드 과정 없이 바로 실행 가능하다.
  • Runtime 상황에서 한줄식 실시간으로 읽어서 실행하기 때문에 컴파일 언어보다 속도가 느리다.

컴파일 언어

ex. C, C++
반면에 컴파일 언어는 원시코드(개발자가 작성한 코드)를 통째로 기계언어로 변환한 뒤, 기계(JVM 같은 가상 머신)에 넣어 기계어 코드를 실행한다. 원시코드를 기계언어로 변환하는 것은 우리에게 새로운 언어로 작성된 문서를 건내기 전에 미리 번역하여 건내주는 것이다. 이런 특징을 갖고 있는 컴파일 언어를 사용할 때 원시 코드를 기계언어로 바꾸는 역할이 바로 컴파일러이다.

  • 소스코드를 기계어로 번역할 때 시간이 소요된다.
  • 런타임 상황에서 기계어로 모든 소스코드가 변환되어 있어 빠르게 실행할 수 있다.

Java 코드 직접 실행하기

다음과 같이 간단한 코드를 작성했다.

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

java파일을 bytecode로 컴파일하기
javac 명령어를 통해 java 파일을 bytecode로 컴파일한다.

ls 명령어로 확인해보면 .class 파일이 생긴 것을 확인할 수 있다.
이제 Java의 Runtime Environment(JRE)에서 실행되기에 최적화된 형태가 된 것이다.

생성된 Test.class 파일을 확인해보면, 컴퓨터가 해석할 수 있는 bytecode로 변환된 것을 확인할 수 있다.

만약 잘못된 코드를 작성했다면 컴파일 과정에서 오류를 확인하여 알려준다.
다음 그림은 임의로 ; 를 작성하지 않아 오류를 발생한 상황이다. 컴파일 과정에서 오류를 확인했기 때문에 Test.class 파일도 생성되지 않을 것이다.

+) IntelliJ에서는 컴파일된 코드의 내부를 볼 수 있는 디컴파일러를 제공한다.

bytecode로 변환된 파일을 실행하기
bytecode로 변환된 파일은 JVM(Java Virtual Machine)을 위한 코드이다. JVM 위에서 코드가 동작하기 때문에 자바를 실행할 수 있는 모든 장치에서 코드가 실행된다.
java 명령어를 통해 컴파일된 파일을 실행할 수 있다.

이러한 컴파일 가정을 통해 자바 컴파일러의 두가지 큰 특징을 이해할 수 있다.

  1. .java 파일에 오류가 있는지 검사한다.
    • 오류가 있다면 컴파일되지 않는다.
  2. JVM을 위한 코드(bytecode)를 생성한다.
    • JVM을 통해 플랫폼에 상관없이 독립적으로 실행 가능한 환경을 제공한다.
    • 이 특징으로 작성된 코드로 하드웨어, OS에 상관없이 어디서든 실행할 수 있다.

Bytecode로 변환된 파일 실행하기

Bytecode로 변환된 파일은 런타임 환경에서 JVM에 의해 어떻게 실행되는 것일까?

  • ClassLoader
  • JVM Memory
  • Execution engine
  • Native Method interface, Native method library

ClassLoader

ClassLoader는 Runtime 시, 흩어져 있는 바이트 코드로 변환된 .class 확장자를 가진 클래스 파일을 찾아 JVM의 메모리에 올려주는 역할을 한다. ClassLoader는 Loading 역할 뿐만 아니라 Linking, Initializing 역할도 한다.

자세한 부분은 Class Loader에 대해 알아보자를 참고하시기 바란다.

JVM Memory(Runtime Data Area)

ClassLoader에 의해 JVM에 탑재된 클래스 파일들이 차지하는 영역을 말한다. JVM Memory에는 JVM 당 하나만 생성되는 Method Area, Heap와 각 Thread 별로 생성되는 Java Stack, PC Register, Native Method Stack이 존재한다.

자세한 부분은 자바 메모리 구조를 참고하시기 바란다.

Execution Engine

Execution Engine은 할당받은 Class Loader를 이용해 메모리(Runtime Data Area)에 할당된 bytecode를 실행시킨다. Execution Engine은 기계가 읽을 수 있는 형태로 ByteCode를 변환해야 한다.

Execution Engine에서는 크게 3가지 구성요소가 실행된다.

  • Interpreter
  • JIT(Just In Time) Compiler
  • Garbage collector

Interpreter

인터프리터는 Bytecode를 기계가 이해할 수 있는 코드로 바꾼다. 이 덕분에 각 플랫폼에 맞는 인터프리터가 바이트 코드를 실행하여 플랫폼에 독립적이다라는 특징을 갖게 된다. 인터프리터는 Runtime 중에 바이트 코드를 한 라인씩 읽고 실행하기 때문에, 속도 문제가 발생한다.

JIT(Just In Time) Compiler

인터프리터는 메서드가 호출될 때마다 해석이 필요하기 때문에 속도가 느려질 수 있다. 이를 해결하기 위해 JIT 컴파일러가 사용된다.

JIT 컴파일러는 메서드가 호출되는 빈도를 파악하고 자주 사용되는 코드(ex. 반복문)에 대해 JVM이 기계 코드로 컴파일해둔다. 이미 컴파일되었기 때문에 Interpreter가 바로 사용할 수 있다.

Garbage Collector

RuntimeDataArea는 힙 영역에서 더 이상 참조되지 않는 객체를 모아서 정리한다.

Summary

  • Java 프로그램의 실행 과정은 컴파일 환경과 런타임 환경으로 구분된다.
  • 컴파일 환경에서는 Java 코드를 바이트 코드로 변환시킨다.
  • 런타임 환경에서는 바이트 코드로 JVM에서 실행시킨다.
    • ClassLoader는 Runtime 시, 흩어져 있는 바이트 코드로 변환된.class 확장자를 가진 클래스 파일을 JVM 메모리에 올려준다.
    • Execution Engine은 Interpreter, JIT Compiler, Garbage Colector로 구성되어 바이트 코드를 기계어로 변환한다.

Reference

profile
https://mywnajsldkf.tistory.com -> 이사 중

0개의 댓글