본문 바로가기
[ 개발 지식 ]

일반인에게 설명하는 [자바 프로그램의 실행 과정] - 2. 자바 프로그램 실행

by 히앤님 2022. 9. 19.
반응형
SMALL
SMALL

그동안 소스코드를 작성하고 언어를 배우며 써왔지만 실제로 어떻게 동작하는지에 대해서는 등한시한 경향이 있었다. 일반인도 이해할 수 있게, 가장 많이 쓰이는 자바 프로그램이 어떻게 동작하는지에 대해 정리를 해보고자 한다.

지난 글에서 프로그램이 어떻게 실행되는지 살펴보았다. 저번 글을 기반으로 자바 프로그램이 어떻게 되는지 확인해보자.

▼ 지난글을 확인해보고 싶다면?

 

일반인에게 설명하는 [자바 프로그램의 실행 과정] - 1. 프로그램 실행

그동안 소스코드를 작성하고 언어를 배우며 써왔지만 실제로 어떻게 동작하는지에 대해서는 등한시한 경향이 있었다. 일반인도 이해할 수 있게, 가장 많이 쓰이는 자바 프로그램이 어떻게 동작

heannim-world.tistory.com



[ 자바 프로그램은 어떻게 실행될까? ]

코딩 →  컴파일  런타임  실행


출처 : http://www.tcpschool.com/java/java_intro_programming

자바 프로그램의 실행과정은 다음과 같다.

자바(JAVA)는 대표적인 하이브리드 타입(Hybrid Type)언어로, 소스코드 전체를 중간코드(바이트코드)로 번역한 뒤 가상머신(VM)에서 한줄씩 실행해준다. 바이트코드(byte Code)로 바꾸는 컴파일 타임 환경과 이를 한 줄씩 번역하면서 실행하는 런타임 환경으로 나뉜다.

하나씩 나눠서 살펴보자.

1. JAVA 컴파일 과정

자바가 컴파일 되는 과정을 살펴보자.

자바의 컴파일 과정

먼저 자바의 소스코드, 원시코드(*.java)는 CPU가 인식하지 못하므로 기계어로 컴파일해줘야 한다. 이 때, 바로 CPU에서 인식할 수 있는 기계어로 컴파일하는 것이 아니라 JVM이라는 가상머신을 거쳐서 가기 때문에 자바 컴파일러(Java compiler)는 중간언어인 자바 바이트코드(java bytecode(*.class))로 변환해준다. JVM에서 이 자바 바이트코드(java bytecode)를 해석해주기 때문에 OS와 관계없이 JVM만 설치되어 있다면 어느 디바이스든 Java 파일을 JVM 위에서 실행할 수 있다.

 

[ 실전 지식 ]

 ▶ Java compiler는 JDK를 설치하면 bin에 존재하는 javac.exe 파일이다.(JDK에 java compiler가 포함되어있다는 말임) javac 명령어를 통해 .java를 .class로 컴파일할 수 있다. 또한 JVM 역시 JDK 디렉토리의 bin 폴더 안에 존재하는 java.exe 파일로 java 명령어를 통해 JVM을 구동할 수 있다.

▶JDK는 Java로 소프트웨어를 개발할 수 있도록 여러 기능들을 제공하는 패키지(키트)이다. JDK는 프로그램을 생성, 실행, 컴파일할 수 있다.

▼ JDK 더보기

더보기

JDK 구성

  • apt : 어노테이션 툴
  • appletviewer : 웹브라우저 없이 자바 애플릿을 실행하고 디버깅하기 위한 툴
  • javac : 자바 컴파일러. 자바 소스파일을 바이트코드로 변환
  • java : javac가 만든 클래스 파일을 해석 및 실행
  • jar : 서로 관련있는 클래스 라이브러리들과 리소스를 하나의 파일로 묶어주는 툴
  • jdb : 자바 디버깅 툴
  • JRE(Java Runtime Enviroment) : 자바 런타임 환경으로 JVM + 자바 클래스 라이브러리(Java Class Library) 등 다양한 파일들을 포함한다. 컴파일 된 Java 프로그램을 실행하려면 JRE를 설치해야한다.
  • JVM(Java Virtual Machine) : Java가 실제로 동작하는 가상 환경. 이 JVM덕분에 하나의 Java프로젝트를 개발해도 여러 환경에서 원활하게 실행시킬 수 있다.

기타 등등...

JDK 종류

  1. Java SE : Java Platform , Standard Edition
    표준 자바 플랫폼으로 표준적인 컴퓨팅 환경을 지원하기 위한 자바 가상머신 규격 및 API 집합을 포함한다.
    JavaEE, JavaME는 구체적인 목적에 따라 자바 SE를 기반으로 API를 추가하거나 자바 가상머신 규격 및 API의 일부를 택하여 정의된다.
  2. Java EE : Java Platform , Enterprise Edition
    JavaSE에 웹 어플리케이션 서버에서 동작하는 기능을 추가한 플랫폼
    이 스펙에 따라 제품을 구현한 것을 웹 어플리케이션 서버(WAS)라 한다. ex. tomcat

