자바 가상 기계 (Java Virtual Machine)
목차 자바 가상 기계 개요(Java virtual machine) 바이트코드 자바 스택 및 메쏘드 호출 JVM 기술 쓰레기 수집(Garbage collection) JVM 구현 KVM 및 휴대전화(Mobile Phone)
자바 가상 기계 개요
자바 가상 기계 바이트코드 실행 환경 지금까지는 소프트웨어적 접근이 일반적임 JVM에 대한 명세 [Sun] 지금까지는 소프트웨어적 접근이 일반적임 자바 프로그램과 하위 플랫폼 사이의 소프트웨어 계층 바이트코드 명령어 실행을 위해 JVM의 몇 단계가 필요함
JVM 개요 스택 기계(Stack machine) 타입이 있는 32-비트 값 오퍼랜드 스택(Operand stack) 계산을 위한 스택 지역 변수(Local variables) 계산 결과를 위한 임시 기억장소 타입이 있는 32-비트 값 구현 종속적이지 않으며 타입이 있는 값
JVM 개요 다중 쓰레드 기계(Multi-threading machine) 객체-지향 지원 심볼 참조(Symbolic reference) 실행 시간 바인딩
JVM 구조 The Java Virtual Machine Basic Java API Basic Java Class Basic Handler Class Memory (Exception, Basic Loader Manager Thread) Java Parser Class Verifier Method Area Interpreter Basic Native (Execution Engine) Native Method Method Linker
JVM 시작 “java App hello everyone” 시작 과정 App 클래스의 main() 메쏘드가 시작점 나머지 인자들은 main()의 String” 매개변수에 전달된다. 시작 과정 실행 환경을 설정하고 초기화 시스템으로부터 힙을 할당 “Object”, “Class”, “String”, “Thread” 등의 클래스 적재 프로그램 시작 클래스 적재 main() 메쏘드 호출
클래스 로더(Class Loader) 동적 클래스 적재(Dynamic, on-demand loading) 클래스가 처음 참조될 때 시스템에 적재된다 클래스 파일을 메모리에 바이트 스트림으로 읽는다. 적재된 클래스 내의 심볼 참조를 링크한다. 플랫폼 종속을 없애기 위해 주소를 사용하지 않고 심볼 참조만 사용 Java 클래스 ClassLoader 클래스 로더를 사용자가 정의 가능 자신만의 적재 정책을 만들 수 있다
연결(Linking) 검증(Verification) 준비(Preparation) 해결(Resolution) 적재된 클래스의 구조적 정확성을 검사한다. 준비(Preparation) 클래스의 정적 필드를 생성하고 0으로 초기화한다. 해결(Resolution) 심볼 참조의 유효성을 검사하고 이들을 직접 참조(direct reference)로 대치한다.
클래스 검증기(Class Verifier) 왜 클래스 검증이 필요한가? 네트워크를 통한 안전하지 못한 클래스로부터 시스템 보호 클래스 파일의 무결성 검증 클래스 파일이 JVM 명세의 제약 조건을 만족하는지 검사 스택과 변수 등의 비정상적 사용을 찾기 위해 실행 전에 실행 과정 시뮬레이션
4 단계 검증 과정 패스 1 클래스 파일 포맷을 만족하는지 검사 패스 2 바이트코드를 보지 않고 클래스 파일이 의미 있는지 검사 모든 클래스는 수퍼클래스가 있어야 한다. 최종 클래스(final class)는 상속될 수 없다. 패스 3 (바이트코드 검증) 타입-수준 자료흐름 분석으로 바이트코드의 적절한 수행 검사 지역 혹은 필드 변수의 올바른 사용, 메쏘드의 올바른 호출 바이트코드의 올바른 오퍼랜드 사용 오퍼랜드 스택의 오버플로우/언더플로우 검사 패스 4(동적 링킹의 일부) 명령어에 의한 참조(reference)가 유효한지 검사
실행 엔진(Execution Engine) JVM의 핵심부 바이트코드 명령어의 꺼내오기(fetch), 해독(decode), 실행(execute) 다른 부분의 동작을 촉발시킨다. 소프트웨어 혹은 하드웨어 구현 소프트웨어 빠른 해석기 구현 Just-in-Time 컴파일러 하드웨어 Java 프로세서
메모리 관리자(Memory Manager) 메모리 구성 메쏘드 영역(method area) 힙(heap) 스택(stack) 메모리 관리 메모리 할당 프로그램 요청(new)에 따라 힙에 객체 할당 JVM 실행에 따른 메모리 할당 쓰레기 회수 미사용 객체의 자동 회수
실행 시간 데이터 영역 메쏘드 영역 실행시간 상수 풀 Unix의 Code/text segment와 비슷한 역할 적재된 클래스에 대한 모든 정보가 저장된다 모든 쓰레드가 공유 실행시간 상수 풀 클래스의 심볼 테이블로 메쏘드 영역에 할당 동적 연결의 핵심
실행 시간 데이터 영역 pc 레지스터 자바 스택 힙 바이트코드 명령어의 주소로 쓰레드 당 하나의 pc 쓰레드 당 하나의 자바 스택 메쏘드 프레임을 위한 기억공간 힙 객체와 배열을 위한 기억공간 쓰레기 수집 모든 쓰레드가 공유하는 기억 공간
예외 관리자(Exception Manager) Java의 예외 처리 메커니즘 프로그래머가 예외 처리를 제어할 수 있다. try – catch – finally 각 메쏘드는 catch 블록들을 나열하는 예외 테이블을 포함 예외 발생하면 예외 관리자가 예외를 처리하기 위해 에외 테이블을 검색한다. 제어 이전을 위한 또 하나의 방법을 제공
네이티브 메쏘드 연결 네이티브 메쏘드(Native Method) JNI (Java Native Interface) C나 C++ 같은 언어로 구현된 메쏘드 하드웨어에 대한 접근 성능 개선 e.g.) MPEG 해독 DLL 같은 라이브러리 JNI (Java Native Interface) Java 시스템과 네이티브 메쏘드 사이의 표준 인터페이스
바이트코드(Bytecode)
JVM의 데이터 타입 기초 타입(Primitive types) 참조 타입(Reference types) 정수 관련 타입 byte(8 bits), short(16 bits), int(32 bits), long(64 bits), char(16 bits, UNICODE) 부동소수점 : float(32 bits), double(64 bits) boolean : true /false returnAddress : 바이트코드 명령어 주소 참조 타입(Reference types) 클래스(class) 배열(array) 인터페이스9interface)
바이트코드 명령어 8-비트 연산코드를 갖는 202개 명령어 스택 기반 실행 오퍼랜드 타입 명시 복잡한 명령어들 오퍼랜드 스택을 사용 명령어 실행 전 오퍼랜드들을 스택에 적재하고 결과 값도 스택에 적재 레지스터는 없고 대신에 지역변수 사용 오퍼랜드 타입 명시 iadd : 정수 덧셈 복잡한 명령어들 메모리 할당 모니터/ 쓰레드 동기화 메쏘드 호출
바이트코드 명령어 카테고리
바이트코드 명령어 타입 타입 힌트 iadd, isub, istore,... 실행 전 타입 검사 가능 보다 안전한 시스템 가능 타입 접두사 없는 명령어 pop, dup, invokevirtual, …
예: 자바 코드 static int factorial(int n) { int res; for (res = 1; n > 0; n--) res = res * n; return res; }
예: 바이트코드 0: iconst_1 // push 1 1: istore_1 // store it in register 1 (변수 res) 2: iload_0 // push register 0 (매개변수 n) 3: ifle 14 // if negative or null, goto PC 14 6: iload_1 // push register 1 (res) 7: iload_0 // push register 0 (n) 8: imul // mutiply the two integers at top of stack 9: istore_1 // pop result and store it in register 1 (res) 10: iinc 0, 01 // decrement register 0 (n) by 1 11: goto 2 // goto PC 2 14: iload_1 // load register 1 (res) 15: ireturn // return its value to caller
명령어 및 타입 접두사
자바 스택 및 메쏘드 호출
자바 스택 및 스택 프레임 쓰레드 당 하나의 자바 스택 메쏘드 호출 당 하나의 스택 프레임 자바 스택 위에 스택 프레임들이 쌓인다. 메쏘드 호출 당 하나의 스택 프레임 오퍼랜드 스택(Operand stack) 지역 변수(Local variable) 프레임 크기는 메쏘드가 컴파일될 때 결정된다.
쓰레드와 자바 스택 Thread 1 Thread 2 Thread 3 Thread 1 Thread 2 Thread 3 Stack frame Stack frame Stack frame Thread 2 Thread 3 Stack frame Stack frame Stack frame Thread 3 Stack frame Stack frame Stack frame native method stacks pc registers Java stacks
메쏘드 호출 명령어 invokevirtual invokeinterface invokespecial Invokestatic 객체의 가상(virtual, instance) 메쏘드 호출 묵시적 매개변수 : this invokeinterface 참조 타입이 인터페이스일 때 객체의 가상 메쏘드 호출 invokespecial 실체 초기화 메쏘드, 전용 메쏘드, 수퍼클래스 메쏘드 호출 Invokestatic 클래스의 정적 메쏘드(static method) 호출
메쏘드 호출과 this 메쏘드 호출의 구현 메쏘드 선언의 구현 대상 객체(target object)는 0-번째 매개변수로 전달 x.m(…); m(x, …); 메쏘드 선언의 구현 this는 0-번째 형식 매개변수 이름 m(…) { … } m(this, …) { … }
메쏘드 호출과 프레임 메쏘드 m 호출 전 메쏘드 m 호출 후 호출의 대상 객체(target object) 주소를 스택에 넣는다. 실 매개변수 값들을 스택에 넣는다. 메쏘드 m 호출 후 m을 위한 새로운 프레임을 자바 스택에 넣는다 호출자의 오퍼랜드 스택으로부터 m의 실 매개변수를 빼낸다. m의 지역변수(형식 매개변수)에 실 매개변수 값을 복사한다. m 의 다른 지역 변수를 초기화한다.
메쏘드 호출 예 호출 전 호출 후 method2 method1 b b int method1 () { a …. a Other local variables of method 2 b b int method1 () { …. method2(a, b); } a a this this ….. ….. method1 Local variables Local variables 호출 전 호출 후
invokevirtual int add12and13() { return addTwo(12, 13); } Method int add12and13 0 aload_0 // Push local variable 0 (this) 1 bipush 12 // Push int constant 12 3 bipush 13 // Push int constant 13 5 invokevirtual #4 // Method Example.addtwo(II)I 8 ireturn // Return int on top of operand stack // it is the int result of addTwo()
invokestatic int add12and13() { return addTwoStatic(12, 13); } Method int add12and13 0 bipush 12 2 bipush 13 4 invokestatic #3 // Method Example.addTwoStatic(II)I 7 ireturn
메쏘드 테이블(Method Table) 클래스 당 자료 구조 메쏘드 호출 구현 상속과 재정의 클래스의 메쏘드들에 대한 포인터를 갖는 테이블 메쏘드 호출을 위한 자료구조 각 객체는 해당 클래스의 메쏘드 테이블 포인터를 갖는다. 메쏘드 호출 구현 객체로부터 메쏘드 테이블 포인터를 얻는다. 메쏘드 아이디를 이용하여 해당 메쏘드의 주소를 얻는다. 그 주소로 점프한다. 상속과 재정의 서브클래스는 수퍼클래스의 메쏘드 테이블을 상속 받는다. 메쏘드가 재정의되면 상응하는 메쏘드 테이블 엔트리도 갱신된다.
클래스와 메쏘드 테이블 Method table of A Method table of B class A { int foo() {…} void bar() {…} }; class B extends A { float boo() {…} address of foo() address of bar() 1 Method table of A address of foo() address of bar() 1 address of boo() 2 Method table of B
메쏘드 호출 예 Get the method table of A foo’s id is 0 Get the address of 0th method Jump to the address A a = new A(); a.foo(); …. a = new B(); a.foo(); a.bar(); Get the method table of B foo’s id is 0 Get the address of 0th method Jump to the address Get the method table of B bar’s id is 1 Get the address of 1st method Jump to the address
예외 JVM은 비정상적 실행 상태를 탐지한다. throw 문 예외 클래스 Array index out of bounds Load and link error Run out of memory throw 문 예외를 발생시킨다. 예외 클래스 Exception 클래스의 서브클래스로 정의 모든 예외는 예외 클래스의 객체로 throw된다.
예외 처리 예외 관리자 정상적인 실행 흐름은 catch 절의 예외 처리기와 분리된다. 예외 발생 지점의 문맥(context)를 저장한다. 예외를 처리할 수 있는 가장 가까운 catch 절을 찾는다. 발생된 예외의 타입과 예외 테이블의 catch 절에 선언된 타입을 비교한다. 찾으면 예외(에 대한 참조)를 오퍼랜드 스택에 넣고 catch 절을 실행한다. 찾지 못하면 해당 자바 쓰레드를 종료한다. 정상적인 실행 흐름은 catch 절의 예외 처리기와 분리된다.
JVM의 try-catch Method void catchOne() 0 aload_0 // Beginning of try block 1 invokevirtual #6 // Method Example.tryItOut()V 4 return // End of try block; normal return 5 astore_1 // Store thrown value in local variable 1 6 aload_0 // Push this 7 aload_1 // Push thrown value 8 invokevirtual #5 // Invoke handler method: // Example.handleExc(LTestExc;)V 11 return // Return after handling TestExc Exception table: From To Target Type 0 4 5 Class TestExc void catchOne() { try { tryItOut(); } catch (TestExc e) { handleExc(e); }
JVM 관련 기술
JVM의 도전적인 주제 실행 속도 메모리 관리 해석 오버헤드(Interpretation overhead) 가상 호출 대 직접 호출(Virtual call vs. direct call) 쓰레드 동기화(Thread synchronization) 예외 관리(Exception management) 메모리 관리 메모리 관리의 안전성 정교한 쓰레기 수집
메모리 관리 객체 할당(Object allocation) 쓰레기 수집 할당 오버헤드를 줄이도 향후 할당 질을 높인다. 조각화(Fragmentation), 캐쉬 성능 쓰레기 수집 GC의 두 단계 죽은 객체 탐지 프로그램에서 참조되지 않는 객체 찾기 정확한 탐지 대 보수적 탐지 죽은 객체 회수 조각화 제거 방법
JVM 구현 사례들 Kaffe SUN HotSpot JVM Most famous open-source JVM Support multiple platforms Poor performance SUN HotSpot JVM Pioneer of feedback-directed dynamic compiler Based on SELF compiler from Stanford SNU LaTTe JVM with classical JIT compiler Outperform SUN HotSpot Being integrated with Kaffe IBM Jalapeno JVM Research JVM implemented in Java Feedback-directed dynamic compiler JVM itself is dynamically translated.
KVM 및 이동전화
KVM (K Virtual Machine) J2ME를 위한 소형 JVM 이동전화, 모바일 인터넷 장치 처음부터 작성 작은 크기를 위해 최적화 다른 플랫폼에 쉽게 이식 가능 정규 JVM과 차이점 부동 소수점 없음 다차원 배열 없음 사전 검증(Preverification) JNI(Java Native Interface) 없음 제한된 클래스 적재 기능 단일 프로세서 가정
무선 환경 통신으로부터 정보 사용으로 이전 이동전화는 응용 프로그램 내려 받기나 업그레이드 를 지원하는 새로운 소프트웨어 환경이 필요하다. 고도의 보안, 튼튼함, 신뢰성 등이 중요하다. Nowadays mobile phones’ role changes from the traditional communication to as an information access device. This change brings up new challenges in the SW system of phones. Mobile phones need new SW environment supporting application downloading and upgrade. As the SW system evolves from a closed system to an open one, high security, robustness, reliability becomes critical. But native update approach is not suitable, since it is open to malicious attacks so requires strict supervision.
JVM 이동전화를 위한 이상적인 플랫폼 가장 인기 있는 VM 개방형 환경에서 이동전화를 보호할 수 있는 안전한 방법 구조 중립적이면 이식성이 좋다. 튼튼하고 안전하다(Robust & Secure) 동적 클래스 로딩(Dynamic Loading) 이동전화를 위한 이상적인 플랫폼 Java is perfect in such a situation and shows its advantages once again. Java is the most popular VM approach and a safe way for operators to protect phones from malicious cracking while keeping an open environment. It is architecture neutral and highly portable, which is required in the current diverse market including GSM, CDMA and next generation services. The fact that Java is also robust, secure, and dynamic makes Java the ideal platform for phones.
Java-가능 이동전화 여러 native 응용 중의 하나 전화 기능에 대한 제한된 접근 허용 Main UI SMS Addr. Book Other … Applications WAP Java Midlets KVM There are several Java technology-enabled phones in market. More than 3 million handsets have been sold so far and the number is expected to reach 20 million by the end of 2001. Their adoption of Java is limited, though, in the sense that Java is not well integrated with other parts of the phone system. Java is just one of those native applications. First, the profile used in phones are MIDP. But the current MIDP can control very limited part of a phone. Since MIDP is designed to support not only phones but also 2way pagers and PDAs, it has little support of phone-specific features. For example, You can’t write an address book from which you can directly make a call. Second, due to limited phone OS or runtime environment we cannot take full advantage of Java. This can be represented like this diagram. As you can see, most applications are written in native even in Java-enabled phones. And Java can access and control very limited part of a phone. Phone OS Phone HW