Neden yapı hizalama alan bir tür ilkel veya kullanıcı tanımlı olup olmadığını bağlıdır?
Noda Time v2, çözünürlük nanosaniye için hareket ediyoruz. Artık 8 baytlık bir tamsayı ilgilendiğimiz zaman aralığını temsil etmek için kullanabiliriz demektir. O beni ele beni CLR uyumu karar hafif bir gariplik ortaya çıkarmak için açmıştır Noda Zaman (çok) yapılar, bellek kullanımını araştırmak üzere harekete geçirdi.
Öncelikle, bunun farkındayımvarsayılan davranış, herhangi bir zamanda değiştirebilecek bir uygulama kararı almış. Bunu ben de fark ettimolabilir[StructLayout]
[FieldOffset]
kullanarak değiştirmek daha ziyade mümkünse o durmamış olan bir çözüm buldum.
Benim temel senaryo bu alanlar basit ambalaj kağıtları nerede başvuru tipi alanı ve değer türü Diğer iki alanları içerir struct
int
için var. Vardıumut16 bayt olarak temsil 64-bit CLR (başvuru ve diğerleri her biri için 4 8), ama nedense 24 byte kullanıyor. Uzay dizileri kullanarak ölçüyorum, bu arada düzen farklı durumlarda farklı olabilir anlıyorum, ama bu mantıklı bir başlangıç noktası gibi hissettim.
İşte örnek bir program sorunu gösteren:
using System;
using System.Runtime.InteropServices;
#pragma warning disable 0169
struct Int32Wrapper
{
int x;
}
struct TwoInt32s
{
int x, y;
}
struct TwoInt32Wrappers
{
Int32Wrapper x, y;
}
struct RefAndTwoInt32s
{
string text;
int x, y;
}
struct RefAndTwoInt32Wrappers
{
string text;
Int32Wrapper x, y;
}
class Test
{
static void Main()
{
Console.WriteLine("Environment: CLR {0} on {1} ({2})",
Environment.Version,
Environment.OSVersion,
Environment.Is64BitProcess ? "64 bit" : "32 bit");
ShowSize<Int32Wrapper>();
ShowSize<TwoInt32s>();
ShowSize<TwoInt32Wrappers>();
ShowSize<RefAndTwoInt32s>();
ShowSize<RefAndTwoInt32Wrappers>();
}
static void ShowSize<T>()
{
long before = GC.GetTotalMemory(true);
T[] array = new T[100000];
long after = GC.GetTotalMemory(true);
Console.WriteLine("{0}: {1}", typeof(T),
(after - before) / array.Length);
}
}
Ve benim laptop derleme ve çıktı:
c:\Users\Jon\Test>csc /debug- /o ShowMemory.cs
Microsoft (R) Visual C# Compiler version 12.0.30501.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.
c:\Users\Jon\Test>ShowMemory.exe
Environment: CLR 4.0.30319.34014 on Microsoft Windows NT 6.2.9200.0 (64 bit)
Int32Wrapper: 4
TwoInt32s: 8
TwoInt32Wrappers: 8
RefAndTwoInt32s: 16
RefAndTwoInt32Wrappers: 24
Yani:
- Başvuru türü alanı yok ise, CLR
Int32Wrapper
alanlar birlikte pack mutlu (TwoInt32Wrappers
8 büyüklüğünde) - Başvuru türü alanı bile, CLR hala
int
alanlar birlikte pack mutlu (RefAndTwoInt32s
16 büyüklüğünde) - İkisini birleştiren
Int32Wrapper
Her alan/8 bayt için sıfır hizada görünüyor. (RefAndTwoInt32Wrappers
boyutu vardır 24.) - Hata ayıklayıcı, aynı kod (ama hala bir yayın oluşturma) çalışan 12 Boyutunu gösterir.
Birkaç Deney benzer sonuçlar vermiştir
- Değer tipi alanları sonra başvuru tipi alanı koyarak bir faydası yok
string
yerineobject
kullanarak yardımcı olmuyor (bekliyorum "herhangi bir başvuru türü")- Kullanarak bir "referans yok ortalıkta" yardım . kapsayıcı bir yapı
- Başvuru etrafında sarıcı olarak genel bir yapı kullanarak yardımcı olmuyor
- Eğer alanları (sadelik için çiftler halinde),
int
eklemeye devam edersem alanlar hala 4 bayt sayısı içinInt32Wrapper
8 bayt sayısını alanları - Görünürde her yapı için
[StructLayout(LayoutKind.Sequential, Pack = 4)]
ekleme sonuçları değiştirmez
Alanları dolu olmak istiyorum herkes bu (ideal olarak başvuru belgeleri ile birlikte) için herhangi bir açıklama veya CLR ipucu nasıl alabilirim bir öneriniz var mıolmadansabit bir alan belirleme ofset?
CEVAP
Bu bir hata olduğunu düşünüyorum. 64-bit modunda 8 bayt katları olan bir adres için önemsiz olmayan alanları hizalamak için seviyor yan etkisi otomatik düzen, görüyorsun. Açıkça [StructLayout(LayoutKind.Sequential)]
öznitelik uygulamak bile oluşur. İşte bunun olmaması lazım.
Yapı üyeleri ortak yapımı ve bu gibi test kodu ekleyerek de görebilirsiniz:
var test = new RefAndTwoInt32Wrappers();
test.text = "adsf";
test.x.x = 0x11111111;
test.y.x = 0x22222222;
Console.ReadLine(); // <=== Breakpoint here
Kesme isabetlerinin sayısı, hata Ayıklama Windows Bellek 1 kullanın. 4-byte tamsayılar ve anahtarı koymak &test
Adres alanı:
0x000000E928B5DE98 0ed750e0 000000e9 11111111 00000000 22222222 00000000
0xe90ed750e0
makinem (senin değil) dize işaretçisi. Kolayca görebilirsiniz 24 bayt boyutuna döndü dolgu fazladan 4 bayt ile Int32Wrappers
,. Yapı dönün ve son dize koyun. Tekrar ve dize işaretçisi olduğunu göreceksinizhalailk. LayoutKind.Sequential
, ihlal var LayoutKind.Auto
.
Zor Microsoft bunu düzeltmek için ikna etmek için olacak, herhangi bir değişiklik kopma olacak bu yüzden çok uzun süre bu şekilde çalıştıbir şey. CLR tek bir yapı yönetilen sürümü için [StructLayout]
onur ve blittable yapmak için bir girişim yapar, genel olarak çabuk vazgeçmez. Herkesin bildiği gibi, herhangi bir yapı için bir DateTime içerir. Sadece bir yapı, sıralama yaparken doğru LayoutKind garanti olsun. Sıralanmış sürümü kesinlikle Marshal.SizeOf()
size söyleyecektir olarak 16 bayt.
LayoutKind.Explicit
kullanarak duymak istediğin bu, değil giderir.
Nasıl değişken olup olmadığını belirle...
Nasıl PHP ilişkisel Dizi veya sıralı o...
Nasıl belirli bir Linux 32 bit veya 64...
Nasıl bir kullanıcı altına kaydırılan ...
Nasıl bir web sayfası doğrudan tarayıc...