为什么Java没有像C ++这样的析构函数?

时间:2010-04-09 10:10:21

标签: java destructor

Java有自己的垃圾收集实现,因此它不需要像C ++这样的任何析构函数。这使得Java开发人员懒得实现内存管理。

我们仍然可以使用析构函数和垃圾收集器,开发人员可以在其中释放资源并且可以节省垃圾收集器的工作。这可以提高应用程序的性能。为什么Java不提供任何析构函数的机制?

开发人员无法控制GC,但他/她可以控制或创建对象。然后为什么不给他们破坏对象的能力呢?

10 个答案:

答案 0 :(得分:55)

你断言“垃圾收集非常昂贵” - 你能用证据支持吗?垃圾收集当然不是 free ,但现代垃圾收集器非常好。

请注意,GC能够高效的方法之一是知道它是进行内存分配和释放(对于托管对象)的唯一方法。允许开发人员显式释放对象可能会妨碍这种效率。如果开发人员试图“使用”一个释放的对象,你还需要担心会发生什么:

Foo f = new Foo();
Foo g = f;
free(f); // Or whatever
System.out.println(g.toString()); // What should this do?

您是否建议每个对象都应该有一个额外的标志“是否已明确释放”,需要在每次取消引用时进行检查?说实话,这就像是灾难的秘诀。

你是对的 - 它确实允许Java开发人员在这个领域懒惰。这是好事。 IDE也允许开发人员变得懒惰 - 高级语言等也是如此。等待内存分配的懒惰使得托管环境中的开发人员能够花费精力来解决业务问题而不是内存管理。

答案 1 :(得分:22)

  

垃圾收集非常昂贵。

事实上,对于复杂的应用程序,垃圾收集的性能与基于malloc / free手动存储管理竞争。 Benjamin Zorn有一篇经典论文清楚地证明了这一点。在本文中,Zorn描述了他如何修改一些大型堆密集型应用程序以使用保守的垃圾收集器而不是mallocfree。然后他对应用程序的原始版本和修改版本进行了基准测试。结果是相当的表现。

本文发表于1993年的软件实践和经验。如果你还没有阅读,你没有资格就垃圾收集的“低效率”发表声明。

请注意,这项研究是使用1993年代的保守垃圾收集器完成的。一个保守的收藏家是没有任何压缩的标记扫描;即非垃圾对象不移动。后者意味着为新对象分配空间与malloc一样缓慢而复杂。相比之下,现代垃圾收集器(例如Java 6/7)是分代式复制收集器,其效率更高。由于复制压缩剩余的非垃圾对象,因此分配速度更快。这使GC更具竞争力......如果能找到一种方法进行比较。


  

开发人员无法控制GC,但他/她可以控制或创建对象。那为什么不给它们破坏物体的能力呢?

这取决于“破坏”究竟是什么意思。

  • 在Java中,您可以分配null。在某些情况下,这可能会加速对象的破坏。

  • 在Java中,你可以使用终结器和Reference类型来注意一个对象即将被销毁......等等。

  • 在Java中,您可以在任何对象上定义close()(或等效)方法,并让它执行适当的操作。然后明确地调用它。

  • 在Java 7中,您可以使用“try with resources”构造在示波器出口上的资源上自动调用close()

但是,您现在无法删除Java对象。不允许这样做的原因是它允许程序创建悬空引用,这可能导致堆损坏和随机JVM崩溃。

这不是Java方式。理念是编写可靠的程序比效率更重要。虽然Java的某些方面没有遵循这一点(例如线程),但没有人想要随机JVM崩溃的可能性。

答案 2 :(得分:7)

C ++析构函数不是一种破坏对象的方法 - 它是在对象被破坏时要完成的一组操作。在Java中,您无法控制对象被破坏的时间(它们甚至可能永远不会被破坏),因此强烈建议不要在对象破坏时执行任何重要代码(尽管可能 - finalize方法)。如果你不是要求析构函数,而是要求一种明确销毁给定对象的方法,那么你就是在你的代码中引用悬空引用。他们不受欢迎。

答案 3 :(得分:5)

  

这使得Java开发人员懒惰   实施内存管理。

不,它会释放它们以执行有用的工作。

  

垃圾收集非常好   昂贵。

相比什么?事实呢?数据?这段话你已经过了20年了。仅仅采用Java就有效地反驳了这种争论。

  

这可能会提高应用程序的性能。

或不。你有一些事实要引用吗?

  

那为什么不给他们破坏对象的能力呢?

因为不需要吗?

答案 4 :(得分:4)

在C ++中对象被销毁时会调用析构函数,不要销毁对象。如果您想保证清理,请让用户调用Destroy方法或类似方法。

答案 5 :(得分:1)

如果您知道不再使用某些大型对象,只需将对它们的引用设置为null即可。这可能会加速这些对象的垃圾收集。

答案 6 :(得分:1)

  

C ++析构函数不是一种破坏对象的方法 - 它是在对象被破坏时要完成的一组操作。

我认为你的术语令人困惑。我就是这样看的:

创建对象=首先分配内存,然后通过构造函数构建

通过析构函数

销毁 object = first destruct ,然后释放内存

如何分配和取消分配内存取决于。如果您使用newdelete,则内存管理由void* operator new(size_t)void operator delete(void*)完成。

答案 7 :(得分:0)

C ++析构函数可用于释放对象拥有的任何资源,而不仅仅是内存。它可能是文件,套接字,互斥锁,信号量或任何其他资源句柄。使用析构函数是防止资源泄漏的明智方法。将资源处理包装在C ++类中,并创建一个释放任何已分配资源的析构函数。我在Java中看不到任何这样的方法。您必须明确释放资源,如果有许多可能的退出路径,这可能会很棘手。

答案 8 :(得分:0)

不,java不支持析构函数。所有释放内存任务都由GARBAGE COLLECTOR完成。

Java使用垃圾收集器拥有自己的内存管理功能。当您使用finalize()时,该对象可用于垃圾回收,您不需要显式调用析构函数。 C#和Java不希望你担心析构函数,因为它们具有垃圾收集功能。

Java是一种字节码语言,它具有非常强大的垃圾检测功能。如果你允许人们定义他们自己的析构函数,他们可能会犯一些错误。通过自动化该过程,Java打算防止这些错误。

答案 9 :(得分:-2)

您确实能够在Java中控制对象销毁。它只是使用不同的习语:

Connection conn = null;
try {
  conn = ...
  // do stuff
} finally {
  try { conn.close(); } catch (Exception e) { }
}

你可以在这一点上指出这不是对象破坏,例如,你可以将该对象传递给其他东西,并且仍然可以引用它。你是对的。它就像Java(和大多数托管平台)一样接近。

但是,正如你所说,没有Java没有像C ++那样的析构函数机制。有些人认为终结者是错误的。它们不是不是析构函数,因此使用它们是危险的。

程序员的内存管理很难。您可以轻松泄漏内存,尤其是在执行多线程编程时(也很难)。经验表明,GC的成本虽然是真实的,有时是实质性的,但在绝大多数情况下,生产率提高和错误发生率都是合理的,这就是为什么绝大多数平台现在都是“管理”的(这意味着他们使用垃圾收集)。