Invincible-Backend-Study / java-basic

자바 기본서를 통한 기초 다지기 스터디!
0 stars 6 forks source link

[이호석] CH02 자바 컴파일 과정에 대한 간단한 이해 + 주석은 성능에 영향을 끼치지 않는다 #6

Open HiiWee opened 1 year ago

HiiWee commented 1 year ago

🤔 왜 이슈를 생성했나요?

현식님의 https://github.com/Invincible-Backend-Study/java-basic/issues/1 이슈에서 현식님과 재홍님은 실습을 통해 증명해주셨습니다. 저는 여기에 이론을 곁들이면 더 좋을것 같아 코멘트를 작성하게 됐습니다.

생각보다 글이 길어져서 별도의 이슈로 분리하게 됐습니다! ㅎㅎ


😁 공유하고 싶은 내용

자바 파일에 대한 이해

우리가 작성한 .java 파일은 모두 텍스트 파일입니다. 따라서 일반 텍스트 편집기에서 읽을 수 있습니다.

일반 텍스트 파일과의 차이점으로는 Java 코드로 이루어져 있고, Java 컴파일러가 읽을 수 있는 확장자라는 부분에서 차이점 있습니다.


자바 컴파일 과정에 대한 이해

그렇다면 일반 텍스트 파일인 .java파일을 어떻게 컴파일 할까요? 자바는 소스 코드 컴파일 과정이 스펙에 명시되어 있지 않지만, C언어의 컴파일 과정을 예시로 보면 다음과 같습니다. 전처리 -> 컴파일 -> 어셈블리 -> 링킹 이때 전처리 과정에서 주석제거, 매크로 인라인화, include(import) 파일 인라인화가 이루어집니다.

자바에서는 다음과 같이 컴파일 과정을 명시합니다.

여기서 컴파일러에서 진행되는 세부 단계를 살펴보면 다음과 같습니다.

  1. Lexical Analysis(어휘 분석) 소스 코드에서 문자 단위로 읽어 어휘소를 식별 하고 토큰 스트림을 생성합니다. public, class, main, for과 같은 키워드 분석, 1L, 2.3f같은 리터럴 분석, 식별자, 연산자, 구분 문자 등을 분석하여 토큰 스트림을 생성합니다.

  2. Syntax Analysis(구문 분석) 어휘 분석 결과로 생성한 토큰 스트림을 이용해 자바 언어의 스펙 형식에 맞는지 문법을 검사합니다. 문법이 틀리다면 컴파일 에러가 발생하고 맞다면 Parse Tree를 생성합니다. (트리 자료구조)

  3. Symantic Analysis(의미 분석) 이곳에서는 타입 검사, 자동 타입 변환 등이 수행됩니다. (int a = "a" -> 컴파일 오류 발생) 이후 파스 트리에 정보들이 추가 됩니다.

  4. Intermediate Code Generation(중간 코드 생성) 윗 단계를 통과하면 파스 트리를 바탕으로 기게어로 변환하기 좋은 형태인 중간 언어로된 중간 코드를 생성합니다. 자바의 바이트 코드가 중간 코드에 해당됩니다. 자바, 클로저, 스칼라, 코틀린과 같은 언어와 같이 JVM에서 실행되는 언어들은 모두 이 바이트 코드(중간 코드)를 생성합니다.

  5. Code Optimization(중간 코드 최적화) 중간 코드가 더 효율적인 기계어로 변환되도록 최적화 하는 과정이 수행됩니다.


이렇게 컴파일이 완료되면 .class 라는 바이트 코드가 생성됩니다. 따라서 컴파일 과정에서 주석이 굉장히 많아질 경우 위의 분석 단계에서 많은 시간을 소요하게 됩니다.

또한 결과물인 바이트 코드에서는 개발자가 작성한 주석이 남아있지 않지만, 그 흔적을 찾아볼 수 있습니다!

자바는 전처리 과정에서 주석이 있던 행 자체가 제거되지 않습니다. 따라서 바이트 코드 내용중에서 자바 소스 코드의 행 번호와 바이트코드 명령어의 위치를 매핑하는 부분에서 주석이 있던 행이 제거되지 않은 상태의 행 번호가 표시됩니다.

Main 클래스가 존재할때 빌드를 하게 되면 image

다음과 같은 바이트코드를 살펴볼 수 있습니다. (IntelliJ) image

위 바이트 코드에 하이라이팅 된 부분을 보면 Line 8에 "Hello World!"를 출력한다는 정보를 살펴볼 수 있습니다. 이는 주석이 제거되지 않은 상태의 행 번호와 일치합니다.

이를 통해 자바 컴파일 과정에서 주석의 양이 굉장히 많아질 경우 컴파일 성능에 차이가 있음을 유추할 수 있습니다.


빌드와 컴파일

개인적으로 자료를 찾아보면서 두 용어에 대한 혼동이 있었습니다.

위 과정을 살펴봤을때 컴파일 과정은 빌드의 부분집합으로 포함됨을 알 수 있습니다.

따라서 주석이 많아지면 컴파일시 속도가 느려진다 -> OK 혹은 주석이 많아지면 빌드 속도가 느려진다 -> OK 모두 맞는 말이라고 생각합니다!


📌 참고자료

What is .java file? Building vs. Compiling (Java) - stackoverflow Back to the Essence - Java 컴파일에서 실행까지 - (1) - HomoEfficio

chhs2131 commented 1 year ago

좋은 내용 잘봤습니다! 자바는 컴파일시 내부적으로 보다 많은 단계를 거치는걸 처음 알았어요 :)

한가지 궁금한점은 말씀해주신 주석의 흔적입니다. 기존 .java 파일에서의 주석의 라인넘버와 .class 에 기재된 LINENUMBER 가 일치한다고 말씀해주셨는데요! 제 50만줄 주석 파일을 확인해보니 LINENUMBER가 한 자리 수로 되어있었습니다.

이는 위 말씀해주신 컴파일 단계 중 5. Code Optimization(중간 코드 최적화) 에 의해 걸러진게 아닐까? 하는 생각이 들었어요 😀 (아마 너무 무식하게 크니깐 알아서 줄여준게 아닐까요?! ㅎㅎ)

사진 좌측.주석이없는파일, 우측.주석50만줄 image

HiiWee commented 1 year ago

오.. 처음알게된 사실입니다! 저도 개인적으로 어느정도 부터 LINENUMBER의 표시가 사라지는지 확인해봤는데 10만줄 정도 넘어가면 사라지는것을 확인했습니다 ㅎㅎ 3만줄까지는 정상적으로 나타나네요! image

중간코드 최적화 부분에서 너무 많은 주석이 존재하면 LINENUMBER 표시자체를 하지 않는것 같습니다! 정확한 JVM 스펙을 찾아보고 싶었지만, 찾아보기가 어려운것 같습니다..

확실히 컴파일 타임에서 여러가지 분석 및 최적화로 주석을 지워주면서 실제 런타임에서 주석 유무에 따른 시간차이가 없을 수 밖에 없겠다는 생각이 듭니다!