Ta članek je zrcalni članek strojnega prevajanja, kliknite tukaj za skok na izvirni članek.

Pogled: 537|Odgovoriti: 1

[Vir] [Obrat]. Kako NET/C# izračuna, koliko pomnilnika zaseda instanca?

[Kopiraj povezavo]
Objavljeno 27. 8. 2025 09:00:22 | | | |
Vsi vemo, da sta CPU in pomnilnik dve najpomembnejši metrici za program, zato se sprašujemo, koliko ljudi je res razmišljalo o vprašanju: Koliko bajtov zaseda primerek tipa (vrednost, tip ali tip reference) v pomnilniku? Veliko nas ne zna odgovoriti. C# ponuja nekaj operatorjev in API-jev za izračun velikosti, vendar nobeden od njih popolnoma ne reši problema, ki sem ga pravkar zastavil. Ta članek ponuja metodo za izračun števila pomnilniških bajtov, ki jih zasedajo primeri vrst vrednosti in vrst referenc. Izvorna koda je prenesena od tukaj.

1. Velikost operatorja
2. Marshal.SizeOf metoda
3. Metoda Unsafe.SizeOf >
4. Ali se lahko izračuna glede na vrsto člana na terenu?
5. Razporeditev vrst vrednosti in vrst aplikacij
6. Direktiva LDFLDA
7. Izračunajte število bajtov tipa vrednosti
8. Preštej število bajtov tipa citata
9. Popoln izračun

1. Velikost operatorja

Velikost operacije se uporablja za določanje števila bajtov, ki jih zaseda instanca določenega tipa, vendar jo je mogoče uporabiti le za neupravljane tipe. Tako imenovani neupravljani tip je omejen na:

Primitivni tipi: Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double in Single)
Decimalni tip
Tip enumeracije
Tip kazalca
Strukture, ki vsebujejo samo podatkovne člane tipa Unmanaged
Kot že ime pove, je neupravljani tip vrednostni tip in ustrezna instanca ne sme vsebovati nobene reference na upravljani objekt. Če definiramo generično metodo, kot je ta, da pokličemo operator velikosti, mora generični parameter T dodati neregulirano omejitev in nevarno oznako metodi.

Samo izvorni in enum tipi lahko neposredno uporabljajo operator velikosti, ki ga je treba dodati, če se uporablja za druge tipe (kazalce in prilagojene strukture)./unsafeprevajalne oznake, in jih je treba tudi vstaviti vNevarnihV kontekstu.

Ker naslednja struktura Foobar ni Unmanaged tip, bo program imel napako pri prevajanju.

2. Marshal.SizeOf metoda

Statični tipi Marshal definira vrsto API-jev, ki nam pomagajo dodeljevati in kopirati neupravljani pomnilnik, pretvarjati med upravljanimi in neupravljanimi tipi ter izvajati vrsto drugih operacij nad neupravljanim pomnilnikom (Marshal v računalniški znanosti pomeni pretvorbo pomnilniških objektov v ustrezen format za shranjevanje ali prenos podatkov). Statičen, ki vključuje naslednje 4 preobremenitve SizeOf za določanje števila bajtov določenega tipa ali objekta.

Metoda Marshal.SizeOf nima omejitve glede določenega tipa za Unmanaged tip, vendar vseeno zahteva, da je določenaVrsta vrednosti。 Če je vhodni objekt objekt, mora biti tudi okvir za tip vrednosti.

Ker je naslednji Foobar definiran kot:vrsta, zato klici obeh SizeOf metod vržejo izjemo ArgumentException in poziv: Tip 'Foobar' ni mogoče maršalirati kot neupravljano strukturo; ni mogoče izračunati smiselne velikosti ali zamika.

Metoda Marshal.SizeOfGenerični izdelki niso podprti, vendar ima tudi zahteve glede postavitve konstrukcije, ki podpira oporoZaporednainIzrecnoNačin postavitve. Ker Foobar struktura, prikazana spodaj, uporablja način Auto postavitve (Auto, ki ne podpira "dinamičnega načrtovanja" postavitve pomnilnika na podlagi članov polja zaradi strožjih zahtev glede postavitve pomnilnika v neupravljanih okoljih), bodo klici metode SizeOf še vedno vrgli isto izjemo ArgumentException kot zgoraj.

3. Metoda Unsafe.SizeOf

Static Unsafe omogoča bolj nizkonivojske operacije za neupravljani pomnilnik, podobne metode SizeIOf pa so prav tako definirane v tej vrsti. Metoda nima nobenih omejitev glede določenega tipa, vendar če določite referenčni tip, vrneŠtevilo bajtov kazalca"(IntPtr.Size)。

