首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >这种对GC.SuppressFinalize()的使用感觉不对

这种对GC.SuppressFinalize()的使用感觉不对
EN

Stack Overflow用户
提问于 2013-03-22 00:05:03
回答 3查看 2.6K关注 0票数 10

我在使用供应商库时遇到了一些问题,有时由库计算的实体会为null,而它应该始终包含有效的数据。

功能代码(与供应商调试问题后)大致如下:

代码语言:javascript
复制
    Task.Factory.StartNew(() => ValidateCalibration(pelRectRaw2Ds, crspFeatures, Calibration.Raw2DFromPhys3Ds));

    .....

    private void ValidateCalibration(List<Rectangle> pelRectRaw2Ds, List<List<3DCrspFeaturesCollection>> crspFeatures, List<3DCameraCalibration> getRaw2DFromPhys3Ds)
    {
        var calibrationValidator = new 3DCameraCalibrationValidator();

        // This is required according to vendor otherwise validationResultsUsingRecomputedExtrinsics is occasionally null after preforming the validation
        GC.SuppressFinalize(calibrationValidator);

        3DCameraCalibrationValidationResult validationResultUsingOriginalCalibrations;
        3DCameraCalibrationValidationResult validationResultsUsingRecomputedExtrinsics;
        calibrationValidator.Execute(pelRectRaw2Ds, crspFeatures, getRaw2DFromPhys3Ds, out validationResultUsingOriginalCalibrations, out validationResultsUsingRecomputedExtrinsics);

        Calibration.CalibrationValidations.Add(new CalibrationValidation
            {
                Timestamp = DateTime.Now,
                UserName = Globals.InspectionSystemObject.CurrentUserName,
                ValidationResultUsingOriginalCalibrations = validationResultUsingOriginalCalibrations,
                ValidationResultsUsingRecomputedExtrinsics = validationResultsUsingRecomputedExtrinsics
            });
    }

验证过程是一个相当耗时的操作,因此我将其交给一个任务。我遇到的问题是,最初我没有调用GC.SuppressFinalize(calibrationValidator),当应用程序从发布版本运行时,out参数validationResultsUsingRecomputedExtrinsics将为null。如果我从Debug构建运行应用程序(无论是否附带了调试器),那么validationResultsUsingRecomputedExtrinsics将包含有效的数据。

我不完全理解GC.SuppressFinalize()在这种情况下做了什么,也不完全理解它是如何解决问题的。我所能找到的关于GC.SuppressFinalize()的所有信息都是在实现IDisposable时使用的。我在“标准”代码中找不到它的任何用途。

如何/为什么添加对GC.SuppressFinalize(calibrationValidator)的调用来解决这个问题?

我知道,如果不对供应商库的内部结构有深入的了解,就不可能确切地知道,但是任何洞察力都会有所帮助。

应用程序是用VS2012编译的,目标是.NET 4.0。该供应商库要求在useLegacyV2RuntimeActivationPolicy=中指定app.config“true”选项。

这就是我从供应商那里得到的理由:

SuppressFinalize命令确保垃圾收集器不会“提前”清理垃圾。由于某种原因,您的应用程序有时会让垃圾收集器在您真正使用垃圾收集器之前对其进行一些热心和清理;它几乎可以肯定是与范围相关的,而且可能是由于多线程导致了calibrationValidator范围上的混乱。下面是我从工程部得到的答复。 因为变量是在本地作用域中创建的,而且该函数在后台线程中运行,垃圾收集运行在主线程中,而且垃圾收集在处理多线程情况时似乎不够聪明。有时,它只是过早地释放它(验证器的内部执行还没有完成,并且仍然需要这个变量)。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-03-22 03:24:42

这很可能是解决早期垃圾收集问题的一次黑客攻击。非托管代码并不少见,在相机应用程序中是典型的。这不是一个健康的黑客,很好的机会,这将导致资源泄漏,因为终结器不执行。非托管代码的包装器在终结器中几乎总是有一些事情要做,它们需要释放非托管内存是非常常见的。

问题在于,在运行非托管代码时,可以垃圾收集calibrationValidator对象。程序中有另一个线程使得这很可能发生,因为另一个线程可以分配对象并触发GC。在测试过程中,代码的所有者很容易忽略这一点,要么是在使用多线程时从未测试过它,要么是没有足够的运气在错误的时间触发GC。

正确的解决方法是确保抖动标记在调用之后使用的对象,这样垃圾收集器就不会收集它。为此,您可以在GC.KeepAlive(calibrationValidator)调用之后添加Execute()

票数 17
EN

Stack Overflow用户

发布于 2013-03-22 01:52:39

说到理解IDisposableGC.SuppressFinalize和C#中的终结器,我认为没有比下面这篇文章更好的解释了。

DG更新:释放、最后确定和资源管理

好了!这是:修改后的“处置、最后确定和资源管理”设计指南条目。我之前提到过这项工作-- 这里这里。在大约25页印刷页,这不是我认为是一个小的更新。我花了比预期更长的时间,但我对结果很满意。我和HSutter,BrianGru,CBrumme,Jeff和其他几个人一起工作,得到了很多反馈.玩得很开心。

这个问题的主要概念是:

很明显,GC.SuppressFinalize()只应该在this上调用,因此本文甚至没有直接提到这一点。但是,它确实提到了包装可终结对象以将其与公共API隔离的做法,以确保外部代码不能对这些资源调用GC.SuppressFinalize() (请参阅下面的引号)。无论是谁设计了原始问题中描述的库,都无法掌握.NET作品中的终结方式。

引用自博客文章:

即使没有上面提到的罕见情况之一,具有可公开访问引用的可终结对象也可能被任意不受信任的调用方阻止其终结。具体来说,他们可以对您调用GC.SuppressFinalize,并防止完全完成,包括关键的终结。解决这一问题的一个好的缓解策略是将关键资源封装在具有终结器的非公共实例中。只要您不将此消息泄露给调用方,他们就无法阻止终结。如果您迁移到类中使用SafeHandle,并且从不在类之外公开它,那么您可以保证您的资源最终确定下来(有上面提到的注意事项,并假设有正确的SafeHandle实现)。

票数 3
EN

Stack Overflow用户

发布于 2013-03-22 18:50:08

有一些提到多线程或本机代码是造成此问题的原因。但同样的事情也可以发生在一个纯粹的管理和主要是单线程程序。

考虑以下方案:

代码语言:javascript
复制
using System;

class Program
{
    private static void Main()
    {
        var outer = new Outer();
        Console.WriteLine(outer.GetValue() == null);
    }
}

class Outer
{
    private Inner m_inner = new Inner();

    public object GetValue()
    {
        return m_inner.GetValue();
    }

    ~Outer()
    {
        m_inner.Dispose();
    }
}

class Inner
{
    private object m_value = new object();

    public object GetValue()
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        return m_value;
    }

    public void Dispose()
    {
        m_value = null;
    }
}

在这里,在调用outer.GetValue()时,outer将被垃圾收集和完成(至少在发布模式下是这样)。终结器将取消Inner对象的字段,这意味着GetValue()将返回null

在实际代码中,很可能不会有GC调用。相反,您将创建一些托管对象,这将导致垃圾收集器运行。

(我说过这段代码大多是单线程的。事实上,终结器将在另一个线程上运行,但是由于对WaitForPendingFinalizers()的调用,它几乎就像运行在主线程上一样。)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15561025

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档