为什么我的内存分配方法会引发堆栈溢出?

时间:2018-09-17 14:24:26

标签: c# .net

我将所有配置文件存储到profileCache中,这会占用大对象堆中的大量内存。因此,我实现了一种方法来帮助删除未使用的缓存。问题在于该方法似乎无法正确清除缓存,并引发了堆栈溢出错误。这是我已经实现的两种方法。

private static void OnScavengeProfileCache(object data)
{
    // loop until the runtime is shutting down
    while(HostingEnvironment.ShutdownReason == ApplicationShutdownReason.None)
    {
        // NOTE: Right now we only do the scavenge when traffic is temporarily low,
        // to try to amortize the overhead of scavenging the cache into a low utilization period.
        // We also only scavenge if the process memory usage is very high.
        if (s_timerNoRequests.ElapsedMilliseconds >= 10000)
        {
            // We dont want to scavenge under lock to avoid slowing down requests,
            // so we get the list of keys under lock and then incrementally scan them
            IEnumerable<string> profileKeys = null;
            lock (s_profileCache)
            {
                 profileKeys = s_profileCache.Keys.ToList();
            }

            ScavengeProfileCacheIncremental(profileKeys.GetEnumerator());
        }

        // wait for a bit
        Thread.Sleep(60 * 1000);
    }
}

我的方法不断扫描流量,当流量较低时,它将收集我的所有配置文件并将其存储到一个名为profileKeys的IEnumerable中。然后,我调用此方法删除未使用的密钥-

private static void ScavengeProfileCacheIncremental(IEnumerator<string> profileKeys)
{
    if (s_thisProcess.PrivateMemorySize64 >= (200 * 1024 * 1024) ) // 3Gb at least
    {
        int numProcessed = 0;
        while(profileKeys.MoveNext())
        {
            var key = profileKeys.Current;
            Profile profile = null;
            if (s_profileCache.TryGetValue(key, out profile))
            {
                // safely check/remove under lock, its fast but makes sure we dont blow away someone currently being addded
                lock (s_profileCache)
                {
                    if (DateTime.UtcNow.Subtract(profile.CreateTime).TotalMinutes > 5)
                    {
                        // can clear it out
                        s_profileCache.Remove(key);
                    }
                }
            }

            if (++numProcessed >= 5)
            {
                // stop this scan and check memory again
                break;
            }

        }
            // Check again to see if we freed up memory, if not continue scanning the profiles?
            ScavengeProfileCacheIncremental(profileKeys);
    }
}

该方法未清除内存,并通过此跟踪引发堆栈溢出错误:

192. ProfileHelper.ScavengeProfileCacheIncremental(
193. ProfileHelper.ScavengeProfileCacheIncremental(
194. ProfileHelper.ScavengeProfileCacheIncremental(
195. ProfileHelper.ScavengeProfileCacheIncremental(
196. ProfileHelper.OnScavengeProfileCache(...)
197. ExecutionContext.RunInternal(...)
198. ExecutionContext.Run(...)
199. IThreadPoolWorkItem.ExecuteWorkItem(...)
200. ThreadPoolWorkQueue.Dispatch(...)

编辑:

那么这将是删除未使用的配置文件密钥并清除LOH的可能解决方案...

private static void ScavengeProfileCacheIncremental(IEnumerator<string> profileKeys)
{
    if (s_thisProcess.PrivateMemorySize64 >= (200 * 1024 * 1024) ) // 3Gb at least
    {
        int numProcessed = 0;
        while(profileKeys.MoveNext())
        {
            var key = profileKeys.Current;
            Profile profile = null;
            if (s_profileCache.TryGetValue(key, out profile))
            {
                // safely check/remove under lock, its fast but makes sure we dont blow away someone currently being addded
                lock (s_profileCache)
                {
                    if (DateTime.UtcNow.Subtract(profile.CreateTime).TotalMinutes > 5)
                    {
                        // can clear it out
                        s_profileCache.Remove(key);
                    }
                }
            }

            if (++numProcessed >= 5)
            {
                // stop this scan and check memory again
                break;
            }
        }
    }
    GC.Collect;
}

2 个答案:

答案 0 :(得分:2)

我相信您的代码遇到了称为 Infinite Recursion (无限递归)的问题。

您正在调用方法ScavengeProfileCacheIncremental,该方法依次在内部调用自身。在某个时候,您调用它的次数足以使堆栈用尽,从而导致溢出

或者在您的堆栈用完之前没有满足您的条件,或者根本没有满足您的条件。调试应该向您显示原因。

您可以阅读有关here主题的更多信息。

答案 1 :(得分:1)

SaveProfileCacheIncremental没有退出。

它执行其工作,然后自称。然后,它执行自己的任务并自行调用。然后,它执行自己的任务并自行调用。然后,它执行自己的任务并自行调用。然后,它执行自己的任务并自行调用。

一段时间后,它将使用所有堆栈空间,并且进程崩溃。

相关问题