How i can clear IEnumerable<ImapX.Messages>?

678 Views Asked by At

I downloaded all the messages and checked their attachment . They are filling RAM. I am called that code in threads. I am try to use client.Dispose() and GC.Collect() but is is not helped :(( enter image description here

using (var client = new ImapClient(hostname, true))
{
    if (client.Connect( /* optional, use parameters here */ ))
    {
        // connection successful
        if (client.Login(login, pass))
        {
            // login successful
            FolderCollection folders = client.Folders;
            int i = 0;
            foreach (Folder myfolder in folders)
            {

                var messages = client.Folders[i].Search("ALL");
                i++;
                foreach (var message in messages)
                {
                    var attachments = message.Attachments;
                    if (attachments.Count() > 0)
                        if (!Directory.Exists(folder + @"\" + login))
                        {
                            DirectoryInfo di = Directory.CreateDirectory(folder + @"\" + login);// Try to create the directory.
                        }
                    foreach (var attachment in attachments)
                    {
                        attachment.Download();
                        attachment.Save(folder + @"\" + login);
                    }
                }

                GC.Collect();
            }
        }
    }
    client.Disconnect();
    client.Dispose();
}
2

There are 2 best solutions below

2
On

You should never call GC.Collect() until you defenitly know what are you doing. And it won't help because when your program takes a lot of RAM CLR runs GC itself. And if you see that memory consumption is growing, it seems that you have unmanaged resources leak.

I edited my code based on ImapX project: there is almost no resources that should be manually disposed. So the only thing can be done here is interning of path. You should also intern login and password as well, because there is no other strings creation by your code. With several improvements is will look like this:

public void DownloadAllAttachments()
{
    using (var client = new ImapClient(hostname, true))
    {
        if (!client.Connect( /* optional, use parameters here */) || !client.Login(login, pass))
            return;

        string path = string.Intern(Path.Combine(folder, login));
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }

        foreach (Folder myfolder in client.Folders)
        {
            foreach (var message in myfolder.Search())
            {
                foreach (var attachment in message.Attachments)
                {
                    attachment.Download();
                    attachment.Save(path);
                }
            }
        }
    }
}
4
On

First of all you are calling GC.Collect(); when Message objects still alive in the current scope.

The other problem that you are calling client.Dispose(); but using statement do actually the same because it's just syntax sugar for next code:

var client = new ImapClient(hostname, true)
try
{
    ...
}
finally
{
    if (client != null)
    {
        ((IDisposable)client).Dispose();
    }
}

Your example have no memory leaks and GC will collect Message objects on next Collect() automatically.

If you want to force garbage collection you can call GC.Collect() after using block but it is really bat practice. It is possible to force garbage collection by calling Collect, but most of the time, this should be avoided because it may create performance issues.

If it is really necessary to collect object inside the loop you try to use GCSettings.LargeObjectHeapCompactionMode property or WaitForPendingFinalizers() method. Full code will looks like this one:

foreach (Folder myfolder in folders)
{

    var messages = client.Folders[i].Search("ALL");
    i++;
    foreach (var message in messages)
    {
        var attachments = message.Attachments;
        if (attachments.Count() > 0)
            if (!Directory.Exists(folder + @"\" + login))
            {
                DirectoryInfo di = Directory.CreateDirectory(folder + @"\" + login);// Try to create the directory.
            }
        foreach (var attachment in attachments)
        {
            attachment.Download();
            attachment.Save(folder + @"\" + login);
        }
        attachments = null;
    }

    messages = null;

    // performance killer
    GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
    GC.Collect(2);
    GC.WaitForPendingFinalizers();
    GC.Collect(2); 
} 

Note that it is not production code but combined workarounds for your problem.