我想知道在一个对象中装入一个值类型是否是一种特殊情况,或者在删除对它的任何引用之后,由.NET构造的“框”是否变为垃圾(GC必须收集)。
例如,StringBuilder.AppendFormat()具有以下重载:
StringBuilder.AppendFormat(string format, object arg0);
StringBuilder.AppendFormat(string format, object arg0, object arg1);
StringBuilder.AppendFormat(string format, object arg0, object arg1, object arg2);
StringBuilder.AppendFormat(string format, params object[] args);
对于具有3个或更少参数的调用具有这些额外的重载可能表明装箱确实是一种特殊情况(或者从性能的角度来看,它是值得的,以避免数组构造)。
理论上,使用普通旧引用计数(可能带有可重用框的池)将是一个有效的实现,因为从一个框到另一个框不能引用,只能从.NET对象到框。
答案 0 :(得分:8)
首先,只是为了澄清:创建一个对象引用数组是不装箱。 “拳击”是一个在.NET中具有非常特殊含义的术语,我认为值得坚持下去。
Boxing 会创建垃圾 - 或者更确切地说,每次你装箱时,它都会创建一个新对象,最终可能会变成垃圾。 (它不会让成为垃圾 - 你可能会在应用程序的剩余时间内引用该对象;这种情况非常罕见。)
但是,你可以拥有一个用于装箱的缓存。实际上,Java确实适用于少数人。如果你写:
Integer x = 5;
Integer y = 5;
System.out.println(x == y); // Reference comparison
然后保证打印true
。
然而,这只是一组固定类型的小缓存 - 它不是通用缓存。你需要平衡一般缓存与弱引用的痛苦(不是引用计数 - .NET中的GC机制只是不是引用计数,你无法真正引入只是对于盒装价值而言)几乎肯定会比拳击创造垃圾的成本更高。
.NET 可以使用与Java相同的方法并装箱 某些类型的某些值,但我不确定它是否值得额外的概念包袱 - 特别是当平台支持自定义值类型时(Java没有)。
值得注意的是,自从.NET 2.0以来,拳击比以前更为罕见。它在数据绑定和反射中发生了相当大的数量,但现在在普通的旧数据操作中不太常见。
答案 1 :(得分:3)
装箱的值类型成为堆上的对象,并且像任何其他对象一样,必须(并且将)在不再引用后进行垃圾回收。
使用3个或更少的参数创建方法重载(如您所见)以避免数组构造,并且是性能优化。请参阅Members with a Variable Number of Parameters中的“考虑在极其性能敏感的API中为具有少量参数的调用提供特殊重载和代码路径”。
但是,创建数组与装箱值类型有根本的不同。调用StringBuilder.AppendFormat
的任何重载将始终将参数设置为值类型,因为参数的类型为object
,无论是否创建了数组。有关拳击的详细说明,请参阅.NET: Type Fundamentals上的“拳击和拆箱”。
答案 2 :(得分:0)
你问的是错误的问题。
您指向的重载是针对直接参数调用进行优化。这意味着编译器将变量放入arg_0,arg_1,arg_2,arg_3,可能有更多,但IL只能将这些变量作为快速访问。其余的都是通过堆栈进行的,因此不比param类型的函数调用更有效。
对于param类型的函数调用,它实际上在scenene后面创建一个数组,并将其作为arg_1发送给函数(在这种情况下,arg_0被字符串占用)。