2010-02-05 37 views
6

Đây là lớp Picture.cs tôi:Bắt một 'ra khỏi bộ nhớ' ngoại lệ trong chương trình tương đối đơn giản này

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO; 
using System.Drawing; 

namespace SharpLibrary_MediaManager 
{ 
    public class Picture:BaseFile 
    { 
     public int Height { get; set; } 
     public int Width { get; set; } 
     public Image Thumbnail { get; set; } 

     /// <summary> 
     /// Sets file information of an image from a given image in the file path. 
     /// </summary> 
     /// <param name="filePath">File path of the image.</param> 
     public override void getFileInformation(string filePath) 
     { 
      FileInfo fileInformation = new FileInfo(filePath); 
      if (fileInformation.Exists) 
      { 
       Name = fileInformation.Name; 
       FileType = fileInformation.Extension; 
       Size = fileInformation.Length; 
       CreationDate = fileInformation.CreationTime; 
       ModificationDate = fileInformation.LastWriteTime; 
       Height = calculatePictureHeight(filePath); 
       Width = calculatePictureWidth(filePath);     
      } 
     } 

     public override void getThumbnail(string filePath) 
     {    
      Image image = Image.FromFile(filePath); 
      Thumbnail = image.GetThumbnailImage(40, 40, null, new IntPtr());    
     } 

     private int calculatePictureHeight(string filePath) 
     { 
      var image = Image.FromFile(filePath); 
      return image.Height; 
     } 

     private int calculatePictureWidth(string filePath) 
     { 
      var image = Image.FromFile(filePath); 
      return image.Width; 
     } 
    } 
} 

Và ở đây, tôi đang sử dụng lớp đó để lấy thông tin từ tất cả các tập tin trong một thư mục nhất định:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.IO; 

namespace SharpLibrary_MediaManager 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 
     } 

     string folderPath = @"D:\Images\PictureFolder"; 

     private void button1_Click(object sender, EventArgs e) 
     { 
      DirectoryInfo folder = new DirectoryInfo(folderPath); 
      List<Picture> lol = new List<Picture>(); 
      foreach (FileInfo x in folder.GetFiles()) 
      { 
       Picture picture = new Picture(); 
       picture.getFileInformation(x.FullName); 
       lol.Add(picture); 
      } 

      MessageBox.Show(lol[0].Name); 
     } 
    } 
} 

Tôi nhận được một ngoại lệ bộ nhớ ngoài và tôi thực sự không biết tại sao. Đây là lần đầu tiên tôi làm điều gì đó như thế này vì vậy tôi khá mới mẻ đối với việc xử lý tệp theo lô, v.v.

Bất kỳ người trợ giúp nào? :)

Edit: tôi mở Task Manager để xem sử dụng bộ nhớ và khi tôi nhấn nút để chạy phương pháp này tôi nhận thấy việc sử dụng bộ nhớ của tôi tăng 100MB ~ mỗi giây.

Chỉnh sửa 2: Trong thư mục của tôi, tôi có khoảng 103 hình ảnh, mỗi hình ảnh là ~ 100kb. Tôi cần một giải pháp mà không quan trọng có bao nhiêu hình ảnh trong một thư mục. Có người đề nghị mở một hình ảnh, làm phép thuật của tôi, rồi đóng nó lại. Tôi không thực sự hiểu ý của anh ấy bằng cách 'đóng'.

Ai đó có thể đề xuất một cách tiếp cận khác không? :)

Chỉnh sửa 3: Vẫn nhận được ngoại lệ bộ nhớ, tôi đã thay đổi mã trong Picture.cs dựa trên đề xuất nhưng tôi không có ý tưởng. Bất kỳ giúp đỡ?

public override void getFileInformation(string filePath) 
     { 
      FileInfo fileInformation = new FileInfo(filePath); 

      using (var image = Image.FromFile(filePath)) 
      { 
       if (fileInformation.Exists) 
       { 
        Name = fileInformation.Name; 
        FileType = fileInformation.Extension; 
        Size = fileInformation.Length; 
        CreationDate = fileInformation.CreationTime; 
        ModificationDate = fileInformation.LastWriteTime; 
        Height = image.Height; 
        Width = image.Width; 
       } 
      } 
     } 

Ngoài ra, tôi có nên mở một câu hỏi mới ngay bây giờ mà câu hỏi này đã phát triển một chút không?

+3

Bạn có thể cung cấp cho chúng tôi dấu vết ngăn xếp của ngoại lệ OOM không? – Steven

+0

Tôi làm cách nào để tìm ra điều đó? –

+0

Một ngoại lệ được ném có một dấu vết ngăn xếp, nhưng không bao giờ nhớ rằng, vấn đề đã được phát hiện trong các phương thức calculatePictureHeight và calculatePictureWidth của bạn. – Steven

Trả lời

14

