【发布时间】:2018-05-21 13:47:38
【问题描述】:
很多次我都提到分配一个对象是昂贵的。
大多数参考是:
- 倾斜,或
- 不完整,因为他们没有进一步解释具体什么成本是什么以及它如何与任何不是对象的东西进行比较,或者因为引用是上下文的并且想要继续它正在讨论的主要话题或完全不诚实。
- 或者是引用隐含地忽略了它们正在比较为对象分配内存的成本。好的,创建一个对象很昂贵,但是反对什么?为什么?如何?多少钱?
我无法找到过去遇到的所有参考资料,但突然发生的一件事让位于我提出这个问题的动机(以及随之而来的挫败感)。它不是权威来源,但在这里:
示例 1
This articlestructs 说:
使用结构可以避免 C# 语言中对象的开销。你 可以组合多个字段。这减少了内存压力。而且它 (有时)提高性能。
现在,我不知道这是在说什么。当然,即使是 CLR 中最简单的对象也会占用 12 个字节,如下所示:
- 4 个字节的东西我忘记了,我想是某种标题;
- 4 个字节用于指向对象方法表的指针;和
- 对象包含的第一个字段需要 4 个字节,即使对象不包含字段或属性也是如此。
structs 和值类型的布局是否不同?当然,structs 也必须有一个方法表和一个标题。那么声明struct 相对于对象的比较成本是多少?或者就此而言,分配对象与声明 any 值类型的成本比较是多少?
示例 2
考虑另一个示例,其中潜在的动机是避免为创建新对象分配内存。
在下面的示例中,代码缓存了一个StringBuilder 以避免重新创建它,即使该对象的所有内容都已被擦除以腾出新内容的位置。这只是新对象的创建,即避免为新对象分配内存。
来自source of KeyValuePair<TKey, TValue>:
public override string ToString()
{
StringBuilder sb = StringBuilderCache.Acquire(0x10);
sb.Append('[');
if (this.Key != null)
{
sb.Append(this.Key.ToString());
}
sb.Append(", ");
if (this.Value != null)
{
sb.Append(this.Value.ToString());
}
sb.Append(']');
return StringBuilderCache.GetStringAndRelease(sb);
}
注意代码的第一行调用StringBuilderCache.Acquire。下面是the code for the StringBuilderCache class,其目的是缓存StringBuilder对象的一个实例,以避免重新创建它。
internal static class StringBuilderCache
{
// Fields
[ThreadStatic]
private static StringBuilder CachedInstance;
private const int MAX_BUILDER_SIZE = 360;
// Methods
public static StringBuilder Acquire(int capacity = 0x10);
public static string GetStringAndRelease(StringBuilder sb);
public static void Release(StringBuilder sb);
}
下面是这个类的source of the Acquire method。请注意,它只是在清空其内容后返回一个缓存的StringBuilder 实例,前提是该实例以前可用。
public static StringBuilder Acquire(int capacity = 0x10)
{
if (capacity <= 360)
{
StringBuilder cachedInstance = CachedInstance;
if ((cachedInstance != null) && (capacity <= cachedInstance.Capacity))
{
CachedInstance = null;
cachedInstance.Clear();
return cachedInstance;
}
}
return new StringBuilder(capacity);
}
示例 3
我刚刚找到了一个权威来源,暗示了上述内容。
MSDN 上的 C# Programming Guide 中的Using Structs 页面,几乎在文章的开头,声明如下:
虽然用
class表示一个点同样方便 自动实现的属性,struct在某些情况下可能更有效 情景。例如,如果您声明一个包含 1000 个 Point 对象的数组, 您将为引用每个对象分配额外的内存;在 在这种情况下,struct会更便宜。
那么,关于创建新对象的这种嘘声是什么?
【问题讨论】:
-
嗯,分配对象并不昂贵。当分配成为瓶颈时,C# 程序将轻松击败 C++ 程序。 访问它们更昂贵。值类型与处理器喜欢使用内存的方式高度兼容,能够将值存储在处理器寄存器中对性能有很大的影响。使用堆栈进行存储很有帮助,因为它总是很热。引用类型的对象需要通过指针进行额外的间接处理,当需要从慢速 RAM 中挖掘对象时,处理器可能会陷入困境。
-
@HansPassant 考虑到这个答案,你所说的很有意义。 (stackoverflow.com/a/45276657/303685) 这家伙做了一个性能测试,只访问
struct和class实例,排除了创建它们的成本,并注意到访问时间的差异。 -
“当然,结构也必须有一个方法表和一个标题”——你为什么这么认为?请记住,结构没有继承的可能性。使用未装箱的结构时,要调用的方法在编译时是已知的。
标签: c# memory-management struct clr