为什么我的 .NET 垃圾没有被收集?

Why isn't my .NET garbage being collected?

我有一个数据密集型应用程序,用户可以在不同帐户之间 select。一次只能 select 编辑一个帐户,这会强制从数据库加载数据。当我将帐户加载到大约半个演出时,我可以在 Windows 任务管理器中看到分配给我的应用程序的内存正在增加。

我们还可以 "unload" 一个帐户,通过该帐户我们可以删除内存中的所有对象(或者至少我们认为我们可以删除)。无论我让应用程序 运行 在我的 PC 上休眠多长时间,它似乎永远不会将分配的内存减少回预加载帐户状态,即使我使用 WeakReference 查看对象状态时说它不是 ALIVE .

如果我在卸载方法的末尾显式调用 GC.Collect(),那么我可以看到未分配的内存或至少 Windows 任务管理器中的程序大小正在下降。这是我们真正希望在内存方面实现的,因为一些用户遇到了内存不足的异常。

我知道垃圾收集器正在做一些管理,因为如果我加载后续帐户,内存不会真正增加超过半个演出所以我认为 GC 已经收集了以前帐户的数据,就像卸载时应该做的那样帐号。

我是否应该继续使用 GC.Collect 考虑到 运行 这个相对于从数据库加载帐户的成本是最小的,即使它被认为 "bad practice" 明确调用这个?

GC收集仅在certain events下触发(例如宿主进程分配的内存已用完,需要分配更多,因此它首先检查是否可以收集一些垃圾)。

OM 异常可能不是由此引起的,而是您在其他地方有内存泄漏,或者某些用户试图加载多个帐户(甚至大于 0.5GB 的帐户)。考虑对您的应用程序进行一些内存分析。 (我为此使用了 YourKit profiler,它非常容易使用)

认为如果您的进程是 x86,它最多有 2GB 可用空间,因此对于如此大的数据,您达到极限并不罕见。

根据您的描述,您似乎以某种方式保留了从数据库加载的项目的硬引用。如果您正在使用 Entity framework 或 NHibernate,请检查您是否正在处理用于从数据库加载数据的上下文,因为它将保存您加载的所有内容的引用,以提供更改跟踪功能。通常要确保您没有以某种方式保留引用,并注意有时这可能很棘手;棘手的硬引用的一个很好的例子是在使用方法范围变量的 lamda 方法中加载一些东西,由于在 lamda 方法中被引用,它会自动提升为 class 变量。

我不知道您是否在代码中执行了此操作,但 using 语句将调用您正在使用的命令的 Dispose() 方法。它可能对您遇到问题的 GC 有所帮助。否则你会让系统处理它。唯一的问题是您使用的 class 必须实现 IDisposable。

    using (SqlCommand cmd = conn.CreateCommand())
{
    conn.Open();
    cmd.CommandText = "sp_SaveSomething";
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add(new SqlParameter("@x", xxx));
    cmd.Parameters.Add(new SqlParameter("@ORG", ORG));        
    cmd.ExecuteNonQuery();
}