Net-Base Revista

23.08.2026

Caçar fuites de memòria: utilitzar de manera selectiva FastMM FullDebugMode i interpretar correctament els stack traces

FastMM FullDebugMode és, en projectes Delphi, una de les eines més efectives contra les fuites de memòria — però només si s'activa de manera específica, s'interpreten correctament els informes i s'eviten suposicions errònies típiques. Aquesta aportació pràctica mostra un procés net des de...

23.08.2026

Del tema de la revista a la pràctica del projecte

Pàgines de serveis i tècniques pertinents per a l'article

Quan una Delphi-aplicació en funcionament s’infla lentament, falla esporàdicament amb Access Violations o, després de dies d’execució, esdevé sobtadament inestable, sovint no hi ha un bug aïllat darrere, sinó un patró: es sol·licita memòria però no s’allibera correctament — o s’allibera massa aviat i després s’utilitza. Precisament aquí FastMM FullDebugMode val el seu pes en or. No com a estat permanent, sinó com a eina de diagnosi dirigida que transforma un «alguna cosa està malament a l’heap» en una causa rastrejable.

El problema: FullDebugMode genera molt output, consumeix rendiment i condueix fàcilment a males interpretacions. Un informe de fuites de memòria no assenyala automàticament el punt on està «l’error». I un stacktrace només és tan bo com la resolució de símbols (fitxer MAP, informació de depuració, inlining). En aquest article repasso el cas límit típic, explico l’enfocament net i les trampes — perquè al final no només trobis leaks, sinó que els eliminis de manera sostenible.

Quan FastMM FullDebugMode és realment útil

FastMM és en versions modernes de Delphi sovint ja el gestor de memòria per defecte o s’integra en molts projectes. El FullDebugMode és, però, una configuració especial: marca els blocs de memòria amb patrons de comprovació addicionals, recull stacktraces d’allocació i comprova de manera més agressiva la corrupció de l’heap (és a dir, dades de gestió al heap corruptes, p. ex. per desbordaments de búfer).

Faig servir FullDebugMode de manera selectiva quan es presenta una d’aquestes situacions:

  • Leak reproduïble: el consum de memòria creix en una execució de prova per operació (p. ex. per sol·licitud/request, per importació, per acció d’interfície d’usuari).
  • AVs esporàdiques: especialment aquelles que passen „ara aquí, ara allà“ dins del mateix àmbit (clàssic: Use-after-free).
  • Corrupció de l’heap: missatges com „Invalid pointer operation“, „Access violation in ntdll“ o crashes en tancar/finalitzar.
  • Recerca de regressions: després d’un refactoring, actualització de llibreria o canvi de compilador, apareix sobtadament nova inestabilitat.

No té sentit utilitzar FullDebugMode com a «activem-ho en totes les builds». L’overhead és alt, el timing canvia i precisament les race conditions poden desaparèixer o desplaçar-se. Per a funcionament continu convé un monitoratge lleuger (p. ex. Working Set del procés, Private Bytes, comptadors per operació) — FullDebugMode és el bisturí, no el sensor del pols.

Principi bàsic: l’informe de leak és un símptoma, el stacktrace és una pista

Un informe de fuites de memòria et mostra primer de tot: Aquests blocs continuen assignats al final del programa. Això només és automàticament un problema si aquests blocs haurien d’haver estat alliberats. Hi ha «leaks» legítims: singletons globals, caches, handles del SO amb durada del procés o biblioteques de tercers que deliberadament no finalitzen. Aquests casos els vols conèixer, però no corregir-los a cegues.

El stacktrace a l’informe mostra el lloc on es va sol·licitar el bloc. Sovint no és el lloc on has «oblidat Free». Realitat habitual en sistemes madurs:

  • Assignació al nivell d’UI o de servei; l’alliberament hauria d’ocórrer en una capa més profunda (ownership poc clar).
  • Assignació en una factory; la propietat (ownership) s’entrega al caller — però el caller pensa que és „owned“.
  • Objectes es mantenen en col·leccions (llistes, dictionaries), però el model d’ownership no és consistent.
  • Un camí d’excepció salta el cleanup perquè falta un try/finally o comença massa tard.

