Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Delphi: Общие вопросы > меньше памяти


Автор: uk- 27.01.07, 14:36
написал прогу, но такое чувство что много памяти кушает =)
Посоветуйте всего начиная от того как очишать все объекты правильно (например CloseHandle - нужно ли вообще, SetLength(массив,0) и т.д.)
до того, как правльно пользоваться процедурами/функциями, при выходе из них, данные/переменные которые былои в них сами уничтожаются или ВСё нужно очищать самом и имеет ли значение (в пользу памяти) если я объявлю процедуру в private или вообще не объявлю и т.д.
За всё всё заранее спасибо!
P.S. вроде были темы тока на счёт сжимание самой проги..

Автор: AlexJ 27.01.07, 16:39
Цитата uk- @
правильно (например CloseHandle - нужно ли вообще, SetLength(массив,0) и т.д.)

В общем случае для всех языков справедливо:

Close handle обязательно.
Также DeleteObject (logical pen, brush, font, bitmap, region, or palette)
Обычно именно эти обьекты и создают утечку памяти если их не удалять.
Если делаете субклассинг, не забывать удалять субклассинг.

по поводу локальных обьектов, за исключением динамических обьектов работающих на базе OLE переменные создаются в стеке, которые самоуничтожаются при выходе из процедуру(функции). Статические и глобальные переменные существуют на все время жизни программы.

Автор: uk- 27.01.07, 17:10
Цитата AlexJ @
Также DeleteObject (logical pen, brush, font, bitmap, region, or palette)
Обычно именно эти обьекты и создают утечку памяти если их не удалять.

тоесть не только BMP.Free; например, а ещё и DeleteObject(BMP); ?

Автор: Smike 27.01.07, 18:06
Цитата uk- @
тоесть не только BMP.Free; например, а ещё и DeleteObject(BMP); ?

Конечно нет, все DeleteObject-ы делаются автоматом ;)

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

Кроме этого, можно прибегнуть и к динамическому созданию вкладок, если они чрезмерно нагружены, панелей инструментов, если их много и они схожи по своему назначению. А еще это даст возможность более удобной и быстрой миграции на другие компоненты, да и работать с неперегруженными формами гораздо проще.

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

Еще одним необходимым условием является необходимость применения try..finally для блоков, где временно создаются какие-то классы. Потому что при возникновении исключения, класс может не освободиться.

Автор: P.O.D 27.01.07, 18:09
да и расскажи в 2 словах что прога делает.

Автор: uk- 27.01.07, 20:06
Цитата P.O.D @
да и расскажи в 2 словах что прога делает.

она занимает мало места в памяти =))
Что-то вроде чата, с не стандартными функциями.. тоесть можно транслировать звук например (вследствии много переменных и объектов).. вообщем.. это имеет значение разве?
Smike пасибо

-Added
Цитата Smike @
Конечно нет, все DeleteObject-ы делаются автоматом ;)

AlexJ говорит обратное или я не понял?

Автор: P.O.D 27.01.07, 20:41
Цитата uk- @
Что-то вроде чата, с не стандартными функциями.. тоесть можно транслировать звук например (вследствии много переменных и объектов).. вообщем.. это имеет значение разве?

А разве нет ? И много это сколько ? А в этом чате еще небойсь куча смайликов (и.т.д) в ресурсах ?

Автор: AlexJ 27.01.07, 21:06
Цитата uk- @
AlexJ говорит обратное или я не понял?

Нет не обратное, я описал в общем, BMP.free и есть под капотом DeleteObject.
Если в чистом АПИ тогда DeleteObject, если средствами языка тогда через free.

Автор: uk- 27.01.07, 21:19
Цитата AlexJ @
Цитата uk- @
AlexJ говорит обратное или я не понял?

Нет не обратное, я описал в общем, BMP.free и есть под капотом DeleteObject.
Если в чистом АПИ тогда DeleteObject, если средствами языка тогда через free.

аа понятно

-Added
Цитата P.O.D @
А разве нет ? И много это сколько ? А в этом чате еще небойсь куча смайликов (и.т.д) в ресурсах ?

Вообще ресорсов не так уж и много, но в любом случае я ресурсы убиваю после использования..
Я не то что жалуюсь на то что у меня много памяти жрёт, а просто спрашиваю как можно с экономить её.. Не только в этом каличном чате =)

Автор: n0wheremany 27.01.07, 21:42
Цитата uk- @
Вообще ресорсов не так уж и много, но в любом случае я ресурсы убиваю после использования..
Я не то что жалуюсь на то что у меня много памяти жрёт, а просто спрашиваю как можно с экономить её.. Не только в этом каличном чате =)


