我有一个事件驱动的应用程序,我的任务是维护。每 30 秒大约有 100 个事件在不同的计时器上运行。随着时间的推移,这些事件会变成每秒大约 1-3 个事件的恒定流。内存使用似乎不依赖于任何给定秒内触发的事件数。每个事件都从 Web 服务轮询数据,使用 LINQ2SQL DataContext 对照先前轮询的数据检查数据(完成后我不会处理或清空 DataContext),如果数据不同,则更新数据库并将新数据推送为通过 TCP 向接收者服务发送 XML 消息。
此应用程序似乎有内存泄漏
- 仅在运行 30m+ 后才出现(调试或发布)
- 分析时不会显示 [我正在使用 .NET Memory Profiler 4.5]
特点:在启动程序使用〜30MB。随着时间的推移,任务管理器中的内存使用量将开始急剧上升,起初只是轻微的,介于 50 到 150MB 之间,最终会变得更糟,在 200MB 和 1GB+ 之间波动。发生这种情况时,它会在一两秒内发生几次,然后在接下来的 10-20 秒左右稳定在 ~150MB。
我一直在尝试使用内存分析来捕捉这种行为。到目前为止,我一直没有成功,当分析器不观看时,我无法让应用程序在内存使用量附近的任何地方进行 pogo 或振荡。但是,当垃圾收集器阶段 1 和 2 运行时,我注意到内存使用的方波模式看起来与我在任务管理器中看到的非常相似,除了方波中的内存使用波动是10MB 宽,而不是 800MB+(200MB 到 1GB+)。现在,根据 Google 图片,正常运行的应用程序中的垃圾收集看起来更像是锯齿波而不是方形。
坦率地说,我没有看到我的应用程序可能在一秒钟内使用 200MB 到 1GB+ 的内存使用量并且不会将 CPU 提高到 100%。
我已经阅读了一些在垃圾收集 + 事件处理之间可能出现的问题,但我有几条路径可以调查,并试图缩小花时间在哪一条上。我在 .NET 上的速度仍然很慢,并且还没有开发出我对运行 C 的嵌入式设备的“直觉”,这通常可以帮助我过滤我应该首先调查的内容。如果感觉可能是某些事件处理程序正在丢失并重新获得对 [大量数据] 的引用(我不知道这怎么可能发生?),因为内存使用量似乎在垃圾收集器运行并将内存使用量降至 200MB。
此应用程序的早期版本没有这些问题。从那以后我做了两个改变包括
- 利用 LINQ2SQL 而不是我们自己的数据管理器(它有一个 ADORecordSetHelper 对象,我们用来执行硬编码的 SQL 语句)
- 更改我们用来将 TCP XML 消息发送到接收器的软件。由于我们在 #2 中所做的事情很简单,它可能是问题的根源,但这种内存使用行为让我不这么认为。
我想我此时的主要问题是
- 在我从创建它们的方法返回之前,我应该在我的 LINQ2SQL DataContexts 上调用 dispose 吗?
- 我应该将它们清空吗?
- 如果在创建 DataContext 后方法中的某处发生异常,是否会导致 DataContext 无限期地保存在内存中?
- 如果我将 LINQ 查询的结果存储到值类型(即 int 而不是 var),那么它是延迟加载的,还是在使用变量时延迟加载的?
- 假设事件驱动框架丢失和重新获得引用的可能性有多大?
编辑:这些事件具有基于实例的订阅,就像这里讨论的那样,并且在应用程序的生命周期内永远不会取消订阅。
编辑2:终于设法在探查器中捕捉到它,似乎是一个 200MB 的 system.string 正在以某种方式创建。感谢大家排除 GC 行为。