컴퓨터 지식 네트워크 - 컴퓨터 구성 - JVM의 원리는 무엇인가요?

JVM의 원리는 무엇인가요?

먼저 여기서 두 가지 개념을 명확히 해보자. JVM 인스턴스와 JVM 실행 엔진 인스턴스는 독립적으로 실행되는 Java 프로그램에 해당하고, JVM 실행 엔진 인스턴스는 사용자 실행 프로그램에 속하는 스레드에 해당한다. ; 즉, JVM 인스턴스는 프로세스 수준에 있고 실행 엔진은 스레드 수준에 있습니다. JVM이란 무엇입니까? —JVM 수명 주기 JVM 인스턴스의 탄생: Java 프로그램이 시작되면 JVM 인스턴스가 생성됩니다. publicstaticvoidmain(String[]args) 함수가 있는 모든 클래스는 JVM 인스턴스 실행의 시작점으로 사용될 수 있습니다. 이 경우 JVM은 classB의 main 대신 classA의 main을 실행하고 있다는 것을 어떻게 알 수 있습니까? 이를 위해서는 JavaclassAhelloworld와 같이 일반적으로 Java 프로그램을 실행하는 명령의 출처인 JVM 클래스 이름을 명시적으로 알려야 합니다. 여기서 Java는 OS에 SunJava2SDK의 Java 가상 머신을 실행하라고 지시하고 classA는 실행하는 데 필요한 클래스 이름을 가리킵니다. JVM. JVM 인스턴스 실행: main()은 프로그램의 초기 스레드에 대한 시작점 역할을 하며 다른 스레드는 이 스레드에 의해 시작됩니다. JVM 내부에는 데몬 스레드와 비데몬 스레드라는 두 가지 유형의 스레드가 있습니다. main()은 비데몬 스레드입니다. 데몬 스레드는 일반적으로 JVM 자체에서 사용되는 스레드를 데몬 스레드로 표시할 수도 있습니다. . JVM 인스턴스 종료: 프로그램의 데몬이 아닌 스레드가 모두 종료되면 JVM이 종료됩니다. 보안 관리자가 허용하는 경우 프로그램은 Runtime 클래스 또는 System.exit()를 사용하여 종료할 수도 있습니다. JVM이란 무엇입니까? —JVM의 아키텍처는 클래스 로더(ClassLoader) 하위 시스템, 런타임 데이터 영역 및 실행 엔진의 세 부분으로 크게 나눌 수 있습니다. 다음은 클래스 로더를 먼저 소개하고, 실행 엔진, 마지막으로 런타임 데이터 영역을 소개합니다. 1. 이름에서 알 수 있듯이 클래스 로더는 .class 파일을 로드하는 데 사용됩니다. JVM의 두 가지 유형의 클래스 로더에는 시작 클래스 로더와 사용자 정의 클래스 로더가 포함됩니다. 시작 클래스 로더는 JVM 구현의 일부이고 사용자 정의 클래스 로더는 Java 프로그램의 일부이며 하위 클래스여야 합니다. ClassLoader 클래스의 . (아래 설명하는 상황은 SunJDK1.2의 경우입니다.) 동적 클래스 로더: 시스템 클래스(JavaAPI 클래스 파일) 설치 경로에서 로드할 클래스만 찾습니다. 사용자 정의 클래스 로더: 시스템 클래스 로더: JVM 생성 시 시작 당시 CLASSPATH 디렉토리에서 로드할 클래스를 찾는 데 사용됩니다. 기타 사용자 정의 클래스 로더: 사용자 정의 클래스 로더가 로드되는 방식을 이해하려면 먼저 ClassLoader 클래스의 여러 메소드에 대해 설명해야 합니다. .class 파일. protectedfinalClassdefineClass(Stringname, bytedata[], inoffset, intlength) protectedfinalClassdefineClass(Stringname, bytedata[], inoffset, intlength, ProtectionDomainprotectionDomain); protectedfinalClassfindSystemClass(Stringname) protectedfinalvoidresolveClass(Classc) 정의 클래스는 바이너리 클래스 파일(새 유형)을 메서드로 가져오는 데 사용됩니다. Area, 즉 여기에서 참조되는 클래스는 사용자 정의 클래스입니다(즉, 클래스 로드를 담당함). findSystemClass는 해당 유형의 정규화된 이름을 사용하고 먼저 시스템 클래스 로더 또는 시작 클래스 로더를 통해 이를 로드합니다. 클래스 객체를 반환합니다.

