Doubly linked list consuming too much memory 双向链表占用过多内存

huangapple go评论56阅读模式
英文:

Doubly linked list consuming too much memory

问题

以下是翻译好的部分:

我正在制作一个应用程序,该应用程序在WindowsForm PictureBox中打开图像,并通过单击左箭头或右箭头来切换到同一文件夹中的上一个或下一个图像。

为了更快地切换图像而无需等待加载,我让程序预先加载图像,但不是从整个文件夹加载,只加载其中的16个图像:当前显示的图像、前面的5个图像和后面的10个图像。

这些图像存储在一个双向链表中,但由于某种原因,程序没有丢弃不再使用的类中的图像,因此查看更多图像会导致程序使用更多内存。

如何解决这个问题?

我尝试在NextImage和PreviousImage函数的末尾使用“System.GC.Collect()”,但没有效果。

这是用于管理的类的代码:

public class ImageController
{
    private string imageFolder = "";

    // 文件夹中每个图像的路径列表
    private List<string> imageFiles;

    private LinkedList<Bitmap> imageLinkedList;
    private LinkedListNode<Bitmap> currentNode;

    // 支持的扩展名列表
    private HashSet<string> supportedExtensions = new HashSet<string>() { "bmp", "gif", "exif", "jpg", "png", "tiff" };

    // 当前图像在imageFiles中的索引
    private IndexObject currentIndex;

    // oldestLoadedImage在imageFiles中的索引
    private IndexObject oldestIndex;

    // newestLoadedImage在imageFiles中的索引
    private IndexObject newestIndex;

    // 链表的限制
    private int oldCache = 5;
    private int newCache = 10;

    // 返回当前图像
    public Bitmap Image { get { return currentNode.Value; } }

    // 返回当前图像的路径
    public string ImagePath { get { return imageFiles[currentIndex.Index]; } }

    public ImageController(string fileName)
    {
        imageFolder = Path.GetDirectoryName(fileName);
        imageFiles = Directory.EnumerateFiles(imageFolder).ToList();
        imageFiles.RemoveAll(t => !supportedExtensions.Contains(Path.GetExtension(t).Replace(".", "").ToLower()));

        currentIndex    = new IndexObject(imageFiles.IndexOf(fileName), imageFiles.Count);
        oldestIndex     = new IndexObject(currentIndex.Index, imageFiles.Count);
        newestIndex     = new IndexObject(currentIndex.Index, imageFiles.Count);

        imageLinkedList = new LinkedList<Bitmap>();
        LoadCache();
    }

    private void LoadCache()
    {
        currentNode = imageLinkedList.AddFirst(new Bitmap(imageFiles[currentIndex.Index]));

        // 加载左侧
        for (int i = 0; i < oldCache; i++)
        {
            oldestIndex.Index--;
            imageLinkedList.AddFirst(new Bitmap(imageFiles[oldestIndex.Index]));
        }

        // 加载右侧
        for (int i = 0; i < newCache; i++)
        {
            newestIndex.Index++;
            imageLinkedList.AddLast(new Bitmap(imageFiles[newestIndex.Index]));
        }
    }

    public Bitmap NextImage()
    {
        currentIndex.Index++;
        oldestIndex.Index++;
        newestIndex.Index++;

        // 删除最旧的图像
        imageLinkedList.First.Value.Dispose();
        imageLinkedList.RemoveFirst();

        // 将新图像添加到链表中
        imageLinkedList.AddLast(new Bitmap(imageFiles[newestIndex.Index]));

        currentNode = currentNode.Next;

        return currentNode.Value;
    }

    public Bitmap PreviousImage()
    {
        currentIndex.Index--;
        oldestIndex.Index--;
        newestIndex.Index--;

        // 删除最新的图像
        imageLinkedList.Last.Value.Dispose();
        imageLinkedList.RemoveLast();

        // 将新图像添加到链表中
        imageLinkedList.AddFirst(new Bitmap(imageFiles[oldestIndex.Index]));

        currentNode = currentNode.Previous;

        return currentNode.Value;
    }
}

public class IndexObject
{
    private int _index;
    private int _limit;
    public IndexObject(int index, int limit)
    { 
        _index = index;
        _limit = limit;
    }
    public int Index
    {
        get
        {
            if (_index >= 0)
                return _index % _limit;
            else
                return _limit + _index;
        }
        set
        {
            if (value >= 0)
                _index = value % _limit;
            else
                _index = _limit + value;
        }
    }
}

编辑:
我曾经使用自己创建的链表类,但@cup建议我使用C# LinkedList,这是我不知道的。我修改了代码以使用这个LinkedList,因为它使代码看起来比我的链表类更清晰。
我还添加了@marsh-wiggle的解决方案,实际上是调用了Bitmap的Dispose方法。

英文:

