Net-Base Magasin

23.08.2026

Jakte på minnelekkasjar: målretta bruk av FastMM FullDebugMode og korrekt tolking av stacktraces

FastMM FullDebugMode er i Delphi-prosjekt eitt av dei skarpaste verktøya mot minnelekkasjar – men berre dersom ein skrur det på målretta, tolkar rapportane riktig og unngår typiske feilantakingar. Denne praksisartikkelen viser ein ryddig arbeidsflyt frå...

23.08.2026

Frå magasinetema til prosjektpraksis

Passande teneste- og tekniske sider til innlegget

Når ein Delphi-applikasjon i produksjon gradvis «blåser opp», sporadisk feilar med Access Violations eller etter dagar med køyring plutseleg blir ustabil, ligg det ofte ikkje ein enkelt feil bak, men eit mønster: minne blir allokert, men ikkje frigitt skikkeleg — eller det blir frigitt for tidleg og brukt att seinare. Det er her FastMM FullDebugMode er gull verdt. Ikkje som ein permanent tilstand, men som eit målretta diagnoseverktøy som gir ei sporbar årsak ut av «eit eller anna i heapen er øydelagt».

Problemet: FullDebugMode produserer mykje output, kostar ytelse og fører raskt til feiltolkingar. Ein leak-rapport viser ikkje automatisk staden der «feilen» er. Og ein stacktrace er berre så god som symboloppløysinga (MAP-Datei, Debug-Infos, inlining). I dette innlegget går eg gjennom det typiske randtilfellet, forklarer den ryddige tilnærminga og fallgropene – slik at du til slutt ikkje berre finn lekkasjar, men stansar dei varig.

Når FastMM FullDebugMode verkeleg er nyttig

FastMM er i moderne Delphi-versjonar ofte allereie standard minnehandsamar eller blir i mange prosjekt uansett tatt i bruk. FullDebugMode er derimot ei spesiell konfigurasjon: han utstyrer minneblokker med tilleggssjekkmønster, samlar allokasjons-stacktraces og sjekkar meir aggressivt for heap-korrupsjon (altså skadde administrasjonsdata i heapen, t.d. gjennom Buffer-Overruns).

Eg brukar FullDebugMode målretta når eitt av desse scenariera føreligg:

  • Reproduserbart minnelekkasje: minneforbruket aukar i testkøyring per operasjon (t.d. per Request, per Import, per UI-aksjon).
  • Sporadiske AVs: særleg dei som skjer «ein gong her, ein gong der» i same område (klassisk: Use-after-free).
  • Heap-korrupsjon: meldingar som «Invalid pointer operation», «Access violation in ntdll» eller krasj ved avslutting/finalisering.
  • Regresjonssøk: etter refactoring, bibliotekoppdatering eller kompilatorbyte plutseleg ny ustabilitet.

FullDebugMode er ikkje fornuftig som «vi slår det på i alle builds». Overheadet er høgt, timing endrar seg, og særleg race-conditions kan forsvinne eller flytte seg. For kontinuerleg drift passar heller eit lettvint overvaking (t.d. prosessens Working-Set, Private Bytes, telleverk per operasjon) – FullDebugMode er skalpelet, ikkje pulsmålaren.

Hovudprinsipp: Leak-rapport er symptom, Stacktrace er spor

Ein leak-rapport viser deg først: Desse blokkene er framleis allokerte ved programslutt. Det er berre automatisk eit problem dersom desse blokkene eigentleg skulle ha vore frigjevne. Det finst legitime «Leaks»: globale Singletons, Caches, OS-Handles med prosess-livstid eller third-party Bibliotheken som medvite ikkje finaliserer. Desse tilfella vil du kjenne til, men ikkje blint «fikse vekk».

Stacktracen i rapporten viser staden der blokka blei allokert. Det er ofte ikkje staden der du gløymde å kalle Free. Vanleg realitet i vaksne system:

  • Allokasjon i UI- eller servicelaget, frigjering burde skje i eit djupare lag (eigarskap uklar).
  • Allokasjon i ei Factory, eigarskap blir overført til kallaren – men kallaren trur det er «owned».
  • Objekt blir heldt i Collections (Listen, Dictionaries), men eigarskapsmodellen er ikkje konsekvent.
  • Ein exception-veg hoppar over opprydding fordi try/finally manglar eller startar for seint.

