Net-Base Lehti

23.08.2026

Muistivuotojen jäljitys: FastMM FullDebugModen kohdennettu käyttö ja stacktracejen oikea tulkinta

FastMM FullDebugMode on Delphi-projekteissa yksi tehokkaimmista työkaluista muistivuotoja vastaan – mutta vain, kun se otetaan kohdennetusti käyttöön, raportit tulkitaan oikein ja tyypilliset virhearvaukset vältetään. Tämä käytännönartikkeli esittelee siistin työnkulun alkaen...

23.08.2026

Lehden aiheesta projektikäytäntöön

Artikkeliin liittyvät palvelu- ja tekniikkasivut

Kun eine Delphi-sovellus käytössä hitaasti „pullistuu“, kaatuu satunnaisesti Access Violation -virheisiin tai muuttuu päivien ajon jälkeen äkillisesti epävakaaksi, taustalla ei usein ole yksittäinen bugi vaan kuvio: muisti allokoidaan mutta sitä ei vapauteta oikein – tai se vapautetaan liian aikaisin ja käytetään myöhemmin. Juuri tässä on FastMM FullDebugMode kullanarvoinen. Ei pysyvään käyttöön, vaan kohdennetuksi diagnostiikkatyökaluksi, joka muuttaa „jossain heapissä on jotain rikki“ -tilanteen jäljitettäväksi syyksi.

Mutta miinuspuoli: FullDebugMode tuottaa paljon outputia, heikentää suorituskykyä ja johtaa helposti väärintulkintoihin. Vuotoraportti ei automaattisesti näytä kohtaa, jossa „virhe“ on. Ja stacktrace on vain yhtä hyvä kuin symbolien resoluutio (MAP-tiedosto, debug-tiedot, inlining). Tässä kirjoituksessa käyn läpi tyypillisen reunatapauksen, selitän puhtaan lähestymistavan ja sudenkuopat – niin että lopussa et vain löydä vuotoja, vaan korjaat ne pysyvästi.

Milloin FastMM FullDebugMode on wirklich sinnvoll

FastMM on moderneissa Delphi-versioissa usein jo oletusmuistinhallinta tai liitetään monissa projekteissa muutenkin. FullDebugMode on kuitenkin erityinen konfiguraatio: se varustaa muistiblokit lisätarkastuskuvioilla, kerää allokaatioiden stacktraceja ja tarkistaa aggressiivisemmin heap-korruptiota (eli heapin hallintatietojen vaurioitumista, esim. buffer-overrunien seurauksena).

Käytän FullDebugModea kohdennetusti, kun jokin näistä tilanteista esiintyy:

  • Toistettava vuoto: Muistin käyttö kasvaa testiajossa jokaista toimenpidettä kohden (esim. jokaista pyyntöä, tuontia tai käyttöliittymätoimintoa kohden).
  • Satunnaiset AV:t: Erityisesti sellaiset, jotka „tapahtuvat joskus täällä, joskus siellä“ samassa alueessa (klassinen: use-after-free).
  • Heap-korruptio: Ilmoitukset kuten „Invalid pointer operation“, „Access violation in ntdll“ tai kaatumiset suljettaessa/finalisoitaessa.
  • Regressioselvitys: Refaktoroinnin, kirjastopäivityksen tai kääntäjän vaihdon jälkeen ilmenevä uusi epävakaus.

FullDebugMode ei ole järkevää ottaa käyttöön kaikissa buildissa. Ylikuorma on korkea, ajoitus muuttuu, ja erityisesti race-conditionit voivat kadota tai siirtyä. Pysyvässä tuotantokäytössä sopii paremmin kevyt monitorointi (esim. prosessin working set, Private Bytes, laskurit per toimenpide) – FullDebugMode on skalpelli, ei pulssinmittari.

Perusperiaate: Vuotoraportti on oire, Stacktrace on jälki