Память - где -
ОЗУ - В диспетчере задач посмотри чего там кушат. Нормальная программа (только форма) порядка 1,4 Mb кушает. Ну а если с визуалом... То больше. Например программа у меня есть которая 20 Мегов жрёт со скинами, а без них 12. И дело тут не в оптимизации памяти, а просто по другому не получится...

А если на ВИнте - то в KOL тебе дорога.

Автор: Smike 28.01.07, 05:42
Цитата n0wheremany @
20 Мегов жрёт со скинами,

Цитата n0wheremany @
И дело тут не в оптимизации памяти, а просто по другому не получится...

20 мегабайт? Как по мне многовато. 20 мегабайт – это битмап размером в несколько раз превышающий размер стандартного разрешения монитора в наибольшей цветности. Куда такой скин?

Автор: n0wheremany 28.01.07, 07:46
Цитата Smike @
20 мегабайт? Как по мне многовато. 20 мегабайт – это битмап размером в несколько раз превышающий размер стандартного разрешения монитора в наибольшей цветности. Куда такой скин?


Ну не всё же ограничивается одним битмапом. Помимо визуализации - есть как никак функциональность, вот эта чать и жрёт 12 мегов. Остальное - скины. (ASkin) - программа многоуровневая, многооконная... Полная утилита в общем... (Если посмотреть хочешь)

Автор: uk- 28.01.07, 10:36
давайте всё же к теме.. Может ещё кто что подскажет.. Типа передавать большие параметры как const или var и т.п.

Автор: n0wheremany 28.01.07, 10:48
Цитата uk- @
Типа передавать большие параметры как const или var и т.п.


Ну это непонятно что. Передавать кому и конкретно что?

Цитата n0wheremany @
ОЗУ - В диспетчере задач посмотри сколько там кушат.

Автор: uk- 28.01.07, 11:12
Цитата
Ну это непонятно что. Передавать кому и конкретно что?

параметр в функцию передавть как var либо const что бы не копировало в стек лишнюю память
Цитата
ОЗУ - В диспетчере задач посмотри сколько там кушат.

:huh: да знаю я где смотреть и сколько её там.. ВОПРОС не в этом..
Цитата
Я не то что жалуюсь на то что у меня много памяти жрёт, а просто спрашиваю как можно с экономить её..

Автор: AndNot 28.01.07, 12:59
Цитата uk- @
Я не то что жалуюсь на то что у меня много памяти жрёт, а просто спрашиваю как можно с экономить её..

Встречал несколько способов.
Первый опробовал сам, еще в пятом дельфи. Отказ от VCL, все формы выполнены в виде диалогов, и хранятся в ресурсах. Сложность разработки увеличивается не на много, зато прога содержащая более двух сотен кнопок (ownerdraw), едитов и диалогов, создающая несколько потоков(для работы с железом) и содержащая элементы анимации кушает 1.4 Мб памяти, т.е. то что виндовс по умолчанию выделяет любой запускаемой программе.
Так же встречал другой любопытный способ. Сама программа написана без VCL, и представляет собой лишь обработчик сообщений, но все что связано с интерфейсом заделано на VCL, и скомпилировано в DLLы, подгружаемые и выгружаемые по мере необходимости. По моему самый лучший вариант для тех, кому удобство VCL превыше всего :yes:
Цитата uk- @
параметр в функцию передавть как var либо const что бы не копировало в стек лишнюю память

Это помимо всего еще и на быстродействии хорошо скажется :yes: А насчет стека могу сказать только то что виндовс по умолчанию выделяет под него небольшой клочок памяти, и если происходит выход за его пределы, то выделяется еще один кусочек, и т.д., пока не будет исчерпан лимит на макс. размер стека программы, после чего есть шанс увидеть "Программа выполнила недопустимую операцию" © :lol:

Автор: Smike 28.01.07, 18:23
Цитата uk- @
параметр в функцию передавть как var либо const что бы не копировало в стек лишнюю память

Только нет смысла передавать небольшие параметры вроде Integer, Boolean таким образом, потому что на передачу адреса этой переменной будет потрачено такое же количество памяти. Так что перебарщивать код лишними const-ами нет смысла.

Автор: n0wheremany 29.01.07, 13:53
Цитата uk- @
да знаю я где смотреть и сколько её там.. ВОПРОС не в этом..


Ну ты даёшь. Тебе хочется уменьшить память программы, а ты не знаешь вообще сколько она жрёт, но каким то сверхчуствительным органом определяешь на всзгяд - какое приложение сколько жрёт памяти...
Я конечно многово повидал, но такого...

