与Java不同,为什么拳击是.NET中的原始值类型未缓存?

Ani*_*Ani 11 .net java boxing memory-management value-type

考虑:

int a = 42;

// Reference equality on two boxed ints with the same value
Console.WriteLine( (object)a == (object)a ); // False

// Same thing - listed only for clarity
Console.WriteLine(ReferenceEquals(a, a));  // False
Run Code Online (Sandbox Code Playgroud)

显然,每个装箱指令分配一个单独的盒装实例Int32,这就是为什么它们之间的引用相等失败的原因.此页面似乎表明这是指定的行为:

box指令将'raw'(未装箱)值类型转换为对象引用(类型O).这是通过创建新对象 并将值类型中的数据复制到新分配的对象中来实现的.

但为什么会出现这种情况呢?有没有令人信服的理由为什么CLR不选择Int32为所有原始值类型(都是不可变的)保持盒装s 的"缓存" ,甚至更强的共同值?我知道Java有这样的东西.

在没有泛型的时代,对于ArrayList主要由小整数组成的大型内存需求以及GC工作负载,它是否会帮助很多?我也肯定存在一些现代的是.NET应用程序使用泛型,但是由于各种原因(反射,接口分配等),跑起来大拳分配可与可大量减少(什么似乎是)一个简单的优化.

那是什么原因?我没有考虑过一些性能影响(我怀疑是否测试该项目是否在缓存中等将导致净性能损失,但我知道什么)?实施困难?不安全代码的问题?打破向后兼容性(我想不出任何好的理由,为什么一个精心编写的程序应该依靠现有的行为)?或者是其他东西?

编辑:我真正建议的是"常见" 原语静态缓存,就像Java所做的那样.有关示例实现,请参阅Jon Skeet的回答.我知道在运行时为任意,可能是可变的值类型或动态 "memoizing"实例执行此操作是完全不同的事情.

编辑:为清晰起见改变了标题.

Jon*_*eet 11

发现引人注目的一个原因是一致性.正如你所说,Java 确实将盒装值缓存在一定范围内......这意味着编写一段有效的代码非常容易:

// Passes in all my tests. Shame it fails if they're > 127...
if (value1 == value2) {
    // Do something
}
Run Code Online (Sandbox Code Playgroud)

我一直被这种方式所困扰 - 幸运的是,在测试中而不是生产代码,但是在某个给定范围之外有一些显着改变行为的东西仍然是令人讨厌的.

不要忘记任何条件行为也会导致所有装箱操作的成本- 所以在不使用缓存的情况下,你实际上发现它更慢(因为它首先必须检查是否使用缓存).

如果你真的想编写自己的缓存盒操作,当然,你可以这样做:

public static class Int32Extensions
{
    private static readonly object[] BoxedIntegers = CreateCache();

    private static object[] CreateCache()
    {
        object[] ret = new object[256];
        for (int i = -128; i < 128; i++)
        {
            ret[i + 128] = i;
        }
    }

    public object Box(this int i)
    {
        return (i >= -128 && i < 128) ? BoxedIntegers[i + 128] : (object) i;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

object y = 100.Box();
object z = 100.Box();

if (y == z)
{
    // Cache is working
}
Run Code Online (Sandbox Code Playgroud)

  • @Ani:与盒子不同,字符串不仅仅是从没有可见操作的值类型中神奇地存在. (2认同)
  • @Ani:他们目前无声地发生 - 虽然不一定是BCL或第三方代码.我真的不能对这种优化感到兴奋 - 感觉它增加了复杂性而没有任何证据表明它会对性能产生有意义的影响.当然,如果你想描述一些重要的应用程序,进行更改以显式使用缓存,然后从数据争论,这是另一回事.在那之前,我会赞成一致性:)*你*可能不会购买它作为一个原因,但作为一个被Java的行为所困扰的人,这对我来说是有意义的:) (2认同)
  • "你是否同意大多数应用程序中的大部分拳击发生在BCL内部"我不同意这一点.当调用带有`object`的API但是你提供value-type的地方时会发生这种情况.这通常是您自己的代码调用BCL代码而不是BCL代码本身的情况.还是有任何重要的案例我都不见了? (2认同)