好吧,我知道在C ++中有诸如malloc / free和在C ++中用于内存管理的new / using-destructor之类的东西,但是我想知道为什么这些语言没有“新更新”,使用户能够是否可以选择手动管理内存,或让系统自动进行内存管理(垃圾回收)?
有点新奇的问题,但仅在CS中使用了大约一年。
好吧,我知道在C ++中有诸如malloc / free和在C ++中用于内存管理的new / using-destructor之类的东西,但是我想知道为什么这些语言没有“新更新”,使用户能够是否可以选择手动管理内存,或让系统自动进行内存管理(垃圾回收)?
有点新奇的问题,但仅在CS中使用了大约一年。
Answers:
垃圾收集需要用于跟踪分配和/或引用计数的数据结构。这些会在内存,性能和语言复杂性方面造成开销。C ++被设计为“接近金属”,换句话说,它在权衡性能与便捷功能之间取舍了更高的性能。其他语言则使折衷方式有所不同。这是选择语言时要考虑的因素之一,因此您偏爱于此。
就是说,C ++中有许多引用计数方案,它们相当轻量级且性能很好,但是它们存在于商业和开源库中,而不是语言本身的一部分。管理对象生命周期的引用计数与垃圾回收不同,但是它解决了许多相同类型的问题,并且更适合C ++的基本方法。
严格来说,C语言根本没有内存管理。malloc()和free()不是语言中的关键字,而只是从库中调用的函数。由于malloc()和free()是C标准库的一部分,并且将由C的任何符合标准的实现提供,因此这种区别现在可能是很古怪的,但是在过去并不总是如此。
您为什么要使用没有标准的内存管理语言?这可以回溯到C的起源,称为“便携式程序集”。在许多情况下,硬件和算法可以受益于甚至需要专门的内存管理技术。据我所知,没有办法完全禁用Java的本机内存管理并将其替换为您自己的。在某些高性能/最小资源情况下,这是完全不可接受的。C提供了几乎完全的灵活性,可以准确地选择程序要使用的基础结构。付出的代价是C语言在编写正确的无错误代码方面几乎没有帮助。
malloc()
或free()
。(例如用于PIC的MLAP编译器)
真正的答案是,建立安全,有效的垃圾回收机制的唯一方法是为不透明引用提供语言级别的支持。(或者相反,缺少对直接内存操作的语言级别支持。)
Java和C#可以做到这一点,因为它们具有无法操作的特殊引用类型。这使运行时可以自由执行诸如在内存中移动分配的对象之类的事情,这对于高性能GC的实现至关重要。
出于记录,没有现代的GC实现使用引用计数,因此这完全是个麻烦。现代GC使用世代集合,其中新分配的处理方式基本上与栈分配在C ++等语言中的处理方式相同,然后将所有仍在运行的新分配的对象定期移至单独的“幸存者”空间,并整代生成的对象立即被释放。
这种方法各有利弊:支持GC的语言中的堆分配与不支持GC的语言中的堆分配一样快,缺点是需要执行清除操作的对象才能被销毁需要一个单独的机制(例如C#的using
关键字),否则它们的清除代码将不确定地运行。
请注意,高性能GC的一个关键是必须为特殊类的引用提供语言支持。C没有这种语言支持,而且永远不会;因为C ++具有运算符重载,所以它可以模拟GC的指针类型,尽管必须谨慎进行。实际上,当Microsoft发明了可以在CLR(.NET运行时)下运行的C ++方言时,他们不得不为“ C#样式引用”(例如Foo^
)发明新的语法,以将其与“ C ++样式引用”区分开。 (例如Foo&
)。
C ++的功能以及C ++程序员经常使用的是智能指针,它实际上只是一种引用计数机制。我不会认为引用计数是“真正的” GC,但它确实提供了许多相同的好处,但代价是比手动内存管理或真正的GC速度慢,但具有确定性销毁的优势。
归根结底,答案实际上归结为一种语言设计功能。C做出了一个选择,C ++做出了使它与C向后兼容的选择,同时仍然提供了足以满足大多数目的的替代方案,而Java和C#做出了与C不兼容但对于C来说也足够好的选择大多数目的。不幸的是,没有万灵药,但是熟悉其中的不同选择将帮助您为当前尝试构建的任何程序选择正确的选择。
std::unique_ptr
“对不透明引用的语言级别支持”吗?(这不是我想要的那种支持,除非在C ++中也删除了对直接内存操作的支持,否则我也不认为这是足够的。)我的回答中确实提到了智能指针,我会考虑std:unique_ptr
使用智能指针。 ,因为它实际上确实进行引用计数,所以它仅支持引用数量为零或一的特殊情况(并且std::move
是引用计数更新机制)。
std::unique_ptr
没有引用计数,std::move
与引用完全没有关系(因此对性能没有影响)。不过,我明白你的意思了,std::shared_ptr
引用计数也隐含地更新了std::move
:)
malloc
和快free
。因此,是的,GC可以快得多。(请注意,我说的是“可以”-当然,每个程序的确切性能受许多因素影响。)
因为,当使用C ++的功能时,没有必要。
赫伯·萨特(Herb Sutter):“ 多年来我都没有写过删除。 ”
请参阅编写现代C ++代码: 21 年以来C ++的发展
这可能会让许多经验丰富的C ++程序员感到惊讶。
“全部”垃圾收集器是一个定期运行的过程,用于检查内存中是否有未引用的对象以及是否将其删除。(是的,我知道这太过简单了)。这不是语言的属性,而是框架的属性。
有为C和C ++编写的垃圾收集器- 例如,这个。
之所以没有将其“添加”到该语言中,一个原因可能是因为现有代码量巨大,因为他们使用自己的代码来管理内存,因此它们永远不会使用它。另一个原因可能是用C和C ++编写的应用程序类型不需要与垃圾回收过程相关的开销。
malloc
和free
,则会破坏我的正确程序。
free
直到完成后我才打电话。但是您提议的在我明确调用之前不会释放内存的垃圾收集器根本不是free
垃圾收集器。
我没有确切的引号,但Bjarne和Herb Sutter都说了一些话:
C ++不需要垃圾收集器,因为它没有垃圾。
在现代C ++中,您使用智能指针,因此没有垃圾。
您能想象用带有垃圾回收的语言编写设备处理程序吗?GC运行时,有多少位可能下降?
还是操作系统?在启动内核之前,如何启动运行的垃圾回收?
C被设计用于接近硬件任务的底层。问题?它是一种很好的语言,因此对于许多更高级别的任务也是不错的选择。语言沙皇意识到了这些用法,但是他们需要优先支持设备驱动程序,嵌入式代码和操作系统的要求。
对于这个问题的简短而无聊的回答是,需要为编写垃圾收集器的人们提供一种非垃圾收集语言。同时要允许对内存布局进行非常精确的控制并在顶部运行GC 的语言在概念上并不容易。
另一个问题是为什么C和C ++没有垃圾收集器。好吧,我知道C ++有很多,但是它们并没有真正流行,因为它们被迫处理最初不是由GC-ed设计的语言,而仍然使用C ++的人这个年龄的确不是那种错过GC的人。
另外,与其将GC添加到旧的非GC版本的语言中,反而更容易地创建一种在支持GC的同时具有大多数相同语法的新语言。Java和C#是很好的例子。
有很多问题,包括...
delete
或free
明确。GC方法仍然有一个优势-没有悬而未决的参考-静态分析可以捕获某些情况,但是同样,对于所有情况都没有一种完美的解决方案。基本上,部分是关于语言的时代,但是无论如何,非GC语言总会有一个地方-即使这是一个小众市场。严重的是,在C ++中,缺少GC并不是什么大问题-内存的管理方式不同,但并非不受管理。
微软托管的C ++至少具有在同一应用程序中混合使用GC和非GC的能力,可以混合使用每种优点,但是我没有经验说这在实践中的效果如何。
代表服务链接到我的相关答案...
垃圾回收从根本上与用于开发具有DMA功能的硬件的驱动程序的系统语言不兼容。
指向对象的唯一指针很可能会存储在某些外设的硬件寄存器中。由于垃圾收集器对此一无所知,因此会认为该对象不可访问并进行收集。
对于压缩GC,此参数具有双重意义。即使您小心地维护对硬件外围设备使用的对象的内存引用,当GC重新放置该对象时,它也不知道如何更新外围设备配置寄存器中包含的指针。
因此,现在您需要混合使用固定的DMA缓冲区和GC管理的对象,这意味着您同时具有这两种缺点。
因为C&C ++是相对低级的语言,通常用于通用目的,例如,甚至可以在嵌入式系统中具有1MB内存的16位处理器上运行,而这不能用gc浪费内存。
在C ++和C中有垃圾收集器。不确定在C中如何工作,但是在C ++中,您可以利用RTTI动态发现对象图并将其用于垃圾收集。
据我所知,没有垃圾收集器就无法编写Java。稍加搜索就发现了这个。
Java和C / C ++之间的主要区别在于,在C / C ++中,选择始终是您的选择,而在Java中,设计常常使您无选择。
这是GC的固有问题的列表,这些问题使它无法在诸如C的系统语言中使用:
GC必须在其管理的对象的代码级别以下运行。内核中根本没有这样的级别。
GC必须不时停止托管代码。现在考虑如果对内核执行该操作会发生什么情况。当GC扫描所有现有的内存分配时,计算机上的所有处理将停止一毫秒。这将终止所有创建在严格的实时要求下运行的系统的尝试。
GC需要能够区分指针和非指针。也就是说,它必须能够查看存在的每个内存对象,并能够生成可在其中找到其指针的偏移量列表。
此发现必须是完美的:GC必须能够追踪其发现的所有指针。如果取消引用误报,则可能会崩溃。如果未能发现误报,则很可能会破坏仍在使用的对象,从而使托管代码崩溃或无声地破坏其数据。
绝对需要将类型信息存储在现有的每个对象中。但是,C和C ++都允许不包含类型信息的普通旧数据对象。
GC本质上是一项缓慢的业务。曾经与Java交往过的程序员可能没有意识到这一点,但是如果不使用Java来实现,程序的速度可能会提高几个数量级。导致Java变慢的因素之一是GC。这就是诸如Java之类的GCed语言无法用于超级计算的原因。如果您的机器每年耗费一百万美元的电力,那么您甚至不想为垃圾回收支付10%的费用。
C和C ++是为支持所有可能的用例而创建的语言。而且,如您所见,垃圾收集排除了许多这些用例。因此,为了支持这些用例,不能对C / C ++进行垃圾收集。