简体   繁体   English

为什么java和c#中有终结器?

[英]Why are there finalizers in java and c#?

I'm not quite understanding why there are finalizers in languages such as java and c#. 我不太明白为什么有java和c#等语言的终结器。 AFAIK, they: AFAIK,他们:

  • are not guaranteed to run (in java) 不保证运行(在java中)
  • if they do run, they may run an arbitrary amount of time after the object in question becomes a candidate for finalization 如果它们确实运行,它们可能会在有问题的对象成为最终确定的候选者之后运行任意时间量
  • and (at least in java), they incur an amazingly huge performance hit to even stick on a class. 并且(至少在java中),即使坚持上课,它们也会产生惊人的巨大性能。

So why were they added at all? 那他们为什么要加入呢? I asked a friend, and he mumbled something about "you want to have every possible chance to clean up things like DB connections", but this strikes me as a bad practice. 我问了一个朋友,他嘟something了一些关于“你想尽可能地清理像数据库连接这样的东西”的东西,但这对我来说是一种不好的做法。 Why should you rely on something with the above described properties for anything , even as a last line of defense? 你为什么要依赖具有上述属性的东西来做任何事情 ,即使是作为最后一道防线? Especially when, if something similar was designed into any API, said API would get laughed out of existence. 特别是当如果在任何API中设计类似的东西时,所述API将会被笑掉。

Well, they are incredibly useful, in certain situations. 嗯,在某些情况下,它们非常有用。

In the .NET CLR, for example: 在.NET CLR中,例如:

  • are not guaranteed to run 不保证会运行

The finalizer will always, eventually, run, if the program isn't killed. 如果程序没有被杀死,终结器将始终最终运行。 It's just not deterministic as to when it will run. 关于何时运行它并不确定。

  • if they do run, they may run an arbitrary amount of time after the object in question becomes a candidate for finalization 如果它们确实运行,它们可能会在有问题的对象成为最终确定的候选者之后运行任意时间量

This is true, however, they still run. 确实如此,它们仍在运行。

In .NET, this is very, very useful. 在.NET中,这非常非常有用。 It's quite common in .NET to wrap native, non-.NET resources into a .NET class. 在.NET中将原生的非.NET资源包装到.NET类中是很常见的。 By implementing a finalizer, you can guarantee that the native resources are cleaned up correctly. 通过实现终结器,您可以保证正确清理本机资源。 Without this, the user would be forced to call a method to perform the cleanup, which dramatically reduces the effectiveness of the garbage collector. 如果没有这个,用户将被迫调用一个方法来执行清理,这会大大降低垃圾收集器的效率。

It's not always easy to know exactly when to release your (native) resources- by implementing a finalizer, you can guarantee that they will get cleaned up correctly, even if your class is used in a less-than-perfect manner. 通过实现终结器来确定何时释放您的(本机)资源并不总是很容易,即使您的课程以不太完美的方式使用,您也可以保证它们能够得到正确的清理。

  • and (at least in java), they incur an amazingly huge performance hit to even stick on a class 并且(至少在java中),即使坚持上课,它们也会产生惊人的巨大性能

Again, the .NET CLR's GC has an advantage here. 同样,.NET CLR的GC在这里具有优势。 If you implement the proper interface ( IDisposable ), AND if the developer implements it correctly, you can prevent the expensive portion of finalization from occuring. 如果您实现了正确的接口( IDisposable ),并且如果开发人员正确实现了它,那么您可以防止发生最终的昂贵部分。 The way this is done is that the user-defined method to do the cleanup can call GC.SuppressFinalize , which bypasses the finalizer. 这样做的方法是用户定义的清理方法可以调用GC.SuppressFinalize ,它绕过终结器。

This gives you the best of both worlds - you can implement a finalizer, and IDisposable. 这为您提供了两全其美的优势 - 您可以实现终结器和IDisposable。 If your user disposes of your object correctly, the finalizer has no impact. 如果您的用户正确处理您的对象,则终结器没有任何影响。 If they don't, the finalizer (eventually) runs and cleans up your unmanaged resources, but you run into a (small) performance loss as it runs. 如果他们不这样做,终结器(最终)将运行并清理您的非托管资源,但在运行时会遇到(小)性能损失。

Hmya, you are getting a picture painted here that's a bit too rosy. Hmya,你得到的照片画得有点过于乐观。 Finalizers are not guaranteed to run in .NET either. 终结器也不能保证在.NET中运行。 Typical mishaps are a finalizer that throws an exception or a time-out on the finalizer thread (2 seconds). 典型的错误是终结器,它会在终结器线程上抛出异常或超时(2秒)。

