Denne artikkelen er en speilartikkel om maskinoversettelse, vennligst klikk her for å hoppe til originalartikkelen.

Utsikt: 537|Svare: 1

[Kilde] [Snu]. Hvordan beregner NET/C# hvor mye minne en instans opptar?

[Kopier lenke]
Publisert 27.08.2025 09:00:22 | | | |
Vi vet alle at CPU og minne er de to viktigste måleparametrene for et program, så hvor mange har egentlig tenkt på spørsmålet: Hvor mange bytes opptar en instans av en type (verditype eller referansetype) i minnet? Mange av oss kan ikke svare. C# tilbyr noen operatorer og API-er for å beregne størrelser, men ingen av dem løser helt problemet jeg nettopp spurte om. Denne artikkelen gir en metode for å beregne antall minnebyte som opptas av instanser av verdityper og referansetyper. Kildekoden lastes ned herfra.

1. størrelsen på operatøren
2. Marshal. SizeOf-metoden
3. Usikker. StørrelseOf-metoden >
4. Kan det beregnes basert på typen feltmedlem?
5. Oppsett av verdityper og applikasjonstyper
6. LDFLDA-direktivet
7. Beregn antall bytes av verditypen
8. Tell antall bytes av siteringstypen
9. Fullstendig beregning

1. størrelsen på operatøren

Sizeof-operasjonen brukes til å bestemme antall byte som opptas av en instans av en type, men den kan kun anvendes på uadministrerte typer. Den såkalte Unmanaged-typen er begrenset til:

Primitive typer: Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double og Single)
Desimaltype
Oppramsingstype
Pekertype
Strukturer som kun inneholder datamedlemmer av typen Ubehandlet
Som navnet antyder, er en Unmanaged-type en verditype, og den tilsvarende instansen kan ikke inneholde noen referanse til det administrerte objektet. Hvis vi definerer en generisk metode som denne til å kalle sizeof-operatoren, må den generiske parameteren T legge til en umananget begrensning og en unsafe-tag til metoden.

Kun native og enum-typer kan bruke sizeof-operatoren direkte, som må legges til hvis den brukes på andre typer (pekere og egendefinerte strukturer)./unsafesamletagger, og må også plasseres iusikkerI kontekst.

Siden den følgende strukturen Foobar ikke er en Unmanaged-type, vil programmet få en kompileringsfeil.

2. Marshal. SizeOf-metoden

Statiske typer Marshal definerer en serie API-er som hjelper oss å allokere og kopiere uadministrert minne, konvertere mellom administrerte og ustyrte typer, og utføre en rekke andre operasjoner på uadministrert minne (Marshal i beregningsvitenskap refererer til operasjonen med å konvertere minneobjekter til tilsvarende format for lagring eller overføring av data). Statisk, som inkluderer følgende 4 SizeOf-metode, overbelaster for å bestemme antall bytes av en gitt type eller objekt.

Marshal.SizeOf-metoden har ingen begrensning på den spesifiserte typen for Unmanaged-typen, men den krever fortsatt at en spesifisert erVerditype。 Hvis det innkommende objektet er et objekt, må det også være en boks for en verditype.

Siden følgende Foobar er definert som:snill, så kall til begge SizeOf-metodene vil kaste et ArgumentException-unntak og prompt: Type 'Foobar' kan ikke marshales som en uadministrert struktur; ingen meningsfull størrelse eller offset kan beregnes.

Marshal. SizeOf-metodenGenerika støttes ikke, men har også krav til utformingen av strukturen, som støtter støtteSekvensiellogUttrykkeligLayout-modus. Siden Foobar-strukturen vist nedenfor benytter Auto-layout-modusen (Auto, som ikke støtter "dynamisk planlegging" av minneoppsett basert på feltmedlemmer på grunn av strengere krav til minneoppsett i uadministrerte miljøer), vil kall til SizeOf-metoden fortsatt kaste det samme ArgumentException-unntaket som ovenfor.

3. Usikker.SizeOf-metoden

Static Unsafe gir flere lavnivåoperasjoner for uadministrert minne, og lignende SizeIOf-metoder er også definert i denne typen. Metoden har ingen begrensninger på den spesifiserte typen, men hvis du spesifiserer en referansetype, returnerer denAntall pekerbytes"(IntPtr.Størrelse)。

