问题标签 [finalization]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
8 回答
1683 浏览

c# - 终结器在其对象仍在使用时启动

摘要: C#/.NET 应该是垃圾收集。C# 有一个析构函数,用于清理资源。当对象 A 在我尝试克隆其变量成员之一的同一行被垃圾收集时会发生什么?显然,在多处理器上,有时垃圾收集器会获胜......

问题

今天,在 C# 培训课程中,老师向我们展示了一些代码,这些代码只有在多处理器上运行时才会包含错误。

我会总结说,有时,编译器或 JIT 在从其调用的方法返回之前调用 C# 类对象的终结器会搞砸。

Visual C++ 2005 文档中给出的完整代码将作为“答案”发布,以避免提出非常大的问题,但基本内容如下:

下面的类有一个“哈希”属性,它将返回一个内部数组的克隆副本。在构造函数中,数组的第一项值为 2。在析构函数中,它的值设置为零。

关键是:如果您尝试获取“示例”的“哈希”属性,您将获得数组的一个干净副本,其第一项仍然是 2,因为正在使用该对象(因此,不是垃圾收集/完成):

但没有什么比这更简单了......使用这个类的代码在一个线程中运行,当然,对于测试,该应用程序是高度多线程的:

DoWork 静态方法是发生问题的代码:

显然,每执行 1,000,000 次 DoWork,垃圾收集器就会施展魔法,并尝试回收“ex”,因为它不再在函数的剩余代码中被引用,而这一次,它比“Hash”更快获取方法。所以我们最终得到的是一个零字节数组的克隆,而不是正确的(第一项在 2)。

我的猜测是代码的内联,它基本上将 DoWork 函数中标记为 [1] 的行替换为以下内容:

如果我们假设 Hash2 是一个简单的访问器,编码如下:

所以,问题是:这是否应该在 C#/.NET 中以这种方式工作,或者这是否可以被视为 JIT 编译器的错误?

编辑

有关解释,请参阅 Chris Brumme 和 Chris Lyons 的博客。

http://blogs.msdn.com/cbrumme/archive/2003/04/19/51365.aspx
http://blogs.msdn.com/clyon/archive/2004/09/21/232445.aspx

每个人的答案都很有趣,但我无法选择一个比另一个更好的答案。所以我给了你们一个+1...

对不起

:-)

编辑 2

尽管在相同条件下使用相同的代码(多个相同的可执行文件同时运行、发布模式等),但我无法在 Linux/Ubuntu/Mono 上重现该问题

0 投票
1 回答
3697 浏览

c# - C# - “析构函数不被继承”实际上是什么意思?

C# 语言规范 3.0的第 10.13 节,析构函数声明如下:

析构函数不是继承的。因此,除了可以在该类中声明的析构函数之外,一个类没有析构函数。

C# 编程指南的析构函数部分包含一个示例,演示如何调用继承层次结构中的析构函数,包括以下语句:

... ...类的析构函数被自动调用,并且按从最派生到最不派生的顺序。

我已经通过各种实际示例对此进行了研究,包括一个具有定义析构函数的基类,以及从基类继承但未定义析构函数的派生类。创建派生类的实例,允许对该实例的所有引用超出范围,然后强制进行垃圾回收,这表明当派生类的实例最终确定时,将调用基类中定义的析构函数。

我的问题是“不继承析构函数”实际上是什么意思,因为虽然你不能显式调用析构函数,但继承链中的析构函数会自动调用,并且即使派生类没有定义析构函数也会调用基类析构函数?

它是否与一些微妙的语义区别有关,即终结是由垃圾收集器而不是 C# 语言/编译器实现的?

编辑1:

虽然 C# 语言规范还声明“实例构造函数不被继承”,但与构造函数相关的行为与析构函数有很大不同,并且更适合 IMO 与“不继承”术语,如下例所示:

与析构函数相关的行为与此非常不同,如以下示例所示,该示例通过仅向基类添加析构函数来扩展前面的构造函数示例。

上面的示例演示了当派生类的实例完成时将调用基类构造函数,即使派生类没有显式定义析构函数。

我的观点很简单,我遇到过很多人没有意识到或不理解会发生这种情况,其中很大一部分原因是“析构函数不是继承的”声明。

编辑2:

C# 语言规范还说明了以下内容并提供了底层实现的代码示例:

析构函数是通过覆盖 System.Object 上的虚拟方法 Finalize 来实现的。不允许 C# 程序覆盖此方法或直接调用它(或覆盖它)。

由于引擎盖下的实现实际上是基于继承的,如上所述,我认为我的问题是有效的,我认为到目前为止我收到的任何回复都没有正确解决这个问题 - 什么“析构函数不是继承的”实际上是什么意思?

0 投票
6 回答
15345 浏览

delphi - 德尔福和定稿在一个单元

我有两个单元 unitA 和 unitB。TFoo 类在 unitB 中声明。

在 unitA 的最终确定中调用 B.Free 是否总是安全的?

它如何取决于 unitA 和 unitB 在 dpr 中的顺序?

执行 unitA 最终确定时,我可以确定 unitB 存在吗?

0 投票
5 回答
25062 浏览

java - Java中终结的目的是什么?

我对定稿的理解是这样的:

为了清理或回收对象占用的内存,垃圾收集器开始行动。(自动被调用?)

然后垃圾收集器取消引用该对象。有时,垃圾收集器无法访问对象。然后调用 finalize 进行最终清理处理,之后可以调用垃圾收集器。

这是对最终确定的准确描述吗?

0 投票
4 回答
2956 浏览

delphi - 应用程序的单元完成顺序,使用运行时包编译?