Vuotoraportti näyttää ensin: nämä lohkot ovat ohjelman lopussa edelleen allokoituina. Se on automaattisesti ongelma vain, jos nämä lohkot olisi pitänyt vapauttaa. On legitimiejä „vuotoja“: globaalit singletonit, välimuistit, OS-handlet prosessin elinkaaren ajan tai kolmannen osapuolen kirjastot, jotka tarkoituksella eivät finalisoi. Nämä tapaukset pitää tunnistaa, mutta niitä ei pidä sokeasti „korjata“.

Reportin stacktrace näyttää kohdan, jossa lohko allokoitiin. Se ei usein ole paikka, jossa olet „unohtanut Free“. Usein käytäntönä kasvaneissa järjestelmissä:

  • Allokaatio käyttöliittymä- tai palvelukerroksessa, vapautuksen pitäisi tapahtua alemmassa kerroksessa (omistajuus epäselvä).
  • Allokaatio factoryssa, omistajuus siirretään kutsujalle – mutta kutsuja olettaa sen olevan omistuksessaan.
  • Objekteja pidetään kokoelmissa (listat, sanakirjat), mutta omistajuusmalli ei ole yhdenmukainen.
  • Poikkeuspolku ohittaa siivouksen, koska try/finally puuttuu tai alkaa liian myöhään.

Siksi selkeä prosessi on: toistaaeristääselvittää Stacktracelöytää Ownership-virhekorjaus regressiotestillä. FastMM antaa sinulle jäljet, mutta sinun on tulkittava ne arkkitehtuurin ja elinkaarten kontekstissa.

FastMM FullDebugMode ottaa käyttöön huolellisesti (ilman sivuvaikutusten huomiotta jättämistä)

Schematische Grafik mit Heap-Blöcken und Prüfrändern, die in einen Leak-Report überführt werden
Yhteenveto: FullDebugMode käyttää lisätarkastusreunoja ja raporttitulostusta.

FullDebugMode otetaan käytännössä käyttöön FastMM-asetusten ja sopivan FastMM-konfiguraation kautta. Tärkeämpää ei ole „mikä on tarkka include-tiedoston nimi“, vaan mitä konfiguraatio tekee ja missä build-ehdoissa sitä käytät.

Suositellut lähtökohdat debug-käännökselle

  • Debug DCUs ja debug-tiedot: Stacktracet ovat hyödyllisiä vain, jos ne voidaan ratkaista todelliselle Unit/riville/osoitteelle. Varmista, että debug-tiedot generoidaan ja että MAP-tiedosto on saatavilla.
  • Valitse optimointi tietoisesti: Stacktracen luettavuuden kannalta ei-optimisoitu käännös on yleensä parempi. Inlining ja aggressiivinen optimointi voivat „hämärtää“ stackframeja.
  • Samat suoritusehdot: Käytä mahdollisuuksien mukaan samoja tietoja, samaa konfiguraatiota, samoja oikeuksia. Monet vuodot riippuvat datasta (esim. harvinaiset formaatit, erikoispolut).
  • Erottele 64-bit ja 32-bit: Muistinkäyttökäyttäytyminen, kohdistus ja kolmannen osapuolen kirjastot eroavat. Debuggaa sillä kohdealustalla, jolla ongelma esiintyy.

Yksi seikka, jonka adminit ja tekniset johtajat usein aliarvioivat: FullDebugMode voi myös muuttaa ajastusta. Jos säikeitä on mukana, race-conditionit voivat esiintyä eri tavalla. Siksi on järkevää ajaa rinnakkain myös käynti ilman FullDebugModea, joka vain varmistaa reproduktion. FullDebugMode on sitten diagnosointivaihe.

Varovaisuus ReportMemoryLeaksOnShutdownin suhteen

Delphi voi ilmoittaa vuodoista ohjelman lopussa käyttäen ReportMemoryLeaksOnShutdown. Tämä on käytännöllistä, mutta monimutkaisissa sovelluksissa (palvelut, plug-in Host, pitkät käyttöajat) se voi johtaa harhaan: sammuttamisen yhteydessä finalisaatiot ajetaan, säikeet pysäytetään, välimuistit tyhjennetään. Vuoto, joka on kriittinen ajon keskivaiheessa, voi kadota ennen loppua — tai päinvastoin: näennäinen vuoto syntyy vasta sammutuksen yhteydessä, koska taustatyö on vielä käynnissä.

