首先让我告诉我,我知道MSDN on boxing and unboxing的内容,并在拳击和拆箱上看到关于SO的帖子。我也理解为什么拳击是有用的,它在高水平上做了什么,并且曾多次与IL合作过......所以请不要拖延。
我想知道的是拳击和拆箱如何完全,最好是证明。我的意思是:
换句话说,我读过的帖子谈到“运行时实现细节”,这正是我想知道的: - )
答案 0 :(得分:0)
最后我觉得我弄清楚了......以下是我找到的答案。如果我犯了错误,请告诉我。
运行时是否真的为每次装箱/拆箱操作复制堆上的数据,还是使用引用计数等技巧?
public void Test6()
{
GC.Collect(GC.MaxGeneration);
GC.WaitForFullGCComplete();
object[] myarr = new object[1024 * 1024];
long mem1 = GC.GetTotalMemory(true);
int a = 1;
for (int i = 0; i < myarr.Length; ++i)
{
myarr[i] = a;
}
long mem2 = GC.GetTotalMemory(true);
Console.WriteLine("Total memory usage is {0} bytes", mem2 - mem1);
// Make sure we use it so that the JIT doesn't optimize our code
int sum = 0;
for (int i = 0; i < myarr.Length; ++i)
{
sum += (int)myarr[i];
}
Console.WriteLine("Sum = {0}", sum);
}
对于x86,结果是12582912 - 这是完整对象的行为:4x1M int,4x1M类型引用和4x1M指针存储在数组中。答:它只是复制到堆中。
这使得运行时不太可能使用不同的规则IMO。
似乎没有。尝试:
private object IntBox1()
{
return 1;
}
private int IntNotBox1()
{
return 1;
}
public int Total1()
{
int sum = 0;
for (int i = 0; i < 100000000; ++i)
{
sum += (int)IntBox1();
}
return sum;
}
public int Total2()
{
int sum = 0;
for (int i = 0; i < 100000000; ++i)
{
sum += IntNotBox1();
}
return sum;
}
时间上有显着差异,所以没有。我还没有找到一种方法来帮助运行时优化装箱/拆箱。如果有人找到任何方法让运行时优化盒子/ unbox操作,请分享。
似乎是这种情况:对值类型的vtable查找要快得多。
public void Test4()
{
int a = 1;
object oa = a;
Stopwatch sw = new Stopwatch();
sw.Start();
int sum = 0;
for (int i = 0; i < 100000000; ++i)
{
sum += a.GetHashCode();
}
Console.WriteLine("Calc {0} took {1:0.000}s", sum, new TimeSpan(sw.ElapsedTicks).TotalSeconds);
sw = new Stopwatch();
sw.Start();
sum = 0;
for (int i = 0; i < 100000000; ++i)
{
sum += oa.GetHashCode();
}
Console.WriteLine("Calc {0} took {1:0.000}s", sum, new TimeSpan(sw.ElapsedTicks).TotalSeconds);
}
我现在认为这与2个原因有关。
结论:装箱真的总是将值类型复制到堆中,它只是一个普通的对象。您可能注意到的唯一奇怪的事情是对象中的类型引用是对未装箱值的原始(值)类型的类型引用。我找不到任何证据表明盒装值不是生活在堆上的“普通类型”对象。