1. 기본 개념
각 Java 프로그램 실행은 Java 프로세스를 생성합니다. 각 Java 프로세스에는 하나 이상의 스레드가 포함될 수 있습니다. 각 Java 프로세스는 고유 한 JVM 인스턴스에 해당하고 각 JVM 인스턴스는 힙에 해당하며 각 스레드에는 자체 개인 스택이 있습니다. 모든 클래스 인스턴스 (즉, 객체) 또는 배열 (참조가 아닌 배열 자체 참조)은 힙에 배치되어 프로세스의 모든 스레드에서 공유됩니다. Java의 할당 된 힙 메모리는 자동으로 초기화됩니다. 즉, 메모리를 객체에 할당 할 때 객체의 변수가 초기화됩니다. Java의 모든 객체의 저장 공간이 힙에 할당되지만,이 객체에 대한 참조는 스택에 할당됩니다. 힙에 할당 된 메모리는 실제로 생성 된 객체 자체를 저장하는 반면 스택에 할당 된 메모리는 힙 객체에 대한 참조 만 저장합니다. 로컬 변수 새가 나오면 스택 공간과 힙 공간에 공간이 할당됩니다. 로컬 가변 수명주기가 끝나면 스택 공간이 즉시 재활용되고 힙 공간 영역이 GC가 재활용되기를 기다리고 있습니다.
특정 개념 : JVM의 메모리는 힙, 스택 및 방법 영역 (정적 영역이라고도 함)의 세 가지 영역으로 나눌 수 있습니다.
스택 영역 :
1. 저장된 모든 것이 객체이며, 각 객체에는 그에 해당하는 정보가 포함되어 있습니다 (클래스의 목적은 작동 지침을 얻는 것입니다).
2.JVM에는 하나의 힙 영역 (힙)이 있으며 모든 스레드가 공유합니다. 힙은 기본 유형과 객체 참조를 저장하지 않고 객체 자체와 배열 자체 만 저장합니다.
스택 영역 :
1. 각 스레드에는 스택 영역이 포함되어 있으며, 이는 기본 데이터 유형 자체 및 사용자 정의 객체에 대한 참조 만 저장합니다.
2. 각 스택의 데이터 (원시 유형 및 객체 참조)는 개인이며 다른 스택은 액세스 할 수 없습니다.
3. 스택은 기본 유형 변수 영역, 실행 환경 컨텍스트 및 작동 지침 영역 (작동 지침 저장)으로 나뉩니다.
방법 영역 (정적 영역) :
1. 모든 스레드가 공유합니다. 메소드 영역에는 모든 클래스가 포함되어 있습니다 (클래스는 클래스의 원래 코드를 나타냅니다. 클래스 객체를 만들려면 클래스 코드를 메소드 영역에로드하고 초기화해야 함) 및 정적 변수.
2. 방법 영역에는 클래스 및 정적 변수와 같이 전체 프로그램에서 항상 고유 한 요소가 포함되어 있습니다.
2. 예제 시연
AppMain.java
공개 클래스 APPMAIN // 실행할 때 JVM은 모든 AppMain 코드를 메소드 영역 {public static void main (String [] args)에 넣습니다. 기본 메소드 자체는 메소드 영역에 배치됩니다. {샘플 test1 = 새로운 샘플 ( "테스트 1"); // test1은 참조이므로 스택 영역에 넣으면 샘플은 사용자 정의 객체이며 힙에 배치해야합니다. 샘플 test2 = 새로운 샘플 ( "테스트 2"); test1.printname (); test2.printname (); }} public class 샘플 // 실행 중에 JVM은 AppMain의 모든 정보를 메소드 영역에 넣습니다. // 새 샘플 인스턴스 후에는 이름 참조가 스택 영역에 배치되며 이름 해당 문자열 객체가 힙에 배치됩니다/** 생성자*/public 샘플 (문자열 이름) {this .name = name; } /** output* /public void printname () // 객체가 없으면 인쇄 방법이 샘플 클래스와 함께 메소드 영역에 배치됩니다. {System.out.println (이름); }}프로그램을 실행할 때 먼저 Java 가상 머신 프로세스를 시작하십시오. 이 프로세스는 먼저 ClassPath에서 AppMain.class 파일을 찾고 파일의 이진 데이터를 읽은 다음 런타임 데이터 영역의 메소드 영역에 AppMain 클래스의 클래스 정보를 저장합니다. 이것은 AppMain 클래스의 로딩 프로세스입니다.
다음으로 Java Virtual Machine은 방법 영역에서 AppMain 클래스의 Main () 메소드의 바이트 코드를 찾아 지침을 실행하기 시작합니다. 이 main () 메소드의 첫 번째 진술은 다음과 같습니다.
코드 사본은 다음과 같습니다.
샘플 test1 = 새로운 샘플 ( "test1");
이 진술의 실행 프로세스 :
1. Java Virtual Machine은 방법 영역에서 샘플 클래스의 유형 정보를 발견했지만 샘플 클래스가 메소드 영역에로드되지 않았기 때문에 발견되지 않았습니다 (여기서 Java의 내부 클래스가 별도로 존재하며 처음에는 포함 된 클래스와 함께로드되지 않을 것임을 알 수 있습니다). Java Virtual Machine은 즉시 샘플 클래스를로드하고 샘플 클래스 유형 정보를 메소드 영역에 저장합니다.
2. Java Virtual Machine은 먼저 힙 영역의 새로운 샘플 인스턴스에 대한 메모리를 할당하고 샘플 클래스 유형 정보가 샘플 인스턴스의 메모리에 저장되는 메소드 영역에 메모리 주소를 저장합니다.
3. JVM 프로세스에서 각 스레드에는 메소드 호출 스택이 있으며 스레드가 실행 중에 일련의 메소드 통화 프로세스를 추적하는 데 사용됩니다. 스택의 각 요소를 스택 프레임이라고합니다. 스레드가 메소드를 호출 할 때마다 새 프레임이 메소드 스택으로 밀려 나옵니다. 여기의 프레임은 작업 중 메소드, 로컬 변수 및 임시 데이터의 매개 변수를 저장하는 데 사용됩니다.
4. TEST1 이전 "="는 main () 메소드에 정의 된 변수 (샘플 객체에 대한 참조)이므로 main () 메소드를 실행하는 기본 스레드의 Java 메소드 호출 스택에 추가됩니다. "="는이 test1 변수를 힙 영역의 샘플 인스턴스에 지적합니다.
5. JVM은 힙 영역에서 또 다른 샘플 인스턴스를 계속 생성하고 힙 영역에서 방금 생성 된 새로운 샘플 인스턴스를 가리키는 기본 메소드의 메소드 호출 스택에 Test2 변수를 추가합니다.
6. JVM은 인쇄 이름 () 메소드를 차례로 실행합니다. Java Virtual Machine이 test1.printname () 메소드를 실행할 때 Java Virtual Machine은 로컬 변수 test1에 의해 보유 된 참조를 기반으로 힙 영역에서 샘플 인스턴스를 찾은 다음 샘플 인스턴스에 의해 보유 된 기준에 기초하여 샘플 클래스 유형 정보를 찾아 printname ()를 포함하여 printname ()를 실행합니다.
III. 구별하다
자바 언어로 된 힙과 스택의 차이점 :
1. 스택과 힙은 모두 Java가 RAM에 데이터를 저장하는 데 사용되는 장소입니다. C ++와 달리 Java는 스택 및 힙을 자동으로 관리하며 프로그래머는 스택이나 힙을 직접 설정할 수 없습니다.
2. 스택의 장점은 액세스 속도가 힙보다 빠르며 CPU 직접 위치에있는 레지스터에 이어 두 번째는 더 빠릅니다. 그러나 단점은 스택의 데이터 크기와 수명이 결정적이어야하고 유연성이 부족하다는 것입니다. 또한 스택 데이터를 공유 할 수 있습니다 (자세한 내용은 아래 소개 참조). 힙의 장점은 메모리 크기를 동적으로 할당 할 수 있으며 수명을 미리 컴파일러에 알 필요가 없다는 것입니다. Java의 쓰레기 수집기는 더 이상 사용되지 않은 데이터를 자동으로 수집합니다. 그러나 단점은 런타임에 메모리가 동적으로 할당되어야하며 액세스 속도가 느리다는 것입니다.
Java의 2 가지 데이터 유형 :
하나는 int, short, long, byte, float, double, boolean, char의 8 개 범주, 즉 8 가지 범주의 원시 유형입니다 (기본 유형의 문자열이 없음). 이 유형의 정의는 int a = 3과 같은 양식으로 정의됩니다. 긴 b = 255l; 자동 변수라고합니다. 자동 변수에는 문자 값이 있으며, 클래스의 인스턴스가 아니라 클래스에 대한 언급이 아니며 여기에는 클래스가 없습니다. 예를 들어, int a = 3; 여기서 a는 문자 유형을 가리키는 참조가 3의 문자 값을 가리키며,이 문자 데이터의 크기와 수명으로 인해,이 문자 그럴 값은 프로그램 블록에서 고정으로 정의되며 프로그램 블록이 종료 된 후에도 필드 값이 사라집니다).
스택에는 매우 중요한 기능이 있습니다. 스택에 존재하는 데이터는 공유 할 수 있습니다. 우리가 동시에 정의한다고 가정합니다 : int a = 3; int b = 3; 컴파일러는 먼저 int a = 3을 처리합니다. 먼저 스택에 변수 A가있는 참조를 생성 한 다음 문자 그대로 값이 3 인 주소가 있는지 확인합니다. 찾을 수없는 경우 문자 그대로 값이 3 인 주소가 열린 다음 3의 주소를 가리킨 다음 int b = 3; B의 기준 변수를 작성한 후, 스택에 이미 문자 그대로의 값이 있기 때문에 B는 3의 주소를 직접 가리 킵니다. 이러한 방식으로 A와 B는 동시에 3을 가리 킵니다.
문자 값에 대한이 참조는 클래스 객체의 값과 다릅니다. 두 클래스 객체의 참조가 동시에 객체를 가리킨다고 가정하면, 한 개체 기준 변수가 객체의 내부 상태를 변경하면 다른 객체 참조 변수는이 변경을 즉시 반영합니다. 대신, 문자 그대로 참조를 통해 그 값을 수정해도 다른 값이 그에 따라 변경되지 않습니다. 위의 예에서와 같이, 우리는 a와 b의 값을 정의한 후에, a = 4; 그런 다음 B는 4와 같거나 3과 같지 않습니다. 컴파일러 내부에서 A = 4가 발생하면 스택에 문자 그대로 값이 4인지 다시 검색합니다. 그렇지 않은 경우 주소를 다시 열어 4의 값을 저장하십시오. 이미 존재하는 경우이 주소를 직접 가리 킵니다. 따라서 값 A의 변화는 값에 영향을 미치지 않습니다. b.
다른 유형은 해당 기본 데이터 유형을 감싸는 정수, 문자열, 이중 등과 같은 클래스 데이터를 포장하는 것입니다. 이 모든 클래스 데이터는 힙에 존재합니다. Java는 새로운 () 문을 사용하여 컴파일러를 표시하고 런타임에 필요한만큼 동적으로 만 생성하므로 더 유연하지만 단점은 더 많은 시간이 걸린다는 것입니다.
4. 요약
Java 메모리 할당 구조는 여전히 매우 명확합니다. 철저히 이해하려면 JVM 관련 책을 확인할 수 있습니다. Java에서 메모리 할당에서 가장 번거로운 것은 문자열 객체입니다. 특별한 특성으로 인해 많은 프로그래머는 혼란을 겪기 쉽습니다. 다음 기사에서 자세히 설명하겠습니다.