Bu makale makine çevirisi ayna makalesidir, orijinal makaleye geçmek için lütfen buraya tıklayın.

Görünüm: 537|Yanıt: 1

[Kaynak] [Dön]. NET/C# bir örneğin ne kadar bellek kapladığını nasıl hesaplar?

[Bağlantıyı kopyala]
2025-8-27 09:00:22 tarihinde yayınlandı | | | |
Hepimiz biliyoruz ki CPU ve bellek bir program için en önemli iki metriktir, peki kaç kişi gerçekten şu soruyu düşündü: Bir türün (değer tipi veya referans türü) bir örneği bellekte kaç bayt kaplayır? Birçoğumuz cevap veremeyiz. C#, boyutları hesaplamak için bazı operatörler ve API'ler sunuyor, ama hiçbiri az önce sorduğum sorunu tamamen çözemiyor. Bu makale, değer tipleri ve referans türlerinin örneklerinin kapladığı bellek bayt sayısını hesaplamak için bir yöntem sunmaktadır. Kaynak kodu buradan indirilebilir.

1. operatörün boyutu
2. Marshal.SizeOf yöntemi
3. Güvensiz.Yöntemin Boyutu >
4. Alan üyesinin türüne göre hesaplanabilir mi?
5. Değer türlerinin ve uygulama türlerinin düzeni
6. LDFLDA Direktifi
7. Değer tipindeki bayt sayısını hesaplayın
8. Atıf tipindeki bayt sayısını sayın
9. Tam hesaplama

1. operatörün boyutu

Sizeof işlemi, bir türün örneğinin kapladığı bayt sayısını belirlemek için kullanılır, ancak yalnızca Yönetilmeyen tiplere uygulanabilir. Sözde Yönetilmeyen tip şu şekilde sınırlıdır:

İlkel Tipler: Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double ve Single)
Ondalık tip
Sayım türü
İşaret tipi
Yalnızca Yönetilmeyen tipte veri üyelerini içeren yapılar
Adından da anlaşılacağı gibi, Yönetilmeyen bir tür değer tipidir ve ilgili örnek yönetilen nesneye herhangi bir referans içermez. Eğer sizeof operatörünü çağıracak şekilde böyle bir genel yöntem tanımlarsak, genel parametre T yönteme yönetilmeyen bir kısıtlama ve güvensiz bir etiket eklemelidir.

Yalnızca yerel ve enum tipleri sizeof' operatörünü doğrudan kullanabilir; bu operatör diğer tiplere (işaretçiler ve özel yapılar) uygulandığında eklenmelidir./unsafederleme etiketleri ve ayrıca yerleştirilmesi gerekiyorgüvensizbağlamda.

Aşağıdaki yapı Foobar yönetilmeyen bir tip olmadığından, programda derleme hatası olur.

2. Marshal.SizeOf yöntemi

Statik tipler Marshal, yönetilmeyen belleği tahsis edip kopyalamamıza, yönetilen ve yönetilmeyen türler arasında dönüştürmemize ve yönetilmeyen bellek üzerinde bir dizi başka işlem gerçekleştirmemize yardımcı olan bir dizi API tanımlar; hesaplama biliminde Marshal, bellek nesnelerinin veri depolama veya aktarımı için ilgili formata dönüştürülmesini ifade eder). Statik, aşağıdaki 4 SizeOf yönteminin aşırı yüklenmesini içerir ve belirli bir tip veya nesnenin bayt sayısını belirler.

Marshal.SizeOf yöntemi, Unmanaged tipi için belirtilen tipe kısıtlama taşımıyor, ancak yine de bir tür belirtilmesini gerektiriyorDeğer türü。 Gelen nesne bir nesne ise, aynı zamanda bir değer tipi için bir kutu olmalıdır.

Aşağıdaki Foobar şu şekilde tanımlandığından:tür, bu nedenle her iki SizeOf metoduna yapılan çağrılar ArgumentException istisnası ve şu yön gönderir: Tip 'Foobar' yönetilmeyen bir yapı olarak düzenlenemez; anlamlı bir boyut veya ofset hesaplanamaz.

Marshal.SizeOf methodJenerik ilaçlar desteklenmez, ancak aynı zamanda yapının yerleşimi için de gereksinimleri vardır ve bu da desteği desteklerSıralıveAçıkDüzen modu. Aşağıda gösterilen Foobar yapısı, Otomatik düzen modunu benimsediğinden (Auto, yönetilmeyen ortamlarda daha sıkı bellek düzeni gereksinimleri nedeniyle alan üyelerine dayalı bellek düzeninin "dinamik planlamasını" desteklemez), SizeOf yöntemine yapılan çağrılar yine yukarıdaki ArgumentException istisnasını verir.

3. Güvensiz.SizeOf yöntemi

Static Unsafe, yönetilmeyen bellek için daha fazla düşük seviyeli işlem sağlar ve benzer SizeIOf yöntemleri de bu türde tanımlanmıştır. Metodun belirtilen tipe herhangi bir kısıtlama yok, ancak bir referans tipi belirtirseniz,İşaretçi bayt sayısı"(IntPtr.Size)。

