G>Маленький ликбез:
G>1)Стандартный аллокатор поддерживает связный список блоков памяти, выделение нового и освобождение блока вызывает проход по списку, который имеет алгоритмическое время O(n) от количества блоков.
Ты сам в это веришь? Это наивная реализация 80-ых годов. Сейчас используются всякого рода оптимизации, позволяющие минимизировать время поиска и частично отказаться от блокировок кучи. Например, в windows heap создается, если мне память не изменяет, на одну кучу 128 ассоциативных однонаправленных списков, не требующих блокировок.
G>При постоянных выделениях-освобождениях памяти получются очень большие затраты на эту простую операцию.
В С# в целом та же херня.
G>2)При выделении блока идет небольшой оверхед, который зависит от реализации, 16-32 байта кажись. Исследования исходных кодов программ показывают что средний размер объекта составляет от 32 до 128 байт, при такких размерах оверхед является очень значительным.
Достаточно в таск менеджере взглянуть на кол-во используемой памяти .net прогой и С++ с эквивалентной функциональностью, чтобы понять, что ты гонишь. Кроме того, в С++ принято мелкие объекты создавать на стеке. А если нужно в динамической памяти разместить объект, то есть placement new, который позволяет разместить объект в уже выделенной памяти.
G>3)Выделение памяти таким алгоритмом создет фрагментацию памяти, то есть остаются блоки невыделенной памяти малых размеров.
Угу, только проблема фрагментации кучи есть и в .net, кстати, как и мемори лики.
G>4)Алгоритм выделения памяти не потокобезопасный, требуется синхронизация.
GC тоже не потокобезопасный, и зачастую требует не просто синхронизации, но и приостановки работы потоков.
G>5)В программах на С++ часто делают свои аллокаторы, которые выделяют память для маленькиз объектов чтобы избежать описанно выше оверхеда как по потребляемой памяти, так и по времени этой операции
В С++ такое делают крайне-крайне редко. Потому что а) есть стэк; b) есть placement new; c) на мелких объектах свет клином не сошелся — можно и без них обойтись в критичной к скорости части;
G>Теперь о GC
G>6)Выделение памяти в .NET происходит очень просто — инкремент указателя. Никаких дополнительных операций не происходитю
Ага, аж десять раз. Это как бог на душу положит. Может и GC запуститься с полной остановкой всех потоков. Веселуха...
G>7)Оверхед по выделяемой памяти равен 8 байтам на 32-битной платформе (может ошибаюсь немного, лень смотреть)
уже писал про мелкие объекты и про то, что общий оверхед памяти для .net программ хорошо больше цэпэпэ.
G>8)Кроме того что выделение памяти выполняется моментально, эта операция еще и потокобезопасна.
в c# выделение памяти не более потокобезопасно, чем в с++
G>9)такое распределение памяти увелиичивает cache-locality
стандартные аллокаторы windows тоже оптимизированы на это дело
G>10)GC собирает мусор не тогжа когда захочется, а при нехватке памяти в нулевом поколении
по сути это и означает, что когда захочется, особенно если речь идет о более-менее больших системах
G>11)Для нулевого поколения объем памяти — пара сотен КБ, этот кусок очень хорошо ложится в кеш процессора.
пофиг, в С++ частое выделение/удаление мелких объектов стараются делать на стэке.
G>12)Единственный критический недостаток GC заключается в том, что он очень плохо работает когда кончается свободная физическая память, сборка мусора во втором поколении заставляет поднимать все страницы памяти из свопа и это может нереально тормозить.
G>13)Но даже этот недостаток можно побороть. Можно сборку мусора во втором поколении маскировать под каую-либо длительную операцию (например сохранение или открытие файла), а при расчетах использовать рецепт описанный здесь
это в общем-то самообман
PS
существенное преимущество GC — не надо следить за уничтожением объектов, что облегчает программирование, позволяет использовать всякие инетересные техники программирования, вроде замыканий. Но за эти удобства мы платим скоростью исполнения и большим расходом памяти. Вот собственно и все.