JVM_03 런타임 데이터 영역 1_Stack_local 변수 table_Local 변수

2. 가상 머신 스택

2.1 개요
2.1.1 배경

크로스 플랫폼 설계로 인해 Java 명령어는 스택에 따라 설계됩니다. 플랫폼마다 CPU 아키텍처가 다르기 때문에 레지스터 기반으로 설계 할 수 없습니다.
장점은 크로스 플랫폼이고 명령어 세트가 작고 컴파일러가 구현하기 쉽다는 것입니다. 그러나 단점은 성능이 저하되고 동일한 기능을 달성하기 위해 더 많은 명령어가 필요하다는 것입니다.

2.1.2 메모리의 힙 및 스택
  • 스택은 런타임 단위이고 힙은 저장 단위입니다.
    즉, 스택은 프로그램의 실행 문제, 즉 프로그램 실행 방법 또는 데이터 처리 방법을 해결합니다. 힙은 데이터 저장 문제, 즉 데이터를 저장하는 방법과 위치를 해결합니다.
  • 일반적으로 객체는 주로 런타임시 데이터 영역의 비교적 큰 부분 인 힙 공간에 배치됩니다.
  • 스택 공간은 기본 데이터 유형의 로컬 변수와 참조 데이터 유형의 객체에 대한 참조를 저장합니다.
2.1.3 가상 머신 스택이란?
  • Java Virtual Machine Stack (Java Virtual Machine Stack), 초기에는 Java 스택이라고도합니다.
    각 스레드가 생성 될 때 가상 머신 스택이 생성되고 스택 프레임 (스택 프레임)이 내부에 저장되며,이 Java 메서드 호출에 해당합니다. 스레드 프라이빗입니다

  • 라이프 사이클과 스레드는 동일합니다.

  • 역할 : 자바 프로그램의 운영을 담당하며 메소드의 로컬 변수 (8 가지 기본 데이터 유형, 객체의 참조 주소), 일부 결과를 저장하고 메소드 호출 및 반환에 참여합니다.

    • 지역 변수 : 멤버 변수 (또는 속성)에 상대적
    • 기본 데이터 변수 : 참조 유형 변수 (클래스, 배열, 인터페이스)에 상대적
2.1.4 스택의 특징
  • 스택은 스토리지를 할당하는 빠르고 효과적인 방법이며 액세스 속도는 PC 레지스터 (프로그램 카운터)에 이어 두 번째입니다.

  • JVM에 의해 Java 스택에 직접 두 가지 작업 만 있습니다.

    • 각 메서드는 스택으로 푸시 (스택으로 푸시, 스택으로 푸시)와 함께 실행됩니다.
    • 실행 후 스택 작업
  • 스택에 대한 가비지 콜렉션 문제가 없습니다.

2.1.5 스택에서 가능한 예외

Java 가상 머신 사양은 Java 스택의 크기를 동적 ​​또는 고정으로 허용합니다.

  • 고정 크기의 JVM (Java Virtual Machine) 스택을 사용하는 경우 스레드 생성시 각 스레드의 JVM (Java Virtual Machine) 스택 용량을 독립적으로 선택할 수 있습니다. 스레드 요청에 의해 할당 된 스택 용량이 Java 가상 머신 스택에서 허용하는 최대 용량을 초과하면 Java 가상 머신에서 StackOverFlowError 예외가 발생합니다.
/**
 * 演示栈中的异常
 */
public class StackErrorTest {
    public static void main(String[] args) {
        main(args);
    }
}
  • Java 가상 머신 스택을 동적으로 확장 할 수 있고 확장을 시도 할 때 충분한 메모리를 적용 할 수 없거나 새 스레드가 생성 될 때 해당 가상 머신 스택을 생성 할 메모리가 충분하지 않은 경우 Java 가상 머신에서 OutOfMemoryError가 발생합니다. 예외
2.1.6 스택의 메모리 크기 설정

매개 변수 -Xss 옵션을 사용하여 스레드의 최대 스택 공간을 설정할 수 있습니다. 스택의 크기는 함수 호출의 최대 도달 가능 깊이를 직접 결정합니다.
(IDEA 설정 방법 : Run-EditConfigurations-VM 옵션은 지정된 stack-Xss256k의 크기를 채 웁니다)

