3

假设您想编写一个处理大型数据集的高性能方法。为什么开发人员不应该能够打开手动内存管理而不是被迫迁移到 C 或 C++?

void Process()
{
    unmanaged
    {
        Byte[] buffer;
        while (true)
        {
            buffer = new Byte[1024000000];

            // process

            delete buffer;
        } 
    }   
}
4

6 回答 6

13

因为允许您在可能仍然存在对它的引用时手动删除内存块(并且运行时无法知道不执行 GC 循环)会产生悬空指针,从而破坏内存安全。GC 语言通常在设计上是内存安全的。

也就是说,特别是在 C# 中,你可以做你想做的事:

void Process()
{
    unsafe
    {
        byte* buffer;
        while (true)
        {
            buffer = Marshal.AllocHGlobal(1024000000);

            // process

            Marshal.FreeHGlobal(buffer);
        } 
    }   
}

请注意,与在 C/C++ 中一样,您对 C# 中的原始指针类型具有完整的指针算术 - 所以buffer[i]orbuffer+i是有效的表达式。

于 2009-12-08T17:45:29.497 回答
3

如果您需要高性能和详细的控制,也许您应该用 C 或 C++ 编写您正在做的事情。并非所有语言都适用于所有事情。

编辑添加:单一语言不会对所有事情都有好处。如果你把所有优秀编程语言的所有有用特性都加起来,你会得到一个非常大的混乱,比 C++ 糟糕得多,即使你可以避免不一致。

功能不是免费的。如果一种语言有一个特性,人们很可能会使用它。如果不学习新的 C# 手动内存管理例程,您将无法很好地学习 C#。编译器团队将以其他有用的编译器功能为代价来实现它。该语言可能会变得像 C 或 C++ 一样难以解析,这会导致编译速度变慢。(作为一个 C++ 人,当我编译我们的一个 C# 项目时,我总是感到惊讶。编译似乎几乎是瞬间完成的。)

功能相互冲突,有时以意想不到的方式。C90 在矩阵计算方面的表现不如 Fortran,因为 C 指针被别名的可能性会阻止一些优化。如果你在一种语言中允许指针算术,你必须接受它的后果。

您建议使用 C# 扩展来允许手动内存管理,并且在某些情况下会很有用。这意味着内存必须以不同的方式分配,并且必须有一种方法来区分手动管理的内存和自动管理的内存。突然间,你的内存管理变得复杂了,程序员搞砸的机会更大,而且内存管理器本身也变得更复杂了。您获得了一些在某些情况下很重要的性能,以换取在所有情况下更复杂和更慢的内存管理。

可能在某个时候,我们将拥有一种几乎适用于所有用途的编程语言,从脚本到数字运算,但没有任何流行的语言能与之相近。与此同时,我们必须愿意接受只使用一种语言的限制,或者学习多种语言并在它们之间切换的挑战。

于 2009-12-08T17:41:50.667 回答
0

在您发布的示例中,为什么不删除缓冲区并重新使用它呢?

于 2009-12-08T17:42:22.710 回答
0

.NET 垃圾收集器非常擅长找出不再引用的对象并及时释放相关的内存。事实上,垃圾收集器有一个特殊的堆(大对象堆),它在其中放置像这样的大对象,这是为了处理它们而优化的。

最重要的是,不允许显式释放引用只会消除大量内存泄漏和悬空指针的错误,从而使代码更安全。

于 2009-12-08T17:46:24.700 回答
0

像在具有显式内存管理的语言中那样单独释放每个未使用的块可能比让垃圾收集器这样做更昂贵,因为 GC 有可能使用复制方案来花费与剩余块数成线性关系的时间(或最近还活着的块的数量)而不是必须处理每个死块。

于 2009-12-08T17:48:27.230 回答
0

与大多数内核不允许您安排自己的线程的原因相同。因为 99.99+% 的时间你并不真正需要这样做,而在其余时间暴露该功能只会诱使你做一些可能愚蠢/危险的事情。

如果你真的需要细粒度的内存控制,把这段代码写在别的地方。

于 2009-12-08T17:59:56.223 回答