💻 개발/Java

[Java] 자바 컴파일 / 빌드 / 배포 과정

개발새발. 2024. 7. 16. 16:54

☕️ Java 컴파일 / 빌드 / 배포 과정

컴파일, 빌드는 IDE가 알아서 해주고
개발/검증 서버 혹은 운영 서버로의 배포는 SA팀에서 해주니까
JVM이 어떻게 동작하는지 깊게 알려고 하지 않았던 나 ㅋ 반성하고 ㅋ
컴파일/빌드/배포까지 아라보자 ~

 

 

1. 컴파일 (Compile)

  • 소스 코드를 기계가 읽을 수 있는 바이너리 코드로 변경하는 과정. 쉽게 말해 .java 파일을 .class 파일로 변환하는 과정
  • 소스 코드 분석 및 문법 오류 분석이 이루어진다.
  • 이렇게 생성된 자바 바이트 코드(.class)는 클래스 로더에 의해서 JVM내로 로드 되고, 실행엔진에 의해 기계어로 해석되어 메모리 상(Runtime Data Area)에 배치된다.
  • 아래 그림은 컴파일 과정 기준으로 정리한 내용이다. 시스템적인 JVM에 대한 더 자세한 내용이나 도식화 된 이미지는 다른 포스팅에 더 설명이... (더보기)

 

 

 

1.1. 클래스 로더 (Class Loader)

  • 자바는 동적으로 클래스를 읽어오므로, 프로그램이 실행 중인 런타임에서야 모든 코드가 자바 가상 머신과 연결된다.
  • 이렇게 동적으로 클래스를 로딩해주는 역할을 하는 것이 바로 클래스 로더(class loader) 이다.
  • 자바에서 소스를 작성하면 .java파일이 생성되고 .java 소스를 컴파일러가 컴파일하면 .class파일이 생성되는데 클래스 로더는 .class 파일을 묶어서 JVM이 운영체제로부터 할당받은 메모리 영역인 Runtime Data Area로 적재한다.

 

1.2. 실행 엔진 (Execution Engine)

  • 실행엔진에는 Interpreter와 JIT(Just-In-Time) Compiler가 있다.
  • Interpreter는 바이트 코드를 한줄씩 읽기 때문에 실행이 느린 단점이 있었고 이러한 단점을 보완하기 위해 나온 것이 JIT Compiler 이다.   
  • JIT Compiler는 인터프리터 방식으로 실행을 하다가 적절한 시점에 바이트 코드 전체를 컴파일 하고 더 이상 인터프리팅 하지 않고 해당 코드를 직접 실행한다.
  • JIT Compiler에 의해 해석된 코드는 캐시에 보관되기 때문에 한번 컴파일 된 후에는 빠르게 수행되는 장점이 있다. 하지만 인터프리팅 방식보다는 훨씬 오래 걸리므로 한번만 실행하면 되는 코드는 인터프리팅 하는 것이 유리하다.
  • JIT 컴파일러를 사용하면 아래 이미지와 같이 먼저 JIT 컴파일러가 소스 코드 전체를 확인한 후 중복된 부분을 미리 기계어로 번역시켜서 저장해놓는다.
  • 이후 인터프리터 방식으로 번역하다가 중복된 부분을 만나게 되면 이미 변환된 기계어 코드를 재사용한다. 따라서 속도 면에서 훨씬 개선된 모습을 보여준다.

 

 

1.3. 런타임 데이터 영역 (Runtime Data Area)

  • 런타임 데이터 영역은 JVM의 메모리 영역으로 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역이다.

  • 모든 스레드가 공유해서 사용 (GC의 대상)
      - 힙 영역 (Heap Area)
      - 메서드 영역(Method Area)
  • 스레드(Thread) 마다 하나씩 생성
      - 스택 영역(Stack Area)
      - PC 레지스터 (PC Register)
      - 네이티브 메서드 스택(Native Method Stack)

 

✍ 메서드 영역 (Method Area)

  • 클래스 멤버 변수의 이름, 데이터 타입, 접근 제어자 정보와 같은 각종 필드 정보들과 메서드 정보, 데이터 Type 정보, Constant Pool, static변수, final class 등이 생성되는 영역

