Здравствуйте, Sinclair, Вы писали:
S>Сохранили наш регексп в static-поле, получив 1 экземпляр на всё время жизни приложения.
Хороший пример, кстати, демонстрирующий разную психологию отношения к памяти.
Программист, для которого управляемый язык родной, сделал бы так, как вы сделали, независимо от compiled и чего бы то ни было. Память не ресурс, чего ее беречь, GC все приберет, поехали.
А вот программист, пришедший из C++/Pascal в управляемый язык, скорее всего сразу подумал бы, что одной переменной хватит. static или иначе — это детали, а вот на каждый new нужен delete, и с какой это стати я буду каждый раз в этом "цикле" new-delete делать, если можно вынести и поставить до "цикла" ?
With best regards
Pavel Dvorkin
Re[6]: Управление памятью в .NET для профессионалов
Здравствуйте, Kolesiki, Вы писали: K>И каким боком это к программистам?! Нашёл утечку и она не твоя — рапортуй в Рэдмонд, делов-то! Это их проблема — ковыряться на системном уровне, ОБЫЧНОМУ программисту это не нужно.
А ещё можно в канализационную трубу орать.
Я имею опыт взаимодействия с Рэдмондом. В самом лучшем случае решение займёт месяцы. А в приведённом случае — годы.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Управление памятью в .NET для профессионалов
Здравствуйте, Pavel Dvorkin, Вы писали: PD>А вот программист, пришедший из C++/Pascal в управляемый язык, скорее всего сразу подумал бы, что одной переменной хватит. static или иначе — это детали, а вот на каждый new нужен delete, и с какой это стати я буду каждый раз в этом "цикле" new-delete делать, если можно вынести и поставить до "цикла" ?
Оно и так стояло до "цикла".
Примерная логика — вот такая:
public IEnumerable<FileRef> GetIncompleteFiles()
{
var r = new Regex("\\/\\/ TODO:.*$", RegexOptions.Compiled);
foreach(var f in this.Project.GetAllFileRefs())
if (r.Match(f.GetContents())
yield return f;
}
Вполне себе невинный код. И в С++ такое запросто — у меня обычная auto-переменная, никаких тебе delete, разрушится по выходу из скоупа.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Управление памятью в .NET для профессионалов
Здравствуйте, Sinclair, Вы писали:
S>Оно и так стояло до "цикла". S>public IEnumerable<FileRef> GetIncompleteFiles()
Я имел в виду до "цикла" по GetIncompleteFiles.
А тут да, действительно автоматическая переменная. Вот только в C++ не было бы никакого new, и создавалась бы она на стеке, а там уничтожение автоматическое и гарантировано в известный момент, об этом и впрямь можно не очень думать. А если бы я хотел new, то пришлось бы в С++ либо Regex*, либо Regex& и delete. Далее см. мое предыдущее сообщение.
With best regards
Pavel Dvorkin
Re[9]: Управление памятью в .NET для профессионалов
Здравствуйте, Sinclair, Вы писали:
S>Примерная логика — вот такая: S>
S>public IEnumerable<FileRef> GetIncompleteFiles()
S>{
S> var r = new Regex("\\/\\/ TODO:.*$", RegexOptions.Compiled);
S> foreach(var f in this.Project.GetAllFileRefs())
S> if (r.Match(f.GetContents())
S> yield return f;
S>}
S>
S>Вполне себе невинный код. И в С++ такое запросто — у меня обычная auto-переменная, никаких тебе delete, разрушится по выходу из скоупа.
Эээ... Но ведь компилирование регекспа — это не самая лёгкая задача. Я интуитивно такие вещи в инициализацию куда-то вынишу.
Спасибо за внимание
Re[10]: Управление памятью в .NET для профессионалов
Здравствуйте, Doom100500, Вы писали: D>Эээ... Но ведь компилирование регекспа — это не самая лёгкая задача. Я интуитивно такие вещи в инициализацию куда-то вынишу.
Там на фоне стоимости обработки данных — несущественные семечки. Код бенчмаркали — он сильно ускорился после включения compiled. Создавать ещё один gc root — ну, такое себе решение.
Его надо принимать осознанно
Ну, и, конечно, на тот момент у нас опыта с дотнетом было недостаточно, чтобы такие вещи применять "интуитивно".
Кстати, примерно во времена третьего шарпа и дотнета 4.0 эта проблема была полностью устранена, в связи с реализацией DynamicMethod.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Управление памятью в .NET для профессионалов
Здравствуйте, Pavel Dvorkin, Вы писали: PD>А тут да, действительно автоматическая переменная. Вот только в C++ не было бы никакого new, и создавалась бы она на стеке, а там уничтожение автоматическое и гарантировано в известный момент, об этом и впрямь можно не очень думать. А если бы я хотел new, то пришлось бы в С++ либо Regex*, либо Regex& и delete. Далее см. мое предыдущее сообщение.
Павел, тут мышление С++ программистов никак не помогло бы и не помешало. Ну был бы у вас там new и delete — и дальше что?
Можно подумать, в вашем коде не бывает new и delete для локальных переменных.
Проблема-то — не в этом. Проблема — в том, что "вызов delete" на такой переменной освобождал не всю память.
На С++ запросто можно сделать такие же грабли — допустим, опция compiled будет динамически порождать исполняемый код, а вот освобождать она его почему-то не будет. VirtualAlloc будет делаться, а вот VirtualFree — нет.
Например, просто разработчик про это забыл. Не добавил этот VirtualFree в деструктор.
Или неправильно сделал подсчёт ссылок на исполняемый код при копировании regex.
Или вообще решил, что делать на каждый Regex отдельный VirtualAlloc — это оверкилл, и лучше сделать общую арену для динамического кода, которая будет освобождаться каким-то другим способом, не в момент вызова деструктора экземпляра regex.
В итоге — результат один: прикладной разработчик, не обладая глубокими знаниями о потрошках этого Regex, может получить точно такую же утечку. И точно так же он может её не заметить, если программа работает не 24*7.
То, как это было сделано в старом дотнете — не ошибка разработчика RegEx, а ограничение платформы.
Обсуждаемый вопрос — это что с этим делать. Вот у нас тут по соседству есть фанат точки зрения "не мои проблемы" — ну, течёт приложение и течёт. "Наверное, баг платформы".
Нам вот было интересно разобраться. Разбираться настолько глубоко, как в приведённых в топике книгах и картинках, не потребовалось, но понять, что вообще происходит — пришлось.
Ну, а после этого уже было очевидно, что можно сделать для решения проблемы.
А могло так оказаться, что реально найден баг в платформе. Тогда разборки дали бы возможность построить минимально воспроизводящий пример и отправить в Редмонд.
И так понятно, что никто в Редмонде не стал бы гонять наше приложение 72 часа под нагрузкой, чтобы найти причины утечки.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: Управление памятью в .NET для профессионалов
Здравствуйте, Sinclair, Вы писали:
S>Павел, тут мышление С++ программистов никак не помогло бы и не помешало. Ну был бы у вас там new и delete — и дальше что?
Я же написал — дальше вопрос, нужно ли это в "цикле". Потому что психологически программист на C++ не склонен к лишним new, поскольку помнит про delete. Вот и все.
S>Можно подумать, в вашем коде не бывает new и delete для локальных переменных.
Бывает, но если иначе нельзя.
S>Проблема-то — не в этом. Проблема — в том, что "вызов delete" на такой переменной освобождал не всю память.
S>На С++ запросто можно сделать такие же грабли — допустим, опция compiled будет динамически порождать исполняемый код, а вот освобождать она его почему-то не будет. VirtualAlloc будет делаться, а вот VirtualFree — нет. S>Например, просто разработчик про это забыл. Не добавил этот VirtualFree в деструктор.
Ну это то же самое. new-delete в другой упаковке. Если она на С++ пишет и грамотный специалист — должен об этом сразу думать.
S>В итоге — результат один: прикладной разработчик, не обладая глубокими знаниями о потрошках этого Regex, может получить точно такую же утечку. И точно так же он может её не заметить, если программа работает не 24*7.
Не обязан программист на С++ знать устройство всех классов STL или boost, а равно и других. Хватит, если в Regex нет ошибок и он правильно выделяет и освобождает память.
With best regards
Pavel Dvorkin
Re[11]: Управление памятью в .NET для профессионалов
Здравствуйте, Sinclair, Вы писали:
S>Ну, и, конечно, на тот момент у нас опыта с дотнетом было недостаточно, чтобы такие вещи применять "интуитивно".
Да я это, скорее, с точки зрения C++ писал как раз.
Но дело не этом. Я сам склонен разбираться до конца и найти проблему прежде всего у себя и не верить в баги комилятора и библиотек до последнего (но, в последнее время это мнение становится всё менее устойчивым ).
И книжку бы почитал, но в электронном виде. В форме 30-60 минут в день.
Edit:
Книга, кстати, интересная. Там не только про дотнет...
В книге представлены:
— теоретические основы автоматического управления памятью;
— глубокое погружение во все аспекты управления памятью в .NET, в т. ч. подробное описание реализации сборщика мусора (GC);
— практические советы по разработке реальных программ;
— правила использования инструментов, относящихся к управлению памятью в .NET;
эффективные методы работы с памятью, включая типы Span и Memory.
Кроме последнего пункта всё остальное для того и сделано, чтобы .Net-разработчику не думать о таких деталях.
Позволю процитировать себе Александреску в отношении rust(как самого замороченного на управлении памятью):
"этот парень пропускал дни кача ног(мем)".
Re[2]: Управление памятью в .NET для профессионалов
Здравствуйте, vaa, Вы писали:
vaa>Кроме последнего пункта всё остальное для того и сделано, чтобы .Net-разработчику не думать о таких деталях. vaa>Позволю процитировать себе Александреску в отношении rust(как самого замороченного на управлении памятью): vaa>"этот парень пропускал дни кача ног(мем)".
Там приведен пример, когда при переборе элементов 2-мерного массива видна практическая разница в скорости доступа — это протягивается с уровня процессора и никаким образом не может быть изменено даже на таком высоком уровне как .Net программа.
Re[2]: Управление памятью в .NET для профессионалов
Здравствуйте, Kolesiki, Вы писали:
K>Практически никогда.
При этом, память и GC это первая тема на собесах. Люди не представляют как их кривые мапинги из ORM через dynamic прямо в ад н...й в json без знания литаний о поколениях GC можно поддерживать.