Zum Hauptinhalt springen
BlockchainMar 28, 2026

Deep EVM #2: Speichermodell — Stack, Memory, Storage und Calldata

OS
Open Soft Team

Engineering Team

Vier Datenbereiche der EVM

Die EVM stellt vier unterschiedliche Datenbereiche bereit, jeder mit radikal unterschiedlichen Kosten, Lebensdauern und Zugriffsmustern. Die falsche Wahl ist die haeufigste Ursache fuer uebermassigen Gasverbrauch in Smart Contracts.

Stack

Der Stack ist der primaere Arbeitsbereich der EVM. Er ist 1024 Elemente tief, wobei jedes Element 256 Bit breit ist. Der Zugriff auf den Stack ist kostenlos (im Gas-Sinne) — nur 3 Gas fuer DUP- und SWAP-Operationen. Aber der Stack hat eine entscheidende Einschraenkung: Sie koennen nur auf die obersten 16 Elemente zugreifen.

Memory (Arbeitsspeicher)

Memory ist ein linearer Byte-Array, der bei Null beginnt und nach Bedarf waechst. Es ist fluechtig — nach Beendigung des Aufrufkontexts wird er verworfen. Der Zugriff erfolgt ueber MLOAD (32 Bytes lesen) und MSTORE (32 Bytes schreiben), jeweils fuer 3 Gas.

Der Haken: Die Speichererweiterungskosten steigen quadratisch. Die erste Erweiterung ist billig, aber bei grossen Speichermengen explodieren die Kosten.

// Speichererweiterungskosten-Formel:
// cost = 3 * words + words^2 / 512
// wobei words = ceil(size / 32)

// Beispiele:
// 32 Bytes (1 Wort):    3 Gas
// 1 KB (32 Woerter):    99 Gas
// 32 KB (1024 Woerter): 5120 Gas
// 1 MB:                 ~3 Millionen Gas

Storage (Permanenter Speicher)

Storage ist der permanente Speicher eines Contracts — er ueberlebt zwischen Transaktionen. Jeder Slot ist 256 Bit breit, adressiert durch einen 256-Bit-Schluessel. Storage ist bei weitem der teuerste Datenbereich:

  • Erster Schreibvorgang (0 -> ungleich Null): 22.100 Gas
  • Aenderung (ungleich Null -> ungleich Null): 5.000 Gas
  • Lesen: 2.100 Gas (kalt) / 100 Gas (warm)

Warum so teuer? Weil jeder Storage-Schreibvorgang von jedem Knoten im Ethereum-Netzwerk fuer immer gespeichert werden muss.

Calldata

Calldata sind die schreibgeschuetzten Eingabedaten einer Transaktion. Sie koennen waehrend der Ausfuehrung nicht veraendert werden. Calldata sind guenstig: 16 Gas pro Nicht-Null-Byte, 4 Gas pro Null-Byte. Deshalb kodiert Solidity Funktionsargumente als Calldata.

Transient Storage (EIP-1153)

EIP-1153 (Dencun-Upgrade, Maerz 2024) fuehrte einen fuenften Datenbereich ein: Transient Storage. Er verhielt sich wie normaler Storage, wird aber am Ende der Transaktion verworfen. Die neuen Opcodes TLOAD und TSTORE kosten nur 100 Gas — deutlich weniger als die 2.100/22.100 des normalen Storage.

Der primaere Anwendungsfall ist Reentrancy-Schutz. Das klassische Reentrancy-Lock erfordert einen SSTORE (teuer). Mit Transient Storage wird das Lock nach der Transaktion automatisch zurueckgesetzt, ohne dass Sie es manuell loeschen muessen.

Speicherlayout in Solidity

Der Solidity-Compiler packt Zustandsvariablen in Storage-Slots nach bestimmten Regeln:

  • Variablen werden in der Reihenfolge ihrer Deklaration in aufeinanderfolgende Slots gepackt.
  • Wenn eine Variable in den verbleibenden Platz des aktuellen Slots passt, wird sie dort platziert.
  • Andernfalls wird ein neuer Slot begonnen.
  • Structs und Arrays beginnen immer einen neuen Slot.
contract StorageLayout {
    // Slot 0: [uint128 a | uint128 b] — gepackt!
    uint128 a; // 16 Bytes
    uint128 b; // 16 Bytes — passt in gleichen Slot
    
    // Slot 1: uint256 c — belegt ganzen Slot
    uint256 c;
    
    // Slot 2: [bool d | address e] — 1 + 20 = 21 Bytes, gepackt
    bool d;    // 1 Byte
    address e; // 20 Bytes
}

Das Verstaendnis des Storage-Layouts ist entscheidend fuer Gasoptimierung. Wenn zwei Variablen, die oft zusammen gelesen werden, in verschiedenen Slots liegen, zahlen Sie fuer zwei Storage-Lesevorgaenge statt einen.

Memory-Layout und Free Memory Pointer

Solidity verwendet einen Free Memory Pointer bei Adresse 0x40, der auf das naechste freie Byte im Memory zeigt. Wenn Sie neuen Memory allozieren, inkrementiert der Compiler den Pointer.

// Memory-Layout:
// 0x00 - 0x3f: Scratch-Space (temporaerer Arbeitsbereich)
// 0x40 - 0x5f: Free Memory Pointer
// 0x60 - 0x7f: Zero-Slot (immer Null, als Anfangswert fuer dynamische Arrays)
// 0x80 - ...:  Frei verfuegbarer Memory

In Yul oder Assembly muessen Sie den Free Memory Pointer manuell verwalten. Wenn Sie ihn vergessen zu aktualisieren, ueberschreiben nachfolgende Allokationen Ihre Daten.

Praktische Optimierung: Datenbereich-Wahl

  1. Verwenden Sie Calldata statt Memory fuer Funktionsargumente — externe Funktionen mit calldata-Parametern sparen Gas, da keine Kopie in Memory erstellt wird.
  2. Packen Sie Storage-Variablen — ordnen Sie Variablen so an, dass sie in minimale Slots passen.
  3. Cachen Sie Storage-Lesevorgaenge in lokalen Variablen — ein kalter SLOAD kostet 2.100 Gas; ein Memory-Lesevorgang kostet 3 Gas.
  4. Verwenden Sie Transient Storage fuer temporaere Flags — Reentrancy-Locks, kurzfristige Zustandsflags.
  5. Vermeiden Sie grosse Memory-Allokationen — die quadratische Kostenerweiterung macht grosse Arrays extrem teuer.

Fazit

Die vier Datenbereiche der EVM — Stack, Memory, Storage und Calldata — haben jeweils spezifische Eigenschaften und Kosten. Die richtige Wahl des Datenbereichs ist der wichtigste Faktor fuer die Gaseffizienz Ihres Smart Contracts. Verstehen Sie die Kosten, verstehen Sie die Einschraenkungen, und Ihre Contracts werden deutlich guenstiger in der Ausfuehrung.