如果我理解正确,垃圾回收会自动释放程序不再使用的对象,就像Java中的垃圾收集器。
我听说在不支持垃圾回收的语言(比如C语言)中,程序可能存在内存泄漏,并最终耗尽内存。
那么,在不支持垃圾回收的语言(比如C语言)中,程序员会犯哪些错误呢?我猜测是没有在不再使用对象后释放它们。但是,由于缺少垃圾收集器,这是我们唯一可能犯的错误吗?
如果我理解正确,垃圾回收会自动释放程序不再使用的对象,就像Java中的垃圾收集器。
我听说在不支持垃圾回收的语言(比如C语言)中,程序可能存在内存泄漏,并最终耗尽内存。
那么,在不支持垃圾回收的语言(比如C语言)中,程序员会犯哪些错误呢?我猜测是没有在不再使用对象后释放它们。但是,由于缺少垃圾收集器,这是我们唯一可能犯的错误吗?
释放你需要的东西
不释放你不再需要的东西(因为你没有很好地跟踪分配/使用/释放)
重新分配已存在的实例(未正确跟踪的副作用)
释放已经释放过的内容
释放不存在的内容(空指针)
可能还有其他问题。关键是:管理内存很麻烦,最好使用某种跟踪机制和分配/释放抽象来处理。因此,最好将其构建到语言中,以使其变得简单易用。手动内存管理并不是世界末日—当然可以实现—但如今,除非您正在编写实时代码、硬件驱动程序或(也许)最新游戏的超级优化核心代码,否则除了作为学术练习外,手动管理内存并不值得付出努力。
free(NULL)
是完全安全的。 - Evan Teran当然,这通常不会被实现 - 部分原因是可以直接使用带垃圾回收功能的语言,但在我的看法中也部分是因为这将打破C++中的关键约定。你知道,许多C++代码(包括标准库)严重依赖资源分配即初始化(RAII)约定,而这需要可靠和及时的析构函数调用。在任何处理循环引用的GC中,你根本就不能保证它们的存在。在解决垃圾循环时,你无法确定要先调用哪个析构函数,因为可能会有更多的循环依赖关系,不仅仅是内存引用,甚至不可能解决。解决方案-在Java等语言中,不存在确保最终器会被调用的保证。垃圾回收只收集一种非常特定的垃圾 - 内存。所有其他资源都必须手动清理,就像在Pascal或C中一样,并且没有可靠的C++样式的析构函数作为优势。
最终结果是,在C++中“自动化”清理的许多工作必须在Java、C#等语言中手动完成。当然,“自动化”需要加引号,因为程序员负责确保对于任何堆分配对象都适当地调用delete - 但是在GC语言中,有不同但互补的程序员责任。无论哪种方式,如果程序员未能正确处理这些责任,就会出现错误。坦白地说,从非GC(垃圾回收)切换到GC(或反之亦然)并不是什么魔法棒。这可能会让通常的问题消失,但这只意味着您需要掌握新的技能来防止(和调试)一组全新的问题。
一个优秀的程序员应该超越“你站哪边”的无用争论,学会处理两种情况。
List<T>
这样的东西可能不需要... - supercatList<T>
的引用,而它期望某个其他对象将某些东西放入其中,而该其他对象已经放弃了该列表并开始使用另一个列表,那么这种情况可能更容易被检测到,如果拥有该列表的对象在放弃它之前使其无效,则可以避免此类问题。因此,应该将不再需要的对象“处理”掉。 - supercat好的,你可能会犯以下错误:
还有其他错误可以犯,但这些是与垃圾回收相关的错误。
malloc
分配内存后,需要手动调用free
来释放内存。尽管这听起来并不那么糟糕,但在处理指向相同数据的单独数据结构(例如链表)时,情况可能变得非常混乱。您可能会访问已释放的内存或重复释放内存,这两者都会导致错误,并可能引入安全漏洞。new[]/delete
和new/delete[]
。const char *getstr() { return "Hello, world!" }
很好,但是
const char *getstr() {
char x[BUF_SIZE];
fgets(x, BUF_SIZE, stdin);
return x;
}
这是一个非常糟糕的事情。
内存模型错误,例如:
指针错误,例如: