Mēs visi zinām, ka CPU un atmiņa ir divi vissvarīgākie programmas rādītāji, tāpēc cik daudz cilvēku patiešām ir domājuši par jautājumu: Cik baitu tipa instance (vērtības tips vai atsauces tips) aizņem atmiņā? Daudzi no mums nevar atbildēt. C# nodrošina dažus operatorus un API izmēru aprēķināšanai, bet neviens no tiem pilnībā neatrisina tikko uzdoto problēmu. Šajā rakstā ir sniegta metode, kā aprēķināt atmiņas baitu skaitu, ko aizņem vērtību tipu un atsauču tipu instances. Avota kods ir lejupielādēts šeit.
1. Operatora lielums 2. Marshal.SizeOf metode 3. Unsafe.SizeOf metode > 4. Vai to var aprēķināt, pamatojoties uz lauka dalībnieka veidu? 5. Vērtību veidu un lietojumprogrammu veidu izkārtojums 6. LDFLDA direktīva 7. Aprēķiniet vērtības tipa baitu skaitu 8. Saskaitiet citāta tipa baitu skaitu 9. Pilnīgs aprēķins
1. Operatora lielums
Operācijas lielums tiek izmantots, lai noteiktu baitu skaitu, ko aizņem tipa instance, bet to var lietot tikai nepārvaldītiem tipiem. Tā sauktais nepārvaldītais tips ir ierobežots ar:
Primitīvie tipi: Būla, baits, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double un Single) Decimāldaļas tips Uzskaitījuma veids Rādītāja tips Struktūras, kas satur tikai datu elementus ar tipu Nepārvaldīts Kā norāda nosaukums, nepārvaldītais tips ir vērtības tips, un atbilstošajā instancē nevar būt nekādas atsauces uz pārvaldīto objektu. Ja mēs definējam šādu vispārīgu metodi, lai izsauktu operatoru sizeof, vispārējam parametram T metodei jāpievieno nepārvaldīts ierobežojums un nedrošs tags.
Tikai vietējie un uzskaitījuma tipi var tieši izmantot operatora sizeof, kas jāpievieno, ja tas tiek lietots citiem tipiem (rādītājiem un pielāgotām struktūrām)./unsafekompilācijas tagiem, kā arī jāievietoNedrošukontekstā.
Tā kā šāds struct Foobar nav nepārvaldīts tips, programmai būs kompilācijas kļūda.
2. Marshal.SizeOf metode
Statiskie tipi Maršals definē virkni API, kas palīdz mums piešķirt un kopēt nepārvaldīto atmiņu, konvertēt starp pārvaldītiem un nepārvaldītiem tipiem un veikt virkni citu darbību ar nepārvaldītu atmiņu (Maršals skaitļošanas zinātnē attiecas uz atmiņas objektu konvertēšanu atbilstošā formātā datu glabāšanai vai pārsūtīšanai). Statisks, kas ietver šādas 4 SizeOf metodes pārslodzes, lai noteiktu konkrēta tipa vai objekta baitu skaitu.
Metodei Marshal.SizeOf nav ierobežojumu attiecībā uz norādīto tipu Nepārvaldītajam tipam, bet tas joprojām ir jānorādaVērtības veids。 Ja ienākošais objekts ir objekts, tam jābūt arī vērtības tipa lodziņam.
Tā kā šāds Foobar ir definēts kā:laipns, tāpēc izsaukumi uz abām SizeOf metodēm radīs ArgumentException izņēmumu un uzvedni: Ierakstiet 'Foobar' nevar sadalīt kā nepārvaldītu struktūru; nav iespējams aprēķināt jēgpilnu izmēru vai nobīdi.
Marshal.SizeOf metodeĢenēriskās zāles netiek atbalstītas, bet ir arī prasības konstrukcijas izkārtojumam, kas atbalsta atbalstuSecīgusunSkaidruIzkārtojuma režīms. Tā kā zemāk redzamā Foobar struktūra pieņem automātisko izkārtojuma režīmu (Auto, kas neatbalsta atmiņas izkārtojuma "dinamisko plānošanu", pamatojoties uz lauka dalībniekiem, jo nepārvaldītā vidē ir stingrākas atmiņas izkārtojuma prasības), SizeOf metodes izsaukumi joprojām izmetīs to pašu ArgumentException izņēmumu kā iepriekš.
3. Nedroša.SizeOf metode
Static Unsafe nodrošina vairāk zema līmeņa operāciju nepārvaldītai atmiņai, un līdzīgas SizeIOf metodes ir definētas arī šajā tipā. Metodei nav nekādu ierobežojumu norādītajam tipam, bet, ja norādāt atsauces tipu, tā atgriežRādītāja baitu skaits"(IntPtr.Size)。
4. Vai to var aprēķināt, pamatojoties uz lauka dalībnieka veidu?
Mēs zinām, ka gan vērtību, gan atsauces veidi tiek kartēti kā nepārtraukts fragments (vai glabāti tieši reģistrā). Tipa mērķis ir norādīt objekta atmiņas izkārtojumu, un tā paša tipa instancēm ir vienāds izkārtojums un baitu skaits, protams, ir vienāds (atsauces tipa laukiem šajā baitu secībā tiek saglabāta tikai atsauces adrese). Tā kā baitu garums tiek noteikts pēc tipa, ja mēs varam noteikt katra lauka dalībnieka tipu, vai mēs nevarētu aprēķināt šim tipam atbilstošo baitu skaitu? Patiesībā tas nav iespējams.
Piemēram, mēs zinām, ka baitu, īsu, int un garu baitu skaits ir 1, 2, 4 un 8, tāpēc baitu bināra baitu skaits ir 2, bet tipa kombinācijai baits + īss, baits + int un baits + garš atbilstošie baiti nav 3, 5 un 9, bet 3, 8 un 16. Jo tas ietver atmiņas izlīdzināšanas jautājumu.
5. Vērtību veidu un atsauces veidu izkārtojums
Arī baitu skaits, ko aizņem atsauces tipa un apakštipa gadījumi, atšķiras tieši vienam un tam pašam datu elementam. Kā parādīts nākamajā attēlā, vērtības tipa instances baitu secībaVisi ir lauka locekļi, ko izmanto, lai to glabātu。 Atsauces tipu gadījumos tipam atbilstošās metodes tabulas adrese tiek saglabāta arī lauka baitu secības priekšā. Metodes tabulā ir gandrīz visi metadati, kas apraksta tipu, un mēs izmantojam šo atsauci, lai noteiktu, kuram tipam pieder instance. Pašā priekšpusē ir arī papildu baiti, kurus mēs sauksimObjekta galveneTo izmanto ne tikai, lai saglabātu objekta bloķēto stāvokli, bet arī jaucējvērtību var saglabāt kešatmiņā. Izveidojot atsauces tipa mainīgo, šis mainīgaisTas nenorāda uz pirmo atmiņas baitu, ko aizņem instance, bet uz vietu, kur tiek saglabāta metodes tabulas adrese。
6. LDFLDA direktīva
Kā mēs esam ieviesuši iepriekš, operatora sizeof un SizeOf metode, ko nodrošina statiskais tips Marshal/Unsafe, nevar īsti atrisināt gadījumu aizņemtā baitu garuma aprēķinu. Cik es zinu, šo problēmu nevar atrisināt tikai C# laukā, bet tas tiek nodrošināts IL līmenīLdfldaInstrukcijas var palīdzēt mums atrisināt šo problēmu. Kā norāda nosaukums, Ldflda apzīmē ielādes lauka adresi, kas palīdz mums iegūt lauka adresi instancē. Tā kā šai IL instrukcijai nav atbilstošas API C#, mēs to varam izmantot tikai šādā formā, izmantojot IL Emit.
Kā parādīts iepriekš minētajā koda fragmentā, mums ir GenerateFieldAddressAccessor metode SizeCalculator tipā, kas ģenerē Func<object?, long[]> tipa pārstāvi, pamatojoties uz norādītā tipa lauku sarakstu, kas palīdz mums atgriezt norādītā objekta atmiņas adresi un visus tā laukus. Ar paša objekta adresi un katra lauka adresi mēs, protams, varam iegūt katra lauka nobīdi un pēc tam viegli aprēķināt atmiņas baitu skaitu, ko aizņem visa instance.
7. Aprēķiniet vērtības tipa baitu skaitu
Tā kā vērtību tipiem un atsauces tipiem atmiņā ir atšķirīgi izkārtojumi, mums ir jāizmanto arī dažādi aprēķini. Tā kā struktūras baits ir visu atmiņas lauku saturs, mēs izmantojam gudru veidu, kā to aprēķināt. Pieņemsim, ka mums ir jānokārto T tipa struktūras baitu skaits, tad mēs izveidojam ValueTuple<T,T> tuple, un tā otrā lauka Item2 nobīde ir struktūras T baitu skaits. Konkrētā aprēķina metode ir atspoguļota šajā CalculateValueTypeInstance metodē.
Kā parādīts iepriekš minētajā koda fragmentā, pieņemot, ka strukturālais tips, kas mums jāaprēķina, ir T, mēs izsaucam metodi GetDefaultAsObject, lai iegūtu default(T) objektu atspoguļojuma formā, un pēc tam izveidojam ValueTuple<T,T>tuple. Pēc metodes GenerateFieldAddressAccessor izsaukšanas, lai iegūtu Func<object?, long[]> pārstāvi instances un tās lauku adrešu aprēķināšanai, mēs izsaucam šo pārstāvi kā argumentu. Trim iegūtajām atmiņas adresēm koda tupelis un 1. un 2. lauku adreses ir vienādas, mēs izmantojam trešo adresi, kas pārstāv Item2 mīnus pirmo adresi, un mēs iegūstam vēlamo rezultātu.
8. Saskaitiet citāta tipa baitu skaitu
Baitu aprēķins atsauces tipiem ir sarežģītāks, izmantojot šo ideju: pēc tam, kad esam ieguvuši pašas instances adresi un katru lauku, mēs kārtojam adreses, lai iegūtu pēdējā lauka nobīdi. Pievienosim šo nobīdi paša pēdējā lauka baitu skaitam un pēc tam vēlamajam rezultātam pievienosim nepieciešamos "pirmos un pēdējos baitus", kas atspoguļojas šajā CalculateReferneceTypeInstance metodē.
Kā parādīts iepriekš minētajā koda fragmentā, ja norādītajam tipam nav definēts neviens lauks, funkcija CalculateReferneceTypeInstance atgriež atsauces tipa instances minimālo baitu skaitu: 3 reizes lielāks par adreses rādītāja baitu skaitu. x86 arhitektūrām lietojumprogrammas tipa objekts aizņem vismaz 12 baitus, ieskaitot ObjectHeader (4 baiti), metožu tabulas rādītājus (baitus) un vismaz 4 baitus lauka satura (šie 4 baiti ir nepieciešami pat tad, ja neviens tips nav definēts bez laukiem). x64 arhitektūras gadījumā šis minimālais baitu skaits būs 24, jo metodes tabulas rādītājs un minimālais lauka saturs kļūs par 8 baitiem, lai gan derīgais ObjectHeader saturs aizņem tikai 4 baitus, bet priekšā tiks pievienoti 4 baiti polsterējuma.
Pēdējā lauka aizņemto baitu norēķini ir arī ļoti vienkārši: ja tips ir vērtības tips, tad iepriekš definētā CalculateValueTypeInstance metode tiek izsaukta, lai aprēķinātu, ja tas ir atsauces tips, laukā saglabātais saturs ir tikai mērķa objekta atmiņas adrese, tāpēc garums ir IntPtr.Size. Tā kā atsauces tipa gadījumi pēc noklusējuma ir saskaņoti ar IntPtr.Size atmiņā, tas tiek darīts arī šeit. Visbeidzot, neaizmirstiet, ka atsauces tipa instances atsauce nenorāda uz pirmo atmiņas baitu, bet gan uz baitu, kurā tiek glabāts metodes tabulas rādītājs, tāpēc jums ir jāpievieno ObjecthHeader (IntPtr.Size) baitu skaits.
9. Pilnīgs aprēķins
Divas metodes, ko izmanto, lai aprēķinātu vērtību tipa un atsauces tipa gadījumu baitu skaitu, tiek izmantotas šajā SizeOf metodē. Tā kā Ldflda instrukcijas izsaukumam ir jānodrošina atbilstoša instance, šī metode nodrošina delegātu, lai iegūtu atbilstošo instanci papildus mērķa tipa nodrošināšanai. Parametrus, kas atbilst šim pārstāvim, var noklusēt, un mēs izmantosim vērtības tipa noklusējuma vērtību. Atsauces tipiem mēs arī mēģināsim izveidot mērķa objektu, izmantojot noklusējuma konstruktoru. Ja šis pārstāvja objekts netiek nodrošināts un mērķa instanci nevar izveidot, metode SizeOf rada izņēmumu. Lai gan mums ir jānorāda mērķa instance, aprēķinātais rezultāts ir saistīts tikai ar tipu, tāpēc mēs kešatmiņā saglabājam aprēķināto rezultātu. Lai atvieglotu zvanīšanu, mēs piedāvājam arī citu vispārīgu SizeOf <T>metodi.
Zemāk esošajā koda fragmentā mēs to izmantojam, lai izvadītu divu struktūru un tipu baitu skaitu ar vienu un to pašu lauka definīciju. Nākamajā rakstā mēs tālāk iegūsim pilnu instances bināro saturu atmiņā, pamatojoties uz aprēķināto baitu skaitu, tāpēc sekojiet līdzi.
Oriģinālā saite:Hipersaites pieteikšanās ir redzama. |