Tämä artikkeli on konekäännöksen peiliartikkeli, klikkaa tästä siirtyäksesi alkuperäiseen artikkeliin.

Näkymä: 537|Vastaus: 1

[Lähde] [Käänny]. Miten NET/C# laskee, kuinka paljon muistia instanssi vie?

[Kopioi linkki]
Julkaistu 2025-8-27 09:00:22 | | | |
Me kaikki tiedämme, että CPU ja muisti ovat ohjelman kaksi tärkeintä mittaria, joten kuinka moni on todella pohtinut kysymystä: Kuinka monta tavua tyypin instanssi (arvotyyppi tai viitetyyppi) vie muistissa? Moni meistä ei osaa vastata. C# tarjoaa joitakin operaattoreita ja rajapintoja kokojen laskemiseen, mutta mikään niistä ei täysin ratkaise juuri kysymääni ongelmaa. Tässä artikkelissa esitellään menetelmä lasketaan, kuinka monta muistitavua arvotyyppien ja viitetyyppien instanssit vievät. Lähdekoodi ladataan täältä.

1. operaattorin koko
2. Marshal.SizeOf method
3. Vaarallinen.Menetelmän koko>
4. Voidaanko se laskea kenttäjäsenen tyypin perusteella?
5. Arvotyyppien ja sovellustyyppien asettelu
6. LDFLDA-direktiivi
7. Laske arvotyypin tavujen määrä
8. Laske viittaustyypin tavujen määrä
9. Täydellinen laskenta

1. operaattorin koko

Sizeof-operaatiota käytetään määrittämään, kuinka monta tavua tietyn tyypin instanssilla on käytössä, mutta sitä voidaan soveltaa vain hallittamattomiin tyyppeihin. Niin sanottu hallitsematon tyyppi rajoittuu seuraaviin piirteisiin:

Primitiivityypit: Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double ja Single)
Desimaalityyppi
Luettelointityyppi
Pointer-tyyppi
Rakenteet, jotka sisältävät vain tyypin Unmanaged datajäseniä
Kuten nimi kertoo, hallitsematon tyyppi on arvotyyppi, eikä vastaava instanssi voi sisältää mitään viittausta hallittuun objektiin. Jos määrittelemme tällaisen geneerisen metodin, joka kutsuu sizeof-operaattoriksi, geneerisen parametrin T täytyy lisätä metodiin hallitsematon rajoite ja turvaton tunniste.

Vain natiivi- ja enum-tyypit voivat käyttää sizeof-operaattoria suoraan, mikä on lisättävä, jos niitä sovelletaan muihin tyyppeihin (osoittimiin ja mukautettuihin rakenteisiin)./unsafeKokoelmatunnisteet, ja ne täytyy myös sijoittaaturvatonkontekstissa.

Koska seuraava rakenne Foobar ei ole hallitsematon tyyppi, ohjelmassa esiintyy käännösvirhe.

2. Marshal.SizeOf method

Staattiset tyypit Marshal määrittelee joukon API-rajapintoja, jotka auttavat meitä varaamaan ja kopioimaan hallitsemattoman muistin, muuntamaan hallitun ja hallitsemattoman tyypin välillä sekä suorittamaan sarjan muita operaatioita hallitsemattomalla muistilla (Marshal laskennassa tarkoittaa muistiobjektien muuntamista vastaavaan muotoon tiedon tallennusta tai siirtoa varten). Static, joka sisältää seuraavat 4 SizeOf-metodin ylikuormitusta määrittääkseen tietyn tyypin tai objektin tavujen määrän.

Marshal.SizeOf-metodilla ei ole rajoitusta määritellyn tyypin suhteen hallitsemattomalle tyypille, mutta se vaatii silti yhden määrittelyäArvotyyppi。 Jos saapuva olio on objekti, sen täytyy olla myös laatikko arvotyypille.

Koska seuraava Foobar määritellään seuraavasti:tyyppi, joten kutsut molempiin SizeOf-metodeihin heittävät ArgumentException-poikkeuksen ja kehotteen: Tyyppiä 'Foobar' ei voi järjestää hallitsemattomaksi rakenteeksi; merkittävää kokoa tai siirtymää ei voida laskea.

Marshal.SizeOf methodGeneeriset lääkkeet eivät ole tuettuja, mutta siinä on myös vaatimuksia rakenteen rakenteelle, joka tukee tukeaPeräkkäinenjaEksplisiittinenLayout-tila. Koska alla esitetty Foobar-rakenne käyttää Auto-asettelutilaa (Auto, joka ei tue "dynaamista suunnittelua" kenttäjäsenten perusteella tiukempien muistiasetteluvaatimusten vuoksi hallitsemattomissa ympäristöissä), SizeOf-metodin kutsut heittävät silti saman ArgumentException-poikkeuksen kuin yllä.

3. Epäturvallinen.SizeOf -menetelmä

Static Unsafe tarjoaa enemmän matalan tason operaatioita hallitsemattomalle muistille, ja samankaltaisia SizeIOf-menetelmiä määritellään myös tässä tyypissä. Metodilla ei ole rajoituksia määritellyn tyypin suhteen, mutta jos määrität viitetyypin, se palauttaaOsoitintavujen määrä"(IntPtr.Size)。

4. Voidaanko se laskea kenttäjäsenen tyypin perusteella?

Tiedämme, että sekä arvo- että referenssityypit kuvataan jatkuvana fragmenttina (tai tallennetaan suoraan rekisteriin). Tyypin tarkoitus on määrittää objektin muistirakenne, ja saman tyypin instansseilla on sama asettelu ja tavujen määrä on luonnollisesti sama (viitetyypin kentillä se tallentaa vain viittatun osoitteen tässä tavujonossa). Koska tavupituus määräytyy tyypin mukaan, jos voimme määrittää kunkin kentän jäsenen tyypin, eikö voisimme laskea kyseisen tyypin vastaavien tavujen määrän? Itse asiassa se ei ole mahdollista.

Esimerkiksi tiedämme, että tavut, short, int ja long tavut ovat 1, 2, 4 ja 8, joten tavubinäärin tavumäärä on 2, mutta tyypin yhdistelmässä byte + short, byte + int ja byte + long, vastaavat tavut eivät ole 3, 5 ja 9, vaan 3, 8 ja 16. Koska tämä liittyy muistin kohdistukseen.

5. Arvotyyppien ja viitetyyppien asettelu

Viitetyypin ja alatyypin instanssien täyttämien tavujen määrä eroaa myös täsmälleen samalle tietojäsenelle. Kuten seuraavassa kuvassa näkyy, arvotyypin instanssin tavujonoKaikki ovat kenttäjäseniä, joita käytetään sen säilyttämiseen。 Referenssityyppien tapauksissa vastaavan metoditaulun osoite tallennetaan myös kenttätavujonon eteen. Metoditaulu sisältää lähes kaiken metatiedot, jotka kuvaavat tyypin, ja käytämme tätä viitettä määrittääksemme, mihin tyyppiin instanssi kuuluu. Aivan edessä on myös ylimääräisiä tavuja, joita kutsummeObjektiotsikkoSitä käytetään paitsi objektin lukitun tilan tallentamiseen, myös hajautusarvo voidaan välimuistittaa tänne. Kun luomme referenssityyppimuuttujan, tämä muuttujaSe ei osoita instanssin ensimmäiseen muistitavuun, vaan kohtaan, jossa metoditaulun osoite on tallennettu



6. LDFLDA-direktiivi

Kuten yllä esittelimme, sizeof-operaattori ja staattisen Marshal/Unsafe -tyypin tarjoama SizeOf-menetelmä eivät oikeastaan ratkaise instanssien täyttämän tavun pituuden laskentaa. Käsittääkseni tätä ongelmaa ei voi ratkaista pelkästään C#-kentällä, mutta se on tarjottu IL-tasollaLdfldaOhjeet voivat auttaa meitä ratkaisemaan tämän ongelman. Kuten nimikin kertoo, Ldflda tarkoittaa Load Field Addressia, joka auttaa meitä saamaan kentän osoitteen kyseisessä instanssissa. Koska tällä IL-käskyllä ei ole vastaavaa rajapintaa C#:ssa, voimme käyttää sitä vain seuraavassa muodossa IL Emitin avulla.

Kuten yllä olevassa koodipätkässä näkyy, meillä on SizeCalculator-tyypissä GenerateFieldAddressAccessor-metodi, joka generoi valtuutetun tyypillä Func<object?, long[]> määriteltyjen kenttien listan perusteella, mikä auttaa palauttamaan määritellyn objektin muistiosoitteen ja kaikki sen kentät. Objektin osoitteen ja kunkin kentän osoitteen avulla voimme luonnollisesti saada kunkin kentän offsetin ja laskea helposti koko instanssin muistin tavujen määrän.

7. Laske arvotyypin tavujen määrä

Koska arvotyypeillä ja viitetyypeillä on erilaiset asettelut muistissa, meidän täytyy myös käyttää erilaisia laskelmia. Koska rakenteen tavu on kaikkien muistikenttien sisältö, käytämme nerokkaasti menetelmää laskea se. Oletetaan, että meidän täytyy määrittää tyypin T rakenteen tavujen määrä, jolloin luodaan ValueTuple<T,T>-tuple, ja sen toisen kentän Item2 siirtymä on rakenteen T tavujen lukumäärä. Tarkka laskentatapa näkyy seuraavassa CalculateValueTypeInstance-menetelmässä.

Kuten yllä olevassa koodipätkässä on esitetty, olettaen, että laskettava rakennetyyppi on T, kutsumme GetDefaultAsObject-metodia saadaksemme oletus(T)-objektin heijastuksena ja luomme sitten ValueTuple<T,T>tuple. Kun kutsumme GenerateFieldAddressAccessor-metodin saadaksemme Func<objekti?, pitkä[]> delegaatti instanssin ja sen kenttäosoitteiden laskemiseen, kutsumme tätä delegaattia argumentiksi. Kolmelle muistiosoitteelle, jotka saamme, koodituple ja kenttien 1 ja 2 osoitteet ovat samat, käytämme kolmatta osoitetta, joka edustaa Item2:ta miinus ensimmäinen, ja saamme halutun tuloksen.

8. Laske viittaustyypin tavujen määrä

Tavulaskenta viitetyypeille on monimutkaisempi tämän idean avulla: kun olemme saaneet instanssin ja jokaisen kentän osoitteen, lajittelemme osoitteet viimeisen kentän siirtymäksi. Lisätään tämä siirtymä viimeisen kentän tavumäärään ja lisätään sitten tarvittava "ensimmäinen ja viimeinen tavu" haluamaamme tulokseen, mikä näkyy seuraavassa CalculateReferneceTypeInstance-menetelmässä.

Kuten yllä olevassa koodipätkässä on esitetty, jos määritetyllä tyypille ei ole määriteltyjä kenttiä, CalculateReferneceTypeInstance palauttaa viitetyypin instanssin minimimäärän tavuja: 3 kertaa osoiteosoitintavujen määrä. x86-arkkitehtuureissa sovellustyyppinen objekti vie vähintään 12 tavua, mukaan lukien ObjectHeader (4 tavua), metoditaulun osoittimet (tavut) ja vähintään 4 tavua kenttäsisältöä (tämä 4 tavua vaaditaan, vaikka tyyppiä ei määritelisi ilman kenttiä). x64-arkkitehtuurissa tämä minimimäärä tavuja on 24, koska metoditaulun osoitin ja minimikenttäsisältö kasvavat 8 tavuksi, vaikka ObjectHeaderin kelvollinen sisältö vie vain 4 tavua, mutta etupuolelle lisätään 4 tavua täytettä.

Viimeisen kentän täyttämien tavujen sijoitus on myös hyvin yksinkertainen: jos tyyppi on arvotyyppi, niin aiemmin määritelty CalculateValueTypeInstance-menetelmä kutsutaan laskemaan; jos se on viitetyyppi, kenttään tallennettu sisältö on vain kohdeobjektin muistiosoite, joten pituus on IntPtr.Size. Koska viitetyypin instanssit ovat oletuksena linjassa muistin IntPtr.Size kanssa, tämä tehdään myös tässä. Lopuksi, älä unohda, että viitetyypin instanssin viittaus ei osoita muistin ensimmäiseen tavuun, vaan tavuun, joka tallentaa metoditaulun osoittimen, joten sinun täytyy lisätä ObjecthHeaderin tavumäärä (IntPtr.Size).

9. Täydellinen laskenta

Kaksi menetelmää, joilla lasketaan arvotyypin ja referenssityypin instanssien tavumäärä, käytetään seuraavassa SizeOf-metodissa. Koska Ldflda-käskyn kutsun täytyy tarjota vastaava instanssi, tämä menetelmä antaa delegaatin hankkia vastaavan instanssin kohdetyypin lisäksi. Tämän delegaatin parametrit voidaan oletuksena, ja käytämme oletusarvoa arvotyypille. Viitetyypeille yritämme myös luoda kohdeobjektin oletuskonstruktorilla. Jos tätä delegaattiobjektia ei ole annettu eikä kohde-instanssia voida luoda, SizeOf-metodi heittää poikkeuksen. Vaikka meidän täytyy antaa kohdeinstanssi, laskettu tulos liittyy vain tyyppiin, joten välimuistiin tallennetaan laskettu tulos. Kutsumisen helpottamiseksi tarjoamme myös toisen yleisen <T>SizeOf-menetelmän.

Alla olevassa koodipätkässä käytämme sitä kahden rakenteen ja tyypin tavumäärän tuottamiseen, joilla on sama kenttämääritelmä. Seuraavassa artikkelissa saamme lisää instanssin koko binäärisisällön muistissa lasketun tavumäärän perusteella, joten pysykää kuulolla.

Alkuperäinen linkki:Hyperlinkin kirjautuminen on näkyvissä.




Edellinen:Front-end-kehys oppii Component-Party -avoimen lähdekoodin projektin
Seuraava:MinIO-tallennus (iii) Kopioi-lataa (siirrä) paikalliset tiedostot minio-ämpäriin
 Vuokraisäntä| Julkaistu 2025-8-27 09:33:22 |
C#-kokoelma sisältää 10 000 tietopalaa, jotka vievät muistia




Koodi:


Vastuuvapauslauseke:
Kaikki Code Farmer Networkin julkaisemat ohjelmistot, ohjelmamateriaalit tai artikkelit ovat tarkoitettu vain oppimis- ja tutkimustarkoituksiin; Yllä mainittua sisältöä ei saa käyttää kaupallisiin tai laittomiin tarkoituksiin, muuten käyttäjät joutuvat kantamaan kaikki seuraukset. Tämän sivuston tiedot ovat peräisin internetistä, eikä tekijänoikeuskiistat liity tähän sivustoon. Sinun tulee poistaa yllä oleva sisältö kokonaan tietokoneeltasi 24 tunnin kuluessa lataamisesta. Jos pidät ohjelmasta, tue aitoa ohjelmistoa, osta rekisteröityminen ja hanki parempia aitoja palveluita. Jos rikkomuksia ilmenee, ota meihin yhteyttä sähköpostitse.

Mail To:help@itsvse.com