Käytännöllisessä vuodonmetsästyksessä on siksi tärkeämpää: mitata vuoto per tapahtuma (esim. 100 requestin jälkeen), ei pelkästään ohjelman lopussa. FastMM voi tässä auttaa, mutta testirakenteen on mallinnettava tuo käyttötilanne.

Tyypillinen reunatapaus: Leak-Report näyttää „irgendein Objekt“, mutta syy on Ownership

Yrityssovelluksissa klassikko: tuontiprosessi luo jokaiselle tietueelle apuobjekteja (esim. StringLists, JSON-parserit, väliaikaislistat). Onnistumistapauksessa ne vapautetaan siististi. Harvoissa tapauksissa (validointiperusteinen skip, Exception, varhainen exit) objekti jää roikkumaan. 10 000 tietueen jälkeen se on näkyvissä.

FastMM FullDebugMode auttaa tässä, koska se näyttää allokointikohdan. Mutta ”korjaus” ei ole ”free siellä, missä allokaatio tapahtuu”. Korjaus on robusti omistajuusmalli:

  • Se, joka luo objektin, ei automaattisesti ole omistaja.
  • Omistajuus on oltava selkeä API-sopimuksessa (parametrit/paluuarvot, dokumentaatio, nimeämiskäytännöt).
  • Kokoelmien on oltava yksiselitteisiä: owning vs. non-owning. Sekamuodot kostautuvat.
  • Poikkeuspolut tarvitsevat varhaiset try/finally-lohkot.

Jos stacktracesta näet vain „TStringList.Create“, tieto ei ole arvotonta – mutta se kertoo vain: tässä jotain luodaan. Kysymys on: mihin sen pitäisi päättyä? Siinä arkkitehtuuriajattelu auttaa enemmän kuin debugger-akrobatia.

Stacktracen oikea tulkinta: mitä siitä voi todellisuudessa päätellä

Lähikuva debuggausanalyysistä epätarkalla debuggerilla ja käsin merkityllä kutsuketjulla
Stacktracessa ratkaisee kutsuketju – ei yksittäinen rivi.

FastMM:n stacktrace on yleensä lista paluuosoitteita, jotka – debug-symbolien avulla – voidaan kartoittaa uniteihin, prosedyyreihin ja ihanteellisesti rivinumeroihin. Kun luet sitä, kolme asiaa on ratkaisevia:

  • Pinon ylin kehys ei aina ole virheen syy: ylimmät framet ovat usein Memory-Manager/RTL. Kiinnostavaksi se muuttuu siellä, missä oma koodisi alkaa.
  • Kutsuketju yksittäisen rivin sijaan: rivi on vain piste. Ketju näyttää, mikä polku johti allokointiin.
  • Samat toistuvat blokit: jos FastMM raportoi useita saman kokoisia muistivuotoja, kyse on usein toistuvasta polusta. Se on hyvä: sinulla on toistettavuus.

Kun rivinumeroita puuttuu: MAP-tiedosto, paketit, Release-DCU:t

Monet tiimit kompastuvat tässä: FullDebugMode on päällä, leak-raportti saapuu, mutta Unitin/ rivin sijaan näkyy vain osoitteita tai kryptisiä symboleja. Tyypillisiä syitä:

  • MAP-tiedostoa ei ole tai debug-tietoja ei ole tuotettu.
  • Ajetaan Release-DCUja tai kolmannen osapuolen DLL:iä ilman symboleita.
  • Sovellus käyttää runtime-paketteja: silloin osa koodista on BPL:issä, ja symbolien resoluution on vastattava tätä.
  • Optimointi/Inlining on tehnyt stacktracesta vaikealukuisemman.

Käytännössä tämä tarkoittaa: muistivuotojen metsästykseen tarvitset buildin, joka on tietoisesti ”diagnosoitava”. Tämä on eri tavoite kuin ”mahdollisimman nopea”. Teknisten leadien tulisi käsitellä tätä omana build-profiilinaan, jotta jokainen tiimin jäsen ei ad hoc muuta projektin asetuksia.

