Здравствуйте, Jolly Roger, Вы писали:
JR>Под Windows "выделенная память" == "сферический конь в вакууме". Я бы рекомендовал для начала почитать об управлении памятью в Windows (например, у Рихтера), потом об управлении памятью NET, а также справку к диспетчеру задач (если Вы им пользуетесь) чтобы ориентироваться, какой смысл имеют значения в разных столбцах.
И, добавлю, не путаться в терминологии, поскольку у меня впечатление такое, что авторы этой терминологии нас специально запутывают
Здравствуйте, Flammable, Вы писали:
F>В результате такой обработки объем выделенной памяти подскакивает с 40 до 60 МБ. F>Подскажите, как сократить объем выделяемой памяти? Есть какие-то методы оптимизации, принудительной сборки мусора?
Под Windows "выделенная память" == "сферический конь в вакууме". Я бы рекомендовал для начала почитать об управлении памятью в Windows (например, у Рихтера), потом об управлении памятью NET, а также справку к диспетчеру задач (если Вы им пользуетесь) чтобы ориентироваться, какой смысл имеют значения в разных столбцах.
Здравствуйте, Don Reba, Вы писали:
DR>Здравствуйте, Jolly Roger, Вы писали:
JR>>Если не хочется углубляться в механизмы, то можно принять за аксиому, что управление памятью в Windows писали отнюдь не ламеры. NET'овскую надстройку, я думаю, тоже.
DR>Только, далеко не факт, что ценности этих не-ламеров совпадают с вашими.
Этот господин Jolly Roger уж очень себя любит и в оскорбительном тоне участвует в обсуждениях. Он обычно изначально уверен в совершенстве мелкософтовских технологий, практически задом своим отвечает
Здравствуйте, Flammable, Вы писали:
J>>2) Этот код будет падать (NullReferenceException) если в каталоге нет ни одного файла с нужной маской (на MapID.Close()). J>>3) MapName не чистится в каждой итерации цикла — в итоге MapNames[] может оказаться заполнен мусорными значениями с прошлых итераций (в первом файле texture была, в остальных нет) J>>4) DoEvents действительно здесь нужна? Просто практика показывает что от этого метода проблема ой как много, а нужен он ой как редко.
F>2. В каталоге всегда есть файлы Хотя, чем черт не шутит. Надо и это предусмотреть, спасибо. F>3. Разве присваивание переменной нового значения не удаляет старое? F>4. DoEvents действительно нужна, иначе форма зависает. Файлы объемные, читаются не моментально. А что за проблемы с этим методом?
2) Ох, если бы мне за каждое "всегда будет" давали денежку когда в эксплуатации оказывалось что и не всегда, и не будет
3) У тебя проблема что новое значение присваивается не всегда, а только если отработает условный блок. Если ни одной "map" или "texture" в файле нет, то нет присвоения => в переменной значение со старого прохода цикла => в массиве мусор. На "всегда будут эти значения" я уже сверху ответил. Пользователи вообще такие шалуны порой
4) Проблемы синронизации. Код с DoEvents должен быть спроетирован также как код с использование потоков — а почему бы тогда не использовать потоки сразу? И опыт подтверждает — видишь в коде DoEvents — будь готов к проблемам, когда пользователь не там щелкнул, сработало событие таймера и изменило состояние объектов посреди метода и прочие прелести многопоточного программирования. Вот быстро нагуглил несколько статей по поводу DoEvents:
Это недокументированные особенности реализации, которые, к тому-же, могут и меняться в разных версиях, потому я могу опираться тут только на свои наблюдения и логику.
Страница из виртуальной в физическую превращается при возникновении ошибки доступа. Если-бы на каждую ошибку при работе, например, с мегабайтным массивом пришлось "кланяться" Working Set Manager'у, то это было-бы весьма накладно. Вполне логично будет передавать процессу сразу много страниц, исходя из текущего положения дел с физ.памятью и даже опираясь на некоторую эвристику. Ну и при освобождении вызовом VirtualFree страницы не изымаются сразу из Process Working Set, что тоже вполне логично — они могут тут-же потребоваться снова, и при наличии резерва зачем делать лишние движения?
Сворачивание окон, видимо, принято за сигнал об окончании активной деятельности приложения, и коль система не занята, то почему-бы не заняться перераспределением физ. памяти?
Но точно знают, конечно, только разработчики. Однако отсутсвие жесткой связи между Working Set и размером аллокированной (а тем более — реально использованной) памяти — вещь совершенно очевидная.
Здравствуйте, Flammable, Вы писали:
F>>>Кроме того меня интересует, почему StreamReader.Close() ничего не делает. Собственно, как и StreamReader.Dispose()
L>>А он и не должен ничего делать. Он ничего не держит в себе, кроме малеьнокого буфера на несколько байт.
F>Тогда на что тратятся 20 мб памяти во время чтения файлов?
Скорее всего на временные строки. Скачайте какой-нить профайлер, который умеет смотреть память .Net-ных процессов и посмотрите, на что тратится память.
Здравствуйте, Don Reba, Вы писали:
JR>>Если не хочется углубляться в механизмы, то можно принять за аксиому, что управление памятью в Windows писали отнюдь не ламеры. NET'овскую надстройку, я думаю, тоже.
DR>Только, далеко не факт, что ценности этих не-ламеров совпадают с вашими.
Ну с моими-то совпадают Тут дело такое, я подобных обсуждений видел множество, и заканчивались они всегда одним из двух вариантов. Либо автор читал что-нибудь по теме, и вопрос снимался, либо автор разбираться не желал, и уходил со стойким убеждением, что "винда маздай"
Ну а в данной ветке особо-то и говорить не о чем, так как ТС так и не сообщил, откуда он берёт озвученные величины. А почти всегда в таких случаях за источник выбирают те показания из диспетчера, которые к реально занятой процессом памяти имеют наименьшее отношение, такая уж традиция сложилась И причина этому, видимо, всё та-же: у тех, кто знаком с механизмами хотя-бы в общих чертах, подобных вопросов не возникает, а те, кто не знаком, почему-то даже справку диспетчера читать не хотят
Здравствуйте, Flammable, Вы писали:
F>Программа немаленькая, уже написана. Не нравятся мне такие огромные траты памяти впустую.
Если Вы инженер, а не блондинка примеряющая платьице, то у вас должно быть не "не нравится"
а — снижает скорость старта/работы других служб на ...%, требует увеличения аппаратной мощности на ...% и т.д.
И что значит — "впустую"/"не впустую"?
F>Сейчас просто загруженная форма занимает 30 мб памяти. Что можно хранить в 30 мегабайтах?
А какая вам то, инженеру разница?
F>вызов метода GC.Collect()
Читайте документацию — вызов Collect() всего лишь просит приблизить время очередной проверки/очистки.
А если GC видит что свободной памяти полно, то ничего он освобождать толком не будет.
Потому что полная сборка мусора — весьма ресурсоемкая операция.
F>Это же извращенство...
Системы с сборкой мусора ВСЕГДА резервируют больше памяти, чем требуется для актуальных данных.
В зависимости от устройства сборщика — в разы может быть.
Пользуйтесь ЯП с ручным распределением памяти тогда.
F>Потому и спрашиваю, может есть способы сократить объем выделяемой памяти.
Потому и отвечаю что вы не о том паритесь.
Здравствуйте, v.a.v, Вы писали:
VAV>Может кто нибудь приведет ссылку, где бы кратко пояснялись принципы управления памятью в Windows и .NET?
Хм, кратко... Хотелось бы мне посмотреть, на такое краткое описание
Начать можно с Рихтера, " Создание эффективных WIN32-приложений". Там, как обычно, упор больше на практическую сторону, однако и обзор общих принципов есть. В принципе, если подойти вдумчиво, то достаточное для практики представление получить можно. Далее, для углубления, можно посмотреть Руссинович, Соломон, "Внутреннее устройство Microsoft Windows" Здесь подход уже куда более фундаментальный. Ну а после, опираясь на эту информацию, уже можно переосмыслить и статьи о работе GC в NET-приложениях.
Почему-то я примерно так это себе и представляла.
Работающий код, пожалуйста. То, что вы написали, работать не может.
Ну даже если предположим, что myReader, MapIDReader и MapID — это одна и та же переменная, которая переименовалась при копипасте, то все равно надо ридеры закрывать внутри цикла, т.е. на каждый new StreamReader должен быть вызов StreamReader.Close().
А в вашем коде вы закрываете только один последний, а создаете их много — по одному для каждого файла.
Здравствуйте, v.a.v, Вы писали:
VAV>C2 требует именно обнуления памяти перед выдачей ее процессу? VAV>Может быть требуется просто не допускать возможности чтения процессом чужого "мусора" в свежеполученной памяти? Это ведь разные вещи. Возможность чтения процессом собственного "мусора" врядли повлияет на безопасность.
С2 требует обнуления, если страница передаётся другому процессу и если она была текущим процессом модифицирована. В этом случае она попадает к потоку обнуления страниц, а оттуда опять в ведение менеджера набора.
JR>Я конечно за то, чтобы закрывать такие ресурсы сразу, но и без этого утечки хёндлов в DotNet не будет. Признаться, наблюдаемое поведение совпадает с моими представлениями, но может быть мои представления ошибочны? Как мне увидеть то явление, о котором вы говорили — то есть наблюдаемую в диспетчере ктечку от незакрытых явно стримов?
Мы обсуждаем утечку от незакрытых стримов или утечку вообще? Не приведу я вам примера. Это ж сколько надо хэндлов держать, чтобы цифра в диспетчере заметно увеличилась? Конечно, это не показатель. Но вообще, программы обычно делают не одну единственную глупость, а много разных вещей. Лучше сразу научиться хэндлы закрывать, чем потом когда-нибудь искать источник неприятностей.
Про раздраженный тон — боюсь это ваши собственые эмоции при чтении так преломляются.
Не хочу я никаких примеров приводить. Попробуйте устроиться на саппорт в контору, которая софт для разработчиков продает Уж вам юзеры таких примеров покажут, никакой фантазии не хватит, чтобы это заранее вообразить. И даже в вашем описании теста, ооочень много ваших потенциальных клиентов нашли бы повод для скандала.
Ну давайте теперь про SafeHandle уточним. Когда будут хэндлы закрыты — при выходе из программы? Есть какая-то вероятность, что вот мы так все бросим, хэндлы сами собой через полчасика закроются, а программа будет продолжать работать?
Создаю StreamReader, читаю им последовательно несколько файлов размером 10-80 МБ. Когда все прочитано, вызываю метод StreamREader.Close(), но никаких изменений в объеме выделенной памяти не происходит. Из каждого файла извлекается по единственной строке длиной ~30 символов. В результате такой обработки объем выделенной памяти подскакивает с 40 до 60 МБ. Даже 40 МБ меня пугают, не говоря уже о 60.
Подскажите, как сократить объем выделяемой памяти? Есть какие-то методы оптимизации, принудительной сборки мусора?
Здравствуйте, Flammable, Вы писали:
F>Подскажите, как сократить объем выделяемой памяти? Есть какие-то методы оптимизации, принудительной сборки мусора?
Сборка мусора для того и сделана чтобы об освобождении памяти — НЕ думать.
Когда нужно, тогда и память и будет освобождена. В том числе и по просьбе ОСи. А если 2Гига и только ваше приложение, то: F>Даже 40 МБ меня пугают, не говоря уже о 60.
чего именно пугаетесь? Что ОСь не просит приложение вернуть в ее распоряжение какие-то там пару десятков мегабайт?
Если Вам хочется ручного управления памятью, переходите на С++
Здравствуйте, Skynin, Вы писали:
S>Здравствуйте, Flammable, Вы писали:
F>>Подскажите, как сократить объем выделяемой памяти? Есть какие-то методы оптимизации, принудительной сборки мусора? S>Сборка мусора для того и сделана чтобы об освобождении памяти — НЕ думать.
S>Когда нужно, тогда и память и будет освобождена. В том числе и по просьбе ОСи. А если 2Гига и только ваше приложение, то: F>>Даже 40 МБ меня пугают, не говоря уже о 60. S>чего именно пугаетесь? Что ОСь не просит приложение вернуть в ее распоряжение какие-то там пару десятков мегабайт?
S>Если Вам хочется ручного управления памятью, переходите на С++
Программа немаленькая, уже написана. Не нравятся мне такие огромные траты памяти впустую.
Добавил после ресурсоемких операций вызов метода GC.Collect(), использование памяти стало на 5 мб меньше.
Сейчас просто загруженная форма занимает 30 мб памяти. Что можно хранить в 30 мегабайтах? Это же извращенство.. Потому и спрашиваю, может есть способы сократить объем выделяемой памяти.
Здравствуйте, Flammable, Вы писали:
F>Кроме того меня интересует, почему StreamReader.Close() ничего не делает. Собственно, как и StreamReader.Dispose()
Ну открой исходники и посмотри. Делов на пять минут.
Здравствуйте, Flammable, Вы писали:
F>Кроме того меня интересует, почему StreamReader.Close() ничего не делает. Собственно, как и StreamReader.Dispose()
А он и не должен ничего делать. Он ничего не держит в себе, кроме малеьнокого буфера на несколько байт.
Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, Flammable, Вы писали:
F>>Кроме того меня интересует, почему StreamReader.Close() ничего не делает. Собственно, как и StreamReader.Dispose()
L>А он и не должен ничего делать. Он ничего не держит в себе, кроме малеьнокого буфера на несколько байт.
Тогда на что тратятся 20 мб памяти во время чтения файлов?
Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, Flammable, Вы писали:
F>>>>Кроме того меня интересует, почему StreamReader.Close() ничего не делает. Собственно, как и StreamReader.Dispose()
L>>>А он и не должен ничего делать. Он ничего не держит в себе, кроме малеьнокого буфера на несколько байт.
F>>Тогда на что тратятся 20 мб памяти во время чтения файлов?
L>Скорее всего на временные строки. Скачайте какой-нить профайлер, который умеет смотреть память .Net-ных процессов и посмотрите, на что тратится память.
И что, от этих бесполезных временных строк нельзя избавляться?
Здравствуйте, Flammable, Вы писали:
F>>>Тогда на что тратятся 20 мб памяти во время чтения файлов?
L>>Скорее всего на временные строки. Скачайте какой-нить профайлер, который умеет смотреть память .Net-ных процессов и посмотрите, на что тратится память.
F>И что, от этих бесполезных временных строк нельзя избавляться?
Можно. Когда будет нужно, сборщик мусора их соберет.
Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, Flammable, Вы писали:
F>>>>Тогда на что тратятся 20 мб памяти во время чтения файлов?
L>>>Скорее всего на временные строки. Скачайте какой-нить профайлер, который умеет смотреть память .Net-ных процессов и посмотрите, на что тратится память.
F>>И что, от этих бесполезных временных строк нельзя избавляться?
L>Можно. Когда будет нужно, сборщик мусора их соберет.
Тем не менее он сборщик мусора ничего не собирает и память не освобождается.
Здравствуйте, Flammable, Вы писали:
F>И что, от этих бесполезных временных строк нельзя избавляться?
Они не бесполезные, и скорее всего сборщиком уже собраны. Если не хочется углубляться в механизмы, то можно принять за аксиому, что управление памятью в Windows писали отнюдь не ламеры. NET'овскую надстройку, я думаю, тоже.
Здравствуйте, Flammable, Вы писали:
F>>>И что, от этих бесполезных временных строк нельзя избавляться?
L>>Можно. Когда будет нужно, сборщик мусора их соберет.
F>Тем не менее он сборщик мусора ничего не собирает и память не освобождается.
Скачайте какой-нить профайлер, который умеет смотреть память .Net-ных процессов и посмотрите, на что тратится память.
Покажите тут в трех строках, как вы последовательно несколько файлов читаете одним ридером. Такое ощущение, что я чего-то пропустила и не представляю, как это вообще можно сделать. Прямо начиная от создания ридера, или от создания того Stream'а, который собственно файл читает. Т.е. с самого начала работы с файлами и до Close.
Скорей всего проблема вовсе не в Closе, а где-то раньше, если она вообще есть конечно
Здравствуйте, Jolly Roger, Вы писали:
JR>Если не хочется углубляться в механизмы, то можно принять за аксиому, что управление памятью в Windows писали отнюдь не ламеры. NET'овскую надстройку, я думаю, тоже.
Только, далеко не факт, что ценности этих не-ламеров совпадают с вашими.
_FR>Это очень легко: стрим, который внутри себя открывает по необходимости дополнительные стримы
Ну нет, автор в вопросе вообще стримы не упоминает. В любом случае, позволю себе считать, что тогда стрим ридер был не один, а с кем-то
Лучше не гадать, а подождать пока автор код покажет. А то на догадках можно тотализатор открывать
Здравствуйте, notacat, Вы писали:
_FR>>Это очень легко: стрим, который внутри себя открывает по необходимости дополнительные стримы N>Ну нет, автор в вопросе вообще стримы не упоминает. В любом случае, позволю себе считать, что тогда стрим ридер был не один, а с кем-то
Автор определённо упоминает созданный им StreamReader, который может быть создан исключительно от стороннего стрима или от строки. По одной строке "читать последовательно несколько файлов" несколько, но затруднительно остаётся один единственный вариант — некий хитрый Stream, передающийся в СтримРидер. Ну или откровенный бред автора
N>Лучше не гадать, а подождать пока автор код покажет. А то на догадках можно тотализатор открывать
ИМХО, в моих рассуждениях относительно того, как через СтримРидер автору удалось "прочесть последовательно несколько файлов" нет никаких догадок, кроме предположения о том, что топикстартер был "в своём уме", как говорится, и знал, что пишет
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, Skynin, Вы писали:
S>Читайте документацию — вызов Collect() всего лишь просит приблизить время очередной проверки/очистки. S>А если GC видит что свободной памяти полно, то ничего он освобождать толком не будет.
Вы неправы. При вызове GC.Collect() сборка мусора начинается немедленно.
S>Потому что полная сборка мусора — весьма ресурсоемкая операция. F>>Это же извращенство... S>Системы с сборкой мусора ВСЕГДА резервируют больше памяти, чем требуется для актуальных данных.
А это правда. Не стоит вмешиваться в работу сборщика мусора пока это действительно не понадобится.
Может кто нибудь приведет ссылку, где бы кратко пояснялись принципы управления памятью в Windows и .NET?
Спасибо почитал. То что описано в первой части статьи я знаю.
А вот вторая часть(о способах сборки мусора) написана как то сумбурно(или плохо переведено).
Перед запуском сборки мусора все управляемые потоки, кроме потока, запустившего сборку мусора, приостанавливаются.
Параллельная сборка мусора позволяет управляемым потокам продолжать операции во время сборки мусора.
Так параллельно или нет? Наверное все таки и та и так(в зависимости от настроек). Но тогда первая фраза в своем контексте не верна.
Начиная с .NET Framework 4, параллельная сборка мусора заменяется фоновой сборкой мусора.
Сборка для эфемерных поколений во время фоновой сборки мусора называется высокоприоритетной сборкой мусора.Во время выполнения высокоприоритетных сборок мусора все управляемые потоки приостанавливаются.
Когда выполняется фоновая сборка мусора и в поколении 0 распределено достаточное количество объектов, среда CLR выполнит высокоприоритетную сборку мусора для поколения 0 или поколения 1.Выделенный поток фоновой сборки мусора проверяет в частых точках, безопасных для сбора мусора, чтобы определить, не появился ли запрос выполнения высокоприоритетной сборки мусора.В этом случае фоновая сборка мусора приостанавливается, чтобы позволить выполниться высокоприоритетной сборке мусора.После выполнения высокоприоритетной сборки мусора работа выделенного потока фоновой сборки мусора и пользовательских потоков возобновляется.
Фоновая сборка мусора удаляет ограничения на распределение, наложенные параллельной сборкой мусора, так как эфемерные сборки мусора могут выполняться во время фоновой сборки мусора.Это означает, что фоновая сборка мусора может удалить неиспользуемые объекты в эфемерных поколениях, а также при необходимости может расширить во время сборки мусора для поколения 1.
Фоновая сборка мусора в настоящее время недоступна для сборки мусора сервера.
Получается что фоновая сборка это некий гибрид между параллельной и не параллельной сборкой.
Изображения почему то не отображаются.
По поводу показаний диспетчера задач.
Включена ли в "рабочий набор" память занятая разделяемыми(системными) библиотеками?
По поводу .NET
Какова стратегия резервирования памяти средой CLR?
Одно и то же .NET приложение при различной "степени дефицитности" памяти в системе может занимать от 1 до 30Мб(по показаниям диспетчера задач). Эта память действительно освобождается, или просто сбрасывается в своп, при дефиците физической памяти?
Здравствуйте, v.a.v, Вы писали:
VAV>Здравствуйте, Jolly Roger, Вы писали:
VAV>По поводу показаний диспетчера задач. VAV>Включена ли в "рабочий набор" память занятая разделяемыми(системными) библиотеками?
Рабочий набор — вообще "тонкая штучка, вещь в себе" Её размер слабо связан с выделением и освобождением памяти приложением, управляет им специальный системный поток, working set manager. По факту (и насколько я понимаю), это набор страниц физической памяти (не виртуальной!) Воздействие на неё со стороны приложения ограничено функцией SetProcessWorkingSetSize, но пользоваться ей без точного понимания не следует, стратегия менеджера удовлетворительна для подавляющего большинства приложений. Разумеется, сюда входят и страницы с разделяемыми библиотеками, точнее с теми их частями которые на данный момент лежат в физ. памяти.
VAV>По поводу .NET VAV>Какова стратегия резервирования памяти средой CLR? VAV>Одно и то же .NET приложение при различной "степени дефицитности" памяти в системе может занимать от 1 до 30Мб(по показаниям диспетчера задач).
Это детали реализации, не столь и существенные с точки зрения прикладного кода, откровенно говоря. Важно то, что он по факту использует "разумно жадную" стратегию, защищая приложение от голода Ведь память покупается для того, чтобы её использовать, а не "шоб було"
VAV>Эта память действительно освобождается, или просто сбрасывается в своп, при дефиците физической памяти?
И это не суть важно с точки зрения приложения. Куда важнее, чтобы он быстро управлялся со своими внутренними данными, максимально быстро реагируя на запросы приложения. И с этой точки зрения чем больше у него взятой у системы памяти, тем лучше. Но конечно, и разумных границ никто не отменял, но он за них и не выходит, по моим наблюдениям.
А сброс в своп зависит не только и не столько от размера выделенной памяти, сколько от размера реально использованной. Если страница не была модифицирована, то никто её записывать на диск не станет, зачем? Но это епархия системного менеджера. Да она и на физ. страницу-то не будет отображена, пока к ней не будет реального обращения.
VAV>Буду читать Рихтера.
Здравствуйте, Jolly Roger, Вы писали:
JR>Да она и на физ. страницу-то не будет отображена, пока к ней не будет реального обращения.
Спасибо, не знал. Но почему же тогда зарезервированная CLR память(и по большей части не использованная), входит в рабочий набор(по показаниям диспетчера задач)? Ведь рабочий набор это физическая память.
Что означают оценки "+1", "1", "2", "3"? На что они влияют?
Здравствуйте, v.a.v, Вы писали:
VAV>Здравствуйте, Jolly Roger, Вы писали:
JR>>Да она и на физ. страницу-то не будет отображена, пока к ней не будет реального обращения. VAV>Спасибо, не знал. Но почему же тогда зарезервированная CLR память(и по большей части не использованная), входит в рабочий набор(по показаниям диспетчера задач)? Ведь рабочий набор это физическая память.
А как Вы это определили? Working Set формируется системным менеджером, исходя из собственных алгоритмов, главная цель которого — облегчить жизнь приложению. Насколько я могу судить, в нём есть и страницы, которые не были ни запрошены, ни использованы приложением. Можете проделать "фокус" — запустите приложение, чтобы оно выполнило какую-нибудь ресурсоёмкую операцию и засеките размер Working Set. Теперь сверните все окна приложения и опять засеките размер. Теперь опять разверните.
VAV>Что означают оценки "+1", "1", "2", "3"? На что они влияют?
Здравствуйте, Flammable, Вы писали:
F>Создаю StreamReader, читаю им последовательно несколько файлов размером 10-80 МБ. Когда все прочитано, вызываю метод StreamREader.Close(), но никаких изменений в объеме выделенной памяти не происходит. Из каждого файла извлекается по единственной строке длиной ~30 символов. В результате такой обработки объем выделенной памяти подскакивает с 40 до 60 МБ. Даже 40 МБ меня пугают, не говоря уже о 60. F>Подскажите, как сократить объем выделяемой памяти? Есть какие-то методы оптимизации, принудительной сборки мусора?
Код покажите.
Может вы там еще какие-нибудь хештаблицы создаете, которые подгружают кучу левых дллок в память
JR>А как Вы это определили? Working Set формируется системным менеджером, исходя из собственных алгоритмов, главная цель которого — облегчить жизнь приложению. Насколько я могу судить, в нём есть и страницы, которые не были ни запрошены, ни использованы приложением.
То есть система может "дарить" процессу страницы физической памяти даже если он их не просил?
Или действует логика:"Если попросит, я(система) смогу их быстро предоставить." То есть система не "дарит" память а просто готовит ее к выдаче по будущим запросам. Так? Тогда размер Working Set действительно ничего не говорит о реальной "прожорливости" процесса.
JR>Можете проделать "фокус" — запустите приложение, чтобы оно выполнило какую-нибудь ресурсоёмкую операцию и засеките размер Working Set. Теперь сверните все окна приложения и опять засеките размер. Теперь опять разверните.
Да и без ресурсоемких операций Working Set уменьшается при сворачивании и увеличивается при разворачивании. Можно подумать что работает логика: "Окно приложения активно, процессу вскорости может потребоваться память. Подготовлю память заранее." Но почему тогда при перемещении окна на задний/передний план эта логика не работает(не видно изменений размера рабочего набора)?
Здравствуйте, Jolly Roger, Вы писали:
JR>Здравствуйте, Don Reba, Вы писали:
JR>>>Если не хочется углубляться в механизмы, то можно принять за аксиому, что управление памятью в Windows писали отнюдь не ламеры. NET'овскую надстройку, я думаю, тоже.
DR>>Только, далеко не факт, что ценности этих не-ламеров совпадают с вашими.
JR>Ну с моими-то совпадают Тут дело такое, я подобных обсуждений видел множество, и заканчивались они всегда одним из двух вариантов. Либо автор читал что-нибудь по теме, и вопрос снимался, либо автор разбираться не желал, и уходил со стойким убеждением, что "винда маздай"
JR>Ну а в данной ветке особо-то и говорить не о чем, так как ТС так и не сообщил, откуда он берёт озвученные величины. А почти всегда в таких случаях за источник выбирают те показания из диспетчера, которые к реально занятой процессом памяти имеют наименьшее отношение, такая уж традиция сложилась И причина этому, видимо, всё та-же: у тех, кто знаком с механизмами хотя-бы в общих чертах, подобных вопросов не возникает, а те, кто не знаком, почему-то даже справку диспетчера читать не хотят
Справку я читал, правда давно В общем, StreamReader.Close() все-таки делает свое дело. Частный рабочий набор с его использованием — 22МБ, без него — 37МБ. Хотя 22МБ тоже толстовато.
Здравствуйте, Flammable, Вы писали:
F>Справку я читал, правда давно В общем, StreamReader.Close() все-таки делает свое дело. Частный рабочий набор с его использованием — 22МБ, без него — 37МБ. Хотя 22МБ тоже толстовато.
Вытащите гигабайт-другой ОЗУ из компьютера и посмотрите сколько будет показывать.
Здравствуйте, Jolly Roger, Вы писали:
JR> Ну и при освобождении вызовом VirtualFree страницы не изымаются сразу из Process Working Set, что тоже вполне логично — они могут тут-же потребоваться снова, и при наличии резерва зачем делать лишние движения?
Это не так. Винда (по крайней мере серверная) сертифицирована по стандарту безопасности С2, который среди всего прочего требует, чтобы ядро ОС всегда обнуляло страницы перед передачей их приложению. Об этом кстати у Рихтера написано.
Здравствуйте, koandrew, Вы писали:
K>Это не так. Винда (по крайней мере серверная) сертифицирована по стандарту безопасности С2, который среди всего прочего требует, чтобы ядро ОС всегда обнуляло страницы перед передачей их приложению. Об этом кстати у Рихтера написано.
Да, С2 требует. Но если я правильно помню, прямой корелляции между освобождённой памятью и изменением рабочего набора я не заметил. То есть механизм, видимо, действует несколько сложнее. Впрочем, так как это было не вчера, то есть вероятность, что память меня таки подводит В любом случае, каждый желающий всегда может поставить экспиремент самостоятельно.
Здравствуйте, Jolly Roger, Вы писали:
JR>Да, С2 требует. Но если я правильно помню, прямой корелляции между освобождённой памятью и изменением рабочего набора я не заметил. То есть механизм, видимо, действует несколько сложнее. Впрочем, так как это было не вчера, то есть вероятность, что память меня таки подводит В любом случае, каждый желающий всегда может поставить экспиремент самостоятельно.
Здравствуйте, Jolly Roger, Вы писали:
JR>Здравствуйте, koandrew, Вы писали:
K>>Это не так. Винда (по крайней мере серверная) сертифицирована по стандарту безопасности С2, который среди всего прочего требует, чтобы ядро ОС всегда обнуляло страницы перед передачей их приложению. Об этом кстати у Рихтера написано.
JR>Да, С2 требует. Но если я правильно помню, прямой корелляции между освобождённой памятью и изменением рабочего набора я не заметил. То есть механизм, видимо, действует несколько сложнее. Впрочем, так как это было не вчера, то есть вероятность, что память меня таки подводит В любом случае, каждый желающий всегда может поставить экспиремент самостоятельно.
C2 требует именно обнуления памяти перед выдачей ее процессу?
Может быть требуется просто не допускать возможности чтения процессом чужого "мусора" в свежеполученной памяти? Это ведь разные вещи. Возможность чтения процессом собственного "мусора" врядли повлияет на безопасность.
Да это я читал, а что не так? Это описание слишком уж расплывчатое, явно видно цель — не сказать-бы чего лишнего Но и там, кстати сказано "Pages can be removed", а не, скажем "are removed", да и VirtualFree не упомянута, хорошо хоть про VirtualUnlock не забыли
Закрывать конечно надо, всё правильно, только вряд-ли это повлияет на обсуждаемое. Не тот показатель ТС использует. Да и вообще, пытаться определить утечки с помощью дисперчера задач для приложений, имеющих локальный менеджер памяти — занятие, мягко говоря, странное. Это его работа, локального менеджер — брать у системы большие регионы памяти и потом выдавать приложению частями по мере надобности, для того локальные менеджеры и пишутся даже там, где GC нет. Ну и отдавать системе память сразу при её освобождении приложением было-бы неразумно с точки зрения производительности, ни один менеджер этого и не делает, а диспетчер задач видит именно то, что взял у системы локальный менеджер процесса. Тем более что это просто диапазон адресов, имеющий смысл только для локального процесса, и влияние его на другие процессы и систему в целом весьма ограничено. Впрочем, об этом уже говорилось.
Здравствуйте, notacat, Вы писали:
N>Почему-то я примерно так это себе и представляла. N>Работающий код, пожалуйста. То, что вы написали, работать не может.
N>Ну даже если предположим, что myReader, MapIDReader и MapID — это одна и та же переменная, которая переименовалась при копипасте, то все равно надо ридеры закрывать внутри цикла, т.е. на каждый new StreamReader должен быть вызов StreamReader.Close(). N>А в вашем коде вы закрываете только один последний, а создаете их много — по одному для каждого файла.
Извиняйте, копипаст действительно кривой, не проверил.
Вот полный код метода:
private string[] GetSocMapNames(string RootFolder)
{
string[] Maps = Directory.GetFiles(RootFolder + @"\mods", "*.xdb0");
string[] MapNames = new string[Maps.Length];
string MapLine = "", MapName = "";
StreamReader MapID = null;
int i = 0;
foreach (string map in Maps)
{
Application.DoEvents();
MapID = new StreamReader(map, Encoding.GetEncoding(1251));
while (!MapID.EndOfStream)
{
MapLine = MapID.ReadLine();
if (MapLine.Contains(@"map\"))
{
MapLine = MapLine.Trim();
if (MapLine.StartsWith("texture"))
{
MapName = MapLine.Substring(MapLine.IndexOf(@"map\") + 8); ////break;
}
}
}
MapNames[i] = MapName;
i++;
}
MapID.Close();
return MapNames;
}
...
О чём и говорили. Закрывай стримы явно каждый либо (что лучше) просто используй using.
Сообщение заговорено потомственным колдуном, целителем и магом в девятом поколении!
Модерирование или минусование сообщения ведет к половому бессилию, венерическим заболеваниям, венцу безбрачия и диарее!
Здравствуйте, Flammable, Вы писали:
F>Вот полный код метода:
1) Повесить using на создание reader, убрать Close().
2) Этот код будет падать (NullReferenceException) если в каталоге нет ни одного файла с нужной маской (на MapID.Close()).
3) MapName не чистится в каждой итерации цикла — в итоге MapNames[] может оказаться заполнен мусорными значениями с прошлых итераций (в первом файле texture была, в остальных нет)
4) DoEvents действительно здесь нужна? Просто практика показывает что от этого метода проблема ой как много, а нужен он ой как редко.
Здравствуйте, Jolly Roger, Вы писали:
JR>Здравствуйте, v.a.v, Вы писали:
VAV>>C2 требует именно обнуления памяти перед выдачей ее процессу? VAV>>Может быть требуется просто не допускать возможности чтения процессом чужого "мусора" в свежеполученной памяти? Это ведь разные вещи. Возможность чтения процессом собственного "мусора" врядли повлияет на безопасность.
JR>С2 требует обнуления, если страница передаётся другому процессу и если она была текущим процессом модифицирована. В этом случае она попадает к потоку обнуления страниц, а оттуда опять в ведение менеджера набора.
И я о том-же. Нет смысла обнулять страницу если планируется её вернуть тому-же процессу. Это не противоречит требованиям C2.
А учитывая что:
"Pages can be removed", а не, скажем "are removed"
Вы были правы, что станицы могут не удаляться из рабочего набора, сразу после их освобождения процессом.
JR>Закрывать конечно надо, всё правильно, только вряд-ли это повлияет на обсуждаемое. Не тот показатель ТС использует. Да и вообще, пытаться определить утечки с помощью дисперчера задач для приложений, имеющих локальный менеджер памяти — занятие, мягко говоря, странное.
Поверьте на слово, что если упорно не закрывать стримы и не освобождать ссылки, то даже диспетчер задач это покажет. И если сделать пример, в котором подозрительные действия выполняются в цикле, и аккуратно просить каждый раз GC все лишнее почистить, то за несколько итераций в диспетчере задач прекрасно видно, течет память или не течет.
А вот остальные детали и лечение — это уже конечно не к диспетчеру задач надо обращаться.
Ну и отдельный вопрос — что половина участников уже забыла, что нужно было топик стартеру. Вы уже все внутренности Windows обсудили, но это не значит, что именно это является обсуждаемым.
Здравствуйте, Jesmus, Вы писали:
J>Здравствуйте, Flammable, Вы писали:
F>>Вот полный код метода:
J>1) Повесить using на создание reader, убрать Close(). J>2) Этот код будет падать (NullReferenceException) если в каталоге нет ни одного файла с нужной маской (на MapID.Close()). J>3) MapName не чистится в каждой итерации цикла — в итоге MapNames[] может оказаться заполнен мусорными значениями с прошлых итераций (в первом файле texture была, в остальных нет) J>4) DoEvents действительно здесь нужна? Просто практика показывает что от этого метода проблема ой как много, а нужен он ой как редко.
2. В каталоге всегда есть файлы Хотя, чем черт не шутит. Надо и это предусмотреть, спасибо.
3. Разве присваивание переменной нового значения не удаляет старое?
4. DoEvents действительно нужна, иначе форма зависает. Файлы объемные, читаются не моментально. А что за проблемы с этим методом?
Здравствуйте, v.a.v, Вы писали:
VAV>Вы были правы, что станицы могут не удаляться из рабочего набора, сразу после их освобождения процессом.
Может да, а может нет В принципе, обнуление страницы может быть выполнено и без удаления из рабочего набора процесса, в системе есть функция для быстрого обнуления страниц, что-то вроде MmZeroInPage или похоже. Но и для противоположного мнения можно найти аргументы. В любом случае, точный ответ знают только разработчики. Но ведь с точки зрения прикладного кода это и не суть важно. Важно то, что мы имеем дело с виртуальной памятью, а отображением её на физическую полностью заведует система. Есть, правда, исключение в виде VirtualLock, но это именнно что исключение.
Здравствуйте, Jesmus, Вы писали:
J>4) Проблемы синронизации. Код с DoEvents должен быть спроетирован также как код с использование потоков — а почему бы тогда не использовать потоки сразу? И опыт подтверждает — видишь в коде DoEvents — будь готов к проблемам, когда пользователь не там щелкнул, сработало событие таймера и изменило состояние объектов посреди метода и прочие прелести многопоточного программирования.
Здравствуйте, notacat, Вы писали:
N>Поверьте на слово, что если упорно не закрывать стримы и не освобождать ссылки, то даже диспетчер задач это покажет. И если сделать пример, в котором подозрительные действия выполняются в цикле, и аккуратно просить каждый раз GC все лишнее почистить, то за несколько итераций в диспетчере задач прекрасно видно, течет память или не течет. N>А вот остальные детали и лечение — это уже конечно не к диспетчеру задач надо обращаться.
Я безусловно и с удовольствием верю Вам на слово, но для приобретения опыта хотелось-бы увидеть явление своими глазами Я вот взял код, приведённый ТС, и запустил его без единого изменения на директории с 1000-й файлов, и даже без принудительного вызова GC.Collect. При первом запуске метода объём Working set вырос с 12288 до 14552, а витуальной памяти — с 11600 до 13572. Дальнейшие многократные запуски этого кода не привели к какому-либо росту обоих показателей, они то чуть возрастают, то чуть уменьшаются. Дескрипторы тоже не растут, что по-моему неудивительно, они ведь завёрнуты в SafeHandle, имеющий деструктор, и GC их в своё время закрывает. Я так понимаю, закрывать стрим нужно, чтобы освободить файл немедленно, а иначе он всё равно будет закрыт коллектором, только несколько позже. Я конечно за то, чтобы закрывать такие ресурсы сразу, но и без этого утечки хёндлов в DotNet не будет. Признаться, наблюдаемое поведение совпадает с моими представлениями, но может быть мои представления ошибочны? Как мне увидеть то явление, о котором вы говорили — то есть наблюдаемую в диспетчере ктечку от незакрытых явно стримов?
N>Ну и отдельный вопрос — что половина участников уже забыла, что нужно было топик стартеру. Вы уже все внутренности Windows обсудили, но это не значит, что именно это является обсуждаемым.
Но ведь эта половина не виновата, что данное явление тесно связана с системными сервисами, без знакомства с которыми невозможно правильно расставить акценты Я вот пытался объяснить ему, почему он эти данные не подходят, но видимо безуспешно А судя по раздражённому тону Вашего поста, он не одинок, но разве это моя вина? Может быть, чтобы никого не раздражать, мне следовало поддержать его мнение, хотя я знаю, что это полная чушь? Как Вы считаете?
Здравствуйте, notacat, Вы писали:
N>Мы обсуждаем утечку от незакрытых стримов или утечку вообще?
Ну лично я готов обсуждать с Вами что угодно и сколько угодно Но вообще-то вроде Вы предлагали открывать в цикле стримы и это должно показать утечку памяти в диспетчере, что, в принципе, соответствует коду ТС. Но я не настаиваю, см. выше
N>Не приведу я вам примера.
Ну и ладно.
N>Это ж сколько надо хэндлов держать, чтобы цифра в диспетчере заметно увеличилась?
Да вобщем-то достаточно одного Диспетчер, он их поштучно считает.
N>Конечно, это не показатель.
А что тогда показатель?
N>Но вообще, программы обычно делают не одну единственную глупость, а много разных вещей. Лучше сразу научиться хэндлы закрывать, чем потом когда-нибудь искать источник неприятностей.
Совершенно с Вами согласен. Но на утечки памяти процесса это не влияет. Точнее, на ту память которую мы тут как-бы пытались обсуждать... но молчу, молчу, во внутренности системы не лезу
N>Про раздраженный тон — боюсь это ваши собственые эмоции при чтении так преломляются.
Вот и чудненько, так и зафиксируем для истории
N>Не хочу я никаких примеров приводить. Попробуйте устроиться на саппорт в контору, которая софт для разработчиков продает Уж вам юзеры таких примеров покажут, никакой фантазии не хватит, чтобы это заранее вообразить. И даже в вашем описании теста, ооочень много ваших потенциальных клиентов нашли бы повод для скандала.
Да я уже понял Бог с ним, с примером.
N>Ну давайте теперь про SafeHandle уточним.
Да-а-а-вайте Только чур, не обвинять меня опять, что я утаскиваю обсуждение в дебри ОС, я совсем чуть-чуть, а Вы сами первая начали
N>Когда будут хэндлы закрыты — при выходе из программы? Есть какая-то вероятность, что вот мы так все бросим, хэндлы сами собой через полчасика закроются, а программа будет продолжать работать?
Хёндлы файлов, как и многих других объектов, закрываются фунцией CloseHandle. Сам по себе хёндл это целое число, размером IntPtr. В DotNet этот хёндл заворачивается в потомка SafeHandle, который в своём методе Dispose и вызывает CloseHandle. Кроме того, у него есть унаследованный от CriticalFinalizerObject виртуальный деструктор, в котором он вызывает свой-же виртуальный Dispose. Ну а когда вызывается деструктор объекта, Вы наверняка знаете не хуже, а то и лучше меня. Так что при одной из сборок мусора хёндлы будут закрыты сборщиком, а связанные с объектами буферы, как и сами объекты будут собраны ещё раньше. Причем чем чаще будет происходить сборка, тем быстрее они будут закрыты. Но и это можно ускорить. Я например, добавил к примеру вызов двух функций
GC.Collect();
GC.WaitForPendingFinalizers();
после чего показания диспетчера вообще практически "замёрзли".
Так что вероятность тут практически 100% Но как Вы справедливо заметили, делать так не следует, прежде всего из-за недетерменированности момента закрытия хёндла, и всё это время объект, чей хёндл не закрыт, будет занят (не всегда, но — не углубляюсь ), а это может создать проблемы как для самого приложения, так и для сторонних процессов.