Net-Base Magasin

23.08.2026

Spor opp minnelekkasjer: Bruk FastMM FullDebugMode målrettet og les stacktraces korrekt

FastMM FullDebugMode er i Delphi-prosjekter ett av de mest effektive verktøyene mot minnelekkasjer – men bare hvis man aktiverer det målrettet, tolker rapportene riktig og unngår typiske feilantakelser. Denne praksisartikkelen viser en ryddig prosess fra...

23.08.2026

Fra magasinetema til prosjektpraksis

Egnede tjeneste- og tekniske sider for innlegget

Når en Delphi-applikasjon i drift langsomt «blåser opp», sporadisk krasjer med Access Violations eller etter flere dagers drift plutselig blir ustabil, ligger det ofte ikke en enkelt feil bak, men et mønster: Minne blir allokert, men ikke frigjort ordentlig – eller det blir frigjort for tidlig og senere fortsatt brukt. Nettopp her er FastMM FullDebugMode gull verdt. Ikke som en permanent tilstand, men som et målrettet diagnoseverktøy som gjør «noe i heapen er ødelagt et eller annet sted» til en etterprøvbar årsak.

Ulempen: FullDebugMode produserer mye output, koster ytelse og fører raskt til feiltolkninger. En leak-rapport viser ikke automatisk stedet der «feilen» er. Og en stacktrace er bare så god som symboloppløsningen (MAP-fil, debug-informasjon, inlining). I dette innlegget går jeg gjennom det typiske grensetilfellet, forklarer en ren tilnærming og fallgruvene – slik at du til slutt ikke bare finner lekkasjer, men lukker dem varig.

Når FastMM FullDebugMode virkelig gir mening

FastMM er i moderne Delphi-versjoner ofte allerede standard memory manager eller blir uansett tatt i bruk i mange prosjekter. FullDebugMode er derimot en spesiell konfigurasjon: Den merker minneblokker med ekstra kontrollmønstre, samler allokerings-stacktraces og sjekker mer aggressivt for heap-korrupsjon (dvs. ødelagte styringsdata i heapen, f.eks. på grunn av buffer-overrun).

Jeg bruker FullDebugMode målrettet når ett av disse scenariene foreligger:

  • Reproduserbart Leak: Minneforbruk øker i testrun per operasjon (f.eks. per Request, per Import, per UI-aksjon).
  • Sporadiske AVs: Spesielt de som «noen ganger her, noen ganger der» skjer i samme område (klassisk: Use-after-free).
  • Heap-korrupsjon: Meldinger som «Invalid pointer operation», «Access violation in ntdll» eller krasj ved avslutning/finalisering.
  • Regresjonssøk: Etter refaktorering, library-update eller compilerwechsel plutselig ny ustabilitet.

FullDebugMode er ikke fornuftig som «vi slår det på i alle builds». Overheadet er høyt, timing endres, og spesielt race-conditions kan forsvinne eller flytte seg som følge av det. For kontinuerlig drift egner et lettvektsmonitorering seg bedre (f.eks. prosessens Working Set, Private Bytes, tellere per operasjon) – FullDebugMode er skalpellen, ikke pulssensoren.

Grunnprinsipp: Leak-rapport er symptom, Stacktrace er spor

En leak-rapport viser deg først og fremst: Disse blokkene er fortsatt allokert ved programmets slutt. Det er bare automatisk et problem hvis disse blokkene egentlig skulle vært frigjort. Det finnes legitime «leaks»: globale Singletons, Caches, OS-Handles med prosess-lifetime eller third-party biblioteker som bevisst ikke finaliserer. Disse tilfellene bør du kjenne til, men ikke blindt «fikse unna».

Stacktracen i rapporten viser stedet der blokken ble allokert. Det er ofte ikke stedet der du «glemte å kalle Free». Vanlig realitet i voksende systemer:

  • Allokering i UI- eller service-laget, frigivelse bør skje i et lavere lag (Ownership uklart).
  • Allokering i en Factory, eierskapet overføres til kalleren – men kalleren tror det er «owned».
  • Objekter holdes i Collections (Lister, Dictionaries), men eierskapsmodellen er ikke konsistent.
  • En exception-vei hopper over cleanup fordi try/finally mangler eller starter for sent.

Den ryddige fremgangsmåten er derfor: reprodusereisolereløse opp Stacktracefinne Ownership-feilenfikse med regresjonstest. FastMM gir deg sporene, men du må oversette dem til arkitektur og livssykluser.

FastMM FullDebugMode aktivere korrekt (uten å overse bivirkninger)

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

FullDebugMode aktiveres i praksis via FastMM-innstillingene og en passende FastMM-konfigurasjon. Avgjørende er mindre «hvordan heter include-filen nøyaktig», og mer hva konfigurasjonen gjør og under hvilke build-betingelser du bruker den.

Anbefalte rammebetingelser for debug-build

  • Debug DCUs og debug-informasjon: Stacktraces er bare nyttige hvis de kan løses til ekte unit/linje/adresse. Sørg for at debug-informasjon genereres og at en MAP-fil er tilgjengelig.
  • Velg optimalisering med omhu: For lesbarhet av stacktraces er en ikke-optimalisert build som regel bedre. Inlining og aggressiv optimalisering kan gjøre stackframes utydelige.
  • Samme kjøretidsbetingelser: Bruk så like data, samme konfigurasjon og samme rettigheter som mulig. Mange lekkasjer er dataavhengige (f.eks. sjeldne formater, særskilte kodeveier).
  • Skill 64-bit og 32-bit: Minneatferd, alignment og tredjepartsbiblioteker varierer. Debugg på målplattformen der problemet oppstår.

Et punkt som admins og tekniske ledere ofte undervurderer: FullDebugMode kan også påvirke timing. Hvis du har threading i bildet, kan race conditions opptre annerledes. Derfor er det fornuftig å ha et samtidig kjør uten FullDebugMode som kun bekrefter reproduksjonen. FullDebugMode er da steget for diagnose.

Forsiktighet ved „ReportMemoryLeaksOnShutdown“

Delphi kan via ReportMemoryLeaksOnShutdown rapportere lekkasjer ved programavslutning. Det er praktisk, men i komplekse applikasjoner (services, plug-in host, lange kjøretider) kan det villede: Ved shutdown kjører finaliseringsseksjoner, tråder stopper, cacher tømmes. En lekkasje som er kritisk midt i kjøretiden kan forsvinne mot slutten — eller omvendt: en tilsynelatende lekkasje oppstår først ved shutdown fordi bakgrunnsarbeid fortsatt pågår.

For praktisk leak-hunting er det derfor viktigere å måle: lekkasje per operasjon (f.eks. etter 100 forespørsler), ikke bare ved avslutning. FastMM kan hjelpe med dette, men testoppsettet må reflektere det.

Det typiske randtilfellet: Leak-rapporten viser „et eller annet objekt“, men årsaken er Ownership

En klassiker i bedriftsapplikasjoner: En importprosess oppretter for hver post hjelpeobjekter (f.eks. StringLists, JSON-parsere, midlertidige lister). I «happy path» frigjøres de korrekt. I sjeldne tilfeller (valideringsbetinget skip, exception, tidlig exit) blir et objekt hengende. Etter 10 000 poster blir det synlig.

FastMM FullDebugMode hjelper her, fordi det viser allokasjonsstedet. Men «fiksen» er ikke «free på stedet for allokasjonen». Fiksen er et robust Ownership-Pattern:

  • Den som oppretter et objekt, er ikke automatisk Owner.
  • Ownership må være tydelig i API-kontrakten (parametre/return, dokumentasjon, navnekonvensjoner).
  • Collections må være entydige: owning vs. non-owning. Blandingsformer slår tilbake.
  • Exception-stier trenger tidlige try/finally-blokker.

Hvis du i stacktracen bare ser „TStringList.Create“, er informasjonen ikke ubrukelig – men den sier deg bare: her oppstår noe. Spørsmålet er: hvor bør det ende? Og der hjelper arkitekturtenkning mer enn debugger-akrobatikk.

Les stacktraces korrekt: Hva du egentlig kan utlede

Detailaufnahme einer Debugging-Analyse mit unscharfem Debugger und handnotierter Call-Chain
I en stacktrace teller kallkjeden – ikke den enkelte linjen.

En stacktrace fra FastMM er vanligvis en liste over returadresser som – med debug-symboler – kan mappes til units, prosedyrer og ideelt sett linjenumre. Når du leser den, er tre ting avgjørende:

  • Top-of-Stack er ikke alltid feilen: De øverste frames er ofte memory-manager/RTL. Det blir interessant der hvor koden din begynner.
  • Call-Chain i stedet for enkeltlinje: Linjen er bare ett punkt. Kjeden viser hvilken vei som førte til allokasjonen.
  • Flere identiske blokker: Hvis FastMM rapporterer flere lekkasjer av samme størrelse, er det ofte en gjentakende sti. Det er bra: du har reproducerbarhet.

Hvis linjenumre mangler: MAP-Datei, Packages, Release-DCUs

Mange team snubler her: FullDebugMode er aktiv, leak-rapporten kommer, men i stedet for unit/linje får du bare adresser eller kryptiske symboler. Typiske årsaker:

  • Ingen MAP-fil eller debug-informasjon ble generert.
  • Du kjører mot Release-DCUs eller tredjeparts-DLLer uten symboler.
  • Applikasjonen bruker Runtime Packages: da ligger deler av koden i BPL-er, og symboloppløsningen må være tilpasset.
  • Optimalisering/inlining har gjort stacktracen mindre lesbar.

I praksis betyr det: For lekkasjejakt trenger du en build som bevisst er «diagnosebar». Det er et annet mål enn «så raskt som mulig». Tekniske leads bør behandle dette som et eget build-profil, slik at ikke hvert teammedlem ad hoc endrer prosjektinnstillinger.

Frames bewerten: „Interessant“ ist oft eine Zeile weiter oben

Et eksempel fra virkeligheten (uten konkret kundekode): Stacktracen viser deg som første frame i koden din en rutine „LoadConfig“. Du ser en objektopprettelse der. Du legger inn et Free, lekkasjen er borte – og plutselig krasjer det et annet sted med Double Free. Hvorfor? Fordi „LoadConfig“ plasserer objektet i en cache, og en annen kodebane er allerede owner og rydder opp senere.

Riktig lesning ville vært: Stacktracen viser deg, hvor blokken oppstår. Fiksen ligger ofte i definisjonen: Hvem eier objektet etter return? Hvis du ikke svarer dette tydelig, bytter du bare feilbildet (Leak → AV).

Heap-Korruption vs. Leak: Warum FullDebugMode oft den echten Übeltäter findet

Grafikk, die einen Buffer-Overrun zeigt, der in benachbarten Speicherbereich überläuft
Heap-Korruption zeigt sich oft zeitversetzt – FullDebugMode macht sie früher sichtbar.

Mange „Leaks“ er i virkeligheten følgefeil: En Buffer-Overrun overskriver Heap-Metadaten, der Memory-Manager kann später nicht sauber freigeben, und am Ende siehst du scheinbar zufällige Leaks oder Invalid Pointer Operations. FullDebugMode er sterkt her, fordi er arbeider med Prüfmustern og utfører ekstra valideringer ved Free/Reuse.

Viktig er forskjellen:

  • Leak: Blokken ble allokert og aldri frigitt. Stabiliteten svekkes over tid, krasj er ikke nødvendigvis gitt.
  • Use-after-free: Blokken blir frigitt, men brukes senere fortsatt. Fører til sporadiske AVs som er vanskelige å reprodusere.
  • Double Free: Blokken frigjøres to ganger. Kan krasje umiddelbart eller først senere (når blokken er blitt gjenbrukt).
  • Heap-Korruption: Noen skriver over grensene til en blokk. Symptomene kommer ofte med forsinkelse.

