2

I have a Visual Studio c# app whose task is download pictures, make some operations like rotations and flip and finnaly store the new picture in an external HD.

I have several threads doing the same work.

When the app has been running for a couple of hours, it arises an exception indicating that the lack of storage memory.

The following picture shows the process memory when the app starts, but when it's been running for a while, such memory increase up to 3Gb.

enter image description here

In the program I close all streams, memorystreams, bitmaps, instances and so on. So, the question is, How can I free memory or avoiding the memory increase during the execution?

try
 {
    request = (HttpWebRequest)WebRequest.Create(new Uri(this._url, UriKind.Absolute));
    request.UserAgent = UserAgent.get_user_agent();
    noCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
    request.CachePolicy = noCachePolicy;
    response = request.GetResponse();
    responseStream = response.GetResponseStream();
    reader = new BinaryReader(responseStream);
    memoryStream = new MemoryStream();

    byte[] bytebuffer = new byte[BytesToRead];
    int bytesRead = reader.Read(bytebuffer, 0, BytesToRead);

    while (bytesRead > 0)
    {
        memoryStream.Write(bytebuffer, 0, bytesRead);
        bytesRead = reader.Read(bytebuffer, 0, BytesToRead);
    }

    image.BeginInit();
    memoryStream.Seek(0, SeekOrigin.Begin);

    image.StreamSource = memoryStream;
    image.EndInit();

    if ( image.Width < 100 || image.Height < 50 )
    {
        _parent.working_image = "Error procesado imagen";
        _parent.isWorking = false;                                                         
        return false;
    }            

    Bitmap image_rotated = CropImage(RotateFlipImage(BitmapImage2Bitmap(image)));            
    if (image_rotated == null)            
    {               
        _parent.working_image = "Error procesado imagen";
        _parent.isWorking = false;
        return false;
    }

    BitmapFrame image200 = CreateResizedImage(Bitmap2BitmapImage(image_rotated), "large");
    BitmapFrame image100 = CreateResizedImage(Bitmap2BitmapImage(image_rotated), "short");

    // Directorio
    int directory_main = (int)(this._Id / 10000);

    // Generamos la imagen de 200px          
    JpegBitmapEncoder encoder = new JpegBitmapEncoder();
    String photolocation = @"D:\hilo_" + hilo + "_200.jpg";  //file name                 
    encoder.Frames.Add(BitmapFrame.Create(image200));
    FileStream filestream = null;

    try
    {
        using (filestream = new FileStream(photolocation, FileMode.Create))
        {
            encoder.Save(filestream);
        }

        // creamos directorio si no existe
        System.IO.Directory.CreateDirectory(@"D:\backoffice\originales\" + directory_main + @"\" + this._Id);
        if (!this.saveImage(photolocation, @"D:\backoffice\originales\" + directory_main + @"\" + this._Id + @"\" + _filename + "_200.jpg"))
        {
            _parent.working_image = "Error procesado imagen";
            _parent.isWorking = false;
            image = null;
            memoryStream.Close();
            filestream.Close();
            System.GC.Collect();
            System.GC.WaitForPendingFinalizers();
            return false;
        }

        filestream.Close();

        // Generamos la imagen de 100px
        encoder = new JpegBitmapEncoder();
        photolocation = @"D:\hilo_" + hilo + "_100.jpg";  //file name 
        encoder.Frames.Add(BitmapFrame.Create(image100));

        using (filestream = new FileStream(photolocation, FileMode.Create))
        {
            encoder.Save(filestream);
        }

        // creamos directorio si no existe
        if (!this.saveImage(photolocation, @"D:\backoffice\originales\" + directory_main + @"\" + this._Id + @"\" + _filename + "_100.jpg"))
        {
            _parent.working_image = "Error procesado imagen";
            _parent.isWorking = false;
            image = null;
            memoryStream.Close();
            filestream.Close();
            System.GC.Collect();
            System.GC.WaitForPendingFinalizers();
            return false;
        }

        filestream.Close();
        encoder = null;
        image100 = null;
        image200 = null;

        SqlConnection con = new SqlConnection(@"Data Source=DESKTOP-UC7AQ3V\SQLEXPRESS;Integrated Security=SSPI;User ID=frank;password=jungla");
        SqlCommand cmd = new SqlCommand("UPDATE dbo.image_objects_download_log SET locked=0, worked=1, isWorking=1 WHERE object_id=@id", con);
        cmd.Parameters.AddWithValue("@id", Id);
        con.Open();
        cmd.ExecuteNonQuery();
        cmd.Dispose();
        con.Close();
         _parent.working_image = "Trabajo finalizado con éxito";
        _parent.isWorking = false;
        image = null;
        memoryStream.Close();
        System.GC.Collect();
        System.GC.WaitForPendingFinalizers();
        return true;
    }
    catch (IOException e)
    {
        Console.WriteLine("---------> " + e.Message);
        _parent.working_image = "Error procesado imagen";
        _parent.isWorking = false;
        image = null;
        memoryStream.Close();
        filestream.Close();
        System.GC.Collect();
        System.GC.WaitForPendingFinalizers();
        return false;
    }
    catch (UriFormatException e)
    {
        _parent.working_image = "Error procesado imagen";
        _parent.isWorking = false;
        image = null;
        memoryStream.Close();
        filestream.Close();
        System.GC.Collect();
        System.GC.WaitForPendingFinalizers();
        return false;
    }
}              
Romano Zumbé
  • 7,893
  • 4
  • 33
  • 55
Apalabrados
  • 1,098
  • 8
  • 21
  • 38

2 Answers2

2

I would recommend you to wrap all objects that implement IDisposable in a using block. I will demonstrate that on the response part of your code:

try
{
     request = (HttpWebRequest)WebRequest.Create(new Uri(this._url, UriKind.Absolute));
     request.UserAgent = UserAgent.get_user_agent();
     noCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
     request.CachePolicy = noCachePolicy;

     (WebResponse response = request.GetResponse())
     {
          // Rest of your code inside here
     }
}

This is particularly important on the image resources you use.

BitmapFrame image200 = null;
BitmapFrame image100 = null;

using(Bitmap image_rotated = CropImage(RotateFlipImage(BitmapImage2Bitmap(image))))
{
     if (image_rotated == null)
     {
          _parent.working_image = "Error procesado imagen";
          _parent.isWorking = false;
          return false;
     }

     image200 = CreateResizedImage(Bitmap2BitmapImage(image_rotated), "large");
     image100 = CreateResizedImage(Bitmap2BitmapImage(image_rotated), "short");
}

You should also check the method Bitmap2BitmapImage

Romano Zumbé
  • 7,893
  • 4
  • 33
  • 55
1

You are not disposing of your Bitmaps, BitmapFrames or Images. I'm not sure where you are creating your image, but I'd suggest creating it locally - so it can be disposed of locally when you have finished processing it.

Wrap the local creation of Bitmaps and Images in a Using statement, which will dispose of the objects immediately after execution. This question has more details on this

Right way to dispose Image/Bitmap and PictureBox

or

You can also call Dispose() on your image objects. Try creating the image locally and disposing of it when you've finished with it.

Use the Finalize() method to clean up the resources of your BitmapFrame.

You can also call Dispose on your image objects. Try creating the image locally and disposing of it when you've finished with it.