That was a problem when Microsoft decided to provide .NET hosting support in SQL Server. 当Microsoft决定在SQL Server中提供.NET托管支持时,这是一个问题。 The kind of application where restarting the app to solve resource leaks isn't considered a viable workaround. 重新启动应用程序以解决资源泄漏的应用程序类型不被视为可行的解决方法。 .NET 2.0 acquired critical finalizers, enabled by deriving from the CriticalFinalizerObject class. .NET 2.0获得了关键终结器,通过派生CriticalFinalizerObject类来实现。 The finalizer of such a class must adhere to the rulez of constrained execution regions (CERs), essentially a region of code where exceptions are suppressed. 这样一个类的终结器必须遵守约束执行区(CERs)的规则,本质上是一个禁止异常的代码区域。 The kind of things you can do in a CER are very limited. 您在CER中可以做的事情非常有限。

Back to your original question, finalizers are necessary to release operating system resources other than memory. 回到最初的问题,终结器是释放内存以外的操作系统资源所必需的。 The garbage collector manages memory very well but doesn't do anything to release pens, brushes, files, sockets, windows, pipes, etc. When an object uses such a resource, it must make sure to release the resource after it is done with it. 垃圾收集器管理内存非常好,但没有做任何事情来释放笔,画笔,文件,套接字,窗口,管道等。当一个对象使用这样的资源时,它必须确保在完成后释放资源它。 Finalizers ensure that happens, even when the program forgot to do so. 即使程序忘记了,终结器也能确保发生这种情况。 You almost never write a class with a finalizer yourself, operating resources are wrapped by classes in the framework. 您几乎从不用自己的终结器编写类,操作资源由框架中的类包装。

The .NET framework also has a programming pattern to ensure such a resource is released early so the resource doesn't linger around until the finalizer runs. .NET框架还有一个编程模式,以确保尽早释放这样的资源,以便在终结器运行之前资源不会延续。 All classes that have finalizers also implement the IDisposable.Dispose() method, allowing your code to release a resource explicitly. 所有具有终结器的类也实现了IDisposable.Dispose()方法,允许您的代码显式释放资源。 This is often forgotten by a .NET programmer but that doesn't typically cause problems because the finalizer ensures it will eventually be done. 这通常被.NET程序员所遗忘,但这通常不会导致问题,因为终结器确保最终完成。 Many .NET programmers have lost hours of sleep worrying whether or not all Dispose() calls are taken care of and massive numbers of threads have been started about it on forums. 许多.NET程序员已经失去了几个小时的睡眠,担心是否所有的Dispose()调用都得到了解决,并且大量的线程已经在论坛上启动了。 Java folks must be a happier lot. Java人一定要快乐得多。


Following up on your comment: exceptions and timeouts in the finalizer thread is something that you don't have to worry about. 跟进您的评论:终结器线程中的异常和超时是您不必担心的事情。 Firstly, if you find yourself writing a finalizer, take a deep breath and ask yourself if you're on the Right Path. 首先,如果你发现自己正在写一个终结者,请深呼吸,问问自己是否在正确的道路上。 Finalizers are for framework classes, you should be using such a class to use an operating resource, you'll get the finalizer built into that class for free. 终结器适用于框架类,您应该使用这样的类来使用操作资源,您将免费获得内置于该类的终结器。 All the way down to the SafeHandle classes, they have a critical finalizer. 一直到SafeHandle类,他们都有一个关键的终结器。

Secondly, finalizer thread failures are gross program failures. 其次,终结器线程故障是严重的程序故障。 Similar to getting an OutOfMemory exception or tripping over the power cord and unplugging the machine. 类似于获取OutOfMemory异常或绊倒电源线并拔出机器。 There isn't anything you can do about them, other than fixing the bug in your code or re-route the cable. 除了修复代码中的错误或重新布线之外,您无法对它们做任何事情。 It was important for Microsoft to design critical finalizers, they can't rely on all programmers that write .NET code for SQL Server to get that code right. 微软设计关键终结器非常重要,他们不能依赖为SQL Server编写.NET代码的所有程序员来正确获取代码。 If you fumble a finalizer yourself then there is no such liability, it will be you that gets the call from the customer, not Microsoft. 如果你自己摸索终结者,那么就没有这样的责任,你将接到客户的电话,而不是微软。

If you read the JavaDoc for finalize() it says it is "Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the finalize method to dispose of system resources or to perform other cleanup." 如果您读取JavaDoc for finalize(),它表示“当垃圾收集确定没有对该对象的更多引用时,由对象上的垃圾收集器调用。子类重写finalize方法以处置系统资源或执行其他清理工作。“

http://java.sun.com/javase/6/docs/api/java/lang/Object.html#finalize http://java.sun.com/javase/6/docs/api/java/lang/Object.html#finalize

So that's the "why". 这就是“为什么”。 I guess you can argue whether their implementation is effective. 我想你可以争论他们的实施是否有效。

The best use I've found for finalize() is to detect bugs with freeing pooled resources. 我发现finalize()的最佳用途是通过释放池化资源来检测错误。 Most leaked objects will get garbage collected eventually and you can generate debug information. 大多数泄漏的对象最终会被垃圾收集,您可以生成调试信息。

class MyResource {
    private Throwable allocatorStack;

