0 / 0 / 0
Регистрация: 16.01.2016
Сообщений: 49
1

Освобождение неиспользуемых ресурсов в приложении

17.01.2016, 00:14. Показов 3921. Ответов 17

Author24 — интернет-сервис помощи студентам
Помогите решить проблему!!! В создаваемом приложении появлялась ошибка переполнения памяти, после поиска ответа на форумах нашёл в настройках компиляции Visual Studio 2013 необходимые настройки (выбор Any CPU). Ошибка исчезла, но появилась другая проблема: по мере работы приложения оперативная память загружается до конца, немного выгружается, опять загружается и выгружается и так непрерывно, пока работает приложение. Файл подкачки увеличивается до максимально возможного предела так, что на системном диске не остаётся свободного места, система зависает и долгое время её даже невозможно перезапустить (Windows 8.1 x64).
Поясню происходящее в приложении. Путём отключения процедур программы нашёл причинную - как и предполагал - это работа с PictureBox. В программу загружаются поочерёдно все jpg файлы из директории для поиска повреждённых. Казалось бы ничего страшного, в один и тот же PictureBox загружается новый файл, соответственно старый должен удаляться, но такого не происходит. Пробовал различные способы выгрузки (закомментировано), ничего не помогает. Подскажите знающие люди, как выгружать ресурсы PictureBox принудительно перед загрузкой нового изображения правильно? Привожу актуальный код подпрограммы поиска повреждённых файлов:

VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Private Sub СписокФайлов(ByVal folderPath As String)
        ListBox1.Items.Clear()
        Dim ИменаФайлов = My.Computer.FileSystem.GetFiles(folderPath, FileIO.SearchOption.SearchAllSubDirectories, "*.jpeg", "*.jpg")
        ProgressBar1.Minimum = 0
        ProgressBar1.Maximum = ИменаФайлов.Count
        ProgressBar1.Step = 1
        ProgressBar1.Value = 0
 
        For Each ИмяФайла As String In ИменаФайлов
            ProgressBar1.Increment(1)
            'PictureBox1.Dispose()
            'PictureBox1.Refresh()
            'PictureBox1.Update()
            Try
                PictureBox1.Image = System.Drawing.Image.FromFile(ИмяФайла)
            Catch ex As Exception
                ListBox1.Items.Add(ИмяФайла)
            End Try
            'Me.Finalize()
            'Me.Dispose()
            'PictureBox1.Dispose()
            Label1.Refresh()
            Label1.Text = ИмяФайла
        Next
 
        ProgressBar1.Visible = False
        Label1.Text = "Поиск в директории завершён!"
    End Sub
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
17.01.2016, 00:14
Ответы с готовыми решениями:

Освобождение ресурсов
В гугле много полемики на эту тему, но вот решения я так и не нашел. В случае, когда активити...

Освобождение ресурсов try with resources
Объясните пожалуйста что я делаю не так ? Таким образом я обращаюсь к базе данных Эта не рабочая...

Освобождение управляемых ресурсов
1)Все знают что dispose() высовобождает неуправлямы ресурсы,а как высвободить управляемы?...

