Artikel ini adalah artikel cermin dari terjemahan mesin, silakan klik di sini untuk melompat ke artikel aslinya.

Melihat: 537|Jawab: 1

[Sumber] [Berputar]. Bagaimana cara NET/C# menghitung berapa banyak memori yang ditempati sebuah instans?

[Salin tautan]
Dipaparkan pada 2025-8-27 09:00:22 | | | |
Kita semua tahu bahwa CPU dan memori adalah dua metrik terpenting untuk suatu program, jadi berapa banyak orang yang benar-benar memikirkan pertanyaan: Berapa banyak byte yang ditempati oleh instance dari suatu jenis (tipe nilai atau tipe referensi) dalam memori? Banyak dari kita tidak bisa menjawab. C# menyediakan beberapa operator dan API untuk menghitung ukuran, tetapi tidak ada yang sepenuhnya menyelesaikan masalah yang baru saja saya tanyakan. Artikel ini menyediakan metode untuk menghitung jumlah byte memori yang ditempati oleh instans jenis nilai dan jenis referensi. Kode sumber diunduh dari sini.

1. Ukuran operator
2. Metode Marshal.SizeOf
3. Metode Unsafe.SizeOf >
4. Apakah dapat dihitung berdasarkan jenis anggota lapangan?
5. Tata letak jenis nilai dan jenis aplikasi
6. Arahan LDFLDA
7. Hitung jumlah byte dari jenis nilai
8. Hitung jumlah byte dari jenis kutipan
9. Perhitungan lengkap

1. Ukuran operator

Operasi sizeof digunakan untuk menentukan jumlah byte yang ditempati oleh instans jenis, tetapi hanya dapat diterapkan ke jenis yang tidak dikelola. Yang disebut jenis Tidak Dikelola terbatas pada:

Jenis Primitif: Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, dan Single)
Tipe desimal
Jenis pencacahan
Jenis penunjuk
Struktur yang hanya berisi anggota data jenis Unmanaged
Seperti namanya, jenis Tidak dikelola adalah jenis nilai, dan instans yang sesuai tidak dapat berisi referensi apa pun ke objek terkelola. Jika kita mendefinisikan metode generik seperti ini untuk memanggil operator sizeof, parameter generik T harus menambahkan batasan yang tidak dikelola dan tag yang tidak aman ke metode.

Hanya jenis native dan enum yang dapat menggunakan operator sizeof secara langsung, yang harus ditambahkan jika diterapkan ke jenis lain (pointer dan struktur kustom)./unsafekompilasi, dan juga perlu ditempatkan ditidak amandalam konteks.

Karena struct Foobar berikut bukan jenis Tidak Dikelola, program akan mengalami kesalahan kompilasi.

2. Metode Marshal.SizeOf

Jenis statis Marshal mendefinisikan serangkaian API yang membantu kami mengalokasikan dan menyalin memori yang tidak dikelola, mengonversi antara jenis terkelola dan tidak terkelola, dan melakukan serangkaian operasi lain pada memori yang tidak dikelola (Marshal dalam ilmu komputasi mengacu pada operasi mengonversi objek memori ke dalam format yang sesuai untuk penyimpanan atau transfer data). Statis, yang mencakup 4 kelebihan beban metode SizeOf berikut untuk menentukan jumlah byte dari jenis atau objek tertentu.

Metode Marshal.SizeOf tidak memiliki batasan pada jenis yang ditentukan untuk jenis Tidak Dikelola, tetapi masih memerlukan satu untuk ditentukanJenis nilai。 Jika objek yang masuk adalah objek, itu juga harus berupa kotak untuk jenis nilai.

Karena Foobar berikut didefinisikan sebagai:jenis, jadi panggilan ke kedua metode SizeOf akan melemparkan pengecualian dan prompt ArgumentException: Type 'Foobar' tidak dapat disusun sebagai struktur yang tidak dikelola; Tidak ada ukuran atau offset yang berarti yang dapat dihitung.

Metode Marshal.SizeOfGenerik tidak didukung, tetapi juga memiliki persyaratan untuk tata letak struktur, yang mendukung dukunganBerurutandanEksplisitMode tata letak. Karena struct Foobar yang ditunjukkan di bawah ini mengadopsi mode tata letak Otomatis (Otomatis, yang tidak mendukung "perencanaan dinamis" tata letak memori berdasarkan anggota bidang karena persyaratan tata letak memori yang lebih ketat di lingkungan yang tidak dikelola), panggilan ke metode SizeOf masih akan melemparkan pengecualian ArgumentException yang sama seperti di atas.

3. Metode Unsafe.SizeOf

Static Unsafe menyediakan lebih banyak operasi tingkat rendah untuk memori yang tidak dikelola, dan metode SizeIOf serupa juga didefinisikan dalam jenis ini. Metode ini tidak memiliki batasan apa pun pada jenis yang ditentukan, tetapi jika Anda menentukan jenis referensi, metode tersebut akan mengembalikanJumlah byte penunjuk"(IntPtr.Ukuran)。

