В нашей компании есть несколько собственных языков и компиляторов / декомпиляторов, так что мы активно используем возможности генерации и отражения кода C# dynamici c. Большая часть кода, который мы пишем, предназначена просто для динамического создания кода во время выполнения или на нашем проприетарном языке сценариев.
В определенный момент мы обнаружили, что добавление типов в динамическую c сборку масштабируется O (n ^ 2), предположительно потому, что вся сборка переписана? Это означало, что когда было создано более 10 000 типов, наше программное обеспечение стало работать медленнее. Затем мы сохранили одну динамическую сборку и модуль c и добавляли в нее типы всякий раз, когда мы генерировали динамический код c (с некоторыми исключениями для DynamicMethods).
Чтобы перенести это, мы сделал градиентный спуск на оптимальной комбинации типов, модулей и сборок для динамического генерирования, чтобы мы могли получить производительность O (n).
![graph of assembly loading optimisations](https://i.stack.imgur.com/llQCL.png)
Мы реализовал слой абстракции, который автоматически создавал модули / сборки по мере необходимости в соответствии с оптимальным способом, показанным результатами, для случаев, когда мы создаем динамические типы c. Это имело желаемые преимущества в производительности. Эти отражающие сборки / модули / типы объектов всегда имеют сильную ссылку, скрытно скрытую за ними в различных словарях и хэш-наборах.
Однако через несколько месяцев начались очень странные проблемы встречающаяся. Первый был чрезвычайно прерывистым и происходил каждые 3/4 пробега. Это было роковое исключение ExecutionEngineException с кодом ошибки 80131506. Это очень сбивало с толку, и мы изначально думали, что это вызвано обновлением с. NET 451 до. NET 472 (у нас очень иногда возникала эта проблема раньше) , В конце концов мы отследили это до установки System.Reflection.Emit.AssemblyBuilderAccess в RunAndCollect. Когда мы изменили это на «Run», у нас больше никогда не возникало этой проблемы. Имейте в виду, что мы всегда держим сильные ссылки на эти сборки, поэтому очень сложно понять, как это может решить проблему.
Проблемы не прекратились здесь, однако, когда мы спустились дальше в пропасть. NET мы обнаружили невыразимые ужасы. Оказалось, что использование памяти нашими программами утроилось, и многие из них терпели неудачу из-за нехватки памяти. Ограничивая «силу» наших программ (параметр, который уменьшает использование памяти), наши программы будут работать, но это займет в три раза больше времени.
При таком поведении кажется, что мы производим слишком много динамических c сборок, и их нужно собирать, иначе у нас не хватит памяти. Однако мне трудно поверить, что динамические c сборки могут занимать много места, и я не понимаю, как их можно собрать, учитывая, что мы поддерживаем объекты, которые являются экземплярами типов в динамических c сборках (которые предположительно содержат ссылку на объекты Type) и прямые прямые ссылки на объекты отражения этих динамических c сборок в любом случае.
Поэтому мой вопрос состоит в том, как именно работает сборка динамических c сборок. NET framework, и почему мы получаем эти исключения FatalExecutionEngine Exception / OutOfMemory?
![enter image description here](https://i.stack.imgur.com/W6Mhk.png)