사람들은 C가 훌륭하다고 말합니다. 왜냐하면 C는 자신의 프로그램이 메모리에서 어떻게 보이는지 정확히 알고 있기 때문입니다. 자바의 경우 운영체제에 대한 지식이 너무 많을 필요도 없고, 메모리 문제에 항상 주의를 기울일 필요도 없지만, 그렇다고 원리를 이해할 필요가 없다는 뜻은 아니다. 그 뒤에 (사실 회사 C OoO에게 조롱당했습니다). Java를 시작하기 쉬운 이유는 가장 어려운 문제를 이전 세대가 해결했기 때문이며, 이는 모두 Java Virtual Machine 덕분입니다. JVM은 자체 명령어 세트를 갖춘 추상 컴퓨터입니다. 언어와 자체 메모리 관리. 이번 시리즈는 그 정체를 하나씩 밝혀나갈 예정이다.
이 기사는 Java HotSpot™ 가상 머신인 JDK 1.8을 기반으로 하며 다음 내용을 논의합니다.
JVM 내부 구조
JVM 메모리 관리
JVM 메모리 모델
그림 1 JVM 내부 구조
1. JVM 내부 구조
프로그램을 실행하는 과정은 다음과 같습니다. 예를 들어 C 언어, 소스 코드는 먼저 실행 파일로 컴파일되어 바이너리 형식으로 디스크에 저장됩니다. 실행되면 먼저 디스크에서 메모리로 로드된 다음 프로세서가 기계 명령어를 실행하기 시작합니다. 대상 프로그램. 반면에 Java는 먼저 바이트코드 파일로 컴파일되며 이는 플랫폼과 관련이 없습니다. JVM은 ClassLoader를 통해 메모리에 로드된 다음 기계 명령어를 실행합니다. 바이트코드와 JVM을 통해 Java는 플랫폼 독립성을 달성합니다. JVM은 시작 시 메모리 조각에 적용되고 기능에 따라 메모리를 다음과 같은 여러 영역으로 나누는 프로세스로 간주될 수도 있습니다.
(1) 힙
매우 중요한 힙(Heap) 영역은 모든 스레드가 공유합니다. 기본적으로 모든 객체 인스턴스가 여기에 할당되며 대부분의 가비지 수집도 여기에서 발생합니다. 이 메모리 부분은 Garbage Collector(자동 메모리 관리 도구)를 사용하여 JVM에 의해 관리됩니다. 이 부분의 역할은 객체에 메모리를 할당하고 여유 메모리를 해제하는 것입니다. Java 힙의 크기는 매개변수를 사용하여 제어하여 고정되어 있는지 또는 동적으로 확장되는지 확인할 수 있습니다.
(2) JVM 스택
스택은 스레드와 밀접한 관련이 있습니다. 스레드와 함께 살고 죽는 메모리입니다. . HotSpot의 Java 스택과 로컬 메소드 스택은 하나로 결합되어 있으며 둘 다 로컬 메모리 공간에 할당됩니다. 메모리의 이 부분은 JVM에서 의도적으로 관리할 필요가 없습니다. JVM 스택은 주로 스택 프레임을 저장하는 데 사용됩니다. 메서드가 호출되면 스택 프레임이 생성되고 메서드가 완료되면 스택의 관점에서 보면 푸시(push)와 팝(pop)이라는 두 가지 작업이 있습니다.
스택 프레임은 지역 변수, 피연산자 스택 및 현재 클래스의 런타임 상수 풀에 대한 참조를 저장하는 데 사용되는 데이터 구조입니다. 지역 변수 배열: 메소드에 정의된 기본 유형 변수를 저장하는 데 사용됩니다. 첨자는 0부터 시작합니다. JVM은 메소드 매개변수를 전달하기 위해 지역 변수 테이블을 사용하며, 0번째 위치는 이 참조를 저장합니다. 현재 객체, 피연산자 스택: 작업을 수행하고 메소드 호출을 위한 매개변수를 준비하고 메소드 결과를 반환하는 데 사용됩니다. 동적 링크: 참조 객체의 런타임 상수 풀.
(3) PC 레지스터
프로그램 카운터는 스레드 전용입니다. 주요 기능은 명령어 주소 저장, 가져오기, 디코딩 및 실행입니다. 각 스레드는 고유한 스택 및 PC 레지스터와 연결됩니다.
(4) 메타스페이스
JDK8 이전의 HotSpot VM인 메타스페이스는 메소드 영역 또는 영구 생성이라고 하며, 클래스의 구조적 정보 등을 저장합니다. 풀, 필드, 메소드 등 힙과 관련되지 않고 로컬 메모리에 저장됩니다.
(5) 네이티브 메서드 스택
JVM 스택은 Java 메서드용으로 준비되고 로컬 메서드 스택은 가상 머신에 로컬 메서드를 호출하는 역할을 합니다.
2. JVM 메모리 관리
Java는 메모리의 직접적인 조작을 허용하지 않으며, 메모리의 적용과 해제는 가상머신에서 처리합니다.
2.1 가비지 수집 자동 메모리 관리
자동 메모리 관리(이하 GC)의 책임:
메모리 할당
참조 객체가 메모리에 남아 있는지 확인 메모리
접근할 수 없는 참조 객체의 메모리 재활용
GC는 대부분의 메모리 할당 문제를 해결하며 자체적으로 특정 리소스를 차지합니다. 가비지 수집은 힙이 가득 차거나 해당 구성 요소 중 하나가 임계값에 도달하면 트리거됩니다. 가비지 수집에서는 주로 재활용 빈도와 시간을 고려합니다. 예를 들어 힙이 작으면 재활용 횟수는 더 많지만 힙이 크면 재활용 횟수가 더 많습니다. 재활용이 오래 걸립니다. 다중 스레드 프로그램에서 가비지 수집 문제가 발생합니다.
가비지 수집 전략:
(1) 직렬 대 병렬
직렬에서는 하나의 가비지 수집 스레드만 동시에 작동할 수 있으며, 병렬에서는 다중 CPU 시스템에서 여러 가비지 수집 스레드가 동시에 작동할 수 있습니다.
(2) Concurrent vs Stop-the-world (Concurrent vs Stop-the-world)
Stop-the-world 가비지 수집기, 재활용 기간 동안 애플리케이션은 작업을 완전히 일시 중지합니다. , 이때 힙은 동결된 것과 동일하며 객체의 상태는 동시에 변경할 수 없으며 하나 이상의 가비지 수집 작업이 애플리케이션과 동시에 실행되고 짧은 Stop-the-world가 발생할 수 있습니다. .수집하는 동안 개체의 상태가 변경될 수 있습니다.
(3) 복사
재활용할 때 남은 객체를 나머지 절반에 복사한 다음 현재 메모리를 지우는 것이 더 쉽습니다. 하지만 메모리 활용도는 상대적으로 낮습니다.
(4) 압축 vs 비압축
메모리 압축 없이 마크 지우기, 재활용 가능 객체 표시, 균일하게 재활용하면 메모리 조각화(Fragmentation)가 많이 발생합니다. 큰 객체를 할당할 때, 인접한 메모리를 찾는 것이 불가능할 수 있습니다. 표시 및 정렬 후에는 먼저 메모리를 압축하고 정렬하고 살아남은 모든 개체를 재활용합니다.
(5) 세대별 수집
힙을 여러 영역으로 나누고, 신세대와 구세대는 위와 같이 서로 다른 재활용 방법을 사용합니다.
2.2 HotSpot의 세대별 수집
HotSpot에서는 메모리가 신세대와 구세대로 나누어져 있으며, 신세대는 동일한 크기의 두 개의 생존자 공간으로 구분됩니다. 그 중 Object는 Eden에 할당되며 일부 대형 객체는 Old Generation에 직접 할당될 수 있습니다.
힙의 구조는 다음과 같습니다.
그림 2 힙