WAS가 무엇인지 잘 모르겠다면 여기를 들어가보자!
WAS 와 웹서버의 차이점을 이해하기 쉽게 알려준다!
감사합니다 작성자님 ☺️

  1. Java ME : Java Platform , Micro Edition
    제한된 자원을 가진 휴대전화, PDA 등에서 Java 프로그래밍 언어를 지원하기 위해 만든 플랫폼 중 하나이다.

결론

  • JavaSE는 Java가 어떠한 문법적인 구성을 가졌는지와 같은 것들을 나타내는 명세표이다. JavaSE를 기반으로 특정 기능을 구현하기 위한 JavaEE, JavaME 플랫폼도 있다.
  • JDK는 JavaSE와 같은 규격을 토대로 만들어진 소프트웨어 패키지이다. 이는 Java를 개발 및 실행하는 데 필요한 툴들을 제공한다.
  • JRE가 Java를 실행하는데 필요한 바로 그 툴이다.

출처 :https://velog.io/@shelly/JAVA-JDK%EB%9E%80 

 

바이트코드와 바이너리 코드는 다르다. Byte Code JVM이 이해할 수 있는 언어로 변환된 자바 소스코드를 의미한다. 자바 컴파일러에 의해 변환된 코드의 명령어 크기가 1바이트라서 자바 바이트 코드라고 불리고 있다. 바이트 코드는 VM이 이해하는 코드이고, CPU가 이해할 수 있으려면 바이너리 코드(=0101 이진코드)로 변환해줘야한다. 모든 기계어는 0과 1로 이루어진 이진코드(바이너리 코드)로 이루어졌을 뿐이다. 바이너리 코드라는 알파벳으로 되어있을 뿐 CPU가 알아들으려면 이해할 수 있게 또다시 번역을 해줘야한다. JVM이 이제 일한다.

 

2. JAVA 런타임 과정

컴파일 과정을 거쳐 .java 파일은 중간 코드인 자바 바이트 코드(.class) 파일로 변환해줬다. 이제 런타임을 통해 자바 바이트 코드(.class)를 컴퓨터가 이해할 수 있는 기계어로 번역해주자.

자바가 런타임 되는 과정을 살펴보기 위해 JVM을 좀 더 까보자.

JVM 구성 요소

JVM에서는 어떤 일이 벌어질까?

1) 클래스 로더(Class Loader)

클래스 로더(Class Loader) JVM 내로 클래스 파일(*.class)를 동적으로 읽어서 메모리에 로드되어 JVM에 링크되게 한다. 컴파일된 .class 파일은 '로딩(Loading)', '링킹(Linking)', '초기화(Initializing)' 단계를 거쳐 JVM에서 사용할 수 있게 된다.

클래스 로더(Class Loader) 더 자세히 보기

더보기
클래스 로더 과정

Loading

우선 클래스 로더는 .class 파일을 읽고, 그 내용에 따라 적절한 바이너리 데이터를 만들고 메소드 영역에 저장하는 동작을 수행한다. 이 과정에서 .class 파일이 JVM 스펙에 맞는지 확인하고, Java Version을 확인한다.

Linking

  1. Verify
    읽은 클래스의 바이너리 데이터가 유효한 것인지 확인해야한다. .class 파일 형식이 유효한지 여러가지 체크를 한 다음 믿을 수 있는 .class 파일 데이터인 경우에 진행한다.
  2. Prepare
    클래스의 static 변수와 기본값에 필요한 메모리 공간을 준비한다.
  3. Resolution
    선택적으로 진행되는 과정으로 사용하는 환경에 따라 동작 유무가 정해진다. 이 과정에서 심볼릭 메모리 레퍼런스를 메소드 영역에 있는 실제 힙 메모리 영역에 있는 인스턴스에 대한 레퍼런스로 교체해준다. Constant Pool의 심볼릭 레퍼런스를 다이렉트 레퍼런스, 즉 실제 메모리 주소 값으로 변경해주는 작업을 한다.

Initializing

링크 단계의 Prepare 단계에서 확보한 메모리 영역에 클래스의 static 값들을 할당한다. 그리고 SuperClass 초기화와 해당 클래스의 초기화를 진행한다.

...일단 그렇다고 한다.

출처 :https://hbase.tistory.com/174

2) 실행 엔진(Execution)

실행 엔진(Execution) 클래스를 실행시킨다. 클래스 로더가 JVM 런타임 데이터 영역(Runtime Data Area)에 바이트 코드를 배치시키면 이것은 실행 엔진에 의해 실행된다. 실행엔진은 바이트코드를 기계어로 변경한다.

바이트 코드는 실시간 번역기(인터프리터) 또는 JIT 컴파일러에 의해 기계가 읽을 수 있는 바이너리 코드로 변환된다.

인터프리터(interpreter)는 한줄씩(명령어 단위로) 읽어서 번역 후 그때그때 실행한다.

만약 여러번 반복되는 소스라면 매번 한줄씩 번역하는건 불필요할 것이다. JIT 컴파일러(just-in-time compliation)는 이 때 사용되어 반복되는 소스를 한번에 변환해준다. 인터프리터 방식의 단점을 보완하기 위해 도입되었다.(VM 가상머신 안에 있음)

JIT 컴파일러(just-in-time compliation) 더 자세히 보기

더보기

JIT 컴파일러(just-in-time compliation)는 한번 바이트 코드 전체를 컴파일해서 기계어(바이너리코드)로 변환하고 나면 그 이후에는 다시 번역하지 않고 이미 번역해놓은 기계어를 직접 실행한다. 이 번역된(컴파일된) 기계어는 캐시에 보관한다. 그래서 한번 컴파일한 코드는 다시 수행하게 되면 빠르게 수행할 수 있게 되는 것이다. 당연히 한줄씩 번역하는것보다는 바이트 코드를 전부 컴파일하는게 느리기 때문에, JVM가 내부적으로 해당 메소드를 얼마나 자주 사용하는지 체크하고 있다가 적절한 시점에 바이트 코드 전체를 컴파일하는 JIT 컴파일러를 수행한다.

 

가비지 콜렉터(Garbage Collerctor) 더이상 사용되지 않는 인스턴스를 찾아 메모리에서 삭제한다. 불필요한 걸 청소해주는 청소부로 메모리를 관리해준다.

▼ 가비지 콜렉터(Garbage Collerctor) 더 자세히 보기

더보기
프로그램을 개발 하다 보면 유효하지 않은 메모리인 가바지(Garbage)가 발생하게 된다. 예를 들어, C언어를 이용하면 free()라는 함수를 통해 직접 메모리를 해제해주어야 한다. 하지만 Java나 Kotlin을 이용해 개발을 하다 보면 개발자가 메모리를 직접 해제해주는 일이 없다. 그 이유는 JVM의 가비지 컬렉터가 불필요한 메모리를 알아서 정리해주기 때문이다.

가비지 컬렉션은 영어로 Garbeage Collection으로 줄여서 GC라고도 부른다. 가비지 컬렉션은 자바의 메모리 관리 방법 중의 하나로 JVM의 Heap 영역에서 동적으로 할당했던 메모리 영역 중 필요 없게 된 메모리 영역을 주기적으로 삭제하는 프로세스를 말한다.

JVM에 탑재되어 있는 가비지 컬렉터가 메모리 관리를 대행해주기 때문에 개발자 입장에서 메모리 관리, 메모리 누수(Memory Leak) 문제에서 대해 완벽하게 관리하지 않아도 되어 오롯이 개발에만 집중할 수 있다는 장점이 있다.

단점은 개발자가 메모리가 언제 해제되는지 정확하게 알 수 없고, 가비지 컬렉션(GC)이 동작하는 동안에는 다른 동작을 멈추기 때문에 오버헤드가 발생한다.

출처 :https://mangkyu.tistory.com/118
https://coding-factory.tistory.com/829 

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

런타임 데이터 영역(Runtime Data Area)  JVM이 프로그램을 수행하기 위해 OS에서 할당받은 메모리 영역으로 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역이다. 한마디로 자바의 메모리 구조.

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

▼ 내가 기초적인 컴퓨터 구조도 모른다면? 메모리도 잘 모르겠는데?

 

보통 WAS의 성능에 문제가 생겼을 때 런타임 데이터 영역(Runtime Data Area)이 원인이 된다.(메모리 누수나 GC 이슈)

PC Register  JVM stack   Native Method stack  Heap  Method Area

좌측 3개의 영역은 Thread별로 생성되고 우측 2개의 영역은 모든 Thread가 공유(=JVM이 시작할 때 단 하나만 생성)한다.

▼ 스레드(Thread)가 뭐여?

더보기

프로그램은 하드디스크 등의 보조기억장치에 저장된 실행코드를 뜻하고, 프로세스(process)프로그램을 구동하여 메모리 상에 실행되는 작업단위이다. 하나의 프로그램을 여러번 구동하면 여러개의 프로세스가 메모리(RAM) 상에서 실행되는 것. 이 프로세스(process)도 뜯어보면 데이터와 메모리 등의 자원, 그리고 스레드로 구성된다.

 

