asp.net core 中必须通过 di 注入 imemorycache,禁用 memorycache.default 和 new memorycache();过期策略不可混用,sizelimit 与 setsize() 必须成对配置;getorcreate() 是防击穿唯一可靠方式。

ASP.NET Core 项目里直接用 MemoryCache.Default 或 new MemoryCache() 配缓存策略,等于没配——过期不触发、内存不回收、高并发击穿、OOM 前无预警。
为什么 SetSlidingExpiration 和 SetAbsoluteExpirationRelativeToNow 不能一起用
不是语法报错的问题,是语义冲突导致行为不可控。两者同时设在同一个 MemoryCacheEntryOptions 实例里,运行时不抛异常,但实际只按绝对时间生效:比如 .SetSlidingExpiration(TimeSpan.FromMinutes(30)) 和 .SetAbsoluteExpirationRelativeToNow(TimeSpan.FromMinutes(10)) 一起调,结果就是 10 分钟后强制删除,滑动逻辑完全被忽略。
真实场景该选哪个:
- 配置类、静态字典、城市列表等低频更新数据 → 用
SetAbsoluteExpirationRelativeToNow - 用户会话、临时 token、高频访问的热点数据 → 用
SetSlidingExpiration - 真要“最长 30 分钟 + 活跃就续命” → 别指望内置组合,得靠
PostEvictionCallback+ 定时检查最后访问时间自己控制
SizeLimit 和 SetSize() 必须成对出现,否则内存保护形同虚设
SizeLimit 是全局开关,SetSize() 是单个缓存项的“计费单位”。只在 AddMemoryCache() 里设了 options.SizeLimit = 100 * 1024 * 1024,但存对象时没调 entry.SetSize(…),那所有项默认按大小 1 计算——一个 8MB 的 byte[] 和一个空字符串都算 “1”,缓存系统根本感知不到内存压力。
实操要点:
- 注册时必须显式设
SizeLimit:否则即使调了SetSize(),也不会触发淘汰 - 大对象(如导出文件流、序列化后的 DTO)务必调
SetSize();小对象可统一设为SetSize(1),但别省略 - 别用
sizeof或Marshal.SizeOf算大小——它们只返回托管头尺寸;推荐序列化成byte[]后取.Length,再乘 1.2 安全系数
GetOrCreate() 是防击穿唯一可靠方式,但 factory 内逻辑必须自洽
手写 if (!_cache.TryGetValue(key, out var value)) { value = LoadFromDb(); _cache.Set(key, value); } 在高并发下必然重复加载。10 个请求同时查不到 key,就会并行执行 10 次数据库查询。
GetOrCreate() 把查+存原子化,但要注意:
- 过期策略必须在 factory 委托内设置:
entry.SlidingExpiration = …,外部设置无效 - factory 里抛异常 → 缓存不写入,调用方直接收到 exception;如需缓存“查不到”的兜底值,得在 factory 里
try/catch后返回 - 加载逻辑含
await(比如异步 DB 查询)→ 必须用GetOrCreateAsync(),否则线程池阻塞 - 返回的是引用类型?别在外部修改字段,否则后续
Get()全部拿到脏数据
别用 MemoryCache.Default 或 new MemoryCache(),DI 注入是唯一正路
MemoryCache.Default 来自 System.Runtime.Caching,和 ASP.NET Core 的 Microsoft.Extensions.Caching.Memory 完全无关;手动 new MemoryCache(new MemoryCacheOptions()) 会创建孤立实例,后果包括:
- 不同 Service/Controller 注入的缓存实例不共享,看似存了,另一处
Get()就是 null -
SizeLimit、CompactionPercentage、PostEvictionCallback全部失效 - 内存紧张时不 compact、GC 压力飙升后无响应,OOM 发生前没有任何征兆
正确路径只有一条:.NET 6+ 项目,在 Program.cs 中调 builder.Services.AddMemoryCache(),然后构造函数注入 IMemoryCache。单元测试若需手动实例化,至少传入带 SizeLimit 和 CompactionPercentage 的 MemoryCacheOptions。
最易被忽略的一点:过期检查是惰性触发的,不是定时扫描。某项到期后不会立刻从内存移除,而是等到下次 Get() 或内存压力触发 compact 时才清理。所以别依赖“到期即失”,尤其在监控或强一致性场景里,得额外加最后访问时间校验。











