프로그래머라면 한 번쯤은 JAVA 언어를 통해서
코드를 작성해봤을 것이다.
하지만, 내가 작성한 JAVA Code 가 어떤 식으로 작동하는지
정확하게 알고 있는 프로그래머는 많지 않을 것이다.
그럼 JAVA 가 어떤식으로 작동하는지 알아보자.
자바에서 실행 과정을 단계별로 나눈다면 크게
컴파일 타임(Compile Time), 클래스 로딩 타임(Class Loading Time), 런타임(Run Time)으로 나눌 수 있다.
컴파일 타임은 소스코드를 작성한 후 javac와 같은 컴파일러를 사용해
. java 파일을. class 파일(바이트 코드)로 컴파일하는 단계이다.
클래스 로딩 타임은 JVM이 클래스 파일을 읽어 메모리에 로딩하는 단계이다.
이 과정에서 클래스를 검증하고, 필요한 메모리 공간을 할당하고, 상수 풀 등의 정보를 생성한다.
런타임은 클래스가 메모리에 로딩된 이후에, 프로그램이 실행되는 단계이다.
이 단계에서 인터프리터나 JIT 컴파일러가 바이트 코드를 기계어로 변환하고 실행한다.
또한 가비지 컬렉터가 메모리에서 더 이상 필요 없는 객체를 제거하는 작업을 수행한다.
즉, 자바는 실행 단계에서도 클래스를 로딩하고,
바이트 코드를 변환하여 실행하는 런타임 환경을 제공한다.
따라서 런타임이라는 용어는 이러한 실행 단계를 말하는 것이다.
1. 컴파일 타임(Compile Time)
프로그래머가 작성한 JAVA code는 JVM(Java Virtual Machine) 이
1차적으로 Byte code 로 변환하는 작업을 진행해 준다.
여기서 헷갈리지 말아야 할 점은 C, C++ 코드를 작성해서 컴파일할 때와 다르게
바로 기계어로 변환이 되는 것이 아니고,
JVM 이 인식하는 바이트 코드로 변환된다.
2. 클래스 로딩 타임(Class Loading Time)
컴파일 타임 때 변경된. class 파일은
Class Loader 가 JVM 메모리 영역인 Runtime Data Area로 로딩해 준다.
Method Area와 Heap Area는 모든 스레드가 공유하는 영역이고.
나머지 Stack, PC Register, Native Method Stack 은 스레드마다 하나씩 생성되는 공간이다.
Method Area (= Class Area, Static Area)
JVM 이 시작될 때 생성되는 공간으로 바이트코드가 이 영역에 저장이 된다.
해당 영역은 는 모든 스레드가 공유하고
클래스 로더에 의해 로딩된 클래스 정보, 상수 정보, 변수 정보, static 으로 선언한 변수가 저장되어 있다.
Method Area는 JVM이 시작될 때 생성되며,
JVM이 종료될 때까지 메모리에 남아있는다.
Method Area의 크기는 -XX:MaxMetaspaceSize와 같은 JVM 인자를 통해 조절이 가능하다.
Heap Area
Heap 영역은 동적으로 생성된 객체가 저장되는 영역으로
GC(Garbage collection)의 대상이 되는 공간이다.
new 연산을 사용해서 동적으로 생성된 인스턴스 변수가 저장된다.
생성된 변수들은 해당 객체가 소멸되기 전이나,
GC에 의해서 정리되기 전까지는 Heap 영역에 남아있게 된다.
Heap 영역은 효과적인 GC를 위해서 5가지 영역으로 나뉜다.
Heap 영역의 크기는 -Xmx 와 같은 JVM 인자를 통해 조절이 가능하다.
Stack Area
Stack 영역은 지연변수나 메서드의 매개변수,
임시적으로 사용되는 변수, 메서드의 정보가 저장되는 영역이다.
지역변수와 매개변수의 특성으로 해당 매서드의 호출이 종료되면,
stack 내부에 선언된 변수들은 모두 사라지게 된다.
예를 들어, 위와 같이 Student 객체의 인스턴스를 생성한 경우
Student 객체를 참조하는 s 변수는 Stack 영역에 저장된다.
하지만, Student 인스턴스 그 자체는 Heap 영역에 저장되게 된다.
또한, Primitive Type 같은 경우에는 해당 값이 바로 Stack에 저장되지만,
Reference Type 은 참조변수는 Stack 에 저장되고
실제 데이터는 Heap 영역에 저장되게 된다.
하지만 여기서 헷갈리면 안 되는 것이 있다.
Primitive Type이라 하더라도 배열과 같은 형태로 사용하면
해당 데이터는 Stack에 저장되지 않는다.
int[] arr = new int[3];
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
위와 같이 Primitive type 인 int를 사용해서 배열을 선언했다고 가정해 보자.
그럼 arr 인 참조변수는 Stack 영역에 저장이 되고,
arr의 값으로 Heap 메모리 영역에 새로운 배열이 생성되고,
배열의 각 요소 (10, 20, 30)도 Heap 영역에 저장된다.
PC Register (Program Counter Register)
PC Register는 Program Counter Register이다.
스레드가 어떤 부분을 어떤 명령어로 수행할지 저장하는 공간이다.
스레드가 시작될 때 생성되며,
현재 수행 중인 JVM의 명령어 주소를 저장하는 공간이다.
예를 들어, 어떤 스레드가 A 메서드를 실행하다가 B 메서드를 호출하면,
스레드는 A 메서드에서 B 메서드로 이동할 것이다.
이때, PC Register는 B 메서드의 첫 번째 명령문의 주소를 기억한다.
그리고 B 메서드 실행 중 다른 메서드를 호출하면
PC Register는 새로 호출된 메서드의 주소를 가리킨다.
이렇게 하여, 스레드가 다음에 실행할 명령문의 주소를 계속해서 기억하고
업데이트하여 실행하게 된다.
Native Method Stack
Native Method Stack 은 JAVA 가 아닌 다른 언어로 작성된 코드를 위한 공간이다.
Java에서 Native Method란, Java로 작성된 코드가 아닌
C, C++ 등의 다른 언어로 작성된 코드를 의미한다.
Java 애플리케이션은 Native Method를 호출하여 외부 코드와 상호작용할 수 있는데,
이때 호출되는 외부 코드의 실행에 필요한 메모리 공간을
Native Method Stack이라고 한다.
Native Method Stack은 JVM 스레드가 Native Method를 실행할 때 사용되며,
일반적인 Java 메서드와 다른 스택 공간을 사용한다.
Native Method Stack은 Java 스택과는 다르게
프로그램의 실행 시간 동안 일정한 크기로 유지된다.
Native Method 호출이 끝나면,
해당 호출에 사용된 Native Method Stack 메모리는 다시 사용 가능한 상태가 된다.
3. 런타임(Run Time)
마지막으로 Run time 단계는 프로그램이 실행되는 단계이다.
이 단계에서 인터프리터나 JIT 컴파일러가 바이트 코드를 기계어로 변환하고 실행한다.
자바 Execution Engine은 자바 코드를 실행하기 위한 가상 머신으로,
Interpreter와 JIT Compiler, 그리고 Garbage Collector로 이루어져 있다.
Interpreter
Interpreter는 자바 코드를 한 줄씩 해석하고 실행하여
바이트 코드를 기계어로 변환한다.
이 과정에서 매번 해석하고 실행하기 때문에 느리다.
하지만 코드 실행 속도가 빠른 대신, 반복적으로 실행되는 코드에 대한 최적화가 어렵다.
JIT Compiler
JIT Compiler는 자바 코드를 기계어로 변환하여 실행한다.
실행 시점에 바이트 코드를 바로 기계어로 컴파일하여 캐시에 저장하고,
이후에 같은 코드가 실행될 때 캐시된 기계어를 사용합니다.
이 과정에서 기계어로 변환하는 비용이 들기 때문에 초기 실행 속도는 느릴 수 있지만,
반복적으로 실행되는 코드에 대해서는 매우 빠르다.
Garbage Collector
Garbage Collector는 더 이상 사용하지 않는 객체를 찾아내고
제거하여 메모리를 확보하는 역할을 수행한다.
Garbage Collector는 자동으로 실행되기 때문에
개발자가 직접 메모리 관리를 할 필요가 없다.
Garbage Collector는 메모리 누수와 같은 문제를 방지한다.
따라서 자바 Execution Engine은 Interpreter와 JIT Compiler,
그리고 Garbage Collector의 협력으로 자바 코드를 실행하며,
높은 이식성과 안정성, 그리고 자동 메모리 관리 등을 제공한다.
'JAVA' 카테고리의 다른 글
[JAVA] JAVA GC(Garbage Collection) (0) | 2023.03.14 |
---|---|
[JAVA] equals와 ==의 차이점 (0) | 2022.06.02 |
[JAVA] StringTokenizer (0) | 2022.03.23 |