Kehysten arviointi: „mielenkiintoinen“ kehys löytyy usein yhden rivin ylempää

Esimerkki käytännöstä (ilman varsinaista asiakaskoodia): Stacktrace näyttää sinulle ensimmäisenä framena koodissasi rutiinin „LoadConfig“. Näet siellä objektin luomisen. Lisäät yhden Free-kutsun, muistivuoto katoaa – ja yhtäkkiä jossain muualla tapahtuu Double Free. Miksi? Koska „LoadConfig“ laittaa objektin Cacheen, ja toinen koodipolku on jo Owner ja siivoaa myöhemmin.

Oikea tulkinta olisi ollut: Stacktrace näyttää sinulle, missä lohko syntyy. Korjaus löytyy usein määrittelystä: Kuka omistaa objektin Returnin jälkeen? Jos et vastaa tähän kysymykseen selkeästi, vaihdat vain virhekuvaa (Leak → AV).

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

Grafiikka, joka näyttää Buffer-Overrunin, joka ylittää viereisen muistialueen
Heap-korruptio ilmenee usein viiveellä – FullDebugMode paljastaa sen aikaisemmin.

Monet „Leaks“ ovat todellisuudessa seurausongelmia: Buffer-Overrun ylikirjoittaa Heap-Metadaten, der Memory-Manager ei pysty myöhemmin vapauttamaan oikein, ja lopuksi näet näennäisesti satunnaisia Leakeja tai Invalid Pointer Operations -virheitä. FullDebugMode on tässä tehokas, koska se käyttää tarkistusmalleja ja tekee Free/Reuse-tapauksissa lisävahvistuksia.

Tärkeä on ero:

  • Leak: Block wurde allokiert und nie freigegeben. Stabilität leidet über Zeit, Crash ist nicht zwingend.
  • Use-after-free: Block wird freigegeben, aber später noch benutzt. Führt zu sporadischen AVs, die schwer reproduzierbar sind.
  • Double Free: Block wird zweimal freigegeben. Kann sofort knallen oder erst später (wenn der Block wiederverwendet wurde).
  • Heap-Korruption: Irgendwer schreibt über die Grenzen eines Blocks. Symptome sind oft zeitversetzt.

FullDebugMode ist besonders wertvoll, wenn du Symptome zeitversetzt siehst. Die zusätzliche Validierung macht Fehler früher sichtbar – oft genau an der Stelle, an der der falsche Zugriff passiert, nicht erst Minuten später in einem beliebigen Free.

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

Jos haluat jahdata Speicherlecks, tarvitset toistettavan prosessin, joka on jaettavissa tiimien kesken. Työskentelen mieluiten kiinteän diagnoseraamin kanssa:

1) Reproduktion in einem deterministischen Szenario

Määrittele testisekvenssi, joka näyttää Leakin luotettavasti: „Käynnistä Service, käsittele 500 Nachrichten, pysäytä Service“ tai „Avaa Maske X, suorita Aktion Y 200-mal“. Tärkeää on dokumentoida sekvenssi parametreineen (Datenbestand, Mandant, Feature-Flags), jotta muut voivat toistaa sen.

2) Minimieren: Leak pro Schritt sichtbar machen

Jos sekvenssi kestää 20 minuuttia, pilko se. Tavoitteena on, että voit mahdollisimman nopeasti verrata „vorher“ ja „nachher“. Suurissa sovelluksissa tämä on usein varsinainen ajanviejä, ei korjaaminen.

3) FullDebugMode einschalten und Report interpretieren

Vasta nyt FastMM FullDebugMode astuu kuvaan. Kerää raportit, ryhmittele lohkokoon/kutsupinon mukaan ja etsi toistuvuuksia. Yksi jäljelle jäävä lohko voi olla tarkoituksenmukainen välimuisti. 10.000 identtistä lohkoa ovat lähes aina todellinen vuoto.

