Šis straipsnis yra veidrodinis mašininio vertimo straipsnis, spauskite čia norėdami pereiti prie originalaus straipsnio.

Rodinys: 537|Atsakyti: 1

[Šaltinis] [Posūkis]. Kaip NET/C# apskaičiuoja, kiek atminties užima egzempliorius?

[Kopijuoti nuorodą]
Publikuota: 2025-8-27 09:00:22 | | | |
Visi žinome, kad procesorius ir atmintis yra du svarbiausi programos rodikliai, tad kiek žmonių iš tikrųjų pagalvojo apie klausimą: kiek baitų atmintyje užima tipo egzempliorius (reikšmės tipas arba nuorodos tipas)? Daugelis iš mūsų negali atsakyti. C# pateikia keletą operatorių ir API dydžiams apskaičiuoti, tačiau nė vienas iš jų visiškai neišsprendžia problemos, kurią ką tik paklausiau. Šiame straipsnyje pateikiamas metodas, kaip apskaičiuoti atminties baitų, kuriuos užima reikšmių tipų ir nuorodų tipų egzemplioriai, skaičių. Šaltinio kodas atsisiunčiamas iš čia.

1. Veiklos vykdytojo dydis
2. Maršalas.DydisMetodas
3. Unsafe.SizeOf metodas >
4. Ar jis gali būti apskaičiuojamas pagal lauko nario tipą?
5. Reikšmių tipų ir programų tipų išdėstymas
6. LDFLDA direktyva
7. Apskaičiuokite vertės tipo baitų skaičių
8. Suskaičiuokite citatos tipo baitų skaičių
9. Išsamus skaičiavimas

1. Veiklos vykdytojo dydis

Operacijos dydis naudojamas baitų skaičiui, kurį užima tipo egzempliorius, nustatyti, tačiau jis gali būti taikomas tik nevaldomiesiems tipams. Vadinamasis nevaldomas tipas apsiriboja:

Primityvūs tipai: "Boolean", "Byte", "SByte", "Int16", "UInt16", "Int32", "UInt32", "Int64", "UInt64", "IntPtr", "UIntPtr", "Char", "Double" ir "Single")
Dešimtainis tipas
Išvardijimo tipas
Žymeklio tipas
Struktūros, kuriose yra tik nevaldomojo tipo duomenų nariai
Kaip rodo pavadinimas, nevaldomasis tipas yra reikšmės tipas, o atitinkamame egzemplioriuje negali būti jokios nuorodos į valdomą objektą. Jei apibrėžiame tokį bendrąjį metodą, kad iškviestume operatorių sizeof, bendrasis parametras T turi pridėti prie metodo nevaldomą apribojimą ir nesaugią žymą.

Tik vietiniai ir išvardijimo tipai gali tiesiogiai naudoti operatoriaus sizeof, kuris turi būti pridėtas, jei taikomas kitiems tipams (rodyklėms ir pasirinktinėms struktūroms)./unsafekompiliacijos žymes, taip pat turi būti įdėtas įNesaugiųkontekste.

Kadangi ši struktūra Foobar nėra nevaldomas tipas, programa turės kompiliavimo klaidą.

2. Maršalas.DydisMetodas

Statiniai tipai Maršalas apibrėžia API seriją, kuri padeda mums paskirstyti ir kopijuoti nevaldomą atmintį, konvertuoti valdomus ir nevaldomus tipus ir atlikti daugybę kitų operacijų su nevaldoma atmintimi (Maršalas skaičiavimo moksle reiškia atminties objektų konvertavimą į atitinkamą duomenų saugojimo ar perdavimo formatą). Statinis, apimantis šias 4 SizeOf metodo perkrovas, skirtas nustatyti tam tikro tipo ar objekto baitų skaičių.

Metodas Marshal.SizeOf neturi nurodyto tipo nevaldomojo tipo apribojimo, tačiau jį vis tiek reikia nurodytiVertės tipas。 Jei gaunamas objektas yra objektas, jis taip pat turi būti reikšmės tipo laukas.