Den reine rekkefølgja er derfor: reprodusereisolereStacktrace løyse oppfinne eierskapsfeilfiks med regresjonstest. FastMM gir deg sporene, men du må omsetje dei til arkitektur og livssyklusar.

FastMM FullDebugMode aktivere ryddig (utan å oversjå sideeffektar)

Schematische Grafik mit Heap-Blöcken und Prüfrändern, die in einen Leak-Report überführt werden
Abstrakt: FullDebugMode arbeider med tilleggskontrollgrenser og rapportutdata.

FullDebugMode blir i praksis aktivert via FastMM-opsjonane og ei passende FastMM-konfigurasjon. Avgjerande er mindre «kva heiter include-fila nøyaktig», og meir kva konfigurasjonen gjer og under kva build-betingelsar du brukar han.

Anbefalte rammevilkår for debug-bygg

  • Debug-DCUs og debug-informasjon: Stacktraces er berre nyttige om dei kan løysast til ekte Unit/linje/adresse. Sørg for at debug-informasjon blir generert og at ei MAP-fil er tilgjengeleg.
  • Vel optimalisering med omhug: For lesbarheit av Stacktraces er eit ikkje-optimalisert build som regel betre. Inlining og aggressiv optimalisering kan «viske ut» stackframes.
  • Same kjøretidsvilkår: Bruk så like data, same konfigurasjon, same rettar som mogleg. Mange lekkasjar er dataavhengige (t.d. sjeldne format, spesialvegar).
  • Skill 64-bit og 32-bit: Minnehandsaming, alignment og tredjepartsbibliotek oppfører seg ulikt. Debugg på målplattformen der problemet faktisk oppstår.

Eit poeng som sysadminar og tekniske leiarar ofte undersel: FullDebugMode kan også endre timing. Når threading er involvert, kan race conditions oppføre seg annleis. Difor er det fornuftig å ha parallelt eit køyr utan FullDebugMode som berre stadfestar reproduseringa. FullDebugMode er då steget for diagnose.

Vaksam ved „ReportMemoryLeaksOnShutdown“

Delphi kan via ReportMemoryLeaksOnShutdown melde lekkasjar ved programavslutning. Det er praktisk, men i komplekse applikasjonar (tjenester, Plug-in Host, langvarig drift) kan det villede: Ved shutdown køyrer finaliseringsseksjonar, trådar stoppar, cache blir rydda. Ei lekkasje som er kritisk midt i kjøretida kan vere borte ved slutt — eller omvendt: ei tilsynelatande lekkasje oppstår først i shutdown fordi bakgrunnsarbeid framleis køyrer.

For praktisk leak-hunting er det derfor viktigare å måle lekkasje per operasjon (t.d. etter 100 requests), ikkje berre ved avslutning. FastMM kan hjelpa med dette, men testoppsettet må spegle det.

Det typiske randtilfellet: Leak-rapporten viser „eit eller anna objekt“, men årsaka er eigarskap

Ein klassikar innan bedriftsapplikasjonar: Ein importprosess opprettar per datasett hjelpeobjekt (t.d. StringLists, JSON-Parser, midlertidige lister). I Happy Path blir dei frigitt ryddig. I sjeldne tilfelle (valideringsbetinga Skip, Exception, tidleg Exit) ligg eit objekt att. Etter 10 000 dataposter blir det synleg.

FastMM FullDebugMode hjelper her, fordi han viser allokasjonsstaden. Men «fiksen» er ikkje å «free på staden for allokasjonen». Fiksen er eit robust Ownership-mønster:

  • Den som opprettar eit objekt, er ikkje automatisk Owner.
  • Ownership må vere klart i API-kontrakten (Parameter/Return, dokumentasjon, namnekonvensjonar).
  • Collections må vere entydige: owning vs. non-owning. Blandingsformer straffar seg.
  • Exception-flytar krev tidlege try/finally-blokker.

Når du i stacktracen berre ser «TStringList.Create», er informasjonen ikkje verdilaus – men han seier berre: her blir noko oppretta. Spørsmålet er: kor bør det ende? Og der hjelper arkitekturtenking meir enn debugger-akrobatikk.