Per tant, el procediment net és: reproduiraïllarresoldre el stacktracetrobar errors d’ownershipfix amb prova de regressió. FastMM et proporciona les traces, però les has de traduir a arquitectura i cicles de vida.

Activar correctament FastMM FullDebugMode (sense passar per alt efectes secundaris)

Gràfic esquemàtic amb blocs del heap i marges de comprovació, convertits en un Leak-Report
Resum: FullDebugMode opera amb marges de comprovació addicionals i sortida d’informes.

En la pràctica, el FullDebugMode s’activa mitjançant les opcions de FastMM i una configuració de FastMM adequada. El que importa menys és «com es diu exactament el fitxer Include», i més bé què fa la configuració i en quines condicions de compilació l’utilitzes.

Condicions recomanades per a la compilació de depuració

  • DCUs de depuració i informació de depuració: Els stacktraces només són útils si es poden resoldre a la unitat/línia/adreça reals. Assegura’t que es generi informació de depuració i que hi hagi disponible un fitxer MAP.
  • Triar l’optimització de manera conscient: Per a la llegibilitat del stacktrace sovint és millor una compilació sense optimització. L’inlining i les optimitzacions agressives poden esborrar els stackframes.
  • Mateixes condicions d’execució: Utilitza, en la mesura del possible, les mateixes dades, la mateixa configuració, els mateixos permisos. Molts Leaks depenen de les dades (p. ex. formats rars, rutes especials).
  • Separar 64-bit i 32-bit: El comportament de memòria, l’alineament i les biblioteques de tercers difereixen. Depura en la plataforma objectiu on es produeix el problema.

Un punt que els Admins i els responsables tècnics sovint subestimen: el FullDebugMode també pot canviar el timing. Si tens threading implicat, les race conditions poden aparèixer de manera diferent. Per això convé tenir, en paral·lel, una execució sense FullDebugMode que només confirmi la reproducció. El FullDebugMode serà aleshores el pas per a la diagnosi.

Precaució bei „ReportMemoryLeaksOnShutdown“

Delphi kann über ReportMemoryLeaksOnShutdown Leaks beim Programmende melden. Das ist praktisch, aber in komplexen Anwendungen (Services, Plug-in Host, lange Laufzeiten) kann es trügen: Beim Shutdown laufen Finalization-Abschnitte, Threads stoppen, Caches werden weggeräumt. Ein Leak, das in der Mitte der Laufzeit kritisch ist, kann bis zum Ende wieder verschwinden – oder umgekehrt: ein scheinbarer Leak entsteht erst im Shutdown, weil noch Hintergrundarbeit läuft.

Per a una caça de leaks pràctica, és més important mesurar el leak per operació (p. ex., després de 100 peticions), no només en finalitzar. FastMM pot ajudar en això, però el disseny del test ha d’expressar-ho.

El cas límit típic: el Leak-Report mostra „algún objecte“, però la causa és Ownership

Un clàssic d’aplicacions empresarials: un procés d’importació crea per cada registre objectes auxiliars (p. ex. StringLists, JSON-Parser, llistes temporals). En el camí feliç s’alliberen correctament. En casos rars (salt per validació, excepció, sortida prematura) un objecte pot quedar penjat. Després de 10.000 registres això es fa visible.

FastMM FullDebugMode ajuda aquí perquè mostra el lloc d’allocació. Però la „solució“ no és „free an die Stelle der Allokation“. La solució és un patró d’ownership robust:

  • Qui crea un objecte no és automàticament Owner.
  • L’ownership ha d’estar clara en el contracte d’API (paràmetres/retorn, documentació, convencions de nom).
  • Les col·leccions han de ser inequívocament owning vs. non-owning. Les formes mixtes s’acaben pagant.
  • Els camins d’excepció requereixen blocs try/finally immediats.

