Fehlerbehebung: Speicher und Performance
Geltungsbereich
Abschnitt betitelt „Geltungsbereich“Diese Einträge behandeln zwei Fehlerfamilien, auf die Sie unter Last stoßen: PHP, das während eines Renders den Speicher erschöpft, und ein Durchsatz, der von einer Klippe fällt, sobald ein Prozess warm oder gesättigt ist. Jeder Eintrag benennt ein Symptom, die wahrscheinlichste Ursache und eine Behebung, die echte NextPDF-Oberfläche oder Standard-PHP-FPM-Kontrollen verwendet. Für das zugrunde liegende Streaming-Modell und ein Worker-Tutorial lesen Sie Streaming und Speicher; diese Seite ist der vorfallseitige Begleiter dazu.
Messen Sie zuerst. Erfassen Sie memory_get_peak_usage(true) vor und nach einem
Render und rufen Sie memory_reset_peak_usage() zwischen den Iterationen auf – so
wie der Benchmark der Engine die Kosten pro Render isoliert. Ein Tuning ohne
Baseline verschiebt die Klippe, statt sie zu entfernen.
Eintrag: „Allowed memory size exhausted“ während der Generierung
Abschnitt betitelt „Eintrag: „Allowed memory size exhausted“ während der Generierung“- Symptom. Ein Render bricht mit einem fatalen
Allowed memory size of <n> bytes exhaustedaus der PHP-Laufzeit ab, oft bei einem großen oder bildlastigen Dokument. - Wahrscheinliche Ursache. Der Standard-Schreibpfad setzt das gesamte Dokument
zusammen und serialisiert es dann, sodass die Speicherspitze der gesamten
Ausgabegröße folgt. Ein großes Dokument, große eingebettete Bilder oder ein
großer eingebetteter Schriftschnitt können die Anfrage über
memory_limithinaustreiben. - Lösung.
- Begrenzen Sie den Bild-Cache.
NextPDF\Core\ConfigstelltimageCacheBytesbereit (Standard52428800, das sind 50 MB). Senken Sie ihn mit dem Instanz-Wither$config->withImageCacheBytes($bytes)(SignaturwithImageCacheBytes(int $bytes): self), sodass ein Build, der viele Bilder einbettet, an einer bekannten Obergrenze schnell fehlschlägt, statt zu swappen. Dies deckelt den In-Memory-Bild-Cache; es resampelt oder re-encodiert die Bilder selbst nicht. - Verkleinern Sie Eingaben vor dem Einbetten. Core skaliert oder re-encodiert Bilder nicht herunter. Skalieren und re-encodieren Sie überdimensionierte Rastergrafiken, bevor Sie sie einbetten, und betten Sie Schriften ein, die Sie tatsächlich verwenden, damit das Subsetting eine kleine Glyphenmenge zu behalten hat (siehe PDF-Dateigröße reduzieren).
- Lassen Sie die Komprimierung an. Eine frische
Confighatcompressauftruegesetzt. Lassen Sie es für normale Builds an;withCompress(false)ist keine Größenoptimierung (es vergrößert die Ausgabe meist). Greifen Sie dazu, um die Pipeline zu debuggen oder zu profilieren – es verschiebt den CPU/Speicher-Tradeoff (das Überspringen des Komprimierungsschritts), statt Speicher zu reduzieren. - Erhöhen Sie
memory_limitbewusst, pro Worker. Dies ist eine Standard-PHP-Einstellung, kein NextPDF-Schlüssel. Setzen Sie sie in der Pool-Konfiguration oder mitini_set('memory_limit', '256M')für den CLI-/Queue-Prozess und dimensionieren Sie sie gegen eine profilierte Spitze, nicht gegen eine Schätzung.
- Begrenzen Sie den Bild-Cache.
- Verwandt. Streaming und Speicher.
Eintrag: Speicher wächst mit der Seitenanzahl bei sehr großen Dokumenten
Abschnitt betitelt „Eintrag: Speicher wächst mit der Seitenanzahl bei sehr großen Dokumenten“- Symptom. Ein mehrtausendseitiges Dokument erschöpft den Speicher, obwohl jede Seite klein ist, und die Spitze steigt etwa im Gleichschritt mit der Seitenanzahl.
- Wahrscheinliche Ursache. Der gepufferte Writer hält das gesamte serialisierte Dokument im Heap. Für sehr große Dokumente ist das die dominante Kostenursache.
- Lösung.
- Bevorzugen Sie den Streaming-Schreibpfad. Verwenden Sie den dokumentierten
Streaming-Schreibpfad, beschrieben in
Streaming und Speicher: Er
serialisiert jede Seite, sobald sie zusammengesetzt ist, und gibt den Puffer
frei, was das Wachstum von Seitenpuffer/Ausgabe reduziert; kleine
Pro-Objekt-Metadaten (Offsets, Seitenbaum) können dennoch mit der
Seiten-/Objektanzahl skalieren. Folgen Sie dem dokumentierten Einstiegspunkt,
statt interne Klassen zu kopieren – die zugrunde liegende Streaming-Engine ist
experimental-eingestuft, und ihre Symbole sind nicht die stabile öffentliche Oberfläche. - Für den nativen
writeHtml()-Parser bedenken Sie, dass der eingabeseitige Speicher sowohl durch die Verschachtelungstiefe- als auch durch die Elementanzahl-Guards begrenzt ist: ADR-001 deckelt die Verschachtelung beiMAX_NESTING_DEPTH = 100und lehnt Dokumente überMAX_ELEMENT_COUNT = 50000ab. Einem Dokument, das die Elementobergrenze erreicht, wird dies explizit mitgeteilt, statt den Speicher stillschweigend zu erschöpfen. Diese ADR-001-Obergrenzen regeln nur den nativen Parser; die optionale Chrome-Bridge (writeHtmlChrome()) rendert prozessextern und hat ihre eigenen, separaten Speicher-/Eingabegrenzen, nicht diese Obergrenzen.
- Bevorzugen Sie den Streaming-Schreibpfad. Verwenden Sie den dokumentierten
Streaming-Schreibpfad, beschrieben in
Streaming und Speicher: Er
serialisiert jede Seite, sobald sie zusammengesetzt ist, und gibt den Puffer
frei, was das Wachstum von Seitenpuffer/Ausgabe reduziert; kleine
Pro-Objekt-Metadaten (Offsets, Seitenbaum) können dennoch mit der
Seiten-/Objektanzahl skalieren. Folgen Sie dem dokumentierten Einstiegspunkt,
statt interne Klassen zu kopieren – die zugrunde liegende Streaming-Engine ist
- Verwandt. Streaming und Speicher.
Eintrag: Ein langlebiger Worker erschöpft den Speicher nach vielen Jobs
Abschnitt betitelt „Eintrag: Ein langlebiger Worker erschöpft den Speicher nach vielen Jobs“- Symptom. Einzelne Renders gelingen, aber ein Queue-Worker, der viele PDFs hintereinander rendert, erschöpft den Speicher nach Minuten oder Stunden.
- Wahrscheinliche Ursache. Ein langlebiger PHP-Prozess akkumuliert Allokationen über Jobs hinweg. Ein langsames Wachstum, das in einer Anfrage unsichtbar ist, summiert sich über Tausende.
- Lösung.
- Teilen Sie Registrys, erstellen Sie Dokumente neu. Bauen Sie die
FontRegistryundImageRegistryeinmal beim Boot und übergeben Sie sie an eineDocumentFactory; erstellen Sie ein frischesDocumentpro Job mit$factory->create($config). Das Parsen von Schriften und Bildern geschieht dann einmal für den Prozess, nicht einmal pro Job, und der Dokumentbaum pro Job wird eingesammelt, sobald er den Gültigkeitsbereich verlässt. Folgen Sieexamples/14-worker-factory.php. - Begrenzen Sie den gemeinsam genutzten Bild-Cache mit
new ImageRegistry(maxCacheBytes: ...), sodass er nicht über Jobs hinweg unbegrenzt wachsen kann. - Recyceln Sie den Worker – Prozesssteuerung, keine Engine-Garantie. Setzen
Sie in PHP-FPM
pm.max_requests, sodass jedes Kind nach einer festen Anzahl von Anfragen neu erzeugt wird. Verwenden Sie in Laravel-Queuesqueue:work --max-jobs/--max-time/--memory; in Symfony Messengermessenger:consume --limit/--time-limit/--memory-limit.
- Teilen Sie Registrys, erstellen Sie Dokumente neu. Bauen Sie die
- Verwandt. Streaming und Speicher.
Eintrag: Durchsatz-Einbruch bei einem kalten oder unzureichend aufgewärmten Prozess
Abschnitt betitelt „Eintrag: Durchsatz-Einbruch bei einem kalten oder unzureichend aufgewärmten Prozess“- Symptom. Die ersten Renders in einem frischen Prozess sind langsam, oder jede Anfrage zahlt Parse-Kosten, die warme Anfragen nicht zahlen sollten.
- Wahrscheinliche Ursache. Zwei Kaltstartkosten stapeln sich. PHP ohne Opcache
kompiliert jede Datei bei jeder Anfrage neu, und eine nicht aufgewärmte
FontRegistryparst jeden Schriftschnitt beim ersten Verwenden. - Lösung.
- Aktivieren Sie Opcache (und JIT, wo es hilft). Setzen Sie
opcache.enable=1und ein großzügigesopcache.memory_consumption; setzen Sie in der Produktionopcache.validate_timestamps=0, sodass der Cache nicht pro Anfrage erneut geprüft wird. Diese Einstellung erfordert einen Deploy-Prozess, der PHP-FPM bei jedem Release neu startet oder neu lädt (oder Opcache anderweitig zurücksetzt, z. B.opcache_reset()/cachetool) – andernfalls liefert Opcache weiter den alten Bytecode, und veralteter Code läuft nach einem Deploy. Dies sind Standard-PHP-Ini-Einstellungen, keine NextPDF-Schlüssel. - Wärmen und sperren Sie die Font-Registry beim Boot. Auf einer
FontRegistry-Instanz parst$fontRegistry->warmup($fontFiles)Schnitte einmal während des Boots, und$fontRegistry->lock()friert die Registry ein, sodass Code zur Anfragezeit gemeinsam genutzten Zustand nicht mutieren kann;$fontRegistry->isLocked()meldet den Zustand. In einem echt langlebigen Worker oder Anwendungsserver – einem Queue-Consumer oder einem RoadRunner/Swoole/Octane-Worker, der denselben PHP-Prozess über viele Anfragen am Leben hält – behält eine aufgewärmte, gesperrte Registry ihre geparsten Schnitte im Objektzustand und verwandelt das Parsen von Schriften pro Anfrage in eine einmalige Prozess-Boot-Kostenstelle. Unter dem Standard-PHP-FPM-Anfragemodell überlebt dieser aufgewärmte Objektzustand nicht über Anfragen hinweg: Opcache cacht kompilierte Klassen und Bytecode, nicht aufgewärmten Userland-Objektzustand, sodass eine aufgewärmteFontRegistrypro Anfrage neu aufgebaut wird (bei jeder Anfrage aus dem Bootstrap des Kindes erneut ausgeführt), nicht über Anfragen innerhalb eines Kindes warm gehalten. Auf schlichtem PHP-FPM amortisiert Opcache hauptsächlich die Kosten der Bytecode-Neukompilierung; akzeptieren Sie, dass das Parsen von Schriften pro Anfrage bezahlt wird, nicht eliminiert. Die anfrageübergreifende Amortisierung – jeden Schnitt einmal für die Lebensdauer des Prozesses zu parsen – gilt nur in einem echt langlebigen Prozess wie einem RoadRunner/Swoole/Octane-Worker oder einem Queue-Consumer, der denselben PHP-Prozess über viele Anfragen am Leben hält. - Parsen Sie nicht dieselbe Vorlage pro Anfrage erneut. Lösen Sie Schriften
und wiederverwendbare Ressourcen einmal beim Boot über die gemeinsam genutzten
Registrys auf; nur das
Documentpro Job sollte in der Anfrage erstellt werden.
- Aktivieren Sie Opcache (und JIT, wo es hilft). Setzen Sie
- Verwandt. Streaming und Speicher.
Eintrag: Server sättigt und Latenz spitzt unter Nebenläufigkeit
Abschnitt betitelt „Eintrag: Server sättigt und Latenz spitzt unter Nebenläufigkeit“- Symptom. Die Render-Latenz ist isoliert in Ordnung, aber unter Last swappt die Box, die CPU sättigt, oder Anfragen stauen sich und laufen in einen Timeout.
- Wahrscheinliche Ursache. Zu viele PHP-FPM-Worker für den verfügbaren RAM, sodass die Summe der Worker-Spitzen den physischen Speicher übersteigt und der Host swappt; oder zu wenige Worker, sodass Anfragen sich hinter einem kleinen Pool serialisieren.
- Lösung.
-
Dimensionieren Sie
pm.max_childrenaus einer profilierten Spitze. Verwenden Sie die Standardformel:pm.max_children = (total RAM - OS/other overhead) / per-worker peak memoryMessen Sie die reale Spitze eines Workers mit einem repräsentativen Dokument (siehe den Profiling-Hinweis im Geltungsbereich), reservieren Sie Spielraum für das OS und alle kolokierten Dienste und dividieren Sie. Lassen Sie eine Marge; dimensionieren Sie nicht auf 100 % des RAM.
-
Verankern Sie die Komprimierungskosten in Ihrem Budget. Die Flate-Komprimierung kann ein erheblicher CPU-Kostenfaktor beim Schreiben eines Streams sein und skaliert mit dem Volumen der komprimierbaren Stream-Bytes, sodass Seitenanzahl und eingebettetes Schriftvolumen die CPU pro Render beeinflussen; Bildverarbeitung, Schrift-Subsetting und Eingabe-Parsing können ebenfalls dominieren. Messen Sie mit repräsentativen Dokumenten und berücksichtigen Sie den realen Treiber, wenn Sie Worker-Anzahl und CPU wählen.
-
Setzen Sie
pm.max_requestsnebenpm.max_children, sodass Kinder recyceln und jegliches langsame Wachstum zurückgewinnen, wie im Worker-Eintrag oben.
-
- Verwandt. Streaming und Speicher.
Eintrag: Große nicht vertrauenswürdige Eingabe ist langsam oder teuer zu parsen
Abschnitt betitelt „Eintrag: Große nicht vertrauenswürdige Eingabe ist langsam oder teuer zu parsen“- Symptom. Ein Render ist langsam oder speicherlastig bei einer großen oder tief verschachtelten Eingabe, besonders HTML oder einer Schrift, die Sie nicht erzeugt haben.
- Wahrscheinliche Ursache. Die Parse-Kosten skalieren mit Eingabegröße und -struktur. Eine pathologische Eingabe (tiefe Verschachtelung, eine enorme Elementanzahl oder eine fehlerhafte Schrift) kann das Budget dominieren.
- Lösung.
- Stützen Sie sich auf die Grenzen der Engine. Der native
writeHtml()-HTML- Parser erzwingtMAX_NESTING_DEPTH = 100undMAX_ELEMENT_COUNT = 50000(ADR-001); Eingaben über diesen Obergrenzen werden abgelehnt, statt zugelassen zu werden, den Prozess zu erschöpfen. (Die optionale Chrome-Bridge,writeHtmlChrome(), liegt außerhalb des Geltungsbereichs dieser ADR-001-Obergrenzen und erzwingt ihre eigenen, separaten Speicher-/Eingabegrenzen.) - Behandeln Sie vom Aufrufer gelieferte Schriften als nicht vertrauenswürdig.
Eine fehlerhafte Schrift löst
NextPDF\Exception\FontParsingExceptionaus, statt die Ausgabe zu beschädigen, also fangen Sie die spezifische Exception ab und lehnen die Eingabe ab, statt es erneut zu versuchen. - Validieren und dimensionieren Sie Eingaben an Ihrer Grenze und wenden Sie anfragebezogene Grenzen für die Dokumentgröße bei vom Aufrufer beeinflusstem Inhalt an.
- Stützen Sie sich auf die Grenzen der Engine. Der native
- Verwandt. Fehlerbehebung: Schriften und Tagging.
Entscheidungstabelle: Symptom zu Hebel
Abschnitt betitelt „Entscheidungstabelle: Symptom zu Hebel“| Symptom | Wahrscheinlichster Hebel |
|---|---|
Allowed memory size … exhausted bei einem einzelnen Render | $config->withImageCacheBytes() senken; Bilder vor dem Einbetten verkleinern; memory_limit pro Worker erhöhen |
| Speicherspitze steigt mit der Seitenanzahl | Den dokumentierten Streaming-Schreibpfad verwenden |
| Worker-Speicher steigt über viele Jobs | FontRegistry/ImageRegistry über DocumentFactory teilen; pm.max_requests / --max-jobs setzen |
| Erste Anfragen langsam, Parse-Kosten pro Anfrage | Opcache aktivieren; $fontRegistry->warmup(), dann ->lock() beim Boot |
| Host swappt / Latenzspitzen unter Last | pm.max_children = (RAM − Overhead) / Worker-Spitze dimensionieren |
| Langsam oder schwer bei großer/nicht vertrauenswürdiger Eingabe | Auf ADR-001-Obergrenzen verlassen; fehlerhafte Schriften bei FontParsingException ablehnen |
Grenzfälle & Stolperfallen
Abschnitt betitelt „Grenzfälle & Stolperfallen“imageCacheBytesist eine Speicherobergrenze, kein Größenregler. Das Senken deckelt den Cache, sodass ein Build schnell fehlschlägt; es resampelt oder re-encodiert die eingebetteten Bilder niemals. Core hat keine Bildqualitätssteuerung.withCompress(false)macht Dateien größer und ist eine Debugging-/ Profiling-Hilfe. Es ist keine Größenoptimierung; es verschiebt den CPU/Speicher-Tradeoff (es überspringt den Komprimierungsschritt), statt Speicher zu reduzieren.- Das genaue Speicherprofil der Streaming-Engine ist eine
experimental-eingestufte Eigenschaft und kann sich zwischen Minor-Releases verschieben. Behandeln Sie jede einzelne Messung als Beobachtung, nicht als portable Konstante. memory_limit,opcache.*,pm.max_childrenundpm.max_requestssind Standard-PHP-/PHP-FPM-Einstellungen. NextPDF stellt keine eigenen Schlüssel dafür bereit; konfigurieren Sie sie in Ihrer Laufzeit, nicht inConfig.
Siehe auch
Abschnitt betitelt „Siehe auch“- Streaming und Speicher – das Streaming-Modell, die ADR-001-Grenzen und das vollständige Batch-Worker-Tutorial.
- PDF-Dateigröße reduzieren – Komprimierung und Schrift-Subsetting, die beiden echten Größensteuerungen.
- Fehlerbehebung: Schriften und Tagging – Schriftauflösungs-, Parsing- und Subsetting-Fehler.
- Index der Wissensdatenbank
Glossar: Streaming-Writer · Schrift-Subsetting