在 Finalizer 中处置 MemoryCache 会引发 AccessViolationException

2023-12-25

EDIT有关更多详细信息,请参阅问题底部的编辑注释。

原问题

我有一个 CacheWrapper 类,它创建并保存 .NET 的实例MemoryCache内部类。

MemoryCache将自身挂钩到 AppDomain 事件中,因此除非显式处置,否则它永远不会被垃圾收集。您可以使用以下代码验证这一点:

Func<bool, WeakReference> create = disposed => {
    var cache = new MemoryCache("my cache");
    if (disposed) { cache.Dispose(); }
    return new WeakReference(cache);
};

// with false, we loop forever. With true, we exit
var weakCache = create(false);
while (weakCache.IsAlive)
{
    "Still waiting...".Dump();
    Thread.Sleep(1000);
    GC.Collect();
    GC.WaitForPendingFinalizers();
}
"Cleaned up!".Dump();

由于这种行为,我认为我的 MemoryCache 实例应该被视为非托管资源。换句话说,我应该确保它在 CacheWrapper 的终结器中被处置(CacheWrapper 本身是 Disposable 遵循标准 Dispose(bool) 模式)。

但是,我发现当我的代码作为 ASP.NET 应用程序的一部分运行时,这会导致问题。卸载应用程序域时,终结器在我的 CacheWrapper 类上运行。这反过来又试图处置MemoryCache实例。这就是我遇到问题的地方。看起来Dispose尝试从 IIS 加载一些配置信息,但失败了(大概是因为我正在卸载应用程序域,但我不确定。这是我的堆栈转储:

MANAGED_STACK: 
    SP               IP               Function
    000000298835E6D0 0000000000000001 System_Web!System.Web.Hosting.UnsafeIISMethods.MgdGetSiteNameFromId(IntPtr, UInt32, IntPtr ByRef, Int32 ByRef)+0x2
    000000298835E7B0 000007F7C56C7F2F System_Web!System.Web.Configuration.ProcessHostConfigUtils.GetSiteNameFromId(UInt32)+0x7f
    000000298835E810 000007F7C56DCB68 System_Web!System.Web.Configuration.ProcessHostMapPath.MapPathCaching(System.String, System.Web.VirtualPath)+0x2a8
    000000298835E8C0 000007F7C5B9FD52 System_Web!System.Web.Hosting.HostingEnvironment.MapPathActual(System.Web.VirtualPath, Boolean)+0x142
    000000298835E940 000007F7C5B9FABB System_Web!System.Web.CachedPathData.GetPhysicalPath(System.Web.VirtualPath)+0x2b
    000000298835E9A0 000007F7C5B99E9E System_Web!System.Web.CachedPathData.GetConfigPathData(System.String)+0x2ce
    000000298835EB00 000007F7C5B99E19 System_Web!System.Web.CachedPathData.GetConfigPathData(System.String)+0x249
    000000298835EC60 000007F7C5BB008D System_Web!System.Web.Configuration.HttpConfigurationSystem.GetApplicationSection(System.String)+0x1d
    000000298835EC90 000007F7C5BAFDD6 System_Configuration!System.Configuration.ConfigurationManager.GetSection(System.String)+0x56
    000000298835ECC0 000007F7C63A11AE System_Runtime_Caching!Unknown+0x3e
    000000298835ED20 000007F7C63A1115 System_Runtime_Caching!Unknown+0x75
    000000298835ED60 000007F7C639C3C5 System_Runtime_Caching!Unknown+0xe5
    000000298835EDD0 000007F7C7628D86 System_Runtime_Caching!Unknown+0x86
    // my code here

有任何已知的解决方案吗?我是否正确地认为我确实需要处置MemoryCache在终结器中?

EDIT

本文 http://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About验证了丹·布莱恩特的回答并讨论了许多有趣的细节。他特别介绍了以下案例:StreamWriter,它面临着与我类似的情况,因为它希望在处置时刷新其缓冲区。文章是这样说的:

一般来说,终结器不能访问托管对象。 然而,对关闭逻辑的支持是必要的 相当复杂的软件。 Windows.Forms 命名空间处理此问题 使用 Application.Exit,它会启动有序关闭。什么时候 设计库组件,有一种方法是有帮助的 支持与现有集成的关闭逻辑 逻辑上类似的 IDisposable (这避免了必须定义一个 IShutdownable 界面,无需任何内置语言支持)。这 通常是通过支持有序关闭来完成的 调用 IDisposable.Dispose,并在调用时中止关闭 不是。如果终结器可以用来做一个,那就更好了 尽可能有序关闭。

微软也遇到了这个问题。 StreamWriter 类 拥有一个 Stream 对象; StreamWriter.Close 将刷新其缓冲区并 然后调用Stream.Close。然而,如果 StreamWriter 没有关闭,它的 终结器无法刷新其缓冲区。微软“解决”了这个问题 不给 StreamWriter 终结器,希望程序员能够 注意缺失的数据并推断出它们的错误。这是一个完美的 需要关闭逻辑的示例。

综上所述,我认为应该可以使用 Wea​​kReference 实现“托管终结”。基本上,让您的类在创建对象时向其自身注册一个 WeakReference 并使用某个队列进行终结操作。然后,队列由后台线程或计时器监视,当配对的 WeakReference 被收集时,后台线程或计时器会调用适当的操作。当然,您必须小心,您的终结操作不会无意中保留类本身,从而完全阻止收集!


您无法在终结器中释放托管对象,因为它们可能已经被终结(或者,正如您在此处看到的,环境的某些部分可能不再处于您期望的状态。)这意味着,如果您包含一个必须显式处理的类,您的类也必须显式处理。没有办法“作弊”并使处置自动进行。不幸的是,在这种情况下,垃圾收集是一个有漏洞的抽象。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Finalizer 中处置 MemoryCache 会引发 AccessViolationException 的相关文章

随机推荐

  • 翻转关联数组并将新值存储在子数组中以防止丢失重复值

    我有一个可能包含重复值的平面关联数组 Array for juniors gt product category for men gt product category coats gt product category for women
  • 删除 .vagrant 文件后如何销毁虚拟机?

    我删除了包含 vagrant 文件的目录 当我安装新的虚拟机时 它抱怨端口正在使用中 那么如何在没有 vagrant 文件的情况下销毁虚拟机呢 以下 VirtualBox 命令可能会有所帮助 如果关闭电源不起作用 请尝试取消注册虚拟机 VB
  • 未捕获的类型错误:(0,_firebase.auth)不是函数

    尝试在 React 应用程序中使用 Firebase 身份验证 我按照 firbase 文档中的说明进行操作 使用 npm 安装 firebase 添加了 firebase js 文件 import as firebase from fir
  • GCP Cloud SDK 在 MAC 中安装失败

    无法使用从 gcloud 下载的包 google cloud sdk 307 0 0 darwin x86 64 tar gz 安装 gcloud sdk 支持的 Python 版本为 3 5 至 3 7 以及 2 7 9 或更高版本 这是
  • 使用 Ajax 的 URL 操作参数

    我正在尝试使用参数将数据从视图传递到控制器 现在我遇到了一些困难 一旦我从表中选择一行并按下具有 ShowTasks 的 onclick 方法的按钮 我就尝试传递这些参数 C 控制器 Route service delivery id sh
  • NullPointerException:println 需要一条消息[重复]

    这个问题在这里已经有答案了 我知道我有一个空指针 但我不知道它出现在哪里或为什么 希望在这里能得到一些帮助 Button add Button findViewById R id addfencebutton add setOnClickL
  • 使用WebView.goBack()方法时如何重新发送POST数据?

    我正在开发一个应用程序 我们为用户提供导航回用户之前查看过的网页的选项 当用户导航回包含 POST 数据的页面时 就会出现问题 我不知道如何检测 WebView 中加载的页面是否有 POST 数据 以便应用程序可以提示确认对话框 无论他是否
  • CodeIgniter 中的 set_value() 默认值

    我使用 formigniter 生成 CI 表单 http formigniter org http formigniter org 那一点效果很好 但是我想为名称字段设置默认值 输入代码如下所示
  • tput cols 在脚本中无法正常工作

    我在脚本中使用 tput cols 一切正常 除非窗口最大化 我的脚本能够正确获取任何窗口大小 但是当窗口最大化时 它会得到错误的值 80 然后我直接在终端中输入 tput cols 然后得到正确的大小 158 所以我的问题是 即使窗口最大
  • ASP.NET 和 Visual Studio - 添加项目引用与 Bin 文件夹 DLL

    我昨天刚刚开始一份新工作 这只是我在 ASP NET 方面的第二份工作 我们正在设置我的开发盒 并且在使用一些第三方组件 例如 Telerik 等 时遇到了问题 我注意到他们安装了这些第三方工具 寻找 DLL 文件 将它们复制到 bin 中
  • cakephp auth组件,使用两种模型

    我的网站有一个供员工使用的公共部分和一个供管理员使用的后端 它使用两种不同的模型 员工模型和管理员模型 我想使用身份验证组件进行员工登录和管理员登录 我知道如何设置 Auth 组件以使用默认用户模型以外的模型 但是我可以让身份验证组件使用两
  • Python——检查对象是否是某个模块中任何类的实例

    需要一种方法来检查对象是否是某个特定模块中任何类的实例 我知道我可以通过从该模块显式导入每个类并检查元组来做到这一点 from my module import ClassOne ClassTwo gt gt gt isinstance m
  • RabbitMQ 和 ActiveMQ 在同一台机器上运行

    出于测试目的 我需要在同一台 Windows 计算机上运行 ActiveMQ 和 RabbitMQ 我已经安装了两者 但无法一起运行它们 我需要停止一项服务才能运行另一项服务 这是我尝试启动运行 ActiveMQ 的 RabbitMQ 时遇
  • 在 Laravel 中迁移表时出现“SQLSTATE[HY000] [2002] No such file or directory”错误

    当我尝试使用 php artisan migrate 命令迁移 Laravel 5 中的表时 出现以下错误 SQLSTATE HY000 2002 中没有这样的文件或目录 vendor laravel framework src Illum
  • 使用代码进行 Entity Framework Core 1.0 代码优先迁移?

    在实体框架的早期版本中 可以使用 DbMigrator 类以编程方式控制代码优先迁移 例如 检查并应用可用迁移 该类是否仍然存在于某处或者是否有功能替代 我发现了一篇关于早期 RC 版本的帖子 其中指出了替代品 但 Core 1 0 中似乎
  • JPopupMenu 行为

    我在下面准备了一个小测试用例 我的问题是当我右键单击窗口时 JPopupMenu 显示 但如果我单击 JWindow 菜单之外的任何位置都不会消失 我必须单击窗口上的某个位置才能将其删除 这不是预期的行为 编辑 阅读 akf 的答案后 我切
  • 如何在Asp.Net Mvc中做Basecamp风格的账户?

    对于 Asp Net 软件即服务应用程序 我想要执行基于帐户的子域 例如 Basecamp 和其他 37Signals 产品 例如 acme myapp com 将加载该客户的帐户并仅提取他们的信息 这在 Ruby on Rails 中很容
  • 滚动到 Bootstrap 模式弹出窗口的顶部

    下面显示的是我的 HTML 代码 它是一个 Bootstrap 模式弹出窗口 我想做的是 如果用户单击 保存 按钮 我正在执行某种验证 如果验证失败 则会显示该消息 并且应自动向上滚动到模式弹出窗口的顶部 但它不向上滚动 下面我还指出了 J
  • 如何在运行时从 Assets 文件夹加载 JAR

    如何在运行时从 Android 应用程序的 asset 文件夹加载 jar 文件 从资产文件夹加载是我的要求 有什么办法可以做到这一点吗 请帮忙 我得到了答案 我在这里添加这个答案 因为这可能对其他一些搜索有帮助 有一些步骤可以实现这一点
  • 在 Finalizer 中处置 MemoryCache 会引发 AccessViolationException

    EDIT有关更多详细信息 请参阅问题底部的编辑注释 原问题 我有一个 CacheWrapper 类 它创建并保存 NET 的实例MemoryCache内部类 MemoryCache将自身挂钩到 AppDomain 事件中 因此除非显式处置