ResolveClass: 클래스 로더가 연결 작업(검증, 메모리 초기화 할당, 유형의 기호 참조를 직접 참조로 확인 포함)을 수행하도록 합니다. 여기에는 JVM이 로드된 클래스에 의해 참조되도록 보장하는 문제가 포함됩니다. 클래스 로더. 모든 클래스는 이 클래스 로더에 의해 로드됩니다. 동일한 클래스 로더에 의해 로드된 클래스는 서로 액세스할 수 있지만, 다른 클래스 로더에 의해 로드된 클래스는 서로 볼 수 없으므로 효과적인 차폐가 달성됩니다. 2. 실행 엔진: 바이트코드를 실행하거나 로컬 메소드를 실행하는 것입니다. 실행 엔진이라고 하려면 명령어 세트가 있어야 합니다. 각 명령어에는 0개 이상의 피연산자가 따라옵니다. (1) 명령어 세트는 레지스터가 아닌 스택을 중심으로 설계되었습니다. 이 명령어 세트 디자인은 어떻게 Java 시스템의 요구 사항을 충족합니까? 플랫폼 독립성: 스택을 중심으로 하면 구현이 더 편리해집니다. 레지스터가 거의 없는 시스템의 Java 컴파일러는 일반적으로 스택을 사용하여 컴파일의 중간 결과를 연결 최적화 프로그램에 전송합니다. 명령어 세트가 스택을 기반으로 하는 경우 런타임에 수행되는 최적화 작업을 실행 엔진과 결합하는 것이 좋습니다. JIT(Just-In-Time) 컴파일 또는 적응형 최적화를 수행하는 것은 평신도의 관점에서 작업에 사용되는 데이터 구조와 컴파일러를 통합하는 것이 최적화에 더 도움이 된다는 것을 의미합니다. 네트워크 이동성: 클래스 파일의 압축성. 안전: 명령어 세트의 대부분의 opcode는 작업 유형을 지정합니다. (데이터 흐름 분석 기간을 명령어 실행 시 검증이 아닌 로딩 시 일회성 검증으로 활용하면 실행 속도 향상에 도움이 됩니다.) (2) 실행 기술 주요 실행 기술로는 해석, JIT(Just-In-Time) 컴파일, 적응형 최적화, 칩 수준 직접 실행 등이 있습니다. 해석은 1세대 JVM에 속하고 JIT는 2세대에 속합니다. JVM 및 적응형 최적화(현재 Sun의 Hotspot JVM에서 사용되는 이 기술)는 1세대 JVM과 2세대 JVM의 경험을 바탕으로 두 가지를 조합하여 적응형 최적화를 수행합니다. 모든 코드를 해석하고 실행하는 것부터 시작합니다. , 코드 실행을 모니터링한 다음 자주 호출되는 메서드를 최적화합니다. 호출된 메서드는 백그라운드 스레드를 시작하고 이를 네이티브 코드로 컴파일한 후 신중하게 최적화합니다. 메서드가 더 이상 자주 사용되지 않으면 컴파일된 코드가 취소되고 여전히 해석되고 실행됩니다. 3. 런타임 데이터 영역: 주로 메소드 영역, 힙, Java 스택, PC 레지스터, 로컬 메소드 스택을 포함합니다. (1) 메소드 영역 및 힙은 런타임 시 프로그램에 의해 생성된 모든 객체 메소드를 저장합니다. : JVM 클래스 로더가 .class 파일을 로드하고 이를 구문 분석한 후 구문 분석된 유형 정보를 메소드 영역에 넣습니다. (2) 새로운 스레드가 생성되는 동안에는 Java 스택과 PC 레지스터가 독점적입니다. (3) 로컬 메소드 스택: 로컬 메소드 호출의 상태를 저장합니다. 런타임 데이터 영역의 주요 내용은 일반적으로 위에서 소개됩니다. 다음은 데이터 영역을 자세히 소개하기 위해 JVM의 데이터 유형을 설명해야 합니다. JVM의 데이터 유형: JVM의 기본 데이터 단위는 단어이며, 단어의 길이는 JVM의 특정 구현자에 의해 결정됩니다. (1) 기본 유형에는 숫자 유형(부울 제외)이 포함됩니다. ) 모든 Java 기본 데이터 유형), boolean(JVM에서 int로 표시, 0은 false, 기타 int 값은 true를 나타냄) 및 returnAddress(JVM의 내부 유형, finally 절을 구현하는 데 사용됨).

