HotPDF izpostavi pomnilniški profil naloženega PDF kot graf, po katerem lahko poizvedujete: BuildLoadedObjectDependencyGraph vrne eno vozlišče na vsak posredni objekt z oceno plitve velikosti, zadržano velikostjo na osnovi dominatorja in zastavico, ali je objekt še vedno dosegljiv iz kataloga dokumenta. To spremeni "ta datoteka porabi 800 MB" v "objekt 4173, slikovni XObject, izključno zadržuje 612 MB", kar je dejstvo, na podlagi katerega lahko ukrepate
Razlika med tema dvema stavkoma je bistvo. Plitva velikost pove, kako velik je en objekt. Zadržana velikost pove, koliko pomnilnika bi se dejansko sprostilo, če bi ta objekt izginil, kar je številka, ki odloči, ali popravek pomaga
Zakaj skupna poraba pomnilnika ni dejstvo, na katerem lahko ukrepate?
Ker v PDF skoraj nič ni v lasti natanko ene stvari. En sam vdelan pisan CID je referenciran iz slovarja virov vsake strani, ki ga uporablja. Profil ICC podpira barvni prostor, ki si ga deli deset različnih vsebinskih tokov. Form XObject, uporabljen kot žig, se pojavi na vseh 400 straneh. Če naivno seštejete velikosti objektov na stran, to pisavo preštejete 400-krat in sklepate, da je vsaka stran ogromna; če jo delite s 400, sklepate, da nič ni drago in da je pomnilnik prišel od nekod drugod
Dominatorska analiza razreši nejasnost na edini način, ki preživi stik z resničnimi dokumenti. Objekt X je pripisan najbližjemu objektu, ki ga izključno zadržuje, kar pomeni, da vsaka referenčna pot od kataloga do X poteka skozi tega dominatorja. Pisava, ki si jo delijo vse strani, ni pripisana nobeni strani; pripisana je najbližjemu vozlišču, skozi katero potekajo vse te poti, kar je običajno kar sam katalog. Pisava, ki jo uporablja natanko ena stran, je pripisana tej strani. Rezultat je, da se EstimatedRetainedBytes pravilno sešteje namesto dvojnega štetja, objekti na vrhu seznama pa so objekti, katerih odstranitev bi resnično sprostila pomnilnik
Kaj graf dejansko vsebuje
Vsako vozlišče THPDFObjectDependencyNode nosi identiteto objekta kot ObjectNumber in GenerationNumber, poleg tega ObjectType, LifecycleState, EstimatedShallowBytes, EstimatedRetainedBytes, IncomingReferenceCount, OutgoingReferenceCount, identiteto svojega neposrednega dominatorja in ReachableFromCatalog. Vsak rob THPDFObjectDependencyEdge zabeleži izvor, cilj, pot slovarja Path, pod katero je bila referenca najdena, in ali je bila Resolved
To polje Path je tisto, ki ga ljudje premalo uporabljajo. Je razlika med tem, da veste, da objekt 91 kaže na objekt 4173, in tem, da veste, da to počne prek /Resources/XObject/Im3, kar vam takoj pove, ali gledate vsebino strani, tok videza pripombe ali skupino izbirne vsebine, ki je nihče nikoli ne izriše
var
Pdf: THotPDF;
Nodes: THPDFObjectDependencyNodeArray;
Edges: THPDFObjectDependencyEdgeArray;
Info: THPDFObjectDependencyGraphInfo;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('report-800mb.pdf') <> 1 then Exit;
if not Pdf.BuildLoadedObjectDependencyGraph(Nodes, Edges, Info) then Exit;
if Info.LimitExceeded then
Log('Graph truncated: raise MaxObjects / MaxEdges');
Log(Format('%d objects, %d edges, %d reachable, catalog retains %d bytes',
[Info.ObjectCount, Info.EdgeCount, Info.ReachableObjectCount,
Info.CatalogRetainedBytes]));
SortByRetainedDescending(Nodes);
for I := 0 to Min(9, High(Nodes)) do
Log(Format('%d %d obj: shallow %d, retained %d, dominator %d',
[Nodes[I].ObjectNumber, Nodes[I].GenerationNumber,
Nodes[I].EstimatedShallowBytes, Nodes[I].EstimatedRetainedBytes,
Nodes[I].ImmediateDominatorObjectNumber]));
finally
Pdf.Free;
end;
end;
Obe meji sta izrecna parametra s privzetima vrednostma 250.000 objektov in 2.000.000 robov. Ko dokument presega katero koli od njiju, je nastavljen LimitExceeded, vrnjeni graf pa je skrajšana predpona in ne laž: delni rezultati z zastavico, ne tiho napačni seštevki. Meje dvignite premišljeno za forenzični zagon in ne pozabite, da analiza obhodi celoten graf objektov, zato sodi v diagnostično pot, ne v vašo zanko izrisa
Kaj vam pove nedosegljiv objekt?
Objekt z vrednostjo ReachableFromCatalog, nastavljeno na False, je pomnilnik, ki ga dokument nosi, a ga pregledovalnik nikoli ne bo prikazal. V praksi izvira iz treh mest: prirastnih posodobitev, ki so nadomestile prejšnjo različico objekta in pustile izvirnik za seboj, izdelovalca, ki je zapisal objekte, ki jih nato ni povezal, ali poškodovane datoteke, katere tabela navzkrižnih sklicev je bila obnovljena in je pobrala definicije, na katere nič ne kaže
Prvi primer je normalen in pričakovan, in je natanko to, za kar so zasnovane prirastne posodobitve in tokovi objektov. Drugi in tretji sta vredna preiskave. Ko velik delež zadržanih bajtov sedi v nedosegljivih vozliščih, ste našli konkreten argument za ponovno zapisovanje datoteke namesto dodajanje nanjo, in imate število bajtov, s katerim to upravičite komur koli, ki vpraša
Dva števca dodeljevalnika, ki ju velja prebrati najprej
Preden sklenete, da je dokument neločljivo velik, preverite, ali je strošek razčlenjevalnik sam. HotPDF izpostavi dva števca, ki opisujeta, kako je potekalo nalaganje, in ne, kaj dokument vsebuje
GetLastParserArenaStatistics poroča o areni zadržanih blokov, uporabljeni za kratkotrajne žetone in medpomnilnike razčlenjevalnika: RetainedBytes, PeakUsedBytes, AllocationCount, ReusedAllocationCount, TokenCount in TemporaryObjectElisionCount. To zadnje polje šteje ključe slovarjev, razčlenjene neposredno v areno namesto prek začasnega imenskega objekta, kar je bila dodelitev, ki je nekoč prevladovala pri razčlenjevanju datotek, bogatih s slovarji
GetDocumentStringInternStatistics poroča o bazenu interniranja v obsegu dokumenta, ki podvaja ponovljena imena PDF, operatorje vsebinskega toka in nespremenljive nize do 64 bajtov. Vrne vam RequestCount, HitCount, MissCount, BypassCount, RetainedBytes in ReusedBytes, skupaj z veljavnimi mejami. Bazen je namerno omejen na 65.536 vnosov in 4 MiB, tako da sovražen dokument, ki ustvari milijon edinstvenih imen, ne more spremeniti optimizacije pomnilnika v pomnilniško ojačitev; ko je meja dosežena, nadaljnji nizi obidejo bazen in BypassCount narašča
var
Arena: THPDFParserArenaStatistics;
Intern: THPDFDocumentStringInternStatistics;
begin
if Pdf.GetLastParserArenaStatistics(Arena) then
Log(Format('arena: peak %d, reused %d of %d allocations, %d tokens',
[Arena.PeakUsedBytes, Arena.ReusedAllocationCount,
Arena.AllocationCount, Arena.TokenCount]));
if Pdf.GetDocumentStringInternStatistics(Intern) then
Log(Format('intern: %d/%d hits, %d bypassed, %d bytes reused',
[Intern.HitCount, Intern.RequestCount, Intern.BypassCount,
Intern.ReusedBytes]));
end;
Visoka vrednost ReusedBytes z nizkim BypassCount pomeni, da ima dokument ponavljajoč se besednjak, kot ga imajo večina resničnih PDF-jev, in da se bazen obrestuje. BypassCount, ki se približuje RequestCount, pomeni nekaj nenavadnega: bodisi resnično ogromen dokument bodisi takega, ki namerno ustvarja edinstvena imena, kar je blag signal, vreden beleženja na nezaupljivi poti sprejema
Vrstni red triaže, ki deluje
Začnite s CatalogRetainedBytes iz podatkov grafa. Če je to število blizu rasti vašega procesa, je pomnilnik v dokumentu in graf vam bo pokazal, kje. Če je precej nižje, je pomnilnik v vaših lastnih predpomnilnikih, v izrisanih bitnih slikah ali v razčlenjevalniku, števci arene pa vam bodo povedali, kje
Nato vzemite prvih deset vozlišč po EstimatedRetainedBytes in poglejte njihov ObjectType. Slikovni XObject-i na vrhu pomenijo, da je datoteka bogata s skeni in da je znižanje ločljivosti popravek. Opisovalci pisav na vrhu pomenijo, da so bile vdelane cele pisave, kjer bi zadostovali podnabori. Vsebinski tokovi na vrhu ponavadi pomenijo ustvarjeno vektorsko grafiko, pogosto zemljevide ali izvoze CAD. Šele nato se splača pogledati nedosegljive objekte in vedenje dodeljevalnika. Če delate v tem vrstnem redu, boste odgovor navadno našli v prvih dveh korakih, za zelo velike dokumente pa je pretočni pristop, opisan v delovnem toku neposrednega API za datoteke, pogosto strukturni popravek in ne kakršna koli optimizacija na posamezen objekt
Vsa ta diagnostika so navadni klici Pascal, ki vračajo navadne zapise, zato se neposredno vklopijo v obstoječo pot beleženja ali telemetrije. HotPDF je izvorna komponenta VCL PDF za Delphi in C++Builder s celotno izvorno kodo; referenca API in preizkusna gradnja sta na strani izdelka HotPDF