/**
 * 演示栈中的异常
 *
 * 默认情况下:count 10818
 * 设置栈的大小: -Xss256k count 1872
 */
public class StackErrorTest {
    private static int count = 1;
    public static void main(String[] args) {
        System.out.println(count);
        count++;
        main(args);
    }
}
2.2 스택의 저장 구조 및 작동 원리
2.2.1
  • 각 스레드에는 자체 스택이 있으며 스택의 데이터는 스택 프레임 형식입니다.
  • 이 스레드에서 실행되는 각 메서드는 자체 스택 프레임에 해당합니다.
  • 스택 프레임은 메모리 블록 및 메소드 실행 중에 다양한 데이터 정보를 유지하는 데이터 세트입니다.
  • JVM은 Java 스택에서 두 개의 직접 작업, 즉 선입 선출 / 후입 선출의 합계 원칙에 따라 스택 프레임을 푸시하고 팝합니다.
  • 활성 스레드에는 특정 시점에 활성 스택 프레임이 하나만 있습니다. 즉, 현재 실행중인 메소드의 스택 프레임 (최상위 스택 프레임) 만 유효하며이 스택 프레임을 현재 스택 프레임 (Current Frame)이라고하며 현재 스택 프레임에 해당하는 메소드가 현재 메소드 (Current Frame)입니다. )
  • 실행 엔진에서 실행하는 모든 바이트 코드 명령어는 현재 스택 프레임에서만 작동합니다.
  • 이 메서드에서 다른 메서드가 호출되면 해당하는 새 스택 프레임이 생성되고 스택의 맨 위에 배치되어 새 현재 스택 프레임이됩니다.
  • 다른 스레드에 포함 된 스택 프레임은 서로 참조 할 수 없습니다. 즉, 다른 스택 프레임에있는 다른 스레드의 스택 프레임을 참조 할 수 없습니다.
  • 현재 메서드가 다른 메서드를 호출하는 경우 메서드가 반환되면 현재 스택 프레임은이 메서드의 실행 결과를 이전 스택 프레임으로 반환 한 다음 가상 머신은 현재 스택 프레임을 삭제하여 이전 스택 프레임을 현재 스택으로 만듭니다. 다시 프레임
  • Java 메소드에서 함수를 리턴하는 방법에는 두 가지가 있습니다. 하나는 리턴 명령을 사용하여 일반 함수에서 리턴하는 것이고 다른 하나는 예외를 발생시키는 것입니다. 어떤 방법을 사용하든 스택 프레임이 팝업됩니다.
    여기에 사진 설명 삽입
/**
 * 栈帧
 */
public class StackFrameTest {
    public static void main(String[] args) {
        StackFrameTest test = new StackFrameTest();
        test.method1();
        //输出 method1()和method2()都作为当前栈帧出现了两次,method3()一次
//        method1()开始执行。。。
//        method2()开始执行。。。
//        method3()开始执行。。。
//        method3()执行结束。。。
//        method2()执行结束。。。
//        method1()执行结束。。。
    }

    public void method1(){
        System.out.println("method1()开始执行。。。");
        method2();
        System.out.println("method1()执行结束。。。");
    }

    public int method2(){
        System.out.println("method2()开始执行。。。");
        int i = 10;
        int m = (int) method3();
        System.out.println("method2()执行结束。。。");
        return i+m;
    }

    public double method3(){
        System.out.println("method3()开始执行。。。");
        double j = 20.0;
        System.out.println("method3()执行结束。。。");
        return j;
    }

}
2.2.2 스택 프레임의 내부 구조

각 스택 프레임에 저장 :

  • 지역 변수 테이블 (지역 변수)
  • 피연산자 스택 (연산자 스택) (또는 표현식 스택)
  • 동적 연결 (또는 런타임 상수 풀을 실행하기위한 메서드 참조)
  • 메소드 리턴 주소 (Return Adress) (또는 메소드의 정상 종료 또는 비정상 종료의 정의)
  • 추가 정보

여기에 사진 설명 삽입