Stacktraces korrekt lesen: Kva du verkeleg kan slutte deg til

Nærbilete av ein debugging‑analyse med utydeleg debugger og handskriven call‑chain
I ein stacktrace tel Call‑Chain – ikkje den einskilde linja.

Ein stacktrace frå FastMM er vanlegvis ei liste av returadresser som – med debug‑symbol – blir avbilda på Units, prosedyrar og helst linjenummer. Når du les det, er tre ting avgjerande:

  • Top‑of‑Stack er ikkje alltid feilen: Dei øvste framene er ofte Memory‑Manager/RTL. Det blir interessant der koden din startar.
  • Call‑Chain i staden for einskildlinje: Linja er berre eit punkt. Kjedja viser deg kva for ein veg som førte til allokasjonen.
  • Fleire identiske blokkar: Når FastMM rapporterer fleire lekkasjar av same storleik, er det ofte ein gjentakande veg. Det er bra: du har reproduksjon.

Når linjenummer manglar: MAP‑Datei, Packages, Release‑DCUs

Mange team snublar her: FullDebugMode er aktiv, Leak‑Report kjem, men i staden for Unit/linje får du berre adresser eller kryptiske symbol. Typiske årsaker:

  • Ingen MAP‑Datei eller debug‑info blei generert.
  • Du køyrer mot Release‑DCUs eller tredjeparts‑DLLar utan symbol.
  • Applikasjonen brukar Runtime Packages: då ligg delar av koden i BPLs, og symboloppløysinga må tilpassast det.
  • Optimalisering/Inlining har gjort stacktracen vanskelegare å lese.

I praksis betyr det: For lekkasjesøking treng du ein build som med vilje er «diagnose‑kapabel». Det er eit anna mål enn «mogelegst raskt». Tekniske leiarar bør handsame dette som eiga Build‑profil, så ikkje kvart teammedlem endrar prosjektopsjonar ad hoc.

Vurdere framar: «Interessant» er ofte ei linje lenger opp

Eit døme frå røynda (utan konkret kundekode): Stacktracen viser deg som første frame i koden din ei rutine „LoadConfig“. Du ser der ei objektoppretting. Du legg inn eit Free, lekkasjen forsvinn – og plutseleg krasjar det ein annan stad med Double Free. Kvifor? Fordi „LoadConfig“ set objektet i ein cache, og ein annan kodeveg er allereie Owner og ryddar opp seinare.

Den rette lesinga ville vore: Stacktracen viser deg, kvar blokka blir skapt. Fiksen ligg ofte i definisjonen: Kven eig objektet etter Return? Om du ikkje svarar dette presist, skifter du berre feilmønsteret (Leak → AV).

Heap-korrupsjon vs. Leak: Kvifor FullDebugMode ofte finn den eigentlege skuldaren

Grafik, die einen Buffer-Overrun zeigt, der in benachbarten Speicherbereich überläuft
Heap-korrupsjon viser seg ofte med tidsforskyving – FullDebugMode gjer ho synleg tidlegare.

Mange „Leaks“ er i røynda følgjeproblem: Ein Buffer-Overrun overskriv Heap-Metadaten, Memory-Manageren kan seinare ikkje frigjere reint, og til slutt ser du tilsynelatande tilfeldige Leaks eller Invalid Pointer Operations. FullDebugMode er sterkt her fordi han nyttar sjekkmønster og gjer ekstra valideringar ved Free/Reuse.

Det er viktig å skilje mellom:

  • Leak: Blokka vart allokert og aldri frigjort. Stabiliteten blir dårlegare over tid; krasj er ikkje naudsynt.
  • Use-after-free: Blokka blir frigitt, men blir framleis brukt seinare. Fører til sporadiske AV-ar som er vanskelege å reprodusere.
  • Double Free: Blokka blir frigitt to gonger. Kan krasje med ein gong eller først seinare (når blokka er tekne i bruk på nytt).
  • Heap-Korruption: Nokon skriv utanfor grensene til ein blokk. Symptom er ofte tidsforskyvde.

