6

我想知道在对象中对值类型进行装箱是否是一种特殊情况,或者在删除对它的任何引用后,.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 对象到一个框。

4

3 回答 3

8

首先,澄清一下:创建对象引用数组不是装箱。“拳击”是一个在 .NET 中具有非常特殊含义的术语,我认为值得坚持下去。

装箱确实会产生垃圾——或者更确切地说,每次装箱时,它都会创建一个可能最终变成垃圾的新对象。(它不必变成垃圾——在应用程序的剩余生命周期,您可能会引用该对象;这非常罕见。)

但是,您可以有一个用于装箱目的的缓存。事实上,Java 确实适用于少量数字。如果你写:

Integer x = 5;
Integer y = 5;
System.out.println(x == y); // Reference comparison

那么保证打印true

但是,这只是一组固定类型的小缓存 - 它不是通用缓存。您需要平衡使用弱引用(不是引用计数 - .NET 中的 GC 机制只是没有引用计数,而且您不能真正针对装箱值引入)的痛苦,这几乎肯定会损害性能不仅仅是拳击制造垃圾的小成本。

.NET可以采用与 Java 相同的方法并将某些类型的一些值装箱,但我不确定是否值得额外的概念包袱——尤其是当平台支持自定义值类型(Java 不支持)时。

可能值得注意的是,从 .NET 2.0 开始,拳击比以前少了一些。它在数据绑定和反射中发生了相当多的情况,但现在在普通的旧数据操作中不太常见。

于 2010-02-09T07:07:25.090 回答
3

装箱的值类型成为堆上的对象,并且与任何其他对象一样,一旦不再被引用,就必须(并且将)进行垃圾回收。

使用 3 个或更少参数创建方法重载(如您所见)是为了避免数组构造,并且是一种性能优化。请参阅“参数数量可变的成员”中的“考虑在对性能极为敏感的 API 中为具有少量参数的调用提供特殊的重载和代码路径” 。

但是,创建数组与装箱值类型根本不同。调用 的任何重载StringBuilder.AppendFormat总是将值类型的参数装箱,因为object无论是否创建数组,参数的类型都是 。有关装箱的详细说明,请参阅.NET:Type Fundamentals中的“装箱和拆箱” 。

于 2010-02-09T07:04:10.777 回答
0

你问错问题了。

您指向的重载是针对直接参数调用进行优化。这意味着编译器会将变量放入 arg_0、arg_1、arg_2、arg_3,可能不止这些,但 IL 仅将这些作为快速访问。其余的无论如何都会通过堆栈,因此并不比 param 类型的函数调用更有效。

对于参数类型的函数调用,它实际上在场景后面创建了一个数组,并将其作为 arg_1 发送到函数(在这种情况下,arg_0 被字符串占用)。

于 2010-02-09T07:18:35.657 回答