본문 바로가기
야미스터디/Java

[Java] Java 컴파일 과정 📌

by 의정부핵꿀밤 2022. 9. 4.
728x90

기존 언어들의 문제점

  • 기존 언어들은 사용자의 프로그램 실행 환경에 맞춰서 다양한 버젼의 프로그램 개발이 필요로 했다
  • 또한 Windows에서 컴파일하여 생성한 실행파일은 오직 Windows머신에서만 실행이 가능하다
  • 즉, OS 별로 실행 파일이 따로 존재해야 한다는 것이다

 

 

 

Java의 등장

  • 자바는 JVM(Java Virtual Machine) 덕분에 OS에 독립적인 특징을 가지고 있다
  • Java는 Virtual Machine(가상머신)이라는 개념의 프로그램을 통해 Java프로그램을 실행한다
  • 개발자들은 가상머신에 맞춰 개발을 진행하고, 사용자는 본인의 실행환경에 맞는 가상머신 프로그램만 구비하면 프로그램을 어디서든 똑같이 실행할 수 있는 것이다
  • 따라서 어떠한 OS든 Java가 설치되어 있다면 JVM에 의해 .java 코드가 기계어로 해석될 수 있다
  • Python과 같은 인터프리터 언어는 별도의 컴파일 과정이 없이 소스 코드를 한줄씩 읽어가며 실행을 하지만, Java의 경우 컴퓨터가 읽기 전에 컴파일 과정을 거친 후 컴퓨터가 이해를 하는 방식이다

 

 

 

 

Java 언어의 특징

1. 운영체제가 독립적이다

  • 일종의 애뮬레이터인 JVM을 통해서 가능한 특징이다
  • 자바 응용 프로그램은 운영체제나 하드웨어가 아닌 JVM 하고만 통신하고, JVM이 자바 응용 프로그램으로부터 전달 받은 명령을 해당 운영체제가 이해할 수 있도록 변환하여 전달한다

 

2. 객체지향 언어이다

  • 상속, 캡슐화, 다형성이 잘 적용된 순수한 객체지향 언어이다

 

3. 비교적 배우기 쉽다

  • 자바의 연산자와 기본 구문은 C++에서, 객체 지향 관련 구문은 스몰톡(small talk)이라는 객체지향 언어에서 가져왔다
  • 이들 언어의 장점은 갖고 불필요한 부분은 제거하여 단순화함으로써 쉽게 배울 수 있고, 간결하고 이해하기 쉬운 코드를 작성할 수 있다

 

4. 자동 메모리 관리(Garbage Collection)

  • 자바로 작성된 프로그램이 실행되면, 가비지 컬렉터(GC)가 자동적으로 메모리를 관리해주기 때문에 프로그래머는 메모리를 따로 관리하지 않아도 된다

 

5. 네트워크와 분산 처리를 지원한다

  • 인터넷과 대규모 분산환경을 염두에 둔 언어여서 풍부하고 다양한 네트워크 프로그래밍 라이브러리(Java API)를 통해 비교적 짧은 시간에 네트워크 관련 프로그램을 쉽게 개발할 수 있도록 지원한다

 

6. 멀티쓰레드를 지원한다

  • 일반적으로 멀티쓰레드의 지원은 사용되는 운영체제에 따라 구현 방법도 상이하며, 처리 방식도 다르다
  • 그러나 자바에서 개발되는 멀티쓰레드 프로그램은 시스템과는 관계없이 구현 가능하며, 관련된 라이브러리(Java API)가 제공되므로 구현이 쉽다
  • 그리고 여러 쓰레드에 대한 스케줄링(scheduling)을 자바 인터프리터가 담당한다

 

7. 동적 로딩(Dynamic Loading)을 지원한다

  • 보통 자바로 작성된 애플리케이션은 여러 개의 클래스로 구성되어 있다
  • 자바는 동적 로딩을 지원하기 때문에 실행 시에 모든 클래스가 로딩되지 않고 필요한 시점에 클래스를 로딩하여 사용할 수 있다는 장점이 있다
  • 그 외에도 일부 클래스가 변경되어도 전체 애플리케이션을 다시 컴파일하지 않아도 되며, 애플리케이션의 변경사항이 발생해도 비교적 적은 작업만으로도 처리할 수 있는 유연한 애플리케이션을 작성할 수 있다

 

🚨 Java의 단점
- 자바는 속도가 느리다는 대표적인 단점이 있다
- 그러나 최근에는 바이트 코드를 하드웨어의 기계어로 바로 변환해주는 JIT 컴파일러와 Hostpot과 같은 기술의 도입으로 JVM의 기능이 향상됨으로써 속도 문제가 상당히 개선되었다!

 

 

 