Диспетчер задач - процессы - Память. В процессах находишь своё приложение и смотришь память...

Вот и отталкивайся.

Автор: uk- 29.01.07, 17:52
:blink: :blink: Я в шоке!
Цитата
да знаю я где смотреть
и сколько она жрёт !
Цитата
ВОПРОС не в этом..
..
..
Цитата
Я не то что жалуюсь на то что у меня много памяти жрёт, а просто спрашиваю как можно с экономить её..

Что не понятного? тут не говорится о чём то конкретном, а вообще способы уменьшения используемой памяти, программой..

Автор: leo 30.01.07, 08:39
Цитата n0wheremany @
Диспетчер задач - процессы - Память. В процессах находишь своё приложение и смотришь память...

Цитата uk- @
да знаю я где смотреть
и сколько она жрёт !

А какую такую память нам показывает диспетчер задач и кто "жрет" память ?
Берем элементарную прогу "Hello, World!" на асме со совмещенной секцией кода\данных\импорта. На диске она занимает всего 2*512=1К, а при загрузке в память сам образ всего - 2*4К=8К. Плюс стэк 8К и главная куча 12К (и там и тут загрузчик уже успел "наследить"). Итого нашего "родного" всего 28К, хотя ради справедливости сюда нужно еще добавить секции данных подгруженных системных dll ~13*4К=52К, итого в сумме 80К. А диспетчер на старте проги (до вывода MesssageBox) показывает аж 568К. Значит винда "вешает" на наш процесс всю свою внутреннюю обслугу процесса. Это и служебные данные, явно спроецированные в процесс (PEB, TEB, environment strings, список загруженных модулей LDR_DATA и еще вагон и маленькая тележка всякого "хлама"), и куча всякого скрытого добра типа таблиц страниц, объектов ядра и еще х.з.чего. Дальше еще интереснее - когда выскакивает MessageBox "занимаемая" память увеличивается до 1.2Mб пока окно сидит в трее (как мин.подгружаются еще 3 dll) и до 1.4Mб при активизации окна. Вот после этого и думай как "сэкономить память" :wacko: Разумеется нельзя расточительно обращаться с памятью, но и мелочная экономия тут тоже не имеет никакого смысла. В частности передавать структуры по ссылке хорошо в плане быстродействия, но к экономии памяти это не имеет никакого отношения ;)

Автор: uk- 30.01.07, 11:55
Цитата leo @
В частности передавать структуры по ссылке хорошо в плане быстродействия, но к экономии памяти это не имеет никакого отношения ;)

почему? а если передать по значению например 10 000 символов строки?

Автор: Shaggy 30.01.07, 12:29
Цитата uk- @
а если передать по значению например 10 000 символов строки?

а если 2Гб символов(строки могут быть такой длины)?

ЗЫ я к тому, что некоторые структуры всегда передаются по ссылке...

Автор: uk- 30.01.07, 13:20
буфер :whistle:

-Added
Цитата Shaggy @
ЗЫ я к тому, что некоторые структуры всегда передаются по ссылке...

ладно спасибо

Автор: leo 31.01.07, 08:09
Цитата uk- @
почему? а если передать по значению например 10 000 символов строки?

Потому что, во-первых, динамические строки и массивы по любому передаются по ссылке, а в стек копируются только статические массивы, во-вторых, 10К это мелочь по сравнению с 1.4М и к тому же из этих 10К нужно вычесть уже выделенную под стек память. В приведенном выше примере на старте проги под стек уже выделено 8К физ.памяти, но реально используется менее сотни байт, поэтому при запихивании в стек 10К его реальный размер возрастет только на 4К. Разумеется злоупотреблять передачей больших массивов по значению, также как и объявлять их в качестве локальных переменных нельзя, т.к. физ.память, выделенная под стек не освобождается - локальные переменные формально "уничтожаются", но лишь в том смысле, что при выходе из процедуры указатель стека автоматически возвращается к первоначальному значению и при вызове другой процедуры новые локальные переменные будут записываться на место "старых".

