Java와 JVM(Java Virtual Machine)

Java와 JVM(Java Virtual Machine)

JavaJVM(Java Virtual Machine)에 대한 정리

 

📕 Java

 

최초 고안자는 제임스 아서 고슬링(James Arthur Gosling)이다.

썬 마이크로시스템즈에서 개발되었고,

현재는 개발사가 Oracle에 인수되어 Oracle의 소유가 된 언어이다.

이 언어로 작성된 프로그램은 정말 너무 많다.

 

당장 생각나는 것만 해도 우리가 개발할 때 쓰는 인텔리제이가 있고,

마인크래프트도 바로 이 자바로 작성되었다.

그리고 구글이 인텔리제이를 활용해 안드로이드 스튜디오를 만들었다.

자바의 사상은 한번 작성되면, 어디서든 동작한다. (Write once, Run anywhere)이다.

이 사상에 의해 JVM(Java Virtual Machine)이라는 굉장한 아키텍처가 등장했다고 봐도 무방할 것이다.

자바의 가장 큰 특징은 플랫폼에 독립적이라는 것이다.

JVM기반으로 동작하기 때문에 해당 플랫폼에 JVM만 설치되어 있다면

그 어떤 플랫폼에서 작성되었던 언어라도 똑같이 실행시킬 수가 있다.

그리고 또 다른 특징으로는 자바는 객체지향 프로그래밍(Object-oriented programming)에 최적화된 언어이다.

그래서 원활한 협업과 유지보수의 용이성을 위해 엔터프라이즈급 환경에서 많이 사용된다.

 

📕 에디션


  1. Java SE (Java Standard Edition / J2SE)
    • 우리가 가장 많이 사용하는 표준 에디션이다.
  2. Jakarta EE (Java Enterprise Edition / J2EE)
    • 기업용 에디션이다.
    • DBCP, Message System, Load Balancing 등의 화려한 기능을 탑재하고 있다.
  3. Java ME (Java Micro / J2ME)
    • 임베디드 시스템 환경에 특화된 에디션이다.
  4. JavaFX
    • 흔히 Swing으로 알려진 Java GUI 라이브러리가 진화한 것이 이 녀석이다.

 

📕 버전


자잘한 것 제외하고 굵직하거나 자주 사용되는 버전만 부분만 정리해보자면

  • JDK 1.0a2
    • 1995.05.23 자바가 공식적으로 태어난 날이자 버전
  • J2SE 1.4
    • 단정문, 정규표현식, IPv6 지원, Non-Blocking IO, XML 지원, Java Web Start 등이 추가됨
  • Java SE 6 (1.6)
    • 이때부터 버전 표기가 J2SE(Java 2 Standard Edition)에서 Java SE로 바뀌었다
    • 스크립트 언어 지원, JDBC 4.0, 자바 컴파일러 API 등이 추가되었다
    • 스크립트 언어 지원과 함께 Rhino JavaScript Engine이 탑재되기 시작했다
  • Java SE 8 (1.8 LTS)
    • 람다 표현식, Rhino -> Nashorn Engine으로 변경, Annotation on java Types, 정수형 타입의 음수 계산 지원
    • 새로운 날짜와 시간 API(LocalDate 계열), Stream API 등 추가
    • 공식적으로 32비트를 지원하는 마지막 Java 버전이다
    • 2021.01 기준 | 우리나라에서 가장 많이 사용하는 버전
  • Java SE 11 (11 LTS)
    • 9와 10 버전에서 정말 많은 기능이 추가되었으나 크리티컬 한 이슈가 많았다. 이를 대부분 해결한 버전
    • 우리나라는 자바 8에서 11로 넘어오는 추세이다
    • JShell 추가, 선행 컴파일 베타 버전 추가, 구조적 불변 컬렉션 추가, 통합 로깅 지원, HTTP/2 지원
    • private interface method 가능, HTML5 Javadoc 추가, UTF-8 지원
    • Deprecated API 삭제, 64비트 지원, var를 이용한 타입 추론 지원, 병렬 처리 GC, 개별 스레드
    • JDK Repository 통합, JVM Heap memory 공유, JIT 컴파일러 추가
    • 2021년 1월부터 Oracle JDK는 유료버전으로 전환, OpenJDK기반의 서드파티는 개인/기업 무료
    • OpenJDK 기반의 서드파티로 Azul Systems에서 개발한 Zulu JDK가 있다

 

📕 자바의 장점


  1. 레퍼런스, 특히 한글 레퍼런스가 풍부하다
    • API를 참고하되, 막히는 부분이 있을 시 구글링 하면 대부분의 문제와 해결방법들이 나온다.
    • 그만큼 많은 개발자들이 자바로 개발을 했고 노하우가 축적되어있는 상태이다
  2. 생산성 대비 안정성이 좋다
    • 타 언어들에 비해 생산성이 좋다고 보기는 힘들겠으나 깐깐한 예외처리 덕분에 안정성이 매우 뛰어나다
    • 원초적인 객체지향 프로그래밍 언어 중 한 개이기 때문에 엔터프라이즈급 환경에서 협업하기 매우 좋은 언어
  3. 플랫폼에 독립적이다
    • 이는 자바뿐만 아니라 JVM기반의 모든 언어가 갖는 특징이기도 하다
    • JVM기반으로 작동하기 때문에 플랫폼(OS와 같은)에 완벽하게 독립적인 코드 생산이 가능하다
    • 아마 가장 큰 장점이 아닐까 싶음
  4. 가비지 컬렉션
    • 가비지 컬렉션(GC) - 메모리(RAM) 관리를 자동으로 해준다

 

📕 자바의 단점


  1. 속도가 느리다
    • 첫 실행 시 JVM이 완벽하게 로딩되어야 하기 때문에 오버헤드가 발생하는 부분이 있다
      • 하드웨어의 엄청난 발달로 현재는 거의 무의미함
  2. 코드가 장황하다
    • 가독성이 나쁜 편은 아닌데 좋다고 보기도 어렵다
    • 코드가 정말 장황하다
      • 예를 들자면 자바로 30줄 작성해서 만들 코드면 스칼라는 3줄 정도만에 완성할 수도 있다
  3. 깐깐한 예외 처리
    • 더럽고 지저분한 try-catch 지옥
    • 예외 처리를 직접 해주지 않으면 컴파일 에러가 떠버린다 (장점일수도 있음)

 

📕 JVM(Java Virtual Machine)


직역하자면 자바 가상 머신이다.

자바로 작성된 코드는 .java의 확장자를 가지며 이 소스 파일은 자바 컴파일러에 의해

바이트 코드(byte code)로 컴파일된다.

그리고 이 바이트 코드는 .class 확장자를 갖고 클래스 파일이라고 부른다.

이 클래스 파일과 해당 플랫폼에 종속된 JVM만 설치되어 있다면 자바로 작성한 프로그램은

그 어떤 환경에서라도 동작할 수 있다.

JDK(Java Development Kit)를 설치하면 JDK안에 JRE(Java Runtime Environment)가 포함되어있으며,

이 JRE안에 JVM이 포함되어 설치된다.

이름을 해석하면 JDK는 자바 개발을 위한 킷, JRE는 자바 실행 환경이다.

그래서 자바로 개발을 하려면 JDK를 필수로 설치해야 하며,

자바로 작성된 프로그램만 실행시키고 싶다면 JRE만 따로 설치하면 된다.

 

📕 Java Virtual Machine Architecture


 

클래스 파일을 가지고 JVM이 동작하는 구조를 알아보자.

먼저 그림을 보자.

중요한 포인트는 JVM은 우리가 사용하는 컴퓨터에서 메모리(RAM)를 사용할 수 있게끔

일정 메모리를 할당받은 프로세스라는 것이다.

여기서 프로세스에 대한 개념이 약간 필요한데, 우리가 사용하는 모든 프로그램은

컴퓨터의 메모리에서 일정한 공간을 할당받아야만 동작할 수 있다.

그리고 클래스 파일에 메모리를 할당시켜주는 것이 JVM이 하는 가장 중요한 일이라고 할 수 있겠다.

한마디로 클래스 파일이 진짜 기계어로 번역되어 실행되기 전에

임시로 적재되고 제어되는 공간이 JVM이다.

그래서 이름이 자바가상머신(Java Virtual Machine)인 것이다.

 

📜 Class Loader Subsystem


자바 컴파일러에 의해 바이트 코드로 변환된 자바 소스파일(클래스 파일)을 실행하려면 JVM에 적재해줘야 한다.

자바 컴파일러에 의해 컴파일 된 클래스 파일이 처음 실행될 때 적재, 연결, 초기화 과정을 거친다.

이 역할을 Class Loader가 담당한다.

 

1. 적재(Loading)


 

Application ClassLoader


시스템의 클래스 파일 경로(class path)에 위치한 모든 클래스 파일을 JVM의 메모리에 적재한다.

 

Extension ClassLoader


jre/lib/ext 경로에 위치한 확장(Extension) 라이브러리를 이 녀석이 적재한다.

WAS가 구동될 때 위의 디렉터리에서 클래스 파일들을 가져오는 것이다.

대표적으로 HttpServlet.class가 있다.

 

Bootstrap ClassLoader


여기서 Bootstrap은 CSS 라이브러리를 말하는 게 아니다.

클래스 로더는 java.lang.ClassLoader에 의해 실행된다.

그렇다면 java.lang.ClassLoader는 대체 누가 실행시킬까?

바로 이 Bootstrap ClassLoader가 실행한다.

 

 

Boot-Strap이란 말 그대로 부츠 뒤에 달린 고리를 뜻하는데,

신발 뒤축에 달린 고리를 집어 들어 자신을 공중에 띄워야 하는 상황을 일컫는다.

전산 쪽에선 이를 자기 참조(Selft-Referential)라고 부른다.

 

운영체제 과목에서는 이런 내용이 나온다.

컴퓨터 전원버튼을 누르면 컴퓨터에 전원이 들어가면서 ROM(비휘발성)으로 구성된 바이오스가 작동한다.

바이오스는 모든 하드웨어 한번를 점검하고 부트스트랩 로더를 실행시키며 제어권을 넘겨준다.

부트스트랩 로더는 하드웨어를 초기화하고 초기화된 하드웨어에 운영체제 커널을 올려준다.

그리고 운영체제 커널로 제어권을 넘긴다.

 

부트스트랩 로더에 의해 실행된 운영체제는 컴퓨터를 제어하며 스케쥴링을 시작한다.

(이 부트스트랩 로더는 C 계열의 Low Level Native Language로 작성되어 있다.)

컴퓨터가 켜져 있는 상태(Runtime)에서 JVM이 실행된다면, 운영체제는 JVM의 부트스트랩 클래스 로더를 실행시켜준다.

그리고 부트스트랩 클래스 로더는 자식 클래스들을 JVM에 적재시키는 것이다.

그래서 부트스트랩이란 대부분의 시스템에서 자기 자신을 최초에 실행해주는 행위를 의미한다.

우선 바로 확인할 수 있는건 WAS 초기화 시 콘솔에 로그가 쭉 뜨는걸 볼 수 있는데

맨 윗줄을 확인해보면 부트스트랩 어쩌고 하는 로그를 확인 해 볼 수 있을것이다.

 

 


클래스 로더에 의해 JVM 메모리에 적재된 클래스 파일들을 연결시키는 부분이다.

이건 확실히는 모르겠고 추상적으로 생각하기로는 아마 상속관계 등을 따져 연결하고 검증한다는 이야기로 해석된다.

초기화를 하기 전에 클래스 파일들이 각자의 코드에 설정된 의존관계대로 모두 연결되고

사용할 수 있는지 검증하는 과정인 듯하다

 

3. 초기화(Initialize)


모든 클래스 파일의 생성자 같은 초기화 로직이 실행되고 메모리를 할당받는다.

또한 static(정적)으로 선언된 모든 변수들을 초기화하고 메모리를 할당한다.

이 정적멤버들은 힙이아닌 메서드영역에 위치한다.

 

📜 Runtime Data Area


JVM이 실행되면서 운영체제로부터 할당받은 메모리영역이다.

JVM의 클래스 로더가 클래스 파일을 적재 한 영역이 바로 이곳이다.

그러니까 컴퓨터의 메모리에서 일부를 JVM이 점유하고 있고,

이 JVM의 내부에 또 실행 데이터 영역(Runtime Data Area)이라는 메모리 영역이 있는 것이다.

결과론적으로 이야기하자면 결국 모두 다 컴퓨터의 메모리를 사용하는 것이라고 볼 수 있겠다.

 