4. Apakah dapat dihitung berdasarkan jenis anggota lapangan?

Kita tahu bahwa jenis nilai dan referensi dipetakan sebagai fragmen kontinu (atau disimpan langsung dalam register). Tujuan dari jenis adalah untuk menentukan tata letak memori objek, dan instans dari jenis yang sama memiliki tata letak yang sama dan jumlah byte secara alami sama (untuk bidang jenis referensi, ini hanya menyimpan alamat yang direferensikan dalam urutan byte ini). Karena panjang byte ditentukan oleh jenis, jika kita dapat menentukan jenis setiap anggota bidang, bukankah kita dapat menghitung jumlah byte yang sesuai dengan jenis itu? Faktanya, itu tidak mungkin.

Misalnya, kita tahu bahwa byte byte, short, int, dan long adalah 1, 2, 4, dan 8, jadi jumlah byte untuk biner byte adalah 2, tetapi untuk kombinasi jenis byte + short, byte + int, dan byte + long, byte yang sesuai bukan 3, 5, dan 9, tetapi 3, 8, dan 16. Karena ini melibatkan masalah penyelarasan memori.

5. Tata letak jenis nilai dan jenis referensi

Jumlah byte yang ditempati oleh instans jenis referensi dan subtipe juga berbeda untuk anggota data yang sama persis. Seperti yang ditunjukkan pada gambar berikut, urutan byte dari instance jenis nilaiSemua adalah anggota bidang yang digunakan untuk menyimpannya。 Untuk instans jenis referensi, alamat tabel metode yang sesuai jenis juga disimpan di depan urutan byte bidang. Tabel metode menyediakan hampir semua metadata yang menjelaskan jenisnya, dan kami menggunakan referensi ini untuk menentukan jenis instans yang berada. Di bagian paling depan, ada juga byte tambahan, yang akan kita sebutHeader ObjekIni tidak hanya digunakan untuk menyimpan status terkunci objek, tetapi nilai hash juga dapat di-cache di sini. Saat kita membuat variabel jenis referensi, variabel iniIni tidak menunjuk ke byte pertama memori yang ditempati oleh instans, tetapi ke tempat di mana alamat tabel metode disimpan



6. Arahan LDFLDA

Seperti yang telah kami perkenalkan di atas, operator sizeof dan metode SizeOf yang disediakan oleh tipe statis Marshal/Unsafe tidak dapat benar-benar menyelesaikan perhitungan panjang byte yang ditempati oleh instance. Sejauh yang saya tahu, masalah ini tidak dapat diselesaikan di bidang C# saja, tetapi disediakan di tingkat ILLdfldaInstruksi dapat membantu kita memecahkan masalah ini. Seperti namanya, Ldflda adalah singkatan dari Load Field Address, yang membantu kita mendapatkan alamat bidang dalam instance tersebut. Karena instruksi IL ini tidak memiliki API yang sesuai di C#, kita hanya dapat menggunakannya dalam bentuk berikut menggunakan IL Emit.

Seperti yang ditunjukkan dalam cuplikan kode di atas, kita memiliki metode GenerateFieldAddressAccessor dalam jenis SizeCalculator, yang menghasilkan delegasi jenis Func<object?, long[]> berdasarkan daftar bidang dari jenis yang ditentukan, yang membantu kita mengembalikan alamat memori objek yang ditentukan dan semua bidangnya. Dengan alamat objek itu sendiri dan alamat setiap bidang, kita secara alami bisa mendapatkan offset dari setiap bidang, dan kemudian dengan mudah menghitung jumlah byte memori yang ditempati oleh seluruh instans.

7. Hitung jumlah byte dari jenis nilai

Karena jenis nilai dan jenis referensi memiliki tata letak yang berbeda dalam memori, kita juga perlu menggunakan perhitungan yang berbeda. Karena byte struct adalah isi dari semua bidang dalam memori, kami menggunakan cara cerdas untuk menghitungnya. Misalkan kita perlu menyelesaikan jumlah byte dari struktur tipe T, maka kita membuat tuple ValueTuple<T,T>, dan offset dari bidang keduanya Item2 adalah jumlah byte struct T. Metode perhitungan spesifik tercermin dalam metode CalculateValueTypeInstance berikut.

Seperti yang ditunjukkan dalam cuplikan kode di atas, dengan asumsi bahwa jenis struct yang perlu kita hitung adalah T, kita memanggil metode GetDefaultAsObject untuk mendapatkan objek default(T) dalam bentuk refleksi, lalu membuat ValueTuple<T,T>tuple. Setelah memanggil metode GenerateFieldAddressAccessor untuk mendapatkan delegasi Func<object?, long[]> untuk menghitung instance dan alamat bidangnya, kita memanggil delegasi ini sebagai argumen. Untuk tiga alamat memori yang kita dapatkan, tuple kode dan alamat bidang 1 dan 2 sama, kita menggunakan alamat ketiga yang mewakili Item2 dikurangi alamat pertama, dan kita mendapatkan hasil yang kita inginkan.

