Concepts of Allocation and Release of Memory in C #

11

I'm doing a web application that has a finite recursive loop. But during the process memory consumption is constantly increasing and in the end Visual Studio has more than 2 Gigabytes of memory usage.

I'd like to know some concepts about memory allocation and deallocation (if that's what those terms are) and what can cause that huge consumption, or what artifacts can I use to free memory.

I'm not very experienced and I believe this may be caused by non-optimized (not to say bad) codes. Here is my code:

protected void BtnExportar_OnClick(object sender, EventArgs e)
{
    string folder = @"D:\Fichas\";

    DateTime data = new DateTime();
    data = DateTime.Now;
    data = data.Date;
    string dataSoNumeros = new String(data.ToString().Where(Char.IsDigit).ToArray());

    if (!Directory.Exists(folder))
    {
        Directory.CreateDirectory(folder);
    }

    if (!Directory.Exists(folder + dataSoNumeros.Substring(0, 8)))
    {
        Directory.CreateDirectory(folder + dataSoNumeros.Substring(0, 8));
    }

    Stop = false;

    this.IniciarExportacao();
}

public void IniciarExportacao()
{
    IList<Ficha> Fichas = null;

    do
    {
        Fichas = this.ObterFichas();
        this.GerarArquivo(Fichas);
    }
    while (Fichas.Count > 0 && Stop == false);

    detalhesArquivo.Text = "Exportação Concluída.";
}    

public IList<Ficha> ObterFichas()
{        
    DetachedCriteria criteria = DetachedCriteria.For<Ficha>();

    criteria.SetMaxResults(1);

    criteria.CreateAlias("exportacao", "e",NHibernate.SqlCommand.JoinType.LeftOuterJoin).Add(Restrictions.IsNull("e.Exportado"));

    var fichasObtidas = DomainService.FichaRepository().LoadAll(criteria);

    return fichasObtidas;
}

public void GerarArquivo(IList<Ficha> Fichas)
{
    msgLog = new StringBuilder();        

    msgLog_Inicio  = "    - Iniciando Exportação da Ficha " + Fichas[0].IdFicha + " / Conta " + Fichas[0].Conta.IdConta + "...";
    msgLog_Gerando = "    - Gerando imagem do arquivo...";        

    foreach (var index in Fichas)
    {
        #region DEFAULT

        byte[] arqB = null;
        string nomarq = "";
        string saveLocation = null;
        System.Drawing.Image imageArquivo = null;

        arqB = index.Arquivo;
        nomarq = index.Conta.IdConta + "_" + index.IdFicha;

        DateTime data = new DateTime();
        data = DateTime.Now;
        data = data.Date;
        string dataSoNumeros = new String(data.ToString().Where(Char.IsDigit).ToArray());

        saveLocation = @"D:\fichas\" + dataSoNumeros.Substring(0, 8) + @"\" + nomarq + ".jpg";

        Ficha ficha = new Ficha();
        Exportacao fichaExportacao = new Exportacao();

        ficha = index;
        fichaExportacao.Ficha = index;
        fichaExportacao.IdFicha = index.IdFicha;            
        fichaExportacao.DataExportacao = DateTime.Now;
        fichaExportacao.IdUsuarioExportador = ControlUsuario.GetSession.Usuario.IdUsuario;

        #endregion

        try
        {
            imageArquivo = this.byteArrayToImage(arqB);
            imageArquivo.Save(saveLocation, System.Drawing.Imaging.ImageFormat.Jpeg);
            fichaExportacao.Exportado = 1;
            msgLog_Imagem = "    - Arquivo gerado na pasta "+ saveLocation +"";
        }
        catch (Exception)
        {
            fichaExportacao.Exportado = 0;
            msgLog_Imagem = "    - O arquivo está corrompido e não foi gerado...";
        }

        msgLog.AppendLine(msgLog_Inicio);
        msgLog.AppendLine(msgLog_Gerando);
        msgLog.AppendLine(msgLog_Imagem);

        this.AtualizarFicha(ficha, fichaExportacao, saveLocation, msgLog);            
    }
}