Bạn không gọi Dispose trên trường hợp Hình ảnh của bạn. Cũng tạo hình ảnh của bạn một lần và sau đó trích xuất dữ liệu của bạn.

Xem thêm:

http://msdn.microsoft.com/en-us/library/8th8381z.aspx

EDIT Nếu sao chép mã của bạn và thử nghiệm nó với Thư viện Hình ảnh của tôi. My avg. FileSize là 2-3 MB cho mỗi tệp. Tôi đã thực hiện chương trình của bạn và nó đã làm chính xác những gì nó cần. GC đã làm chính xác những gì tôi đã mong đợi.

Bộ nhớ Chương trình của bạn luôn là khoảng 11-35 MB Bộ làm việc riêng tư, Kích thước cam kết ổn định ở mức 43 MB.

Tôi đã hủy chương trình sau 1156 tệp với tổng kích thước hình ảnh là 2,9 GB.

Vì vậy, phải có lý do khác cho bạn ngoài ngoại lệ bộ nhớ.

Dưới đây là đầu ra chương trình của tôi và mã:

1133: Total Size = 2.842,11 MB 
1134: Total Size = 2.844,88 MB 
1135: Total Size = 2.847,56 MB 
1136: Total Size = 2.850,21 MB 
1137: Total Size = 2.853,09 MB 
1138: Total Size = 2.855,86 MB 
1139: Total Size = 2.858,59 MB 
1140: Total Size = 2.861,26 MB 
1141: Total Size = 2.863,65 MB 
1142: Total Size = 2.866,15 MB 
1143: Total Size = 2.868,52 MB 
1144: Total Size = 2.870,93 MB 
1145: Total Size = 2.873,64 MB 
1146: Total Size = 2.876,15 MB 
1147: Total Size = 2.878,84 MB 
1148: Total Size = 2.881,92 MB 
1149: Total Size = 2.885,02 MB 
1150: Total Size = 2.887,78 MB 
1151: Total Size = 2.890,57 MB 
1152: Total Size = 2.893,55 MB 
1153: Total Size = 2.896,32 MB 
1154: Total Size = 2.898,92 MB 
1155: Total Size = 2.901,48 MB 
1156: Total Size = 2.904,02 MB 

sourcecode:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO; 
using System.Drawing; 

namespace SharpLibrary_MediaManager 
{ 
    public abstract class BaseFile 
    { 
     public string Name { get; set; } 
     public string FileType { get; set; } 
     public long Size { get; set; } 
     public DateTime CreationDate { get; set; } 
     public DateTime ModificationDate { get; set; } 

     public abstract void getFileInformation(string filePath); 

    } 


    public class Picture : BaseFile 
    { 
     public int Height { get; set; } 
     public int Width { get; set; } 
     public Image Thumbnail { get; set; } 

     public override void getFileInformation(string filePath) 
     { 
      FileInfo fileInformation = new FileInfo(filePath); 

      using (var image = Image.FromFile(filePath)) 
      { 
       if (fileInformation.Exists) 
       { 
        Name = fileInformation.Name; 
        FileType = fileInformation.Extension; 
        Size = fileInformation.Length; 
        CreationDate = fileInformation.CreationTime; 
        ModificationDate = fileInformation.LastWriteTime; 
        Height = image.Height; 
        Width = image.Width; 
        Thumbnail = image.GetThumbnailImage(40, 40, null, new IntPtr()); 
       } 
      } 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      string folderPath = @"C:\Users\arthur\Pictures"; 

      DirectoryInfo folder = new DirectoryInfo(folderPath); 
      List<Picture> lol = new List<Picture>(); 
      double totalFileSize = 0; 
      int counter = 0; 
      foreach (FileInfo x in folder.GetFiles("*.jpg", SearchOption.AllDirectories)) 
      { 
       Picture p = new Picture(); 
       p.getFileInformation(x.FullName); 
       lol.Add(p); 
       totalFileSize += p.Size; 
       Console.WriteLine("{0}: Total Size = {1:n2} MB", ++counter, totalFileSize/1048576.0); 
      } 

      foreach (var p in lol) 
      { 
       Console.WriteLine("{0}: {1}x{2} px", p.Name, p.Width, p.Height); 
      } 
     } 
    } 
} 
1

Bạn có bao nhiêu ảnh trong thư mục đó. Tôi thấy bạn lặp qua tất cả các hình ảnh và tải chúng vào bộ nhớ trực tiếp và lưu trữ chúng trong một danh sách. Nó có thể là nguyên nhân.

+0

Có vẻ như những gì anh ta đang cố gắng làm, ít nhất, là vòng qua chúng, có được chiều cao, chiều rộng và hình thu nhỏ, và chỉ lưu trữ chúng. Vì vậy, mỗi hình ảnh thực tế chỉ nên có trong bộ nhớ trong khi những hình ảnh đang được tính toán. –