8. Hitung jumlah byte dari jenis kutipan

Perhitungan byte untuk jenis referensi lebih rumit, menggunakan ide ini: setelah kita mendapatkan alamat instance itu sendiri dan setiap bidang, kita mengurutkan alamat untuk mendapatkan offset dari bidang terakhir. Mari tambahkan offset ini ke jumlah byte dari bidang terakhir itu sendiri, lalu tambahkan "byte pertama dan terakhir" yang diperlukan ke hasil yang kita inginkan, yang tercermin dalam metode CalculateReferneceTypeInstance berikut.

Seperti yang ditunjukkan dalam cuplikan kode di atas, jika jenis yang ditentukan tidak memiliki bidang yang ditentukan, CalculateReferneceTypeInstance mengembalikan jumlah byte minimum dari instans jenis referensi: 3 kali jumlah byte penunjuk alamat. Untuk arsitektur x86, objek jenis aplikasi membutuhkan setidaknya 12 byte, termasuk ObjectHeader (4 byte), penunjuk tabel metode (byte), dan setidaknya 4 byte konten bidang (4 byte ini diperlukan meskipun tidak ada jenis yang ditentukan tanpa bidang apa pun). Dalam kasus arsitektur x64, jumlah minimum byte ini akan menjadi 24, karena penunjuk tabel metode dan konten bidang minimum akan menjadi 8 byte, meskipun konten yang valid dari ObjectHeader hanya menempati 4 byte, tetapi 4 byte padding akan ditambahkan di depan.

Penyelesaian byte yang ditempati oleh bidang terakhir juga sangat sederhana: jika jenisnya adalah jenis nilai, maka metode CalculateValueTypeInstance yang ditentukan sebelumnya dipanggil untuk menghitung, jika itu adalah jenis referensi, konten yang disimpan di bidang hanya alamat memori objek target, jadi panjangnya adalah IntPtr.Size. Karena instans jenis referensi diselaraskan dengan IntPtr.Size dalam memori secara default, ini juga dilakukan di sini. Terakhir, jangan lupa bahwa referensi instance jenis referensi tidak mengarah ke byte pertama memori, tetapi ke byte yang menyimpan penunjuk tabel metode, jadi Anda harus menambahkan jumlah byte ObjecthHeader (IntPtr.Size).

9. Perhitungan lengkap

Dua metode yang digunakan untuk menghitung jumlah byte jenis nilai dan instans jenis referensi digunakan dalam metode SizeOf berikut. Karena panggilan instruksi Ldflda perlu menyediakan instance yang sesuai, metode ini menyediakan delegasi untuk mendapatkan instance yang sesuai selain menyediakan jenis target. Parameter yang sesuai dengan delegasi ini dapat ditetapkan secara default, dan kita akan menggunakan nilai default untuk jenis nilai. Untuk jenis referensi, kita juga akan mencoba membuat objek target menggunakan konstruktor default. Jika objek delegasi ini tidak disediakan dan instans target tidak dapat dibuat, metode SizeOf melemparkan pengecualian. Meskipun kita perlu memberikan instance target, hasil yang dihitung hanya terkait dengan jenisnya, jadi kita menyimpan hasil yang dihitung dalam cache. Untuk kemudahan panggilan, kami juga menyediakan metode SizeOf generik lainnya<T>.

Dalam cuplikan kode di bawah ini, kita menggunakannya untuk mengeluarkan jumlah byte dari dua struktur dan jenis dengan definisi bidang yang sama. Pada artikel berikutnya, kita selanjutnya akan mendapatkan konten biner lengkap dari instance dalam memori berdasarkan jumlah byte yang dihitung, jadi pantau terus.

Tautan asli:Login hyperlink terlihat.




Mantan:Kerangka kerja front-end mempelajari proyek open source Component-Party
Depan:Penyimpanan MinIO (iii) Salin-unggah (migrasikan) file lokal ke bucket minio
 Tuan tanah| Dipaparkan pada 2025-8-27 09:33:22 |
Koleksi C# menyisipkan 10.000 potongan data, yang menempati memori




Kode:


Sanggahan:
Semua perangkat lunak, materi pemrograman, atau artikel yang diterbitkan oleh Code Farmer Network hanya untuk tujuan pembelajaran dan penelitian; Konten di atas tidak boleh digunakan untuk tujuan komersial atau ilegal, jika tidak, pengguna akan menanggung semua konsekuensi. Informasi di situs ini berasal dari Internet, dan sengketa hak cipta tidak ada hubungannya dengan situs ini. Anda harus sepenuhnya menghapus konten di atas dari komputer Anda dalam waktu 24 jam setelah pengunduhan. Jika Anda menyukai program ini, harap dukung perangkat lunak asli, pembelian pendaftaran, dan dapatkan layanan asli yang lebih baik. Jika ada pelanggaran, silakan hubungi kami melalui email.

Mail To:help@itsvse.com