먼저 두 가지 개념을 명확히 하겠습니다. JVM 인스턴스와 JVM 실행 엔진 인스턴스, JVM 인스턴스는 독립적으로 실행되는 Java 프로그램에 해당하고, JVM 실행 엔진 인스턴스는 사용자가 프로그램을 실행하는 스레드에 해당합니다. 즉, JVM 인스턴스는 프로세스 레벨이고 실행 엔진은 스레드 레벨입니다. JVM 이란 무엇입니까? —JVM 의 라이프 사이클 JVM 인스턴스의 탄생: Java 프로그램을 시작할 때 JVM 인스턴스가 생성되고 publicstaticvoidmain (string [] args) 함수를 가진 모든 클래스가 JVM 인스턴스 실행의 시작점으로 사용될 수 있습니다 이를 위해서는 JavaclassAhelloworld 와 같이 일반적으로 Javaclassahelloworld 를 실행하는 JVM 클래스 이름을 명시적으로 알려야 합니다. 여기서 Java 는 OS 에 SunJava2SDK 를 실행하는 Java 가상 시스템이고 classA 는 JVM 을 실행하는 데 필요한 클래스 이름을 나타냅니다. JVM 인스턴스 실행: main () 은 다른 모든 스레드가 시작되는 프로그램의 초기 스레드의 시작점으로 사용됩니다. JVM 내부에는 데몬 스레드와 비데몬 스레드의 두 가지 스레드가 있습니다. main () 은 비데몬 스레드이고, 데몬 스레드는 일반적으로 JVM 자체에서 사용되며, Java 프로그램은 자신이 만든 스레드가 데몬 스레드임을 나타낼 수도 있습니다. JVM 인스턴스의 소멸: JVM 은 프로그램의 모든 비데몬 스레드가 종료될 때 종료됩니다. 보안 관리자에서 허용하는 경우 Runtime 클래스나 System.exit () 를 사용하여 프로그램을 종료할 수도 있습니다. JVM 이란 무엇입니까? —JVM 의 아키텍처는 클래스 로더 하위 시스템, 런타임 데이터 영역, 실행 엔진의 세 부분으로 나뉩니다. 다음은 클래스 로더, 실행 엔진, 런타임 데이터 영역 1, 클래스 로더에 대해 설명합니다. 이름에서 알 수 있듯이. class 파일을 마운트하는 데 사용됩니다. JVM 의 두 가지 유형의 로더에는 시작 클래스 로더와 사용자 정의 클래스 로더가 있습니다. 시작 클래스 로더는 JVM 구현의 일부이며 사용자 정의 클래스 로더는 Java 프로그램의 일부이며 ClassLoader 클래스의 하위 클래스여야 합니다. (SunJDK1.2 의 경우) 클래스 로더: 시스템 클래스 (JavaAPI 의 클래스 파일) 의 설치 경로에만 마운트할 클래스 찾기 사용자 정의 클래스 로더: 시스템 클래스 로더: JVM 시작 시 만들어 CLASSPATH 디렉토리에서 마운트할 클래스 찾기 기타 사용자 정의 클래스 로더: 여기 Protectedfinalclassdefineclass (string name, bytedata[], intoffset, intlength) protectedfinalclassdefineclass Protectedfinalclassfindsystemclass (stringname) protectedfinalvoidresolveclass (class) defineclass 는 이진 클래스 파일 ( 즉, 사용자 정의 클래스 (즉, 마운트 클래스) 인 findSystemClass 는 유형의 전체 이름을 통해 먼저 시스템 클래스 로더나 시작 클래스 로더를 통해 마운트되고 Class 객체를 반환합니다.
ResolveClass: 클래스 로더가 연결 작업 (검증, 메모리 초기화 할당, 유형의 기호 참조를 직접 참조로 분석 포함) 을 수행하도록 합니다. 여기에는 Java 네임스페이스와 관련된 문제가 포함됩니다. JVM 은 클래스 로더에 의해 마운트된 클래스에서 참조하는 모든 클래스가 해당 클래스 로더에 의해 마운트되고 동일한 클래스 로더에 마운트된 클래스 간에 서로 액세스할 수 있도록 보장합니다. 2, 실행 엔진: 바이트 코드를 실행하거나 로컬 메서드를 실행하여 엔진을 실행하려면 각 명령어에 1 바이트 opcode 가 포함되어 있고 그 뒤에 0 개 이상의 피연산자가 와야 합니다. (a) 명령어 세트는 레지스터 중심이 아닌 스택 중심 디자인 센터를 기반으로 합니다. 이러한 명령어 세트 설계는 Java 시스템의 요구 사항을 어떻게 충족시킬 수 있습니까? 플랫폼 독립성: 스택 중심: 소수의 레지스터만 있는 시스템에서 Java 를 구현하는 것이 더 편리합니다. compiler 는 일반적으로 stack 을 사용하여 연결 최적기에 컴파일된 중간 결과를 전달합니다. 스크립트가 stack 을 기반으로 하는 경우 유용합니다. 네트워크 이동성: 클래스 파일의 소형화. 보안: 스크립트의 대부분의 opcode 는 작업 유형을 나타냅니다. (각 명령을 실행할 때가 아니라 로드 시 데이터 흐름 분석 기간을 사용하여 일회성 검증을 수행하면 실행 속도가 향상됩니다.) (2) 실행 기술의 주요 실행 기술은 해석, 실시간 컴파일, 어댑티브 최적화, 칩 레벨 직접 실행입니다. 그 중 해석은 1 세대 JVM 에 속하고, JIT 는 2 세대 JVM 에 속하며, 어댑티브 최적화 (현재 Sun 의 HotspotJVM 은 이 기술을 채택하고 있음) 는 1 세대 JVM 과 2 세대 JVM 의 경험을 흡수합니다. 메서드가 더 이상 자주 사용되지 않으면 컴파일된 코드가 취소되고 해석되어 실행됩니다. 3, 런타임 데이터 영역: 주로 메소드 영역, 힙, Java 스택, PC 레지스터, 로컬 메소드 스택 (1) 메소드 영역 및 모든 스레드에 의한 힙 * * * 공유 힙: 모든 프로그램이 런타임에 생성하는 객체 메소드 영역 저장: JVM 의 클래스 로더가. class 를 로드할 때 (2)Java 스택 및 PC 레지스터는 스레드에서 독점적으로 독점적으로, 새 스레드 생성 시간 동안 (3) 로컬 메소드 스택: 로컬 메소드 호출을 저장하는 상태 위에는 런타임 데이터 영역의 주요 내용이 요약되어 있으며, 아래에 자세히 설명되어 있습니다. 데이터 영역을 소개하려면 JVM 의 데이터 유형을 설명해야 합니다. JVM 의 데이터 유형: JVM 의 기본 데이터 단위는 word 이고, word 의 길이는 JVM 별 구현자에 의해 결정되며, 데이터 유형에는 기본 및 참조 유형이 포함됩니다. (1) 기본 유형에는 boolean 을 제외한 모든 Java 기본 데이터 유형을 포함한 숫자 유형, boolean 이 포함됩니다
(2) 참조 유형에는 배열 유형, 클래스 유형, 인터페이스 유형 앞에 JVM 의 데이터 표현이 나와 있습니다. JVM 에 입력된 데이터 영역은 먼저 메소드 영역을 살펴 보겠습니다. 위에서 언급했듯이 메소드 영역은 주로 JVM 이 class 파일에서 추출한 유형 정보를 저장하는 데 사용됩니다. 유형 정보는 어떻게 저장됩니까? 아시다시피, 자바 (Java) 는 빅 엔디안 (big) 을 사용합니까? 엔디안 (endian): 즉, 낮은 바이트의 데이터는 높은 메모리에 저장됩니다. 예를 들어, 1234, 12 는 높은 데이터, 34 는 낮은 데이터인 경우 Java 의 저장 형식은 12 가 있는 낮은 주소, 34 는 메모리가 있는 높은 주소, x86 의 저장 형식은 반대로 class 파일에 있는 데이터를 저장해야 합니다 유형 정보: Class 의 전체 이름, class 의 직계 상위 클래스, 클래스 유형 또는 인터페이스 유형, 클래스의 수정자 (public 등), 모든 직계 상위 인터페이스의 목록, class 객체는 Class.forName ( 여러분이 보면, (J)getName (), getSuperClass (), isInterface (), getInterfaces (), getclassloader 가 갑자기 깨달을 것이라고 믿습니다 Static 변수는 유형 정보의 일부로 ClassLoader 클래스에 대한 참조를 저장합니다. 즉, 동적 연결 시 클래스에서 참조하는 다른 클래스가 Class 클래스에 대한 참조를 로드합니다. 필요한 경우 위에서 설명한 유형의 상수 풀: 직접 상수 포함 (String, Integer 및 floatpoint 상수) 및 다른 유형, 필드 및 메서드에 대한 기호 참조 상수 풀을 Java 프로그램 동적 연결에서 중요한 필드 정보 중 하나로 만듭니다. 일반적인 의미에서 선언된 필드 메소드 정보: 유형 내 각 메소드에 대한 정보 컴파일 기간 상수: final 선언 또는 컴파일 시 알려진 값으로 초기화된 클래스 변수 class 는 모든 상수를 해당 상수 풀 또는 바이트 코드 스트림에 복사합니다. 메서드 테이블: 해당 인스턴스에서 호출할 수 있는 모든 인스턴스 메서드에 대한 직접 참조 (상위 클래스에서 상속된 항목 포함) 를 포함하는 배열입니다. 또한 클래스가 추상 및 로컬이 아닌 경우 메서드의 바이트 코드, 피연산자 스택 및 해당 메서드의 스택 프레임, 예외 테이블을 저장합니다. 예: classlava {private intspeed = 5; Voidflow () {} classvolcano {publicstaticvoidmain (string [] args) {lavalava = new lava (); Lava.flow (); }}} 명령 실행 JavaVolcano;; (1)JVM 은 Volcano.class 를 찾아 붓고 해당 유형 정보를 메소드 영역으로 추출합니다. 메소드 영역에서 바이트 코드를 실행하여 JVM 은 main () 메서드를 실행합니다. 실행 시 Vocano 클래스의 상수 풀에 대한 포인터가 항상 저장됩니다. (2)Main () 의 첫 번째 지시어는 JVM 에 상수 풀의 첫 번째 항목에 나열된 클래스에 메모리를 할당해야 한다고 알려줍니다. 여기서 상수 풀은 상수 정보만 저장하는 것이 아닙니다 Lava 클래스가 마운트되어 있고 그 결과 마운트되지 않은 경우 "Lava.class" 를 찾아 유형 정보를 메소드 영역에 쓰고 Volcano 원시 상수 풀의 기호 참조를 메소드 영역 Lava 클래스 정보에 대한 포인터로 바꿉니다. 즉, 기호 참조를 직접 참조로 바꿉니다.
(3)JVM 은 flow 키워드를 보고, Lava 에 메모리를 할당하고, Volcano 상수 풀의 첫 번째 항목을 기준으로 메소드 영역에서 Lava 의 위치를 찾고, 필요한 공간 쌍을 분석하고, 파악한 후 스택에 공간을 할당하고, speed 변수를 0 으로 초기화하고, lava 객체에 대한 참조를 스택에 밀어 넣습니다 (4 힙 Java 객체의 힙 구현을 살펴 보겠습니다. Java 객체는 주로 인스턴스 변수 (자신이 속한 클래스 및 상위 클래스에 의해 선언됨 포함) 와 메서드 영역의 클래스 데이터에 대한 포인터, 메서드 테이블에 대한 포인터, 객체 잠금 (필수가 아님), 컬렉션 대기 (필수가 아님), GC 관련 데이터 (필수가 아님); 그렇다면 왜 Java 객체에 클래스 데이터에 대한 포인터가 있어야 할까요? 먼저 프로그램에서 한 객체 참조를 다른 유형으로 변환할 때 변환이 허용되는지 확인하는 방법을 고려해 보겠습니다. 클래스 데이터가 필요합니다. 둘째, 동적 바인딩에서는 참조 유형이 필요하지 않지만 런타임 유형이 필요합니다. 여기서 혼동은 클래스 데이터에 참조 유형이 아닌 실제 유형이 저장되는 이유입니다. 이 문제는 먼저 남아 있습니다. 후속 독서 노트에서 메서드 테이블에 대한 포인터를 이해할 수 있어야 합니다. C++ 의 VTBL 과 유사하며 메서드 호출의 효율성을 높이는 데 도움이 됩니다. 여러 스레드에서 * * * 데이터를 즐길 수 있는 상호 배타적인 액세스 대기 집합: 여러 스레드가 * * * (Object 클래스의 wait (), notify (), notifyAll () 메서드 참고). Java 배열의 힙 구현: 배열에는 해당 클래스와 연관된 클래스 인스턴스도 있고, 동일한 dimension 및 type 을 가진 배열은 동일한 클래스의 인스턴스입니다. 배열 클래스 이름의 표현: 예: [[LJava/lang/Object 는 Object[][], [I 는 int[], [[b 는 byte[][][] 까지 Thread 가 native 메소드를 실행하는 경우 Pc 값은 undefinedJava 스택입니다. Java 스택은 스레드의 실행 상태를 프레임 단위로 저장하고 Java 스택은 프레임 스택과 스택이라는 두 가지 작업만 수행합니다. 각 프레임은 메서드를 나타냅니다. Java 메서드에는 두 가지 반환 방법, 즉 return 과 예외가 발생합니다. 두 가지 방법으로 인해 해당 메서드에 해당하는 프레임이 스택되고 메모리가 해제됩니다. 프레임 구성: 로컬 변수 영역 (메서드 및 로컬 변수 포함, instance 메서드의 경우 먼저 this 유형도 저장합니다. 여기서 메서드 매개 변수는 선언 순서에 따라 엄격하게 배치되고 로컬 변수는 임의로 배치할 수 있습니다.), 피연산자 스택, 프레임 데이터 영역 (상수 풀 해결을 지원하는 데 사용됨, 일반 메서드 반환 및 예외 처리). 로컬 메소드 스택: 로컬 메소드 구현에 따라 달라집니다. 예를 들어, JVM 구현의 로컬 메소드 핑계가 C 연결 모델을 사용하는 경우 로컬 메소드 스택은 C 스택입니다. 즉, 스레드가 로컬 메소드를 호출할 때 JVM 에 의해 제한되지 않는 영역으로 들어갑니다. 즉, JVM 은 로컬 메소드를 사용하여 동적으로 확장할 수 있습니다. 나는 모두가 JVM 이 무엇인지 이해한다고 믿는다. 원본 링크: blogs.com/chenzhao/archive/2011/08/14/2137713.html