FullDebugMode er særskilt verdifullt når du ser symptom med tidsforskyving. Den ekstra valideringa gjer feil synlege tidlegare – ofte nett der den feilaktige åtkomsten skjer, ikkje fyrst minutt seinare i eit tilfeldig Free.

Framgangsmåte i prosjekt: Reproduserbar lekkasjejakt i staden for «feilsøking i tåka»

Om du skal jakte på minnelekkasjar, treng du ein prosess som er repeterbar og som kan delast i team. Eg brukar gjerne ein fast diagnoseramme:

1) Reproduksjon i eit deterministisk scenario

Lag ein testsekvens som påliteleg viser lekkasjen: «Start service, prosesser 500 meldingar, stopp service» eller «Opne skjerm X, utfør handling Y 200 gonger». Det er viktig at du dokumenterer sekvensen med parameterar (datagrunnlag, mandant, feature-flags), slik at andre kan etterprøve han.

2) Minimere: Gjer lekkasjen synleg per steg

Om sekvensen tek 20 minutt, del han opp. Målet er at du så raskt som mogleg kan samanlikne «før» og «etter». I store applikasjonar er dette ofte den verkelege tidstyven, ikkje det å fikse feilen.

3) Slå på FullDebugMode og tolke rapporten

No kjem fyrst FastMM FullDebugMode i spel. Samle rapportane, grupper etter blokkstorleik/callstack og sjå etter repetisjonar. Ein enkelt attverande blokk kan vere ein legitim cache. 10.000 identiske blokkar er nesten alltid ei ekte lekkasje.

4) Ownership-Klärung und Fix in der passenden Schicht

Fiks lekkasjar der eigarskap blir definert: Factory, API-Vertrag, Collection-Wrapper. „Schnell ein Free einbauen“ direkte ved Create er ofte feil stad når objektet blir vidaregjeve.

5) Regression: gleiche Sequenz, gleicher Build, gleicher Report

Fiksen er først god når sekvensen køyrer attende utan at verken lekkasjar eller nye minnefeil opptrer. Særleg ved Use-after-free er eit „Leak weg“ ikkje eit bevis, men berre eit nytt symptom.

Typische Fallstricke in Delphi-Code, die FastMM sichtbar macht

Collections und Ownership (Listen, Dictionaries, Interfaces)

Mange lekkasjar kjem ikkje frå kompliserte algoritmar, men frå kvardagslege datastrukturar. To klassiske feilbilete:

  • Ei liste inneheld objekt, men ingen veit kven som frigjev dei. Løysing: owning Liste verwenden eller konsekvent im finally räumen.
  • Eit Dictionary hält Objekte als Values; beim Remove wird der Value nicht freigegeben oder beim Clear vergessen.

I tillegg er Interfaces tricky: Referenzzählung (ARC-ähnlich) er komfortabel, men blanding med objekt-Ownership kan ved sirkulære referansar eller hendingar gje lekkasjar. FullDebugMode viser deg då ofte allokasjonsstien, men årsaka er ein referansesyklus (A hält B über Interface, B hält A über Callback).

Exceptions und frühe Exits

I vaksne Business-Software-System er Exceptions ofte ein del av normal styring (z. B. Validierung, Abbruch, Retry). Problemet er sjeldan Exceptionen sjølv, men vegen rundt: Eit objekt blir oppretta før try/finally, så kjem ei Exception, og oppryddinga blir hoppa over. FullDebugMode levererar dir den Stacktrace der Allokation – und du musst prüfen, ob es einen garantiert ausgeführten Freigabepfad gibt.

Threads und Lebenszeit: „Freigeben im falschen Thread“

Bei VCL/FMX und Services mit Worker-Threads entsteht ein weiterer Randfall: Eit objekt blir i ein Thread oppretta, men i UI-Thread freigegeben (oder umgekehrt), weil man per Queue/Synchronize „nur schnell“ etwas rüberreicht. Det kan fungere, men kan også føre til Use-after-free dersom produsenten arbeider vidare medan konsumenten allereie frigjev.

FastMM FullDebugMode kann hier helfen, weil er zeitversetzte Fehler früher detektiert. Den eigentlege fiksen er likevel eit tydeleg levetidsmodell: klare eigarskapsforhold, overlevering berre via immutable Daten eller eindeutige Ownership-Transfer-Punkte.