Si del stacktrace només veus „TStringList.Create“, la informació no és inútil — però només t’indica: aquí s’està creant alguna cosa. La qüestió és: on hauria de finalitzar? I aquí l’enfocament arquitectònic ajuda més que l’acrobàcia del debugger.

Llegir correctament els stacktraces: què en pots deduir realment

Detailaufnahme einer Debugging-Analyse mit unscharfem Debugger und handnotierter Call-Chain
En un stacktrace compta la cadena de crides – no la línia aïllada.

Un stacktrace de FastMM és, en general, una llista d’adreces de retorn, que – amb símbols de depuració – es tradueixen a Units, procediments i, idealment, números de línia. Quan ho llegeixes, tres coses són determinants:

  • El top-of-stack no sempre indica l’error: els frames superiors sovint pertanyen al Memory-Manager/RTL. El rellevant és on comença el teu codi.
  • Cadena de crides en lloc d’una sola línia: la línia és només un punt. La cadena et mostra quin camí va conduir a l’allocació.
  • Diversos blocs idèntics: si FastMM informa diversos leaks de la mateixa mida, sovint és un camí recurrent. Això és positiu: tens reproduïbilitat.

Quan falten números de línia: fitxer MAP, Packages, Release-DCUs

Molts equips topen en aquest punt: FullDebugMode està actiu, arriba el report de leaks, però en lloc d’Unit/Línia només hi ha adreces o símbols críptics. Causes típiques:

  • No s’ha generat fitxer MAP o no s’han creat les dades de depuració.
  • Estàs executant contra Release-DCUs o DLLs de tercers sense símbols.
  • L’aplicació utilitza Runtime Packages: aleshores parts del codi estan en BPLs, i la resolució de símbols ha d’ajustar-s’hi.
  • L’optimització/Inlining ha fet el stacktrace més difícil de llegir.

En la pràctica això vol dir: per caçar leaks necessites una build deliberadament „diagnosticable“. Això té un objectiu diferent de „més ràpid possible“. Els responsables tècnics haurien de tractar-ho com un perfil de build propi, perquè no cada membre de l’equip modifiqui opcions del projecte ad hoc.

Avaluar frames: „Interessant“ ist oft eine Zeile weiter oben

Un exemple de la realitat (sense codi concret de client): el stacktrace et mostra, com a primer frame en el teu codi, una rutina „LoadConfig“. Hi veus una creació d’objecte. Afeges una crida a Free, la fuita de memòria desapareix – i de sobte esclata en una altra part amb un Double Free. Per què? Perquè „LoadConfig“ posa l’objecte en una memòria cau, i un altre camí de codi ja és Owner i el neteja més tard.

La lectura correcta hauria estat: el stacktrace et mostra, on es crea el bloc. La solució sol estar en la definició: Qui és l’Owner de l’objecte després del Return? Si no respons aquesta pregunta de manera clara, només canvies la manifestació de l’error (Leak → AV).

Corrupció de l’heap vs. Leak: Per què FullDebugMode sovint detecta el veritable culpable

Grafik, die einen Buffer-Overrun zeigt, der in benachbarten Speicherbereich überläuft
La corrupció de l’heap sovint es manifesta amb retard – FullDebugMode la fa visible abans.

Moltes „Leaks“ són en realitat problemes secundaris: un Buffer-Overrun sobreescriu metadades de l’heap, el gestor de memòria no pot alliberar netament més endavant i, al final, veus fuites de memòria aparentment aleatòries o operacions amb punters invàlids. FullDebugMode és útil aquí, perquè treballa amb patrons de comprovació i fa validacions addicionals en Free/Reuse.

Cal fer la distinció:

  • Leak: el bloc va ser al·locat i mai alliberat. La estabilitat empitjora amb el temps; un crash no és necessàriament immediat.
  • Use-after-free: el bloc s’allibera però després encara s’utilitza. Provoca AVs esporàdics, difícils de reproduir.
  • Double Free: el bloc s’allibera dues vegades. Pot fallar de seguida o més tard (quan el bloc s’ha reutilitzat).
  • Corrupció de l’heap: algú escriu més enllà dels límits d’un bloc. Els símptomes sovint apareixen amb retard.

