我正在处理图像处理项目,但遇到了问题。
我有一种方法,该方法将(图像的)流作为输入并返回包含所提供图像子部分的流的列表:
public List<System.IO.Stream> ImageProcessing(System.IO.Stream input){
Bitmap inputAsBitmap = new Bitmap(input);
// --- Applying miscellaneous filters ---
List<Bitmap> subParts = GetSubParts(inputAsBitmap);
inputAsBitmap.Dispose();
List<System.IO.Stream> streams = new List<Stream>();
for(int i = 0; i < subParts.Count; i++){
MemoryStream memoryStream = new MemoryStream();
subParts[i].Save(memoryStream, ImageFormat.Jpeg);
streams.Add(memoryStream);
subParts[i].Dispose();
}
return streams;
}
当我通过OpenFileDialog选择图像手动调用此方法时,一切正常:
using (Stream imageStream = File.OpenRead(filename)){
List<Stream> streams = ImageProcessing(imageStream);
for(int i = 0; i < streams.Count; i++){
System.Drawing.Image.FromStream(streams[i]).Save();
streams[i].Dispose();
}
}
但是当我尝试选择一个文件夹并在for循环中调用它的每个图像时,都会抛出一个OutOfMemoryException
:
for(int i = 0; i < filenames.Count; i++){
using (Stream imageStream = File.OpenRead(filenames[i])){
List<Stream> streams = ImageProcessing(imageStream);
for(int j = 0; j < streams.Count; j++){
System.Drawing.Image.FromStream(streams[j]).Save();
streams[j].Dispose();
}
}
}
在调查时,我发现拥有一条指令块需要花费大约20ms的时间来执行,可以清除内存,从而防止引发异常:
for(int i = 0; i < filenames.Count; i++){
// --- A block of instructions placed here will prevent the exception ---
using (Stream imageStream = File.OpenRead(filenames[i])){
// -- Same code as above ---
}
}
此外,在检查多个点的内存(使用Process.GetCurrentProcess().PrivateMemorySize64
或System.GC.GetTotalMemory()
)时,处置Bitmap
和Stream
似乎并没有改变金额的已用内存。
关于如何摆脱这个OutOfMemoryException
的任何提示?
您是否对以下事实有任何解释:在for循环中调用ImageProcessing
方法时,如果在手动完成两次调用之间执行操作,则不会清除内存?
编辑:
这是我现在拥有的,但仍然无法正常工作:
for(int i = 0; i < filenames.Count; i++){
using (Stream imageStream = File.OpenRead(filenames[i])){
List<Stream> streams = ImageProcessing(imageStream);
for(int j = 0; j < streams.Count; j++){
using (var image = System.Drawing.Image.FromStream(streams[j]))
image.Save();
streams[j] = null;
//System.GC.Collect();
}
}
}
//System.GC.Collect();
答案 0 :(得分:3)
您可以做一些改善的事情。
首先,您应强制将应用程序作为64位应用程序运行,并在发布模式下运行该程序,因为调试模式可能会使该程序在实例上的保留时间更长。
然后,对System.Drawing.Image.FromStream(streams[j]).Save();
的调用会返回一个图像,因此也必须将其丢弃。
最后,当我完成一些变量操作后,我不喜欢放null
。 JIT / GC通常 知道何时不再使用变量。但是,对于您来说,stream[j]
实例在整个过程中都保留在列表中。也许您可以尝试使用IEnumerable
和yield return
来稍微改变程序的结构,以使实例不会在列表中停留太长时间。这里是关键点:
public IEnumerable<Bitmap> GetSubParts()
{
...
// Assuming there is a loop somewhere:
for(***)
{
yield return ** Bitmap
}
}
与ImageProcessing相同:
public IEnumerable<System.IO.Stream> ImageProcessing(System.IO.Stream input)
{
using(Bitmap inputAsBitmap = new Bitmap(input))
{
// --- Applying miscellaneous filters ---
foreach(var subPart in GetSubParts(inputAsBitmap))
{
MemoryStream memoryStream = new MemoryStream();
using(subPart)
{
subPart.Save(memoryStream, ImageFormat.Jpeg);
}
yield return memoryStream;
}
}
}
最后:
for(int i = 0; i < filenames.Count; i++)
{
using (Stream imageStream = File.OpenRead(filenames[i]))
{
foreach(var stream = ImageProcessing(imageStream))
{
using(stream)
{
using(var image = System.Drawing.Image.FromStream(stream))
image.Save();
}
}
}
}