Ez a cikk egy tükör gépi fordítás, kérjük, kattintson ide, hogy ugorjon az eredeti cikkre.

Nézet: 537|Válasz: 1

[Forrás] [Fordulás]. Hogyan számolja ki a NET/C#, mennyi memóriát foglal el egy példány?

[Linket másol]
Közzétéve: 2025-8-27 09:00:22 | | | |
Mindannyian tudjuk, hogy a CPU és a memória a két legfontosabb mérőszám egy programban, szóval hányan gondolkodtak már ezen a kérdésen: Hány bájtot foglal el egy példány egy típus (értéktípus vagy referenciatípus) a memóriában? Sokan közülünk nem tudnak válaszolni. A C# néhány operátort és API-t kínál a méretszámításhoz, de egyik sem oldja meg teljesen azt a problémát, amit most kérdeztem. Ez a cikk egy módszert ad arra, hogy kiszámítsuk az érték- és referenciatípusok példányainak memória bájtjai számát. A forráskód innen letölthető.

1. Operátor mérete
2. Marsall. A módszer mérete
3. Veszélyes.A módszer mérete >
4. Kiszámítható a mezőtag típusa alapján?
5. Az értéktípusok és alkalmazástípusok elrendezése
6. LDFLDA irányelv
7. Számold ki az értéktípus bájtainak számát
8. Számoljuk meg a hivatkozási típusú bájtok számát
9. Teljes számítás

1. Operátor mérete

A sizeof művelet meghatározására szolgál, hogy egy típus példánya hány bájtot foglal el, de csak kezeletlen típusokra alkalmazható. Az úgynevezett kezeletlen típus a következőkre korlátozódik:

Primitív típusok: Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double és Single)
Tizedes típus
Felsorolási típus
Mutató típus
Olyan szerkezetek, amelyek csak Unmanaged típusú adattagokat tartalmaznak
Ahogy a neve is mutatja, egy nem menedzselt típus értéktípus, és a megfelelő példány nem tartalmazhat hivatkozást a kezelt objektumra. Ha egy ilyen általános metódust definiálunk a sizeof operátor hívására, akkor a generikus T paraméternek hozzá kell adnia egy kezeletlen korlátozást és egy veszélyes címkét a metódushoz.

Csak natív és enum típusok használhatják közvetlenül a sizeof-operátort, amit más típusokra (mutatókra és egyedi szerkezetekre) alkalmazva hozzá kell adni./unsafefordítási címkék, és be kell helyezniBiztonságoskontextusban.

Mivel a következő szerkezet, Foobar nem kezeletlen típus, a program fordítási hibát mutat.

2. Marsall. A módszer mérete

Statikus típusok Marshal egy sor API-t határoz meg, amelyek segítenek kezeletlen memóriák elosztásában és másolásában, átalakítani a kezelt és nem menedzselt típusok között, valamint további műveletek végrehajtását a kezeletlen memórián (a Marshal a számítástudományban a memóriaobjektumok megfelelő formátumba történő átalakítását jelenti adattároláshoz vagy átvitelhez). Static, amely tartalmazza a következő 4 SizeOf metódus túlterhelését, hogy meghatározzák egy adott típus vagy objektum bájtainak számát.

A Marshal.SizeOf metódus nem korlátoz a megadott típusra az Unmanaged típusra, de mégis megköveteli a megadott típustÉrtéktípus。 Ha a bejövő objektum objektum, akkor annak is egy értéktípus dobozának kell lennie.

Mivel a következő Foobar a következőképpen definiáljuk:fajta, így mindkét SizeOf metódus hívása ArgumentException kivételt és promptot eredményez: A 'Foobar' típus nem szervezhető kezeletlen struktúráként; nem lehet jelentős méretet vagy eloszlást kiszámítani.

Marshal.A módszer méreteAz általános típusok nem támogatottak, de követelményeket is tartalmaznak a szerkezet elrendezésére vonatkozóan, ami támogatja a támogatástEgymást követőésVilágosElrendezés mód. Mivel az alább bemutatott Foobar szerkezet az Auto layout módot alkalmazza (Auto, amely nem támogatja a memória elrendezés "dinamikus tervezését" mezőtagok alapján, mivel a menedzselt nem kezelt környezetekben szigorúbb memóriaelrendezési követelmények vonatkoznak), a SizeOf metódus hívásai továbbra is ugyanazt az ArgumentException kivételt adják elő, mint fent.

3. Veszélyes.A módszer mérete

A Static Unsafe több alacsony szintű műveletet biztosít a kezeletlen memóriákhoz, és hasonló SizeIOf módszerek is definiálhatók ebben a típusban. A metódusnak nincs korlátozása a megadott típusra, de ha megadsz egy referenciatípust, az aA mutató bájtok száma"(IntPtr.Size)。

4. Kiszámítható a mezőtag típusa alapján?

Tudjuk, hogy mind az érték, mind a referencia típusok folyamatos töredékként leképezhetők (vagy közvetlenül egy regiszterben tárolva). A típus célja az objektum memóriaelrendezésének meghatározása, és ugyanilyen típusú példányok azonos elrendezésűek, és a bájtok száma természetesen ugyanaz (referencia típusú mezők esetén csak a hivatkozott címet tárolja ebben a bájtsorozatban). Mivel a bájthosszt a típus határozza meg, ha meg tudjuk határozni az egyes mezőtagok típusát, nem tudnánk kiszámítani az adott típusnak megfelelő bájtok számát? Valójában ez nem lehetséges.

Például tudjuk, hogy a bájt, rövid, int és hosszú bájtjai 1, 2, 4 és 8, így a bájtbináris bájtok száma 2, de egy bájt + rövid, bájt + int és bájt + hosszú típuskombinációja esetén a megfelelő bájtok nem 3, 5 és 9, hanem 3, 8 és 16. Mert ez a memória igazításának kérdését érinti.

5. Az értéktípusok és referenciatípusok elrendezése

A referencia típus és altípus példányainak elfoglalt bájtszáma is eltér ugyanazon adattagnál. Ahogy a következő képen látható, az értéktípus példányának bájtsoraMind olyan mezőtagok, amelyeket a tárolására használnak。 Referenciatípusok esetén a megfelelő metódustábla címe is a mező bájtsorozata előtt van tárolva. A metódustábla szinte az összes metaadatot tartalmazza, amely leírja a típust, és ezt a hivatkozást használjuk annak meghatározására, hogy melyik típushoz tartozik az instance. A legelején extra bájtok is vannak, amelyeket nevezünkObjektum fejlécNemcsak az objektum zárolt állapotának tárolására használják, hanem a hash érték is gyorsatárázható itt. Amikor referencia típusú változót hozunk létre, ez a változóNem az instance által elfoglalt első bájtra mutat, hanem arra a helyre, ahol a metódustábla címe tárolódik



6. LDFLDA irányelv

Ahogy fentebb bemutattuk, a statikus Marshal/Unsafe típusú operátor méret és a SizeOf módszer nem igazán nem oldhatja meg az instance-ok által elfoglalt bájthossz számítását. Tudomásom szerint ezt a problémát nem lehet egyedül C# mezőben megoldani, de az IL szinten van elérhetőLdfldaAz utasítások segíthetnek megoldani ezt a problémát. Ahogy a neve is mutatja, az Ldflda a Load Field Address (Load Field Address) rövidítése, amely segít megszerezni egy mező címét az instance-ban. Mivel ennek az IL utasításnak nincs megfelelő API-ja C#-ban, csak az alábbi formában használhatjuk IL Emit-mel.

Ahogy a fenti kódrészletben látható, a SizeCalculator típusban van egy GenerateFieldAddressAccessor metódus, amely egy Func<object?, long[]> típusú delegált generál a megadott típusú mezők listája alapján, ami segít visszaadni a megadott objektum memóriacímét és minden mezőjét. Az objektum címével és minden mező címével természetesen megkaphatjuk az egyes mezők eltolódását, majd könnyen kiszámíthatjuk, hány bájt memóriát foglal el az egész példány.

7. Számold ki az értéktípus bájtainak számát

Mivel az értéktípusok és referenciatípusok memóriája eltérő elrendezésű, különböző számításokat is kell alkalmazni. Mivel a szerkezet bájtja az összes memóriában lévő mező tartalma, okos módszert használunk a számításra. Tegyük fel, hogy ki kell számolnunk egy T típusú szerkezet bájtainak számát, akkor létrehozunk egy ValueTuple<T,T> tuple-t, és a második mező, az Item 2 eltolása a struct T bájtainak száma. A konkrét számítási módszert a következő CalculateValueTypeInstance módszer is tükrözi.

Ahogy a fenti kódrészletben is látható, feltételezve, hogy a kiszámítandó szerkezettípus T, hívjuk a GetDefaultAsObject metódust, hogy az alapértelmezett (T) objektumot tükröződés formájában kapjuk, majd létrehozunk egy ValueTuple<T,T>tuple-t. Miután a GenerateFieldAddressAccessor metódumust hívtuk meg, hogy megkapjuk a Func<objektum?, hosszú[]> delegáltát az instance és a mezőcímek kiszámítására, ezt a delegált argumentumként hívjuk. A három memóriacím esetén a kód tuple és az 1-es és 2-es mezők címei ugyanazok, a harmadik címet használjuk, amely az Item 2-t mínusz az első címet képviseli, és megkapjuk a kívánt eredményt.

