오늘부터 Java 강의를 듣고 과제를 수행한다.
1~3주차 강의의 내용은 아주 기본적인 내용들이었다.
몇몇은 건너뛰기도 하고, 1.5배속으로 훑으면서 가물가물한 부분들은 따로 공부를 진행하려고 메모를 해두었다.
그리하여 이제 메모한 내용들을 공부할 시간 ...
먼저 JVM에 대해 정리하고 넘어가자
JVM이란?
JVM은 Java Virtual Machine의 줄임말로, Java로 짜여진 코드들을 운영체제에서 실행할 수 있도록 만들어주는 가상의 기기(소프트웨어)이다.
자바로 작성된 어플리케이션이 컴퓨터에서 동작하기 위해서는 반드시 JVM이 필요하다.
일반적인 다른 어플리케이션이 바로 OS와 상호작용하는 것과 달리 자바 어플리케이션은 JVM과 상호작용하며, JVM이 OS와 상호작용하는 역할을 한다.
이 때문에 어플리케이션 실행 시 속도가 느려진다는 단점이 있지만 (한 단계를 더 거치므로), OS에 맞춰 어플리케이션을 변경할 필요가 없으므로 운영체제에 독립적이다는 장점을 가진다.
🧐 Java는 운영체제에 독립적이다?
정확히 말하자면, Java 어플리케이션은 운영체제에 독립적이다.
반면, JVM은 OS에 종속적이다. OS와 직접적으로 상호작용하여 각 OS가 이해할 수 있도록 어플리케이션 코드들을 변환하여야 하기 때문이다. 일반적으로 많이 사용되는 주요 OS용 JVM을 Java 개발사인 썬에서 제공하고 있다.
JVM 구성요소
(+) Java 컴파일러
*.java 파일을 *.class 파일로 변환한다.
🧐 class 파일은 무엇으로 구성되어 있는가?
자바 바이트 코드로 구성되어 있다. ( = 어셈블리어 = 예를 들어, add $2 $4 $2)
개발자가 이해할 수 있도록 짜여진 .java 파일(고급 언어)을 해석하여 JVM이 이해할 수 있는 코드(중간 언어)로 변환한 것이 바이트 코드이다.
클래스 로더
프로그램이 실행되면, *.class 바이트 코드를, 메모리 영역에 저장한다.
프로그램 실행 시 운영체제는 JVM이 사용할 수 있도록 메모리 영역을 할당해준다. 클래스 로더가 이 메모리 영역에 바이트 코드를 저장한다.
실행 엔진 1. 인터프리터
*.class 바이트 코드를 읽어서 바이너리 코드로 변환한다.
바이트 코드는 JVM이 해석할 수 있는 언어이다. 이제 운영체제가 이해할 수 있는 언어(이진코드 0101...)로 다시 바꾸어줘야 하는데, 이 역할을 인터프리터가 한다.
인터프리터는 자바 바이트 코드를 명령어 단위로 읽어서 해석한다.
- 실행 시 매번 소스 코드를 해석해야 하므로 대규모 프로그램이나 복잡한 연산 수행 시 성능 저하가 발생할 수 있다.
- 이 성능 저하를 해결하기 위해 JIT 컴파일러가 사용된다.
실행 엔진 2. JIT 컴파일러
인터프리터와 같이 기계어 코드를 생성한다. 컴파일 기준에 들어온 코드를 컴파일하여 캐싱한다.
- '컴파일 기준'은 얼마나 자주 코드가 실행되었는가? 이다.
- 즉, 자주 반복되어 실행되는 바이트 코드는 또 다시 해석하지 않고 바로 가져다 쓸 수 있도록 바이너리 코드 전체를 저장해주는 역할을 한다.
실행 엔진 3. 가비지 콜렉터
런타임 데이터 영역에서 '더는 사용하지 않는 메모리'를 알아서 해제해준다.
예를 들어 더이상 참조되지 않는 영역이라던가 ...
런타임 데이터 영역 (메모리 영역)
JVM이 사용할 수 있는 메모리 영역으로, 크~게 Heap / Stack / Static(Method) 영역으로 나누어져 있다.
(더 자세히는 PC Register와 Native Method Stack라는 영역도 있다.)
- Static (Method)
클래스 변수나 static으로 선언된 것들, JVM이 실행되고 Class가 로딩될 때 생성되는 데이터들이 저장되는 영역이다.
JVM이 실행될 때 생성되어서, JVM이 종료될 때 메모리에서 해제된다. 즉, 프로그램 종료 전까지는 메모리가 해제 되지 않으며, 어디서든 접근 가능하다. (모든 스레드가 공유한다.)
- Heap
인스턴스를 생성할 때 사용되는 영역으로, 런타임시 동적인 크기로 할당되는 데이터들이 여기에 저장된다.
예를 들어 new 키워드로 인스턴스를 생성하면 Heap 영역에 생성된 객체를 저장한다. 그리고 이 객체가 저장된 주소는 Stack에 저장된다.
즉, 힙 영역에 참조형 데이터를 저장하고, 해당 영역의 주소가 변수명과 함께 Stack에 저장된다.
만약 이 주소를 참조하는 변수가 없어지게 된다면? GC(Garbage Collector)에 의해 해당 주소의 데이터는 제거되어 메모리가 해제된다.
// 예를 들어
int a = 10;
// 변수 a에 넣은 값 10은 스택 영역에 저장된다.
int[] arr = { 10, 20, 30 };
// 변수 arr에 넣은 값 10, 20, 30은 힙 영역에 저장된다.
// 변수 arr가 가진 값은 { 10, 20, 30}이 저장된 주소(힙 영역의 주소)이며, 이 주소는 스택 영역에 저장된다.
🧐 동적인 크기로 할당되는 데이터들?
기본 자료형(boolean,char,byte,short,int,long,float,double)이 아닌 참조형 데이터(배열이나, 객체)들을 의미한다
- Stack
기본 자료형과 지역변수, 매개변수등 임시적인 데이터들이 여기에 저장된다.
- 하나의 메서드가 호출되면, 메서드 마다 스택 프레임이 생겨난다. (스택에 들어가는 하나의 프레임으로, 하나의 메서드에 할당된 공간)
- 스택 프레임에는 지역변수나 매개변수가 저장되며 만일 참조형 데이터가 생성될 경우 적재 데이터 주소를 가진 참조형 변수 역시 저장된다.
- 메서드가 종료되면 해당 스택 프레임은 스택에서 제거된다.
- 각 스레드 별로 스택 영역을 하나씩 가지며, 런타임 시 스택 사이즈를 유동적으로 바꿀 수 없다. (런타임 전인 경우 바꿀 수 있긴 하다.)
- 이 때, 프로그램 실행 중 메모리 크기가 충분하지 않다면 StackOverFlowError가 발생하게 된다. (ex. 종료 조건 없이 재귀함수를 실행시킬 경우, 스택 프레임이 계속해서 늘어나 StackOverFlow 발생)
JRE / JDK / JDB
JRE (Java Runtime Environment)
.class 파일을 실행시켜준다.
정말 단순히 .class 파일을 읽고 해석하여 실행시키는 역할만 하기 때문에, java 파일을 작성하여 개발하고 .class 파일로 변환하는 등의 작업을 위해서는 JDK를 설치하여야 한다.
JDK (Java Development Kit)
Java 개발 & 어플리케이션 실행에 관련된 모든 모듈들을 모아둔 것
- javac : *.java 파일을 *.class 파일로 변환시키는 명령어
- jdb : 디버깅 기능
'프로그래밍 언어 > Java' 카테고리의 다른 글
Java Enum 이놈아 (1) | 2025.01.03 |
---|---|
Java의 예외처리 (함수 너 회피형이야?) (1) | 2025.01.02 |
[Java] 문자열(String) 내장 함수 정리 (1) | 2024.01.11 |
[Java] CharSequence란? (0) | 2024.01.11 |
[Java] 문자열(String) 클래스 정리 (0) | 2024.01.11 |