Memory Overhead of RTTI in C++

Run-Time Type Information (RTTI) in C++ allows programs to safely query object types at runtime via operators like typeid and dynamic_cast. While RTTI provides powerful mechanisms for introspection and type-safe downcasting, it incurs memory overhead in both binary footprint (disk space/read-only memory) and dynamic execution memory. This article examines the architectural components that contribute to RTTI memory usage, breaks down per-class and per-object costs, and explains where this overhead manifests.

1. Per-Instance Overhead (Objects)

For individual object instances, RTTI typically incurs zero additional memory overhead beyond what is already required for polymorphism.

In standard C++ implementations (such as the Itanium C++ ABI used by GCC and Clang, as well as the MSVC ABI), RTTI is linked directly to the virtual method table mechanism:

  • If a class is already polymorphic (contains at least one virtual function), the object already contains a virtual pointer (vptr), which typically occupies 8 bytes on a 64-bit architecture or 4 bytes on a 32-bit architecture.
  • The runtime uses this existing vptr to reach both virtual functions and RTTI metadata. Thus, instantiating millions of objects of a polymorphic type consumes no additional per-object RAM when RTTI is enabled.
  • Non-polymorphic types do not gain a vptr simply because RTTI is enabled globally, although using typeid on a non-polymorphic object evaluates statically at compile time.

2. Per-Class Overhead (Virtual Tables)

The first direct cost of RTTI appears in the virtual table (vtable) generated for each polymorphic class:

  • Vtable Pointer to Type Info: ABI specifications designate an entry in the vtable structure (typically at index -1, right before the virtual function pointers) reserved for a pointer to the type’s descriptor.
  • Cost: 8 bytes per vtable on 64-bit systems (4 bytes on 32-bit systems). If a class uses multiple inheritance, it may generate multiple vtables or sub-vtables, each carrying its own pointer to type information.

3. Type Descriptor Metadata (std::type_info)

The largest component of RTTI overhead resides in the static metadata structures generated by the compiler to describe types. These structures are stored in read-only data segments (.rodata on ELF systems, or .rdata on PE/Windows systems).

For every class that requires runtime type resolution, the compiler emits a specialized descriptor derived from std::type_info:

  • Base std::type_info Structure: Contains a pointer to its own virtual table, along with an internal pointer to the type's mangled name string. This occupies roughly 16 to 24 bytes on 64-bit platforms.
  • Type Name String: A null-terminated string containing the mangled name of the type (e.g., _ZTS3Foo). The length of this string depends directly on the length of the class name, its enclosing namespaces, and template parameters. For heavily templated code, mangled names can span hundreds or thousands of bytes per class.
  • Inheritance Descriptors: For types involved in inheritance hierarchies, the compiler emits extended structures (such as __si_class_type_info for single inheritance or __vmi_class_type_info for virtual/multiple inheritance):
    • Single Inheritance: Adds a pointer to the base class's type descriptor (~8 bytes).
    • Multiple or Virtual Inheritance: Includes flags, counts, base class offsets, and pointers to the descriptors of all parent classes. This data enables dynamic_cast to navigate complex class hierarchies and adjust pointers correctly at runtime.

4. Summary of Overhead Characteristics

  • Per Object: 0 bytes (reuses the existing vptr).
  • Per Vtable: 8 bytes (pointer to std::type_info).
  • Per Class (Metadata): Typically 40 to 100+ bytes for basic classes, scaling up significantly with long names, extensive template specializations, and complex multiple inheritance hierarchies.
  • Binary Footprint: Cumulative growth of the executable or shared library size due to mangled strings and inheritance graphs, which can impact instruction cache efficiency and startup memory footprint (working set).

In resource-constrained environments such as embedded systems, operating system kernels, or high-performance game engines, this static footprint across thousands of types often motivates disabling RTTI via compiler flags such as -fno-rtti (GCC/Clang) or /GR- (MSVC).