본문 바로가기
Backend/Java

Java Compile

by DooDuZ 2024. 6. 7.

코딩을 처음 시작했을 때 Java 컴파일에 대한 내 인상은 아래와 같았다.

  1. Java 소스코드는 자바 컴파일러에 의해 컴파일된다.
  2. 컴파일은 파일 저장 시 즉시 일어난다.

그리고 놀랍게도 이 생각은 최근까지 이어져왔는데, 이번에 기술 면접 대비 공부를 하면서 Java의 compile과정을 설명하라는 질문을 마주치게 됐다. 저장하면 컴파일러가 하는 게 컴파일이다. 이 말이 내가 사용하는 환경에서 틀린 말은 아니지만 면접에서 저렇게 대답할 순 없는 노릇이었다. 그러다 작년 원티드 프리온보딩에서 진행했던 Java GC 세션이 떠올랐다. 강사님이 굉장히 디테일한 부분까지 설명해 주셔서 여태 참여한 챌린지 중 가장 만족도가 높은 세션이었는데, 강의 초반 Java와 JVM에 대해 논하면서 컴파일에 대한 얘기를 했던 기억이 떠올랐다. 강의 자료를 열어보니 역시나, 저장한다고 끝이 아니라 컴파일에도 나름의 과정이 있었다. 그래서 해당 키워드로 열심히 알아보기 시작했다. 익숙한 개념이 아니어서 정확할진 모르겠지만 최대한 이해한 대로 정리해보려 한다.

 

 

컴파일러에 대해 알아볼 때 가장 많이 보이는 다섯 키워드를 묶어봤다. 위의 모든 키워드가 Java Compiler에도 적용되어 있는지는 모르겠지만 왼쪽부터 차례대로 무엇인지 알아보자. 일부 기능을 직접 알아보기 위해, IDE를 사용하지 않고 터미널을 사용하여 소스코드를 직접 컴파일한다.

 

Lexical Analysis(어휘 분석)

작성된 소스코드에서 리터럴, 변수, 예약어, 연산자 등을 수집하는데 이를 토큰이라고 한다. 아래 코드에 public, class, static 같은 예약어, 0 변수나 Hello World 리터럴, =같은 연산자도 보인다. Lexical Analysis 단계에선 이런 것들을 단위로 쪼개서 수집한다.

public class Hello {
    public static void main(String[] args) {
        int number = 0;
        String hello = "Hello World!";
        System.out.println(hello);        
    }
}

 

Syntax Analysis(구문 분석)

위키피디아 AST 이미지

 

수집한 토큰으로 추상 구문 트리(Abstract Syntax Tree, AST)를 생성하여 검사한다. 흔히 말하는 문법 에러를 잡아내는 단계다. 테스트를 위해 2번 라인의 public과 3번 라인의 ;을 의도적으로 수정해서 컴파일한다. 그렇게 하면 수정된 두 곳에서 컴파일 에러가 발생한다. 

 

Semantic Analysis(의미 분석)

구문 분석이 문법 오류를 잡아냈다면, 의미 분석은 코드 실행에 대한 검사를 진행한다. 변수가 올바른 타입에 대입되고 있는지, 메서드 호출엔 문제가 없는지 검사하는 단계다. 사실 위의 컴파일러 이미지 안에는 이 부분이 없는데, 까먹어서 추가 못한 거다. 머쓱하니 실패 테스트로 넘어가겠다.

int에 문자열이, String에 long타입이 대입되는 부분과 print 함수의 잘못된 호출에서 에러가 발생한다. 만약 구문 분석을 통과하지 못한다면 이런 문제들이 아무리 많아도 의미 분석까지 진행되지 않고 문법으로 인한 컴파일 에러만 발생한다.

 

Intermediate Code Generating(중간 코드 생성)

분석이 끝났다면 중간 코드로의 변환이 일어난다. Java의 경우 바이트 코드로의 변환이 일어나는데, 다른 컴파일 언어들이 중간 코드를 거쳐서 타겟 코드로 진행되는 반면 Java는 바이트 코드를 JVM의 Execution Engine이 기계어로 변환해 주기 때문에 바이트 코드가 목적 코드가 된다.

출처 : https://www.baeldung.com/java-compiled-interpreted

 

Code Optimizer

만약 중간 코드 생성이 완료 됐다면, 컴파일러는 목적 코드로 변환 전에 코드 최적화를 실행한다. 여기서도 Java와 다른 Compile 언어들의 차이가 나타나는 것 같은데, 꽤 오랜 시간 Java compiler의 optimize 키워드를 찾아봤는데도 별다른 정보를 찾지 못했다. 이는 바이트코드에서 기계어로의 완전한 컴파일을 JVM이 하기 때문으로 보이는데, JVM Interpreter가 바이트코드를 한 줄씩 컴파일하고 실행할 동안 프로파일러가 이를 감시하고, 자주 호출되는 메서드는 JIT 컴파일러가 한 번에 컴파일하여 사용한다.

 

Target Code Generating

위 과정이 모두 끝났다면 최종 목적 코드로의 컴파일이 이루어지게 된다. 위에서 언급했다시피 Java는 컴파일러가 기계어까지 변환하지 않고, JVM이 이를 수행하므로 Java Compiler의 역할은 사실상 중간 코드 생성 단계에서 마무리된다고 봐도 좋을 것 같다.

 

 

보다 상세하고 정확한 포스팅을 원한다면 아래 링크를 참고하는 걸 추천한다. 이 포스트를 작성하는데 많은 참고가 됐다. 글 말미를 빌어 작성자님께 감사를 전한다.

 

[Java-1] 컴파일 방법 & 과정

JAVA를 사용하기에 앞서, JVM은 무엇이며 자바 코드는 어떻게 실행 되는지에 대해서 알아보는 과정을 가져보도록 합시다. 이 과정을 알아보기전에 몇가지에 대해서 짚고 넘어가도록 하자 1. 시작 J

catch-me-java.tistory.com

 

'Backend > Java' 카테고리의 다른 글

Virtual Thread  (0) 2024.08.05
Serial GC / Parallel GC / G1 GC  (0) 2024.06.19
JVM Stack / Heap - GC  (0) 2024.06.19