스레드(Thread)프로세스(process) 내에서 실제로 작업을 수행하는 주체를 의미한다. 프로세스 내부에 있는 CPU 수행 단위로, 모든 프로세스는 한 개 이상의 스레드가 존재하여 작업을 수행한다. 프로세스 내부에 여러개의 스레드가 구성된다면 자기들끼리 할당된 메모리와 자원을 공유한다.

프로세스와 같이 실행, 준비, 대기 등의 실행상태를 가지며, 실행상태가 변할때마다 스레드 문맥교환(context switching)을 수행한다. 각각의 스레드별로 자신만의 스택과 레지스터를 가지고 있다.

각각의 스레드별로 자신만의 스택과 레지스터를 가지고 있다.

CPU는 한개의 프로세스만을 실행할 수 있다. 하지만 멀티스레딩(Multi Thread)을 거친다면 하나의 프로세스 내에서 여러 스레드가 동시에 작업을 수행하는 것이 가능하다. 한 프로세스 내에서 프로세스 내의 주소공간이나 자원 공유가 가능하기 때문에 독립적인 작업 수행을 가능하게 하며, 아주 짧은 시간동안 여러 작업을 번갈아가면서 수행할 수 있게 해주니 동시에 여러 작업이 수행되는 것처럼 보인다. 이 손발을 스레드(Thread)가 가능하게 해준다.

 

JVM 단위에서의 런타임 데이터 영역

JVM단위는 JVM이 시작될 때 단 하나만 생성되며, 모든 스레드들이 공유한다.

Heap

힙 영역은 모든 클래스 인스턴스와 Array객체같이 긴 생명주기를 가진 데이터들이 저장되는 공간이다.
Heap의 메모리 해제는 오직 Garbage Collector 의해 수행된다.
또한 모든 스레드들이 공유하는 영역이라 Race Condition을 유발할 수 있다.

Method Area

메서드 영역은 클래스 로더에 의해 로드 된 클래스 정보를 맨 처음 메모리 공간에 올릴 때, 초기화 되는 대상을 저장하는 공간이다. 런타임 상수 풀 그리고 그림에는 안나와 있지만 필드, 메서드, 생성자, 데이터 등의 코드 내용으로 이루어져 있다.

Runtime Constant Pool

런타임 상수풀은 메서드 영역에 클래스가 정보가 로드될 때 생성된다.
런타임 상수 풀에는 런타임에 해석되는 메서드와 필드의 참조 등 여러 종류의 상수가 저장되어있다.

Thread 단위에서의 런타임 데이터 영역

PC Register

PC Register는 스레드가 생성될 때마다 생기는 공간으로 스레드가 현재 실행중인 명령을 저장하는 역할을 한다.

JVM Stack

JVM Stack은 JVM Frame을 저장하는 역할을 한다.
Stack의 메모리 크기는 고정크기와 가변크기가 있다.

JVM Frame

JVM Frame은 메소드가 호출될 때마다 생성되며, 메소드의 상태정보를 저장한다.
Frame읜 구성요소는 총 3가지로 다음과 같다.

  • Local Variables
  • Operand Stack
  • Constant Pool Reference

Local Variables

Local Variables의 크기는 컴파일 타임에 결정되며, 자바 바이트 코드에서 Code속성에 locals로 표현된다.

long, double은 2개의 슬롯을 차지하지만,
boolean, byte, char, short, int float, reference, retrunAddress는 1개의 슬롯을 차지한다.
메서드가 호출될 때 해당 메서드의 파라미터 값은 Local Variables를 통해 넘겨진다.

만약 호출되는 메서드가 클래스 메서드일 경우에는 메서드의 첫번째 파라미터는 Local Variables의 0번째 index에 저장되면 이후 차례대로 저장된다.

반대로 메서드가 인스턴스 메서드일 경우에는 this가 Local Variables의 0번째 index에 저장되고 이후 메서드의 파라미터가 차례대로 저장된다.

Operand Stack

Operand Stack의 크기 또한 Local Variables처럼 컴파일 타임에 결정되며, 자바 바이트 코드에서 Code속성에 stack으로 표현된다.

Operand Stack은 프레임이 생성될 당시에는 비어있으며, 메소드 내 계산 과정 등 모든 과정에서 이용된다.

Constant Pool Reference

Constant Pool Reference 영역은 해당 프레임과 대응되는 메소드가 속한 클래스 단위의 런타임 상수풀에 대한 참조를 의미한다.

Native Method Stack

Native Method Stack은 자바가 아닌 다른 언어로 작성된 네이티브 메서드를 지원하기 위한 스택이다.

 

▼ JVM 구조 요약

 

JVM 구조

JVM 구조 JVM : 자바가상머신. 자바 바이트코드를 실행시키는 주체. 운영체제 종류와 무관하게 가능 즉, 운영체제 위에서 동작하는 프로세스로 자바를 컴파일해서 얻은 바이트코드를 기계

heannim-world.tistory.com

 

반응형
LIST

댓글