Kadangi šis Foobar apibrėžiamas kaip:rūšis, todėl iškvietimai į abu SizeOf metodus pateiks ArgumentException išimtį ir raginimą: Tipas 'Foobar' negali būti suskirstytas kaip nevaldoma struktūra; jokio reikšmingo dydžio ar poslinkio negalima apskaičiuoti.

Marshal.SizeOf metodasGeneriniai vaistai nepalaikomi, bet taip pat turi reikalavimus konstrukcijos išdėstymui, kuris palaiko atramąNuosekliusirAiškusIšdėstymo režimas. Kadangi žemiau parodyta Foobar struktūra naudoja automatinio išdėstymo režimą (automatinis, kuris nepalaiko "dinaminio planavimo" atminties išdėstymo pagal lauko narius dėl griežtesnių atminties išdėstymo reikalavimų nevaldomoje aplinkoje), iškvietimai į SizeOf metodą vis tiek pateiks tą pačią ArgumentException išimtį, kaip ir aukščiau.

3. Unsafe.SizeOf metodas

Static Unsafe suteikia daugiau žemo lygio operacijų nevaldomai atminčiai, o panašūs SizeIOf metodai taip pat apibrėžiami šiame tipe. Metodas neturi jokių nurodyto tipo apribojimų, bet jei nurodysite nuorodos tipą, jis grąžinsŽymeklio baitų skaičius"(IntPtr.Size)。

4. Ar jis gali būti apskaičiuojamas pagal lauko nario tipą?

Mes žinome, kad tiek vertės, tiek nuorodos tipai yra susieti kaip ištisinis fragmentas (arba saugomi tiesiogiai registre). Tipo paskirtis yra nurodyti objekto atminties išdėstymą, o to paties tipo egzemplioriai turi tą patį išdėstymą, o baitų skaičius natūraliai yra vienodas (nuorodos tipo laukuose šioje baitų sekoje saugomas tik nurodytas adresas). Kadangi baitų ilgis nustatomas pagal tipą, jei galime nustatyti kiekvieno lauko nario tipą, ar negalėtume apskaičiuoti tą tipą atitinkančio baitų skaičiaus? Tiesą sakant, tai neįmanoma.

Pavyzdžiui, žinome, kad baitų, trumpųjų, int ir ilgųjų baitų skaičius yra 1, 2, 4 ir 8, taigi baitų dvejetainio failo baitų skaičius yra 2, tačiau tipo kombinacijai baitas + trumpas, baitas + int ir baitas + ilgis atitinkami baitai yra ne 3, 5 ir 9, o 3, 8 ir 16. Nes tai susiję su atminties išlyginimo klausimu.

5. Reikšmių tipų ir nuorodų tipų išdėstymas

Nuorodos tipo ir potipio egzempliorių užimamų baitų skaičius taip pat skiriasi tam pačiam duomenų nariui. Kaip parodyta toliau pateiktame paveikslėlyje, reikšmės tipo egzemplioriaus baitų sekaVisi yra lauko nariai, naudojami saugoti。 Nuorodų tipų egzemplioriuose atitinkamos metodo lentelės adresas taip pat saugomas priešais lauko baitų seką. Metodų lentelėje pateikiami beveik visi metaduomenys, apibūdinantys tipą, ir mes naudojame šią nuorodą, kad nustatytume, kuriam tipui egzempliorius priklauso. Pačiame priekyje taip pat yra papildomų baitų, kuriuos pavadinsimeObjekto antraštėJis naudojamas ne tik užrakintai objekto būsenai saugoti, bet ir maišos reikšmę čia galima saugoti talpykloje. Kai sukuriame nuorodos tipo kintamąjį, šis kintamasisJis nurodo ne pirmąjį atminties baitą, kurį užima egzempliorius, o vietą, kurioje saugomas metodų lentelės adresas



6. LDFLDA direktyva