2.3 지역 변수 테이블 (지역 변수)
2.3.1 개요
  • 지역 변수 테이블은 지역 변수 배열 또는 지역 변수 테이블이라고도합니다.
  • 주로 메소드 본문에 정의 된 메소드 매개 변수 및 로컬 변수를 저장하는 데 사용되는 숫자 형 배열 로 정의됩니다 . 이러한 데이터 유형에는 다양한 기본 데이터 유형, 객체 참조 및 returnAddressleixing이 포함됩니다.
  • 로컬 변수 테이블은 스레드 스택에 빌드되므로 스레드 전용 데이터이므로 데이터 보안 문제가 없습니다.
  • 로컬 변수 테이블의 필요한 용량은 컴파일 중에 결정되고 메서드의 Code 속성의 최대 로컬 변수 데이터 항목에 저장됩니다. 메서드를 실행하는 동안 로컬 변수 테이블의 크기는 변경되지 않습니다.
  • 중첩 된 메서드 호출 수는 스택 크기에 따라 결정됩니다. 일반적으로 스택이 클수록 중첩 된 메서드 호출이 많아집니다. 함수의 경우 매개 변수와 지역 변수가 많을수록 지역 변수 테이블이 클수록 스택 프레임이 커지므로 메서드 호출에 의해 전달되는 정보를 늘리는 요구 사항을 충족 할 수 있습니다. 차례로 함수 호출은 더 많은 스택 공간을 차지하므로 중첩 호출이 줄어 듭니다.
  • 지역 변수 테이블의 변수는 현재 메서드 호출에서만 유효합니다. 메서드가 실행되면 가상 머신은 로컬 변수 테이블을 사용하여 파라미터 값을 파라미터 변수 목록으로 전송하는 과정을 완료합니다. 메서드 호출이 종료되면 메서드 스택 프레임이 파괴되면서 지역 변수 테이블이 삭제됩니다.

그림과 같이 javap 명령을 사용하여 바이트 코드 파일을 구문 분석하여 로컬 변수 테이블을 봅니다.

여기에 사진 설명 삽입

여기에 사진 설명 삽입
여기에 사진 설명 삽입
여기에 사진 설명 삽입
여기에 사진 설명 삽입

操作数栈:
bipush -> 放在操作数栈中
istore -> 放在局部变量表中
如果是非static的话局部变量表开始为1 0为this
iload -> 局部变量表到Operand stack

byte i = 15;
int j = 8;
int k = i + j;

bipush 15
istore_1
bipush 8
istore_2
iload_1
iload_2
iadd
istore_3
reture
Slot变量槽,局部变量表在编译期就确定大小了。
局部变量表就是数组
slot:double和long为2个slot,int,float为1个slot

여기에 사진 설명 삽입

  • JVM은 로컬 변수 테이블의 각 슬롯에 액세스 인덱스를 할당합니다.이 인덱스를 통해 로컬 변수 테이블에 지정된 로컬 변수 값에 성공적으로 액세스 할 수 있습니다.
  • 인스턴스 메서드가 호출되면 메서드 본문에 정의 된 메서드 매개 변수와 로컬 변수가 순서대로 로컬 변수 테이블의 각 슬롯에 복사됩니다.
  • 로컬 변수 테이블에서 64 비트 로컬 변수 값에 액세스해야하는 경우 인덱스에만 서명하면됩니다. (예 : long 또는 double 유형 변수에 액세스)
  • 현재 프레임이 생성자 또는 인스턴스 메서드에 의해 생성 된 경우 객체 참조가 인덱스 0으로 슬롯에 저장되고 나머지 매개 변수는 매개 변수 테이블의 순서로 정렬됩니다.
public class LocalVariablesTest {

    private int count = 1;
    //静态方法不能使用this
    public static void testStatic(){
        //编译错误,因为this变量不存在与当前方法的局部变量表中!!!
        System.out.println(this.count);
    }
}
슬롯 재사용

스택 프레임의 로컬 변수 테이블에있는 슬롯은 재사용 할 수 있습니다. 로컬 변수가 범위를 초과 할 경우 해당 범위 이후에 선언 된 새 로컬 변수는 만료 된 로컬 변수 슬롯을 재사용하여 자원 절약 목적을 달성 할 수 있습니다. .

private void test2() {
        int a = 0;
        {
            int b = 0;
            b = a+1;
        }
        //变量c使用之前以及经销毁的变量b占据的slot位置
        int c = a+1;
    }

추천

출처blog.csdn.net/qq_43141726/article/details/114579985