4) Ownershipin selvitys ja korjaus oikeassa kerroksessa

Korjaa vuodot siellä, missä Ownership määritellään: Factory, API-sopimus, Collection-Wrapper. „Pikainen Free:n lisääminen“ suoraan Create:n viereen on usein väärä paikka, jos objekti luovutetaan eteenpäin.

5) Regressio: sama sekvenssi, sama build, sama raportti

Korjaus on hyvä vasta, kun sekvenssi taas suoritetaan eikä ilmene vuotoja tai uusia muistivirheitä. Varsinkin Use-after-free-tapauksissa „vuoto poissa“ ei ole todiste, vaan vain uusi oire.

Tyypilliset sudenkuopat Delphi-koodissa, jotka FastMM paljastaa

Kokoelmat ja Ownership (listat, sanakirjat, rajapinnat)

Monet vuodot eivät johdu monimutkaisista algoritmeista, vaan arkipäiväisistä tietorakenteista. Kaksi klassista virhetilannetta:

  • Lista sisältää objekteja, mutta kukaan ei tiedä, kuka vapauttaa ne. Ratkaisu: käytä owning-listaa tai siivoa johdonmukaisesti finally-lohkossa.
  • Sanakirja pitää objekteja arvoina; Remove- tai Clear-toiminnossa arvo saatetaan jättää vapauttamatta.

Lisäksi hankalia ovat Rajapinnat: viitemäärälaskenta (ARC-tyyppinen) on kätevää, mutta sen sekoittaminen objektin omistajuuden kanssa voi aiheuttaa vuotoja syklisissä viittauksissa tai tapahtumissa. FullDebugMode näyttää usein allokaatiopolun, mutta syy on viitesykli (A pitää B:tä rajapinnan kautta, B pitää A:ta callbackin kautta).

Poikkeukset ja varhaiset poistumiset

Kasvaneissa yritysohjelmistoissa poikkeukset ovat usein osa normaalia ohjausta (esim. validointi, peruutus, retry). Ongelmana ei yleensä ole itse poikkeus, vaan sitä ympäröivä polku: objekti luodaan ennen try/finallytä, sitten poikkeus heitetään ja puhdistus ohitetaan. FullDebugMode antaa allokaation stacktracen – ja sinun täytyy tarkistaa, onko olemassa takuuvarma vapautuspolku.

Säikeet ja elinkaari: „vapautus väärässä säikeessä“

VCL/FMX:n ja worker-threadien kanssa toimivissa palveluissa syntyy toinen reunatapaus: objekti luodaan yhdessä säikeessä mutta vapautetaan UI-säikeessä (tai päinvastoin), koska asioita „pikaisesti“ siirretään Queue/Synchronize:n kautta. Tämä voi toimia, mutta voi myös johtaa Use-after-free:hen, jos tuottaja jatkaa työtä samalla kun kuluttaja on jo vapauttanut.

FastMM FullDebugMode voi tässä auttaa, koska se havaitsee ajallisesti siirtyvät virheet aikaisemmin. Varsinainen korjaus on kuitenkin selkeä elinkaarimalli: selkeät omistussuhteet, siirto vain immutable-datan kautta tai yksiselitteiset ownership-siirtopisteet.

Miten saat raportit käyttökelpoisiksi: suodatus, vertailu, dokumentointi

Tiimeissä kannattaa käsitellä leak-raportteja pelkän „katselemisen“ sijaan artefaktina. Kolme käytännönläheistä toimenpidettä, jotka ovat osoittautuneet toimiviksi:

  • Baseline-raportti: Tunnettu tila (esim. nykyinen tuotteen versio) ajetaan kerran FullDebugMode-tilassa ja tallennetaan referenssiksi. Näin tunnistat uudet vuodot välittömästi.
  • Käyttötapauskohtainen vertailu: kriittisille työnkuluillesi (tuonti, vienti, API-pyyntö, UI-massatoiminto) määrittele lyhyt, säännöllisesti toistettava sekvenssi.
  • Dokumentoitu „legitiimi vuoto“: Jos välimuisti jätetään tietoisesti finalisoimatta, dokumentoi tämä. Muuten kuuden kuukauden kuluttua joku metsästää samat merkinnät uudelleen.