Но выделять локальные буферы до единиц-десятков Кб вполне допустимо и даже может иметь определенные преимущества по сравнению с GetMem, т.к. позволяет избежать или уменьшить фрагментацию кучи. Например, что может произойти и часто происходит при чтении файлов. Выделяем в куче буфер большого размера через GetMem или SetLength, читаем файл, обрабатываем и не освободив буфер выводим сообщение в глобальнцю строку, например Label1.Caption. Если куча не фрагментирована и в ней не нашлось подходящей "дырки" под строку, то строка будет выделена в конце кучи после буфера. В этом случае после освобождения буфера в куче образуется большая дыра и выделенная память не уменьшается, т.к. менеджер памяти освобождает страницы (decommit) только с хвоста кучи, который в данном случае занят строкой. Примерно то же происходит и при TStringList.LoadFromFile - в результате загрузки занимаемая память может увеличиваться более чем на 2 размера файла, т.к. текст сначала целиком грузится в темп-строку, а потом разбивается на отдельные строки, которые размещаются в куче после темп-строки. Если же читать файл в "сторонний" буфер (локальный или Heap\VirtualAlloc), то подобных больших дыр в куче не возникает.

Автор: AndNot 31.01.07, 11:30
Цитата leo @
В приведенном выше примере на старте проги под стек уже выделено 8К физ.памяти, но реально используется менее сотни байт, поэтому при запихивании в стек 10К его реальный размер возрастет только на 4К

Верно, но теперь представь что в процедуре есть локальная переменная, куда при обработке помещается переданный массив. Вот и еще возврастание стэка на 3 страницы. А ведь стек жрет физическую память, и как ты верно заметил:
Цитата leo @
физ.память, выделенная под стек не освобождается

Так что как ни крути, а с памятью нужно поосторожнее работать. Сам ведь небось встречался с ситуацией, когда в стек пытаются десятки мегов данных запихать :lol: Самое хреновое, что Дельфи тупо пытается это сделать, на что справедливо получает Stack Overflow :wacko:

Автор: leo 01.02.07, 08:26
Цитата AndNot @
Так что как ни крути, а с памятью нужно поосторожнее работать

Я бы сказал не "поосторожнее", а с умом и со знанием дела ;) Тогда и переполнения стека не будет и память будет использоваться рационально - без излишеств и злоупотреблений, но и без мелочной параноидальной экономии ;)

И твоего восхищения "любопытным" и "самым лучшим" способом динамической загрузки\выгрузки VCL-библиотек я не понимаю :( Такое впечатление, что люди или застряли в прошлом веке MS DOS и слабеньких компов с парой мегабайт оперативы, или участвуют в каком то непонятном соревновании - у кого диспетчер задач меньше цифирек покажет :D . А что он на самом деле показывает - фиг его знает. Но как бы не критиковали MS за отдельные штучки, но управление виртуальной и физической памятью в виндах устроено очень разумно - по принципу "не чеши пока не чешется". Во-первых, если кто-то думает, что при "загрузке" приложения весь образ срузу загружается в ОЗУ, тот глубоко ошибается - код и данные подгружаются с диска динамически по мере обращения к еще незагруженным страницам. Поэтому если мы открыли увесистую многофункциональную прогу, выполнили пару действий и закрыли, то возможно, что значительная часть неиспользуемого кода и данных даже и не загружалась с диска. С dll ситуация несколько хитрее, т.к. если она грузится не по своему базовому адресу, то нужно переустанвить релоки\фиксапы, и делается ли это сразу за один раз или также динамически - я на сей момент не знаю (надо бы поэкспериментировать).
Во-вторых, если кто-то думает, что при освобождении памяти, например при "выгрузке" dll или SetProcessWorkingSetSize(..,-1,-1) винда срузу безвозвратно "уничтожает" выделенные страницы, то тоже ошибается - умная винда просто помечает соответствующие страницы как неприсутствующие в пространстве процесса и находящиеся в переходном состоянии. В этом состоянии они могут находится сколь угодно долго, пока жив процесс и есть достаточный запас свободных и обнуленных страниц ОЗУ. Если через какое-то время процесс снова обращается к переходной странице или заново "грузится" dll, то винда просто возвращает переходные страницы процессу, экономя при этом "время и деньги". Поэтому выкрутасы с SetProcessWorkingSetSize и загрузками\выгрузками dll это просто самообман - реально из ОЗУ может ничего не выгружаться, но формально мы можем гордиться тем, что это уже не мы занимаем память, а винда ;) В результате на общем занимаемом объекме ОЗУ это никак не сказавается, но зато сильно напрягает систему и "раскаляет" процессор лишними ненужными действиями (обработка только одного отказа страницы занимает несколько тысяч тактов). Поэтому лучше не ломиться в вотчину винды как слон в посудную лавку, а довериться ее менеджеру виртуальной памяти - он сам сделает что нужно и когда нужно, сам подгрузит код и данные по первому требованию, а в случае нехватки физ.памяти сам ее почистит, выбросив наиболее давно неиспользуемые страницы

Автор: AlexJ 01.02.07, 21:20
:yes:
leo,
хорошо сказано !