4. Alan üyesinin türüne göre hesaplanabilir mi?

Hem değer hem de referans türlerinin sürekli bir parça olarak eşlendiğini (veya doğrudan bir kayıtta depolandığını) biliyoruz. Bir türün amacı, bir nesnenin bellek düzenini belirtmektir ve aynı tipteki örnekler aynı düzene sahiptir ve bayt sayısı doğal olarak aynıdır (referans tipli alanlar için, bu bayt dizisinde yalnızca referans verilen adresi saklar). Bayt uzunluğu tipe göre belirlendiği için, her alan üyesinin türünü belirleyebilirsek, o tipe karşılık gelen bayt sayısını hesaplayamaz mıyız? Aslında, bu mümkün değil.

Örneğin, bayt, kısa, int ve uzun baytlarının 1, 2, 4 ve 8 olduğunu biliyoruz, yani bir bayt ikili için bayt sayısı 2'dir, ancak bayt + kısa, bayt + int ve bayt + uzun kombinasyonu için karşılık gelen baytlar 3, 5 ve 9 değil, 3, 8 ve 16'dır. Çünkü bu, hafıza hizalanması meselesini içeriyor.

5. Değer tiplerinin ve referans türlerinin düzeni

Referans tipi ve alt tipin örneklerinin işgal ettiği bayt sayısı da aynı veri üyesi için farklıdır. Aşağıdaki görselde gösterildiği gibi, değer tipi örneğinin bayt dizisiHepsi onu depolamak için kullanılan saha üyeleridir。 Referans tipleri örneklerinde, tip karşılık gelen metod tablosunun adresi de alan bayt dizisinin önünde saklanır. Metod tablosu, tipi tanımlayan neredeyse tüm meta verileri sağlar ve bu referansı örnekin hangi tipe ait olduğunu belirlemek için kullanırız. En önde ayrıca ekstra baytlar da var, bunlara diyeceğizNesne BaşlığıSadece nesnenin kilitli durumunu depolamak için kullanılmaz, aynı zamanda hash değeri de burada önbelleğe alınabilir. Bir referans tipi değişken oluşturduğumuzda, bu değişkenBu, örneğin kapladığı bellek baytına değil, metod tablosu adresinin saklandığı yere işaret eder



6. LDFLDA Direktifi

Yukarıda tanıttığımız gibi, sizeof operatörü ve statik tip Marshal/Unsafe tarafından sağlanan SizeOf yöntemi, örnekler tarafından işgal edilen bayt uzunluğunun hesaplanmasını gerçekten çözemez. Bildiğim kadarıyla, bu sorun sadece C# alanında çözülemez, ancak IL seviyesinde sağlanmıştırLdfldaTalimatlar bu sorunu çözmemize yardımcı olabilir. Adından da anlaşılacağı gibi, Ldflda Load Field Address (Load Field Address) anlamına gelir ve bu adres örneğinde bir alanın adresini almamıza yardımcı olur. Bu IL komutunun C#'da karşılık gelen API'si olmadığından, sadece aşağıdaki biçimde IL Emit ile kullanabiliriz.

Yukarıdaki kod parçasında gösterildiği gibi, SizeCalculator tipinde GenerateFieldAddressAccessor yöntemimiz var; bu yöntem, belirtilen türdeki alan listesine dayalı olarak Func<object?, uzun[]> tipinde bir delege oluşturur; bu da belirtilen nesnenin bellek adresini ve tüm alanlarını geri getirmemize yardımcı olur. Nesnenin kendisinin adresi ve her alanın adresi ile, doğal olarak her alanın ofsetini elde edebilir ve ardından tüm örnek tarafından kaplanan bayt belleğini kolayca hesaplayabiliriz.

7. Değer tipindeki bayt sayısını hesaplayın

Değer tipleri ve referans tipleri bellekte farklı düzenlere sahip olduğundan, farklı hesaplamalar da kullanmamız gerekir. Structun baytı, hafızadaki tüm alanların içeriği olduğundan, bunu hesaplamak için akıllıca bir yöntem kullanıyoruz. Diyelim ki T tipi bir yapının bayt sayısını belirlememiz gerekiyor, sonra bir ValueTuple<T,T> tuple oluştururuz ve ikinci alanı Item2'nin ofseti yapı T'nin bayt sayısıdır. Belirli hesaplama yöntemi, aşağıdaki CalculateValueTypeInstance yönteminde yansıtılır.

Yukarıdaki kod parçasında gösterildiği gibi, hesaplamamız gereken yapı tipinin T olduğunu varsayarsak, GetDefaultAsObject yöntemini çağırarak varsayılan (T) nesnesini yansıma şeklinde alırız ve ardından ValueTuple<T,T>tuple oluştururuz. GenerateFieldAddressAccessor metodunu çağırarak Func<object?, örneğin ve alan adreslerinin hesaplanması için uzun[]> delegesini elde ettikten sonra, bu delege argüman olarak adlandırılır. Aldığımız üç bellek adresi için, kod tuple ile 1 ve 2 alanlarının adresleri aynıdır, üçüncü adresi Öğe 2'yi çıkarır ve ilk adresi alırız ve istediğimiz sonucu alırız.

