C# Win32Exception“;存储空间不足,无法处理此命令;创建大量BitmapSource对象时
在我们的应用程序中,我们导出了大量图像,并遇到了此Win32Exception:C# Win32Exception“;存储空间不足,无法处理此命令;创建大量BitmapSource对象时,c#,.net,wpf,C#,.net,Wpf,在我们的应用程序中,我们导出了大量图像,并遇到了此Win32Exception: System.ComponentModel.Win32Exception (0x80004005): Not enough storage is available to process this command at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int3
System.ComponentModel.Win32Exception (0x80004005): Not enough storage is available to process this command
at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
at System.Windows.Threading.Dispatcher..ctor()
at System.Windows.DependencyObject..ctor()
at System.Windows.Media.Imaging.BitmapSource..ctor(Boolean useVirtuals)
at System.Windows.Media.Imaging.CachedBitmap..ctor(Int32 pixelWidth, Int32 pixelHeight, Double dpiX, Double dpiY, PixelFormat pixelFormat, BitmapPalette palette, Array pixels, Int32 stride)
at System.Windows.Media.Imaging.BitmapSource.Create(Int32 pixelWidth, Int32 pixelHeight, Double dpiX, Double dpiY, PixelFormat pixelFormat, BitmapPalette palette, Array pixels, Int32 stride)
问题是,我们在不同的线程中创建BitmapSource对象,并且存在内存泄漏。如果执行此代码,内存中仍有许多对象:
List<Thread> threads = new List<Thread>();
for (int i = 0; i < 100; i++)
{
Thread t = new Thread(new ThreadStart(() =>
{
BitmapSource temp = BitmapSource.Create(1, 1, 96, 96, PixelFormats.Bgr24, null, new byte[3], 3);
temp.Freeze();
}));
t.Start();
threads.Add(t);
}
foreach (var thread in threads)
{
thread.Join();
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
List threads=newlist();
对于(int i=0;i<100;i++)
{
线程t=新线程(新线程开始(()=>
{
BitmapSource temp=BitmapSource.Create(1,1,96,96,PixelFormats.Bgr24,null,新字节[3],3);
温度冻结();
}));
t、 Start();
添加(t);
}
foreach(线程中的var线程)
{
thread.Join();
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
我们在Windows服务中执行此操作,几天后系统崩溃。带有“存储空间不足…”消息。此时,许多其他应用程序崩溃,您无法打开编辑器或其他Windows应用程序。桌面堆是否已满?内存不会增加很多(最大160MB),而且机器有16GB的RAM
如何正确处理位图源?如中所示,
BitmapSource.Create
只需调用CachedBitmap
构造函数,默认情况下,该构造函数使用BitmapCachinOptions.default
作为缓存选项。这意味着创建的每个位图都将保存在缓存中
您需要使用从BitmapSoure
继承的不使用缓存的其他类。如果您想自己创建位图,可以尝试WritableBitmap
。如中所示,
BitmapSource.Create
只需调用CachedBitmap
构造函数,默认情况下,该构造函数使用BitmapCachinOptions.default
作为缓存选项。这意味着创建的每个位图都将保存在缓存中
您需要使用从
BitmapSoure
继承的不使用缓存的其他类。如果您想自己创建位图,可以尝试WritableBitmap
。我们找到了一个解决方案,没有发现内存泄漏:
List<Thread> threads = new List<Thread>();
for (int i = 0; i < 100; i++)
{
Thread t = new Thread(new ThreadStart(() =>
{
BitmapSource temp = BitmapSource.Create(1, 1, 96, 96, PixelFormats.Bgr24, null, new byte[3], 3);
// Shutdown Dispatcher
if (temp.Dispatcher != null)
{
temp.Dispatcher.InvokeShutdown();
}
temp.Freeze();
}));
t.Start();
threads.Add(t);
}
foreach (var thread in threads)
{
thread.Join();
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
List threads=newlist();
对于(int i=0;i<100;i++)
{
线程t=新线程(新线程开始(()=>
{
BitmapSource temp=BitmapSource.Create(1,1,96,96,PixelFormats.Bgr24,null,新字节[3],3);
//停机调度员
如果(临时调度器!=null)
{
temp.Dispatcher.InvokeShutdown();
}
温度冻结();
}));
t、 Start();
添加(t);
}
foreach(线程中的var线程)
{
thread.Join();
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
我们必须看看这是否会导致其他问题,您认为如何?我们找到了一个解决方案,我们没有发现memroy泄漏:
List<Thread> threads = new List<Thread>();
for (int i = 0; i < 100; i++)
{
Thread t = new Thread(new ThreadStart(() =>
{
BitmapSource temp = BitmapSource.Create(1, 1, 96, 96, PixelFormats.Bgr24, null, new byte[3], 3);
// Shutdown Dispatcher
if (temp.Dispatcher != null)
{
temp.Dispatcher.InvokeShutdown();
}
temp.Freeze();
}));
t.Start();
threads.Add(t);
}
foreach (var thread in threads)
{
thread.Join();
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
List threads=newlist();
对于(int i=0;i<100;i++)
{
线程t=新线程(新线程开始(()=>
{
BitmapSource temp=BitmapSource.Create(1,1,96,96,PixelFormats.Bgr24,null,新字节[3],3);
//停机调度员
如果(临时调度器!=null)
{
temp.Dispatcher.InvokeShutdown();
}
温度冻结();
}));
t、 Start();
添加(t);
}
foreach(线程中的var线程)
{
thread.Join();
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
我们必须看看这是否会导致其他问题,您认为如何?内存泄漏似乎与未在主线程上创建位图以及dispatcher保留引用有关。您找到的解决方案可能有效 然而,我想建议另一个改进。使用无限数量的线程可能不是一个好主意。使用线程池可能是有意义的。最简单的方法是使用
Task.Run
计划在线程池上创建位图。事实上,在使用线程池时,我没有看到任何内存泄漏
var tasks = new List<Task>();
for (int i = 0; i < 100000; i++)
{
var task = Task.Run(() =>
{
BitmapSource temp = null;
temp = BitmapSource.Create(
1,
1,
96,
96,
PixelFormats.Bgr24,
null,
new byte[3],
3);
temp.Freeze();
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
var tasks=newlist();
对于(int i=0;i<100000;i++)
{
var task=task.Run(()=>
{
BitmapSource temp=null;
temp=BitmapSource.Create(
1.
1.
96,
96,
PixelFormats.Bgr24,
无效的
新字节[3],
3);
温度冻结();
});
任务。添加(任务);
}
Task.WaitAll(tasks.ToArray());
内存泄漏似乎与未在主线程上创建位图以及dispatcher保留引用有关。您找到的解决方案可能有效
然而,我想建议另一个改进。使用无限数量的线程可能不是一个好主意。使用线程池可能是有意义的。最简单的方法是使用Task.Run
计划在线程池上创建位图。事实上,在使用线程池时,我没有看到任何内存泄漏
var tasks = new List<Task>();
for (int i = 0; i < 100000; i++)
{
var task = Task.Run(() =>
{
BitmapSource temp = null;
temp = BitmapSource.Create(
1,
1,
96,
96,
PixelFormats.Bgr24,
null,
new byte[3],
3);
temp.Freeze();
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
var tasks=newlist();
对于(int i=0;i<100000;i++)
{
var task=task.Run(()=>
{
BitmapSource temp=null;
temp=BitmapSource.Create(
1.
1.
96,
96,
PixelFormats.Bgr24,
无效的
新字节[3],
3);
温度冻结();
});
任务。添加(任务);
}
Task.WaitAll(tasks.ToArray());
是否在某处保留对BitmapSource
对象的引用?一旦不再使用BitmapSource,GC应该进行清理,根据Nope的说法,这是导致内存和可能的句柄泄漏的整个代码片段,我现在看到了问题所在。仅当BitmapSource在其自己的线程上创建时,才会发生此错误。可能是错误/不受支持的情况(看起来调度器可能保留了一个防止垃圾收集的引用)您是否保留了对BitmapSource
对象的引用?一旦不再使用BitmapSource,GC就应该进行清理,根据Nope的说法,这是lea的全部代码片段