执行 SQL 作业时线程池 System.OutOfMemory 异常
我在 WinForms 应用程序中不断收到 System.OutOfMemory 异常,我猜这是在执行 SQL 更新时引发的。我该如何解决这个问题?情况如下,我正在使用 SQLEXPRESS,在 32 位计算机上尝试代码,因此 ThreadPool 在启动时默认有 1023 个可用工作线程。
MyClass myClass = new MyClass();
for (int i = 0; i < 1000000; i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(Foo), myClass);
}
private void Foo(object state)
{
//Some Stuff and SQL UPDATE
}
当应用程序启动时,ThreadPool 以 8 个线程启动,并开始增加分配的线程数来执行作业。过了一段时间,例如达到200个线程,就无法再处理了,抛出System.OutOfMemory异常。当我检查堆栈跟踪时,我可以看到该方法的 SQL 操作发生异常。我应该怎么办?我需要增加数据库的缓冲区大小吗?我不想顺便限制 ThreadPools 的最大大小,也不想尝试 Thread.Sleep() 来降低对数据库的较慢、较不频繁的请求。
I keep getting System.OutOfMemory exception in my WinForms App which is thrown while doing an SQL Update I guess. How can I fix that? Here is the case, I am using SQLEXPRESS, trying the code on a 32-bit machine so ThreadPool has default 1023 avaliable worker threads on startup.
MyClass myClass = new MyClass();
for (int i = 0; i < 1000000; i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(Foo), myClass);
}
private void Foo(object state)
{
//Some Stuff and SQL UPDATE
}
When the application starts, the ThreadPool starts with 8 threads and starts to increase allocated thread number to perform the job. A while later, it comes to 200 threads for example and can handle it no more, gives out System.OutOfMemory exception. When I check the stack trace, i can see that exception occurs in SQL operation of the method. What should I do? Do I need to increase Buffer Size of my database? I do not want to limit ThreadPools max size by the way or try Thread.Sleep() to have slower, less frequent requests to DB.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
盲目地创建大量线程来使操作运行得更快并不是一个好的解决方案,而且几乎永远不会起作用(除非运气好)。请记住,每个线程都会分配自己的堆栈,我认为默认情况下为 1MB。因此,如果您有 1000 个线程运行,则将使用 1GB 的 RAM,就像这样。
如果您访问数据库,那么您可以并行执行的工作很可能会受到磁盘 I/O 的限制,因此向其投入更多线程实际上可能会使情况变得更糟。
另请注意,异步操作是在线程池线程上执行的,如果您将它们全部与您自己的工作捆绑在一起,您可能会遇到使这些操作匮乏的问题(这意味着它们可能永远不会或很晚执行)。线程池被设计为仅运行短期任务。如果您需要长时间运行的任务,请使用不同的线程池(例如 SmartThreadPool)或者创建您自己的一组线程来处理工作。
根据您的 SQL 操作,您可能会遇到大对象堆碎片问题。大于 85.000 字节的对象被放置在未压缩的 LOH 上,您可能会遇到意外的 OOM 异常。因此,请检查您是否正在创建大型数组或对象列表。
否则:使用Windows 调试工具进行内存转储并查看您可以在哪些对象上设置吃掉所有内存以及保存引用以保持它们存活的设置。或者,您可以使用 .NET 内存分析器,但大多数真正有用的内存分析器都不是免费的(但是它们通常有 X 天的评估期)。
Blindly creating lots of threads to make an operation go faster is not a good solution and almost never works (except by luck maybe). Keep in mind that each threads gets its own stack allocated which is 1MB by default I think. So in case you get 1000 threads going that will use 1GB of RAM just like that.
If you hit the DB then the work you can do in parallel will be very likely limited by disk I/O so throwing more threads at it might actually make it worse.
Also be aware that asynchroneous operations are executed on threadpool threads and if you tie them all up with your own work you may run into the problem to starve those operations (meaning they might be never or very late executed). The threadpool is designed to run only short lived tasks. If you need long running tasks then use a different threadpool (for example the SmartThreadPool) or create you own set of threads to handle the work.
Depending on what your SQL operations are you might hit the Large Object Heap fragmentation problem. Objects larger than 85.000 bytes are put on the LOH which is not being compacted and you can run into unexpected OOM ecceptions. So check if you are creating large arrays or lists of objects.
Otherwise: Use the debugging tools for windows to make a memory dump and look at what objects you have settings around eating all the memory and what holds the references to keep them alive. Alternatively you could use a .NET memory profiler but most of the really usefull ones are not free (however they usually come with a X days evaluation period).