2013-07-23 28 views
5

Tôi đang cố định lại kích thước hình ảnh trong một tác vụ hàng loạt. Khi tôi sử dụng các lớp .Net được cung cấp, bộ nhớ không được phát hành đúng cách để OutOfMemoryException được ném ra. Tôi nghĩ tôi sử dụng câu lệnh đúng cách. Mã bên dưới:. Rò rỉ bộ nhớ thay đổi kích thước hình ảnh Net

private static byte[] Resize(byte[] imageBytes, int width, int height) 
    { 
      using (var img = Image.FromStream(new MemoryStream(imageBytes))) 
      { 
       using (var outStream = new MemoryStream()) 
       { 
        double y = img.Height; 
        double x = img.Width; 

        double factor = 1; 
        if (width > 0) 
         factor = width/x; 
        else if (height > 0) 
         factor = height/y; 

        var imgOut = new Bitmap((int)(x * factor), (int)(y * factor)); 
        var g = Graphics.FromImage(imgOut); 
        g.Clear(Color.White); 
        g.DrawImage(img, new Rectangle(0, 0, (int)(factor * x), 
                (int)(factor * y)), 
           new Rectangle(0, 0, (int)x, (int)y), GraphicsUnit.Pixel); 

        imgOut.Save(outStream, ImageFormat.Jpeg); 

        return outStream.ToArray(); 
       } 
      } 
     } 

Thay thế cho mã này là sử dụng thư viện FreeImage. Khi tôi sử dụng FreeImage, không có vấn đề về bộ nhớ. Mã với FreeImage:

private static byte[] Resize(byte[] imageBytes, int width, int height) 
    { 
     var img = new FIBITMAP(); 
     var rescaled = new FIBITMAP(); 
     try 
     { 
      using (var inStream = new MemoryStream(imageBytes)) 
      { 
       img = FreeImage.LoadFromStream(inStream); 
       rescaled = FreeImage.Rescale(img, width, height, FREE_IMAGE_FILTER.FILTER_BICUBIC); 

       using (var outStream = new MemoryStream()) 
       { 
        FreeImage.SaveToStream(rescaled, outStream, FREE_IMAGE_FORMAT.FIF_JPEG); 
        return outStream.ToArray(); 
       } 
      } 
     } 
     finally 
     { 
      if (!img.IsNull) 
       FreeImage.Unload(img); 

      img.SetNull(); 

      if (!rescaled.IsNull) 
       FreeImage.Unload(rescaled); 

      rescaled.SetNull(); 
     } 
    } 

Điều gì bị thiếu trong mã đầu tiên của tôi?

+2

Bạn không sử dụng Sử dụng trên Bitmap .... bạn cũng muốn đặt một Sử dụng trên Graphics g quá. –

+1

Bạn không bỏ đối tượng 'Graphics' (' var g = Graphics.FromImage (imgOut); ') –

+0

hoặc trên đối tượng' g' - đối tượng Đồ họa – SteveLove

Trả lời

5

Tôi tin rò rỉ của bạn là với hai dòng sau:

var imgOut = new Bitmap((int)(x * factor), (int)(y * factor)); 
var g = Graphics.FromImage(imgOut); 

Cả BitmapGraphics thực hiện IDisposable và do đó cần được xử lý khi bạn đã kết thúc việc sử dụng chúng.

tôi sẽ đề nghị gói chúng cả trong một khối using:

using(imgOut = new Bitmap((int)(x * factor), (int)(y * factor))) 
{ 
    using(var g = Graphics.FromImage(imgOut)) 
    { 
     //rest of code... 
    } 
} 

Here is a list of GDI objects để giữ một mắt ra cho, chắc chắn rằng bạn làm sạch chúng lên đúng nếu bạn sử dụng chúng.

+0

cũng, 'sử dụng (var g = Graphics.FromImage (imgOut))' ... –

+0

@AlexFilipovici: Vâng, tôi nhận thấy các nhận xét. Tôi đã không nhận thức được sự cần thiết phải vứt bỏ mà vì vậy tôi chỉ điều tra nó đầu tiên. Tôi chỉ có thể sử dụng nó trong một bộ xử lý sự kiện sơn, vì vậy có lẽ đó là lý do tại sao tôi đã không nhận thấy một vấn đề trước khi – musefan

0

Một cách chính xác hơn:

private static byte[] Resize(byte[] imageBytes, int width, int height) 
    { 
     using (var imagestream = new MemoryStream(imageBytes)) 
     { 
      using (var img = Image.FromStream(imagestream)) 
      { 
       using (var outStream = new MemoryStream()) 
       { 
        double y = img.Height; 
        double x = img.Width; 

        double factor = 1; 
        if (width > 0) 
         factor = width/x; 
        else if (height > 0) 
         factor = height/y; 

        using (var imgOut = new Bitmap((int)(x * factor), (int)(y * factor))) 
        { 
         using (var g = Graphics.FromImage(imgOut)) 
         { 
          g.Clear(Color.White); 
          g.DrawImage(img, new Rectangle(0, 0, (int)(factor * x), 
                (int)(factor * y)), 
           new Rectangle(0, 0, (int)x, (int)y), GraphicsUnit.Pixel); 
         } 

         imgOut.Save(outStream, ImageFormat.Jpeg); 
        } 

        return outStream.ToArray(); 
       } 
      } 
     } 

} 

Bạn cũng cần phải được rất cẩn thận khi phân bổ và thu hồi các đối tượng lớn (những người> = 85000 bytes) ... bởi vì họ đi trên LOH (Object lớn Heap), và nó có thể phân mảnh nó và sau đó chạy ra khỏi bộ nhớ nhanh hơn bạn mong đợi (có nhiều kỹ thuật khác nhau để có được xung quanh đó nếu bạn chạy vào vấn đề đó).