Java 컴파일 순서

  1. 개발자가 자바 소스코드(.java)를 작성한다
  2. 자바 컴파일러(Java Compiler)가 자바 소스파일을 컴파일한다
    • Java Compiler는 javac 라는 명령어를 사용하여 .class 파일을 생성한다
    • 이 때 나오는 파일은 자바 *바이트 코드(.class) 파일로, 아직 컴퓨터가 읽을 수는 없지만 자바 가상 머신(JVM)이 이해할 수 있는 코드이다
    • 바이트 코드의 각 명령어는 1바이트 크기의 Opcode와 추가 피연산자로 이루어져 있다
  3. 컴파일 된 바이트 코드(.class)를 JVM의 클래스 로더(Class Loader)에게 전달한다
  4. 클래스 로더는 *동적 로딩(Dynamic Loading)을 통해 필요한 클래스들을 로딩 및 *링크하여 런타임 데이터 영역(Runtime Data area), 즉 JVM의 메모리에 올린다
    • 클래스 로더의 세부 동작
    • 1) 로드 : 클래스 파일을 가져와서 JVM의 메모리에 로드한다
    • 2) 검증 : *자바 언어 명세서(Java Language Specification) 및 JVM 명세에 명시된 대로 구성되어 있는지 준비한다
    • 3) 준비 : 클래스가 필요로 하는 메모리를 할당한다 (필드, 메서드, 인터페이스 등)
    • 4) 분석 : 클래스의 상수 풀 내 모든 *심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경한다
    • 5) 초기화 : 클래스 변수들을 적절한 값으로 초기화한다 (static 필드)
  5. 실행 엔진(Execution Engine)은 JVM 메모리에 올라온 바이트 코드들을 명령어 단위로 하나씩 가져와서 실행한다. 이 때 실행 엔진에서 바이트코드로 변환하는 방식에는 두 가지가 존재한다
    1. 인터프리터
      • 바이트 코드 명령어를 하나씩 읽어서 해석하고 실행한다
      • 하나하나의 실행은 빠르나, 전체적인 실행 속도가 느리다는 단점이 있다
    2. JIT 컴파일러 (Just-In Time Compiler)
      • 인터프리터의 단점을 보완하기 위해 도입된 방식
      • 바이트 코드 전체를 컴파일하여 바이너리 코드로 변경하고 이후에는 해당 메서드를 더이상 인터프리팅 하지 않고, 바이너리 코드로 직접 실행하는 방식이다
      • 하나씩 인터프리팅하여 실행하는 것이 아니라 바이트 코드 전체가 컴파일 된 바이너리 코드를 실행하는 것이기 때문에 전체적인 실행속도는 인터프리팅 방식보다 빠르다
      • 또한 JLT Compiler에 의해 해석된 코드는 캐시에 보관하기 때문에 한 번 컴파일된 후에는 빠르게 수행된다
      • 그러나 인터프리팅 방식보다는 훨씬 오래 걸리므로 한번만 실행하면 되는 코드는 인터프리팅하는 것이 유리하다

컴파일러 vs 인터프리터

 

💡 동적 로딩
Java는 동적 로딩을 지원하기 때문에 실행 시, 모든 클래스가 로딩되지 않고 필요한 시점에 클래스를 로딩하여 사용할 수 있다

 

💡 링크
- 소스코드의 양이 늘어남에 따라 한 파일에 모든 소스코드를 작성할 수 없게 되어 파일들을 분리되어 있는데, 이러한 여러 소스 파일들을 하나로 합치는 것
- 이를 수행하는 프로그램을 링커라고 한다

 

💡 자바 언어 명세서 (Java Language Specification)
- Java 언어의 명세서를 뜻한다
- JLS는 자바 언어를 위한 문법과 정상/비정상적인 규칙들을 보여준다
- 또한 정상적인 프로그램을 실행하기 위한 프로그램 방법들을 보여준다

 

💡 Symbolic Reference
- 실제 메모리 주소 값이 아닌, 참조하는 대상의 이름으로만 지정하고 있는 것이다
- JVM 클래스 로더가 Linking 단계의 분석(Resolving)에서 실제 주소값(다이렉트 레퍼런스)으로 변경한다

 

 

 

 