Освобождение ресурсов Wand Image
Добрый день, коллеги! Прошу помочь в освобождении памяти, которое не происходит (как мне...

17
Администратор
Эксперт .NET
16989 / 13348 / 5209
Регистрация: 17.03.2014
Сообщений: 27,293
Записей в блоге: 1
17.01.2016, 01:20 2
dymitri, это известный прикол с методом Image.FromFile(). Он продолжает держать файл открытым. Обходится с помощью чтения файла в память.
VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
For Each ИмяФайла As String In ИменаФайлов
    ProgressBar1.Increment(1)
    Try
        Using mstream As New System.IO.MemoryStream(File.ReadAllBytes(ИмяФайла))
            PictureBox1.Image = System.Drawing.Image.FromStream(mstream)
        End Using
    Catch ex As Exception
        ListBox1.Items.Add(ИмяФайла)
    End Try
    Label1.Refresh()
    Label1.Text = ИмяФайла
Next
Еще можно уменьшить потребление памяти переписав инициализацию переменной ИменаФайлов следующим образом (нужен Imports System.IO)
VB.NET
1
Dim ИменаФайлов = Directory.EnumerateFiles(folderPath, "*.jp*g", SearchOption.AllDirectories)
Метод EnumerateFiles возвращает файлы по одному вместо возврата коллекции целиком. Кроме того благодаря маске "*.jp*g" мы обходим каждый каталог только один раз. Для надежности можно добавить Where
VB.NET
1
2
Dim ИменаФайлов = Directory.EnumerateFiles(folderPath, "*.jp*g", SearchOption.AllDirectories). _
    Where(Function(n) ".jpg".Equals(Path.GetExtension(n), StringComparison.OrdinalIgnoreCase) OrElse ".jpeg".Equals(Path.GetExtension(n), StringComparison.OrdinalIgnoreCase))
9
0 / 0 / 0
Регистрация: 16.01.2016
Сообщений: 49
17.01.2016, 14:55  [ТС] 3
Спасибо огромное за дельные советы! Всё исправил по вашему примеру, но... Проблема остаётся практически та же. Приложение работает теперь чуть дольше, но переполнение памяти, увеличение файла подкачки до возможных пределов остаются. Система зависает, а в варианте на Windows 7 x64 после закрытия приложения вообще ушла в синий экран. Уже пытался компилировать в Visual Studio 2010 - безрезультатно.
Если раньше программа зависала (вернее система) после 2000 проверенных изображений, то теперь её хватает аж до 3000, но этого мало, учитывая, что изображений десятки тысяч. В Семёрке хватило ещё дольше, но это лишь потому, что на системном диске больше свободного пространства было под файл подкачки.
Понимаю, что возможно есть другие алгоритмы для проверки целостности изображений jpg, но просто не даёт покоя причина невозможности выгрузки неиспользующихся ресурсов.
Что никак не умаляет ваших трудов, OwenGlendower, ещё раз спасибо за потраченное время. Но может есть дополнительные советы в данной ситуации?
0
Администратор
Эксперт .NET
16989 / 13348 / 5209
Регистрация: 17.03.2014
Сообщений: 27,293
Записей в блоге: 1
17.01.2016, 16:45 4
Лучший ответ Сообщение было отмечено dymitri как решение

Решение

dymitri, я забыл про Dispose для картинки. Он здесь все равно нужен
VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
For Each ИмяФайла As String In ИменаФайлов
    ProgressBar1.Increment(1)
    Try
        Using mstream As New System.IO.MemoryStream(File.ReadAllBytes(ИмяФайла))
            PictureBox1.Image = System.Drawing.Image.FromStream(mstream)
            PictureBox1.Image.Dispose()
            PictureBox1.Image = Nothing
        End Using
    Catch ex As Exception
        ListBox1.Items.Add(ИмяФайла)
    End Try
    Label1.Refresh()
    Label1.Text = ИмяФайла
Next
1
0 / 0 / 0
Регистрация: 16.01.2016
Сообщений: 49
17.01.2016, 17:09  [ТС] 5
OwenGlendower, всё, теперь работает, без каких-либо багов. Осталось только мелочь - настройка интерфейса. Ещё раз огромное человеческое спасибо , про Dispose помню, а про Using...End Using вылетело из головы (т.к. кроме теории, на практике не помню что бы приходилось использовать). Удачи вам и дай Бог здоровья!!!
0
4407 / 3531 / 843
Регистрация: 02.02.2013
Сообщений: 3,417
Записей в блоге: 2
17.01.2016, 17:10 6
Обязательно ли грузить картинку в PictureBox? М.б. достаточно такого кода
VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    For Each fName As String In fNames
        findBad(fName)
    Next
    MsgBox("theEnd")
'…
Public Sub findBad(ByVal fNm As String)
    Try
        Dim bmp As Bitmap = New Bitmap(fNm)
        Using bmp
            If Not bmp.RawFormat.Equals(ImageFormat.Jpeg) Then
                ListBox1.Items.Add(fNm)
            End If
        End Using
    Catch ex As Exception
        ListBox1.Items.Add(fNm)
    End Try
End Sub
2
0 / 0 / 0
Регистрация: 16.01.2016
Сообщений: 49
17.01.2016, 19:22  [ТС] 7
ovva, спасибо за иной вариант проверки. Но этот вариант работает почему-то в несколько раз медленнее. Хотя и находит больше "ошибочных", которые визуально совсем не выглядят испорченными файлами. Думаю идеальный алгоритм - это использование обоих вариантов один после другого. Надо будет подумать и воплотить... Ещё раз большое спасибо всем небезразличным!
0
4407 / 3531 / 843
Регистрация: 02.02.2013
Сообщений: 3,417
Записей в блоге: 2
17.01.2016, 20:10 8
Скорость обработки сравняется если заменить
VB.NET
1
Dim bmp As Bitmap = New Bitmap(fNm)
на
VB.NET
1
Dim bmp As Bitmap = System.Drawing.Image.FromStream(New System.IO.MemoryStream(File.ReadAllBytes(fNm)))
2
0 / 0 / 0
Регистрация: 16.01.2016
Сообщений: 49
17.01.2016, 20:46  [ТС] 9
ovva, протестировал сам, просидел с секундомером в двух вариантах кода в одной и той же директории засёк проверку 500 файлов, к сожалению везде выдало 1 мин. 31 сек. независимо от того используется "New Bitmap(fNm)" или "System.Drawing.Image.FromStream(New System.IO.MemoryStream(File.ReadAllBytes(fNm)))". Результат полностью идентичен даже секунду в секунду!

Добавлено через 7 минут
Кстати...
В варианте от OwenGlendower ушло ровно 24 сек. на те же 500 файлов. Но для объективности напомню, что при вашем, ovva, варианте находит больше ошибок. Этимологию этих ошибок файлов ещё буду изучать в различных редакторах, позже отпишусь, действительно ли эти файлы ошибочны (т.е. ваш вариант действительно прав), или же файлы целы, и возникли левые причины (вариант видит неактуальные причины, хотя в одних и тех же файлах, - несколько раз проверял).
Ещё раз спасибо за советы!
0
4407 / 3531 / 843
Регистрация: 02.02.2013
Сообщений: 3,417
Записей в блоге: 2
17.01.2016, 23:14 10
Заинтересовал вопрос, почему равнозначные в общем то процедуры имеют различие в скорости выполнения. Несколько подкорректировал процедуру и провел тест на сравнение скорости выполнения. Т.к. "плохих" файлов нет, то и запись в ListBox не влияет на результаты.
Всего файлов 2621. Время в миллисекундах.
V1 / V2
18078 / 15469
15511 / 15477
15515 / 15564
15478 / 15555
VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    Dim fNames = My.Computer.FileSystem.GetFiles("C:\Users\Pictures\ImagesPrj", FileIO.SearchOption.SearchAllSubDirectories, "*.jpeg", "*.jpg")
    Dim sW As New Stopwatch
    sW.Start()
    For Each fName As String In fNames
        findBad(fName)
    Next
    sW.Stop()
    Dim v1 As String = "v1=" & sW.ElapsedMilliseconds.ToString
    sW = New Stopwatch
    sW.Start()
    For Each fName As String In fNames
        findBad2(fName)
    Next
    sW.Stop()
    Dim v2 As String = "v2=" & sW.ElapsedMilliseconds.ToString
    MsgBox("Всего файлов " & fNames.Count & vbCrLf & v1 & vbCrLf & v2, , "test")
End Sub
Public Sub findBad(ByVal fNm As String)
    Try
        Using mstream As New System.IO.MemoryStream(File.ReadAllBytes(fNm))
            If System.Drawing.Image.FromStream(mstream).RawFormat.Equals(ImageFormat.Jpeg) Then Exit Sub
            ListBox1.Items.Add(fNm)
        End Using
    Catch ex As Exception
        ListBox1.Items.Add(fNm)
    End Try
End Sub
Public Sub findBad2(ByVal fNm As String)
    Try
        Using mstream As New System.IO.MemoryStream(File.ReadAllBytes(fNm))
            PictureBox1.Image = System.Drawing.Image.FromStream(mstream)
            PictureBox1.Image.Dispose()
            PictureBox1.Image = Nothing
        End Using
    Catch ex As Exception
        ListBox1.Items.Add(fNm)
    End Try
End Sub
1
0 / 0 / 0
Регистрация: 16.01.2016
Сообщений: 49
18.01.2016, 07:05  [ТС] 11
ovva, в приведённом вами примере сделал лишь изменение с расположением десятков тысяч фото в директории, и запустил. После некоторого времени выскочила ошибка: "Необработанное исключение типа "System.OutOfMemoryException" в System.Windows.Forms.dll" Когда же выбрал директорию всего с несколькими тысячами, то всё прошло удачно и результат действительно сильно не отличался (v1 немного больше чем v2). Иду сейчас в больницу (на больничном) после - изучу ещё раз отличия в моей программе и в вашем примере кода. В чём же может быть такая большая разница в продолжительности выполнения?... Спасибо за пример.
0
0 / 0 / 0
Регистрация: 16.01.2016
Сообщений: 49
18.01.2016, 12:13  [ТС] 12
Протестировал сейчас вчерашние варианты с одинаковыми настройками компиляции - время практически одинаковое. Так что извините что запутал со временем выполнения. Видимо просто разные настройки CPU были или ещё что... Но, к сожалению, ни один вариант не помогает найти повреждённые фото. Вернее находят или совсем испорченные, или полностью нормально отображаемые стандартным просмотрщиком, но с неправильными метаданными (Фотошоп ругается тоже). А как бы недогруженные или из-за сбоя хранилища получившие ошибки - пропускает любой из вариантов программы. Вот очень бы интересовал побитовый алгоритм построения JPG (что бы понять принцип сжатия и обратного процесса для программного кода проверки целостности). Ведь есть же сторонние программы, которые видят эти нарушения (как Фотошоп). Просто невозможно через Фотошоп пропустить тысячи фото, дабы выявить ошибки...
Прикрепляю примеры файлов, о которых говорю:

Освобождение неиспользуемых ресурсов в приложении


Освобождение неиспользуемых ресурсов в приложении


Освобождение неиспользуемых ресурсов в приложении


Освобождение неиспользуемых ресурсов в приложении


Освобождение неиспользуемых ресурсов в приложении


Освобождение неиспользуемых ресурсов в приложении


и так далее...
0
4407 / 3531 / 843
Регистрация: 02.02.2013
Сообщений: 3,417
Записей в блоге: 2
18.01.2016, 13:46 13
Насколько понимаю, ваши файлы, с формальной точки зрения это вполне корректные jpeg файлы. И вас в них не устраивает их содержание, или вы можете указать конкретные признаки их некорректности.
Неплохо бы посмотреть на пример подобного файла (не картинки).
0
0 / 0 / 0
Регистрация: 16.01.2016
Сообщений: 49
18.01.2016, 15:22  [ТС] 14
ovva, Если вы имеете ввиду оригиналы изображений "не картинки" , то могу в архиве: Плохие файлы.rar. Здесь в первой папке те файлы, которые находит приложение (путём ошибки открытия). Фотошоп тоже говорит что нельзя открыть ввиду неправильных маркеров. В просмотрщике Windows эти файлы никак не выглядят повреждёнными. Во второй папке представлены файлы урезанные, затёртые и т.д., которые приложение не распознаёт за "ошибочные", но визуально в просмотрищике видны явные повреждения, да и Фотошоп тоже говорит о неполном файле. Вот их-то и хотелось бы "узнавать программно", неким вычислением бит между маркерами, что ли, размером блоков в файле, дабы выявлять подобные ошибки в коде.
0
4407 / 3531 / 843
Регистрация: 02.02.2013
Сообщений: 3,417
Записей в блоге: 2
18.01.2016, 15:41 15
Открыть архив не смог ("Архив поврежден…")
0
OwenGlendower
18.01.2016, 15:45
  #16

Не по теме:

Цитата Сообщение от ovva Посмотреть сообщение
Открыть архив не смог ("Архив поврежден…")
Архив в порядке. Обнови WinRAR до 5 версии.

0
4407 / 3531 / 843
Регистрация: 02.02.2013
Сообщений: 3,417
Записей в блоге: 2
18.01.2016, 21:56 17
Лучший ответ Сообщение было отмечено OwenGlendower как решение

Решение

Мои наблюдения:
1. Оба файла с "ош. маркера" распознаются вариантом v1. При просмотре в hex-редакторе видно, что там действительно не хватает маркера начала (FF D8) и конца файла (FF D9).
2. Файлы из категории "урезанные" не определяются ни процедурой v1, ни процедурой v2. На мой взгляд, это вполне ожидаемо т.к. файлы имеют правильную структуру, просто их картинка не отвечает ожиданиям тестера. Хотя некоторая информация (скорее косвенная) о возможных проблемах там все же есть, не зря ведь Ps делает предположение (не утверждение) о том, что файл м.б. поврежден. Скорее всего, определяются некоторые статистики относительно структуры закодированного изображения. Другие программы этих нюансов не различают.
3. Создать процедуру в рамках VB.NET для отбора файлов типа "урезанные" (за приемлемое время) считаю затруднительным. Очевидно, не обойтись без использования внешних библиотек специализирующихся на работе с Jpeg, и придется разобраться, как это делает Ps.
4. Интересно, что при открытии такого растра в MSWord (Word 2003) нет сообщения об ошибке, но и растр не открывается (показывается только рамка растра). М.б. в рамках VBA этот факт можно использовать. Ну и можно попробовать использовать Ps в режиме пакетной обработки или, например, ImageMagick (http://forum.oszone.net/thread-209258.html).
1
0 / 0 / 0
Регистрация: 16.01.2016
Сообщений: 49
19.01.2016, 23:47  [ТС] 18
Цитата Сообщение от ovva Посмотреть сообщение
распознаются
Да, как я и говорил - эти распознаются, уже пол дела, но интересует ещё поиск "урезанных". Спасибо за ссылку, посмотрю эту программу, может поможет для пакетного поиска (т.к. приоритетным является именно поиск, идентификация таких файлов, а не их обработка), пусть даже сторонними программами, хотя интересно было бы оформить интерфейс под себя, сделать под свои потребности. Ещё раз спасибо за участие!
0
19.01.2016, 23:47
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
19.01.2016, 23:47
Помогаю со студенческими работами здесь

Gdiplus - освобождение ресурсов GDI
Каким образом в gduplus в C# освобождать дескрипторы?! В C++ (без .net) вызывал просто функцию...

Освобождение ресурсов при удаленнии объекта
Здравствуйте. Используется простое консольное приложение. Есть объект, который пользуется...

освобождение ресурсов после открытия файла?
Здравствуйте , я загружаю файл вот так , как показано ниже ,скажите в чём минус такого варианта , в...

Освобождение ресурсов и ошибка System.NullReferenceException
Я написал класс: public class Database Который в веду сложности создания, имеет в конструкторе...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
18
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru