3

我有一个充当多线程 Web 服务器的 C# 程序。它对 Sql Server 数据库中保存的 Xml 结构进行了大量处理。

随着这些 Xml 结构的大小增加,我发现应用程序内存不足。

我已经部署了 ANTS 内存分析器来查看发生了什么,并设法减少了处理过程中内存中保存的大字符串的数量,并稍微改进了一些东西。

但是,我现在留下了一个碎片化的大对象堆,这是由连接池中保存的大字节数组引起的。大字节数组是

TdsParserStateObject._bTmp
in TdsParser._physicalStateObj
in SqlInternalConnectionIds._parser
in DbConnectionInternal[0]
in DbConnectionPool._objectList

我 99.9% 确信我只在 using 语句中使用连接,尽管我确实在线程运行时保持每个线程打开一个连接(这是一种优化,但我怀疑它是否会让事情变得更糟)。

我可以对连接做些什么来减少它所拥有的内存量(除了关闭或处置它)?

还是我应该总是在每次使用时立即关闭或处理每个连接?

[后来-根据我的评论]我重构了代码以对每个数据库访问使用一个新连接,然后将其处理掉(当然,除了事务,我从事务的开始到结束都使用相同的连接,并且将其与交易一起处置)。

即使程序处于空闲状态(即没有正在使用的连接),连接池中仍有连接占用大量内存,并造成碎片。

为什么已处置的连接会在连接池中保留 58MB 的内存?

[Even later] 我有一个解决方案可以防止 Sql Server 连接池将大堆碎片化 - 这是为了检测哪些连接可能会有一个巨大的缓冲区,并标记它们以便在处理时从池中删除,使用

SqlConnection.ClearPool(连接)

我目前以一种相当老套的方式执行此操作,方法是对 DataReader 进行子类化并检测返回的任何字段是否超过 10MB。

欢迎提出有关检测哪些连接具有大缓冲区的更好方法的建议。

请注意,我已经恢复了在每次数据库访问时打开和处理连接的更改,因为跟踪在事务中使用哪个连接(显然必须是事务)正在做我的工作。

在线程开始时打开并在结束时关闭的连接很好,因为几乎所有线程都是短暂的(作为对单个 Web 请求的响应)。例外情况是可能在单个事务的上下文中运行的批处理过程。

4

2 回答 2

3

使用 ADO.NET 时无需保持连接打开。默认情况下,连接将为您池化(除非您自己关闭池化),并且 ADO.NET 将完成池的所有管理。您甚至不必担心不同的用户凭据等,因为仅当连接参数相同时才会汇集连接。从内存使用的角度来看,处理连接只会有帮助。

更多关于这个:

于 2013-11-10T00:35:58.010 回答
2

Szymon 的回答在一定程度上很好,所以我投了赞成票。

但是,他的回答没有解决许多问题。每当通过 SqlConnection 检索非常大的数据(在我的例子中是一个包含大约 10 MB Xml 的单个文本字段)时,连接的内部缓冲区似乎变得非常大。

如果你有一个程序同时使用多个连接,数据很大,那么在你 Disposed 所涉及的 SqlConnection 之后,这些大缓冲区仍然被连接池引用,这会导致大数据堆的碎片化。

我发现防止这种情况的唯一方法是检测已用于大数据的 SqlConnections,并在处理它们之前将它们从连接池中删除。

从我的问题后面的编辑可以看出,我还没有找到一种优雅的方法来检测 SqlConnection 内部缓冲区是否很大,只是一种非常不优雅的方法,通过子类化 DataReader 并检测返回的任何字段是否结束10MB大小!

于 2013-11-18T18:00:47.677 回答