【发布时间】:2011-06-14 06:13:51
【问题描述】:
我用 C# 编写了一个多线程的 Windows 服务。出于某种原因,每次生成线程时都会启动 csc.exe。我怀疑它与线程本身有关,但事实上它是在每个线程的基础上发生的,而且这些线程是短暂的,这使得问题非常明显:许多 csc.exe 进程不断启动和停止。
性能仍然相当不错,但如果我能消除它,我希望它会有所改善。然而,更让我担心的是,McAfee 正试图扫描 csc.exe 实例并最终终止该服务,显然是当其中一个实例在扫描过程中退出时。我需要在商业上部署此服务,因此更改 McAfee 设置不是解决方案。
我假设我的代码中的某些内容正在触发动态编译,但我不确定是什么。还有其他人遇到这个问题吗?有什么解决办法吗?
更新 1:
根据@sixlettervariables 的建议和链接进一步研究,问题似乎源于XML序列化的实现,如Microsoft's documentation on XmlSerializer:所示
为了提高性能,XML 序列化基础结构动态生成程序集以序列化和反序列化指定类型。
Microsoft 在同一个文档中指出了进一步的优化:
基础架构会找到并重用这些程序集。此行为仅在使用以下构造函数时发生:
XmlSerializer.XmlSerializer(类型)
XmlSerializer.XmlSerializer(类型,字符串)
这似乎表明代码生成和编译只会发生一次,第一次使用时,只要使用两个指定的构造函数之一。但是,我并没有从这种优化中受益,因为我使用了另一种形式的构造函数,特别是:
public XmlSerializer(Type type, Type[] extraTypes)
进一步阅读,事实证明,这也恰好是我在代码执行时观察到的内存泄漏的可能解释。同样,来自同一个文档:
如果您使用任何其他构造函数,则会生成同一程序集的多个版本并且永远不会卸载,这会导致内存泄漏和性能下降。最简单的解决方案是使用前面提到的两个构造函数之一。否则,您必须将程序集缓存在 Hashtable 中。
Microsoft 上面建议的两种解决方法对我来说是最后的手段。不推荐使用另一种形式的构造函数(我使用“extratypes”形式对派生类进行序列化,这是微软文档支持的用法),我不确定我是否喜欢管理缓存的想法跨多个线程使用的程序集。
所以,我有sgen'd,并看到为我的类型按预期生成的序列化程序的结果程序集,但是当我的代码执行时,sgen 生成的程序集未加载(根据融合日志查看器和进程中的观察监视器)。我目前正在探索为什么会这样。
更新 2:
当我使用两个“更友好”的 XmlSerializer 构造函数之一时,sgen 的程序集加载正常(参见上面的更新 1)。例如,当我使用XmlSerializer(Type) 时,会加载 sgen 的程序集并且不执行运行时代码生成/编译。但是,当我使用XmlSerializer(Type, Type[]) 时,程序集不会加载。找不到任何合理的解释。
所以我将恢复使用支持的构造函数之一和 sgen'ing。这种组合消除了我最初的问题(启动 csc.exe),以及另一个相关问题(上面更新 1 中提到的 XmlSerializer 引起的内存泄漏)。然而,这确实意味着我必须为派生类型恢复一种不太优化的序列化形式(在基本类型上使用XmlInclude),直到框架发生某些变化来解决这种情况。
【问题讨论】:
-
代码很丰富。我认为没有任何代码部分特别可疑。
-
@lukas - 不,我不使用动态类型。我认为@sixlettervariables 有点意思,因为我实际上是在序列化。现在正在调查。