【问题标题】:Why does Java not have any destructor like C++? [closed]为什么Java没有像C++那样的析构函数? [关闭]
【发布时间】:2011-02-06 02:01:15
【问题描述】:

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

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

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

【问题讨论】:

  • 你的问题也有答案。 java执行垃圾回收而不是析构函数
  • -1 表示关于垃圾收集的未经证实的声明。
  • @Abhishek:如果这是你唯一的目标,那么你应该避免提出这种有争议的、未经证实的主张。
  • “这让 Java 开发人员在实现内存管理时变得懒惰”这完全是垃圾。当您不了解自己在做什么时,您可以像在 C++ 中一样懒惰。有能力的 Java 开发人员也能够管理内存(通过防止泄漏),只是不是明确的。
  • 仅仅因为 Java 更易于开发并且上市时间更快,并不意味着 Java 开发人员是懒惰的,只是更有效率。 ;)

标签: java destructor


【解决方案1】:

您在断言“垃圾收集非常昂贵”——您能用证据证明这一点吗?垃圾收集当然不是免费,但现代垃圾收集器非常好。

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

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

您是否建议每个对象都应该有一个额外的标志“是否已显式释放”,需要在每次取消引用时检查?老实说,这感觉就像是一场灾难。

你是对的 - 它确实允许 Java 开发人员在这方面偷懒。这是好事。 IDE 也允许开发人员变得懒惰 - 高级语言等也是如此。内存分配方面的懒惰让托管环境中的开发人员将精力花在担心业务问题而不是内存管理上。

【讨论】:

  • 我认为开发人员天生就是懒惰的(以一种好的方式)!
  • free 也不是免费的
  • @TomHawtin-tackline 正如一个人所说,如果有东西是免费的,那就意味着你就是产品:)
  • @JamesB “我选择一个懒惰的人来做艰苦的工作。因为懒惰的人会找到一个简单的方法来做这件事。” - 比卢斯·加图斯。
  • 您提出了一个很好的案例,它允许 Java 开发人员在这方面懒惰,因此他们可以将时间花在其他事情上。但是您没有回答为什么禁止在需要时故意破坏对象。
【解决方案2】:

垃圾收集非常昂贵。

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

这篇论文发表于 1993 年的 Software Practice and Experience,如果你没有读过,你没有资格对垃圾收集的“低效率”发表意见。

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


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

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

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

  • 在 Java 中,您可以使用终结器和 Reference 类型来通知对象即将被销毁......等等。

  • 在 Java 中,您可以在任何对象上定义 close()(或等效)方法并让它做一些适当的事情。然后显式调用它。

  • 在 Java 7 中,您可以使用“try with resources”构造在范围退出时对资源自动调用 close()

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

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

【讨论】:

  • @Abhishek:如果您期待一个与您的断言有争议的答案,您为什么一开始就将其作为断言包含在内?为什么不直接问“垃圾收集贵吗?” IMO,这将不那么有争议。
  • 实际上,我并没有只是对这个断言提出异议。我还提供了(IMO)反驳的确凿证据的参考。
  • 致随机匿名投票者:请随时解释为什么您认为这是一个错误的答案。
【解决方案3】:

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

【讨论】:

  • C++ 不会让您“无法控制对象被破坏的时间”。 C++ 使用确定性终结。当你删除一个对象时,它的析构函数被立即调用,然后它的存储空间被立即释放。没有 C++ GC。我并不是说这一定是一件好事,但它是 C++ 的工作原理。
  • @andyjohnson:要么你误解了我,要么我的语法被破坏了。无论如何,我试图修复它。 Java 中无法控制对象被破坏的时间。在 C++ 中,您可以完全控制它(对临时生命周期的复杂规则取模)。
  • @andyjohnson:存储立即不可用。实际的解除分配可能会在以后发生 - 批处理并非闻所未闻。
  • @andy 当然有一个用于 C++ 的垃圾收集器,它只是不标准:hpl.hp.com/personal/Hans_Boehm/gc
【解决方案4】:

这让 Java 开发者懒惰 实现内存管理。

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

而且垃圾回收非常 很贵。

