Я правильно понимаю, что в данном случае нет ничего необычного — глядя в таблицу методов (с рутами), GC видит, что в дальнейшем на переменную никто не ссылается и просто принимает решение о том, что её можно удалить?
Никакой таблицы методов, насколько я помню механизм, у каждого объекта есть свойство, которое показывает строчку (вернее, адрес команды), после которой он может быть безопасно удален. Поэтому например метод GC.KeepAlive выглядит так:
Он ничего не делает, просто перезаписывает адрес безопасного удаления. Единственный нюанс: атрибут NoInlining, иначе компилятор бы его заинлайнил, ну и весь смысл от него тогда пропал бы.
А в остальном да, все верно.
Не могли бы вы поподробнее описать, что здесь имелось в виду и как на самом деле на нижнем уровне происходит вызов (через call или callvirt, по какому адресу осуществляется вызов, какое значение имеет указатель this и как влияет на вызов удаление экземпляра сборщиком мусора).
когда вы пишете
void Foo(int a,int b)
{
c = a+b;
}
компилятор превращает это в такой код:
static void Foo(object this, int a,int b)
{
this.c = a+b;
}
соответственно экземпляр, который вызывает объект, ничем не отличается от остальных ссылочных параметров метода. Поэтому мы его и можем удалить, если видим, что дальше ни одно поле объекта не используется, а на него никто не ссылается.
что касается call и callvirt можно почитать об этом, например, здесь.
Да, про CriticalFinalizerObject забыл, сейчас добавлю.
Нет, это не перепечатка, основная информация из Clr via C# и Pro .Net perfomance. Последняя мне очень понравилась, в еще намного легче читать, чем Рихтера. Хотя, конечно, классику знать надо.
И зачем? Поражают такие люди, особенно на всяких программерских форумах для новичков, когда велосипедят linq-код просто для того, чтобы показать, что они его знают. На обычных циклах даже текста меньше займет, не говоря про понятливость/производительность. А уж про подводные камни наподобие упаковки для значимых типов вообще не хочется вспоминать. Тогда уж: IEnumerable<T> Infinite<T>() {while(true) yield return default(T);}
А еще можно извратиться и написать лямбду вместо этого метода, гулять так гулять, сэкономим одну строчку и уедем за край экрана!
Я не спорю, что человека, который не закрывает ресурсы юзингом, нужно гнать в шею. Но упомянуть про обязательное освобождение в деструкторе, как последнем рубеже обороны, в статье про IDisposable считаю необходимым.
Это значимый тип данных. Значение переменной valType будет храниться в стэке.
Дальше можно не читать.
100 раз говорили: значимые типы почти всегда размещаются в куче, т.к. являются полями классов. А на стеке размещаются только локальные переменные значимого типа. А ссылочные типы далеко не всегда размещаются в куче, см. stackalloc. Поэтому это утверждение не просто ошибочное, а в корне неверное.
Эрик давно уже статью целую наваял: если бы это было так, были бы «стековые» и «кучевые» типы данных (stack and heap values), а не «значимые» и «ссылочные».
Странно, что он так взъелся на JIT и GC. Тот же Рихтер имеет противоположенное мнение: что у JIT'а больше вариантов для оптимизаций, а GC зачастую эффективнее, чем ручное управление памятью, так как выделение является крайне дешевой операцией по сравнению с С/С++. Существует проблема фрагментации, с этим не спорю, но при грамотном построении приложения получается, что GC даже быстрее, как раз из-за описанных преимуществ. Не говоря о том, что на хабре полгода назад была статья, что будет новый JIT, кстати, вот она.
Хотя в целом С++ побыстрее, конечно, будет, да и грамотно работают с памятью далеко не все. Но это скорее не проблемы языка, а проблемы программистов, которым проще засунуть планку на 4гб, чем оптимизировать приложение. В целом по моим ощущениям C# медленнее С++ в среднем на величину около 30%, а иногда даже быстрее. Зато плюшек у него ощутимо больше. Хотя, конечно, могли бы JIT сделать поумнее: например, компилировать быстро без оптимизаций, а затем собирая статистику, перекомпилировать «hot path» в приложении. В конце концов, раз уж ругают «монструозный .Net», жрущий кучу ресурсов и памяти, пусть хоть с толком ими распоряжается.
Ничего страшного, видимо, привычка у меня пошла еще со времен учебников, где половина формул приводится со словами «очевидно» и «доказательство оставим для тренировки читателю».
Решение может приведу, ближе к выходным. Пока рабочая неделя, нет сил на детализированное доказательство, а концепцию я расписал выше.
При чем тут целочисленные ограничения? Еще раз:
1) СНИМАЕМ ограничение на целочисленность
2) Решаем любым способом на континууме возможных значений оптимальную точку, можно методами нелинейного программирования: Розенброк вполне подходит, если доп. ограничения какие-то — стандартно введем штрафную/барьерную функцию. Получим точку. Можем повторить несколько раз для различных точек, чтобы быть уверенными, что мы попали в глобальный экстремум.
3) Очевидно, что оптимальное целочисленное решение будет находится в окрестности нецелого решения. Т.О. расширяем область до тех пор, пока на окружности не будет лежать хотя бы одной точки с целочисленными координатами.
4) записываем ответ.
У меня был пример одной из лабораторок, где решалась эта задача, но к сожалению за давностью не смог найти.
Может я чего-то не понимаю, но по-моему эта задача похожим образом решается во всяких методичках, достаточно просто принять, что мы перевозим не товары, а партии, а затем снимая ограничение на целочисленность находим тем же МВГ допустимое решение, а потом ищем ближайшее к нему целочисленное.
Ясно. То есть по вашей логике если физика, математика и пр вышли в далекие времена из философии, то они на нее опираются? И непонятно, зачем капающая лопата сдалась…
А ваше высказывание про «физическую логику» заставляет меня потерять всякий интерес к беседе, т.к. очевидно, что вы не разбираетесь в вопросе. Всего доброго.
Ну, в случае с IList, это не баг, это фича. Имеются соответствующие свойства, которые показывают, какой перед нами IList: read-only, fixed-size или variable-size.
угу, зачем нам вообще ООП, давайте везде пихать if else switch case. Смысл как раз и в том, чтобы приводить к нужному интерфейсу, а дальше с ним работать. Что мешает имплементировать нужные интерфейсы в любом количестве, как это сейчас и бывает?
У нас есть T[], т.е. массив, но он не интерфейс.
Не уловил смысла в этой фразе. T[] от int[] не отличается ничем, кроме того, что тип T нам пока неизвестен.
Думаю, причина примерно та же, почему мы не можем считать одинаковыми классы:
class Foo { public int _foo; }
class Bar { public int _bar; }
в этом есть определенный смысл, но от такого каста пользы было бы много.
Никакой таблицы методов, насколько я помню механизм, у каждого объекта есть свойство, которое показывает строчку (вернее, адрес команды), после которой он может быть безопасно удален. Поэтому например метод GC.KeepAlive выглядит так:
Он ничего не делает, просто перезаписывает адрес безопасного удаления. Единственный нюанс: атрибут NoInlining, иначе компилятор бы его заинлайнил, ну и весь смысл от него тогда пропал бы.
А в остальном да, все верно.
когда вы пишете
компилятор превращает это в такой код:
соответственно экземпляр, который вызывает объект, ничем не отличается от остальных ссылочных параметров метода. Поэтому мы его и можем удалить, если видим, что дальше ни одно поле объекта не используется, а на него никто не ссылается.
что касается call и callvirt можно почитать об этом, например, здесь.
Нет, это не перепечатка, основная информация из Clr via C# и Pro .Net perfomance. Последняя мне очень понравилась, в еще намного легче читать, чем Рихтера. Хотя, конечно, классику знать надо.
И зачем? Поражают такие люди, особенно на всяких программерских форумах для новичков, когда велосипедят linq-код просто для того, чтобы показать, что они его знают. На обычных циклах даже текста меньше займет, не говоря про понятливость/производительность. А уж про подводные камни наподобие упаковки для значимых типов вообще не хочется вспоминать. Тогда уж:
IEnumerable<T> Infinite<T>() {while(true) yield return default(T);}
А еще можно извратиться и написать лямбду вместо этого метода, гулять так гулять, сэкономим одну строчку и уедем за край экрана!
И да, речь действительно не об этом, поэтому я и на предыдущий комментарий не ответил. Засим завершим беседу.
Дальше можно не читать.
100 раз говорили: значимые типы почти всегда размещаются в куче, т.к. являются полями классов. А на стеке размещаются только локальные переменные значимого типа. А ссылочные типы далеко не всегда размещаются в куче, см. stackalloc. Поэтому это утверждение не просто ошибочное, а в корне неверное.
Эрик давно уже статью целую наваял: если бы это было так, были бы «стековые» и «кучевые» типы данных (stack and heap values), а не «значимые» и «ссылочные».
Советую ознакомиться:
blogs.msdn.com/b/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx
blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx
Хотя в целом С++ побыстрее, конечно, будет, да и грамотно работают с памятью далеко не все. Но это скорее не проблемы языка, а проблемы программистов, которым проще засунуть планку на 4гб, чем оптимизировать приложение. В целом по моим ощущениям C# медленнее С++ в среднем на величину около 30%, а иногда даже быстрее. Зато плюшек у него ощутимо больше. Хотя, конечно, могли бы JIT сделать поумнее: например, компилировать быстро без оптимизаций, а затем собирая статистику, перекомпилировать «hot path» в приложении. В конце концов, раз уж ругают «монструозный .Net», жрущий кучу ресурсов и памяти, пусть хоть с толком ими распоряжается.
Решение может приведу, ближе к выходным. Пока рабочая неделя, нет сил на детализированное доказательство, а концепцию я расписал выше.
1) СНИМАЕМ ограничение на целочисленность
2) Решаем любым способом на континууме возможных значений оптимальную точку, можно методами нелинейного программирования: Розенброк вполне подходит, если доп. ограничения какие-то — стандартно введем штрафную/барьерную функцию. Получим точку. Можем повторить несколько раз для различных точек, чтобы быть уверенными, что мы попали в глобальный экстремум.
3) Очевидно, что оптимальное целочисленное решение будет находится в окрестности нецелого решения. Т.О. расширяем область до тех пор, пока на окружности не будет лежать хотя бы одной точки с целочисленными координатами.
4) записываем ответ.
У меня был пример одной из лабораторок, где решалась эта задача, но к сожалению за давностью не смог найти.
А ваше высказывание про «физическую логику» заставляет меня потерять всякий интерес к беседе, т.к. очевидно, что вы не разбираетесь в вопросе. Всего доброго.
угу, зачем нам вообще ООП, давайте везде пихать if else switch case. Смысл как раз и в том, чтобы приводить к нужному интерфейсу, а дальше с ним работать. Что мешает имплементировать нужные интерфейсы в любом количестве, как это сейчас и бывает?
Не уловил смысла в этой фразе. T[] от int[] не отличается ничем, кроме того, что тип T нам пока неизвестен.
в этом есть определенный смысл, но от такого каста пользы было бы много.
то есть считать делегаты с одной сигнатурой — алиасами одного и того же делегата. Но нет, это ведь так сложно…
Я уже молчу про IList. Как один умный человек, насколько лучше было бы изначально сделать такие интерфейсы:
а не наследовать массивы от IList(T), а потом бросать исключения при вызове половины методов…