首页 > 解决方案 > 为什么我的内存分配方法会抛出 Stack Overflow?

问题描述

我将所有配置文件存储到 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;
}

标签: c#.net

解决方案


我相信您的代码遇到了一个称为Infinite Recursion的问题。

您正在调用 method ScavengeProfileCacheIncremental,而后者又在内部调用自身。在某些时候,您调用它的次数足够多,以至于您用尽了stack,从而导致了overflow

要么你的条件在你用完堆栈之前没有得到满足,要么你的条件根本就没有得到满足。调试应该告诉你原因。

您可以在此处阅读有关该主题的更多信息。


推荐阅读