FixThisBug.de Logo
FixThisBug.de
🇬🇧Bugfix WissensdatenbankAnmelden
Startseite
ImpressumDatenschutzerklärung
Kategorie: luaSchwierigkeit: ProVeröffentlicht:

Speicherverwaltung mit Tabellen in Lua

Das Problem

Schlechte Tabellenverwaltung in Lua kann zu Speicherlecks, übermäßiger Speichernutzung und Leistungseinbußen führen, besonders in lang laufenden Anwendungen.

-- Fehlerbeispiel 1: Speicherleck durch zirkuläre Referenzen local function erzeugeKnoten() local knoten1 = {} local knoten2 = {} knoten1.next = knoten2 knoten2.prev = knoten1 -- Zirkuläre Referenz return knoten1 end -- Fehlerbeispiel 2: Wachsende Tabellen ohne Bereinigung local cache = {} local function verarbeiteDaten(daten) cache[#cache + 1] = daten -- Wächst unbegrenzt end

Warum das passiert

Luas Garbage Collector kann keinen Speicher freigeben, wenn:

  1. Tabellen zirkuläre Referenzen enthalten
  2. Tabellen unbegrenzt wachsen ohne Bereinigung
  3. Tabellen Referenzen auf ungenutzte große Objekte halten
  4. Schwache Referenzen nicht genutzt werden, wo angebracht

Die Lösung

  1. Verwendung schwacher Tabellen zum Brechen von Referenzzyklen:
-- Lösung 1: Schwache Referenzen local function erzeugeKnoten() local knoten1 = {} local knoten2 = {} -- Erstelle schwache Referenztabelle local refs = setmetatable({}, {__mode = "v"}) knoten1.next = knoten2 refs[1] = knoten2 -- Speichere schwache Referenz return knoten1, refs end
  1. Implementierung eines größenbeschränkten Caches:
-- Lösung 2: LRU-Cache Implementierung local LRUCache = {} LRUCache.__index = LRUCache function LRUCache.new(maxGroesse) return setmetatable({ maxGroesse = maxGroesse, elemente = {}, zugriffe = {} }, LRUCache) end function LRUCache:setze(schluessel, wert) if #self.elemente >= self.maxGroesse then -- Entferne am längsten nicht genutztes Element local lru = table.remove(self.zugriffe, 1) self.elemente[lru] = nil end self.elemente[schluessel] = wert table.insert(self.zugriffe, schluessel) end
  1. Tabellen-Pooling für häufige Allokationen:
-- Lösung 3: Objekt-Pool local Pool = {} Pool.__index = Pool function Pool.new() return setmetatable({ frei = {}, aktiv = setmetatable({}, {__mode = "v"}) -- Schwache Werte }, Pool) end function Pool:nehmen() local obj = table.remove(self.frei) or {} self.aktiv[obj] = true return obj end function Pool:zurueckgeben(obj) if self.aktiv[obj] then self.aktiv[obj] = nil table.insert(self.frei, obj) for k in pairs(obj) do obj[k] = nil -- Objekt leeren end end end

Best Practices

  1. Schwache Tabellen für Caches und Observer verwenden
  2. Größenlimits für wachsende Sammlungen implementieren
  3. Tabellenfelder leeren, wenn nicht mehr benötigt
  4. Tabellen-Pools für häufige Allokationen/Deallokationen nutzen
  5. Unnötige Tabellenerstellung in Schleifen vermeiden

Speicher-Profiling

-- Speichernutzung verfolgen local function getSpeichernutzung() return collectgarbage("count") * 1024 -- in Bytes end local function verfolgeSpeicer(fn) local vorher = getSpeichernutzung() fn() local nachher = getSpeichernutzung() print(string.format("Speicheränderung: %.2f KB", (nachher - vorher) / 1024)) end

Häufige Fallstricke

-- Fallstrick 1: Zu lange gehaltene Referenzen local function verarbeiteGrosseDaten(daten) local ergebnisse = {} for i = 1, #daten do ergebnisse[i] = schwereBerechnnung(daten[i]) -- Ergebnisse sofort verarbeiten statt zu speichern verarbeiteErgebnis(ergebnisse[i]) ergebnisse[i] = nil -- Referenz löschen end end -- Fallstrick 2: Nicht geleerte Tabellenfelder local function tabelleWiederverwenden(t) -- Falsch: nur Werte überschreiben t[1] = neuerWert -- Richtig: alte Werte zuerst löschen for k in pairs(t) do t[k] = nil end t[1] = neuerWert end

Beispiel für schwache Referenzen

-- Implementierung eines Eventsystems mit schwachen Referenzen local EventSystem = {} function EventSystem.new() return setmetatable({ listener = setmetatable({}, {__mode = "k"}) }, {__index = EventSystem}) end function EventSystem:abonnieren(beobachter, callback) self.listener[beobachter] = callback end function EventSystem:auslösen(ereignis) for beobachter, callback in pairs(self.listener) do callback(ereignis) end end

Verwandte Konzepte

  • Garbage Collection
  • Referenzzählung
  • Schwache Referenzen
  • Objekt-Pooling
  • Cache-Invalidierung

Mehr erfahren

  • Lua Referenzhandbuch: Garbage Collection
  • Programming in Lua: Garbage Collection

Selbst ausprobieren

Verbleibende Korrekturen: 10