Image.SelectActiveFrame проблема с памятью - PullRequest
0 голосов
/ 26 сентября 2011

Я пишу элемент управления для показа изображений.
Моя проблема возникает с использованием класса изображений на многостраничных TIFF.
Я использую это (я публикую только соответствующий код) в начале:

Image img;
int pages;
img = Bitmap.FromFile(filename);
pages = img.GetFrameCount(FrameDimension.Page);

затем, когда пользователь хочет показать другую страницу:

public override Image GetPage(int page)
{
    if (page < 1 || page > pages) return null;
    try
    {
        #if !TEST
            img.SelectActiveFrame(FrameDimension.Page, page - 1);
            return new Bitmap(img);
        #else
            MemoryStream ms = new MemoryStream();
            img.SelectActiveFrame(FrameDimension.Page, page - 1);
            img.Save(ms, ImageFormat.Jpeg);
            Image ret = Image.FromStream(ms);
            ms.Flush();
            ms.Dispose();
            return ret;
        #endif
    }
    catch (Exception ex)
    {
        "Tiff GetPage error: {0}".ToDebug(ex.Message);
        return null;
    }
}

При использовании img.SelectActiveFrame(FrameDimension.Page, page - 1); (в обеих версиях) в памяти выделяется около 7 МБ, и те никогда не освобождаются (даже при выходе из метода)!!!
Если я перехожу на следующую страницу, 7МБ выделяются и не освобождаются каждый раз , а при возврате (на уже посещенных страницах) используется ранее выделенная память.
Чтобы дать вам пример: думаю, что диспетчер задач сообщает, что мое приложение использует x МБ;при переходе на одну страницу вперед объем памяти увеличивается до x + y (после SelectActiveFrame()) + z (Image ret = ...).Ну, у меня должно быть x + z (часть y должна быть равна нулю или GC собирается при выходе из метода), но, очевидно, это не то, что происходит, даже вызывая GC.Collect вручную.
Возвращаясь к ранее посещенной странице, память эффективно увеличиваетсякак и ожидалось, только с z.
Я нахожу это ужасным (подумайте о файле с 80 страницами ...), но как я могу заставить объект img освободить выделенную память?Я делаю что-то не так?
Я уже думал, что закрытие и повторное открытие IMG, но скорость не очень хорошая.
Спасибо всем

Ответы [ 3 ]

2 голосов
/ 20 февраля 2016

Не используйте новое растровое изображение (img), поскольку оно заставит систему создавать новую память для нового растрового объекта, использующего 32-разрядный цвет по умолчанию.

Вы можете просто использовать var bitmap = (Bitmap) img; чтобы получить растровый объект для этой страницы.

0 голосов
/ 27 сентября 2011

Я отвечаю на мой вопрос, попробовав различные решения, и принимаю решение, данное пользователем 965487, потому что в конце он был прав (спасибо также Гансу Пассанту).
Если у вас есть класс (назовите его QV), похожий на этот

public class QV
{
    Image img;
    int pages;

    public QV(filename) {
       img = Bitmap.FromFile(filename);
       pages = img.GetFrameCount(FrameDimension.Page);
    }

    ~QV() {
        img.Dispose();
        img = null;
    }

    public Image GetPage(int page) {
        if (page < 1 || page > pages) return null;
        img.SelectActiveFrame(FrameDimension.Page, page - 1);
        return new Bitmap(img);
    }
}

тогда каждый раз, когда вы звоните GetPage(...), ваша память будет увеличиваться не только для размера возвращаемого изображения, но и для оператора img.SelectActiveFrame(...). Я не знаю почему и как, но это случается. Освобождение возвращенного изображения и вызов освобождают память для размера изображения, а не для суммы, взятой из SelectActiveFrame () (в любом случае эта память не дублируется, если вы вернетесь на предыдущую страницу).
Поэтому вам лучше каждый раз открывать и закрывать изображение, например:

public class QV
{
    Image img;
    int pages;

    public QV(filename) {
       img = Bitmap.FromFile(filename);
       pages = img.GetFrameCount(FrameDimension.Page);
       img.Dispose();
    }

    public Image GetPage(int page) {
        if (page < 1 || page > pages) return null;
        img = Bitmap.FromFile(filename);
        img.SelectActiveFrame(FrameDimension.Page, page - 1);
        Image ret = Bitmap(img);
        img.Dispose();
        return ret;
    }
}

Полезная нагрузка для открытия и удаления изображения каждый раз, когда пользователь запрашивает новую страницу, на самом деле ничто по сравнению с опасным указанием памяти, выполненным с первым решением.
Надеюсь, кому-то это нужно.

0 голосов
/ 26 сентября 2011

Если я не ошибаюсь, вы не уничтожаете использованные элементы управления в каждой возможной точке. Я думаю, вам может понадобиться проверить это, но убедитесь, что вы утилизируете все использованные элементы управления.

т. ImageControlUsed.Dispose();

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...