가상 머신 [JVM] 바이트 코드의 실행 엔진

다음 개념 모델은 전형적인 스택 프레임 구조 (각 스레드가 자신의 스택을 갖고, 즉, 스레드 스택 전용 임)이다.

일반적인 스택 프레임 구조
  • 로컬 변수 테이블에
    저장된 메소드 파라미터 및 메소드 정의 내부 로컬 변수 . 컴파일시에, 클래스 코드 파일 속성 과정에 필요한 데이터 항목 할당의 max_locals의 로컬 변수 테이블의 최대 용량을 결정합니다. (특정 개체를 포함 아니라 변수). </ BR>
    최소 단위로 가변 내부 홈 (가변 슬롯)의 로컬 변수 테이블. 를 들어 byte, char, float, int, short, boolean, reference, returnAddress등 데이터 타입하게는 32 이상의 비트에, 각각의 로컬 변수 슬롯 이중 차지하고, 2 개의 64 비트 길이의 데이터 형식은 두 개의 슬롯이 필요 </ BR>
    과정에서 실행될 때 가상 컴퓨터 실행 인스턴스 메소드 (아닌 경우, 변수 패러미터의리스트를 형성하기 위해 로컬 변수 변수 값이다 static법), 슬롯 0의 로컬 변수 테이블 인덱스 객체 인스턴스가 속하는 송신하는 기본 방법 상기 방법에 의해 참조 this암시 파라미터를 액세스. </ BR>
    바이트 코드 카운터 PC의 현재 값이 변수의 범위를 벗어나는 경우, 로컬 변수 테이블 슬롯은 재사용, 다른 변수에 대응하는 변수는 슬롯 번호를 재사용 할 수있다. 재사용 스택 공간을 절약 할뿐만 아니라 가상 시스템 운영 매개 변수 설정 :( 부작용을 일으킬 수 있습니다 플러스 -verbose:gc는 출력 수 gc) 로그 정보
//--------------------------测试1---------------------------//
public static void main(String[] args){ byte[] placeholder = new byte[64*1000*1000]; System.gc(); } //查看日志,并未回收 [GC (System.gc()) 69437K->63438K(251392K), 0.0012879 secs] [Full GC (System.gc()) 63438K->63277K(251392K), 0.0058505 secs] //------------------------测试2-----------------------------// public static void main(String[] args) { { byte[] placeholder = new byte[64 * 1000 * 1000]; } System.gc(); } //查看日志,并未回收 [GC (System.gc()) 69437K->63420K(251392K), 0.0011785 secs] [Full GC (System.gc()) 63420K->63277K(251392K), 0.0058676 secs] //------------------------测试3-----------------------------// public static void main(String[] args) { { byte[] placeholder = new byte[64 * 1000 * 1000]; } int a = 0; System.gc(); } //查看日志,回收了 [GC (System.gc()) 69437K->63454K(251392K), 0.0011921 secs] [Full GC (System.gc()) 63454K->777K(251392K), 0.0056915 secs] 

테스트 1에서 System.gc(), 변수는 placeholder시험 2 상기 내부 재순환되지에서 여전히 유효 System.gc()가변 placeholder이상 범위이지만, 그러나 placeholder그렇게 부분 GC 루트의 일부로서, 아직 점유 슬롯 다중화 있었다 변수 테이블은 관련된 남아, 그래서 복구하지 않습니다. 이 협회의 영향은 대부분의 적절한 휴식되지 않았습니다
약간 낮은하지만 방법이 있는지 전면는 많은 양의 메모리를 정의 반면, 오랜 시간이 소요되는 작업 뒤에 몇 가지 코드가 더 이상 실제이 없습니다 널 말이에, 사용되는 변수가 수동으로 설정합니다. </ BR>
또 다른 점은 클래스 변수와는 달리 로컬 변수 디폴트 값이없는 동일한 제제의 존재를 (에만 개질 정적 변수, 인스턴스 변수를 포함하지 않음)이다. 이것은 로컬 변수의 초기 값으로 정의되어야한다 . (지정하지 않음, 컴파일러는 오류가 발생하지).

  • 오퍼랜드 스택
    에는 스택 동작이라고도 오퍼랜드 스택은, 그 스택의 처음이다. 요소 포함한 임의의 Java 데이터 유형 인 것을 특징으로 long하고 double. 32 비트의 데이터 용량의 유형 번호 2 비트 1,64이다. 임의의 시간에 수행에있어서, 오퍼랜드 스택의 최대 깊이는 코드 속성 초과하지 max_stacks설정된 최대 데이터 항목. </ BR>
    메소드가 실행을 시작할 때, 오퍼랜드 스택은 상기 방법의 실행 동안, 스택 / 스택에서 바이트 코드의 각종 지시가 비어있다. 산술 연산을 수행 할 때, 예를 들어, 방법은 스택의 조작에 의해 수행되는 다른 송신 파라미터를 호출 할 때 스택의 조작에 의해 행해진 다. </ BR>는
    예를 들어, 정수 가산 바이트 코드 명령 iadd이 명령이 실행될 때 실행 시간은 스택의 상단에 가장 근접한 스택 동작은 두 가지 것, 두 INT 타입 값으로 두 요소를 가지고 다음 int 값 스택을 추가하고 스택에 추가 한 결과. </ BR>
    개념적 모델에서, 가상 머신 스택 소자로서 2 개의 프레임 스택은 완전히 분리되어 있지만, 두 개의 스택 프레임은 부분적으로 중첩 표시 할 것인가 가장 최적화 된 가상 시스템을 달성하기 위해 다음 스택 프레임하자 상기 방법이 호출되는 데이터의 공통 부분의 전달 파라미터 불필요한 복사를 방지 할 수 있도록, 프레임 부와 오퍼랜드 스택 로컬 변수 테이블 오버랩 상부 스택.

  • 동적 연결은
    각 스택 프레임 스택 프레임 관련 방법의 실행 상수 풀에 대한 참조를 포함한다. 이 동적 연결 방법 호출을 지원하기 위해 참조를 보유하고 있습니다. 메소드의 바이트 코드의 호출 명령 (여기서, "메소드 호출"은 수정 명령이며, 그하지 잘못) 기호 매개 변수로 정수 풀 기준점 방법. 이 기호는 처음 또는 직접 참조로 사용되는 경우, 이러한 변화는 호출에 대한 클래스 로딩 단계의 일부를 인용 할 번역 정적 해상도 . 또 다른 부분은이 부분이라고, 각 작업 중에 직접 참조 변환됩니다 동적 링크를 .

  • 방법은 주소를 반환
    하는 방법을 시작할 때이 방법 중 두 가지 방법이 있습니다 :

  • 정상 완료 출구
    실행 엔진이 방법에 의해 리턴되는 바이트 코드 명령어 호출자 상부 방법과 그 반환 값을 발생 (여부 리턴 값의 종류 및 리턴 명령의 방법에 의해 결정되는 값을 반환)

  • 이상 완료 출구
    예외 방법의 실행 중에 발생하고, 예외가 (이상이 Java 가상 머신의 내부에서 발생 될 수있다 또한, 상기 코드에서 사용될 수있는 방법은 몸에서 처리되지 않고 athrow, 이상 발생의 바이트 코드 명령). </ BR>
    방법은 실제 종료 스택 현재 프레임이므로 출구 때 동작이 수행 될 수있다 : 로컬 변수 테이블 및 오퍼랜드 스택은 호출자의 스택 프레임 피연산자로 상층에있어서, 리턴 값 (있는 경우)를 복구 명령어 다음 메소드 호출 지시를 가리키는 카운터 PC의 값을 조정하는, 스택.

  • 추가 정보 : 표준 외부, 특정 가상 머신의 구현에 따라 달라집니다.


메소드 호출

메소드 호출은 동일한 방법으로 수행없고, 통화의 목적은 호출 방법의 스테이지 판 (즉, 어떤 방법을 호출한다)을 결정한다 . 클래스 파일의 모든 메소드 호출은 상징적 참조를 저장하고, 직접 참조 (실제 런타임 메모리 레이아웃에 주소 입력 방법)은 방법이 아닙니다.

