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:
- Tabellen zirkuläre Referenzen enthalten
- Tabellen unbegrenzt wachsen ohne Bereinigung
- Tabellen Referenzen auf ungenutzte große Objekte halten
- Schwache Referenzen nicht genutzt werden, wo angebracht
Die Lösung
- 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
- 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
- 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
- Schwache Tabellen für Caches und Observer verwenden
- Größenlimits für wachsende Sammlungen implementieren
- Tabellenfelder leeren, wenn nicht mehr benötigt
- Tabellen-Pools für häufige Allokationen/Deallokationen nutzen
- 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