바이트 코드란?

  • 바이트코드(bytecode)는 JVM이 이해할 수 있는 저수준 언어(반기계어)이다
  • 각 바이트 코드는 1byte 크기의 opcode로 이루어져 있어서 바이트 코드라고 명명되었다
  • .java파일을 컴파일 한 결과로 생성된 .class파일은 바이트코드로 이루어져있다
  • 기존의 언어의 컴파일 결과로 생성되는 오브젝트파일(.obj)과 달리, 바이트코드(.class)는 모든 플랫폼의 JVM에서 실행가능하다
  • Java만 설치되어 있다면 Windows환경에서 생성된 바이트코드는 Mac환경에서도 실행이 가능하다는 것이다
  • 바이트 코드 변환을 통해서 CPU 및 OS에 독립적으로 수행되지만, 기계어 코드를 직접 읽는 것보다 느리다
  • javap -c (.class file) 명령을 통해 바이트 코드를 확인할 수 있다
  • javap 명령은 클래스 파일의 필드, 생성자, 메소드를 출력한다

 

Java로 작성된 소스코드

  • 위의 Java로 작성된 소스코드를 javac컴파일러를 통해 .class파일을 생성한다
  • 생성된 클래스파일은 바이트코드로 작성되어있을 것이다

 

  • 바이트 코드를 열어보면 다음과 같이 변환되어 있다
  • 기존의 어셈블리어와 유사한 모습을 보인다

 

 

 

 

JVM의 역할

  • 바이트 코드로 변환된 코드를 클래스 로더가 JVM의 메모리에 올리면, JVM의 실행엔진인 인터프리터 혹은 JIT 컴파일러가 해석하고 실행하는 역할을 한다
  • 즉, JVM은 다른 프로그램을 실행시키는 것이 목적이다
  • 자바 프로그램이 어느 기기나 운영체제 상에서도 실행될 수 있도록 하고, 프로그램 메모리를 관리하고 최적화한다
  • JVM 에서의 실행 과정
    1. Class Loader를 통해 .class 파일들을 JVM에 올린다
    2. JVM에 있는 .class 파일들을 Execution Engine의 Interpreter와 JLT Compiler를 통해 해석된다
    3. 해석된 바이트 코드는 Runtime Data Area에 배치되어 실질적인 수행이 이루어진다

 

 

 

 

Runtime Data Area

Stack Area

  • 클래스 내의 메소드에서 사용되는 정보들이 저장되는 공간이다
  • 매개변수, 지역변수, 리턴값 등이 저장되며 LIFO(Last In First Out) 방식으로 메소드 실행 시 저장되었다가 실행이 완료되면 제거된다
  • 임시 저장공간으로 생각하면 된다

 

Method(Class, Static) Area

  • 클래스와 메소드, 멤버(클래스, 인스턴스)변수와 상수(final) 정보 등이 저장되는 공간이다

 

Heap Area

  • new 명령어를 통해 생성한 인스턴스와 배열 등의 참조형 변수정보가 저장되는 공간이다
  • 물론 Method Area에 올라온 클래스들만 생성이 가능하다
  • GC(Garbage Collection)의 대상이 된다.

 

PC Register Area

  • 쓰레드마다 하나씩 생성되는 공간
  • JVM 명령의 주소값이 저장되는 공간이다

 

Native Method Stack Area

  • 자바 외 다른 언어의 호출을 위해 할당되는 영역이다
  • 자바에서 C/C++의 메소드를 호출할 때 사용하는 Stack 영역이라고 생각하면 된다

 

 

[메모리 실행 에시]

List의 CRUD 메소드가 있는 ListController가 있다고 가정해보자

  • 클래스와 각 메소드의 정보는 실행엔진에 의해 Method 영역에 올라간다
  • 클래스의 메소드 호출이 발생하면 Method 영역의 정보를 읽어 해당 메소드의 매개변수, 지역변수 리턴값 등이 Stack 영역에 올라가 처리된다
  • 그리고 메소드의 실행이 끝나면 Stack 영역에서 자동으로 제거된다
  • 만약 메소드 내에 new 명령어로 생성한 인스턴스나 배열이 있는 경우, 해당 값은 Heap 영역에 저장되고, Stack 영역에서는 Heap 영역의 값을 참조할 수 있는 메모리 주소 값만 저장된다
    • 배열을 System.out.println(); 을 통해 출력하면 메모리 주소값이 출력되는 이유가 바로 이것이다!

 

 

 

 