4. Ali se lahko izračuna glede na vrsto člana na terenu?

Vemo, da sta tako vrednost kot referenčni tip preslikana kot zvezni fragment (ali shranjena neposredno v registeru). Namen tipa je določiti postavitev pomnilnika objekta, primerki istega tipa pa imajo enako postavitev in število bajtov je naravno enako (za polja referenčnega tipa shrani le referenčni naslov v tem zaporedju bajtov). Ker je dolžina bajta določena s tipom, če lahko določimo tip vsakega člana polja, ali ne bi mogli izračunati števila bajtov, ki ustrezajo temu tipu? Pravzaprav to ni mogoče.

Na primer, vemo, da so bajti bajtov, short, int in long 1, 2, 4 in 8, zato je število bajtov za bajtni binarni sistem 2, vendar za tipno kombinacijo bajt + short, bajt + int in bajt + long ustrezni bajti niso 3, 5 in 9, temveč 3, 8 in 16. Ker to vključuje vprašanje poravnave spomina.

5. Razporeditev vrst vrednosti in vrst referenc

Število bajtov, ki jih zasedajo primerki referenčnega tipa in podtipa, je prav tako različno za točno istega podatkovnega člana. Kot je prikazano na naslednji sliki, bajtna zaporedje primera tipa vrednostiVsi so člani terena, ki ga shranjujejo。 Za primere referenčnih tipov je naslov ustrezne tabele metod shranjen tudi pred zaporedjem bajtov polja. Tabela metod zagotavlja skoraj vse metapodatke, ki opisujejo tip, in to referenco uporabimo za določitev, kateri vrsti pripada primerek. Na samem začetku so tudi dodatni bajti, ki jih bomo imenovaliGlava objektaNe uporablja se le za shranjevanje zaklenjenega stanja objekta, ampak je mogoče tudi zgoščevalno vrednost shraniti tukaj. Ko ustvarimo spremenljivko referenčnega tipa, ta spremenljivkaNe kaže na prvi bajt pomnilnika, ki ga zaseda instanca, temveč na mesto, kjer je shranjen naslov tabele metod



6. Direktiva LDFLDA

Kot smo uvedli zgoraj, operator sizeof in metoda SizeOf, ki jo zagotavlja statični tip Marshal/Unsafe, v resnici ne moreta rešiti izračuna dolžine bajta, ki ga zasedajo instance. Kolikor vem, tega problema ni mogoče rešiti samo na področju C#, vendar je na ravni ILLDFLDANavodila nam lahko pomagajo rešiti ta problem. Kot ime pove, Ldflda pomeni Load Field Address (naslov polja za nalaganje), ki nam pomaga pridobiti naslov polja v instanci. Ker ta ukaz IL nima ustreznega API-ja v C#, ga lahko uporabimo le v naslednji obliki z uporabo IL Emit.

Kot je prikazano v zgornjem kodnem izrezku, imamo metodo GenerateFieldAddressAccessor v tipu SizeCalculator, ki generira delegata tipa Func<object?, long[]> na podlagi seznama polj določenega tipa, kar nam pomaga vrniti pomnilniški naslov določenega objekta in vsa njegova polja. Z naslovom samega objekta in naslovom vsakega polja lahko naravno dobimo zamik vsakega polja in nato enostavno izračunamo število bajtov pomnilnika, ki jih zaseda celotna instanca.

7. Izračunajte število bajtov tipa vrednosti

Ker imajo tipi vrednosti in referenc različne postavitve v pomnilniku, moramo uporabiti tudi različne izračune. Ker je bajt strukture vsebina vseh polj v pomnilniku, uporabimo pameten način za izračun. Predpostavimo, da moramo poravnati število bajtov strukture tipa T, potem ustvarimo ValueTuple<T,T> torico, in zamik njenega drugega polja Item2 je število bajtov strukture T. Specifična metoda izračuna je prikazana v naslednji metodi CalculateValueTypeInstance.

Kot je prikazano v zgornjem kodnem odlomku, ob predpostavki, da je tip strukture, ki ga potrebujemo za izračun, T, pokličemo metodo GetDefaultAsObject, da dobimo privzeti(T) objekt v obliki odsev, nato pa ustvarimo ValueTuple<T,T>tuple. Po klicu metode GenerateFieldAddressAccessor za pridobitev Func<object?, long[]> delegata za izračun instance in njenih naslovov polj, ta delegat imenujemo kot argument. Za tri pomnilniške naslove, ki jih dobimo, so kodna n-tica in naslovi polj 1 in 2 enaki, uporabimo tretji naslov, ki predstavlja postavko 2 minus prvi naslov, in dobimo želeni rezultat.