FullDebugMode és especialment valuós quan veus símptomes amb retard. La validació addicional fa visibles els errors abans – sovint precisament al lloc on es produeix l’accés incorrecte, i no minuts més tard en un Free qualsevol.

Procediment en projectes: caça de fuites reproduïble en lloc de «depuració en la boira»

Si vols caçar fuites de memòria, necessites un procediment que sigui repetible i compartible dins d’un equip. M’agrada treballar amb un marc de diagnosi fix:

1) Reproducció en un escenari determinista

Defineix una seqüència de proves que mostri la fuga de manera fiable: „Inicia el servei, processa 500 missatges, atura el servei“ o „Obre la pantalla X, executa l’acció Y 200 vegades“. És important documentar la seqüència amb paràmetres (conjunt de dades, client, Feature-Flags) perquè altres la puguin reproduir.

2) Minimitzar: fer visible la fuita per cada pas

Si la seqüència dura 20 minuts, divideix-la. L’objectiu és: vols comparar «abans» i «després» tan ràpidament com sigui possible. En aplicacions grans això sovint és el que consumeix més temps, no tant el fer la correcció.

3) Activar FullDebugMode i interpretar l’informe

Ara és quan entra en joc FastMM FullDebugMode. Recull els informes, agrupa’ls per mida de bloc/pila de crides i cerca repeticions. Un sol bloc restant pot ser una memòria cau legítima. 10.000 blocs idèntics són gairebé sempre una fuita de memòria real.

4) Aclariment de la propietat i correcció a la capa corresponent

Corregeix les fuites allà on es defineix la propietat: Factory, contracte d’API, wrapper de col·lecció. «Posar un Free ràpid» just al costat del Create sovint és el lloc equivocat si l’objecte es passa més endavant.

5) Regressió: mateixa seqüència, mateix build, mateix informe

La correcció només es pot considerar bona quan la seqüència s’executa de nou i no apareixen ni fuites ni nous errors de memòria. Especialment en casos de use-after-free, un «Leak desaparegut» no és prova, sinó només un nou símptoma.

Trampes típiques en codi Delphi que FastMM fa visibles

Col·leccions i ownership (llistes, diccionaris, interfícies)

Moltes fuites no provenen d’algoritmes complexos, sinó d’estructures de dades quotidianes. Dos patrons d’error clàssics:

  • Una llista conté objectes, però ningú no sap qui els allibera. Solució: utilitzar una llista propietària (owning) o buidar-la de manera consistent dins d’un finally.
  • Un diccionari manté objectes com a values; en fer Remove no s’allibera el value o s’oblida en un Clear.

Les interfícies són addicionalment delicades: el recompte de referències (semblant a ARC) és còmode, però el funcionament mixt amb ownership d’objectes pot generar fuites en presència de referències cícliques o events. FullDebugMode sovint et mostra el camí d’allocació, però la causa real sol ser un cicle de referències (A manté B via interfície, B manté A via callback).

Excepcions i sortides anticipades

En sistemes de software empresarial amb evolució històrica, les excepcions sovint formen part del flux normal (p. ex. validació, cancel·lació, retry). El problema rarament és l’excepció en si, sinó el camí al seu voltant: un objecte es crea abans del try/finally, salta una excepció i el netejament s’omet. FullDebugMode et proporciona el stacktrace de l’allocació: cal verificar si existeix un camí de liberació garantit.

Fils i cicle de vida: „Alliberar en el fil equivocat“

Amb VCL/FMX i serveis amb worker-threads apareix un cas límit addicional: un objecte s’alloca en un fil però s’allibera en el fil UI (o a l’inrevés) perquè s’ha transferit «ràpidament» alguna cosa amb Queue/Synchronize. Això pot funcionar, però també pot causar use-after-free si el productor continua treballant mentre el consumidor ja ha alliberat.

FastMM FullDebugMode pot ajudar aquí perquè detecta errors retardats més aviat. La correcció real, però, és un model de cicle de vida net: propietats clares, transferències només per dades immutables o punts d’ownership-transfer ben definits.

