[JVM] 객체 만들기, 메모리 레이아웃, 액세스 위치

4102 단어
일반 Java 객체(배열 및 Class 객체 제외)의 작성만 논의합니다.

객체 작성


주로 다음과 같은 몇 가지 절차로 나뉜다
1. 대상에 대응하는 클래스가 불러오는지 확인
가상 머신이 new 명령을 만났을 때, 이 명령의 매개 변수가 상수지에서 하나의 종류의 기호 인용을 찾을 수 있는지 확인하고, 이 기호 인용이 대표하는 클래스가 불러오고, 해석되고, 초기화되었는지 확인하십시오.없으면, 클래스의 불러오는 과정을 먼저 실행합니다.
2. 신입생 대상의 메모리 분배
대상에 필요한 메모리 크기는 클래스가 불러온 후에 완전히 확정할 수 있습니다. 이 때 실행할 때 데이터 영역의 무더기에서 특정 크기의 메모리를 구분해서 대상에게 주어야 합니다.
  • 메모리 분할
  • 바늘 충돌 가설 더미 속의 메모리는 절대적으로 정연하고 사용한 메모리는 한쪽에 놓고 사용하지 않은 것은 한쪽에 놓고 중간에 바늘을 경계점의 지시기로 한다.메모리를 분배하는 것은 단지 바늘을 빈 공간 쪽으로 대상의 크기와 같은 거리를 옮기는 것이다.
  • 빈 목록은 메모리가 정연하지 않고 사용된 메모리와 빈 메모리가 서로 엇갈린다고 가정한다. 이때 가상 컴퓨터는 어떤 메모리가 사용할 수 있는지 기록하기 위해 목록을 유지한다.메모리를 분배할 때, 목록에서 대상에게 충분한 공간을 찾아서, 목록의 기록을 업데이트합니다.도대체 어떤 분배 방식을 사용하는지는 자바더미의 정돈 여부와 관련이 있고 자바더미의 정돈 여부는 선택한 스팸 수집기와 압축정리 기능을 가지고 있는지에 따라 결정된다.Serial, ParNew 등 Compact 프로세스가 있는 컬렉터를 사용할 경우 포인터 충돌 방식이 사용됩니다.CMS와 같은 Mark-Sweep 알고리즘 기반 컬렉터를 사용할 경우 유휴 목록 방식을 사용합니다.
  • 구분 시 라인 안전 확보
  • 메모리 공간을 분배하는 동작을 동기화하여 가상 기기를 처리하는데 실제적으로 CAS+실패 재시도 방식으로 업데이트 작업의 원자성을 확보한다.
  • 메모리 분배 동작을 라인에 따라 서로 다른 공간에 구분한다. 즉, 각 라인이 더미에서 작은 메모리 구역을 미리 분배하는 것을 로컬 라인 분배 버퍼Thread Local Allocation Buffer,TLAB라고 한다.메모리를 분배할 라인은 어느 라인의 TLAB에 분배하고 TLAB가 다 쓰고 새로운 TLAB를 분배할 때만 동기화 잠금이 필요합니다.가상 머신이 TLAB를 사용할지 여부는 -XX:+/-UseTLAB 매개변수를 통해 설정할 수 있습니다.

  • 3. 메모리 공간 초기화
    할당된 메모리 공간을 모두 0값으로 초기화합니다. 이 단계는 대상의 실례 필드가 자바 코드에서 초기 값을 부여하지 않고 바로 사용할 수 있고 프로그램이 이 필드의 데이터 형식에 대응하는 0값에 접근할 수 있도록 합니다.
    public class ObjectInit {
        private String s;
        private int a;
    
        @org.junit.Test
        public void test(){
            int b;
            String s1;
            System.out.println(s);  ---1
            System.out.println(a);-----2
            //System.out.println(b);----3
            //System.out.println(s1);---4
        }
    }
    

    위의 코드는 정상적으로 실행되며, 최종적으로null과 0을 출력합니다.만약 3, 4가 표시된 두 개의 코드를 주석을 제거하면test() 방법을 실행하면 알림java: b s1이 표시됩니다.
    4. 객체 설정
    예를 들어 이 대상이 어떤 종류의 실례인지, 어떻게 해야 클래스의 메타데이터 정보를 찾을 수 있는지, 대상의Hash코드, 대상의 GC분대 연령 정보 등이다.이 정보는 대상의 대상 헤더에 저장된다.
    5. 객체 초기화
    위의 작업이 끝난 후에 새로운 대상이 생겼지만 모든 필드는 0입니다.일반적으로 new 명령을 실행한 후에 실행 방법에 따라 대상을 프로그래머의 설정에 따라 초기화합니다.이때, 진정으로 사용할 수 있는 대상이 완전히 발생한다.

    객체의 메모리 레이아웃(HotSpot 가상 시스템의 경우)


    대상이 메모리에 저장된 레이아웃은 대상 헤드, 실례 데이터, 정렬 채우기 세 개의 영역으로 나눌 수 있다.
  • 객체 헤드 헤더 객체 헤드에는 Mark Word와 유형 포인터
  • MarkWord는 해시코드, GC세대별 나이, 잠금 상태 표시기, 스레드가 가지고 있는 잠금, 편향 스레드 ID, 편향 스탬프 등 개체 자체의 운행 시 데이터를 저장하는 데 사용한다.이러한 정보는 개체 자체가 정의한 데이터와 무관한 추가 저장 비용으로 32비트와 64비트의 가상 기기에서 이 부분의 데이터 길이는 각각 32bit와 64bit이다.Mark Word는 객체의 상태에 따라 다음과 같이 저장됩니다.
  • 컨디션
    저장 내용
    표지 위치
    잠기지 않음
    대상 해시 코드, 대상 세대별 연령
    01
    경량급 잠금
    자물쇠를 가리키는 지침
    00
    헤비급 잠금
    중량급 자물쇠를 가리키는 바늘
    10
    GC 태그
    정보를 기록하지 않음
    11
    편향 가능
    편향 스레드 ID, 편향 시간 스탬프, 대상 세대 구분 연령
    01
    주: 이곳의 잠금과 편향은 라인이 이 대상을 경쟁할 때의 개념에 속한다.
  • 유형 지침은 대상이 클래스 데이터를 가리키는 지침으로 가상 기기는 이 지침을 통해 대상이 어떤 종류의 실례인지 확인한다.(모든 가상 기기가 대상 데이터에 유형 포인터를 보존해야 하는 것은 아니다. 즉, 대상의 실례를 찾는 메타데이터 정보가 그 자체를 거쳐야 하는 것은 아니다)
  • 실례 데이터 실례 데이터 부분은 대상이 진정으로 저장한 유효한 정보이자 프로그램 코드에 정의된 각종 유형의 필드 내용이다.부류에서 물려받은 것이든 자류에서 정의된 것이든 모두 기록해야 한다.필드의 저장 순서는 (HotSpot 기본값): longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers)입니다.같은 폭의 필드는 항상 분배 정책에 따라 함께 분배됩니다.이 전제를 충족시키는 조건하에서 부류에 정의된 변수는 항상 자류 앞에 나타난다.CompactFields 매개 변수가true(기본값true)라면 하위 클래스의 좁은 변수도 부모 클래스의 빈틈에 삽입될 수 있습니다.
  • 정렬 충전 정렬 충전은 필연적으로 존재하는 것이 아니라 특별한 함유도 없고 단지 자리를 차지하는 역할만 한다.

  • 객체에 대한 액세스 위치 지정


    대상을 만들면 창고의reference 데이터를 통해 쌓인 구체적인 대상을 조작합니다.가상 기기 규범에서reference 형식 데이터는 지향 대상의 인용이지만, 이 인용은 어떻게 위치를 정하고, 액세스 더미의 대상의 구체적인 위치를 정하는지는 가상 기기가 실현하는 것이다.현재 주류의 접근 방식은 핸들과 직접 지침 두 가지를 사용하는 것이다.
  • 핸들 더미에서 하나의 메모리를 핸들 탱크로 구분하고reference에 저장된 것은 대상의 핸들 주소이며 핸들에는 대상의 실례 데이터와 유형 데이터의 구체적인 주소 정보가 포함되어 있다.이러한 접근 방식의 장점은reference에 저장된 것은 안정적인 핸들 주소이고 대상이 이동된 후(예를 들어 쓰레기 수집 시) 핸들에 저장된 실례 데이터 지침만 바뀔 뿐reference 자체는 수정할 필요가 없다는 것이다.
  • 직접 지침reference에 저장된 직접 대상 주소를 사용합니다.이런 접근 방식을 사용하는 장점은 속도가 더욱 빠르고 바늘로 위치를 정하는 시간 비용을 절약하는 것이다.(HotSpot에서 사용하는 이런 방식)
  • 내용은 에서 발췌한 것이다.

    좋은 웹페이지 즐겨찾기