728x90
JAVA는 JVM(Java Virtual Machine)에서 동작하기 떄문에 호환되는 운영체제만 있다면 어떤 OS에서도 사용 가능하다.
자바 코드는 위의 그림과 같은 단계로 실행이 된다.
- 자바 코드로 파일을 작성한다.
- 자바 컴파일러(javac)를 통해 자바 바이트 코드(*.class 파일)로 컴파일한다.
- 컴파일된 바이트 코드(*.class)를 JVM의 클래스로더(Class Loader)에게 전달한다.
- 클래스로더는 동적로딩(Dynamic Loading)을 통해 필요한 클래스들을 로딩,링크하여 Runtime Data Area(JVM의 메모리)에 올린다.
- 실행엔진이 JVM 메모리에 올라온 바이트 코드들을 명령어 단위로 하나씩 실행한다.
JVM의 내부
- 클래스 로더(Class Loader)
- 실행 엔진(Execution Engine)
- 런타임 데이터 영역(Runtime Data Area)
크게 3가지로 구성되어있다.
- 클래스 로더(Class Loader)
클래스 로더는 JVM내부로 클래스파일(*.class)을 동적으로 로드하고, 링크를 통해 배치한다.
로딩 순서는 3단계로 구성이 된다.
(Loading → Linking → Initialization)
-
- Loading(로드) : 클래스 파일을 가져와서 JVM의 메모리에 로드한다.
- Linking(링크): 클래스 파일을 사용하기 위해 검증하는 과정
- Verifying: 읽어들인 클래스가 JVM 명세에 명시된 대로 구성되어 있는지 검사
- Preparing: 클래스가 필요로 하는 메모리를 할당
- Resolving: 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경
- Initialization(초기화): 클래스 변수들을 적절한 값으로 초기화
- 실행엔진(Execution Engine)
- 실행 엔진은 클래스로더를 통해 런타임 데이터 영역에 배치된 바이트 코드를 명령어 단위로 읽어서 실행.
- 바이트 코드(*.class)는 기계가 수행하는 언어이기보다는 가상머신이 이해할 수 있는 레벨의 컴파일된 코드
- 그렇기 때문에 실행 엔진이 바이트코드를 JVM내부에서 기계가 실행할 수 있는 형태로 변경해준다.
- 변경 과정에서 실행 엔진은 인터프리터와 JIT 컴파일러 두가지를 혼합해 바이트 코드를 실행한다.
- 인터프리터 : 바이트 코드 명령어를 하나씩 읽고 해석 및 실행. 하나하나의 해석은 빠르지만 전체적인 실행 속도는 느린 단점이 있다. JVM 내부에서 바이트코드는 기본적으로 인터프리터 방식으로 동작
- JIT 컴파일러(Just-In-Time Compiler) : 인터프리터의 단점을 보완하기 위해 도입된 방식. 바이트 코드 전체를 컴파일하여 네이티브 코드로 변경하고 이후에는 해당 메서드를 더 이상 인터프리팅 하지 않고 네이티브 코드로 직접 실행하는 방식이다. 바이트 코드 전체가 컴파일된 네이티브 코드를 실행하기 때문에 전체적인 실행 속도는 인터프리팅 방식보다 빠르다
- 런타임 데이터 영역(Runtime Data Area)
- 메서드 영역(Method Area) : 모든 스레드가 공유하는 영역, JVM이 시작될 때 생성. JVM이 읽어 들인 각각의 클래스와 인터페이스에 대한 런타임 상수 풀, 필드와 메서드에 대한 정보, Static 변수, 메서드의 바이트 코드 등을 보관
- 런타임 상수 풀(Runtime Constant Pool) : JVM 동작에서 가장 핵심적인 역할을 수행하는 곳, JVM 명세에서도 따로 중요하게 기술. 각 클래스와 인터페이스의 상수 뿐만 아니라, 메서드와 필드에 대한 모든 레퍼런스까지 담고 있는 테이블로 어떤 메서드나 필드를 참조할 때 JVM은 런타임 상수 풀을 통해 해당 메서드나 필드의 실제 메모리상 주소를 찾아서 참조
- 힙 : 인스턴스 또는 객체를 저장하는 공간으로 가비지 컬렉션(Garbage Collection) 대상. JVM 성능 등의 이슈에서 가장 많이 언급되는 공간. 힙 구성 방식이나 가비지 컬렉션 방법 등은 JVM 벤더들의 재량
- JVM 스택(JVM Stack) : 스택 프레임(Stack Frame)이라는 구조체를 저장하는 스택. 예외 발생 시 printStackTrace() 메서드로 보여주는 Stack Trace의 각 라인 하나가 스택 프레임을 표현. JVM 스택 역시 PC 레지스터와 마찬가지로 스레드가 시작될 때 생성되며 각 스레드마다 하나씩 존재
- PC 레지스터(PC Register) : PC(Program Counter) 레지스터는 현재 수행 중인 명령의 주소를 가지며 스레드가 시작될 때 생성되며 각 스레드마다 하나씩 존재
- 네이티브 메서드 스택(Native Method Stack) : JAVA 외의 언어로 작성된 네이티브 코드를 위한 스택. JNI(JAVA Native Interface)를 통해 호출하는 C/C++ 등의 코드를 수행하기 위한 스택, 언어에 맞게 스택이 생성. (C면 C스택, C++이면 C++스택 생성)
가비지 컬렉션
- Garbage Collection, 줄여서 GC라고 부른다.
- 메모리 관리 방법중 하나로 시스템에서 더이상 사용하지 않는 동적 할당된 메모리 블럭을 찾아 자동으로 다시 사용 가능한 자원으로 회수하는 것
- 시스템에서 가비지컬렉션을 수행하는 부분을 가비지 컬렉터라 부른다.
- 가비지 컬렉션이 없다면 프로그래머가 메모리를 할당한 뒤 수동으로 해제까지 직접 해야 한다.
- 메모리를 할당해놓고 필요 없어진 뒤에도 해제를 안하여 메모리 누수가 생기거나 해제했던 메모리를 다시 사용하는 실수 등에서 버그가 양산
- 이런 문제들을 해결하기 위해서 제시된 것이 가비지 컬렉션이다. 가비지 컬렉션에서 제공하는 할당과 해제를 이용하여 프로그램이 실행되며 생기는 쓸모없어지는 메모리들을 알아서 수집하고 관리해준다.
가비지 컬렉터
가비지 컬렉터의 역할은 아래와 같다.
- 메모리 할당
- 사용중인 메모리 인식
- 사용하지 않는 메모리 인식
즉, 메모리가 부족할 때 쓰레기를 정리해주는 프로그램을 가비지 컬렉터라고 한다.
프로그램을 실행할때 메모리를 관리하는 OS에 프로그램 실행에 필요한 메모리를 요청하게 된다.
이때 주소를 할당하는데 이 주소를 offset 주소라고 부른다.
이 할당된 메모리들은 프로그램이 돌아가며 가비지가 발생하게 된다. 기존에 가리키고 있던 메모리를 새롭게 선언되거나 형변환이 되며 다른곳을 가리키게 된다. 그러면 주소를 잃어버리고 다시 찾을 수 없게 되면서 정리되지 않은 메모리가 생기기 때문이다.
그래서 가비지 컬렉터는 가비지를 다른 용도로 사용할 수 있도록 메모리 해제를 시킨다. 이것이 가비지 컬렉터의 목적이다.
따라서 자바에서는 JVM이 메모리를 부여받아 프로그램을 실행하고, 메모리가 부족해지는 순간이 오게되면 추가적으로 메모리를 요청한다. 이때 가비지 컬렉터가 실행된다.
728x90
'STUDY > JAVA' 카테고리의 다른 글
JAVA-객체 지향 프로그래밍이란 (0) | 2023.12.28 |
---|