Com fer útils els informes: filtrar, comparar, documentar

En equips val la pena no només «mirar» els informes de fuites, sinó tractar-los com un artefacte. Tres mesures pragmàtiques provades:

  • Baseline-Report: Un «estat conegut» (p. ex. la versió actual del producte) s’executa un cop amb FullDebugMode i s’emmagatzema com a referència. Així detectes noves fuites immediatament.
  • Comparació per cas d’ús: Per fluxos crítics (importació, exportació, petició d’API, operacions massives de la UI) defineixis una seqüència curta i repetible que s’executi regularment.
  • Fuites «llegítimes» documentades: Si un cache es deixa deliberadament sense finalitzar, documenta-ho. Altrament, d’aquí sis mesos algú tornarà a perseguir les mateixes entrades.

Això no és burocràcia, sinó un estalvi de temps: la recerca de fuites de memòria es converteix ràpidament en un bucle sense fi perquè els mateixos patrons reapareixen a cada sprint.

Quan paga la pena l’esforç — i quan hauries d’actuar d’una altra manera

FastMM FullDebugMode és una eina de diagnosi amb costos. L’esforç paga la pena especialment si:

  • L’aplicació s’executa durant molt de temps (servei, client de Terminal Server, sistema de torns, processos 24/7).
  • Estàs processant fluxos reals de dades de clients i no cobreixes tots els camins en les proves.
  • La estabilitat és més important que la velocitat de lliurament de noves funcionalitats a curt termini (típic en solucions de programari properes al procés).

Si, en canvi, només tens un petit assistent d’escriptori que acaba passats 30 segons, la cerca de fuites sovint és secundària. Igualment: si tens un pic de memòria puntual (p. ex. una exportació gran), sovint no és una fuga, sinó una qüestió d’estratègia de streaming i de la càrrega punta al heap.

Conclusió pràctica: FullDebugMode no és un interruptor, sinó un procés

FastMM FullDebugMode aporta estructura a la recerca d’errors de memòria: fa visibles les al·locacions, detecta abans la corrupció del heap i proporciona stacktraces amb els quals pots arreglar la causa en lloc del símptoma. La palanca decisiva, però, no és l’eina sinó el procediment: escenaris reproductibles, builds aptes per a diagnosi, acords clars d’ownership i regressió respecte d’una baseline.

Si estàs encallat amb una fuga persistent o un error esporàdic del heap i vols estabilitzar aquest tema de manera sostenible en un sistema més gran Delphi, val la pena una configuració de diagnosi breu i neta amb una seqüència clara i informes analitzables. Si necessites suport en anàlisi, perfils de build o refactorització d’arquitectura: contacta amb la Net-Base Software GmbH.

Per a aquest tema també són importants Delphi trobar fuites de memòria i llegir el Fastmm Leak Report. L’article situa aquests aspectes de manera comprensible i mostra què és rellevant en el dia a dia.

Parla del projecte o d’una iniciativa de modernització amb Net-Base.

Pas següent

Quan d'un tema se'ndevé un projecte real, s'han de considerar aviat i de manera conjunta l'arquitectura, els actius existents i l'operació.

No només donem suport en qüestions puntuals, sinó també quan, a partir de fragments de codi font, temes de sistemes heredats o idees de portal, ha de sorgir un projecte empresarial sòlid.

  • L'estat actual, la visió objectiu i els riscos tècnics s'avaluen conjuntament.
  • REST, accés a dades, portals i desplegament no es posposaran com a efectes retardats.
  • Veu aviat quin camí és viable des del punt de vista econòmic i operatiu.

Comparteix la publicació

Comparteix aquesta publicació directament

LinkedIn, X, XING, Facebook, WhatsApp i correu electrònic estan disponibles immediatament. Per Instagram preparem l'enllaç i un text curt immediatament.

Correu electrònic

Instagram s'obre en una pestanya nova. L'enllaç i el text curt es copien prèviament al porta-retalls.