c# - 为什么我的内存分配方法会抛出 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;
}
解决方案
我相信您的代码遇到了一个称为Infinite Recursion的问题。
您正在调用 method ScavengeProfileCacheIncremental
,而后者又在内部调用自身。在某些时候,您调用它的次数足够多,以至于您用尽了stack,从而导致了overflow。
要么你的条件在你用完堆栈之前没有得到满足,要么你的条件根本就没有得到满足。调试应该告诉你原因。
您可以在此处阅读有关该主题的更多信息。
推荐阅读
- c# - C# EF CF - 违反参照完整性约束:多个外键到同一个表
- python - 如何在 docker 镜像中编辑库包
- c# - 获取单个传递参数的名称和类型?
- java - Toast.makeText 在 Andriod Studio 上只出现一次
- dialogflow-es - Dialogflow:跟进直接对 webhook 的快速回复
- java - How to fix fatal error when executing setText()
- php - 将“完整性约束违规:1048 列不能为空”变成警报消息
- android - 为多应用程序 android 项目设置 Jenkins
- java - 如果 JSON 具有无效的属性名称,RestTemplate (w/Jackson) 调用是否应该失败
- sql - 查询postgresql表的效率问题