1. JVM이란 ?
자바 가상 머신(Java Virtual Machine)은 컴퓨터가 자바 프로그램을 실행할 수 있도록 도와준다.
JVM은 자바 바이트코드를 실행할 수 있는 주체이다.
C/C++ 는 컴파일 플랫폼이(운영체제 + CPU 아키텍처) 다를 경우, 프로그램이 동작하지 않는다.
동일한 플랫폼에서 컴파일과 실행한다면 프로그램은 아무 이상 없이 동작하겠지만, ⚠︎ 플랫폼이 달라질 경우, 타깃 플랫폼에서 프로그램이 동작하지 않는다. (예를 들면 윈도우에서 돌게 컴파일한 건 맥이나 리눅스에서 안 돌아가는 것)
➡︎ 그래서 이를 크로스 컴파일(타깃 플랫폼에 맞춰 컴파일하는 것)로 해결했다.
자바는 네트워크에 연결된 모든 디바이스에서 작동하는 것이 목적이었다.
디바이스마다 운영체제나 하드웨어가 다르기 때문에, 자연스럽게 플랫폼에 의존하지 않도록 언어 설계하였다. 그 결과가 Java Bytecode, JVM이다.
✔︎ JVM은 자바 바이트코드는 JVM이 설치된 환경이라면, 타깃 플랫폼에 상관없이 JVM 위에서 동작한다. (물론, JVM은 타겟 플랫폼에 의존한다.)
쉽게 말하면 JVM은 통역사 같은 느낌이다. 자바로 코딩하고 컴파일을 하면 바이트코드가 생긴다(기계어 X) 바이트코드란 사람이 쓰는 자바 코드에서 컴퓨터가 읽는 기계어로의 중간 단계에 있는 코드라고 생각하면 된다.
- .java 확장자가 자바인 파일 생성 (자바라는 컴퓨터 언어의 문법에 맞게 만들어진 소스코드)
- 이 소스코드는 사람이 이해할 수 있다. 기계는 이해할 수 없다.
- 따라서 확장자가 자바인 파일을 기계가 이해할 수 있도록 전환하는 작업을 컴파일이라고 한다.
- Hello.java를 만든다고 했을 때, 터미널에서 자바 컴파일러로 (javac Hello.java) 명령을 내리면 같은 위치에 Hello.class가 만들어진다. 이때 .class 파일이 Java Application인데, 이를 바이트코드라고 하는 것.
- 이 프로그램을 실행할 때 JVM이 바이트코드를 그때그때 기계어로 통역을 해준다.( .class인 파일을 읽어서 거기에 있는 대로 컴퓨터를 동작하게 된다. )
따라서 JVM은 자바로 짜서 컴파일한 소프트웨어들이 JVM이 깔린 어떤 종류의 컴퓨터에서든 돌 수 있게 해준다. JVM을 통해 한번 작성하면 모든 곳에서 실행한다는 'Write Once Run Anywhere' WORA 자바의 원칙을 알 수 있다.
* JVM을 통해 실행한다면 장점 - 운영체제 독립적, 자동으로 메모리 관리, 안정적
2. 컴파일하는 방법
위에서도 설명했듯이, 컴파일은 고급언어(인간이 구분하기 쉬운 언어 .java )로 작성된 프로그램을 기계어(바이트코드 .class)로 번역하는 것을 말한다.
1. Eclipse 또는 IntelliJ에서는 확장자가 자바인 파일을 생성하면, 자동으로 .class 파일이 만들어진다. 코드를 작성하는 것이 컴파일하고 있다는 것.
2. cmd에서 컴파일하는 방법
$ javac Hello.java
3. 실행하는 방법
1. IDE에서 run 해주는 방법이 있다.
2. cmd에서 실행하는 방법
$ java Hello
JVM의 구성요소인 Class Loader가 fileName.class 파일을 메모리상의 JVM으로 가져온다.
4. 바이트코드란?
자바 프로그램의 컴파일된 형태. 특정 하드웨어가 아닌 가상 컴퓨터에서 돌아가는 실행 프로그램을 위한 표현법이다.
컴퓨터가 이해할 수 있는 언어가 바이너리코드라면 바이트코드는 가상 머신이 이해할 수 있는 언어이다.
자바 컴파일러에 의해 변환되는 코드의 명령어의 크기가 1byte라서 자바 바이트코드라고 불린다.
Java에서는 컴파일러(javac)에 의해 소스파일(.java)이 컴퓨터가 바로 인식할 수 없는 바이트코드(.class)로 변환된다.
이러한 과정을 거치는 이유는 어떠한 플랫폼에도 종속되지 않고 JVM에 의해 실행될 수 있도록 하기 위함이다.
바이트코드에서 부수적으로 나오는 장점이 Groovy나 Scala, Clojure, Kotlin과 같은 언어들도 JVM이 전용 바이트코드로 컴파일이 된다. 그 언어들로도 JVM을 돌리는 기계에서의 프로그램을 짤 수 있는 것.
5. JIT(Just In Time ) 컴파일러
프로그램을 실제 실행하는 시점(런타임 시)에 기계어로 통번역해주는 방식인 컴파일 기법이다. 동적 번역이라고도 한다.
JIt 컴파일러는 인터프리트 방식과 정적 컴파일 방식을 혼합한 방식이다.
실행 시점에서 인터프리트 방식으로 기계어 코드를 생성하면서 그 코드를 캐싱하여, 같은 함수가 여러 번 불릴 때 매번 기계어 코드를 생성하는 것을 방지한다. 또한 JIT 컴파일러는 바이트코드를 실행하는 OS 및 CPU에 맞춘 2진코드로 일괄 변환하고 나서 실행한다. (이는 Java interpreter 방식의 10배 ~ 20배 정도의 성능을 얻을 수 있다고 한다.)
* 인터프리트 방식 : 실행 중 프로그래밍 언어를 읽어가면서 해당 기능에 대응하는 기계어 코드를 실행
* 정적 컴파일 방식 : 실행하기 전에 프로그램 코드를 기계어로 번역한다.
jit 컴파일러를 사용하는 jvm은 내부적으로 해당 메서드가 얼마나 자주 수행되는지 체크하고 일정 정도를 넘을 때만 컴파일을 수행
6. JVM 구성요소
- Method 영역 : JVM이 시작될 때 생성되는 공간으로 바이트코드가 이 영역에 저장된다.
- (클래스 정보, 변수 정보, static으로 선언한 변수가 저장되고 모든 스레드가 공유하는 영역)
- Class Loader 가 클래스 파일을 읽어오면, 클래스 정보를 파싱해서 Method Area에 저장
- Heap 영역 : 동적으로 생성된 객체가 저장되는 영역으로 GC(Garbage Collector)의 대상이 되는 공간이다.
- 즉 new 연산을 통하여 동적으로 생성된 인스턴스 변수가 저장된다. (클래스의 객체, 배열 등) 이렇게 생성된 변수는 해당 객체가 소멸되기 전이나, GC가 정리하기 전까지는 Heap 영역에 남아있다. 쉽게 소멸되는 데이터 아님
- Heap영역은 효율적인 GC을 위해서 세부적으로는 5가지 영역으로 나뉜다.
- stack 영역 : 지역변수나 메서드의 매개변수, 임시적으로 사용되는 변수, 메서드의 정보가 저장되는 영역이다.
- 지역변수나 매개변수의 특성상, 해당 메서드의 호출이 종료되면 이 안에 선언된 변수들은 스택에서 제거된다. 그러므로 주로 금방 사용되고 금방 사용이 끝나는 데이터가 저장되는 영역이다.
Reference Type 이 실행될 때마다 스택에 쌓여 넣었다 뺐다 하면 비효율적이므로, 스택이 아닌 힙 영역에 진짜 메모리를 저장하고 그 메모리를 참조하는 주소를 stack 영역에 저장한다.
- PC Register : program counter 스레드가 어떤 부분을 어떤 명령어로 수행할지 저장하는 공간. 즉 스레드가 시작될 때 생성되며, 현재 수행 중인 JVM 명령어 주소를 저장하는 공간이다.
- 각 스레드는 메서드를 실행하고 있고, pc는 그 메서드 안에서 몇 번째 줄을 실행해야 하는지 나타내는 역할
- Native Method Stack : JAVA가 아닌 다른 언어로 작성된 코드를 위한 공간이다.
- 즉, JNI(Java Native Interface)를 통해 호출하는 C/C++ 등의 코드를 수행하기 위한 공간이다.
- 자바 프로그램이 컴파일되어 생성되는, 바이트코드가 아닌, 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행시키는 영역
- Execution Engine : 로드된 클래스 파일의 바이트코드를 실행하는 엔진.
- Runtime Data Area에 로딩된 클래스 파일이 Execution Engine을 통해 해석된다.
- 바이트코드를 실행시키기 위해서는, 컴퓨터가 아는 기계어로 바꾸는 작업이 필요하다.
- interpreter의 단점을 해결하기 위한 방법이 Jit
* 이렇게 기계어로 해석된 것들이 런타임 데이터 영역에 배치되어 스레드 동기화나 가비지 컬렉션을 수행하게 된다.
- JNI : jvm에 의해 실행되는 코드 중 네이티브로 실행하는 것이 있다면 해당 네이티브 코드를 호출하거나 호출될 수 있도록 만든 일종의 프레임 워크
- Native Method Library : 네이티브 메소드 실행에 필요한 라이브러리
* 자바는 JVM 만들 때 register대신 왜 stack을 썼을까?
디바이스마다 register의 수준이 다르기 때문에, register 기반으로 하게 된다면 구현을 강요하게 된다. 따라서 stack을 쓰면 계산과정은 귀찮고 복잡해질 수 있더라도 하드웨어의 스펙에 최소한의 관여만 하게 되므로 stack을 사용하게 되었다.
7. JDK와 JRE의 차이
- JDK(Java Development Kit) : 자바 개발 도구. JDK는 JRE를 포함한다. 추가해서 JDK에는 개발에 필요한 도구들 (jdac, jdb, jar 등)을 제공한다. 이처럼 최종 사용자가 아닌 개발자를 위한 기능도 같이 탑재를 하고 있다.
- JRE(Java Runtime Environment) : 자바 런타임 환경, 자바로 만들어진 프로그램을 구동하는데 필요한 실행환경을 제공한다(java, javaw, libaray 등). 자바 개발자가 아닌 자바를 사용하는 일반 사용자가 자바가 동작할 수 있게 자신의 컴퓨터에 설치해야 하는 프로그램이다.
참조
https://www.youtube.com/watch?v=AWXPnMDZ9I0
https://www.youtube.com/watch?v=UzaGOXKVhwU&t=742s
https://www.youtube.com/watch?v=9V0rdrm59X4
https://www.youtube.com/watch?v=VvVruEDCSSY
https://ko.wikipedia.org/wiki/JIT_컴파일