Garbage Collector(GC)

  • GC는 Heap 영역의 메모리를 관리한다
  • 자바에서는 메모리를 명시적으로 지정하여 해제하지 않기 때문에 GC의 역할이 중요하다
  • GC는 간단하게 말하자면, reachability라는 개념을 사용하여 참조되지 않는 객체들의 메모리를 회수하는 역할을 한다
  • System.gc(); 로 GC를 실행하는 것처럼 할 수는 있으니, 실제로 바로 실행되는 것은 아니고 실행을 요청하는 것으로 이는 안티패턴이라 사용을 권장하지 않는다

 

  • Heap 영역은 위와 같이 크게 3가지 영역으로 나뉜다
    • Java 8부터는 Permanent 영역 대신에 Java Heap 영역이 아닌 Metaspace로 호출되는 네이티브 영역에 저장된다
  • Minor GC
    • Heap 영역에 객체가 생성되면 최초로 Eden 영역에 할당된다
    • 그리고 이 영역에 데이터가 어느정도 쌓이면 참조 정도에 따라 Servivor1, Servivorr2 중 빈 공간으로 이동되거나 회수된다
    • New Gemeration(Eden+Servivor) 영역이 차게 되면, 또 참조 정도에 따라 Old 영역으로 이동되거나 회수된다
    • 이렇게 New Generation에서의 GC를 Minor GC라고 한다
  • Major GC
    • Old 영역에 할당된 메모리가 허용치를 넘게 되면, Old 영역에 있는 모든 객체들을 검사하여 참조되지 않는 객체들을 한꺼번에 삭제하는 GC가 실행된다
    • 시간이 오래 걸리는 작업이고 이 때 GC를 실행하는 쓰레드를 제외한 모든 쓰레드는 작업을 멈춘다
    • 이를 Stop-the-World 라고 한다
    • Stop-the-World가 발생하고 Old 영역의 메모리를 회수하는 GC를 Major GC라고 한다
    • Major GC가 실행되면 이것이 종료될 때까지 다른 모든 쓰레드가 멈추기 때문에 성능에 영향을 끼칠 수 밖에 없다

 

 

 

 

 

 


참고)

https://gyoogle.dev/blog/computer-language/Java/%EC%BB%B4%ED%8C%8C%EC%9D%BC%20%EA%B3%BC%EC%A0%95.html

 

[Java] 컴파일 과정 | 👨🏻‍💻 Tech Interview

[Java] 컴파일 과정 들어가기전 자바는 OS에 독립적인 특징을 가지고 있다. 그게 가능한 이유는 JVM(Java Vitual Machine) 덕분이다. 그렇다면 JVM(Java Vitual Machine)의 어떠한 기능 때문에, OS에 독립적으로

gyoogle.dev

https://upsw-p.tistory.com/48

 

JAVA Compile 과정, 이 일대기를 들어보자

JAVA Compiler 순서 개발자가 자바 소스코드(.java)를 작성합니다. 자바 컴파일러(Java Compiler)가 자바 소스 파일을 컴파일합니다. 이때 나오는 파일은 자바 바이트코드(.class)파일로 아직 컴퓨터가 읽을

upsw-p.tistory.com

https://velog.io/@woo00oo/%EC%9E%90%EB%B0%94-%EC%BB%B4%ED%8C%8C%EC%9D%BC-%EA%B3%BC%EC%A0%95

 

자바 컴파일 과정

자바는 OS에 독립적인 특징을 가지고 있다. 그게 가능한 이유는 JVM(Java Vitual Machine) 덕분이다. 그렇다면 JVM의 어떠한 기능 때문에, OS에 독립적으로 실행시킬 수 있는지 자바 컴파일 과정을 통해

velog.io

https://93jpark.tistory.com/54

 

[Java] 자바의 컴파일 과정

기존의 문제🥲 이전에 전통적인 언어의 컴파일과 프로그램 실행과정에 대해 알아보았다. 기존의 문제는 사용자의 프로그램 실행 환경에 맞춰서 다양한 버젼의 프로그램 개발이 필요로 했다는

93jpark.tistory.com

https://kwangkyun-world.tistory.com/entry/Java-%EC%BB%B4%ED%8C%8C%EC%9D%BC-%EA%B3%BC%EC%A0%95

 

Java 컴파일 과정

들어가기전 면접에서 자주 나오는 컴파일 과정 공부 ! 자바는 OS에 독립적인 특징을 가지고 있다. 그게 가능한 이유는 JVM(Java Vitual Machine) 덕분이다. 그렇다면 JVM(Java Vitual Machine)의 어떠한 기능 때

kwangkyun-world.tistory.com

https://aljjabaegi.tistory.com/387

 

알기쉽게 정리한 JAVA의 컴파일과정 및 JVM 메모리 구조, JVM GC

알기쉽게 정리한 JAVA의 컴파일과정 및 JVM 메모리 구조, JVM GC 자바 개발자들이 간과 하기 쉬운 JAVA의 메모리 구조에 대해 포스팅 해보려고 합니다. 이와 관련하여 JAVA의 컴파일 과정과 Garbage Collec

aljjabaegi.tistory.com

 

728x90

댓글