8. Atıf tipindeki bayt sayısını sayın

Referans tipleri için bayt hesaplaması daha karmaşıktır; bu fikir kullanılır: örneğin kendisinin adresini ve her alanı aldıktan sonra, adresleri sıralayarak son alanın ofsetini elde ederiz. Bu ofseti son alanın bayt sayısına ekleyelim ve ardından gerekli "ilk ve son baytları" istediğimiz sonuca ekleyelim; bu da aşağıdaki CalculateReferneceTypeInstance yönteminde yansıtılır.

Yukarıdaki kod parçasında gösterildiği gibi, belirtilen tipte tanımlanmış alanlar yoksa, CalculateReferneceTypeInstance referans tipi örneğinin minimum bayt sayısını döndürür: adres işaretçi baytlarının sayısının 3 katı. x86 mimarileri için, bir uygulama türü nesne, ObjectHeader (4 bayt), metod tablosu işaretçileri (baytlar) ve en az 4 bayt alan içeriği dahil olmak üzere en az 12 bayt kaplar (bu 4 bayt, herhangi bir tür tanımlanmamış olsa bile gereklidir). x64 mimarisi durumunda, bu minimum bayt sayısı 24 olur, çünkü metod tablosu işaretçisi ve minimum alan içeriği 8 bayt olur, ancak ObjectHeader'ın geçerli içeriği sadece 4 bayt kaplar, ancak önüne 4 bayt doldurma eklenecektir.

Son alanın kaplayan baytların yerleşimi de çok basittir: eğer tip bir değer tipi ise, daha önce tanımlanan CalculateValueTypeInstance yöntemi hesaplamak için çağrılır; eğer referans tipse, alanda depolanan içerik sadece hedef nesnenin bellek adresidir, bu yüzden uzunluk IntPtr.Size'dir. Referans tipi örnekler varsayılan olarak bellekte IntPtr.Size ile hizalandığından, burada da bu yapılır. Son olarak, referans türü örneğinin referansının belleğin ilk baytına değil, metod tablosu işaretçisini saklayan bayta işaret ettiğini unutmayın, bu yüzden ObjecthHeader'ın bayt sayısını eklemeniz gerekir (IntPtr.Size).

9. Tam hesaplama

Değer tipi ve referans tipi örneklerinin bayt sayısını hesaplamak için kullanılan iki yöntem, aşağıdaki SizeOf yönteminde kullanılır. Ldflda komutunun çağrısı karşılık gelen bir örnek sağlaması gerektiğinden, bu yöntem hedef tipin yanı sıra ilgili örneği elde etmek için bir delege sağlar. Bu delegeye karşılık gelen parametreler varsayılan olarak belirlenebilir ve değer tipi için varsayılan değeri kullanacağız. Referans türleri için, varsayılan yapıcı kullanarak hedef nesneyi oluşturmaya da çalışacağız. Eğer bu delege nesnesi sağlanmazsa ve hedef örnek oluşturulamazsa, SizeOf yöntemi bir istisna oluşturur. Hedef örneği sağlamamız gerekse de, hesaplanan sonuç sadece tiple ilişkilidir, bu yüzden hesaplanan sonucu önbellekler. Çağrı kolaylığı için, başka bir genel SizeOf <T>yöntemi de sunuyoruz.

Aşağıdaki kod parçasında, aynı alan tanımına sahip iki yapı ve tipin bayt sayısını çıkarmak için kullanıyoruz. Bir sonraki makalede, hesaplanan bayt sayısına göre hafızadaki örnekin tam ikili içeriğini daha ayrıntılı olarak alacağız, bu yüzden takipte kalın.

Orijinal bağlantı:Bağlantı girişi görünür.




Önceki:Ön uç çerçevesi, Bileşen-Taraf açık kaynak projesini öğrenir
Önümüzdeki:MinIO depolama (iii) Yerel dosyaları minio kovasına kopyala-yükle (taşıma)
 Ev sahibi| 2025-8-27 09:33:22 tarihinde yayınlandı |
C# koleksiyonu, belleği kaplayan 10.000 veri parçası ekliyor




Kod:


Feragatname:
Code Farmer Network tarafından yayımlanan tüm yazılım, programlama materyalleri veya makaleler yalnızca öğrenme ve araştırma amaçları içindir; Yukarıdaki içerik ticari veya yasa dışı amaçlarla kullanılamaz, aksi takdirde kullanıcılar tüm sonuçları ödemelidir. Bu sitedeki bilgiler internetten alınmakta olup, telif hakkı anlaşmazlıklarının bu siteyle hiçbir ilgisi yoktur. Yukarıdaki içeriği indirmeden sonraki 24 saat içinde bilgisayarınızdan tamamen silmelisiniz. Programı beğendiyseniz, lütfen orijinal yazılımı destekleyin, kayıt satın alın ve daha iyi orijinal hizmetler alın. Herhangi bir ihlal olursa, lütfen bizimle e-posta yoluyla iletişime geçin.

Mail To:help@itsvse.com