I`m making an application that opens an image in a WindowsForm PictureBox and by clicking left arrow or right arrow, it will change to previous or next image in the same folder.

In order to change image the image faster without having to wait to load, I made the program load the images in advance but not from the entire folder, only 16 of them : the current one being displayed, 5 previous ones and 10 next ones.

The images are stored in a Doubly Linked List but for some reason the program is not discarding the images in the classes that are not being used anymore so the program keeps using more and more memory the more images i see.

How can i solve this issue ?

I tried using "System.GC.Collect()" at the end of NextImage and PreviousImage functions but it didn`t work.

This is the code of the class used to manage this :

 public class ImageController
{
private string imageFolder = &quot;&quot;;
// List with the paths of every image in the folder
private List&lt;string&gt; imageFiles;
private LinkedList&lt;Bitmap&gt; imageLinkedList;
private LinkedListNode&lt;Bitmap&gt; currentNode;
// List of supported extensions
private HashSet&lt;string&gt; supportedExtensions = new HashSet&lt;string&gt;() { &quot;bmp&quot;, &quot;gif&quot;, &quot;exif&quot;, &quot;jpg&quot;, &quot;png&quot;, &quot;tiff&quot; };
// Index in the imageFiles that correspond to currentImage
private IndexObject currentIndex;
// Index in the imageFiles that correspond to oldestLoadedImage
private IndexObject oldestIndex;
// Index in the imageFiles that correspond to newestLoadedImage
private IndexObject newestIndex;
// Limits of linked list
private int oldCache = 5;
private int newCache = 10;
// Returns current image
public Bitmap Image { get { return currentNode.Value; } }
// Returns path of current image
public string ImagePath { get { return imageFiles[currentIndex.Index]; } }
public ImageController(string fileName)
{
imageFolder = Path.GetDirectoryName(fileName);
imageFiles = Directory.EnumerateFiles(imageFolder).ToList();
imageFiles.RemoveAll(t =&gt; !supportedExtensions.Contains(Path.GetExtension(t).Replace(&quot;.&quot;, &quot;&quot;).ToLower()));
currentIndex    = new IndexObject(imageFiles.IndexOf(fileName), imageFiles.Count);
oldestIndex     = new IndexObject(currentIndex.Index, imageFiles.Count);
newestIndex     = new IndexObject(currentIndex.Index, imageFiles.Count);
imageLinkedList = new LinkedList&lt;Bitmap&gt;();
LoadCache();
}
private void LoadCache()
{
currentNode = imageLinkedList.AddFirst(new Bitmap(imageFiles[currentIndex.Index]));
// Load left side
for (int i = 0; i &lt; oldCache; i++)
{
oldestIndex.Index--;
imageLinkedList.AddFirst(new Bitmap(imageFiles[oldestIndex.Index]));
}
// Load right side
for (int i = 0; i &lt; newCache; i++)
{
newestIndex.Index++;
imageLinkedList.AddLast(new Bitmap(imageFiles[newestIndex.Index]));
}
}
public Bitmap NextImage()
{
currentIndex.Index++;
oldestIndex.Index++;
newestIndex.Index++;
// Delete oldest image
imageLinkedList.First.Value.Dispose();
imageLinkedList.RemoveFirst();
// Add new image to Linked List
imageLinkedList.AddLast(new Bitmap(imageFiles[newestIndex.Index]));
currentNode = currentNode.Next;
return currentNode.Value;
}
public Bitmap PreviousImage()
{
currentIndex.Index--;
oldestIndex.Index--;
newestIndex.Index--;
// Delete newest image
imageLinkedList.Last.Value.Dispose();
imageLinkedList.RemoveLast();
// Add new image to Linked List
imageLinkedList.AddFirst(new Bitmap(imageFiles[oldestIndex.Index]));
currentNode = currentNode.Previous;
return currentNode.Value;
}
}
public class IndexObject
{
private int _index;
private int _limit;
public IndexObject(int index, int limit)
{ 
_index = index;
_limit = limit;
}
public int Index
{
get
{
if (_index &gt;= 0)
return _index % _limit;
else
return _limit + _index;
}
set
{
if (value &gt;= 0)
_index = value % _limit;
else
_index = _limit + value;
}
}
}

Edits :
I was using my own linked list class that I created but @cup gave me the suggestion of using C# LinkedList<T> which I was not aware of. I changed the code to use this LinkedList<T> because it made the code look cleaner than my own linked list class.
I also added @marsh-wiggle solution which was actually to call the dispose method for the Bitmap.

答案1

得分: 0

看起来你从未处理旧图像。在加载新图像之前尝试调用以下内容。这可以减少你的内存消耗。

DisposeLinkedImage(oldestLoadedImage);

private void DisposeLinkedImage(LinkedImage linkedImage)
{
    linkedImage.Image.Dispose();
    linkedImage.Image = null;
}
英文:

It looks like you never dispose old images. Try to call something this before loading a new image. This may reduce your memory consumption.

DisposeLinkedImage(oldestLoadedImage);
private void DisposeLinkedImage(LinkedImage linkedImage)
{
linkedImage.Image.Dispose();
linkedImage.Image = null;
}

huangapple
  • 本文由 发表于 2023年5月30日 00:37:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76358981.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定