1. 메서드 영역(Method Area)


JVM내의 모든 스레드가 공유하는 자원이다.

그래서 이 메서드 영역은 JVM마다 하나만 존재하는 자원이다.

컴파일된 코드의 정보를 클래스단위로 저장한다.

그리고 static으로 선언된 정적 멤버들은 미리 Interpret 되어 이 영역에 할당된다.

 

2. 힙 영역(Heap Area)


힙 영역 또한 메서드 영역과 마찬가지로 모든 스레드가 공유하는 자원이다.

이 역시 각 JVM당 하나만 존재한다.

이곳엔 메서드 영역에 위치하는 인스턴스 파일의 모든 구성 코드에

실제로 메모리를 할당하여 실제로 실행되고 있는 인스턴스가 위치하는 공간이다.

여기서 인스턴스란 것은 new(생성자)를 통해

메서드 영역에 올려져 있는 클래스 파일을 Interpret 하여

기계어로 번역된 실제 실행 되는 파일을 말한다.

그래서 이 메서드 영역을 이 힙 영역의 논리적 부분(Logical Part)라고도 부른다.

힙 영역물리적인 부분(Physical Part)이라고 볼 수 있겠다.

또한 힙 영역에 위치하게 된 인스턴스는 스택 영역에서 참조가 되지 않을 때에서야

비로소 가비지 컬렉터(Garbage Collector)에 의해 제거된다.

 

3. 스택 영역(Stack Area)


스택 영역은 각 스레드마다 독자적으로 하나씩 존재한다.

그러니까 공유되는 자원이 아니다.

스택과 힙이 다른 점은 힙에는 객체 단위의 인스턴스가 적재되는 것이고,

스택에는 힙에 위치한 이 인스턴스를 참조하는 요소들이 적재된다는 것이다.

이 스택은 이름 그대로 스택 구조로 되어있어 메서드가 실행되면 push

끝나면 pop 되는 식으로 동작한다.

 

4. 프로그램 카운터(Program Count Register)


운영체제 과목에서 나오는 개념인데, 각 스레드에 한 개씩 존재하며 이들은 Runtime 중에 자신이 속한 스레드의

현재 작업을 저장하고 다음에 해야 할 일을 가리키는 역할을 한다.

5. Native Method Stack


자바 스레드와 네이티브 코드(C 같은)로 작성된 코드를 연결해주는 역할을 한다.

 

📜 실행 엔진(Excution Engine)


자바 컴파일러가 변환한 바이트 코드가 실제로 실행되는 공간이다.

Runtime Data Area에 적재될 기계어들을 이곳에서 Interpret 한다.

1. 인터프리터(Interpreter)


인터프리터는 다들 아는 대로 한 줄 읽고 해석하기를 반복하는 프로그램이다.

여기서는 바이트 코드를 한 줄 읽어 기계어로 반환하는 식으로 동작할 것이다.

2. Just In Time Compiler(JIT)


자바는 자바 코드를 바이트코드로 한번 컴파일한 후 이 바이트 코드를 또다시 기계어로 인터프리팅한다.

이를 JIT라고 부르며 이 방식은 정적 번역(C와 같은 컴파일), 동적 번역(Python과 같은 인터프리팅)을 모두 사용하는 방식을 말한다.

그래서 자바는 타 언어에 비해 컴파일을 두 번 하므로 (자바 코드 -> 바이트코드 -> 기계어) 타 언어들보다 속도가 느릴 수밖에 없다.

대신 플랫폼에 독립적이라는 트레이드 오프(trade-off)가 있다.

 

자바는 이 느린 속도를 해결하기 위해 CPU의 캐시 메모리를 사용하는데

바이트 코드를 한 줄 읽고 기계어로 해석한 후 이를 캐시메모리에 저장해두는 것이다.

그리고 다음에 캐시된 기계어를 재사용하는 식으로 동작한다.

 

다만 캐시는 굉장히 비싼 자원이다.

클래스 파일이 위의 모든 과정을 거쳐 기계어로 번역되고 메모리를 할당받으면

OS가 이를 인식하고 CPU가 연산하여 처리해준다.

그리고 우리는 클래스 파일의 처리 결과를 볼 수 있게 된다.

 


© 2022. All rights reserved.