C # WPF app uses too much memory while GC.GetTotalMemory () is low

I wrote a small WPF application with two threads - the main thread is a GUI thread, and the other thread is a worker.
The application has one WPF form with some controls. There is a button that allows you to select a directory. After selecting a directory, the application scans the .jpg files in this directory and checks if their thumbnails are in the hash table. if they are, he does nothing. otherwise it will add their full file names to the worker queue.
The worker accepts the file names from this queue, uploads JPEG images (using WPF JpegBitmapDecoder and BitmapFrame), creating their thumbnails (using WPF TransformedBitmap) and adding them to the hash table.
Everything works fine, except for the memory consumption of this application when creating thumbnails for large images (for example, 5000x5000 pixels). I added text fields to my form to show the memory consumption (GC.GetTotalMemory () and Process.GetCurrentProcess (). PrivateMemorySize64) and was very surprised because GC.GetTotalMemory () remained close to 1-2 MB and the size is private memory is constantly growing, especially when loading a new image (~ + 100 MB per image).
Even after downloading all the images, creating their thumbnails and freeing the original images, the amount of private memory is ~ 700-800 MB. My VirtualBox is limited to 512 MB of physical memory, and Windows in VirtualBox is starting to change to cope with this huge memory consumption. I think I am doing something wrong, but I do not know how to investigate this problem, because according to GC, the size of the allocated memory is very low.

Attaching thumbnail loader class code:

class ThumbnailLoader
{
    Hashtable thumbnails;
    Queue<string> taskqueue;
    EventWaitHandle wh;
    Thread[] workers;
    bool stop;
    object locker;
    int width, height, processed, added;

    public ThumbnailLoader()
    {
        int workercount,i;
        wh = new AutoResetEvent(false);
        thumbnails = new Hashtable();
        taskqueue = new Queue<string>();
        stop = false;
        locker = new object();
        width = height = 64;
        processed = added = 0;
        workercount = Environment.ProcessorCount;
        workers=new Thread[workercount];
        for (i = 0; i < workercount; i++) {
            workers[i] = new Thread(Worker);
            workers[i].IsBackground = true;
            workers[i].Priority = ThreadPriority.Highest;
            workers[i].Start();
        }
    }

    public void SetThumbnailSize(int twidth, int theight)
    {
        width = twidth;
        height = theight;
        if (thumbnails.Count!=0) AddTask("#resethash");
    }

    public void GetProgress(out int Added, out int Processed)
    {
        Added = added;
        Processed = processed;
    }

    private void AddTask(string filename)
    {
        lock(locker) {
            taskqueue.Enqueue(filename);
            wh.Set();
            added++;
        }
    }

    private string NextTask()
    {
        lock(locker) {
            if (taskqueue.Count == 0) return null;
            else {
                processed++;
                return taskqueue.Dequeue();
            }
        }
    }

    public static string FileNameToHash(string s)
    {
        return FormsAuthentication.HashPasswordForStoringInConfigFile(s, "MD5");
    }

    public bool GetThumbnail(string filename,out BitmapFrame thumbnail)
    {
        string hash;
        hash = FileNameToHash(filename);
        if (thumbnails.ContainsKey(hash)) {
            thumbnail=(BitmapFrame)thumbnails[hash];
            return true;
        }
        AddTask(filename);
        thumbnail = null;
        return false;
    }

    private BitmapFrame LoadThumbnail(string filename)
    {
        FileStream fs;
        JpegBitmapDecoder bd;
        BitmapFrame oldbf, bf;
        TransformedBitmap tb;
        double scale, dx, dy;
        fs = new FileStream(filename, FileMode.Open);
        bd = new JpegBitmapDecoder(fs, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
        oldbf = bd.Frames[0];
        dx = (double)oldbf.Width / width;
        dy = (double)oldbf.Height / height;
        if (dx > dy) scale = 1 / dx;
        else scale = 1 / dy;
        tb = new TransformedBitmap(oldbf, new ScaleTransform(scale, scale));
        bf = BitmapFrame.Create(tb);
        fs.Close();
        oldbf = null;
        bd = null;
        GC.Collect();
        return bf;
    }

    public void Dispose()
    {
        lock(locker) {
            stop = true;
        }
        AddTask(null);
        foreach (Thread worker in workers) {
            worker.Join();
        }
        wh.Close();
    }

    private void Worker()
    {
        string curtask,hash;
        while (!stop) {
            curtask = NextTask();
            if (curtask == null) wh.WaitOne();
            else {
                if (curtask == "#resethash") thumbnails.Clear();
                else {
                    hash = FileNameToHash(curtask);
                    try {
                        thumbnails[hash] = LoadThumbnail(curtask);
                    }
                    catch {
                        thumbnails[hash] = null;
                    }
                }
            }
        }
    }
}
+3
source share
3 answers

The problem is solved.
Just needed to replace BitmapCacheOption.OnLoad with BitmapCacheOption.None :)

+3
source

, BitmapCacheOption.OnLoad Framework, , GC. BitmapCacheOption.None , . . .

+10

, - Image , GC.

, - , GC , - .

- , , , .

+2
source

Source: https://habr.com/ru/post/1739395/


All Articles