: 가상 머신에서,이 호출 다섯 개 가지 방법 바이트 코드 명령이다
invokestatic상기 정적 메소드 호출 :
invokespecial인스턴스 생성자 호출 <init>방법 및 개인 방법 방법 상속]
invokevirtual: 모든 가상 메소드 호출;
invokeinterface: 메소드 호출 인터페이스 ; 상기 런타임이 인터페이스의 오브젝트를 결정
invokedynamic런타임시에 참조 된 제 동적 메소드 호출 시점 한정자를 파싱하고 상기 방법을 수행.

통화 방법은 구문 분석 통화 및 발신 통화로 나눌 수 있습니다.

  • 해상도 전화
    호출을 실행하기 전에 버전을 확인하는 방법이 있으며, : 해상도 단계에서 클래스로드시는 대상 메소드가 직접 참조로 상징 메서드 호출 참조의 일부가 될 것입니다,이 부분의 전제는 변형 될 수있다 운전 중이 방법의 버전을 호출하는 것은 변경할 수 없습니다. 분석이라고이 메소드를 호출 (또는 호출을 구문 분석).
    Java에서는, 상기 특성 (공지 컴파일러 런타임 불변) 메소드 라인 정적 방법 및 개인 방법. 한 수 있듯이, 명령 및 해당 메서드를 호출 invokestatic하고 invokespecial메서드 호출 지침, 해상도 단계라는 유일한 버전을 확인한다. 비 가상 메소드라고 이러한 방법은 다른 방법과 같은 방법으로 가상 칭한다.
    Java에서는, 비 가상 메소드 이외에 invokestaticinvokespecial외측을 더 포함하는, 방법은 명령어를 호출 할 수있는 final방법. </ BR>
    해상도 통화가 정적 공정해야 완전히 컴파일시에 결정할 수있다, 당신이 직접 참조하는 방법에 대한 기호 참조에 투입 할 수있는 클래스 로더 구문 분석 단계가 완료 될 때까지 지연 동안 다시 실행되지 않습니다.

  • 디스패치 호출
    디스패치 호는 상속, 다형성 중요한 캡슐화 (특히 과부하 및 재 기입)을 알 수있다. 그것의 이해, 더 명확하게 가상 머신이 얼마나 정확한 타겟 방법을 결정하기 위해 수 있습니다.

  • 정적 할당은
    정적 유형과 실제 유형 : 처음 두 개념을 이해합니다.

Human human = new Man();//这里假设Man是Human的子类

상기 코드 : Human변수 스태틱 형 Man변수의 실제 타입.
인수의 정적 유형보다는 판단의 근거로 실제의 형태를 통해 부하에서 가상 머신 (정확하게 컴파일러).

public class StaticDispatch {

    static abstract class Human{} static class Man extends Human{} static class Woman extends Human{} public void sayHello(Human human){ System.out.println("hello, human"); } public void sayHello(Man man){ System.out.println("hello, man"); } public void sayHello(Woman woman){ System.out.println("hello, woman"); } @Test public void test(){ Human man = new Man(); Human woman = new Woman(); StaticDispatch dispatch = new StaticDispatch(); dispatch.sayHello(man); dispatch.sayHello(woman); } } //最终的打印结果如下:(重载时是以静态类型判断的) hello, human hello, human 

컴파일러의 대형 버전이 방법을 결정 할 수 있지만, 많은 경우에이 "만"의 대형 버전 아니지만, 단지 "더 적절한"버전을 확인할 수 있습니다. 이에 대한 주된 이유는 정적, 정적 유형은 이해하고 언어의 규칙을 채택하는 추측 할 수 표시되지 않습니다 문자 입력 된 리터럴 필요가 정의 할 수 없습니다.

public class OverLoad {

        public static void sayHello(Object object){ System.out.println("hello Object"); } public static void sayHello(int a){ System.out.println("hello int"); } public static void sayHello(long a){ System.out.println("hello long"); } public static void sayHello(Character character){ System.out.println("hello Character"); } public static void sayHello(char c){ System.out.println("hello char"); } public static void sayHello(char... c){ System.out.println("hello char..."); } public static void sayHello(Serializable serializable){ System.out.println("hello Serializable"); } @Test public void test(){ sayHello('a'); } } 

이 코드가 인쇄됩니다 hello char;
①됩니다 sayHello(char c)주석, 출력한다 : hello int,
② 계속 sayHello(int a)인쇄를 주석 : hello long,
이유는 두 단계의 문자가 a자동 변환 (주입 하였다> INT-> 장기> 플로트 발생 -> 더블)
계속 ③ sayHello(long a)이 인쇄됩니다, 주석 : hello Character
이유는 캐릭터가 있다는 것입니다 a자동으로 박스되는 Character타입
계속 ④ sayHello(Character character)주석, 인쇄됩니다 : hello Serializable
이유가되어 a자동으로 박스 Character타입 여전히 자동 계속 할 수있는 방법을 찾을 수 없습니다 변환, Character목적을 달성 Serializable인터페이스.
계속 ⑤ sayHello(Serializable serializable)주석, 그것은 인쇄됩니다 : hello Object
낮은 상위 우선 순위를 접근에 대한 여러 부모 클래스 계층 구조에서 상향식 (bottom-up) 검색이있는 경우 그 이유는, 문자 후 부모 클래스 복싱의 변화이다.
계속 ⑥ sayHello(Object object)주석, 그것은 인쇄됩니다 : hello char...
가시 : 가변 인자 과부하 우선 순위가 가장 낮다.

  • 동적 할당
    동작 동안, 파라미터의 실제 형태에 따라 수행되는 방법의 버전을 결정하는 방법. 주로 자바 재 작성에 해당합니다.
public class DynamicDispatch {

        static abstract class Human { abstract void sayHello(); } static class Man extends Human { @Override void sayHello() { System.out.println("man say hello"); } } static class Woman extends Human { @Override void sayHello() { System.out.println("woman say hello"); } } @Test public void test() { Human man = new Man(); Human woman = new Woman(); man.sayHello(); woman.sayHello(); } } //将会打印 man say hello woman say hello 

상술하면, 정적 및 동적 디스패치 디스패치 디스패치 또 다른 메소드 호출 직접 참조의 판정에서 발생할 수있는 다른 촬영 호출 상황 방식이 아닌 하나 또는 모두 정적 할당을 모두 이용한다 및 동적 할당을 사용합니다. 정적 할당을 사용할 때 오버 방법에있어서 재기록하는 동적 디스패치 때 결정하도록 결정하는 데 사용. 즉이되는 정적 형식 매개 변수를 참조 과부하, 실제의 형태 파라미터에서보기를 재 작성 . 여기 파라미터 과부하시 메소드의 파라미터리스트의 파라미터, 다시 쓰기 방법의 발신자를 의미한다.

  • 단일 및 다중 디스패치 디스패치
    파라미터 및 방법받는 방법은 단일 디스패치으로 분할 다중 송출 두 종류 전달할 수있는 몇 가지 경우에 기초하여 상기 할당 된 량에 따라, 상기 방법 변수 라 함.
    자바 언어가 속한 정적 여러 파견, 동적 단일 할당 언어.

  • 가상 머신의 동적 할당

  • 동적 언어를 지원 입력 : TODO


해석 실행 엔진 // TODO 스택 기반의 바이트 코드

그것은 가상 머신의 바이트 코드 명령 방법을 구현하는 방법에 초점을 맞추고 있습니다. 자바 코드의 구현에 많은 Java 가상 머신의 실행 엔진 (컴파일러에 의해 생성 된 네이티브 코드가 실시간으로 수행하기 위해) 여기 토론으로 해석 두 가지 선택, 해석 (인터프리터 실행을 통해) 실행을 컴파일하고있다.

해석
스택 기반 명령어 세트 및 레지스터 - 기반 명령 세트
스택 기반의 인터프리터 실행 과정

저자 : maxwellyue
링크 : HTTPS : //www.jianshu.com/p/3f427d7476b3
출처 : 제인 책

추천

출처www.cnblogs.com/xiaoshen666/p/11258569.html