FullDebugMode er spesielt verdifullt når du ser symptomer forsinket. Den ekstra valideringen gjør feil synlige tidligere – ofte akkurat der det feilaktige tilgangen skjer, ikke først minutter senere i et hvilket som helst Free.

Vorgehen in Projekten: Reproduzierbare Leak-Jagd statt „Debugging im Nebel“

Hvis du vil jage minnelekasjer, trenger du en prosess som er repeterbar og kan deles i team. Jeg foretrekker å arbeide med en fast diagnoseramme:

1) Reproduktion in einem deterministischen Szenario

Sett opp en testsekvens som pålitelig viser lekkasjen: „Start service, prosesser 500 meldinger, stopp service“ eller „Åpne skjerm X, utfør handling Y 200 ganger“. Det er viktig at du dokumenterer sekvensen med parametere (datagrunnlag, klient, feature-flags), slik at andre kan gjenskape den.

2) Minimieren: Leak pro Schritt sichtbar machen

Hvis sekvensen tar 20 minutter, del den opp. Målet er at du så raskt som mulig kan sammenligne „før“ og „etter“. I store applikasjoner er dette ofte den egentlige tidstyven, ikke selve fiksen.

3) FullDebugMode einschalten und Report interpretieren

Først nå kommer FastMM FullDebugMode inn i bildet. Samle rapportene, grupper etter blokstørrelse/callstack og se etter gjentakelser. En enkelt gjenværende blokk kan være en legitim cache. 10.000 identiske blokker er nesten alltid en reell lekkasje.

4) Avklaring av eierskap og fiks i riktig lag

Fiks lekkasjer der eierskapet defineres: Factory, API-kontrakt, Collection-Wrapper. Å raskt sette inn en Free rett ved Create er ofte feil sted når objektet videreføres.

5) Regresjon: samme sekvens, samme build, samme rapport

Fiksen er først god når sekvensen går igjen uten at verken lekkasjer eller nye minnefeil oppstår. Spesielt ved Use-after-free er en ‚lekkasje borte‘ ikke bevis, men bare et nytt symptom.

Typiske fallgruver i Delphi-kode som FastMM synliggjør

Collections og eierskap (lister, dictionaries, interfaces)

Mange lekkasjer kommer ikke fra kompliserte algoritmer, men fra hverdagslige datastrukturer. To klassiske feilscenarier:

  • En liste inneholder objekter, men ingen vet hvem som frigjør dem. Løsning: bruk en owning-liste eller tøm konsekvent i finally.
  • Et dictionary holder objekter som Values; ved Remove blir Value ikke frigjort eller det glemmes ved Clear.

I tillegg er interfaces tricky: referansetelling (ARC-lignende) er praktisk, men blanding med objekt-eierskap kan gi lekkasjer ved sykliske referanser eller events. FullDebugMode viser deg ofte allokeringsstien, men årsaken er en referansesyklus (A holder B via Interface, B holder A via Callback).

Exceptions og tidlige avbrudd

I etablerte forretningssystemer er Exceptions ofte del av normal kontrollflyt (f.eks. validering, avbrudd, retry). Problemet er sjelden selve exceptionen, men banen rundt den: et objekt opprettes før try/finally, så kastes en exception, og cleanup hoppes over. FullDebugMode gir deg stacktracen for allokasjonen – og du må sjekke om det finnes en garantert utført frigivelsessti.

Tråder og levetid: «frigjøre i feil tråd»

Med VCL/FMX og tjenester med worker-threads oppstår et annet grense-case: et objekt opprettes i én tråd, men frigjøres i UI-tråden (eller omvendt), fordi man via Queue/Synchronize ‚bare raskt‘ sender noe over. Det kan fungere, men kan også føre til Use-after-free hvis produsenten fortsetter å jobbe mens konsumenten allerede frigjør.

FastMM FullDebugMode kan hjelpe her fordi den oppdager forsinkede feil tidligere. Den egentlige fiksen er derimot en ren levetidsmodell: klare eierskapsforhold, overføring kun via immutable data eller entydige eierskapsoverføringspunkter.

Hvordan gjøre rapporter nyttige: filtrere, sammenligne, dokumentere

I team lønner det seg å ikke bare ’se på‘ leak-rapporter, men behandle dem som et artefakt. Tre pragmatiske tiltak som har vist seg nyttige:

  • Baseline-Report: En ‚kjent tilstand‘ (f.eks. gjeldende produktversjon) kjøres én gang med FullDebugMode og lagres som referanse. Da oppdager du nye lekkasjer umiddelbart.
  • Vergleich nach Use-Case: For kritiske arbeidsflyter (import, eksport, API-Request, UI-masseoperasjon) definerer du for hvert tilfelle en kort sekvens som er regelmessig repeterbar.
  • Dokumenterte ‚legitime lekkasjer‘: Hvis en cache bevisst ikke finaliseres, dokumenter det. Ellers vil noen om seks måneder jakte de samme oppføringene igjen.

Dette er ikke byråkrati, men en tidsbesparelse: lekkasjejakt blir ellers raskt en endeløs sløyfe, fordi de samme mønstrene oppstår på nytt i hver sprint.

Når innsatsen lønner seg – og når du bør gå fram annerledes

FastMM FullDebugMode er et diagnostikkverktøy med kostnader. Innsatsen lønner seg særlig når:

  • Applikasjonen kjører lenge (tjeneste, terminalserver-klient, skiftsystem, 24/7-prosesser).
  • Du behandler reelle kundedatastrømmer og ikke dekker alle stier i testene.
  • Når stabilitet er viktigere enn kortsiktig fart i funksjonsleveranse (typisk i prosessnære programvareløsninger).

Hvis du derimot bare har en liten desktop-helper som avsluttes etter 30 sekunder, er lekkasjejakt ofte sekundært. På samme måte: hvis du har et enkeltstående memory-spike-problem (f.eks. stor eksport), er det ofte ikke en lekkasje, men et spørsmål om streamingstrategi og peak-belastning i heapen.

Praksiskonklusjon: FullDebugMode er ikke en bryter, men en prosess

FastMM FullDebugMode gir struktur i feilsøkingen av minne: det gjør allokasjoner synlige, avdekker heap-korrupsjon tidligere og leverer stacktraces som lar deg fikse årsak i stedet for symptom. Den avgjørende løftestangen er likevel ikke verktøyet, men prosessen: reproduserbare scenarier, diagnostiske byggekonfigurasjoner, klare eierskapsavtaler og regresjon mot en baseline.

Hvis du sitter fast med en seiglivet lekkasje eller sporadisk heap-feil og vil stabilisere dette varig i et større Delphi-system, lønner det seg med et kort, ryddig diagnoseoppsett med klar sekvens og evaluerbare rapporter. Hvis du trenger støtte til analyse, build-profiler eller arkitektur-refaktorering: kontakt Net-Base Software GmbH.

For dette temaet er det også viktig med Delphi finne minnelekkasjer og å lese Fastmm Leak Report. Innlegget setter disse aspektene i en forståelig kontekst og viser hva som er viktig i hverdagen.

Diskuter prosjekt eller moderniseringsoppdrag med Net-Base.

Neste trinn

Når et tema blir et reelt prosjekt, bør arkitektur, eksisterende systemer og drift vurderes samlet allerede tidlig i prosessen.

Vi bistår ikke bare med enkeltspørsmål, men også når kodesnutter, legacy-temaer eller portalideer skal utvikles til et robust virksomhetsprosjekt.

  • Eksisterende tilstand, målbildet og tekniske risikoer vurderes samlet.
  • REST, datatilgang, portaler og utrulling blir ikke utsatt som etterfølgende oppgaver.
  • Dere ser tidlig hvilken vei som er økonomisk og driftsmessig levedyktig.

Del innlegg

Del dette innlegget direkte

LinkedIn, X, XING, Facebook, WhatsApp og e-post er umiddelbart tilgjengelige. For Instagram forbereder vi lenke og kort tekst umiddelbart.

E-post

Instagram åpnes i en ny fane. Lenken og kortteksten kopieres først til utklippstavlen.