반응형
JVM이란 무엇인가?
JVM(Java Virture Machine)
- 자바 가상 머신으로 자바 바이트 코드(.class 파일)를 실행할 수 있는 공간이다.
- 가상 머신이므로 어떤 OS에서도 독립적으로 실행이 가능하다.
- 핵심은 JVM은 OS 위에서 동작하게 되며, 자바 코드를 컴파일한 바이트 코드를 운영 체제가 이해할 수 있는
기계어로 변환하여 코드를 실행 시킨다.
JVM 구성요소
1. ClassLoader
- 바이트 코드를 JVM이 운영 체제로 부터 할당받은 메모리 영역인 Runtime Data Area로 적재하는 역할
- 메모리를 할당 받았기 때문에 당연히 애플리케이션을 실행하면 수행된다.
- .java 파일을 javac.exe가 컴파일한 .class(바이트 코드)가 적재된다.
적재되는 시점은 Runtime시 적재된다. - 이름을 보면 이해할 수 있다. Class를 불러온다는 의미 그대로 생각하면 좋다.
2. Execution Engine
- Runtime Data Area(메모리 영역)에 적재된 바이트 코드를 기계어로 변경하여 실행한다.
- 기계어로 변경 후 명령어 단위로 실행하게 되는데 JIT컴파일러와 Interpreter 방식이 있다.
3. Garbage Collector
- Heap 메모리 영역에 적재된 객체들 중 참조되지 않는 객체들을 제거한다.
- GC가 언제 실행되는지는 알 수 없다.
- JVM에서 우리가 가장 잘 알아야하는 부분이다. 다른 부분은 그렇구나 정도만 알면 된다.
GC는 실제로 성능에 관련되어서 커스터 마이징 할 때가 있다고 한다.
4. Runtime Data Area(자바 런타임 메모리)
- 메모리 영역으로 자바 애플리케이션이 실행할 때 사용되는 데이터가 적재되는 곳이다.
- 총 5가지 영역이 존재한다.
Method Area
- 클래스 수준의 정보(클래스 이름, 부모 클래스 이름, 메서드, 변수)를 저장한다.
static String name = "dugi" String name1 = "dugi1"
- 애플리케이션에서 공유되는 영역이다. 즉, 모든 쓰레드가 공유
- 이 부분이 이해가 조금 안됐는데 메타 데이터가 들어있다고 생각하면 된다.
Heap Area
- new 키워드로 생성된 객체와 배열이 생성되는 영역
- Garbage Collector에 의해 사라질 때 까지 남아있다.
- 쓰레드가 공통으로 사용하는 영역이다.
Stack Area
- 지역 변수, 파라미터, 리턴 값이 저장된다.
- Primitive 타입은 값도 저장된다. 하지만 참조 타입은 참조의 주소를 자장한다.
- 쓰레드 마다 개별 생성되는 영역
- 즉, main 쓰레드를 추가로 생성하지 않으면, 한 개만 존재한다.
PC(Program Counter) Register
- 쓰레드가 생성될 때 마다 생성되는 영역
- 현재 쓰레드가 실행되는 주소와 명령을 저장하고 있기 때문에 이를 이용해 쓰레드를 돌아가면서 실행할 수 있다.
Native Method Stack
- 자바 외 언어로 작성된 네이티브 코드를 위한 메모리 영역이다.
- 보통 c의 코드를 수행하기 위한 영역이다.
- Object 클래스의 hashCode 메서드가 native 타입이다. 실제 OS의 메서드이다.
- Native 메서드를 사용하기 위해서는 반드시 JNI(네이티브 메서드 인터페이스)를 사용해야 하는데
hashCode 메서드가 JNI가 되는 것이고 JNI가 네이티브 메서드 라이브러리를 호출하여 사용하게 된다.
Class Loader의 구성요소
- 클래스 로더는 로딩, 링크, 초기화로 나뉜다.
- 클래스 로더가 .class 파일을 읽어 기계어를 만들고 메서드 영역에 저장한다.
이 때 저장하는 데이터는 FQCN(풀패키지경로 + 클래스명), 클래스/인터페이스/이넘, 메서드와 변수이다. - 로딩이 끝나면 Class 객체를 생성하여 힙 영역에 저장한다.
Class 객체란 Class class = Solution.class인 타입을 뜻한다. - 클래스 로더는 세가지 종류를 가지는데 Bootstrap -> Extension -> Application이다.
가장 먼저 최상위 부모인 Bootstrap에게 .class 파일을 로딩하라고 요청하고 없으면 Application까지 내려온다.
만약 없으면 ClassNotFoundException을 만나게 되는 것이다. Class 객체에는 많은 정보들이 들어있다.
아래와 같이 보면 알 수 있다. 마지막 최상위인 bootstrap 클래스 로더는 native이기 때문에 볼 수가 없다고 한다.링크 -
@Test void ddd() throws NoSuchFieldException { Solution solution = new Solution(); Class<Solution> aClass = (Class<Solution>) solution.getClass(); // 클래스 로더 정보 System.out.println("==========클래스로더 정보=========="); System.out.println("classloader = " + aClass.getClassLoader()); System.out.println("parent classloader = " + aClass.getClassLoader().getParent()); System.out.println("parent parent classloader = " + aClass.getClassLoader().getParent().getParent()); // 클래스 정보들 System.out.println("==========클래스 정보=========="); System.out.println("class name = " + aClass.getName()); System.out.println("field a = " + aClass.getDeclaredField("a")); System.out.println("field str = " + aClass.getDeclaredField("str")); /* 결과 ==========클래스로더 정보========== classloader = sun.misc.Launcher$AppClassLoader@18b4aac2 parent classloader = sun.misc.Launcher$ExtClassLoader@545997b1 parent parent classloader = null ==========클래스 정보========== class name = Solution field a = private int Solution.a field str = private java.lang.String Solution.str */ }
- Verify : .class 파일이 잘 만들어졌는지 검증한다. 혹시 .class 파일을 내가 막 조작한다면 여기서 걸리는 것이다.
- Preparation : 클래스 변수(static)와 기본값에 필요한 메모리를 준비하는 과정
- Resolve : 심볼릭 메모리 레퍼런스를 메서드 영역(힙 영역아닌가?)에 있는 실제 레퍼런스로 교체하는 과정.
초기화// 실제 힙에 있는 레퍼런스가 아니라 논리적인 레퍼런스이다. Solution solution = new Solution(); // 심볼릭 레퍼런스
optional이라 항상 그런건 아니다.
- Preparation에서 준비한 메모리에 static한 변수를 할당한다.
- 클래스 로더가 .class 파일을 읽어 기계어를 만들고 메서드 영역에 저장한다.
- 로딩
컴파일 하는 방법 및 실행하는 방법
컴파일 이란?
- 사람이 해석하기 쉬운 고급 언어를 컴퓨터가 이해하는 바이너리 코드(기계어)로 변환하는 것.
컴파일 하는 방법
- javac.exe 컴파일러를 통해 바이트 코드(.class)로 변환
- 명령어 :
javac User.java
- 컴파일이 완료되면 컴파일을 실행한 위치에
.class
파일이 생성된다.
실행 방법
- 컴파일된 .class 파일을 실행한다.
- 명령어 :
java User
-> .class는 제외해야 함.
Javac.exe, java.exe가 어떤 위치에서도 실행될 수 있는데 이유는 환경변수가 해당 경로를 보고 있기 때문이다.
바이트 코드란?
먼저 바이너리 코드의 이해가 필요하다.
바이너리 코드
- C언어 컴파일러는 소스 파일을 컴파일 하면, 바로 바이너리 코드 가 된다.
이진 코드(0 ,1)이지만 기계어는 아닌 상태이므로 운영체제가 실행이 불가하다.
기계어 는 운영체제가 유일하게 실행할 수 있는 바이너리 코드 이며, CPU 마다 다를 수 있다.
CPU 마다 다를 수 있다는 것은 단지 1과 0의 조합이 다를 수 있다는 의미일 뿐이다.
바이트 코드
- 자바는 바이너리 코드 과정을 거치지 않고 JVM이 바로 바이트 코드로 변환한다.
- JVM이 이해할 수 있는 코드이다.
- 바이트 코드는 다시 인터프리터 or JIT컴파일러 를 통해 바이너리 코드로 변환하게 된다.
JIT 컴파일러란
인터프리터와 컴파일러란?
- 인터프리터
실행되는 코드를 한 줄씩 해석한다.
같은 코드가 다시 나와도 계속 반복하여 해석한다.
즉, 초기 로딩이 빠르다. 단 계속 같은 코드도 다시 해석 하므로 느릴 수 있다. - 컴파일러
딱 한 번 코드를 컴파일 하기 때문에 초기 로딩은 느리다.
이후에는 다시 해석하지 않고, 캐싱하므로 더욱 빠를 수 있다.
JIT(Just In Time) 컴파일러
- 인터프리터가 한 줄씩 해석하면서 기계어로 변환하는데, 반복되는 코드는 JIT 컴파일러가 한방에 기계어로 변환하여,
캐싱해두고 재사용을 하게 된다. - 즉, 필요한 것만 컴파일 하는 것이다.
- 인터프리터의 단점을 보완한 것이다.
- 컴파일러는 한방에 기계어로 변환하기 때문에 초기에 느린 단점이 있다.
- 이런 장단점으로 인해 JVM이 인터프리터 방식과 JIT 방식을 섞어 쓰는 것이다.
javac와 JIT 컴파일러
javac도 컴파일러 JIT도 컴파일러? 도대체 무슨 말이지?
- javac는 JVM외부에서 자바소스를 JVM이 해석할 수 있는 언어인 바이트 코드로 변환하는 컴파일러입니다.
컴파일러라는 것이 OS가 해석할 수 있는 기계어로 변환하는 것인데 JVM도 가상 OS입니다.
그렇기 때문에 JVM OS의 언어인 바이트 코드로 변환하는 것입니다. - JIT 컴파일러와 인터프리터는 JVM의 컴파일러입니다. 이제 JVM이 아닌 실제 OS가 해석할 수 있도록 기계어로 변환하는 것입니다.
즉, JVM 내부에서 실제 windows, mac, linux와 같은 OS가 해석할 수 있도록 해당 기계어로 변환하는 컴파일러 입니다.
JDK와 JRE 차이
JRE(Java Runtime Environment)
- 자바 실행 환경으로 실행만 가능하다.(java.exe)
- 즉, 소스코드 작성 불가.
- 실행만 하는 환경이라면 JRE만 있으면 된다.
JDK(Java Development Kit)
- JRE를 포함한 패키지로 소스코드도 작성할 수 있다.
- 즉, 실행도 가능하고, 개발도 가능한것으로 실행만 할 것이라면 JDK는 필요없다.
Java의 세 가지 Edition
- Java SE(Java Standard Edition)
가장 기본 버전으로 우리가 하는 대부분 웹 개발 모두 가능하다. - Java EE(Java Enterprise Edition)
웹 서비스, 메일 송신 등 기업에서 필요로 하는 기능들이 포함 되어있어 더욱 편리하게 사용할 수 있다. 하지만 SE로 보통 가능하다. - Java ME(Java Micro Edition)
휴대전화, 가전제품과 같은 임베디드 지원 SE에 비해 기능은 적지만, 리소스가 적게 들어 임베디드에 적합.
반응형
'Java' 카테고리의 다른 글
Java로 자료구조 이해하기 4편(Set) (0) | 2021.10.16 |
---|---|
인터페이스로 객체지향스럽게 다형성을 이용하여 중복 제거 하기 (0) | 2021.10.14 |
Arrays의 asList가 뭐고 왜 써야 할까? (0) | 2021.10.08 |
hashCode와 toString (0) | 2021.10.05 |
Java로 자료구조 이해하기 3편(Stack과 Queue) (0) | 2021.10.02 |