8. Számoljuk meg a hivatkozási típusú bájtok számát

A hivatkozási típusok bájt-számítása bonyolultabb, ezt az ötletet alkalmazva: miután megkaptuk a példány címét és minden mezőt, rendezzük a címeket, hogy az utolsó mező eltolódását kapjuk. Adjuk hozzá ezt az eltolást az utolsó mező bájtszámához, majd adjuk hozzá a szükséges "első és utolsó bájtokat" az eredményhez, amit a következő CalculateReferneceTypeInstance módszer is tükröz.

Ahogy a fenti kódrészletben látható, ha a megadott típusnak nincs definiálva mezője, a CalculateReferneceTypeInstance a referencia típus példányának minimális bájtszámát adja vissza: háromszorosa a címmutató bájtok számának. x86 architektúrák esetén egy alkalmazástípus objektum legalább 12 bájtot foglal el, beleértve az ObjectHeadert (4 bájt), metódustábla mutatókat (bájtokat), valamint legalább 4 bájt mezőtartalmat (ez a 4 bájt akkor is, ha nincs definiált típus mező nélkül). Az x64 architektúra esetében ez a minimális bájtszám 24 lesz, mivel a metódustábla mutatója és a minimális mezőtartalom 8 bájtos lesz, bár az Objektumfejléc érvényes tartalma csak 4 bájtot foglal el, de az elején 4 bájt töltőt adnak hozzá.

Az utolsó mező által elfoglalt bájtok elhelyezése szintén nagyon egyszerű: ha a típus értéktípus, akkor a korábban definiált CalculateValueTypeInstance metódus meghívja a számításra, ha referenciatípus, akkor a mezőben tárolt tartalom csak a célobjektum memóriacíme, így a hossz IntPtr.Size. Mivel a referencia típusú példányok alapértelmezetten az IntPtr.Size memóriával vannak igazítva, itt is megtörténik. Végül ne feledd, hogy a referenciatípus példányának hivatkozása nem az első bájtra mutat, hanem arra a bájtra, amely tárolja a metódustábla mutatót, ezért hozzá kell adnod az ObjecthHeader (IntPtr.Size) bájtszámát.

9. Teljes számítás

A két módszert, amelyet az érték típus és referencia típus egyes bájtainak számának kiszámítására használnak, a következő SizeOf metódban található. Mivel az Ldflda utasítás hívásának megfelelő példányt kell biztosítania, ez a módszer egy delegált lehetőséget ad a megfelelő példány megszerzésére a céltípus megadása mellett. A delegálthoz tartozó paraméterek alapértelmezetté állíthatódunk, és az alapértelmezett értéket használjuk az értéktípushoz. Referenciatípusokhoz megpróbáljuk létrehozni a célobjektumot az alapértelmezett konstruktor segítségével. Ha ez a delegált objektum nincs megadva, és a célpéldány nem hozható létre, a SizeOf metódus kivételt ad ki. Bár meg kell adnunk a célpéldányt, a kiszámított eredmény csak a típushoz kapcsolódik, ezért gyorsítótárba gyűjtjük a kiszámított eredményt. A hívás megkönnyítése érdekében egy másik általános SizeOf <T>módszert is kínálunk.

Az alábbi kódrészletben azt használjuk, hogy két szerkezet és típus bájtainak számát adjuk ki, amelyek azonos meződefinícióval rendelkeznek. A következő cikkben további információt kapunk a instance teljes bináris tartalmáról a memóriában a kiszámított bájtszám alapján, ezért maradjatok velünk.

Eredeti link:A hiperlink bejelentkezés látható.




Előző:A front-end keretrendszer megtanulja a Component-Party nyílt forráskódú projektet
Következő:MinIO tárolás (iii) Helyi fájlok másolása, feltöltése (migrálása) a minio vödörbe
 Háziúr| Közzétéve: 2025-8-27 09:33:22 |
A C# gyűjtemény 10 000 adatdarabot helyez be, amelyek memóriát foglalnak el




Kód:


Lemondás:
A Code Farmer Network által közzétett összes szoftver, programozási anyag vagy cikk kizárólag tanulási és kutatási célokra szolgál; A fenti tartalmat nem szabad kereskedelmi vagy illegális célokra használni, különben a felhasználók viselik az összes következményet. Az oldalon található információk az internetről származnak, és a szerzői jogi vitáknak semmi köze ehhez az oldalhoz. A fenti tartalmat a letöltés után 24 órán belül teljesen törölni kell a számítógépéről. Ha tetszik a program, kérjük, támogassa a valódi szoftvert, vásároljon regisztrációt, és szerezzen jobb hiteles szolgáltatásokat. Ha bármilyen jogsértés történik, kérjük, vegye fel velünk a kapcsolatot e-mailben.

Mail To:help@itsvse.com