Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Ruby 내부:Ruby 객체의 메모리 레이아웃 탐색

Ruby 내부를 간단히 둘러보고 싶으신가요?

그럼 당신은 대접을받을 것입니다.

때문에

Ruby 객체가 메모리에 배치되는 방식과 내부 데이터 구조를 조작하여 멋진 작업을 수행하는 방법을 함께 탐구할 것입니다.

안전벨트를 매고 Ruby 인터프리터의 세계로 여행을 떠날 준비를 하세요!

배열의 메모리 레이아웃

배열을 생성할 때 Ruby는 약간의 시스템 메모리와 약간의 메타데이터로 이를 백업해야 합니다.

메타데이터 포함 :

  • 배열 크기(항목 수)
  • 어레이 용량
  • 수업
  • 객체 상태(고정 여부)
  • 메모리에서 데이터가 저장된 위치에 대한 포인터

기본 Ruby 인터프리터(MRI)는 C로 작성되어 있으므로 개체가 없습니다.

하지만 다른 것이 있습니다. 구조체 .

C의 구조체는 관련 데이터를 함께 저장하는 데 도움이 되며, 이는 Array와 같은 것을 나타내기 위해 MRI의 소스 코드에서 많이 사용됩니다. , String 의 및 기타 종류의 개체입니다.

이러한 구조체 중 하나를 보고 개체의 메모리 레이아웃을 유추할 수 있습니다.

Array의 구조체를 살펴보겠습니다. , RArray라고 함 :

struct RArray {
  struct RBasic basic;

  union {
    struct {
      long len;

      union {
        long capa;
        VALUE shared;
      } aux;

      const VALUE *ptr;
    } heap;

    const VALUE ary[RARRAY_EMBED_LEN_MAX];
  } as;
};

C에 익숙하지 않다면 이것이 다소 위협적으로 보일 수 있다는 것을 알고 있지만 걱정하지 마십시오! 나는 이것을 소화하기 쉬운 부분으로 분해하는 데 도움을 줄 것입니다 🙂

우리가 가지고 있는 첫 번째 것은 이 RBasic입니다. 구조체이기도 한 것:

struct RBasic {
  VALUE flags;
  VALUE klass;
}

이것은 대부분의 Ruby 객체가 가지고 있으며 이 객체에 대한 클래스와 이 객체가 고정되었는지 여부(및 '오염된' 속성과 같은 기타 사항)를 알려주는 일부 바이너리 플래그와 같은 몇 가지를 포함합니다.

:

RBasic 개체에 대한 일반 메타데이터를 포함합니다.

그 후에 배열의 길이를 포함하는 또 다른 구조체가 있습니다(len ).

통합 표현식은 aux capa일 수 있습니다. (용량용) 또는 shared . 이것은 대부분 최적화에 관한 것이며 Pat Shaughnessy의 이 훌륭한 게시물에서 더 자세히 설명합니다. 메모리 할당 측면에서 컴파일러는 유니온 내에서 가장 큰 유형을 사용합니다.

그런 다음 ptr이 있습니다. , 실제 Array가 있는 메모리 주소를 포함합니다. 데이터가 저장됩니다.

다음은 이것이 어떻게 보이는지 그림입니다(모든 흰색/회색 상자는 32비트 시스템에서 4바이트 ):

Ruby 내부:Ruby 객체의 메모리 레이아웃 탐색

ObjectSpace 모듈을 사용하여 개체의 메모리 크기를 볼 수 있습니다.

require 'objspace'

ObjectSpace.memsize_of([])
# 20

이제 즐거운 시간을 보낼 준비가 되었습니다!

Fiddle:재미있는 실험

RBasic은 32비트 시스템에서 정확히 8바이트이고 64비트 시스템에서 16바이트입니다. 이것을 알면 Fiddle 모듈을 사용하여 개체의 원시 메모리 바이트에 액세스하고 재미있는 실험을 위해 변경할 수 있습니다.

예를 들어 :

단일 비트를 토글하여 고정 상태를 변경할 수 있습니다.

이것은 본질적으로 freeze 방법이 하는 일이지만 어떻게 unfreeze 방법이 없는지 확인하십시오.

재미로 구현해 봅시다!

먼저 Fiddle 모듈(Ruby 표준 라이브러리의 일부) 및 고정 문자열을 만듭니다.

require 'fiddle'

str = 'water'.freeze
str.frozen?
# true

다음:

우리는 문자열에 대한 메모리 주소가 필요합니다. 이것은 다음과 같이 얻을 수 있습니다.

memory_address = str.object_id * 2

마지막으로:

Ruby가 객체가 고정되었는지 확인하기 위해 검사하는 정확한 비트를 뒤집습니다. 또한 frozen?을 호출하여 이것이 작동하는지 확인합니다. 방법.

Fiddle::Pointer.new(memory_address)[1] ^= 8

str.frozen?
# false

인덱스 [1] 플래그의 두 번째 바이트를 나타냅니다. 값(총 4바이트로 구성됨).

그런 다음 ^=를 사용합니다. 이것은 해당 비트를 뒤집는 "XOR"(배타적 OR) 연산자입니다.

플래그 내부의 비트가 다르기 때문에 이렇게 하는 것입니다. 의미가 다르며 관련 없는 것을 변경하고 싶지 않습니다.

내 루비 트릭 게시물을 읽었다면 전에 이것을 보았을 수도 있지만 이제 어떻게 작동하는지 알 것입니다 🙂

시도할 수 있는 또 다른 방법은 배열의 길이를 변경하고 배열을 인쇄하는 것입니다.

어레이가 어떻게 짧아지는지 알 수 있습니다!

클래스를 변경하여 Array를 만들 수도 있습니다. String이라고 생각하세요. …

결론

Ruby가 내부적으로 어떻게 작동하는지에 대해 조금 배웠습니다. Ruby 객체의 메모리 배치 방법 및 Fiddle 사용 방법 가지고 놀 수 있는 모듈입니다.

Fiddle을 사용하면 안 됩니다. 실제 앱에서 이와 같지만 실험해 보는 것은 재미있습니다.

이 게시물을 공유하는 것을 잊지 마세요. 더 많은 분들이 보실 수 있도록 🙂