与什么相比?事实?图?你的那句话已经过时了大约 20 年。仅使用 Java 就有效地反驳了这一论点。

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

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

那为什么不给他们破坏物体的能力呢?

因为不需要?

【讨论】:

  • "这不是必需的" 那么,你怎么能调用你的清理代码呢? Java 对 finalize() 没有任何保证。您可以在构造函数中进行所有初始化,但对于清理 activities,Java 什么也不提供。
  • @foo 相反。 Java 提供 finally {} 块。
  • 如果您坚持冗长:Java 对 finalize() 方法不做任何保证,并且在可靠的对象销毁方面没有任何提供。 (是的,在控制块级别,有 finally 块 - 这对于对象生命周期几乎没有用。)
  • @foo 您的第一条评论的答案是,您可以在 finally {} 块中调用清理代码,这就是 Java 在“可靠对象销毁”方面提供的。你不能一直假装它不存在。
  • @foo 怀疑大多数 Java 程序员没有遇到过使用析构函数有用的情况。我个人最喜欢的是在记录/跟踪中编写这样的代码:{ FN_ENTER(FUNCTION);做一点事(); FN_ENTER 宏创建一个新对象,该对象导致“输入的 ”被记录/跟踪。当该对象超出范围时,析构函数会导致“Exited ”被记录/跟踪。它使您不必在退出时显式调用 FN_EXIT,并且它可以防止不匹配的日志,因为即使您提前返回,析构函数也会被调用 = win。
【解决方案5】:

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

【讨论】:

  • 您不能使用户调用 Destroy() 方法。如果您需要控制以正确释放与对象关联的内部资源,那么您需要一些隐式调用的 for of 方法(如析构函数)。
  • @jarmod 如果用户不调用 Destroy(),那么他会泄漏资源。我的意思是 .NET 中的 IDispose。你调用Dispose() 来显式地处理一个对象。
【解决方案6】:

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

【讨论】:

  • 很少有好主意,IMO。在大多数情况下,垃圾收集器足够聪明,可以为您解决这个问题,但它会弄乱您的代码。有一些例外,但根据我的经验,它们并不常见。
  • 没错。大多数时候你不需要这个,但是对于那些认为他可以从java中欺骗一些东西来做一些事情比如释放内存的程序员来说,这是一种可能性。
  • 它至少表明你想要什么,即使 Java 忽略它或优化它。而且它会让更多的实现错误抛出异常,这是一件好事。 GC 的加速是可能的,具体取决于 VM 实现使用的算法,但这不应该成为这样做的理由。
【解决方案7】:

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

我认为您混淆了术语。这是我的看法:

create object = 先分配内存,然后construct通过构造函数

destroy object = 首先通过析构函数destruct,然后释放内存

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

【讨论】:

    【解决方案8】:

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

    【讨论】:

      【解决方案9】:

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

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

      Java 是一种字节码语言,它具有很强的垃圾检测能力。如果您允许人们定义自己的析构函数,那么他们很可能会犯一些错误。通过自动化流程,Java 旨在防止这些错误。

      【讨论】:

        【解决方案10】:

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

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

        此时您可以指出这不是对象破坏,例如,您可以将该对象传递给其他对象,并且仍然具有对它的引用。你在这两个方面都是对的。它与 Java(和大多数托管平台)一样接近。

        但是,正如您所说,没有 Java 没有 C++ 中的析构函数机制。有些人将终结器误认为是这个。它们不是析构函数,这样使用它们很危险。

        程序员的内存管理很困难。您很容易泄漏内存,尤其是在进行多线程编程时(也很难)。经验表明,GC 的成本虽然真实且有时相当可观,但在绝大多数情况下,在生产力提高和错误发生率方面是完全合理的,这就是为什么现在绝大多数平台都是“托管”的(意味着他们使用垃圾收集)。

        【讨论】:

        • 这不会破坏 object - 它会释放一些与对象关联的资源(可能是底层操作系统句柄),但不会释放对象本身。
        猜你喜欢
        • 1970-01-01
        • 2011-11-01
        • 2010-09-24
        • 1970-01-01
        • 2010-09-05
        • 1970-01-01
        • 2015-02-04
        • 1970-01-01
        相关资源
        最近更新 更多