8. Preštej število bajtov tipa citata

Izračun bajtov za tipe referenc je bolj zapleten, saj uporabljamo to idejo: ko dobimo naslov samega primerka in vsakega polja, razvrstimo naslove, da dobimo zamik zadnjega polja. Dodajmo ta zamik k številu bajtov samega zadnjega polja, nato pa dodamo potrebne "prve in zadnje bajte" k želenemu rezultatu, kar se odraža v naslednji metodi CalculateReferneceTypeInstance.

Kot je prikazano v zgornjem kodnem izrezku, če določeni tip nima definiranih polj, CalculateReferneceTypeInstance vrne minimalno število bajtov primerka referenčnega tipa: 3-krat več bajtov kazalca na naslov. Pri arhitekturah x86 objekt tipa aplikacije zavzame vsaj 12 bajtov, vključno z ObjectHeader (4 bajti), kazalci na tabelo metod (bajti) in vsaj 4 bajti vsebine polja (ti 4 bajti so potrebni tudi, če ni definiran noben tip brez polja). V primeru arhitekture x64 bo to minimalno število bajtov 24, saj bo kazalec tabele metode in minimalna vsebina polja postala 8 bajtov, čeprav veljavna vsebina ObjectHeaderja zavzema le 4 bajte, pred njim pa bodo dodani 4 bajti polnila.

Razporeditev bajtov, ki jih zaseda zadnje polje, je prav tako zelo preprosta: če je tip vrednostni tip, se prej definirana metoda CalculateValueTypeInstance kliče za izračun, če je referenčni tip, je vsebina, shranjena v polju, le pomnilniški naslov ciljnega objekta, torej je dolžina IntPtr.Size. Ker so primerki referenčnega tipa privzeto poravnani z IntPtr.Size v pomnilniku, se to tudi tukaj počne. Nazadnje ne pozabite, da referenca primerka referenčnega tipa ne kaže na prvi bajt pomnilnika, temveč na bajt, ki shranjuje kazalec na tabelo metod, zato morate dodati število bajtov ObjecthHeader (IntPtr.Size).

9. Popoln izračun

Dve metodi, ki se uporabljata za izračun števila bajtov primerkov tipa vrednosti in referenčnega tipa, se uporabljata v naslednji metodi SizeOf. Ker mora klic ukaza Ldflda zagotoviti ustrezno instanco, ta metoda omogoča delegata za pridobitev ustrezne instance poleg navedbe ciljne vrste. Parametri, ki ustrezajo temu delegatu, so lahko privzeti in za tip vrednosti uporabimo privzeto vrednost. Za vrste referenc bomo prav tako poskušali ustvariti ciljni objekt z privzetim konstruktorjem. Če ta objekt delegate ni na voljo in ciljne instance ni mogoče ustvariti, metoda SizeOf sproži izjemo. Čeprav moramo zagotoviti ciljno instanco, je izračunani rezultat povezan le s tipom, zato izračunani rezultat predpomnimo. Za lažje klicanje ponujamo še eno generično <T>metodo SizeOf.

V spodnjem kodnem izrezku ga uporabimo za izpis števila bajtov dveh struktur in tipov z isto definicijo polja. V naslednjem članku bomo na podlagi izračunanega števila bajtov dobili celotno binarno vsebino primerka v pomnilniku, zato ostanite z nami.

Izvirna povezava:Prijava do hiperpovezave je vidna.




Prejšnji:Front-end okvir se uči odprtokodnega projekta Component-Party
Naslednji:MinIO shranjevanje (iii) Kopiranje-nalaganje (migracija) lokalnih datotek v minio vedro
 Najemodajalec| Objavljeno 27. 8. 2025 09:33:22 |
Zbirka C# vstavi 10.000 podatkov, ki zasedejo pomnilnik




Koda:


Disclaimer:
Vsa programska oprema, programski materiali ali članki, ki jih izdaja Code Farmer Network, so namenjeni zgolj učnim in raziskovalnim namenom; Zgornja vsebina ne sme biti uporabljena v komercialne ali nezakonite namene, sicer uporabniki nosijo vse posledice. Informacije na tej strani prihajajo z interneta, spori glede avtorskih pravic pa nimajo nobene zveze s to stranjo. Zgornjo vsebino morate popolnoma izbrisati z računalnika v 24 urah po prenosu. Če vam je program všeč, podprite pristno programsko opremo, kupite registracijo in pridobite boljše pristne storitve. Če pride do kakršne koli kršitve, nas prosimo kontaktirajte po elektronski pošti.

Mail To:help@itsvse.com