✍ 힙 영역 (Heap Area)

  • Heap Area는 효율적인 GC를 위해 아래와 같이 크게 3가지 영역으로 나뉜다.

  • Young Generation 영역은 자바 객체가 생성되자마자 저장되고, 생긴 지 얼마 안되는 객체가 저장되는 공간
  • Heap 영역에 객체가 생성되면 최초로 Eden 영역에 할당
  • 이 영역에 데이터가 어느정도 쌓이게 되면 참조정도에 따라 survivor의 빈 공간으로 이동되거나 회수됨
  • Young Generation (Eden+Survivor) 영역이 차게 되면, 참조 정도에 따라 Old영역으로 이동되거나 회수됨
  • 이렇게 Young Generation과 Tenured Generation 에서의 GC를 Minor GC 라고 한다.
  • Old영역에 할당된 메모리가 허용치를 넘게 되면, Old 영역에 있는 모든 객체들을 검사하여 참조되지 않는 객체들을 한꺼번에 삭제하는 GC가 실행됨
  • 시간이 오래 걸리는 작업이고 이 때 GC를 실행하는 쓰레드를 제외한 모든 스레드는 작업을 멈추는데, 이를 'Stop-the-World' 라 한다.
  • 그리고 이렇게 'Stop-the-World'가 발생하고 Old영역의 메모리를 회수하는 GC는 Major GC 이다.

 

✍ 스택 영역 (Stack Area)

  • 지역변수, 파라미터, 리턴 값, 연산에 사용되는 임시 값 등이 생성되는 영역


✍ PC 레지스터 (PC Register)

  • Thread가 생성될 때마다 생성되는 영역으로 프로그램 카운터, 즉 현재 스레드가 실행되는 부분의 주소와 명령을 저장하고 있는 영역

 

✍ 네이티브 메서드 스택 (Native Method Stack)

  • 자바 이외의 언어(C, C++, 어셈블리 등)로 작성된 네이티브 코드를 실행할 때 사용되는 메모리 영역으로 일반적인 C 스택을 사용한다.
  • 보통 C/C++ 등의 코드를 수행하기 위한 스택을 말하며 (JNI) 자바 컴파일러에 의해 변환된 자바 바이트 코드를 읽고 해석하는 역할을 하는 것이 자바 인터프리터(interpreter)

 

 

2. 빌드 (Build)

  • 소스 코드를 실행 가능한 독립적인 소프트웨어 산출물로 만드는 과정
  • 빌드의 산출물

👉 JAR (Java Archive) : 자바에서 사용되는 압축 양식, 클래스 + 리소스파일로 구성

👉 WAR (Web Archive) : 웹 어플리케이션을 압축하고 배포하는데 사용되는 파일 형태. JAR에 비해 자바 서블릿, XML 파일, 정적 파일 등 필요한 자원이 더 많다.

 

 



3. 배포 (Deployment)

  • 사용자가 사용할 수 있도록 소프트웨어를 제공하는 과정

 

3.1. CI (Continuous Integration)

  • 지속적 통합이라고도 하며, 코드 변경사항마다 빌드, 테스트까지 자동으로 진행하고 결과물을 보고 받고 리포지토리에 자동 통합되는 프로세스
  • CI가 필요한 이유
      - 협업 시 마지막에 프로젝트를 합칠 시에 Integration Hell이 발생
      - 협업 시 중간마다 프로젝트를 합칠 시에 지속적으로 시간 소모
  • CI 특징
      - 클래스, 전체 애플리케이션에서의 테스트를 수월하게 수행
      - 신규 코드와 기존 코드의 충돌을 빠르게 수정 가능
      - 협업 시에 애플리케이션이 최신 상태로 유지됨을 믿을 수 있음


3.2. CD (Continuous Deployment)

  • 개발자들이 코드에 변경 사항을 줄 경우 파이프 라인을 통해 이동하여 프로덕션 단계까지 자동으로 배포하는 프로세스
  • 리포지토리까지만 가고 수동으로 배포하는 프로세스의 경우에는 Continuous Delivery가 구축되었다고 표현한다.

  • CI & CD에 운영, 모니터링 그리고 다음 기능 기획에 반영하게 하려는 것을 구현한 것이 DevOps이다.
    - DevOps: Development와 Operation을 결합한 단어로 개발과 운영이 연계하여 협력하여 서비스를 이끌어 나가는 방법론