JVM
- 자바는 WORA("한번 작성해 어디에서나 실행한다")의 원칙을 기반으로 한 언어이다. 1995년 자바가 나오기 이전의 모든 컴퓨터 프로그램은 특정 운영체제에 종속되어 설계되어 있었으며 프로그램 메모리는 전부 개발자가 관리했었어야 했다.
- JVM은 자바 프로그램이 다양한 운영체제와 다양한 기기에서도 실행될 수 있게 하는 Virtual Machine이다 Machine이라고 표현하지만 사실은 소프트웨어이다.
컴파일된 바이너리 코드는 플랫폼에 종속받지 않고 실행시키는 것뿐만 아니라 프로그램 메모리를 관리하고 최적화까지 해주는 소프트웨어이다.
-JVM은 스택 기반 가상 머신이다. 가상 머신은 레지스터 기반과 스택 기반이 존재하는데 둘의 차이는 피연산자를 저장하고 다시 호출하는 메커니즘이 다르다. 스택은 피연산자와 연산 후 결과를 스택에 저장하며
스택 구조라서 PUSH & POP을 사용하며
레지스터 기반은 각 피연산자들의 주소를 개별적으로 기억하기 때문에 메모리에 할당되며 대신 명령어를 직접적으로 접근할 수 있기에 속도가 빠르며 명령어 최적화를 할 수 있다.
자바 애플리케이션을 실행하기 위해 JVM은 자바 클래스 로더(Class Loader)와 자바 실행 엔진(Execution Engine)과 Runtime Data Areas(할당받은 메모리 공간)에 의존한다.
Loding -> Linking -> initalization
클래스 로더란?
우리가 만든 클래스들을 JVM으로 동적으로 로드하는 JRE의 일부입니다.
바이너리 코드를 읽어와 메모리에 적절히 배치하는 역할을 합니다.
JVM이 시작되면 3개의 클래스 로더들이 사용된다
-
부트스트랩 클래스 로더
-
확장 클래스 로더
-
시스템 클래스 로더
부트스트랩 클래스 로더는 <JAVA_HOME>/jre/lib 디렉터리에 위치한 핵심 자바 라이브러리들을 불러들인다.
핵심 JVM의 일부분인 이 클래스 로더는 네이티브 코드로 작성되어 있다.
확장 클래스 로더는 확장 디렉터리(<JAVA_HOME>/jre/lib/ext 또는 java.ext.dirs 시스템 속성에 지정된 기타 디렉터리)에 코드를 로드한다. sun.misc.Launcher$ExtClassLoader 클래스에 의해 구현되어 있다.
java.class.path에서 볼 수 있는 시스템 클래스 로더는 CLASSPATH 환경 변수에 매핑된다. sun.misc.Launcher$AppClassLoader 클래스에 의해 구현되어 있다.
JVM은 클래스 로더 덕분에 파일의 구조나 파일 시스템을 고려하지 않을 수 있으면서 자바 프로그램을 작동시킬 수 있다.
자바 클래스들은 한 번에 모든 클래스가 메모리에 올라가지 않는다. 각 클래스들은 필요할 때 애플리케이션에 올라가게 되며, 이 작업을 클래스 로더가 해주게 된다.
1. java 컴파일러가 java 코드를 해석한 뒤. class파일 형태로 결과를 생성한다.
이 코드는 byte코드이다.
2. 클래스 로더에 의해. class 파일들은 다음 단계를 거친다.
(1) Loading : 클래스 파일에서 패키지 경로 , 클래스의 상속 정보 , 변수 , 생성자 등등 클래스의 관한 정보를 로딩해서 바이너리 데이터로 변경. 그런 뒤 메서드 영역에 저장한다.
Bootstrap , Extension , Application 순으로 앞의 loader가 로딩할 수 없다면 그다음 Loader가 읽어낸다.
(2) Linking : 바이트 코드를 검증하고 필요한 만큼 메모리를 할당한다.
Verify :. class 파일 형식이 유효한지 검사한다.
Prepare : static 변수와 기본 값에 필요한 메모리를 준비한다.
Resolve : 심볼릭 메모리 레퍼런스를 실제 메모리 레퍼런스로 교체한다. 다만 교체될 수도 있으며 사용이 일어날 때 동적으로 교체될 수도 있다.
-Symbolic Reference : 실제 메모리 주소 값이 아닌, 참조하는 대상의 이름으로만 지칭하고 있는 것이다.
(3) Initialization : static block의 초기화 및 static 데이터들을 할당한다.
Execution engine
-클래스 로더가 런타임 데이터 영역에 바이트 코드를 배치시키면 실행 엔진에 의해 실행된다.
바이트 코드는 네이티브 코드가 아니기 때문에 실행 엔진은 네이티브 코드로 변환하는데 2가지 방식이 존재한다.
(1) Interperter
- 인터프리터는 바이트 코드를 Line by Line으로 해석하고 실행시킨다.
한 줄식 해석하기 때문에 속도면에서 느린 편이다.
(2) JIT
-인터프리터의 단점을 보안하기 위한 컴파일러, 인터프리터 방식으로 동작하다가 적절한 시점에 바이트 코드를 전체 컴파일하여 네이티브 코드로 변경하며, 그 이후에는 네이티브 코드를 직접 실행하는 방식입니다.
변환 덴 네이티브 코드는 캐시에 보관하기 때문에 한번 컴파일된 코드는 빠르게 수행됩니다. 하지만 컴파일하는 과정이 추가되었기 때문에 인터프리팅하는 것보다 오랜 시간이 소요됩니다.
(3) Garbage Collector
- Un-referenced Object 들을 제거해주는 가비지 컬렉팅을 수행한다.
Runtime Data Areas
메모리는 다음과 같이 나눌 수 있다.
(heap , method Area)는 전체 공유자원으로 분류하며, (JVM stack, PC, Native Method stack)는 스레드 단위의 자원으로 분류됩니다.
Method area : JVM 메서드 영역은 메타데이터, 상수, runtime pool 및 메서드 코드와 같은 클래스 구조를 저장합니다.
Heap : 모든 객체, 관련 인스턴스 변수 및 배열은 힙에 저장됩니다. 이 메모리는 여러 스레드에서 공유됩니다.
Natice method stack : java가 아닌 다른 언어로 작성된 코드를 위한 공간이다. 기계어로 작성된 프로그램을 실행시키는 영역이다.
JVM 스택 영역 : 지역 변수를 임시 저장하며 부분적인 결과(호출된 메서드의 리턴 값, 연산 시 발생되는 값)입니다. 메서드가 호출 시마다 새 프레임이 작성되고 메서드 호출 프로세스가 완료되면 삭제됩니다.
PC Register : 현재 실행할 부분의 가상 머신 명령어의 주소를 저장하며 각 스레드마다 별도의 PC가 존재합니다.
Thread가 어떠한 부분을 어떤 명령으로 실행해야 할 지에 대한 기록을 하는 부분입니다.
Java가 느린 주요 이유는
1. 동적 Linking : c와 달리 링크는 런타임 시 프로그램이 java로 실행될 때마다 바이트코드를 검증하고 메모리를 할당하기때문입니다
2. 런타임 인터프리터 : 바이트 코드를 네이티브 코드로 변환하면서 런타임 시 java에서 수행됩니다
-최신 버전 java는 성능 병목 현상을 상당 부분 해결했습니다.
'JAVA > [JAVA] 바구니' 카테고리의 다른 글
[JAVA] 예외 처리 (0) | 2021.03.27 |
---|---|
Java의 기본 log : Logger (0) | 2021.02.26 |
[JAVA] RuntimeException (0) | 2020.06.06 |
[JAVA] Exception (0) | 2020.06.06 |
[JAVA] Wrapper Class (0) | 2020.05.25 |
댓글