我将所有配置文件存储到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;
}
答案 0 :(得分:2)
我相信您的代码遇到了称为 Infinite Recursion (无限递归)的问题。
您正在调用方法ScavengeProfileCacheIncremental
,该方法依次在内部调用自身。在某个时候,您调用它的次数足以使堆栈用尽,从而导致溢出。
或者在您的堆栈用完之前没有满足您的条件,或者根本没有满足您的条件。调试应该向您显示原因。
您可以阅读有关here主题的更多信息。
答案 1 :(得分:1)
SaveProfileCacheIncremental没有退出。
它执行其工作,然后自称。然后,它执行自己的任务并自行调用。然后,它执行自己的任务并自行调用。然后,它执行自己的任务并自行调用。然后,它执行自己的任务并自行调用。
一段时间后,它将使用所有堆栈空间,并且进程崩溃。