본 발표는 Ruby on Rails 환경에서 ActiveRecord 인스턴스에 새로운 컬럼을 추가할 때 발생하는 메모리 오버헤드를 분석한 결과를 제시합니다. 특히, 정수형 컬럼 추가 시 메모리 사용량 변화에 초점을 맞춰 심층 분석을 수행했습니다. 이 분석은 개발자가 ActiveRecord 인스턴스의 메모리 발자국을 이해하고, 애플리케이션의 성능 및 리소스 사용을 최적화하는 데 중요한 기초 정보를 제공합니다. 분석 결과, 정수형 컬럼 하나를 추가할 경우 ActiveRecord 인스턴스당 224바이트의 메모리가 증가하는 것으로 확인되었습니다. 이는 많은 개발자들이 예상하는 것보다 작거나 클 수 있으며, 그 구성 요소에 대한 이해는 더욱 효율적인 개발을 가능하게 합니다.
메모리 사용량 측정은 ObjectSpace.memsize_of
와 reachable_objects_from
메서드를 활용하여 수행되었습니다. 특히 reachable_objects_from
을 통해 객체 그래프를 탐색하며 정확한 메모리 사용량을 파악했습니다. 분석 결과, 224바이트의 메모리 증가는 주로 네 가지 유형의 객체 증가에 기인합니다. 첫째, 추가된 컬럼의 이름(“some_id”)을 나타내는 문자열 객체가 40바이트를 차지합니다. 둘째, 데이터베이스에서 가져온 어트리뷰트 값을 표현하는 ActiveRecord::Attribute::FromDatabase
객체가 72바이트를 차지합니다. 셋째, 해당 컬럼의 데이터 타입(정수형)을 관리하는 ActiveModel::Type::Integer
객체가 72바이트를 차지합니다. 넷째, 해당 타입 객체가 내부적으로 사용하는 Range
객체가 40바이트를 차지합니다. 이 네 가지 객체의 합산 크기가 224바이트입니다.
이러한 메모리 크기는 C Ruby의 객체 표현 방식과 밀접하게 관련되어 있습니다. C Ruby에서 모든 객체는 RVALUE
라는 고정 크기(40바이트)의 메모리 공간을 사용하여 표현됩니다. RVALUE
는 객체의 타입 정보와 데이터를 담는 구조체로, 헤더와 바디로 구성됩니다. 대부분의 객체는 RVALUE
에 대한 포인터인 VALUE
를 통해 참조되며, VALUE
자체는 8바이트 크기입니다. 그러나 특정 조건에서는 객체의 값이 VALUE
자체에 직접 임베딩될 수 있습니다(VALUE
임베딩). 예를 들어, 작은 정수(Fixnum
), nil
, true
, false
등은 40바이트 RVALUE
를 별도로 할당하지 않고 VALUE
에 직접 값을 저장하여 메모리 효율을 높입니다. 이것이 바로 컬럼에 저장된 실제 정수 값(예: 1)이 추가적인 40바이트를 차지하지 않는 이유입니다.
72바이트를 차지하는 ActiveRecord::Attribute::FromDatabase
와 ActiveModel::Type::Integer
같은 사용자 정의 클래스의 인스턴스는 RObject
구조체를 사용하여 표현됩니다. 이 구조체는 RVALUE
(40바이트) 외에 인스턴스 변수를 저장하기 위한 별도의 메모리 공간(IV 포인터 배열)을 동적으로 할당할 수 있습니다. 인스턴스 변수의 개수에 따라 이 추가 공간의 크기가 결정되며, 본 예시에서는 RVALUE
40바이트에 인스턴스 변수 저장을 위한 공간 32바이트(8바이트 * 4개)가 더해져 총 72바이트가 됩니다. 반면, 문자열(“some_id”)과 Range
객체는 RString
및 RStruct
구조체를 사용하며, 이들은 비교적 작은 값을 RVALUE
바디 내에 직접 저장할 수 있어 별도의 IV 포인터 공간 없이 40바이트로 표현됩니다.
참고로, Ruby 3.2부터는 size pool 개념이 도입되어 RVALUE
슬롯 크기가 다양화되고, VALUE
임베딩 가능한 범위(특히 문자열, 인스턴스 변수 개수)가 확장되어 메모리 효율성이 더욱 향상되었습니다.
결론적으로, Ruby on Rails에서 ActiveRecord 인스턴스에 정수형 컬럼 하나를 추가하는 것은 224바이트의 메모리 오버헤드를 발생시킵니다. 이는 컬럼 이름 문자열, 어트리뷰트 객체, 타입 객체, Range 객체 등 네 가지 주요 객체의 생성 및 관련 메모리 할당에 기인하며, C Ruby의 `RVALUE`, `VALUE` 구조 및 임베딩 메커니즘에 의해 그 크기가 결정됩니다. 이러한 분석은 ActiveRecord 인스턴스가 단순히 데이터베이스 행을 나타내는 것을 넘어, 다양한 메타데이터와 타입 정보를 포함하는 복합적인 객체임을 보여줍니다. 실제 애플리케이션 개발 환경, 특히 메모리 사용에 민감한 대규모 서비스에서는 이러한 미세한 메모리 증가도 누적될 수 있으나, 일반적으로 컬럼 추가 자체가 심각한 메모리 문제를 야기할 가능성은 낮다고 판단할 수 있습니다. 발표자는 이러한 지식을 바탕으로 핵심 테이블의 스키마 설계 시 데이터 분산(Horizontal Partitioning) 등을 고려하는 경험을 공유하며 마무리합니다.