js study #객체가 프로퍼티를 저장하는 방식

Oak_Cassia·2024년 2월 22일
0

js 스터디 2023

목록 보기
5/5

다음의 데이터는 각각 분리된 공간에 저장

  • 다양한 사용 패턴에 대한 속성/요소 효율적 추가/액세스 가능
  • Elements: [1, 2, 3]
    • 정수형 인덱스로 위치 파악 가능
    • 메모리 절약 위해 sparse dictionary 사용 가능
    • Array-indexed properties: elements store에 저장
  • Properties: [a: 'hello', b: 'world']
    • 키로 위치 파악 어려움
    • HiddenClass 사용
    • Named properties: properties store에 저장
  • Elements와 properties는 각각 배열이나 딕셔너리(해쉬 테이블)에 저장될 수 있음

Hidden Class

  • 객체의 구조와 속성의 메타 데이터를 저장하는 구조

    V8 엔진에서 객체의 첫 번째 필드는 히든 클래스를 가리키는 포인터

  • 히든 클래스에 포함되는 정보
    • 프로퍼티 개수 (세 번째 필드)
    • 프로토타입 참조
    • Descriptor Array 포인터
  • Descripitor Array는 프로퍼티 이름과 위치 저장
    - length
    - EnumCache
    - key
    - details (enumerable, configurable, writeable)
  • 동일한 구조를 가진 객체는 동일한 히든 클래스 공유
  • 새로운 속성이 추가 시 히든 클래스 변경
    - 기존 히든 클래스는 보존되어 transition tree 형성
    - 같은 transition tree 내의 히든 클래스는 Descriptor Array 공유
    - Transition Array에는 형제 히든 클래스로 분기하기 위한 property name을 저장

Named Properties

  • 딕셔너리는 인라인 캐시를 방해

In-object properties

  • 객체 내에 저장되는 프로퍼티
    - 개수는 초기 크기에 의해 결정
    - 객체의 공간을 초과하는 추가 속성은 properties store에 저장

Fast Properties

  • 선형 시간에 접근 가능한 경우 일반적으로 빠르다고 판단
  • properteis store 내의 Fast properties는 인덱스로 접근 가능
  • 히든 클래스의 descriptor array를 참조하여 실제 위치에 접근

Slow Properties

  • 객체에 많은 속성이 추가되거나 삭제되면 히든 클래스 및 descriptor array의 유지에 오버헤드가 발생
  • 이런 경우에는 딕셔너리가 사용
  • 메타데이터는 descriptor array가 아니라 직접 딕셔너리에 저장
    - 각 항목은 key, value, details로 구성
    - details (enumerable, configurable, writeable)

인라인 캐시?

Elements, Array-indexed Properties

Packed & Holey Elements

  • 배열에서 중간 원소를 삭제하거나 정의하지 않은 경우 빈 공간 존재
  • 비어 있는 element가 있으면 프로토타입 체인을 확인하여 속성 검색
  • _hole 값은 존재하지 않는 속성을 표시하는 특별한 값
    - _hole 값이 없는 경우 배열은 packed된 상태
    - 프로토타입 체인을 확인할 필요 없음

Fast & Dictionary Elements

  • Fast elements: 요소 인덱스가 elements store의 인덱스와 직접 매핑되어 빠른 접근 속도를 제공
  • Dictionary elements: 메모리를 절약하기 위해 사용되는 방식이며, 큰 희소 배열이나 구멍이 많은 배열에 적합
  • 키 - 값 - 디스크립터 저장
  • 사용자 정의 설명자를 정의할 때 히든 클래스에 설명자 세부 정보를 저장할 수 없어 dictionary(slow) elements를 사용
  • 정수만 배열에 저장하면 GC는 array를 탐색할 필요 없음 (small integers로 인코딩 됨)

    Smis: small integer, 최하위 비트를 0로 두어 포인터와 구분 하는 정수
    32비트 환경에서는 [31비트 signed int] 0
    64비트 환경에서는 [32비트 signed int] [31비트 0 패딩] 0

  • Double만 배열에 저장하면 raw double 값으로 저장됩니다.

    Doubles: 부동 소수점 숫자 및 Smi로 표현할 수 없는 정수
    기존 부동 소수점 숫자는 몇 개의 워드를 차지하는 객체

ElementAccessor

  • 20가지의 요소 유형에 대해 Array 함수를 개별적으로 작성하는 것을 방지
  • Array 함수 호출 시 ElementAccessor를 통해 특정 유형에 맞는 함수 버전으로 디스패치
  • backing store에서 elements에 접근하는 간단한 함수만 구현

typed array, string wrapper에 해당하는 배열은 어떻게?

hidden class에 덧 붙이는 내용

  • JavaScript에서 class는 syntax sugar
  • Hidden class는 어떻게 구현되고 동작할까
function Peak(name, height, extra) {
	this.name = name;
	this.height = height;
	if (isNaN(extra)) {
		this.experience = extra;  
	} else {    
		this.prominence = extra;  
	}
}

m1 = new Peak("Matterhorn", 4478, 1040);
m2 = new Peak("Wendelstein", 1838, "good");

이 때 히든 클래스의 transition은
1. 프로퍼티가 없는 hidden class
2. name
3. name, height
4. name, heigt, promonence / name, height, experience

이 상태에서 7개의 peak 객체를 만들면 3개의 in-object property가 있고 추가적인 프로퍼티는 in-object로 둘 수 없다. 객체의 프로퍼티 backing store에 저장될텐데 이건 위에서 본 프로퍼티 value들의 배열이다. 인덱스는 앞서 봤듯 Descriptor Array에서 가져온다.

이 때 m2 변수에 프로퍼티를 추가를 한 뒤 V8의 관점에서 hidden class들을 보면 프로퍼티들은 모두 const로 보일 텐데 이는 생성자 이후로 아무것도 변경되지 않았기 때문이다.
TurboFan은 이런 상황을 좋아한다.
어떤 함수가 m2를 constant global로 참조한다면 m2.cost 는 컴파일 타임에 알 수 있다.
cost는 backing stroe에 저장되어 있을 것이다. (0번 인덱스에)
`%DebugPrint(m2):`

profile
어떻게 살아야 하나

0개의 댓글