Problemen oplossen: geheugen en prestaties
Reikwijdte
Sectie met titel “Reikwijdte”Deze items behandelen twee faalfamilies die je onder belasting tegenkomt: PHP dat tijdens een render het geheugen uitput, en doorvoer die van een klif valt zodra een proces warm of verzadigd is. Elk item benoemt een symptoom, de meest waarschijnlijke oorzaak en een oplossing die echt NextPDF-oppervlak of standaard PHP-FPM-besturingselementen gebruikt. Lees voor het onderliggende streamingmodel en een worker-tutorial Streaming en geheugen; deze pagina is het incidentzijde-gezelschap daarvan.
Meet eerst. Bemonster memory_get_peak_usage(true) voor en na een render en roep
memory_reset_peak_usage() aan tussen iteraties, op de manier waarop de benchmark
van de engine de kosten per render isoleert. Afstemmen zonder baseline verplaatst de
klif in plaats van die te verwijderen.
Item: “Allowed memory size exhausted” tijdens generatie
Sectie met titel “Item: “Allowed memory size exhausted” tijdens generatie”- Symptoom. Een render breekt af met een fatale
Allowed memory size of <n> bytes exhausteduit de PHP-runtime, vaak op een groot of afbeeldingszwaar document. - Waarschijnlijke oorzaak. Het standaard schrijfpad stelt het hele document
samen, serialiseert het dan, dus het piekgeheugen volgt de totale uitvoergrootte.
Een groot document, grote ingesloten afbeeldingen of een grote ingesloten
lettertype-face kunnen de request voorbij
memory_limitduwen. - Oplossing.
- Begrens de afbeeldingscache.
NextPDF\Core\ConfigsteltimageCacheBytesbeschikbaar (standaard52428800, dat is 50 MB). Verlaag het met de instance-wither$config->withImageCacheBytes($bytes)(signatuurwithImageCacheBytes(int $bytes): self) zodat een build die veel afbeeldingen insluit snel faalt op een bekend plafond in plaats van te swappen. Dit begrenst de in-memory afbeeldingscache; het hersampelt of hercodeert de afbeeldingen zelf niet. - Verklein invoer vóór het insluiten. Core schaalt of hercodeert afbeeldingen niet. Herschaal en hercodeer te groot rasterwerk voordat je het insluit, en sluit lettertypen in die je daadwerkelijk gebruikt zodat subsetting een kleine glyphset overhoudt (zie PDF-bestandsgrootte verkleinen).
- Houd compressie aan. Een verse
Configheeftcompressoptruestaan. Laat het aan voor normale builds;withCompress(false)is geen grootteoptimalisatie (het vergroot de uitvoer meestal). Grijp ernaar om de pijplijn te debuggen of te profileren — het verschuift de CPU/geheugen-tradeoff (de compressiestap overslaan) in plaats van geheugen te verminderen. - Verhoog
memory_limitbewust, per worker. Dit is een standaard PHP-instelling, geen NextPDF-key. Stel het in in de pool-config of metini_set('memory_limit', '256M')voor het CLI/queue-proces, en stem het af op een geprofileerde piek, niet op een gok.
- Begrens de afbeeldingscache.
- Gerelateerd. Streaming en geheugen.
Item: geheugen groeit met paginaaantal op zeer grote documenten
Sectie met titel “Item: geheugen groeit met paginaaantal op zeer grote documenten”- Symptoom. Een document van vele duizenden pagina’s put het geheugen uit ook al is elke pagina klein, en de piek stijgt ruwweg in de pas met het paginaaantal.
- Waarschijnlijke oorzaak. De gebufferde writer houdt het hele geserialiseerde document in de heap. Voor zeer grote documenten is dat de dominante kost.
- Oplossing.
- Geef de voorkeur aan het streaming-schrijfpad. Gebruik het gedocumenteerde
streaming-schrijfpad beschreven in
Streaming en geheugen: het
serialiseert elke pagina terwijl die wordt samengesteld en laat de buffer vrij,
wat de pagina-buffer-/uitvoergroei vermindert; kleine per-object-metadata
(offsets, paginaboom) kan nog steeds met pagina-/objectaantal meeschalen. Volg
het gedocumenteerde toegangspunt in plaats van interne klassen te kopiëren — de
onderliggende streaming-engine is
experimental-niveau en zijn symbolen zijn niet het stabiele publieke oppervlak. - Onthoud voor de native
writeHtml()-parser dat invoerzijdig geheugen wordt begrensd door zowel de nestingsdiepte- als de element-aantal-guards: ADR-001 begrenst nesting opMAX_NESTING_DEPTH = 100en weigert documenten bovenMAX_ELEMENT_COUNT = 50000. Een document dat de element-cap raakt, krijgt dat expliciet te horen in plaats van stilzwijgend het geheugen uit te putten. Deze ADR-001-caps gelden alleen voor de native parser; de optionele Chrome-brug (writeHtmlChrome()) rendert out of process en heeft zijn eigen aparte geheugen-/invoerlimieten, niet deze caps.
- Geef de voorkeur aan het streaming-schrijfpad. Gebruik het gedocumenteerde
streaming-schrijfpad beschreven in
Streaming en geheugen: het
serialiseert elke pagina terwijl die wordt samengesteld en laat de buffer vrij,
wat de pagina-buffer-/uitvoergroei vermindert; kleine per-object-metadata
(offsets, paginaboom) kan nog steeds met pagina-/objectaantal meeschalen. Volg
het gedocumenteerde toegangspunt in plaats van interne klassen te kopiëren — de
onderliggende streaming-engine is
- Gerelateerd. Streaming en geheugen.
Item: een langlevende worker put geheugen uit na veel jobs
Sectie met titel “Item: een langlevende worker put geheugen uit na veel jobs”- Symptoom. Enkele renders slagen, maar een queue worker die veel PDF’s back-to-back rendert, put geheugen uit na minuten of uren.
- Waarschijnlijke oorzaak. Een langlevend PHP-proces verzamelt allocaties over jobs heen. Een trage groei die in één request onzichtbaar is, stapelt op over duizenden.
- Oplossing.
- Deel registers, maak documenten opnieuw. Bouw het
FontRegistryenImageRegistryeenmaal tijdens het opstarten en geef ze door aan eenDocumentFactory; maak een versDocumentper job met$factory->create($config). Het parsen van lettertypen en afbeeldingen gebeurt dan eenmaal voor het proces, niet eenmaal per job, en de documentboom per job wordt opgeruimd wanneer die buiten scope raakt. Volgexamples/14-worker-factory.php. - Begrens de gedeelde afbeeldingscache met
new ImageRegistry(maxCacheBytes: ...)zodat die niet onbegrensd kan groeien over jobs heen. - Recycle de worker — processturing, geen engine-garantie. Stel in PHP-FPM
pm.max_requestsin zodat elk kind respawnt na een vast aantal requests. Gebruik in Laravel-queuesqueue:work --max-jobs/--max-time/--memory; gebruik in Symfony Messengermessenger:consume --limit/--time-limit/--memory-limit.
- Deel registers, maak documenten opnieuw. Bouw het
- Gerelateerd. Streaming en geheugen.
Item: doorvoerklif op een koud of onvoldoende opgewarmd proces
Sectie met titel “Item: doorvoerklif op een koud of onvoldoende opgewarmd proces”- Symptoom. De eerste renders in een vers proces zijn traag, of elke request betaalt een parse-kost die warme requests niet zouden moeten betalen.
- Waarschijnlijke oorzaak. Twee koudestartkosten stapelen op. PHP zonder opcache
hercompileert elk bestand bij elke request, en een onverwarmd
FontRegistryparset elke lettertype-face de eerste keer dat die wordt gebruikt. - Oplossing.
- Schakel opcache in (en JIT waar het helpt). Stel
opcache.enable=1en een royaleopcache.memory_consumptionin; stel in productieopcache.validate_timestamps=0in zodat de cache niet per request wordt hercontroleerd. Die instelling vereist een deploy-proces dat PHP-FPM herstart of herlaadt (of opcache anderszins reset, bijv.opcache_reset()/cachetool) bij elke release — anders blijft opcache de oude bytecode bedienen en draait er verouderde code na een deploy. Dit zijn standaard PHP-ini-instellingen, geen NextPDF-keys. - Warm en lock het lettertyperegister tijdens het opstarten. Op een
FontRegistry-instance parset$fontRegistry->warmup($fontFiles)faces eenmaal tijdens het opstarten, en$fontRegistry->lock()bevriest het register zodat request-time-code de gedeelde staat niet kan muteren;$fontRegistry->isLocked()rapporteert de staat. In een werkelijk langlevende worker of applicatieserver — een queue consumer of een RoadRunner/Swoole/Octane-worker die hetzelfde PHP-proces over veel requests levend houdt — behoudt een opgewarmd, gelockt register zijn geparste faces in objectstaat, wat per-request-lettertypeparsing omzet in een eenmalige proces-opstartkost. Onder het standaard PHP-FPM-requestmodel overleeft die opgewarmde objectstaat niet over requests heen: opcache cachet gecompileerde klassen en bytecode, geen opgewarmde userland-objectstaat, dus een opgewarmdFontRegistrywordt per request herbouwd (per request opnieuw uitgevoerd vanuit de bootstrap van het kind), niet warm gehouden over requests binnen een kind. Op gewone PHP-FPM amortiseert opcache vooral de bytecode-hercompileerkost; accepteer dat lettertypeparsing per request wordt betaald, niet geëlimineerd. Cross-request-amortisatie — elke face eenmaal parsen voor de levensduur van het proces — geldt alleen in een werkelijk langlevend proces zoals een RoadRunner/Swoole/Octane-worker of een queue consumer die hetzelfde PHP-proces over veel requests levend houdt. - Parse niet hetzelfde template per request opnieuw. Los lettertypen en
herbruikbare resources eenmaal op tijdens het opstarten via de gedeelde
registers; alleen het
Documentper job zou in de request moeten worden gemaakt.
- Schakel opcache in (en JIT waar het helpt). Stel
- Gerelateerd. Streaming en geheugen.
Item: server verzadigt en latency piekt onder concurrency
Sectie met titel “Item: server verzadigt en latency piekt onder concurrency”- Symptoom. Per-render-latency is prima in isolatie, maar onder belasting swapt de doos, verzadigt de CPU, of stapelen requests op en time-outen.
- Waarschijnlijke oorzaak. Te veel PHP-FPM-workers voor het beschikbare RAM, zodat de som van de workerpieken het fysieke geheugen overschrijdt en de host swapt; of te weinig workers, zodat requests serialiseren achter een kleine pool.
- Oplossing.
-
Dimensioneer
pm.max_childrenvanuit een geprofileerde piek. Gebruik de standaardformule:pm.max_children = (total RAM - OS/other overhead) / per-worker peak memoryMeet de echte piek van een worker met een representatief document (zie de profileringsnotitie in Reikwijdte), reserveer speling voor het OS en eventuele gecolokeerde services, en deel. Laat een marge; dimensioneer niet op 100% van RAM.
-
Pin compressiekost in je budget. Flate-compressie kan een significante CPU-kost van het schrijven van een stream zijn en schaalt met het volume aan comprimeerbare stream-bytes, dus paginaaantal en ingesloten-lettertype-volume beïnvloeden CPU per render; afbeeldingsverwerking, lettertype-subsetting en invoerparsing kunnen ook domineren. Meet met representatieve documenten, en houd rekening met de echte driver wanneer je workeraantal en CPU kiest.
-
Stel
pm.max_requestsnaastpm.max_childrenin zodat kinderen recyclen en eventuele trage groei terugwinnen, zoals in het worker-item hierboven.
-
- Gerelateerd. Streaming en geheugen.
Item: grote niet-vertrouwde invoer is traag of duur om te parsen
Sectie met titel “Item: grote niet-vertrouwde invoer is traag of duur om te parsen”- Symptoom. Een render is traag of geheugenzwaar op een grote of diep geneste invoer, vooral HTML of een lettertype dat je niet hebt geproduceerd.
- Waarschijnlijke oorzaak. Parse-kost schaalt met invoergrootte en -structuur. Een pathologische invoer (diepe nesting, een enorm element-aantal of een misvormd lettertype) kan het budget domineren.
- Oplossing.
- Leun op de grenzen van de engine. De native
writeHtml()-HTML-parser dwingtMAX_NESTING_DEPTH = 100enMAX_ELEMENT_COUNT = 50000af (ADR-001); invoer boven die caps wordt geweigerd in plaats van toegestaan om het proces uit te putten. (De optionele Chrome-brug,writeHtmlChrome(), valt buiten de scope van deze ADR-001-caps en dwingt zijn eigen aparte geheugen-/invoerlimieten af.) - Behandel door de aanroeper geleverde lettertypen als niet-vertrouwd. Een
misvormd lettertype werpt
NextPDF\Exception\FontParsingExceptionop in plaats van de uitvoer te beschadigen, dus vang de specifieke exceptie en weiger de invoer in plaats van opnieuw te proberen. - Valideer en dimensioneer invoer aan je grens, en pas request-niveau-limieten op documentgrootte toe voor door de aanroeper beïnvloede inhoud.
- Leun op de grenzen van de engine. De native
- Gerelateerd. Troubleshooting: lettertypen en tagging.
Beslistabel: symptoom naar hefboom
Sectie met titel “Beslistabel: symptoom naar hefboom”| Symptoom | Meest waarschijnlijke hefboom |
|---|---|
Allowed memory size … exhausted op één render | Verlaag $config->withImageCacheBytes(); verklein afbeeldingen vóór insluiten; verhoog memory_limit per worker |
| Piekgeheugen stijgt met paginaaantal | Gebruik het gedocumenteerde streaming-schrijfpad |
| Workergeheugen klimt over veel jobs | Deel FontRegistry/ImageRegistry via DocumentFactory; stel pm.max_requests / --max-jobs in |
| Eerste requests traag, parse-kost per request | Schakel opcache in; $fontRegistry->warmup() gevolgd door ->lock() tijdens opstarten |
| Host swapt / latency piekt onder belasting | Dimensioneer pm.max_children = (RAM − overhead) / piek per worker |
| Traag of zwaar op grote/niet-vertrouwde invoer | Leun op ADR-001-caps; weiger misvormde lettertypen bij FontParsingException |
Randgevallen en valkuilen
Sectie met titel “Randgevallen en valkuilen”imageCacheBytesis een geheugenplafond, geen grootteknop. Het verlagen begrenst de cache zodat een build snel faalt; het hersampelt of hercodeert nooit de afbeeldingen die je insluit. Core heeft geen afbeeldingskwaliteitsbesturing.withCompress(false)maakt bestanden groter en is een debug-/profileerhulp. Het is geen grootteoptimalisatie; het verschuift de CPU/geheugen-tradeoff (het slaat de compressiestap over) in plaats van geheugen te verminderen.- Het exacte geheugenprofiel van de streaming-engine is een
experimental-niveau-eigenschap en kan verschuiven tussen minor-releases. Behandel elke enkele meting als een observatie, geen overdraagbare constante. memory_limit,opcache.*,pm.max_childrenenpm.max_requestszijn standaard PHP- / PHP-FPM-instellingen. NextPDF stelt er geen eigen keys voor beschikbaar; configureer ze in je runtime, niet inConfig.
Zie ook
Sectie met titel “Zie ook”- Streaming en geheugen — het streamingmodel, de ADR-001-grenzen en de volledige batch-worker-tutorial.
- PDF-bestandsgrootte verkleinen — compressie en lettertype-subsetting, de twee echte groottebesturingen.
- Troubleshooting: lettertypen en tagging — lettertype-oplossing, parsing en subsetting-fouten.
- Kennisbankindex
Glossary: streaming writer · font subsetting