4. Kan det beregnes basert på typen feltmedlem?

Vi vet at både verdi- og referansetyper kartlegges som et kontinuerlig fragment (eller lagres direkte i et register). Formålet med en type er å spesifisere minneoppsettet til et objekt, og instanser av samme type har samme oppsett og antall bytes er naturlig det samme (for felt av referansetype lagres kun den refererte adressen i denne bytesekvensen). Siden bytelengden bestemmes av type, hvis vi kan bestemme typen til hvert feltmedlem, ville vi ikke kunne beregne antall bytes som tilsvarer den typen? Faktisk er det ikke mulig.

For eksempel vet vi at bytene til byte, short, int og long er 1, 2, 4 og 8, så antall bytes for en byte-binær er 2, men for en typekombinasjon av byte + short, byte + int, og byte + long, er de tilsvarende bytene ikke 3, 5 og 9, men 3, 8 og 16. Fordi dette involverer spørsmålet om hukommelsesjustering.

5. Oppsett av verdityper og referansetyper

Antallet bytes som opptas av instanser av referansetypen og undertypen er også forskjellig for nøyaktig samme datamedlem. Som vist i bildet nedenfor, er bytesekvensen til verditypeinstansenAlle er feltmedlemmer som brukes til å lagre den。 For instanser av referansetyper lagres adressen til tabellen for tilsvarende type også foran feltbytesekvensen. Metodetabellen gir nesten all metadata som beskriver typen, og vi bruker denne referansen for å avgjøre hvilken type instansen tilhører. Helt foran er det også ekstra bytes, som vi vil kalleObjektoverskriftDen brukes ikke bare til å lagre objektets låste tilstand, men hashverdien kan også caches her. Når vi lager en referansetypevariabel, er denne variabelenDen peker ikke på den første byte minne som opptas av instansen, men på stedet hvor adresseadressen til metodetabellen er lagret



6. LDFLDA-direktivet

Som vi har introdusert ovenfor, kan sizeof-operatoren og SizeOf-metoden gitt av den statiske typen Marshal/Unsafe egentlig ikke løse beregningen av bytelengden som opptas av instanser. Så vidt jeg vet kan ikke dette problemet løses kun i C#-feltet, men det tilbys på IL-nivåLdfldaInstruksjoner kan hjelpe oss å løse dette problemet. Som navnet antyder, står Ldflda for Load Field Address, som hjelper oss å få adressen til et felt i instansen. Siden denne IL-instruksjonen ikke har noe tilsvarende API i C#, kan vi kun bruke den i følgende form ved bruk av IL Emit.

Som vist i kodeutsnittet ovenfor, har vi en GenerateFieldAddressAccessor-metode i typen SizeCalculator, som genererer en delegat av typen Func<object?, long[]> basert på listen over felt av den spesifiserte typen, som hjelper oss å returnere minneadressen til det angitte objektet og alle dets felt. Med adressen til objektet selv og adressen til hvert felt kan vi naturlig få offset for hvert felt, og deretter enkelt beregne antall bytes minne som hele instansen opptar.

7. Beregn antall bytes av verditypen

Siden verdityper og referansetyper har forskjellige oppsett i minnet, må vi også bruke forskjellige beregninger. Siden byten til strukturen er innholdet i alle feltene i minnet, bruker vi en smart måte å beregne den på. Anta at vi må fastsette antall bytes for en struct av typen T, så lager vi en ValueTuple<T,T> tuple, og offsettet til det andre feltet Item2 er antall bytes av struct T. Den spesifikke beregningsmetoden gjenspeiles i følgende CalculateValueTypeInstance-metode.

Som vist i kodeutdraget ovenfor, forutsatt at strukturtypen vi må beregne er T, kaller vi GetDefaultAsObject-metoden for å hente default(T)-objektet i form av en refleksjon, og lager deretter en ValueTuple<T,T>tuple. Etter å ha kalt GenerateFieldAddressAccessor-metoden for å hente Func<object?, long[]> delegat for å beregne instansen og dens feltadresser, kaller vi denne delegaten som et argument. For de tre minneadressene vi får, kodetuplen og adressene til felt 1 og 2 er de samme, vi bruker den tredje adressen som representerer Element2 minus den første adressen, og vi får det resultatet vi ønsker.

8. Tell antall bytes av siteringstypen

Byteberegning for referansetyper er mer komplisert, med denne ideen: etter at vi har fått adressen til selve instansen og hvert felt, sorterer vi adressene for å få offset til det siste feltet. La oss legge denne offset til antall bytes i det siste feltet selv, og deretter legge til de nødvendige "første og siste bytene" til ønsket resultat, som reflekteres i følgende CalculateReferneceTypeInstance-metode.

Som vist i kodesnippeten ovenfor, hvis den angitte typen ikke har noen definerte felt, returnerer CalculateReferneceTypeInstance minimum antall bytes for referansetypeinstansen: 3 ganger antall adressepekerbytes. For x86-arkitekturer opptar et applikasjonstypeobjekt minst 12 byte, inkludert ObjectHeader (4 byte), metodetabellpekere (bytes) og minst 4 byte feltinnhold (disse 4 bytene kreves selv om ingen type er definert uten felt). I tilfellet x64-arkitektur vil dette minimumsantallet bytes være 24, fordi metodetabellpekeren og minimum feltinnhold vil bli 8 byte, selv om det gyldige innholdet i ObjectHeader bare opptar 4 byte, men 4 byte fyllstoff vil bli lagt til foran.

Oppgjøret av bytes som er opptatt av det siste feltet er også veldig enkelt: hvis typen er en verditype, kalles CalculateValueTypeInstance-metoden definert tidligere for å beregne, hvis det er en referansetype, er innholdet lagret i feltet kun minneadressen til målobjektet, så lengden er IntPtr.Size. Siden referansetypeinstanser er justert med IntPtr.Size i minnet som standard, gjøres dette også her. Til slutt, ikke glem at referansetypen ikke peker på den første minnebyten, men på byten som lagrer metodetabellpekeren, så du må legge til antall bytes i ObjecthHeader (IntPtr.Size).

9. Fullstendig beregning

De to metodene som brukes for å beregne antall bytes av verditype- og referansetypeinstanser brukes i følgende SizeOf-metode. Siden kallet til Ldflda-instruksjonen må gi en tilsvarende instans, gir denne metoden en delegat til å hente den tilsvarende instansen i tillegg til å gi måltypen. Parameterne som tilsvarer denne delegaten kan standardiseres, og vi vil bruke standardverdien for verditypen. For referansetyper vil vi også prøve å lage målobjektet ved å bruke standardkonstruktøren. Hvis dette delegatobjektet ikke er gitt og målinstansen ikke kan opprettes, kaster SizeOf-metoden et unntak. Selv om vi må oppgi målinstansen, er det beregnede resultatet kun relatert til typen, så vi cacher det beregnede resultatet. For enkelhets skyld tilbyr vi også en annen generell <T>SizeOf-metode.

I kodeutsnittet nedenfor bruker vi det til å sende ut antall bytes for to strukturer og typer med samme feltdefinisjon. I neste artikkel skal vi få hele binære innholdet i instansen i minnet basert på det beregnede antallet bytes, så følg med.

Original lenke:Innloggingen med hyperkoblingen er synlig.




Foregående:Front-end-rammeverket lærer Component-Party open source-prosjektet
Neste:MinIO-lagring (iii) Kopier-opplasting (migrer) lokale filer til minio-bøtten
 Vert| Publisert 27.08.2025 09:33:22 |
C#-samlingen setter inn 10 000 databiter, som opptar minnet




Kode:


Ansvarsfraskrivelse:
All programvare, programmeringsmateriell eller artikler publisert av Code Farmer Network er kun for lærings- og forskningsformål; Innholdet ovenfor skal ikke brukes til kommersielle eller ulovlige formål, ellers skal brukerne bære alle konsekvenser. Informasjonen på dette nettstedet kommer fra Internett, og opphavsrettstvister har ingenting med dette nettstedet å gjøre. Du må fullstendig slette innholdet ovenfor fra datamaskinen din innen 24 timer etter nedlasting. Hvis du liker programmet, vennligst støtt ekte programvare, kjøp registrering, og få bedre ekte tjenester. Hvis det foreligger noen krenkelse, vennligst kontakt oss på e-post.

Mail To:help@itsvse.com