HotPDF paljastaa ladatun PDF:n muistiprofiilin kyseltävänä graafina: BuildLoadedObjectDependencyGraph palauttaa yhden solmun jokaista epäsuoraa objektia kohti arvioidulla pintakoolla, dominaattoripohjaisella säilytetyllä koolla ja lipulla siitä, onko objekti yhä saavutettavissa asiakirjan Catalog-objektista. Tämä muuttaa väitteen "tämä tiedosto käyttää 800 Mt" väitteeksi "objekti 4173, kuva-XObject, säilyttää yksinomaan 612 Mt", mikä on fakta, jonka perusteella voi toimia
Ero näiden kahden lauseen välillä on koko pointti. Pintakoko kertoo, kuinka iso yksi objekti on. Säilytetty koko kertoo, kuinka paljon muistia todella vapautuisi, jos tuo objekti katoaisi, ja se on luku, joka ratkaisee, auttaako korjaus
Miksi kokonaismuistinkäyttö ei ole toimintakelpoinen fakta?
Koska PDF:ssä lähes mikään ei ole täsmälleen yhden asian omistuksessa. Yhteen upotettuun CID-fonttiin viitataan jokaisen sitä käyttävän sivun resurssisanakirjasta. ICC-profiilivirta tukee väriavaruutta, jota kymmenen eri sisältövirtaa jakaa. Leimana käytetty Form XObject esiintyy kaikilla 400 sivulla. Jos lasket objektikoot naiivisti yhteen sivukohtaisesti, lasket tuon fontin 400 kertaa ja päättelet jokaisen sivun olevan valtava; jos jaat sen 400:lla, päättelet, ettei mikään ole kallista ja muisti tuli jostain muualta
Dominaattorianalyysi ratkaisee epäselvyyden ainoalla tavalla, joka selviää kosketuksesta todellisiin asiakirjoihin. Objekti X kohdistetaan lähimpään objektiin, joka säilyttää sen yksinomaan, mikä tarkoittaa, että jokainen viittauspolku Catalogista X:ään kulkee tuon dominaattorin kautta. Kaikkien sivujen jakamaa fonttia ei kohdisteta millekään sivulle; se kohdistetaan lähimpään solmuun, jonka kaikki nuo polut kulkevat läpi, mikä on yleensä itse Catalog. Vain yhdellä sivulla käytetty fontti kohdistetaan sille sivulle. Tuloksena on, että EstimatedRetainedBytes summautuu oikein sen sijaan, että se laskisi kaksinkertaisesti, ja listan kärjessä olevat objektit ovat objekteja, joiden poistaminen todella vapauttaisi muistia
Mitä graafi todella sisältää
Jokainen THPDFObjectDependencyNode kantaa objektin identiteetin arvoina ObjectNumber ja GenerationNumber, sekä ObjectType, LifecycleState, EstimatedShallowBytes, EstimatedRetainedBytes, IncomingReferenceCount, OutgoingReferenceCount, sen välittömän dominaattorin identiteetin ja ReachableFromCatalog-arvon. Jokainen THPDFObjectDependencyEdge tallentaa lähteen, kohteen, sanakirjapolun Path, jonka alla viittaus löytyi, ja sen, oliko se Resolved
Tuo Path-kenttä on se, jota käytetään liian vähän. Se on ero sen tietämisen välillä, että objekti 91 osoittaa objektiin 4173, ja sen tietämisen välillä, että se tekee niin kentän /Resources/XObject/Im3 kautta, mikä kertoo sinulle heti, katsotko sivun sisältöä, huomautuksen ulkoasuvirtaa vai valinnaista sisältöryhmää, jota kukaan ei koskaan renderöi
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;
Molemmat rajat ovat eksplisiittisiä parametreja, joiden oletusarvot ovat 250 000 objektia ja 2 000 000 kaarta. Kun asiakirja ylittää jommankumman, LimitExceeded asetetaan, ja palautettu graafi on katkaistu etuliite eikä valhe: osittaiset tulokset lipun kanssa, ei hiljaa väärät summat. Nosta rajoja tarkoituksella oikeuslääketieteellistä ajoa varten, ja muista, että analyysi kävelee koko objektigraafin läpi, joten se kuuluu diagnostiikkapolkuun, ei renderöintisilmukkaasi
Mitä saavuttamaton objekti kertoo?
Objekti, jonka ReachableFromCatalog on False, on muistia, jota asiakirja kantaa mutta jota katseluohjelma ei koskaan näytä. Käytännössä se tulee kolmesta paikasta: inkrementaalisista päivityksistä, jotka korvasivat objektin aiemman version ja jättivät alkuperäisen jälkeensä, tuottajasta, joka kirjoitti objekteja, joita se sitten ei onnistunut linkittämään, tai vioittuneesta tiedostosta, jonka ristiviitetaulukko rakennettiin uudelleen ja poimi määrityksiä, joihin mikään ei viittaa
Ensimmäinen tapaus on normaali ja odotettu, ja se on juuri se, mitä artikkelissa inkrementaaliset päivitykset ja objektivirrat kuvattu toiminta on suunniteltu tekemään. Toinen ja kolmas kannattaa tutkia. Kun suuri osa säilytetyistä tavuista istuu saavuttamattomissa solmuissa, olet löytänyt konkreettisen perusteen tiedoston uudelleenkirjoittamiselle sen sijaan, että liittäisit siihen lisää, ja sinulla on tavumäärä, jolla perustella ylimääräinen käsittelyaika kenelle tahansa kysyjälle
Kaksi varaajan laskuria, jotka kannattaa lukea ensin
Ennen kuin päättelet, että asiakirja on luonnostaan suuri, tarkista, onko itse jäsennin kustannuksen aiheuttaja. HotPDF paljastaa kaksi laskuria, jotka kuvaavat, miten lataus käyttäytyi, eivät sitä, mitä asiakirja sisältää
GetLastParserArenaStatistics raportoi lyhytikäisille jäsentimen tokeneille ja puskureille käytetyn säilytetyn lohkoareenan: RetainedBytes, PeakUsedBytes, AllocationCount, ReusedAllocationCount, TokenCount ja TemporaryObjectElisionCount. Tuo viimeinen kenttä laskee sanakirja-avaimet, jotka jäsennettiin suoraan areenaan väliaikaisen nimiobjektin sijaan, mikä on se varaus, joka aiemmin hallitsi sanakirjaraskaiden tiedostojen jäsennystä
GetDocumentStringInternStatistics raportoi asiakirjakohtaisen internointialtaan, joka poistaa kaksoiskappaleet toistuvista PDF-nimistä, sisältövirran operaattoreista ja muuttumattomista enintään 64 tavun merkkijonoista. Se antaa sinulle arvot RequestCount, HitCount, MissCount, BypassCount, RetainedBytes ja ReusedBytes, sekä voimassa olevat katot. Allas on tarkoituksella rajattu 65 536 merkintään ja 4 MiB:iin, joten vihamielinen asiakirja, joka generoi miljoona yksilöllistä nimeä, ei voi muuttaa muistioptimointia muistivahvistimeksi; kun katto saavutetaan, myöhemmät merkkijonot ohittavat altaan ja BypassCount nousee
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;
Korkea ReusedBytes-arvo yhdessä matalan BypassCount-arvon kanssa tarkoittaa, että asiakirjalla on se toistuva sanasto, joka useimmilla todellisilla PDF-tiedostoilla on, ja allas ansaitsee paikkansa. BypassCount, joka lähestyy arvoa RequestCount, tarkoittaa jotain epätavallista: joko aidosti valtavaa asiakirjaa, tai sellaista, joka generoi yksilöllisiä nimiä tarkoituksella, mikä on lievä signaali, joka kannattaa kirjata lokiin epäluotettavalla vastaanottopolulla
Toimiva priorisointijärjestys
Aloita graafin tiedoista löytyvästä CatalogRetainedBytes-arvosta. Jos tuo luku on lähellä prosessisi kasvua, muisti on asiakirjassa ja graafi näyttää sinulle missä. Jos se on paljon alempi, muisti on omissa välimuisteissasi, renderöidyissä bittikartoissa tai jäsentimessä, ja areenan laskurit kertovat kummassa
Ota sitten kymmenen ylintä solmua EstimatedRetainedBytes-arvon mukaan ja katso niiden ObjectType-arvoa. Kuva-XObjectit kärjessä tarkoittavat, että tiedosto on skannausraskas ja alinäytteistys on korjaus. Fonttikuvaajat kärjessä tarkoittavat, että täysiä fontteja upotettiin siellä, missä osajoukot riittäisivät. Sisältövirrat kärjessä tarkoittavat yleensä generoitua vektorigrafiikkaa, usein karttoja tai CAD-vientejä. Vasta sen jälkeen kannattaa katsoa saavuttamattomia objekteja ja varaajan käyttäytymistä. Tässä järjestyksessä työskennellen löydät yleensä vastauksen kahdessa ensimmäisessä vaiheessa, ja erittäin suurille asiakirjoille artikkelissa suoran tiedosto-API:n työnkulku kuvattu striimauslähestymistapa on usein rakenteellinen korjaus minkä tahansa objektikohtaisen optimoinnin sijaan
Kaikki nämä diagnostiikat ovat tavallisia Pascal-kutsuja, jotka palauttavat tavallisia tietueita, joten ne pudotetaan suoraan olemassa olevaan loki- tai telemetriapolkuun. HotPDF on natiivi VCL PDF component Delphille ja C++Builderille täydellä lähdekoodilla; API-viite ja kokeiluversio löytyvät sivulta HotPDF Delphi PDF component page