Добавлю немножко: работа с OLE-обьектами, а непосредственно динамические строки,
очень часто встречается необходимость акумулирования строки
string=string+... при работе с динамической строкой - это может стать "убийством" для винды если пытаться суммировать данные больших размеров в циклах. Лучше выделить необходимый обьем с запасом(любых размеров до 2 гиг) но одноразово, чем в динамике передергивать OLE-engine, который может зарыться на полчаса запросто, вошкаясь с копированием в своп и обратно.

Автор: AndNot 02.02.07, 11:03
Цитата leo @
И твоего восхищения "любопытным" и "самым лучшим" способом динамической загрузки\выгрузки VCL-библиотек я не понимаю

А можно факты? Ведь на предположениях и документации от мягкотелых далеко не уедешь :lol:
Цитата leo @
впечатление, что люди или застряли в прошлом веке MS DOS и слабеньких компов с парой мегабайт оперативы

[offtop] dos есть и в настоящем, не так давно вышел PTS DOS 32, причем недорого :) [/offtop]
У меня из знакомых только человек 10 имеют более-менее современные компы, остальные в лучшем случае какой-нибудь селерончик, мегагерц эдак на 300-1000 ;)
И я прекрасно знаю, что при наличии выбора, они всегда выберут ту прогу, которая менее требовательна к ресурсам, пускай даже за счет некоторого снижения функциональности :whistle:
Цитата leo @
но управление виртуальной и физической памятью в виндах устроено очень разумно - по принципу "не чеши пока не чешется".

Обычное заблуждение позволяющее наплевать на все детали и кодить как бог на душу положит. Так ведь удобнее. К чему лишние проблемы? У юзера прога сильно свопит? Так пускай тачку обновит, он ведь уже пол года не апгрейдил. Зато мне так легче :) Так что ли?
Цитата leo @
Поэтому если мы открыли увесистую многофункциональную прогу, выполнили пару действий и закрыли, то возможно, что значительная часть неиспользуемого кода и данных даже и не загружалась с диска.

Ага, особенно это заметно по программам написанным на дельфи VCL :lol: Ты пробовал замерять время загрузки той же VCL-проги на метр кода и какой либо сторонней?
Цитата leo @
винда срузу безвозвратно "уничтожает" выделенные страницы

Она всего лишь помечает участки памяти как свободные. И они имеют все шансы при нехватке физической памяти отойти другой программе :) В этом и состоит преимущество DLL. Так что это не самообман.
Цитата leo @
В результате на общем занимаемом объекме ОЗУ это никак не сказавается, но зато сильно напрягает систему и "раскаляет" процессор лишними ненужными действиями

Это не сказывается когда ОЗУ более чем достаточно, а вот при его нехватке это очень даже полезые "выкрутасы". Проверено сотни раз на слабеньких тачках :tong:
А насчет напряга системы, так извини, это неумелые программеры ее "раскаляют", которые по всяким пустякам заводят треды. А переключение задач знаешь во сколько тактов обходится? Причем зачастую там где можно и без тредов обойтись.
Цитата leo @
а довериться ее менеджеру виртуальной памяти

"На бога надейся, а сам не оплошай" © Народ.
Нельзя же все оставлять на откуп системе. Дружба с головой еще никому не вредила :)
Цитата AlexJ @
Лучше выделить необходимый обьем с запасом(любых размеров до 2 гиг) но одноразово

:yes: Именно. Только желательно "необходимый обьем" поточнее вычислить. Все таки ресурсы не безграничны, и нет ничего хуже спопищайся программы!

Автор: leo 03.02.07, 07:59
Цитата AndNot @
Дружба с головой еще никому не вредила

Вот-вот, и я о том же:
Цитата leo @
без излишеств и злоупотреблений, но и без мелочной параноидальной экономии

:D

Цитата AndNot @
А можно факты?

Какие ? Если о подгрузке данных и кода по "требованию", то это элементарно проверяется по замерам времени обращения к странице по rdtsc (этим способом можно, хотя и не очень надежно, определять совал ли кто уже нос в твой процесс, см. Еще один вариант IsDebuggerPresent по RDTSC). Также можно проверить и время обращения к страницам dll при первой и последующих загрузках, хотя это видно и на глаз - при первом запуске приложение грузится дольше, а при повторных быстрее - системный кэш рулит ;)

Автор: AndNot 03.02.07, 11:17
leo, спасибо за ссылочку :yes: Можно попробовать метод в качестве универсального "детектора вирусов" :D Ведь теоретически вирь, получив управление, вызовет подгрузку страниц. Или я ошибаюсь?

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)