public void AtualizarFicha(Ficha ficha, Exportacao fichaExportacao, string saveLocation, StringBuilder msgLog)
{
    if (ficha.exportacao == null)
    {
        try
        {
            DomainService.ExportacaoRepository().SaveNew(fichaExportacao);
            msgLog_Update = "    - Atualizando registro no banco de dados...";
            msgLog_Usuario = "    - Exportação concluída por " + ControlUsuario.GetSession.Usuario.Nome + ".";
        }
        catch (Exception)
        {
            msgLog_Update = "    - Falha ao atualizar banco de dados...";
            msgLog_Usuario = "    - Exportação não pôde ser concluída. Exportador: " + ControlUsuario.GetSession.Usuario.Nome + ".";
        }
    }
    else
    {
        try
        {
            DomainService.ExportacaoRepository().Update(fichaExportacao);
            msgLog_Update = "    - Atualizando registro no banco de dados...";
            msgLog_Usuario = "    - Exportação concluída por " + ControlUsuario.GetSession.Usuario.Nome + ".";
        }
        catch (Exception)
        {
            msgLog_Update = "    - Falha ao atualizar banco de dados...";
            msgLog_Usuario = "    - Exportação não pôde ser concluída. Exportador: " + ControlUsuario.GetSession.Usuario.Nome + ".";
        }

    }

    msgLog.AppendLine(msgLog_Update);
    msgLog.AppendLine(msgLog_Usuario);

    using (StreamWriter w = new StreamWriter(@"D:\fichas\log.txt", true, Encoding.UTF8))
    {                
        Log(msgLog, w);
    }

    using (StreamReader r = File.OpenText(@"D:\fichas\log.txt"))
    {
        DumpLog(r);
    }
}

#region ARQUIVO IMAGEM

public byte[] Arquivo(object sender, EventArgs e)
{
    return base.Cache[base.Request.QueryString["id"]] as byte[];
}  

public byte[] imageToByteArray(System.Drawing.Image imageIn)
{
    using (var ms = new MemoryStream())
    {
        imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
        return ms.ToArray();
    }
}

public System.Drawing.Image byteArrayToImage(byte[] byteArrayIn)
{
    MemoryStream ms = new MemoryStream(byteArrayIn);
    System.Drawing.Image returnImage = System.Drawing.Image.FromStream(ms);
    return returnImage;
}

#endregion

#region LOGS

public static void Log(StringBuilder msgLog, TextWriter w)
{
    w.WriteLine("[{0} {1}] ---------------------------------------", DateTime.Now.ToLongTimeString(), DateTime.Now.ToLongDateString());
    w.WriteLine("{0}", msgLog.ToString());
}

public static void DumpLog(StreamReader r)
{
    string line;
    while ((line = r.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
}

#endregion
    
asked by anonymous 09.05.2014 / 20:01

3 answers

11

You have two problems here.

The first is images that can remain indefinitely in memory. You do not call the Dispose() method and let your reference fall out of scope. From here the memory allocated for the image will only be released when the Finalize() method is called by the Garbage Collector. And the GC is usually only called when there is a need to allocate memory, not always. So it is possible that the memory will only be released after all loops have run. The solution to this? Call Dispose() at the end of each loop!

  

MSDN : Always call Dispose before you release your last reference to the Image. Otherwise, the resources it is using will not be freed until the garbage collector calls the Image object's Finalize method.

The second is with the log. You're holding it down with a% of% in memory . So when the last record loop is running you will have a huge string saved. The correct one with logs is to write it directly to files. Even because if all else fails and your program crashes, you will have the log saved. Open a file at the beginning of the process and go directly by typing the messages in this file.

    
09.05.2014 / 21:28
6

Your imageArquivo variable is of type System.Drawing.Bitmap . This type inherits from System.Drawing.Image , which implements the IDIsposable . Hence you do not call the Dispose method of these images anywhere, you can this is the cause of excessive memory consumption. Images do not stay in memory space managed by GC;)

In other words: Garbage Collector has no way of knowing when the memory occupied by the images can be released. It is up to you to indicate to the GC when it can deallocate this memory. You can do this in two ways: by explicitly calling the Dispose method in your images when you are sure they will not be used anymore , or create the images using the word

09.05.2014 / 20:19
0

My error in question was to use this method of reading the file in each loop:

public static void DumpLog(StreamReader r)
{
    string line;
    while ((line = r.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
}
    
09.05.2014 / 21:27