(2) 참조 유형에는 배열 유형, 클래스 유형, 인터페이스 유형이 포함됩니다. JVM의 데이터 표현은 먼저 JVM에 데이터 영역을 입력하고 메소드 영역을 살펴보겠습니다. 메소드 영역은 주로 저장용으로 사용되는데, JVM은 클래스 파일에서 타입 정보를 추출하는데, 타입 정보는 어떻게 저장되나요? 우리 모두 알고 있듯이 Java는 빅 엔디안(big? endian)을 사용합니다. 즉, 낮은 바이트 데이터는 높은 바이트 메모리에 저장됩니다. 예를 들어 1234의 경우 12는 높은 바이트 데이터이고 34는 낮은 바이트 데이터입니다. Java의 저장 형식은 12여야 합니다. 메모리의 하위 주소인 34는 메모리의 상위 주소에 존재하며 x86의 저장 형식은 반대입니다. 이는 실제로 데이터를 저장하는 형식입니다. class 파일이지만 데이터가 메소드 영역에 쏟아지면 JVM은 어떤 방식으로든 저장할 수 있습니다. 유형 정보: 클래스의 정규화된 이름, 클래스의 직접 상위 클래스, 클래스 유형 또는 인터페이스 유형, 클래스 수정자(공용 등), 모든 직접 상위 인터페이스 목록, 클래스 포함 개체는 이 정보에 액세스할 수 있는 창을 제공합니다(Class.forName("") 또는 인스턴스.getClass()를 통해 얻을 수 있음). 다음은 Class의 메서드입니다. 모든 사람이 읽은 후에 갑자기 이해할 것이라고 믿습니다. (그게 다입니다) getName (), getSuperClass(), isInterface(), getInterfaces(), getClassLoader(); 정적 변수는 ClassLoader 클래스에 대한 참조를 유형 정보의 일부로 저장합니다. 클래스에서 참조되는 다른 클래스는 동적 연결 중에 로드됩니다. Class 클래스: 필연적으로 이 유형의 상수 풀은 위에 설명되어 있습니다. 직접 상수(문자열, 정수 및 부동 소수점 상수)와 다른 유형, 필드 및 메소드에 대한 기호 참조를 포함합니다(참고: 여기서 상수 풀은 다음을 위한 장소가 아닙니다). 일반적인 의미에서 상수를 저장합니다. 이러한 기호 참조는 프로그래밍에서 접하게 되는 항목일 수 있습니다. 이러한 기호 참조로 인해 상수 풀은 Java 프로그램 필드 정보의 동적 연결에서 중요한 부분이 되었습니다. 상식적으로 타입에 선언된 메소드 정보: 타입의 각 메소드에 대한 정보 컴파일 타임 상수: final로 선언되거나 컴파일 타임에 알려진 값으로 초기화된 클래스 변수는 모든 상수를 상수 풀이나 바이트코드에 복사합니다. 개울. 메소드 테이블: 인스턴스가 호출할 수 있는 모든 인스턴스 메소드(상위 클래스에서 상속된 메소드 포함)에 대한 직접 참조를 포함하는 배열입니다. 또한 클래스가 추상 및 로컬이 아닌 경우 메소드의 리터럴도 저장합니다. , 피연산자 스택 및 메소드의 스택 프레임, 예외 테이블. 예: classLava{ privateintspeed=5; voidflow(){} classVolcano{ publicstaticvoidmain(String[]args){ Lavalava=newLava(); lava.flow() } } JVM이 Volcano.class를 찾습니다. 메소드 영역에 해당 유형 정보를 입력하고 추출합니다. 메소드 영역에서 바이트코드를 실행함으로써 JVM은 main() 메소드를 실행합니다. (Vocano 클래스의 상수 풀에 대한 포인터는 실행 중에 항상 저장됩니다.) (2) Main()의 첫 번째 명령어는 JVM에 이를 알립니다. 풀에 있는 첫 번째 항목의 클래스가 메모리를 할당합니다(여기서도 상수 풀은 상수 정보만 저장하는 것이 아니라는 점을 다시 설명함)에 나열된 상수여야 하며, JVM은 상수 풀의 첫 번째 항목을 찾아서 다음을 찾습니다. 그런 다음 메소드 영역을 확인하고 Lava 클래스를 로드할지 여부를 확인하며 결과는 로드되지 않았다는 것입니다. 그런 다음 "Lava.class"를 검색하고 다음을 작성합니다. 메소드 영역에 정보를 입력하고 Volcano의 원래 상수 풀에 있는 기호 참조를 메소드 영역의 Lava 클래스 정보에 대한 포인터로 대체합니다. 즉, 기호 참조를 직접 대체합니다.

(3) JVM은 새로운 키워드를 보고 Lava에 대한 메모리 할당을 준비하며 Volcano의 상수 풀의 첫 번째 항목에 따라 메소드 영역에서 Lava의 위치를 ​​찾고 몇 쌍의 공간이 필요한지 분석합니다. 힙에 공간을 할당하고 속도 변수를 0으로 초기화하고 lava 객체의 참조를 스택에 푸시합니다. (4) lava의 flow() 메서드를 호출합니다. 이제 메서드 영역의 내용에 대해 전반적으로 이해했습니다. , 힙 Java 객체의 힙 구현을 살펴보겠습니다. : Java 객체는 주로 인스턴스 변수(자신이 속한 클래스와 상위 클래스에 의해 선언된 변수 포함), 메소드 영역의 클래스 데이터에 대한 포인터로 구성됩니다. , 메소드 테이블에 대한 포인터, 객체 잠금(필요하지 않음) 및 대기 컬렉션(필요하지 않음)(주로 GC 알고리즘에 따라 다름. 예를 들어 표시 및 지우기 알고리즘의 경우 다음이 필요함) 객체가 참조되는지와 finalize() 메서드가 호출되었는지 여부를 표시합니다. 그렇다면 Java 객체에 클래스 데이터에 대한 포인터가 필요한 이유는 무엇입니까? 여러 측면에서 생각해 봅시다. 첫째, 프로그램에서 객체 참조가 다른 유형으로 변환될 때 변환이 허용되는지 어떻게 확인합니까? 둘째, 동적으로 바인딩할 때는 참조 유형이 필요하지 않지만 런타임 유형은 필요합니다. 여기서 혼동되는 점은 참조 유형 대신 실제 유형이 클래스 데이터에 저장되는 이유입니다. 지금은 이 질문을 남겨 두겠습니다. 다음 읽기 노트에서 메소드 테이블에 대한 포인터를 이해할 수 있을 것 같습니다. 이는 C의 VTBL과 유사하며 메소드 호출의 효율성을 높이는 데 유용합니다. 다중 스레드 쌍 구현* 공유 데이터에 대한 상호 배타적 액세스 대기 세트: 여러 스레드가 동일한 목표를 달성하기 위해 노력을 조정할 수 있도록 하는 데 사용됩니다. (Object 클래스의 wait(), inform(), informAll() 메소드에 유의하십시오). Java 배열의 힙 구현: 배열에는 해당 클래스와 연관된 Class 인스턴스도 있습니다. 차원과 유형이 동일한 배열은 동일한 클래스의 인스턴스입니다. 배열 클래스 이름 표현: 예를 들어, [[LJava/lang/Object는 Object[][]를 나타내고, [I는 int[]를 나타내고, [[[B는 byte[][][]를 나타냅니다.) 이 시점에서 힙은 다음과 같습니다. 프로그램 카운터와 Java 스택 프로그램 카운터 소개: 스레드가 시작될 때 생성되는 각 스레드에 대해 PC는 다음 실행 명령의 주소를 저장합니다. 스레드가 기본 메소드를 실행하는 경우 Pc 값은 정의되지 않습니다. Java 스택은 스레드의 실행 상태를 프레임 단위로 저장합니다. 프레임 푸시 및 팝핑. 각 프레임은 메서드를 나타냅니다. Java 메서드에는 반환 및 예외 발생이라는 두 가지 반환 메서드가 있습니다. 두 메서드 모두 해당 메서드에 해당하는 프레임이 스택에서 튀어나오고 메모리가 해제됩니다. 프레임 구성: 지역 변수 영역(메소드 매개변수 및 지역 변수 포함. 인스턴스 메소드의 경우 이 유형을 먼저 저장해야 하며, 여기서 메소드 매개변수는 선언 순서대로 엄격하게 배치되며 지역 변수는 임의로 배치할 수 있습니다. ), 피연산자 스택, 프레임 데이터 영역(상수 풀 구문 분석, 일반 메서드 반환 및 예외 처리를 지원하는 데 사용됨) 로컬 메소드 스택: 로컬 메소드의 구현에 따라 다릅니다. 예를 들어 특정 JVM이 구현하는 로컬 메소드 인터페이스는 C 연결 모델을 사용하며, 로컬 메소드 스택은 스레드가 호출할 때 C 스택이라고 할 수 있습니다. 로컬 메소드에서는 JVM의 영향을 받지 않는 스레드에 진입합니다. 즉, JVM은 기본 메소드를 사용하여 자체를 동적으로 확장할 수 있습니다. 나는 모두가 JVM이 무엇인지 이해하고 있다고 믿습니다. 원본 링크: blogs.com/chenzhao/archive/2011/08/14/2137713.html

上篇: LOL의 세 판타지 신은 무엇을 의미하나요? 下篇: Windows 7의 하드웨어 요구 사항은 무엇입니까?
관련 내용