1. 서문
Java는 크로스 하드웨어 플랫폼 객체 지향 고위급 프로그래밍 언어입니다. Java 프로그램은 JVM (Java Virtual Machines)에서 실행되며 JVMS에서 메모리를 관리합니다. 이것은 C ++와 가장 큰 차이입니다. 메모리는 JVM에 의해 관리되지만 JVM이 메모리를 관리하는 방법도 이해해야합니다. JVM이 하나만있을뿐만 아니라 현재 수십 개의 가상 머신이 존재할 수 있지만 사양을 준수하는 가상 머신 설계는 "Java Virtual Machine Specification"을 따라야합니다. 이 기사는 핫스팟 가상 머신에 대한 설명을 기반으로하며 다른 가상 머신과 차이가있는 경우 언급됩니다. 이 기사는 주로 JVM에 메모리가 분산되는 방법, Java 프로그램의 객체 저장 및 액세스 방법 및 다양한 메모리 영역에서 가능한 예외를 설명합니다.
2. JVM의 메모리 분포 (지역)
Java 프로그램을 실행할 때 JVM은 메모리를 관리를 위해 여러 다른 데이터 영역으로 나눕니다. 이 영역마다 기능, 창조 및 파괴 시간이 다릅니다. JVM 프로세스가 시작될 때 일부 영역은 할당되는 반면, 다른 영역은 사용자 스레드 (프로그램 자체의 스레드)의 수명주기와 관련이 있습니다. JVM 사양에 따르면 JVM이 관리하는 메모리 영역은 다음 런타임 데이터 영역으로 나뉩니다.
1. 가상 기계 스택
이 메모리 영역은 스레드에 의해 비공개이며 스레드가 파괴 될 때 스레드가 시작되고 파괴 될 때 생성됩니다. Virtual Machine 스택에 의해 설명 된 Java 메소드 실행을위한 메모리 모델 : 각 방법은 실행 시작시 스택 프레임 (스택 프레임)을 생성하며 로컬 변수 테이블, 오페라 스택, 동적 링크, 메소드 종료 등을 저장하는 데 사용됩니다. 각 방법의 실행 및 리턴이 완료되며 가상 기계 스택에 스택 프레임이 있습니다.
이름에서 알 수 있듯이 로컬 변수 테이블은 로컬 변수를 저장하는 메모리 영역입니다. 기본 데이터 유형 (8 Java 기본 데이터 유형), 참조 유형 및 컴파일러 기간 동안 찾을 수있는 반환 주소를 저장합니다. 64 비트를 차지하는 길고 이중 유형은 2 개의 로컬 가변 공간을 차지하고 기타 데이터 유형은 1을 차지합니다. 유형 크기가 결정되고 컴파일 기간 동안 변수 수를 알 수 있으므로 로컬 변수 테이블은 생성 될 때 알려진 크기를 갖습니다. 메모리 공간 의이 부분은 컴파일 기간 동안 할당 될 수 있으며 메소드 실행 중에 로컬 변수 테이블 크기를 수정할 필요가 없습니다.
가상 머신 사양 에서이 메모리 영역에는 두 가지 예외가 지정됩니다.
1. 스레드가 요청한 스택 깊이가 허용 된 깊이 (?)보다 크면 StackOverflowError 예외가 발생됩니다.
2. 가상 머신이 동적으로 확장 될 수 있다면, 확장이 충분한 메모리에 적용 할 수 없을 때, OutOfMemory 예외가 발생합니다.
2. 로컬 메소드 스택
로컬 메소드 스택은 스레드-프라이버시이며 그 기능은 가상 머신 스택과 거의 동일합니다. 가상 머신 스택은 Java 메소드 실행을위한 In and-Out 스택 서비스를 제공하는 반면 로컬 메소드 스택은 가상 머신에 기본 메소드를 실행할 수있는 서비스를 제공합니다.
가상 머신 사양에는 로컬 메소드 스택의 구현 방법에 대한 필수 규정이 없으며 특정 가상 머신에서 자유롭게 구현할 수 있습니다. 핫스팟 가상 머신은 가상 머신 스택과 로컬 메소드 스택을 직접 결합합니다. 다른 가상 머신 이이 방법을 구현하려면 독자가 관심있는 경우 관련 정보를 쿼리 할 수 있습니다.
가상 머신 스택과 마찬가지로 로컬 메소드 스택은 StackOverflowError和OutOfMemory 예외를 던집니다.
3. 프로그램 계산기
프로그램 계산기는 또한 스레드의 개인 메모리 영역입니다. 스레드가 바이트 코드를 실행하기위한 줄 번호 표시기 (명령어를 가리키는)로 간주 될 수 있습니다. Java가 실행되면 카운터 값을 변경하여 다음 명령을 실행할 수 있습니다. 분기, 루프, 점프, 예외 처리, 스레드 복구 등의 실행 순서는 모두이 카운터에 의존하여 완료합니다. 가상 머신의 멀티 스레딩은 차례로 전환하고 프로세서 실행 시간을 할당함으로써 달성됩니다. 프로세서 (멀티 코어 프로세서의 코어)는 한 번에 하나의 명령 만 실행할 수 있습니다. 따라서 스레드가 스위칭을 수행 한 후 올바른 실행 위치로 복원해야합니다. 각 스레드에는 독립적 인 프로그램 계산기가 있습니다.
Java 메소드를 실행할 때 프로그램 계산기는 현재 스레드가 실행되는 바이트 코드 명령의 주소를 기록합니다 (포인트). 기본 방법이 실행되는 경우이 계산기의 값은 정의되지 않습니다. 핫스팟 가상 머신 스레드 모델은 기본 스레드 모델이기 때문에 각 Java 스레드는 OS (운영 체제)의 스레드를 직접 매핑하기 때문입니다. 기본 방법을 실행할 때 OS에 의해 직접 실행됩니다. 가상 머신 의이 카운터의 가치는 쓸모가 없습니다. 이 계산기는 공간이 매우 작은 메모리 영역이기 때문에 개인이며 확장이 필요하지 않습니다. 가상 머신 사양의 유일한 영역은 OutOfMemoryError 예외를 지정하지 않습니다.
4. 힙 메모리 (힙)
Java 힙은 스레드가 공유하는 메모리 영역입니다. 가상 머신에서 관리하는 가장 큰 메모리 영역이며 가상 머신이 시작될 때 생성됩니다. Java 힙 메모리는 주로 객체 인스턴스를 저장하며 거의 모든 객체 인스턴스 (배열 포함)가 여기에 저장됩니다. 따라서 이것은 또한 쓰레기 수집 (GC)의 주요 메모리 영역이기도합니다. GC에 대한 내용은 여기에 설명되지 않습니다.
가상 머신 사양에 따르면, Java 힙 메모리는 불연속적인 물리적 메모리에있을 수 있습니다. 논리적으로 연속적이고 우주 확장에 제한이없는 한 고정 크기 또는 확장 트리 일 수 있습니다. 힙 메모리에 인스턴스 할당을 완료하기에 충분한 공간이없고 확장 할 수없는 경우 OutOfMemoryError 예외가 발생됩니다.
5. 방법 영역
방법 영역은 힙 메모리와 마찬가지로 스레드가 공유하는 메모리 영역, 유형 정보, 상수, 정적 변수, 즉석 컴파일 기간 및 기타 데이터 중에 컴파일 된 코드를 저장합니다. 가상 머신 사양은 메소드 영역 구현에 너무 많은 제한이 없으며 힙 메모리와 마찬가지로 지속적인 물리적 메모리 공간이 필요하지 않으며 크기는 고정되거나 확장 가능하며 가비지 수집을 구현하지 않도록 선택할 수도 있습니다. 메소드 영역이 메모리 할당 요구 사항을 충족 할 수없는 경우 OutOfMemoryError 예외가 발생됩니다.
6. 직접 메모리
직접 메모리는 가상 머신의 관리 메모리의 일부가 아니지만 메모리 의이 부분은 여전히 자주 사용될 수 있습니다. Java 프로그램이 기본 방법 (예 : NIO, NIO, 여기에 설명이 나오지 않음)을 사용하면 메모리가 직접 오프하에 할당 될 수 있지만 전체 메모리 공간이 제한되어 있으며 메모리가 충분하지 않으며 OutOfMemoryError 예외도 발생합니다.
2. 인스턴스 객체 저장 액세스
위의 첫 번째 요점은 가상 머신의 각 영역에서 메모리에 대한 일반적인 설명이 있습니다. 각 영역마다 데이터가 작성, 배치 및 액세스하는 방법에 문제가 있습니다. 가장 일반적으로 사용되는 힙 메모리를 사용하여 핫스팟을 기반 으로이 세 가지 측면에 대해 이야기하겠습니다.
1. 인스턴스 객체 생성
가상 머신이 새 명령을 실행하면 먼저, 먼저 상수 풀에서 생성 객체의 클래스 기호 참조를 찾아 클래스가로드되고 초기화되었는지 판단합니다. 로드되지 않으면 클래스로드 초기화 프로세스가 실행됩니다 (클래스로드에 대한 설명은 여기에서 작성되지 않습니다). 이 클래스를 찾을 수없는 경우 일반적인 ClassNotFoundException 예외가 발생합니다.
클래스 로딩 확인 후 물리적 메모리 (힙 메모리)가 실제로 객체에 할당됩니다. 객체에 필요한 메모리 공간은 해당 클래스에 의해 결정됩니다. 클래스 로딩 후,이 클래스의 객체에 필요한 메모리 공간이 고정됩니다. 물체에 대한 메모리 공간을 할당하는 것은 힙에서 조각을 나누고이 객체에 할당하는 것과 같습니다.
메모리 공간이 연속적인지 여부에 따라 (할당되지 않고 할당되지 않은 두 부분으로 나뉩니다) 메모리를 할당하는 두 가지 방법으로 나뉩니다.
1. 연속 메모리 : 포인터는 할당 된 메모리와 할당되지 않은 메모리 사이의 분할 지점으로 사용됩니다. 객체 메모리 할당은 포인터 만 공간 크기를 할당되지 않은 메모리 세그먼트로 이동해야합니다. 이 방법을 "포인터 충돌"이라고합니다.
2. 불연속 메모리 : 가상 머신은 할당되지 않은 힙에 해당 메모리 블록을 기록하는 목록을 유지해야합니다. 객체 메모리를 할당 할 때는 적절한 크기의 메모리 영역을 선택하여 객체에 할당 하고이 목록을 업데이트하십시오. 이 방법을 "무료 목록"이라고합니다.
객체 메모리의 할당은 또한 동시성 문제가 발생합니다. 가상 머신은이 스레드 안전 문제를 해결하기 위해 두 가지 솔루션을 사용합니다. 먼저 CAS (비교 및 세트)+를 사용하여 할당 작업의 원자력을 확인하고 재 시도합니다. 둘째, 메모리 할당은 나사산에 따라 다른 공간으로 나뉘어져 있습니다. 즉, 각 스레드는 힙에 스레드-프라이버시 메모리 조각을 사전에 할당하여 로컬 스레드-합금 버퍼 (TLAB); 해당 스레드가 메모리를 할당하려면 tlab에서 직접 할당됩니다. 스레드의 TLAB가 다시 합금 후 할당 된 경우에만 힙에서 동기 작업을 할당 할 수 있습니다. 이 솔루션은 스레드 간의 객체 할당 힙 메모리의 동시성을 효과적으로 감소시킵니다. 가상 시스템을 사용하는지 TLAB을 사용하는지 여부는 JVM 매개 변수 -xx : +/- usetlab을 통해 설정됩니다.
메모리 할당을 완료 한 후, 객체 헤더 정보 외에도 가상 머신은 할당 된 메모리 공간을 0으로 초기화하여 객체 인스턴스의 필드가 값을 할당하지 않고 데이터 유형에 해당하는 0 값에 직접 사용될 수 있도록합니다. 그런 다음 인스턴스 개체가 작성되기 전에 코드에 따라 초기화를 완료하기 위해 초기 메소드를 실행하십시오.
2. 메모리에있는 물체의 레이아웃
핫스팟 가상 머신에서 객체는 메모리의 세 부분으로 나뉩니다 : 객체 헤더, 인스턴스 데이터, 정렬 및 충전 :
객체 헤더는 두 부분으로 나뉩니다. IT의 일부는 해시 코드, 쓰레기 수집 생성 연령, 객체 잠금 상태, 스레드 유지 잠금 잠금 장치, 바이어스 스레드 ID, 바이어스 타임 스탬프 등을 포함한 객체 런타임 데이터를 저장합니다. 32 비트 및 64 비트 가상 머신 에서이 데이터 의이 부분은 각각 32 비트 및 64 비트를 차지합니다. 런타임 데이터가 많기 때문에 32 비트 또는 64 비트는 모든 데이터를 완전히 저장하기에 충분하지 않으므로이 부분은 런타임 데이터를 고정 형식으로 저장하도록 설계되었지만 다른 비트를 사용하여 객체의 상태에 따라 데이터를 저장합니다. 다른 부분은 객체 유형 포인터를 저장 하여이 객체의 클래스를 가리키지 만 필요는 없으며, 객체의 클래스 메타 데이터는 스토리지 의이 부분을 사용하여 반드시 결정할 필요는 없습니다 (아래에서 설명합니다).
인스턴스 데이터는 객체에 의해 정의 된 다양한 유형의 데이터의 내용이며, 이러한 프로그램에 의해 정의 된 데이터는 정의 된 순서로 저장되지 않습니다. 그것들은 가상 머신 할당 정책 및 정의 순서대로 결정됩니다 : long/double, int, short/char, byte/byte/boolean, oop (일반 객체 ponint) , 정책이 유형의 자리 표시 자 수에 따라 할당되는 것을 알 수 있으며, 동일한 유형이 메모리를 함께 할당한다는 것을 알 수 있습니다. 그리고 이러한 조건의 만족하에, 부모 클래스 변수의 순서는 서브 클래스가 선행됩니다.
물체 충전 부분이 반드시 존재하지는 않습니다. 자리 표시 자 정렬에서만 역할을합니다. 핫스팟에서 가상 머신 메모리 관리는 8 바이트 단위로 관리됩니다. 따라서 메모리가 할당되면 객체 크기는 8의 배수가 아니며 정렬 충전물이 완료됩니다.
3. Object Access <br /> Java 프로그램에서 객체를 생성하고 실제로 참조 유형 변수를 얻습니다. 여기서 실제로 힙 메모리에서 인스턴스를 작동합니다. 가상 머신 사양에서, 참조 유형은 객체를 가리키는 참조 유형으로 만 규정되어 있으며,이 참조는 힙의 인스턴스를 찾아서 액세스하는 방법을 지정하지 않습니다. 현재 주류 가상 머신에는 객체 액세스를 구현하는 두 가지 주요 방법이 있습니다.
1. 핸들 방법 : 영역은 핸들 풀로 힙 메모리로 나뉩니다. 참조 변수는 객체의 핸들 주소를 저장하고 핸들은 샘플 객체 및 객체 유형의 특정 주소 정보를 저장합니다. 따라서 객체 헤더는 객체 유형을 포함 할 수 없습니다.
2. 포인터에 대한 직접 액세스 : 참조 유형은 인스턴스 객체의 주소 정보를 힙에 직접 저장하지만 인스턴스 객체의 레이아웃에는 객체 유형이 포함되어야합니다.
이 두 가지 액세스 방법에는 자체 장점이 있습니다. 객체 주소가 변경되면 (메모리 분류, 쓰레기 수집) 핸들 액세스 오브젝트 인 참조 변수를 변경할 필요가 없지만 핸들의 객체 주소 값 만 변경됩니다. 포인터 직접 액세스 방법을 사용하는 동안이 객체의 모든 참조를 수정해야합니다. 그러나 포인터 방법은 하나의 주소 지정 작업을 줄일 수 있으며, 많은 객체 액세스의 경우이 방법의 장점이 더 분명합니다. 핫스팟 가상 머신은이 포인터 직접 액세스 방법을 사용합니다.
3. 런타임 메모리 예외
Java 프로그램에서 실행할 때 발생할 수있는 두 가지 주요 예외가 있습니다 : OutofMemoryError 및 StackoverFlowerror; 그 메모리 영역에서 어떤 일이 일어날까요? 프로그램 카운터를 제외하고 이전에 언급했듯이 다른 메모리 영역이 발생합니다. 이 섹션은 주로 인스턴스 코드를 통해 각 메모리 영역의 예외를 보여 주며, 일반적으로 사용되는 많은 가상 머신 시작 매개 변수는 상황을 더 잘 설명하는 데 사용됩니다. (매개 변수로 프로그램을 실행하는 방법은 여기에 설명되어 있지 않습니다)
1. Java 힙 메모리 오버플로
힙 메모리 오버 플로우는 힙 용량이 최대 힙 용량에 도달 한 후 물체가 생성 될 때 발생합니다. 이 프로그램에서 객체는 지속적으로 만들어지고 이러한 객체는 쓰레기를 수집하지 않도록 보장됩니다.
/** * 가상 머신 매개 변수 : * -xms20m 최소 힙 용량 * -xmx20m 최대 힙 용량 * @author hwz */public class headoutofmemoryError {public static void main (문자열 [] args) {// 컨테이너를 사용하여 객체가 수집되지 않도록하기 위해 컨테이너를 사용하지 않도록합니다. ArrayList <HeadoutOfMemoryError> (); while (true) {// 객체를 연속적으로 생성하여 컨테이너 목록에 추가합니다. }}} 가상 머신 매개 변수를 추가 할 수 있습니다 :-XX:HeapDumpOnOutOfMemoryError . OOM 예외를 보낼 때 가상 머신이 현재 힙의 스냅 샷 파일을 덤프하도록하십시오. 앞으로이 파일 단어 세그먼트 예외 문제를 사용할 수 있습니다. 이것은 자세히 설명되지 않습니다. MAT 도구를 사용하여 메모리 문제를 분석하기 위해 자세히 설명하기 위해 블로그를 작성하겠습니다.
2. 가상 기계 스택 및 로컬 메소드 스택 오버플로
핫스팟 가상 머신 에서이 두 가지 메소드 스택은 함께 구현되지 않습니다. 가상 머신 사양에 따르면이 두 가지 예외는이 두 가지 메모리 영역에서 발생합니다.
1. 스레드가 가상 시스템에서 허용하는 최대 깊이보다 스택 깊이를 요청하면 stackoverflowerror 예외를 던지십시오.
2. 스택 공간을 확장 할 때 가상 머신이 큰 메모리 공간에 적용 할 수없는 경우 OutofMemoryError 예외가 발생합니다.
이 두 가지 상황 사이에는 실제로 겹치는 것이 있습니다. 스택 공간을 할당 할 수없는 경우 메모리가 너무 작거나 중고 스택 깊이가 너무 커지는 지 구별 할 수 없습니다.
코드를 테스트하기 위해 두 가지 방법을 사용하십시오
1. -xss 매개 변수를 사용하여 스택 크기를 줄이고 메소드를 무한히 재귀 적으로 호출하고 스택 깊이를 무한히 높이십시오.
/** * 가상 머신 매개 변수 : <br> * -xss128k 스택 용량 * @author hwz * */public class stackoverflowerror {private int stackdeep = 1; / *** 무한 재귀, 통화 스택 깊이*/ public void recursiveInvoke () {stackDeep ++; recursiveInvoke (); } public static void main (String [] args) {stackoverflowerror soe = new StackoverFlowerror (); try {soe.recursiveInvoke (); } catch (Throwable e) {System.out.println ( "stack deep =" + soe.stackDeep); e 던지기; }}} 많은 수의 로컬 변수가 방법에 정의되어 있으며, 메소드 스택의 로컬 변수 테이블의 길이는 무한히 재귀 적으로라고도합니다.
/** * @author hwz * */public class stackoomeerror {private int stackdeep = 1; / *** 많은 수의 로컬 변수를 정의하고, 스택에서 로컬 변수 테이블을 늘리고, 무한 재귀* 호출 스택의 깊이를 무한히 증가시킵니다*/ public void recursiveInvoke () {double i; 이중 I2; //........... 여기에서 많은 수의 변수 정의가 생략됩니다. recursiveInvoke (); } public static void main (String [] args) {stackoomeerror soe = new StackoomeError (); try {soe.recursiveInvoke (); } catch (Throwable e) {System.out.println ( "stack deep =" + soe.stackDeep); e 던지기; }}}위의 코드 테스트는 프레임 스택이 너무 크거나 가상 기계 용량이 너무 작 든 메모리를 할당 할 수 없을 때 모든 stackoverflowerror가 던져 졌음을 보여줍니다.
3. 방법 영역 및 런타임 상수 풀 오버플로
여기서 우리는 먼저 문자열의 인턴 메소드를 설명합니다. 문자열 상수 풀에 이미이 문자열 객체와 같은 문자열이 포함되어 있으면이 문자열을 나타내는 문자열 객체를 반환합니다. 그렇지 않으면이 문자열 객체를 상수 풀에 추가 하고이 문자열 객체에 대한 참조를 반환하십시오. 이 방법을 통해 상수 풀에 문자열 객체를 지속적으로 추가하여 오버플로를 만듭니다.
/** * 가상 머신 매개 변수 : <br> * -xx : permsize = 10m 영구 영역 크기 * -xx : maxpermsize = 10m 영구적 인 영역 최대 용량 * @author hwz * */public class runtimeconstancepooloom {public static void main (문자열 [] args) {// 목록이 없는지 확인하기 위해 컨테이너를 사용하지 않도록하기 위해 컨테이너를 사용하지 않도록합니다. ArrayList <string> (); // string.intern 메소드를 사용하여 (int i = 1; true; i ++) {list.add (string.valueof (i) .intern ()); }}}그러나이 테스트 코드는 JDK1.7의 런타임 상수 풀에서 넘치지 않지만 JDK1.6에서 발생합니다. 이러한 이유로이 문제를 확인하기 위해 다른 테스트 코드를 작성하십시오.
/** * string.intern 메소드는 다른 jdks * @author hwz */public class stringInterntest {public static void main (string [] args) {string str1 = new StringBuilder ( "test"). Append ( "01"). toString (); System.out.println (str1.intern () == str1); 문자열 str2 = new StringBuilder ( "Test"). Append ( "02"). ToString (); System.out.println (str2.intern () == str2); }} JDK1.6에서 실행 한 결과는 다음과 같습니다. false, false;
JDK1.7에 따라 실행 한 결과는 다음과 같습니다.
jdk1.6에서 인턴 () 메소드는 첫 번째로 문자열 인스턴스를 영구 생성에 복사하며, 이는 영구 생성의 인스턴스에 대한 참조이며 StringBuilder가 만든 문자열 인스턴스는 힙에 있으므로 동일하지 않습니다.
JDK1.7에서 인턴 () 메소드는 인스턴스를 복사하지 않지만 상수 풀에 나타나는 첫 번째 인스턴스의 참조 만 기록합니다. 따라서 인턴이 반환 한 참조는 StringBuilder가 작성한 인스턴스와 동일하므로 true를 반환합니다.
따라서, 일정한 풀 오버플로에 대한 테스트 코드는 일정한 풀 오버플로 예외가 없지만 연속 실행 후 힙 메모리 오버플로 예외가 불충분 할 수 있습니다.
그런 다음 메소드 영역의 오버플로를 테스트하고 클래스 이름, 액세스 수정 자, 일정한 풀 등과 같은 메소드 영역에 물건을 계속 추가해야합니다. 프로그램에 많은 수의 클래스를로드하여 메소드 영역을 지속적으로 채우도록하여 오버 플로우로 이어질 수 있습니다. CGLIB를 사용하여 바이트 코드를 직접 조작하여 다수의 동적 클래스를 생성합니다.
/** * 메소드 영역 메모리 오버 플로우 테스트 클래스 * @author hwz */public class methodareaoom {public static void main (string [] args) {// gclib를 사용하여 하위 클래스를 무한대 (true) {Enhancer Enhancer = new Enhancer (); Enhancer.SetSuperClass (maoomclass.class); Enhancer.setUsecache (false); Enhancer.setCallback (new MethodInterceptor () {@override public object intercept (Object Obj, Method [] args, methodProxy proxy) 던지기 가능 {return proxy.invokesuper (obj, args);}}); Enhancer.create (); }} 정적 클래스 maoomclass {}} VisualVM 관찰을 통해 JVM로드 클래스의 수가 Pergen의 사용과 함께 직선으로 증가 함을 알 수 있습니다.
4. 직접 메모리 오버플로
직접 메모리의 크기는 가상 머신 매개 변수 : -xx : maxDirectMemorySize 를 통해 설정할 수 있습니다. 직접 메모리 오버플로를 만들려면 직접 메모리를 지속적으로 적용하면됩니다. 다음은 Java Nio의 직접 메모리 캐시 테스트와 동일합니다.
/** * 가상 머신 매개 변수 : <br> * -xx : maxDirectMemorySize = 30m 직접 메모리 크기 * @Author hwz * */public class directMemoryoom {public static void main (String [] args) {list <bust> buffers = new arraylist <Bufler> (); int i = 0; while (true) {// 현재 System.out.println (++ i)을 인쇄합니다. // 캐시 버퍼에서 직접 버퍼 메모리 소비를 지속적으로 적용하여 직접 메모리 소비량을 제공합니다 (bytebuffer.allocatedirect (1024*1024)); // 매번 1M 계정}}} 루프에서 1m 직접 메모리가 적용될 때마다 최대 직접 메모리가 30m로 설정되며 프로그램이 java.lang.OutOfMemoryError: Direct buffer memory 번 실행될 때 예외가 발생합니다.
4. 요약
위는이 기사의 모든 내용입니다. 이 기사는 주로 JVM의 다양한 메모리 영역에서 발생할 수있는 메모리, 객체 저장 및 메모리 예외의 레이아웃 구조에 대해 설명합니다. 주요 참조 책 "Java Virtual Machine에 대한 심층적 인 이해 (두 번째 판)". 잘못된 점이 있으면 의견을 지적하십시오. wulin.com을 지원 해주셔서 감사합니다.