Wie du Reports nutzbar machst: Filtern, vergleichen, dokumentieren

I team løner det seg å ikkje berre «anzuschauen» Leak-Reports, men handsame dei som eit artefakt. Tre pragmatiske Maßnahmen, die sich bewährt haben:

  • Baseline-Report: Ein „bekannter Zustand“ (z. B. aktuelle Produktversion) wird einmal mit FullDebugMode durchlaufen und als Referenz abgelegt. Dann erkennst du neue Leaks sofort.
  • Vergleich nach Use-Case: Für kritische Workflows (Import, Export, API-Request, UI-Massenoperation) legst du jeweils eine kurze Sequenz fest, die regelmäßig wiederholbar ist.
  • Dokumentierte „legitime Leaks“: Wenn ein Cache bewusst nicht finalisiert, dokumentiere das. Sonst jagt in sechs Monaten jemand wieder die gleichen Einträge.

Dette er ikkje byråkrati, men ei tidsbesparing: lekkasjejakt blir elles fort ei endelaus løkke, fordi dei same mønstra opptrer i kvar sprint på nytt.

Når innsatsen løner seg – og når du bør gå annleis fram

FastMM FullDebugMode er eit diagnoseverktøy med kostnad. Innsatsen lønner seg særleg når:

  • Applikasjonen køyrer lenge (teneste, terminalserver-klient, skiftsystem, 24/7-prosessar).
  • Du handterer reelle kundedataflytar og ikkje dekkjer alle vegar i testane.
  • Stabilitet er viktigare enn kortsiktig fart i å levere funksjonar (typisk i prosessnære programvareløysingar).

Hvis du derimot berre har ein liten skrivebordsassistent som avsluttar etter 30 sekund, er lekkasjejakt ofte sekundær. Likeeins: Har du eit enkeltståande minne-spike-problem (t.d. stor eksport), er det ofte ikkje eit lekkasje, men eit spørsmål om streamingstrategi og toppbelastning i heapen.

Praktisk konklusjon: FullDebugMode er ikkje ein brytar, men ein prosess

FastMM FullDebugMode gir struktur til søkinga etter minnefeil: Det gjer allokasjonar synlege, avdekkjer heap-korrupsjon tidlegare og leverer stacktraces som du kan bruke til å rette årsaka framfor symptomet. Den avgjerande grepet er likevel ikkje verktøyet, men prosessen: reproduserbare scenario, diagnostiske bygg, klare eigarskapsavtalar og regresjon mot ein baseline.

Hvis du sit fast i eit hardnakka lekkasjeproblem eller sporadiske heap-feil og vil stabilisere temaet varig i eit større Delphi-system, løner det seg med eit kort, ryddig diagnose-oppsett med klar sekvens og evaluerbare rapportar. Treng du støtte til analyse, build-profilar eller arkitektur-refaktorering: ta kontakt med Net-Base Software GmbH.

For dette temaet er òg Delphi Finne minnelekkasje og Lese Fastmm Leak Report viktige. Innlegget set desse aspekta inn i ein forståeleg ramme og viser kva som tel i kvardagen.

Drøft prosjekt eller moderniseringsprosjekt med Net-Base.

neste steg

Når temaet blir eit reelt prosjekt, bør arkitektur, eksisterande system og drift tidleg saman vurderast.

Vi støttar ikkje berre ved enkeltspørsmål, men òg når korte kildekodesnuttar, legacy-tema eller portalidéar skal utviklast til eit robust bedriftsprosjekt.

  • Eksisterande tilstand, målbiletet og tekniske risikoar blir vurderast samla.
  • REST, datatilgang, portalar og utrulling blir ikkje utsett til seinare fasar.
  • De ser tidleg kva veg som er økonomisk og driftsmessig berekraftig.

Del innlegg

Del dette innlegget direkte

LinkedIn, X, XING, Facebook, WhatsApp og e-post er straks tilgjengelege. For Instagram klargjer vi lenke og kort tekst med det same.

E-post

Instagram opnar i ein ny fane. Lenkje og kort tekst blir kopiert til utklippstavla på førehand.