+0

Vâng, đúng vậy. –

1

Tệp ảnh của bạn lớn đến mức nào? Có bao nhiêu tệp trong thư mục của bạn? Tùy thuộc vào các thông số đó tôi có thể thấy cách bạn có thể nhận được OutOfMemory. Bạn có thể muốn kiểm tra lại cách tiếp cận của mình. Thay vì tải tất cả mọi thứ vào bộ nhớ, bạn nên tải từng hình ảnh riêng lẻ, thực hiện hành động của bạn và sau đó tiếp tục (sau khi bạn xử lý hình ảnh trước đó của bạn, tất nhiên).

+0

Vui lòng xem chỉnh sửa của tôi. :) –

2

Bạn không nên lưu trữ tất cả hình ảnh trong một danh sách, bạn nên làm những gì bạn có với một hình ảnh tại một thời điểm và xử lý mỗi hình ảnh sau mỗi lần lặp.

Kiểm tra phương thức Image.Dispose().

+0

Làm thế nào tôi có thể vứt bỏ một lớp học được instanced? –

+0

bạn có thể sử dụng câu lệnh sử dụng, như sử dụng (Picture p = new Picture()) {// do work here} sau đó phương thức vứt bỏ được gọi ở cuối dấu ngoặc đơn hoặc bạn có thể gọi phương thức Dispose() đơn giản Ảnh. – Pedro

5

Một vài vấn đề tôi thấy off the bat. Thứ nhất, bạn đang tải mỗi hình ảnh hai lần với các cuộc gọi tiếp theo của bạn để CalculatePictureWidth và CalculatePictureHeight. Thứ hai, bạn không bao giờ thực sự làm bất cứ điều gì với hình thu nhỏ, nó xuất hiện. Thứ ba, bạn nên gọi Dispose trên trường hợp Image khi bạn đã hoàn thành việc thu thập thông tin từ chúng.

+0

Bạn có thể xem bản chỉnh sửa của tôi không. –

+0

Vâng, mà không nhìn thấy StackTrace của Out Of Memory ngoại lệ (khi ngoại lệ phá vỡ vào IDE, bạn sẽ có thể bấm vào CallStack Window (Debug-> Windows-> CallStack), điều duy nhất tôi nhận thấy là bạn đang lấy FileInfo, lặp qua mỗi File, chuyển Name đến một hàm khác và sau đó lấy lại FileInfo tương tự, có thể được sắp xếp hợp lý, nhưng tôi không nghĩ nó gây ra vấn đề của bạn –

3

Tệp hình ảnh chứa một tay cầm cho một khối lớn dữ liệu không được quản lý. Dữ liệu này phải được loại bỏ và bạn làm điều này bằng cách gọi rõ ràng Image.Dispose. Bởi vì bạn không gọi Dispose trong phương thức calculatePictureHeight và calculatePictureWidth, các tệp hình ảnh được lưu giữ trong bộ nhớ, điều này gây ra các ngoại lệ OOM của bạn.

[Cập nhật]: Có lẽ tôi ít nền tảng hữu ích hơn, tại sao điều này thực sự xảy ra. Sau khi tất cả, không phải là GC để làm sạch đống lộn xộn này cho chúng ta? Có, GC là khá hiệu quả. Nó cuối cùng cũng sẽ làm sạch các đối tượng, mà chúng tôi đã phải vứt bỏ.

Lớp hình ảnh chứa trình kết thúc. Trình kết thúc này sẽ đảm bảo rằng tất cả các tài nguyên sẽ bị xóa, ngay cả khi bạn quên gọi Dispose. Tuy nhiên, những gì xảy ra ở đây là bạn hết bộ nhớ, ngay lập tức kích hoạt GC.Collect. Trong quá trình thu thập này, GC sẽ tìm tất cả các Hình ảnh không được trả lời của bạn. Tuy nhiên, vì GC sẽ không chạy phương thức finalizer ngay lập tức, tất cả các đối tượng của bạn được quảng bá Gen1 (chúng được lưu giữ trong bộ nhớ) và vì vậy tất cả các tài nguyên của bạn (bộ nhớ riêng). Vì vậy, ngay cả sau khi GC đã khởi động, vẫn còn quá ít bộ nhớ và bạn nhận được một ngoại lệ OOM.

Đây là lý do tại sao bạn nên luôn bỏ các đối tượng triển khai IDisposable.

6

Bạn phải giải phóng tài nguyên được sử dụng khi mở đối tượng Image.

Hoặc bạn có thể gọi hàm Dispose, hoặc tạo ra hình ảnh của bạn trong một tuyên bố Using

ví dụ

public override void getThumbnail(string filePath) 
{ 
    using (Image image = Image.FromFile(filePath)) 
    { 
     Thumbnail = image.GetThumbnailImage(40, 40, null, new IntPtr()); 
    } 
} 

Và như lớp học của bạn có chứa một Image bạn nên thực hiện các giao diện IDisposable, vì vậy bạn cũng có thể sử dụng nó trong một tuyên bố using.

+0

Tôi không thấy 'sử dụng 'tuyên bố nhận được sử dụng rất thường xuyên. Nó chỉ là nó không nổi tiếng? – FrustratedWithFormsDesigner

+2

Tôi sử dụng nó ở khắp mọi nơi tôi có thể (tức làkhi lớp đích là 'IDisposable' và luồng chương trình của tôi cho phép nó). Bạn không cần phải chơi với 'Dispose', và bạn chắc chắn rằng việc quản lý bộ nhớ của bạn được thực hiện đúng. – Shimrod

+0

Điều này đã sửa nó cho tôi. – sauv0168

1

Hãy thử xem từng ảnh một. Tôi đã tìm thấy một số nguyên nhân của .jpg này - các thư viện thao tác hình ảnh có vấn đề với một số hình ảnh, không chắc chắn lý do tại sao. Nhưng chúng tôi đã có một số .jpg được lưu từ photoshop một số cách kỳ lạ gây ra điều này. Tôi sẽ đặt cược bạn sẽ tìm thấy nó là một quả táo xấu.

-1

Một số người đã đề cập đến việc bỏ cuộc gọi khi thực hiện xong với hình ảnh. Tuy nhiên, điều này là không đủ và một cái gì đó.net framework sẽ làm cho bạn khi sử dụng bộ nhớ tăng lên. Vì bạn đã thêm từng ảnh vào một danh sách được liên kết nên khung công tác .NET không thể xử lý nó, vì bạn ngầm giữ một tham chiếu.

Điều này có nghĩa là tất cả dữ liệu được lưu trữ trong lớp hình ảnh được lưu giữ trong vòng lặp foreach. Từ đoạn mã tôi không thể nhìn thấy nó là gì, nó có thể bao gồm một phiên bản nén hoặc thậm chí là một hình ảnh không nén mà có thể giải thích sự bùng nổ bộ nhớ của bạn.

[Chỉnh sửa] đã xóa một phần của anwer mà những người khác đã chỉ ra là không liên quan; Tôi cho rằng lớp hình ảnh là lớp hình ảnh XNA.

+0

Tôi không sử dụng XNA, lớp của tôi chỉ được gọi là Hình ảnh. –

+0

Những hình ảnh tôi đề cập đến khi tôi nói về việc xử lý chúng là những hình ảnh thực tế, không phải hình thu nhỏ. Vì vậy, tôi nghĩ (nhưng tôi phải nhầm lẫn) rằng không có tài liệu tham khảo được lưu giữ và do đó xử lý chúng sẽ là sufficent. Những người duy nhất sẽ được giữ lại là những hình thu nhỏ, và đó là lý do tại sao tôi khuyên anh ta nên thực hiện 'IDisposable' trong lớp' Picture' của mình. – Shimrod

+0

khuôn khổ sẽ gọi là hoàn thành khi thu thập rác tuy nhiên ông giữ một tham chiếu đến hình ảnh để nó sẽ không bao giờ được thu thập –

0

Bạn có thể làm điều này không?

public class Picture:BaseFile 
{ 
    public int Height { get; set; } 
    public int Width { get; set; } 
    public Image Thumbnail { get; set; } 

    /// <summary> 
    /// Sets file information of an image from a given image in the file path. 
    /// </summary> 
    /// <param name="filePath">File path of the image.</param> 
    public override void getFileInformation(string filePath) 
    { 
     FileInfo fileInformation = new FileInfo(filePath); 
     if (fileInformation.Exists) 
     { 
      /* 
      Name, FileType, Size, etc 
      */ 
      using (Image image = Image.FromFile(filePath)) 
      { 
       Height = image.Height; 
       Width = image.Width; 
       Thumbnail = image.GetThumbnailImage(40, 40, new Image.GetThumbnailImageAbort(ThumbnailCallback), default(IntPtr)); 
      } 
     } 
    } 

    public bool ThumbnailCallback() 
    { 
     return false; 
    } 
} 
0

Tôi nghĩ vấn đề là:

Nếu bạn gọi GetThumbnailImage với một trong những chiều rộng hoặc chiều cao đối số là -1, nó sẽ ném một OutOfMemoryException là tốt.

hoặc

Nếu bạn tạo một đối tượng hình ảnh bằng cách sử dụng Image.FromStream (ví dụ như một MemoryStream) và sau đó đóng dòng (MSDN nói rõ ràng không làm điều đó cho thời gian tồn tại của đối tượng) sau đó gọi này phương thức (GetThumbnailImage) trên đối tượng sẽ ném ra một OutOfMemoryException.

Các vấn đề liên quan