我需要在完成 SysUtils 单元后执行我的代码。

我已将代码放在单独的单元中,并将其首先包含在 dpr 文件的 uses 子句中,如下所示:

MyUnit 看起来像这样:

请注意,MyUnit 没有任何用途。

这是通常的 Windows exe,没有控制台,没有表单,并使用默认运行时包编译。MyUnit 不是任何包的一部分(但我也尝试从包中使用它)。

我希望 MyUnit 的最终化部分将在 SysUtils 的最终化部分之后执行。这是德尔福的帮助告诉我的。

然而,这并非总是如此。

我有 2 个测试应用程序,它们在使用中列出的测试例程/dpr 文件和单元中的代码略有不同。然而,MyUnit 在所有情况下都排在首位。

一个应用程序按预期运行:Halt0 -> FinalizeUnits -> ...其他单元... -> SysUtils's finalization -> MyUnit's finalization -> ...其他单元...

但第二个不是。MyUnit 的终结在 SysUtils 的终结之前被调用。实际的调用链如下所示:Halt0 -> FinalizeUnits -> ...其他单元... -> SysUtils 的完成(跳过) -> MyUnit 的完成 -> ...其他单元... -> SysUtils 的完成(执行)

这两个项目的设置非常相似。我尝试了很多来消除/最小化它们的差异,但我仍然看不出这种行为的原因。

我尝试对此进行调试并发现:似乎每个单元都有某种引用计数。似乎 InitTable 包含对同一单元的多次引用。当 SysUtils 的 finalization 部分第一次被调用时——它改变了引用计数器并且什么都不做。然后执行 MyUnit 的 finalization。然后再次调用 SysUtils,但这次 ref-count 达到零并执行 finalization 部分:

任何人都可以解决这个问题吗?我错过了什么吗?

0 投票
2 回答
2339 浏览

vb.net - VB中的内存泄漏

我们注意到一个关于 VB 内存管理的有趣问题,我们不理解。如果有人可以帮助我们解决这个问题,请这样做。

我们有一个简单的类,只有一个事件。我们创建和销毁该类的 5000 个实例,并在运行测试之前读取进程内存使用情况。最后,我们强制 GC 并再次检查内存。我们注意到,我们有一个不断增长的内存。我们在 C# 中做了相同的示例,但没有遇到这个问题。现在这里是有线点。如果我们从类中省略事件声明,内存将按预期清理。有谁知道为什么,以及完成这门课的正确方法是什么。

这是示例代码:

和测试类:

多次运行后的打印输出:

0 投票
1 回答
1099 浏览

wpf - WPF 应用程序初始化和完成

我正准备编写一个使用 ICE(互联网通信引擎)中间件的 WPF 客户端应用程序。ICE 需要正确的初始化和完成。所有的例子都展示了如何在一个普通的控制台应用程序中实现这一点——这很容易,因为你只需要 try-finally 块并在其中做一些事情。

WPF 呢?我怎样才能确定某些代码将被调用,无论如何,最终确定应用程序会发生什么?

0 投票
0 回答
194 浏览

java-native-interface - JNI 非 Java 类成员

我想针对一些具有 C 接口的第三方库创建一个 Java 包装器。该库在一个复杂的上下文实体上运行,该实体本质上是一个 C++ 对象(C++ 在该库内部使用,但 API 是纯 C 语言)。将此实体包装到可从 Java 访问的类中是很自然的。为此,应将指向 Context 的指针存储在某处。

我看到两个选项可以做到这一点:

  • 在 Java 端声明一个新成员(例如,只要)并将其转换为 JNI 方法实现中的指针类型
  • 在 JNI 标头中声明一个新成员(如果 Java 依赖于 javah 为我生成的结构的大小,这可能是非法的)

所有关于 JNI 的教程都太简单了,无法提示我如何用 Java 类包装复杂的实体,感谢任何有关更详细文档的链接。

我还想知道在哪里调用上下文销毁函数(内部的 C++ 析构函数)我不想为此使用 Java finalize,因为 Java 不支持 finalize 方法,我怀疑有一种方法可以定义销毁过程在本地。

0 投票
4 回答
314 浏览

java - 我很确定 finalize 在以后的 JVM 上仍然是个坏消息——还有其他选择吗?

我想实现一个 ORM 风格的系统,当调用者不再可以访问 POJO 时,它可以保存对 POJO 的更新。

我认为引用类可以做到这一点,但它们似乎只在对象被清除后才将引用加入队列(我希望是在它们能够被收集的时候),所以一旦加入队列, .get() 方法将始终返回无效的。

我可以使用终结器,但上次我检查这些是有问题的(不能保证立即运行或根本运行)--我相信终结器和 runShutdownHook() 的组合会起作用,但这进入了相当混乱的领域。

除了强制性的“当他完成后让调用者调用 .save() ”之外,还有其他我没有想到的路径吗?

0 投票
3 回答
2270 浏览

java - 定义:未完成的对象与可完成的对象

为了理解 Java 中的弱引用,我不得不查阅 Java 语言规范。来自第 12.6 节的以下部分让我感到困惑:

未终结的对象从未自动调用其终结器;已完成的对象已自动调用其终结器。可终结对象从未自动调用其终结器,但 Java 虚拟机最终可能会自动调用其终结器。

那么 unfinalized 和 finalizable 对象之间的正式区别是什么?从引用看来,如果 unfinalized 和 finalizable 要不同,那么对于一个 unfinalized 对象,JVM 最终可能会调用它的终结器是不正确的。有点混乱,或者我还有一些英语语义要研究;)

链接到 Java 规范中的部分:实现终结