Tämä ei ole byrokratiaa, vaan ajan säästöä: vuotojen metsästys muuttuu muuten nopeasti noidankehäksi, koska samat kaavat toistuvat jokaisessa sprintissä.

Milloin vaivannäkö kannattaa – ja milloin kannattaa toimia toisin

FastMM FullDebugMode on diagnostiikkatyökalu, joka aiheuttaa kustannuksia. Vaivannäkö kannattaa erityisesti, kun:

  • Sovellus on pitkään käynnissä (palvelu, terminaalipalvelinasiakas, vuorotyöjärjestelmä, 24/7-prosessit).
  • Käsittelet todellisia asiakasdatavirtoja etkä kata testauksessa kaikkia polkuja.
  • Stabiilisuus on tärkeämpää kuin ominaisuuksien nopea julkaisutahti (tyypillistä prosessiläheisissä ohjelmaratkaisuissa).

Jos taas kyse on vain pienestä työpöytäapusta, joka päättyy 30 sekunnin kuluttua, vuotojen etsintä on usein toissijaista. Samoin: jos kyseessä on kertaluonteinen muistipiikki (esim. suuri vienti), kyse ei usein ole vuodosta vaan streaming-strategiasta ja muistin huippukuormituksesta.

Käytännön yhteenveto: FullDebugMode ei ole kytkin, vaan prosessi

FastMM FullDebugMode tuo rakennetta muistivirheiden etsintään: se tekee muistiallokoinnit näkyviksi, paljastaa heap-korruption aikaisemmin ja tuottaa stacktracet, joiden avulla voit korjata syyn oireen sijaan. Ratkaiseva vipu ei kuitenkaan ole työkalu vaan prosessi: toistettavat skenaariot, diagnosoitavat buildit, selkeät omistajuussopimukset ja perustilaa vastaan suoritettava regressiotestaus.

Jos jumitut itsepäiseen vuotoon tai satunnaisiin heap-virheisiin ja haluat vakauttaa aihetta pysyvästi laajemmassa Delphi-järjestelmässä, kannattaa tehdä lyhyt, huolellinen diagnostiikka-asetelma, jossa on selkeä sekvenssi ja analysoitavat raportit. Jos tarvitset tukea analyysissa, build-profiileissa tai arkkitehtuurin refaktoroinnissa: ota yhteyttä Net-Base Software GmbH.

Tähän aiheeseen liittyen ovat myös Delphi Muistivuodon löytäminen ja Fastmm Leak Reportin lukeminen tärkeitä. Artikkeli asettaa nämä näkökohdat ymmärrettävään kontekstiin ja osoittaa, mikä on arjessa olennaista.

Keskustele projektista tai modernisointihankkeesta Net-Base kanssa.

Seuraava vaihe

Kun aiheesta muodostuu todellinen projekti, arkkitehtuuri, nykytila ja operointi on tarkasteltava yhdessä varhaisessa vaiheessa.

Emme tue pelkästään yksittäiskysymyksissä, vaan myös silloin, kun lähdekoodipalasista, legacy-aiheista tai portaali-ideoista halutaan muodostaa luotettava yrityshanke.

  • Nykytila, tavoitetila ja tekniset riskit arvioidaan yhdessä.
  • REST, tietojen käyttö, portaalit ja käyttöönotto eivät siirry myöhempään vaiheeseen.
  • Näette ajoissa, mikä vaihtoehto on taloudellisesti ja operatiivisesti kannattava.

Jaa artikkeli

Jaa tämä viesti suoraan

LinkedIn, X, XING, Facebook, WhatsApp ja sähköposti ovat välittömästi saatavilla. Instagramia varten valmistelemme linkin ja lyhyen tekstin.

Sähköposti

Instagram avautuu uuteen välilehteen. Linkki ja lyhyt teksti kopioidaan ensin leikepöydälle.