JVMMEMORYMODEL
이 기사에서는 주로 JVM 사양에 설명 된 런타임 데이터 영역 (runtimedataareas)을 소개합니다. 이 영역은 JVM 자체가 사용하는 데이터 또는 JVM에서 실행되는 프로그램을 저장하도록 설계되었습니다.
먼저 JVM에 대한 개요를 한 다음 바이트 코드를 소개하고 마지막으로 다른 데이터 영역을 소개합시다.
개요
운영 체제의 추상화로서 JVM은 동일한 코드가 다른 하드웨어 또는 운영 체제에서 일관되게 작동하도록합니다.
예를 들어:
기본 유형 INT의 경우 16 비트/32 비트/64 비트 운영 체제에 관계없이 32 비트 서명 정수입니다. 범위는 -2^31 ~ 2^31-1입니다
운영 체제 또는 하드웨어가 크거나 작은 바이트 순서인지 여부에 관계없이 JVM에서 저장하고 사용하는 메모리의 데이터가 크거나 작은 바이트 순서 (고 비트 바이트를 먼저 읽으십시오).
다른 JVM 구현은 다소 다를 수 있지만 일반적으로 동일합니다.
위의 그림은 JVM의 개요입니다
JVM은 컴파일러 생성 바이트 코드를 해석합니다. JVM은 Java Virtual Machines의 약어이지만, 바이트 코드로 컴파일 할 수있는 언어 인 한 Scala, Groovy <� "/kf/ware/vc/"와 같은 JVM을 기반으로 실행할 수 있습니다. 대상 = "_ blank"> vcd4ncjxwps6qwcux3mpixrw3sbxetmxfzekvt6os19a92slru+gxu2nsyxnzbg9hzgvyvnpu2lkiu7q05r W91MVQ0MQXYV2+3CF41TC1XNK7UPBH+NPYO6ZWQRXAVNPU2MV8TCRJBGFZC2XVYWRLCRG7Z/Q72BVY1D9KVK3NO9A51MVQ0KGJPC 9wpg0kpha+vnpu2lxe19a9a92slrzai5/da00nds/cfmkgv4zwn1dglgglvbiblbmdpbmupvfjq0l3iys26zda00na8l3a+dqo8cd7wt ndq0v3h5tdo0qq05rsis8zq8snpz8loxkosscji57pm0plwtndqtb3exnk70ncjrlvy1d/k/b7dvmbl47xe1tc85l3hufs8l3a+d qo8cd7wtndq0v3h5tkyulru8lsmwo3t67xxsuoy2df3z7xns7xevbu7ptwvcd4ncjxwpioquty24epwtba8yrxp1shlvltksbhg 0uu5pstckepjvd1qdxn0igluihihrpbwupoanksvs+zcrhsng+rboj1rtq0lxetprc6yjiylxjtprc6ymx4nlrs8mxvrxytprc6yho YXRPDMUGQ29KZSMHO7TMT8VKSVSX4NLRYFQZYBT6WUU1XMF40/KZXS6QPC9WPG0KPHA+TPRC67U6TOBH+Chdb2Rlienh y2gpoao8tmqxseds67y8yvuosklukby2tpo1xmzhun/by0pwtbxe0nte3coqpc9wpg0kpggyiglkpq == "스택 기반 아키텍처 "> 스택 기반 아키텍처
JVM은 스택 기반 아키텍처를 사용합니다. 스택은 개발자에게 투명하지만 생성 된 바이트 코드 및 JVM에 매우 중요한 역할이나 영향을 미칩니다.
우리가 개발 한 프로그램은 저수준 작업을 변환하여 바이트 코드에 저장합니다. JVM의 피연산자를 통한 운영 지침에지도. JVM 사양에 따르면, 작동 지침에 필요한 매개 변수는 피연산자 스택에서 얻습니다.
두 숫자를 추가하는 예를 들어 봅시다. 이 작업을 IADD라고합니다. 다음은 바이트 코드에서 3+4의 프로세스입니다
먼저 3과 4를 피연산자 스택으로 밀어 넣습니다
IADD 지침에 전화하십시오
IADD 명령은 피연산자 스택 상단에서 2 숫자를 나타냅니다.
3+4의 결과는 나중에 사용하기 위해 피연산자 스택으로 밀려납니다.
이 접근법을 스택 기반 아키텍처라고합니다. 레지스터 기반 아키텍처와 같은 저수준 작업을 처리하는 다른 방법이 있습니다.
바이트 코드
Java Bytecode는 Java 소스 코드를 일련의 저수준 작업으로 변환 한 결과입니다. 각 작업은 제로 이상의 바이트 길이 매개 변수가있는 Opcode 또는 작동 코드로 구성됩니다 (대부분의 작업은 오페라 스택을 통해 얻은 매개 변수를 사용합니다). 하나의 바이트는 0x00에서 0xff까지 256 개의 숫자를 나타낼 수 있으며 현재 Java8에 총 204 개가 사용됩니다.
다음은 다양한 유형의 바이트 코드 opcodes와 그 범위와 간단한 설명을 나열합니다.
상수 : 상수 풀 또는 알려진 값의 값을 피연산자 스택으로 밀어 넣으십시오. 0x00 -0x14
로드 : 로컬 가변 값을 피연산자 스택으로 푸시하십시오. 0x15-0x35
상점 : 피연산자 스택에서 로컬 변수까지의 로딩 값 0x36-0x56
스택 : 프로세스 피연산자 스택 0x57-0x5f
수학 : 기본 수학적 계산을위한 피연산자 스택에서 값을 얻으십시오 0x60-0x84
변환 : 유형 사이에 변환 0x85-0x 93
Comaprisons : 두 값의 비교 작동 0x94-0xa6
컨트롤 : GOTO, 리턴, 루프 등을 실행합니다. 제어 작업 0XA7 -0XB1
참조 : 할당 오브젝트 또는 배열을 실행하고 객체, 메소드 및 정적 메소드에 대한 참조를 얻거나 점검합니다. 정적 메소드도 호출 할 수 있습니다. 0xB2 -OXC3
확장 : 확장 : 이후에 추가 된 다른 범주의 작업. 값 0xc4에서 0xc9에서 0xc9
(이 문장은 무엇을 의미합니까? ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ....
예약 : JVM 구현은 슬롯 0xca, oxfe, oxff를 사용합니다
이 204 개의 작업은 매우 간단합니다. 몇 가지 예를 들어보세요.
IFEQ (0x99)는 두 값이 동일한지 여부를 결정합니다
IADD (0x60)는 두 숫자를 추가합니다
I2L (0x85)는 int 비트를 약간 길게 변환합니다
ArrayLength (0xBe) 배열 길이를 반환합니다
POP (0x57) 피연산자 스택 상단에서 값을 팝
바이트 코드 파일을 만들려면 컴파일러가 필요하며 표준 Java 컴파일러는 JDK의 Javac입니다.
공개 클래스 테스트 {public static void main (String [] args) {int a = 1; int b = 15; int result = add (a, b); } public static int add (int a, int b) {int result = a + b; 반환 결과; }}"javac test.java"를 통해 "test.class"의 바이트 코드 파일을 얻을 수 있습니다. Bytecode 파일은 바이너리이며 Javap을 통해 이진 바이트 코드 파일을 텍스트 형식으로 변환 할 수 있습니다.
Java -verbose test.class
classfile /c:/tmp/test.class last modified 1 avr. 2015; 크기 367 바이트 MD5 체크섬 ADB9FF75F12FC6CE1CDDE22A9C4C7426 "Test.java"공개 클래스 com.codinggeek.jvm.Test SourceFile : "Test.java"Minor 버전 : 51 플래그 : Acc_public, Acc_superConstant : #111111115 #15. Java/Lang/Object. "<init>":() V #2 = MethodRef #3. #16 // com/codinggeek/jvm/test.add : (ii) I #3 = 클래스 #17 // com/codinggeek/jvm/test #4 = 클래스 #18 // java/lang/object #5 = utf8 <init> #6 = utf8 () V # # # # # # # # #8 #8 ( linEnbertable #9 = UTF8 메인 #10 = UTF8 ([ljava/lang/string;) v #11 = utf8 추가 #12 = utf8 (ii) i) I #13 = utf8 sourcefile #14 = utf8 test.java #15 = nameandtype #5 : #6 // "<init>"v #16 = nameandtype #11 : #11 : #11 : #16. #17 = UTF8 COM/CodingGeek/JVM/Test #18 = UTF8 Java/Lang/Object {public com.codinggeek.jvm.test (); 플래그 : Acc_public 코드 : stack = 1, locals = 1, args_size = 1 0 : aload_0 1 : invokespecial #1 // 메소드 Java/lang/Object. "<init>":() v 4 : lineba 3 : 0 public static void main (java.lang.string []); 플래그 : acc_public, acc_static code : stack = 2, locals = 4, args_size = 1 0 : iconst_1 1 : istore_1 2 : bipush 15 4 : istore_2 5 : iload_1 6 : iload_2 7 : invokestatic #2 // method add : (ii) i 10 : istore_3 11 : linenbermetly : 5 : 5 : 5 : intore_3 11 : Linenbertly : 9 행 : 11 Public STATIC INT ADD (int, int); 플래그 : acc_public, acc_static 코드 : stack = 2, locals = 3, args_size = 2 0 : iload_0 1 : iload_1 2 : iadd 3 : istore_2 4 : iload_2 5 : ireturn linenbertable : line 12 : 0 행 13 : 4}바이트 코드는 Java 코드의 간단한 번역 일뿐 아니라 다음을 포함한다는 것을 알 수 있습니다.
클래스의 상수 수영장 설명. 일정한 풀은 클래스 내부의 메소드 이름, 매개 변수 목록 등과 같은 클래스 메타 데이터를 저장하는 데 사용되는 JVM 데이터 영역입니다. JVM이 클래스를로드하면 메타 데이터가 상수 풀에로드됩니다.
행 번호 테이블 및 로컬 변수 테이블을 통해 바이트 코드에서 함수 및 TMALL 변수의 특정 위치 정보를 제공하십시오.
Java 코드의 번역 (숨겨진 부모 클래스 구성 포함)
피연산자 스택에서보다 구체적인 작업을 제공하고 매개 변수를 전달하고 얻는보다 완전한 방법
아래는 바이트 코드 파일 스토리지 정보에 대한 간단한 설명입니다.
classfile {u4 마법; u2 minor_version; U2 major_version; U2 constant_pool_count; cp_info constant_pool [constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; U2 인터페이스 [interfaces_count]; U2 Fields_Count; field_info 필드 [fields_count]; u2 attributes_count; attribute_info attributes [attributes_count];}런타임 데이터 영역
런타임 데이터 영역은 데이터를 저장하도록 설계된 메모리 영역입니다. 이 데이터는 개발자 또는 JVM이 내부적으로 사용하기위한 것입니다.
더미
힙은 JVM이 시작될 때 생성되며 모든 JVM 스레드가 공유합니다. 모든 클래스 인스턴스와 어레이는 힙에 할당됩니다 (신규로 생성).
힙은 쓰레기 수집기에 의해 관리되어야하며, 이는 개발자가 만든 객체를 공개하는 책임이 있으며 다시 사용되지 않습니다.
가비지 수집 전략의 경우, JVM 구현에 의해 결정됩니다 (예 : 핫스팟은 여러 알고리즘을 제공합니다).
힙 메모리에는 최대 한계가 있습니다. 이 값이 초과되면 JVM은 OutofMemroy 예외를 던집니다.
방법 영역
이 방법 영역은 또한 JVM의 모든 스레드와 공유됩니다. JVM 스타트 업도 마찬가지입니다. 메소드 영역에 저장된 데이터는 클래스 로더에 의해 바이트 코드에서로드되며, 클래스 로더를로드하거나 JVM이 중지되지 않는 한 애플리케이션 실행 중에 일관되게 존재합니다.
방법 영역은 다음 데이터를 저장합니다.
클래스 정보 (속성 이름, 메소드 이름, 부모 클래스 이름, 변명 이름, 버전 등)
방법 및 제작 된 바이트 코드
각 클래스를로드 할 때 생성 된 런타임 상수 풀
JVM 사양은 방법 영역이 힙에 구현되도록 강요하지 않습니다. Java7 이전에 Hotspot은 Permgen이라는 영역을 사용하여 방법 영역을 구현했습니다. 영구 대역은 힙에 인접 해 있습니다 (메모리 관리는 힙과 동일), 기본 비트는 64MB입니다.
Java8에서 시작하여 HPTSPOT는 별도의 로컬 메모리를 사용하여 방법 영역을 구현하고 메타 데이터 영역 (metaspace)을 명명합니다. 메타 데이터 영역에서 사용 가능한 최대 공간은 전체 시스템의 사용 가능한 메모리입니다.
메소드가 사용 가능한 메모리에 적용 할 수없는 경우 JVM도 unfmemoryError를 던집니다.
런타임 상수 풀
런타임 상수 풀은 방법 영역의 일부입니다. 메타 데이터로 일정한 풀을 실행하는 것이 중요하기 때문에 방법 영역 외부의 Java 사양에 별도로 설명됩니다. 런타임 상수 풀은로드 된 클래스와 인터페이스로 자랍니다.
일정한 수영장은 전통적인 언어로 된 구문 테이블입니다. 다시 말해, 클래스, 메소드 또는 속성이 호출되면 JVM은 런타임 상수 풀을 통해 메모리 에서이 데이터의 실제 주소를 검색합니다. 런타임 상수 수영장에는 문자 리터럴 상수 또는 원시 유형도 포함됩니다.
stirng mystring = "이것은 문자열 쓰레기입니다"정적 최종 int my_constant = 2;
PC (프로그램 카운터) 레지스터 (스레드 당) PC 레지스터 (스레드 당)
각 스레드에는 자체 PC (프로그램 카운터) 레지스터가 있으며 스레드 생성과 함께 생성됩니다. 각 스레드는 현재 스레드의 현재 메소드라고하는 시점에서 하나의 메소드 만 실행할 수 있습니다. PC 레지스터에는 현재 지침을 실행하는 JVM의 주소가 포함되어 있습니다 (방법 영역).
현재 실행 된 메소드가 로컬 메소드 인 경우 PC 레지스터의 값은 정의되지 않습니다.
스레드마다 가상 기계 스택-자바-바이어-메카 네 스택-스레드 당 "> 가상 머신 스택 (스레드 당) 자바 가상 머신 스택 (스레드 당)
가상 머신 스택은 여러 프레임을 저장하므로 스택을 설명하기 전에 먼저 프레임을 살펴 보겠습니다.
프레임
프레임은 스레드가 실행중인 현재 메소드 상태를 나타내는 여러 데이터를 포함하는 데이터 구조입니다.
피연산자 스택 : 앞에서 언급했듯이 바이트 코드 지침은 오페라 스택을 사용하여 매개 변수를 전달합니다.
로컬 변수 배열 :이 배열에는 현재 실행 된 방법의 범위 내의 모든 로컬 변수가 포함됩니다. 이 배열에는 원시 유형, 기준 또는 반환 주소가 포함될 수 있습니다. 로컬 변수 배열의 크기는 컴파일 시간에 결정됩니다. JVM은 로컬 변수를 사용하여 메소드를 호출 할 때 매개 변수를 전달하며 호출 메소드의 로컬 변수 배열은 호출 메소드의 피연산자 스택을 통해 생성됩니다.
런타임 상수 풀 참조 : 현재 클래스의 현재 방법의 상수 풀에 대한 참조. JVM은 일정한 풀 참조를 사용하여 실제 메모리 참조로의 통과 신호를 사용합니다.
스택 (스택)
각 JVM 스레드에는 개인 JVM 스택이 있으며 스레드와 동시에 생성됩니다. Java Virtual Machine Stack은 프레임을 저장합니다. 메소드가 호출 될 때마다 프레임이 생성되어 가상 머신 스택으로 밀려납니다. 이 방법이 실행되면 프레임도 파괴됩니다 (메소드가 정상적으로 실행되는지 또는 예외가 발생하는지 여부에 관계없이)
스레드를 실행하는 동안 하나의 프레임 만 사용할 수 있습니다. 이 프레임을 현재 프레임이라고합니다.
로컬 변수 및 피연산자 스택에 대한 작업에는 일반적으로 현재 프레임에 대한 참조가 동반됩니다.
추가의 또 다른 예를 살펴 보겠습니다
public int add (int a, int b) {return a + b;} public void functiona () {// 함수가없는 일부 코드 int int result = add (2,3); // 함수로 전화를 걸어 b // 함수가없는 일부 코드 호출}내부 메소드 A, 프레임 A는 가상 머신 스택의 상단에 위치한 현재 프레임입니다. ADD 메소드를 호출하기 시작하면 새 프레임 B가 생성되어 가상 머신 스택으로 밀립니다. 프레임 B는 새로운 전류 프레임이됩니다.
프레임 B의 로컬 변수 배열은 프레임 A의 피연산자 스택의 데이터로 채워져 있습니다. 추가 방법이 끝나면 프레임 B가 파괴되고 프레임 A는 현재 프레임으로 다시 설정됩니다. ADD 방법의 결과는 프레임 A의 피연산자 스택으로 밀려서 방법 A가 프레임 A의 피연산자 스택을 통해 추가 된 결과를 얻을 수 있습니다.
요약
위의 것은 Java Virtual Machine 런타임의 데이터 영역 분석에 관한 것입니다. 모든 사람에게 도움이되기를 바랍니다.
단점이 있으면 메시지를 남겨 두십시오.