    public MyResource() {
        allocatorStack = new RuntimeException("trace to allocator");
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            System.out.println("Bug!");
            allocatorStack.printStackTrace();
        } finally {
            super.finalize();
        }
    }
 }

In java finalizers exist to allow for the clean up of external resources (things that exist outside of the JVM and can't be garbage collected when the 'parent' java object is). 在java中,存在终结器以允许清理外部资源(存在于JVM之外的东西,并且当'父'java对象存在时不能被垃圾收集)。 This has always been rare. 这一直是罕见的。 On example might be if you are interfacing with some custom hardware. 例如,如果您正在与某些自定义硬件连接。

I think the reason that finalizers in java aren't guaranteed to run is that they might not have a chance to do so at program termination. 我认为java中的终结器不能保证运行的原因是它们可能没有机会在程序终止时这样做。

One thing you might do with a finalizer in 'pure' java is use it to test termination conditions- for example to check that all connections are closed and report an error if they are not. 使用'纯'java中的终结器可能会做的一件事就是用它来测试终止条件 - 例如检查所有连接是否已关闭,如果不是则报告错误。 You aren't guaranteed that the error will be always caught but it will likely be caught at least some of the time which is enough to reveal a bug. 您无法保证始终会捕获错误,但可能至少会在某些时间内捕获到足以显示错误的错误。

Most java code has no call for finalizers. 大多数java代码都没有调用终结器。

They're meant for freeing up native resources (eg sockets, open files, devices) that can't be released until all references to the object have been broken, which is something that a particular caller would (in general) have no way of knowing. 它们用于释放本机资源(例如套接字,打开文件,设备),直到对象的所有引用都被破坏才能释放,这是特定调用者(通常)无法解决的问题。会心。 The alternative would be subtle, impossible-to-trace resource leaks... 替代方案是微妙的,不可能追踪的资源泄漏......

Of course, in many cases as the application author you'll know that there's only one reference to the DB connection (for example); 当然,在许多情况下,作为应用程序作者, 您将知道只有一个对DB连接的引用(例如); in which case finalizers are no substitute for closing it properly when you know you're finished with it. 在这种情况下,当你知道你已经完成它时,终结器不能替代它。

In .Net land, t is not guaranteed when they run. 在.Net land中,运行不保证t。 But they will run. 但他们会跑。

Are you refering to Object.Finalize? 你在引用Object.Finalize吗?

According to msdn, "In C# code, Object.Finalize cannot be called or overridden". 根据msdn,“在C#代码中,无法调用或覆盖Object.Finalize”。 In fact, they recommend using the Dispose method because it is more controllable. 实际上,他们建议使用Dispose方法,因为它更易于控制。

There's an additional complication with finalizers in .NET. .NET中的终结器还有一个复杂的问题。 If the class has a finalizer and does not get Dispose()'d, or Dispose() does not suppress the finalizer, the garbage collector will defer collecting until after compacting generation 2 memory (the last generation), so the object is "sort of" but not quite a memory leak. 如果类具有终结器并且没有得到Dispose()'d,或者Dispose()没有抑制终结器,则垃圾收集器将推迟收集,直到压缩第2代内存(上一代),因此对象是“排序” “但不是内存泄漏。 (Yes, it will get cleaned up eventually, but quite possibly not until application termination.) (是的,它最终会被清理干净,但很可能直到应用程序终止。)

As others have mentioned, if an object holds non-managed resources, it should implement the IDisposable pattern. 正如其他人所提到的,如果一个对象拥有非托管资源,它应该实现IDisposable模式。 Developers should be aware that if an object implements IDisposable, then it's Dispose() method should always be called. 开发人员应该知道,如果一个对象实现了IDisposable,那么应该始终调用它的Dispose()方法。 C# provides a way to automate this with the using statement: C#提供了一种使用using语句自动执行此操作的方法:

using (myDataContext myDC = new myDataContext())
{
    // code using the data context here
}

The using block automatically calls Dispose() on block exit, even exits by return or exceptions being thrown. using块在块退出时自动调用Dispose(),甚至通过抛出返回或异常退出。 The using statement only works with objects that implement IDisposable. using语句仅适用于实现IDisposable的对象。

And beware another confusion point; 并且要注意另一个混乱点; Dispose() is an opportunity for an object to release resources, but it does not actually release the Dispose()'d object. Dispose()方法是对象释放资源的机会,但它实际上并没有释放的Dispose()'d对象。 .NET objects are elligible for garbage collection when there are no active references to them. 当没有对它们的活动引用时,.NET对象可以进行垃圾收集。 Technically, they can't be reached by any chain of object references, starting from the AppDomain. 从技术上讲,从AppDomain开始,任何对象引用链都无法访问它们。

The equivalent of destructor() in C++ is finalizer() in Java. C ++中destructor()的等价物是Java中的finalizer()

They are invoked when the life cycle of an object is about to end. 当对象的生命周期即将结束时调用它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM