Сегодня я расскажу вам об одной неприятной ситуации, которая связана с JIT в .NET 4.6. Вот несколько фактов:
- Если вы поставили .NET Framework 4.6 Preview, то у вас по дефолту стоит RyuJIT.
- Если вы поставили Visual Studio 2015 CTP, то она включает .NET Framework 4.6 Preview, а значит у вас по дефолту стоит RyuJIT.
- Если вы работаете на Windows 10 Tech Preview, то она также включает.NET Framework 4.6 Preview, а значит у вас по дефолту стоит RyuJIT.
- Если вы хотите вернуть старый JIT, то это можно сделать с помощью ключа регистра, переменной среды или app.config-настройки
useLegacyJit
. - Если включить
useLegacyJit
в Windows 8 или Windows 8.1, то вы получите большое количество проблем, связанных с компиляцией и запуском приложений. - RyuJIT всё ещё не готов к реальному использованию, некоторые программы могут работать некорректно на нём.
Возможно, я просто что-то не понимаю в этой жизни. Давайте разберёмся в ситуации вместе.
Обнаружение проблемы
Не так давно я занимался исследованием особенностей различных версий JIT в .NET. В какой-то момент я обратил внимание на то, что ASM-код для 64-битной версии программы не похож на старый JIT-x64. Вместо этого он был похож на результат работы RyuJIT. Я не очень люблю RyuJIT: в нём нет многих оптимизаций, а код зачастую исполняется дольше. Я проверил ключ регистра HKLMSOFTWAREMicrosoft.NETFrameworkAltJit
и переменную среды COMPLUS_AltJit
: всё было нормально, RyuJIT должен был выключен. Я проверил версию JIT своим любимым способом: действительно, код обрабатывается с помощью RyuJIT. Тогда я полез в гугл и узнал кое-что новое: при установке .NET Framework 4.6 Preview вы совершенно бесплатно получаете RyuJIT в качестве дефолта (на моём компьютере установка произошла вместе с Visual Studio 2015). При этом существует три способа вернуть всё на место:
- Установить переменную среды
COMPLUS_useLegacyJit=1
- Установить ключ регистра
useLegacyJit=1
(REG_WORD
) вHKEY_LOCAL_MACHINESOFTWAREMicrosoft.NETFramework
илиHKEY_CURRENT_USERSOFTWAREMicrosoft.NETFramework
- В
app.config
-файле прописать волшебные строчки:<configuration> <runtime> <useLegacyJit enabled="1"> </runtime> </configuration>
Ок, давайте попробуем. Действительно, способ рабочий, он возвращает старый JIT на место, но вот только при этом портит разные полезные штуки. Несколько примеров:
- Самое воспроизводимое: вызов
GC.Collect()
падает с исключением (чтобы всё было правильно не забудьте поставить Platform Target: x64, Target framework: .NET 4+).
- csc2 плюётся страшными кодами выхода.
- Постоянно вылетающий mscorlib recursive resource lookup bug.
- Многие приложения падают на старте (например, dotMemory, Paint.NET).
- А некоторые кидают исключения во время работы (например, GitExtensions).
В остальном при большом желании работать можно, но постоянные ошибки очень мешают. Пригорюнился я, да и выключил useLegacyJit
. «Будем жить с новым RyuJIT, что поделать-то» — сказал я себе.
Но на этом мои проблемы не закончились. На днях я решил воспользоваться одной из .NET-утилит в проекте на своей работе. А она упала. Падение отлично воспроизводилось. Как порядочный разработчик, я пошёл и завёл баг в JIRA. Вскоре тикет был закрыт со статусом Cannot Reproduce: у всех всё работало, кроме меня. «Хм…» — подумал я. Пошёл в реестр, включил useLegacyJit
, снова запустил утилиту — всё отработало на отличненько. Расследование бага всё ещё ведётся, но факт остаётся фактом: RyuJIT портил нормальную работу нашей замечательной утилитки.
И тогда я разозлился. «Да не так уж мне и нужен этот .NET 4.6» — подумал я. И снёс его вместе с Visual Studio 2015. Проверяю: RyuJIT на месте. Я пошёл в установку и удаление программ и снёс всё, что шло вместе с .NET 4.6. Проверяю: RyuJIT на месте. Тогда я совсем разозлился: начал сносить вообще всё, что в своём названии содержало «Microsoft» или «.NET». Проверяю: RyuJIT на месте. Я пошёл на диск C: и начал руками удалять всё, посвящённое .NET, что вообще удалялось. До конца мне .NET снести не удалось, поэтому я прошёлся по системным настройкам и повыключал все выключаемые Windows-компоненты, связанные с .NET. После всех процедур делаю последнюю проверку: RyuJIT на месте.
Воспроизведение проблемы
Жить на руинах системы было не очень весело, поставить все .NET-штуки заново оказалось затруднительно, поэтому я начал процесс восстановления системы. Откатился к заводским настройкам, поставил всё программы, настроил рабочее окружение и кинулся проверять работу GC.Collect()
: старый добрый Jit-x64 на месте, сборка мусора работает нормально. Я уже предвидел результат, но экспериментатор в моей душе заставил меня поставить Visual Studio 2015 (включая .NET 4.6 и дефолтный RyuJIT). Прогнал все свои тесты, результат не поменялся: моя .NET-утилитка не работала под RyuJIT, а useLegacyJit
кидался ошибками на каждый мой шаг.
«Может у меня Windows какой-то дефектный» — подумал я. Переходим к следующему этапу: я нашёл в интернете образы чистых Windows 8 и Windows 8.1, поставил их под виртуалкой. На новенькие системы мной была установлена Visual Studio 2015 CTP6 Ultimate (минимальная конфигурация, без всяких дополнительных опций). Проводим эксперимент: собираем консольное x64-приложение с единственным вызовом GC.Collect()
, включаем useLegacyJIT
, запускаем и видим, как приложение плюётся исключением прямо нам в лицо.
Далее я поставил под виртуалкой Window 10 Tech Preview. Меня ждало ещё одно открытие: там .NET 4.6 встроенный, RyuJIT сразу идёт по дефолту. Порадовало одно: опция useLegacyJit
работает нормально, никаких исключений не наблюдается. А вот без неё на дефолтном RyuJIT моя .NET утилитка всё ещё продолжает падать (обязательно разберусь в чём же там дело, увы, это не так просто).
Заключение
Мне всё ещё кажется, что я просто что-то не понимаю об этой жизни и что-то не так делаю. Я написал этот пост, т. к. мне кажется, что кто-нибудь ещё также может что-то не понять и наступить на те же грабли. Эксперименты проводились на моём рабочем ноутбуке и под виртуалками: мне этого недостаточно, чтобы объявить, что всё действительно плохо. Буду признателен всем, кто проверит ситуацию у себя на машине и отпишется о результатах. Если кто-то разбирается в ситуации лучше меня, то большая просьба рассказать кто виноват и что делать.
Ссылки
- .NET Framework 4.6 — Testing with RyuJIT
- Microsoft .NET Framework 4.6 Preview (Web Installer)
- Visual Studio 2015 CTPs
Автор: DreamWalker