HotPDF atskleidžia įkelto PDF atminties profilį kaip grafą, kurį galite užklausti: BuildLoadedObjectDependencyGraph grąžina po vieną mazgą kiekvienam netiesioginiam objektui su numatomu paviršiniu dydžiu, dominavimu pagrįstu išlaikomu dydžiu ir vėliavėle, ar objektas vis dar pasiekiamas iš dokumento katalogo. Tai paverčia „šis failas naudoja 800 MB" į „objektas 4173, paveikslėlio XObject, išskirtinai išlaiko 612 MB", o tai jau faktas, kuriuo galima remtis
Skirtumas tarp šių dviejų sakinių yra visa esmė. Paviršinis dydis pasako, koks didelis vienas objektas. Išlaikomas dydis pasako, kiek atminties iš tikrųjų būtų atlaisvinta, jei tas objektas dingtų, o tai ir yra skaičius, nulemiantis, ar taisymas padės
Kodėl bendras atminties naudojimas nėra veiksmingas faktas?
Todėl, kad PDF faile beveik niekas nepriklauso tiksliai vienam dalykui. Vienas įterptas CID šriftas nurodomas iš kiekvieno puslapio, kuris jį naudoja, išteklių žodyno. ICC profilio srautas palaiko spalvų erdvę, kurią dalijasi dešimt skirtingų turinio srautų. Formos XObject, naudojamas kaip antspaudas, pasirodo visuose 400 puslapių. Jei naiviai sudėsite objektų dydžius pagal puslapį, jūs suskaičiuosite tą šriftą 400 kartų ir padarysite išvadą, kad kiekvienas puslapis milžiniškas; jei padalinsite iš 400, padarysite išvadą, kad niekas nebrangu ir atmintis atsirado iš kažkur kitur
Dominavimo analizė išsprendžia dviprasmybę vieninteliu būdu, kuris išlieka susidūrus su realiais dokumentais. Objektas X priskiriamas artimiausiam objektui, kuris išskirtinai jį išlaiko, tai reiškia, kad kiekvienas nuorodų kelias nuo katalogo iki X eina per tą dominuojantį objektą. Šriftas, kurį dalijasi visi puslapiai, nepriskiriamas nė vienam puslapiui; jis priskiriamas artimiausiam mazgui, per kurį eina visi tie keliai, o tai paprastai yra pats katalogas. Šriftas, naudojamas tik viename puslapyje, priskiriamas tam puslapiui. Rezultatas toks, kad EstimatedRetainedBytes susumuoja teisingai vietoje dvigubo skaičiavimo, o objektai sąrašo viršuje yra tie, kurių pašalinimas iš tikrųjų atlaisvintų atmintį
Ką iš tikrųjų turi grafas
Kiekvienas THPDFObjectDependencyNode turi objekto tapatybę kaip ObjectNumber ir GenerationNumber, taip pat ObjectType, LifecycleState, EstimatedShallowBytes, EstimatedRetainedBytes, IncomingReferenceCount, OutgoingReferenceCount, tiesioginio dominuojančio objekto tapatybę ir ReachableFromCatalog. Kiekvienas THPDFObjectDependencyEdge užrašo šaltinį, taikinį, žodyno Path, kuriame nuoroda buvo rasta, ir ar ji Resolved
Tas Path laukas yra tas, kurio žmonės nepakankamai naudoja. Tai skirtumas tarp žinojimo, kad objektas 91 nurodo į objektą 4173, ir žinojimo, kad tai daroma per /Resources/XObject/Im3, kas iš karto pasako, ar žiūrite į puslapio turinį, anotacijos išvaizdos srautą, ar pasirinktinio turinio grupę, kurios niekas niekada neatvaizduoja
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;
Abi ribos yra aiškūs parametrai su numatytosiomis reikšmėmis 250 000 objektų ir 2 000 000 briaunų. Kai dokumentas viršija bet kurią iš jų, LimitExceeded nustatoma, o grąžintas grafas yra apkarpytas priešdėlis, o ne melas: daliniai rezultatai su vėliavėle, ne tyliai neteisingos sumos. Padidinkite ribas sąmoningai kriminalistiniam paleidimui ir atminkite, kad analizė apeina visą objektų grafą, todėl jos vieta yra diagnostikos kelias, ne atvaizdavimo ciklas
Ką pasako nepasiekiamas objektas?
Objektas su ReachableFromCatalog, nustatytu į False, yra atmintis, kurią dokumentas neša, tačiau peržiūros programa jos niekada nerodys. Praktiškai jis atsiranda iš trijų vietų: prieauginiai atnaujinimai, kurie pakeitė ankstesnę objekto versiją ir paliko originalą; generatorius, kuris parašė objektus, kurių vėliau nesusiejo; arba sugadintas failas, kurio kryžminių nuorodų lentelė buvo atkurta ir pasiėmė apibrėžimus, kurių niekas nenurodo
Pirmasis atvejis normalus ir tikėtinas, ir tai lygiai tas, ko yra skirti prieauginiai atnaujinimai ir objektų srautai. Antrasis ir trečiasis vertas ištirti. Kai didelė dalis išlaikomų baitų sėdi nepasiekiamuose mazguose, jūs rasite konkretų argumentą perrašyti failą vietoje jo papildymo, ir turėsite baitų skaičių, kad pagrįstumėte papildomą apdorojimo laiką bet kam, kas paklaus
Du paskirstytojo skaitikliai, kuriuos verta patikrinti pirmiausia
Prieš darant išvadą, kad dokumentas iš prigimties didelis, patikrinkite, ar pats analizatorius yra kainos priežastis. HotPDF atskleidžia du skaitiklius, kurie aprašo, kaip vyko įkėlimas, o ne ką turi dokumentas
GetLastParserArenaStatistics praneša apie išlaikomą blokų areną, naudojamą trumpai gyvuojantiems analizatoriaus žetonams ir buferiams: RetainedBytes, PeakUsedBytes, AllocationCount, ReusedAllocationCount, TokenCount ir TemporaryObjectElisionCount. Šis paskutinis laukas skaičiuoja žodyno raktus, analizuojamus tiesiai į areną, o ne per laikiną vardo objektą, o tai yra išskyrimas, kuris anksčiau dominuodavo žodynais turtingų failų analizavime
GetDocumentStringInternStatistics praneša apie dokumento apimties internavimo baseiną, kuris pašalina pasikartojančių PDF vardų, turinio srauto operatorių ir nekintamų eilučių iki 64 baitų dubliavimą. Jis suteikia RequestCount, HitCount, MissCount, BypassCount, RetainedBytes ir ReusedBytes, kartu su galiojančiomis ribomis. Baseinas sąmoningai apribotas iki 65 536 įrašų ir 4 MiB, todėl priešiškas dokumentas, generuojantis milijoną unikalių vardų, negali paversti atminties optimizavimo atminties stiprintuvu; kai riba pasiekiama, tolesnės eilutės apeina baseiną, o BypassCount auga
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;
Aukšta ReusedBytes reikšmė su žemu BypassCount reiškia, kad dokumentas turi pasikartojantį žodyną, kurį turi dauguma realių PDF, o baseinas pasiteisina. BypassCount, artėjantis prie RequestCount, reiškia kažką neįprasto: arba tikrai milžinišką dokumentą, arba tokį, kuris sąmoningai generuoja unikalius vardus, o tai švelnus signalas, vertas registruoti nepatikimame priėmimo kelyje
Prioritetų tvarka, kuri veikia
Pradėkite nuo CatalogRetainedBytes iš grafo informacijos. Jei šis skaičius artimas jūsų proceso augimui, atmintis yra dokumente, ir grafas parodys, kur. Jei jis gerokai mažesnis, atmintis yra jūsų pačių podėliuose, atvaizduotuose bitmapose arba analizatoriuje, o arenos skaitikliai pasakys, kur būtent
Tada paimkite dešimt viršutinių mazgų pagal EstimatedRetainedBytes ir pažiūrėkite į jų ObjectType. Paveikslėlių XObject viršuje reiškia, kad failas gausus skenuotų vaizdų, ir mažinimo skiriamoji geba yra sprendimas. Šriftų aprašai viršuje reiškia, kad buvo įterpti pilni šriftai ten, kur pakaktų poaibių. Turinio srautai viršuje paprastai reiškia sugeneruotą vektorinę grafiką, dažnai žemėlapius arba CAD eksportus. Tik po to verta žiūrėti į nepasiekiamus objektus ir paskirstytojo elgseną. Dirbdami šia tvarka, dažniausiai atsakymą rasite pirmuosiuose dviejuose žingsniuose, o labai dideliems dokumentams srautinis metodas, aprašytas straipsnyje apie tiesioginio failų API darbo eigą, dažnai yra struktūrinis sprendimas, o ne bet koks optimizavimas pagal atskirą objektą
Visos šios diagnostikos yra paprasti Pascal iškvietimai, grąžinantys paprastus įrašus, todėl jie tiesiogiai įsilieja į esamą žurnalų ar telemetrijos kelią. HotPDF yra natyvus VCL PDF komponentas Delphi ir C++Builder su pilnu šaltiniu; API dokumentacija ir bandomoji versija pateikta HotPDF Delphi PDF komponento puslapyje