Kaip jau minėjome aukščiau, operatoriaus dydis ir SizeOf metodas, kurį pateikia statinis tipas Marshal/Unsafe, iš tikrųjų negali išspręsti egzempliorių užimamo baitų ilgio apskaičiavimo. Kiek žinau, šios problemos negalima išspręsti vien C# lauke, tačiau ji teikiama IL lygiuLdfldaInstrukcijos gali padėti išspręsti šią problemą. Kaip rodo pavadinimas, Ldflda reiškia Įkelti lauko adresą, kuris padeda mums gauti egzemplioriaus lauko adresą. Kadangi ši IL instrukcija neturi atitinkamos API C#, galime ją naudoti tik tokia forma naudodami IL Emit.

Kaip parodyta aukščiau esančiame kodo fragmente, SizeCalculator tipe turime GenerateFieldAddressAccessor metodą, kuris generuoja Func<object?, long[]> tipo atstovą pagal nurodyto tipo laukų sąrašą, kuris padeda mums grąžinti nurodyto objekto ir visų jo laukų atminties adresą. Turėdami paties objekto adresą ir kiekvieno lauko adresą, galime natūraliai gauti kiekvieno lauko poslinkį ir lengvai apskaičiuoti viso egzemplioriaus užimamų atminties baitų skaičių.

7. Apskaičiuokite vertės tipo baitų skaičių

Kadangi reikšmių tipai ir nuorodų tipai atmintyje turi skirtingus išdėstymus, taip pat turime naudoti skirtingus skaičiavimus. Kadangi konstrukcijos baitas yra visų atminties laukų turinys, mes naudojame protingą būdą jį apskaičiuoti. Tarkime, kad turime suskaičiuoti T tipo struktūros baitų skaičių, tada sukuriame ValueTuple<T,T> tuple, o jo antrojo lauko Item2 poslinkis yra struktūros T baitų skaičius. Konkretus skaičiavimo metodas atsispindi šiame CalculateValueTypeInstance metode.

Kaip parodyta aukščiau esančiame kodo fragmente, darant prielaidą, kad struktūros tipas, kurį turime apskaičiuoti, yra T, mes iškviečiame GetDefaultAsObject metodą, kad gautume default(T) objektą atspindžio pavidalu, o tada sukuriame ValueTuple<T,T>tuple. Iškvietę metodą GenerateFieldAddressAccessor, kad gautume Func<object?, long[]> atstovą egzemplioriui ir jo laukų adresams apskaičiuoti, šį atstovą vadiname argumentu. Trims gautiems atminties adresams kodo tupelis ir 1 ir 2 laukų adresai yra vienodi, naudojame trečiąjį adresą, reiškiantį Item2 atėmus pirmąjį adresą, ir gauname norimą rezultatą.

8. Suskaičiuokite citatos tipo baitų skaičių

Nuorodų tipų baitų skaičiavimas yra sudėtingesnis, naudojant šią idėją: gavę paties egzemplioriaus ir kiekvieno lauko adresą, rūšiuojame adresus, kad gautume paskutinio lauko poslinkį. Pridėkime šį poslinkį prie paties paskutinio lauko baitų skaičiaus, o tada prie norimo rezultato pridėkime reikiamus "pirmuosius ir paskutinius baitus", kurie atsispindi šiame CalculateReferneceTypeInstance metode.

Kaip parodyta aukščiau pateiktame kodo fragmente, jei nurodytame tipe nėra apibrėžtų laukų, CalculateReferneceTypeInstance grąžina mažiausią nuorodos tipo egzemplioriaus baitų skaičių: 3 kartus didesnį už adreso žymeklio baitų skaičių. x86 architektūrose programos tipo objektas užima mažiausiai 12 baitų, įskaitant ObjectHeader (4 baitus), metodų lentelės rodykles (baitus) ir mažiausiai 4 baitus lauko turinio (šie 4 baitai reikalingi, net jei nėra apibrėžtas joks tipas be jokių laukų). x64 architektūros atveju šis minimalus baitų skaičius bus 24, nes metodo lentelės rodyklė ir minimalus lauko turinys taps 8 baitais, nors galiojantis ObjectHeader turinys užima tik 4 baitus, tačiau priekyje bus pridėti 4 baitai užpildymo.

Paskutinio lauko užimamų baitų atsiskaitymas taip pat yra labai paprastas: jei tipas yra vertės tipas, tada skaičiavimui iškviečiamas anksčiau apibrėžtas CalculateValueTypeInstance metodas, jei tai yra nuorodos tipas, lauke saugomas turinys yra tik tikslinio objekto atminties adresas, todėl ilgis yra IntPtr.Size. Kadangi nuorodos tipo egzemplioriai pagal numatytuosius nustatymus yra suderinti su IntPtr.Size atmintyje, tai taip pat daroma čia. Galiausiai, nepamirškite, kad nuorodos tipo egzemplioriaus nuoroda nurodo ne pirmąjį atminties baitą, o baitą, kuriame saugomas metodo lentelės žymeklis, todėl turite pridėti ObjecthHeader (IntPtr.Size) baitų skaičių.

9. Išsamus skaičiavimas

Du metodai, naudojami reikšmės tipo ir nuorodos tipo egzempliorių baitų skaičiui apskaičiuoti, naudojami šiame SizeOf metode. Kadangi Ldflda instrukcijos iškvietimas turi pateikti atitinkamą egzempliorių, šis metodas suteikia delegatą, kad gautų atitinkamą egzempliorių, be tikslinio tipo. Šį atstovą atitinkantys parametrai gali būti numatytieji ir mes naudosime numatytąją vertės tipo reikšmę. Nuorodų tipams taip pat bandysime sukurti tikslinį objektą naudodami numatytąjį konstruktorių. Jei šis atstovo objektas nepateikiamas ir paskirties egzemplioriaus sukurti negalima, metodas SizeOf pateikia išimtį. Nors turime pateikti tikslinį egzempliorių, apskaičiuotas rezultatas yra susijęs tik su tipu, todėl apskaičiuojamą rezultatą saugome talpykloje. Kad būtų lengviau skambinti, taip pat pateikiame kitą bendrą SizeOf <T>metodą.

Žemiau esančiame kodo fragmente jį naudojame dviejų struktūrų ir tipų baitų skaičiui su tuo pačiu lauko apibrėžimu išvesti. Kitame straipsnyje toliau gausime visą dvejetainį egzemplioriaus turinį atmintyje pagal apskaičiuotą baitų skaičių, todėl sekite naujienas.

Originali nuoroda:Hipersaito prisijungimas matomas.




Ankstesnis:Front-end sistema mokosi Component-Party atvirojo kodo projekto
Kitą:MinIO saugykla (iii) Kopijuoti ir įkelti (perkelti) vietinius failus į minio kibirą
 Savininkas| Publikuota: 2025-8-27 09:33:22 |
C# kolekcijoje įterpta 10 000 duomenų, kurie užima atmintį




Kodas:


Atsakomybės apribojimas:
Visa programinė įranga, programavimo medžiaga ar straipsniai, kuriuos skelbia Code Farmer Network, yra skirti tik mokymosi ir mokslinių tyrimų tikslams; Aukščiau nurodytas turinys negali būti naudojamas komerciniais ar neteisėtais tikslais, priešingu atveju vartotojai prisiima visas pasekmes. Šioje svetainėje pateikiama informacija gaunama iš interneto, o ginčai dėl autorių teisių neturi nieko bendra su šia svetaine. Turite visiškai ištrinti aukščiau pateiktą turinį iš savo kompiuterio per 24 valandas nuo atsisiuntimo. Jei jums patinka programa, palaikykite autentišką programinę įrangą, įsigykite registraciją ir gaukite geresnes autentiškas paslaugas. Jei yra kokių nors pažeidimų, susisiekite su mumis el. paštu.

Mail To:help@itsvse.com