Руководство по net framework

Search code, repositories, users, issues, pull requests…

Provide feedback

Saved searches

Use saved searches to filter your results more quickly

Sign up

Время на прочтение
27 мин

Количество просмотров 56K

Вниманию читателей «Хабрахабра» представляется перевод статьи Хану Коммалапати и Тома Кристиана об внутреннем устройстве .NET. Существует альтернативный вариант перевода на сайте Microsoft.

В статье рассматривается:

  • Системный домен (SystemDomain), Домен общего доступа (SharedDomain) и домен по умолчанию (DefaultDomain)
  • Представление объекта и другие особенности организации памяти
  • Представление таблицы методов
  • Распределение методов

Используемые технологии: .NET Framework, C#

Содержание

  1. Домены создаваемые начальным загрузчиком
  2. Системный домен
  3. Домен общего доступа (разделяемый)
  4. Дефолтный домен
  5. Загрузчик куч
  6. Основы типов
  7. Экземпляр объекта
  8. Таблица методов
  9. Размер базового экземпляра
  10. Таблица слотов метода
  11. Описатель метода
  12. Карта таблиц виртуальных методов интерфейсов и карта интерфейса
  13. Виртуальное распределение
  14. Статические переменные
  15. EEClass
  16. Заключение

Общая среда исполнения (CLR) становится (или уже стала) главной инфраструктурой для построения приложений в Windows, поэтому наличие глубокого понимания его внутреннего устройства поможет создавать эффективные приложения промышленного класса.

В этой статье мы будем исследовать внутреннее устройство CLR, включая макет экземпляра объекта, макет таблицы методов, распределение методов, интерфейсное распределение, а также различные структуры данных.

Мы будем использовать очень простые фрагменты С# кода, любое неявное использование синтаксиса языка программирования подразумевает С#. Некоторые обсуждаемые структуры данных и алгоритмы будут изменены в следующих версиях среды Microsoft® .NET Framework, но концептуальные основы останутся прежними. Будем использовать отладчик Visual Studio® .NET 2003 и расширение для отладки Son of Strike (SOS) чтобы просматривать структуры данных обсуждаемые в статье. SOS загружает внутренние данные CLR, и позволяет просматривать, сохранять интересующую информацию. Посмотрите процедуру загрузки SOS.dll в процесс отладчика в соответствующих источниках.
See the «Son of Strike» sidebar for loading SOS.dll into the Visual Studio .NET 2003 debugger process.

В статье мы будем описывать классы соответствующие реализациям в Shared Source CLI (SSCLI).

Таблица на рисунке 1 поможет в исследовании мегабайтов кода в SSCLI во время поиска необходимых структур.

Рисунок 1 SSCLI Ссылки

Компонент SSCLI Путь
AppDomain /sscli/clr/src/vm/appdomain.hpp
AppDomainStringLiteralMap /sscli/clr/src/vm/stringliteralmap.h
BaseDomain /sscli/clr/src/vm/appdomain.hpp
ClassLoader /sscli/clr/src/vm/clsload.hpp
EEClass /sscli/clr/src/vm/class.h
FieldDescs /sscli/clr/src/vm/field.h
GCHeap /sscli/clr/src/vm/gc.h
GlobalStringLiteralMap /sscli/clr/src/vm/stringliteralmap.h
HandleTable /sscli/clr/src/vm/handletable.h
InterfaceVTableMapMgr /sscli/clr/src/vm/appdomain.hpp
Large Object Heap /sscli/clr/src/vm/gc.h
LayoutKind /sscli/clr/src/bcl/system/runtime/interopservices/layoutkind.cs
LoaderHeaps /sscli/clr/src/inc/utilcode.h
MethodDescs /sscli/clr/src/vm/method.hpp
MethodTables /sscli/clr/src/vm/class.h
OBJECTREF /sscli/clr/src/vm/typehandle.h
SecurityContext /sscli/clr/src/vm/security.h
SecurityDescriptor /sscli/clr/src/vm/security.h
SharedDomain /sscli/clr/src/vm/appdomain.hpp
StructLayoutAttribute /sscli/clr/src/bcl/system/runtime/interopservices/attributes.cs
SyncTableEntry /sscli/clr/src/vm/syncblk.h
System namespace /sscli/clr/src/bcl/system
SystemDomain /sscli/clr/src/vm/appdomain.hpp
TypeHandle /sscli/clr/src/vm/typehandle.h

Момент на который стоит обратить внимание перед тем как мы пойдём дальше – информация предоставленная в этой статье действительна, только для .NET Framework 1.1 (также в основном это соответствует Shared Source CLI 1.0, с учётом ряда заметных исключений, присутствующих в различных сценариях взаимодействия) при исполнении на платформе x86. Информация изменена в следующих версиях .NET Framework, поэтому пожалуйста не занимайтесь сборкой ваших приложений с абсолютными ссылками на эти внутренние структуры.

Домены, создаваемые загрузчиком CLR

Перед тем, как запустить первую строчку управляемого кода, создаётся три домена приложения. Два из них не доступны в управляемом коде и даже не видимы для хоста CLR. Они могут быть только созданы только при загрузке CLR обеспечиваемой шиной mscoree.dll и mscorwks.dll (или mscorsvr.dll для мультипроцессорных систем). Как вы можете видеть на рисунке 2, это системный домен и разделяемый (общий) домен, они могут существовать только в одном экземпляре. Третий домен – дефолтный, только экземпляр этого домена приложения имеет наименование. Для простого хоста CLR, такого как консольное приложение, имя дефолтного домена приложений содержит имя исполняемого образа. Дополнительные домены, могут быть созданы из управляемого кода методом AppDomain.CreateDomain или из хоста неуправляемого кода используя интерфейс ICORRuntimeHost.

Сложные хосты, такие как ASP.NET создают необходимое количество доменов, в соответствии с количеством приложений, работающих в обслуживаемом Web сайте.


Рисунок 2. Домены созданные загрузчиком CLR

Системный домен

Системный домен создаёт и инициализирует домен общего доступа (SharedDomain) и домен по умолчанию (Default). Он же выполняет загрузку системной библиотеки mscorlib.dll в домен общего доступа.

Также системный домен содержит доступные в границах процесса строковые константы, интернированные явно или не явно.

Интернирование строк это функционал оптимизации, немного тоталитарный в среде .NET Framework 1.1, так как CLR не даёт возможности сборкам оптимизировать этот фунционал. При этом, память используется для хранения только одного экземпляра строки для всех строковых литералов во всех доменах приложения.

Системный домен также служит для генерации идентификаторов интерфейсов в границах процесса, которые используются при создании карты интерфейсов (InterfaceVtableMaps) в каждом домене приложений (AppDomain).

Системный домен прослеживает все домены в процессе и предоставляет функциональность загрузки и выгрузки доменов приложений.

Домен общего доступа (SharedDomain)

Весь доменно-нейтральный код загружается в домен общего доступа. Mscorlib, системная библиотека, необходима для кода пользователя во всех доменах приложений(AppDomains). Эта бибилиотека автоматически загружается в домен общего доступа. Базовые типы из пространства имён System, такие как Object, ValueType, Array, Enum, String и Delegate загружаются предварительно в этот домен в процессе загрузки CLR загрузчиком. Код пользователя может также быть загружен в этот домен, с помощью установки атрибутов LoaderOptimization приложением хоcтом CLR во время вызова CorBindToRuntimeEx. Консольное приложение может загружать код в домен общего доступа при добавлении атрибута System.LoaderOptimizationAttribute к методу Main приложения. Домен общего доступа также управляет картой сборок, индексированной относительно базового адреса, карта действует как таблица справочник для управления общими зависимостями сборок, загруженных в дефолтный домен и другие домены приложений, созданные в управляемом коде. Дефолтный домен служит только для загрузки частного кода пользователя, который не должен быть доступен другим приложениям.

Дефолтный домен

Дефолтный домен это экземпляр домена приложения, где как правило выполняется код приложения. В то время как некоторые приложения требуют, чтобы дополнительные домены приложения были созданы во время выполнения (такие, что имеют архитектуру плагинов или приложения выполняющие генерацию значительного количества кода во время выполнения), большинство приложений создают один домен во время их выполнения. Весь код, выполняемый в этом домене, контекстно ограничен на уровне домена. Если в приложении создано несколько доменов приложения, любой кросс-доменный доступ будет происходить через прокси .NET Remoting. Дополнительные внутри-доменные границы могут быть созданы используя типы наследованные от System.ContextBoundObject.

Каждый домен приложений имеет свои собственные SecurityDescriptor, SecurityContext и DefaultContext, также как собственный загрузчик куч (High-Frequency Heap, Low-Frequency Heap, and Stub Heap),
Таблицы описателей (Handle Table, Large Object Heap Handle Table), Менеджер карты интерейсов Vtable и кэш сборок.

Кучи загрузчика

Кучи загрузчика(LoaderHeaps) предназначены для загрузки различных артефактов времени выполнения CLR и артефактов оптимизации, существующих в течение всего времени существования домена. Эти кучи увеличиваются на предсказуемые фрагменты для минимизации фрагментации. Кучи загрузчика отличаются от кучи сборщика мусора (GC) (или набора куч в случае симметричных мультипроцессоров SMP) в том, что куча сборщика мусора содержит экземпляры объектов, а кучи загрузчика содержат системные типы. Часто запрашиваемые структуры, такие как таблицы методов, описатели методов (MethodDescs), описатели полей (FieldDescs) и карта интерфейсов располагаются в куче частого доступа (HighFrequencyHeap). Структуры, к которым обращения более редки, такие как EEClass и загрузчик классов(ClassLoader), а также их служебные таблицы, располагаются в куче с низкой частотой обращений (LowFrequencyHeap). Служебная куча (StubHeap) содержит блоки, обеспечивающие поддержку безопасности доступа в коде code access security (CAS), оболочку COM вызовов и вызовов P/Invoke. Рассмотрев домены и кучи загрузчики на высоком уровне, теперь посмотрим на их физическую организацию более пристально в контексте простого приложения на рисунке 3. Остановим выполнение программы на «mc.Method1();» и создадим дамп домена с помощью расширенной команды DumpDomain отладчика SOS. Ниже представлен результат:

!DumpDomain
System Domain: 793e9d58, LowFrequencyHeap: 793e9dbc, 
HighFrequencyHeap: 793e9e14, StubHeap: 793e9e6c,
Assembly: 0015aa68 [mscorlib], ClassLoader: 0015ab40
</br>
Shared Domain: 793eb278, LowFrequencyHeap: 793eb2dc,
HighFrequencyHeap: 793eb334, StubHeap: 793eb38c,
Assembly: 0015aa68 [mscorlib], ClassLoader: 0015ab40
</br>
Domain 1: 149100, LowFrequencyHeap: 00149164,
HighFrequencyHeap: 001491bc, StubHeap: 00149214,
Name: Sample1.exe, Assembly: 00164938 [Sample1],
ClassLoader: 00164a78

Рисунок 3 Sample1.exe

using System;

public interface MyInterface1
{
    void Method1();
    void Method2();
}
public interface MyInterface2
{
    void Method2();
    void Method3();
}

class MyClass : MyInterface1, MyInterface2
{
    public static string str = "MyString";
    public static uint   ui = 0xAAAAAAAA;
    public void Method1() { Console.WriteLine("Method1"); }
    public void Method2() { Console.WriteLine("Method2"); }
    public virtual void Method3() { Console.WriteLine("Method3"); }
}

class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        MyInterface1 mi1 = mc;
        MyInterface2 mi2 = mc;

        int i = MyClass.str.Length;
        uint j = MyClass.ui;

        mc.Method1();
        mi1.Method1();
        mi1.Method2();
        mi2.Method2();
        mi2.Method3();
        mc.Method3();
    }
}

Наше консольное приложение, Sample1.exe, загружено в домен приложения (AppDomain), который имеет имя «Sample1.exe”. Mscorlib.dll загружен в домен общего доступа (SharedDomain), но также фигурирует в системном домене (SystemDomain), как системная библиотека ядра. Куча высокочастотного доступа (HighFrequencyHeap), низкочастотного доступа (LowFrequencyHeap) и stub-куча(StubHeap) располагаются в каждом домене. Системный домен и домен общего доступа используют один и тот же загрузчик классов (ClassLoader), в то время как дефолтный домен (Default AppDomain) использует свой собственный.

Результат команды не отображает зарезервированный и используемый размер куч загрузчика. Куча высокочастотного доступа первоначально резервирует 32Кб и использует 4Кб.

Куча низкочастотного доступа stub кучи первоначально резервируют 8Кб и занимают 4Кб.

Также не показана куча карты интерфейсов (InterfaceVtableMap, далее IVMap) Каждый домен обладает картой интерфейсов, которая создаётся на своей собственной куче загрузчика во время фазы инициализации домена. Куча карты интерфейсов (IVMap) резервирует 4Кб и занимает 4Кб первоначально. Мы обсудим значимость карты интерфейсов, когда будем исследовать макет типа (type layout) в последующих секциях.

На Рисуноке 2 показаны куча дефолтного процесса (default Process Heap), куча компилятора времени выполнения (JIT Code), куча сборщика мусора (GC) для маленьких объектов (SOH) и куча больших объектов (LOH) (для объектов с размером 85000 байт или более) чтобы проиллюстрировать семантическое различие между ними и кучами загрузчика. JIT-компилятор или компилятор времени выполнения генерирует инструкции для архитектуры x86 и сохраняет их в куче для JIT кода. Куча сборщика мусора и куча больших объектов являются кучами, которые обрабатываются сборщиком мусора, на этих кучах создаются управляемые объекты.

Основы типов

Тип является фундаментальным элементом программирования в .NET. В C# тип может быть объявлен с помощью следующих ключевых слов: class, struct и interface. Большинство типов создаются самим программистом явно, однако, в особых случаях взаимодействия и в сценариях вызовов удалённых объектов (.NET Remoting), .NET CLR генерирует типы неявно. Эти генерируемые типы включают COM и вызываемые обертки времени выполнения (Runtime Callable Wrappers) и сквозные прокси (Transparent Proxies).

Мы исследуем .NET фундаментальные типы, начиная со структуры стека что содержит ссылки на объект (как правило, стек – одно из мест, с которых экземпляр объекта начинает своё сущесвование).
Код приведённый на Рисунке 4 содержит простую программу с консольной точкой входа, где вызывается статический метод.

Метод Method1создаёт экземпляр типа SmallClass, который содержит массив байт используемый для демонстрации создания экземпляра объекта в куче больших объектов LOH. Код тривиален, но будет задействован в нашем обсуждении.

Рисунок 4 Большие и маленькие объекты

using System;

class SmallClass
{
    private byte[] _largeObj;
    public SmallClass(int size)
    {
        _largeObj = new byte[size];
        _largeObj[0] = 0xAA;
        _largeObj[1] = 0xBB;
        _largeObj[2] = 0xCC;
    }

    public byte[] LargeObj
    {
        get { return this._largeObj; }
    }
}

class SimpleProgram
{
    static void Main(string[] args)
    {
        SmallClass smallObj = SimpleProgram.Create(84930,10,15,20,25);
        return;
    }

    static SmallClass Create(int size1, int size2, int size3, 
        int size4, int size5)
    {
        int objSize = size1 + size2 + size3 + size4 + size5;
        SmallClass smallObj = new SmallClass(objSize);
        return smallObj;
    }
}

Рисунок 5 показывает снимок типичного стека вызовов fastcall остановленного в точке останова на строке „return smallObj;“ в методе Create. (Fastcall — .NET конвенция вызовов которая определяет, что аргументы передаются в функции в регистрах, когда это возможно, с остальными аргументами передаваемыми через стек справа на лево и затем извлекаемыми из стека вызываемой функцией
Локальная переменная значимого типа или типа-значения objSize размещена прямо в стеке. Переменные ссылочного типа, такие как smallObj хранятся с фиксированным занимаемым размером (4 битовое двойное слово DWORD) в стеке и содержат адрес экземпляров объектов размещённых в обычной куче сборщика мусора.

В традиционном C++, this – это указатель на объект; в управляемом мире программирования this — это референс или ссылка на объект (object reference). Тем не менее, она содержит адрес экземпляра объекта. Мы будем использовать термин экземпляр объекта (ObjectInstance) для структуры данных расположенной на адресе указанном в ссылке на объект.


Рисунок 5. SimpleProgram стек и кучи

Экземпляр объекта smallObj на обычной куче сборщика мусора содержит Byte[] указывающий на _largeObj, чей размер 85000 байт ( заметьте, что рисунок показывает 85016 байт, что является действительным размером занимаемой области). CLR обращается с объектами размером более чем или равному 85000 байт по другому, в отличие от объектов меньшего. Большие объекты располагаются в куче больших объектов (LOH), в то время как маленькте объекты создаются а обычной куче сборщика мусора, которая оптимизирует размещение объектов и сбор мусора. LOH не сжимается, при этом обычная куча сжимается при каждом сборе мусора. Более того LOH очищается только при полном сборе мусора.

Экземпляр smallObj содержит описатель типа указывающий на таблицу методов (MethodTable) соответствующего типа. Будет присутствовать по одной таблице методов для каждого объявленного и все экземпляры объектов одного и того же типа будут указывать на одну и ту же таблицу методов. Также описатель будет содержать информацию о разновидности типа (интерфейс, абстрактный класс, конкретный класс, обёртка COM, прокси), число реализованных интерфейсов, карту интерфейсов для распределения методов, число слотов в таблице методов и таблицу слотов указывающих на реализацию.

Одна важная структура данных указывает на EEClass. Загрузчик классов CLR создаёт EEClass из метаданных до того как формируется таблица методов. На Рисунке 4, таблица методов SmallClass указывает на его EEClass. Эти структуры указывают на их модули и сборки. Таблица методов и EEClass как правило располагаются в домен-спeцифичных кучах загрузчика. Byte[] — это особый случай; Таблица методов и EEClass располагаются в кучах загрузчика домена общего доступа. Кучи загрузчика относятся к определённому домену (домен-специфичны) и любые структуры данных, упомянутые ранее, однажды загруженные, никуда не денутся пока домен не будет выгружен. Также, дефолтный домен не может быть выгружен и следовательно код существует пока не будет остановлен CLR.

Экземпляр объекта

Как мы заметили, все экземпляры типов-значений либо встраиваются в стек потока или встраиваются в кучу сборщика мусора. Все ссылочные типы создаются на куче сборщика мусора или куче больших объектов(LOH). Рисунок 6 показывает типичный макет экземпляра объекта. На объект может ссылаться локальная переменная, созданная на стеке, таблиц описателей в ситуациях внешнего взаимодействия и P/Invoke сценариях, из регистров (это может быть this-указать и аргументы метода в течении выполнения метода) или из очереди завершителя(finalizer) для объектов имеющих завершающие методы(finalizer methods). OBJECTREF не указывает на начало экземпляра объекта, а указывает со смещением в 4 байта(DWORD) от начала. DWORD называется заголовком объекта и содержит индекс (номер синхронизирующего блока synblk, начинающийся с единицы) в таблице SyncTableEntry. Так как распределение происходит через индекс, CLR может переместить таблицу в памяти когда необходимо увеличение размера. The SyncTableEntry обслуживает мягкие ссылки обратно к объекту, так что владение блоком синхронизации может быть прослежено CLR. Мягкие ссылки позволяют сборщику мусора выполнять очистку, когда уже не существуют другие жёсткие ссылки. SyncTableEntry также хранит указатель на SyncBlock содержащий полезную информацию, но менее необходимую для всех экземпляров объекта. Эта информация включает блокировки объекта, его хеш-код, любые данные преобразования и индекс домена (AppDomainIndex). Для большинства экземпляров объектов, не будет существовать пространства выделенного для блока синхронизации (SyncBlock) и номер syncblock будет равен нулю. Это изменится когда выполняемый поток наткнется на выражение lock(obj) или obj.GetHashCode, как показано ниже:

SmallClass obj = new SmallClass() // Do some work here 
lock(obj) { /* Do some synchronized work here */ } 
obj.GetHashCode();


Рисунок 6. Представление экземпляра объекта

В этом коде, smallObj будет использовать ноль (нет syncblk) в качестве его номера в таблице блоков синхронизации (Syncblk Entry Table). Инструкция lock заставляет CLR создать syncblock запись и записать в заголовок соответствующий номер. Поскольку ключевое слово lock в С# развертывается в блок try-catch с использованием класса Monitor, объект Monitor создаётся в SyncBlock для синхронизации. Вызов метода GetHashCode() заполняет поле Hashcode хэш-кодом объекта в SyncBlock.

SyncBlock содержит другие поля используемые в взаимодействии с COM и маршалинге делегатов в неуправляемый код, но не относящиеся к типичному использованию объектов.

Хендлер типа (TypeHandle) следуют за номером syncblk в экземпляре объекта. В порядке поддержания непрерывности рассуждения, я буду обсуждать хендлер типа после разъяснения экземпляров переменных. Переменный список полей экземпляра следует за хендлером типа. По умолчанию, поля экземпляра размещаются таким образом, что бы использование памяти было эффективным и пропуски при выравнивании были минимальны. Код на Рисунке 7 содержит простой класс SimpleClass имеющий набор переменных экземпляра содержащихся в нём, с различными размерами.

Рисунок 7 SimpleClass с экземплярными переменными

class SimpleClass
{
    private byte b1 = 1;                // 1 byte
    private byte b2 = 2;                // 1 byte
    private byte b3 = 3;                // 1 byte
    private byte b4 = 4;                // 1 byte
    private char c1 = 'A';              // 2 bytes
    private char c2 = 'B';              // 2 bytes
    private short s1 = 11;              // 2 bytes
    private short s2 = 12;              // 2 bytes
    private int i1 = 21;                // 4 bytes
    private long l1 = 31;               // 8 bytes
    private string str = "MyString"; // 4 bytes (only OBJECTREF)

    //Total instance variable size = 28 bytes 

    static void Main()
    {
        SimpleClass simpleObj = new SimpleClass();
        return;
    }
}

Рисунок 8 содержит пример экземпляра объекта SimpleClass отображаемый в окне памяти отладчика Visual Studio. Мы установили точку останова на операторе return рисунок 7 и использовали адрес simpleObj содержащийся в регистре ECX чтобы отобразить экземпляр объекта в окне просмотра памяти. Первый 4-х байтовый блок это номер syncblk. Мы не используем экземпляр в любом коде требующем синхронизации (и не обращаемся к методу HashCode), поэтому это поле установлено в 0. Ссылка на объект сохранена в переменной стека, указывает на 4 байта, расположенные со смещением 4. Байтовые переменные b1, b2, b3 и b4 располагаются бок о бок друг с другом. Байтовые переменные b1, b2, b3, и b4 все размещены подряд, рядом друг к другом. Обе переменные типа short s1 и s2 также размещены рядом. Строковая переменная str это 4-х байтовый ODJECTREF указывающий на актуальный экземпляр строки расположенный в куче сборщика мусора. Строка (String) специальный тип, все экземпляры содержащие одинаковый текст будут указывать на один и тот же экземпляр в глобальной таблице строк – это выполняется в процессе загрузки сборки. Этот процесс называется интернированием строк и спроектирован для оптимизации использования памяти. Как мы заметили ранее в .NET Framework 1.1 сборка не может отключить процесс интернирования, возможно в будущих версиях среды исполнения CLR будет предоставлена такая возможность.


Рисунок 8. Отладочное окно отображающее экземпляр объекта в памяти

Таким образом лексическая последовательность членов переменных в исходном коде не поддерживается в памяти по умолчанию. В сценариях внешнего взаимодействия, где лексическая последовательность должна быть перенесена в память, атрибут StructLayoutAttribute может быть использован, который принимает значение перечисления LayoutKind в качестве аргумента. LayoutKind.Sequential будет обеспечивать лексическую последовательность для маршализированных данных. В .NET Framework это не повлияет на управляемый макет (в версии .NET Framework 2.0 применение атрибута будет иметь эффект). В сценариях внешних взаимодействий где вам на самом деле необходимо иметь дополнительное смещение и явный контроль над последовательностью полей, LayoutKind.Explicit может быть использован совместно с атрибутом FieldOffset на уровне поля. Взглянув на непосредственное содержимое памяти, давайте воспользуемся отладчиком SOS чтобы посмотреть нва содержимое экземпляра объекта. Одна полезная команда это DumpHeap, которая позволяет выводить всё содержимое кучи и все экземпляры определённого типа. Вместо использования регистров, DumpHeap может показать адрес только что созданного нами объекта:

!DumpHeap -type SimpleClass
Loaded Son of Strike data table version 5 from 
"C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/mscorwks.dll"
 Address       MT     Size
00a8197c 00955124       36
Last good object: 00a819a0
total 1 objects
Statistics:
      MT    Count TotalSize Class Name
  955124        1        36 SimpleClass

Общий размер объекта 36 байт. Не имеет значения, на сколько велика строка, экземпляры SimpleClass содержат только DWORD OBJECTREF. Переменные экземпляра SimpleClass занимают только 28 байт. Оставшиеся 8 байтов включают хендлер типа TypeHandle (4 байта) и номер блока синхронизации syncblk (4 байта). Получив адрес экземпляра simpleObj, давайте снимем дамп содержимого этого экземпляра использую команду DumpObj, как показано здесь:

!DumpObj 0x00a8197c
Name: SimpleClass
MethodTable 0x00955124
EEClass 0x02ca33b0
Size 36(0x24) bytes
FieldDesc*: 00955064
      MT    Field   Offset                 Type       Attr    Value Name
00955124  400000a        4         System.Int64   instance      31 l1
00955124  400000b        c                CLASS   instance 00a819a0 str
    << some fields omitted from the display for brevity >>
00955124  4000003       1e          System.Byte   instance        3 b3
00955124  4000004       1f          System.Byte   instance        4 b4

Как отмечено, макетом размещения по умолчанию, сгенерированным для классов компилятором C# является LayoutType.Auto (для структур используется LayoutType.Sequential ); таким образом загрузчик классов переупорядочивает поля экземпляра для минимизации смещений. Мы можем использовать ObjSize для получения графа включающего пространство, занятое экземпляром, str. Здесь полученный вывод:

!ObjSize 0x00a8197c
sizeof(00a8197c) = 72 ( 0x48) bytes (SimpleClass)

Son of Strike
SOS отладочное расширение используемое для отображения содержимого структур данных CLR в этой статье. Это часть пакета установки .NET Framework и расположено по пути %windir%\Microsoft.NET\Framework\v1.1.4322. До загрузки SOS в процесс, включите управляемую отладку в свойствах проекта в Visual Studio .NET. Добавьте директорию в которой расположен SOS.dll в переменную окружения PATH. Для загрузки SOS при остановке в точке останова, откройте Debug | Windows | Immediate. В окне immediate выполните .load sos.dll. Используйте !help для получения списка команд отладчика. Для более подробной информации о SOS смотрите документацию msdn Bugslayer column.

Если вы отнимите размер экземпляра SimpleClass (36 байтов) от всего размера графа объектов (72 байта), вы получите размер str, который составляет 36 байт. Давайте проверим это сняв дамп экземпляра str. Ниже результат вывода команды:

!DumpObj 0x00a819a0
Name: System.String
MethodTable 0x009742d8
EEClass 0x02c4c6c4
Size 36(0x24) bytes

Если вы добавите размер экземпляра строки str (36 байт) к размеру экземпляра SimpleClass (36 байт), вы получите общий размер 72 байта, что соответствует выводу команды ObjSize. Заметьте, что ObjSize не будет включать память занятую инфраструктурой syncblk. Также, в .NET Framework 1.1, CLR не известно о памяти занятой любыми неуправляемыми ресурсами, такими как GDI объекты, COM объекты, файловые хендлеры, и так далее; поэтому они не будут отражены этой командой.
Хендлер типа (TypeHandle), указатель на таблицу методов (MethodTable), расположен прямо после номера syncblk. До создания экземпляра объекта, CLR просматривает загруженные типы и загружает информацию о типе если тип не обнаружен, получает адрес таблицы методов, создаёт экземпляр объекта и заносит значение в TypeHandle экземпляра объекта. Код скомпилированный JIT компилятором использует хендлер типа TypeHandle для нахождения таблицы методов MethodTable для распределения методов. Скомпилированный JIT-компилятором код использует хендлер типа (TypeHandle) для позиционирования таблицы методов (MethodTable) для распределения вызовов методов. CLR использует хендлер типа (TypeHandle), когда необходимо найти загруженный тип через таблицу методов MethodTable.

Таблица методов MethodTable

Каждый класс и интерфейс, когда загружен в домен приложения, будет представлен в памяти структурой данных MethodTable. Это является результатом действий по загрузке классов до создания самого первого экземпляра объекта. В то время как экземпляр объекта ObjectInstance хранит состояние, MethodTable хранит информацию о поведении. MethodTable связывает экземпляр объекта с отображёнными в памяти структурами метаданных сгенерированными компилятором языка с помощью EEClass. Информация в таблице методов MethodTable и структуры данных, прикреплённые к ней могут быть доступны из управляемого кода через System.Type Указатель на таблицу методов может быть также получен даже в управляемом коде через свойство Type.RuntimeTypeHandle. Хендлер типа TypeHandle содержащийся в ObjectInstance, указывает на смещение от начала таблицы методов. Это смещение составляет 12 байт по умолчанию и содержит информацию для сборщика мусора, которая здесь обсуждаться не будет.

Рисунок 9 показывает типичное представление таблицы методов. Мы покажем некоторые важные поля хенлера типа, но для более полного списка используйте рисунок. Давайте начнём с Base Instance Size, так как он имеет прямую корреляцию с профилем памяти времени выполнения.


Рисунок 9 Представление таблицы методов

Базовый размер экземпляраBase Instance Size

Базовый размер экземпляра это размер объекта, вычисляемый загрузчиком класса, основанный на декларациях полей в коде. Как рассмотрено ранее, текущая реализация сборщика мусора требует размер экземпляра объекта как минимум 12 байт. Если класс не имеет ни одного объявленного экземплярного поля, это приведёт к избыточности в 4 байта.

Остальные 8 байт будут заняты заголовком (Object Header) (который может содержать номер блока синхронизации syncblk) и хендлером типа (TypeHandle). Снова размер объекта может быть подвержен влиянию StructLayoutAttribute.

Посмотрим на снимок памяти (окно памяти в Visual Studio .NET 2003 ) таблицы методов для MyClass из рисунка 3 (MyClass с двумя интерфейсами) и сравним это с генерированным с помощью SOS выводом. На рисунке 9, размер объекта расположен по 4-х байтовому смещению и имеет значение 12 (0x0000000C) байт. Следующее является выводом DumpHeap из SOS:

!DumpHeap -type MyClass
 Address       MT     Size
00a819ac 009552a0       12
total 1 objects
Statistics:
    MT  Count TotalSize Class Name
9552a0      1        12    MyClass

Таблица слотов методов

Встроенная в таблице методов таблица слотов указывает на соответствующие описатели методов (MethodDesc), предоставляющие поведение типа. Таблица слотов методов создаётся на базе линейного списка объявлений методов располагающихся в следующем порядке: наследованные виртуальные методы, объявленные виртуальные методы, экземплярные методы, статические методы. Загрузчик классов идёт через метаданные текущего класса, родительского класса и интерфейсы и создаёт таблицу методов. В процессе формирования заменяются переопределённые виртуальные методы, заменяются скрываемые методы родительского класса, создаются новые слоты и дублируются слоты по необходимости. Дублирование слотов необходимо для создания иллюзии что каждый интерфейс имеет свою собственную мини vtable. Однако, слоты дубликаты указывают на ту же физическую реализацию.MyClass имеет три экземплярных метода, конструктор класса (.cctor) и конструктор объекта (.ctor). Конструктор объекта автоматически генерируется компилятором C# для всех объектов не имеющих конструкторов определённых явно. Конструктор класса генерируется компилятором когда мы имеем статические переменные определённые и инициализированные. Рисунок 10 показывает представление таблицы методов для MyClass. Представление показывает 10 методов потому что имеет место дублирование слота Method2 для IVMap, который будет рассмотрен следующим. Рисунок 11 показывает редактируемый SOS дамп таблицы методов класса MyClass.


Рисунок 10 Представление таблицы методов MyClass

Рисунок 11 SOS дамп таблицы методов для MyClass

!DumpMT -MD 0x9552a0
  Entry  MethodDesc  Return Type       Name
0097203b 00972040    String            System.Object.ToString()
009720fb 00972100    Boolean           System.Object.Equals(Object)
00972113 00972118    I4                System.Object.GetHashCode()
0097207b 00972080    Void              System.Object.Finalize()
00955253 00955258    Void              MyClass.Method1()
00955263 00955268    Void              MyClass.Method2()
00955263 00955268    Void              MyClass.Method2()
00955273 00955278    Void              MyClass.Method3()
00955283 00955288    Void              MyClass..cctor()
00955293 00955298    Void              MyClass..ctor()

Первые 4 метода любого типа будут всегда ToString, Equals, GetHashCode и Finalize. Эти методы виртуальные наследованные от System.Object. Слот Method2 имеет дубликат, но оба указывают на один и тот же дескриптор метода. Явно кодированный .cctor и .ctor будут сгруппированы со статическими и экземплярными методами соответственно.

Описатель метода

Описатель метода (MethodDesc) это инкапсуляция реализации метода как его понимает CLR. Существует много типов описателей методов, что поддерживают вызовы к различным реализациям внешних взаимодействий, в добавок к управляемым реализациям. В этой статье мы будем рассматривать, только управляемый MethodDesc в контексте код а показанного на рисунке 3. MethodDesc сгенерирован как часть процесса загрузки класса и первоначально указывает на промежуточный язык (IL). Каждый описатель метода MethodDesc заполнен содержимым PreJitStub, который отвечает за включение JIT компиляции. Рисунок 12 показывает типичное представление. Запись слота таблицы методов на самом деле указывает на заглушку вместо настоящей структуры данных MethodDesc. Эта запись располагается по отрицательному смещению из 5-и байт от настоящего MethodDesc и является частью 8-и байтового заполнения, наследуемого каждым методом. Эти 5 байт содержат инструкции для вызова подпрограммы PreJitStub. Это 5-и байтовое смещение может быть видно из вывода DumpMT (для MyClass на рисунке 11) of SOS, поскольку MethodDesc всегда 5 байт после расположения указанного в записи таблицы слотов методов. До первого вызова выполняется вызов подпрограммы JIT компиляции. После выполнения компиляции 5 байт содержащие инструкцию вызова будут перезаписаны командой безусловного перехода на JIT скомпилированный код в архитектуре x86.


Рисунок 12 Описатель метода

Дизассемблирование кода на который указывает запись в таблице слотов методов на рисунке 12 будет показывать вызов на PreJitStub. Здесь сокращённый вывод дизассемблирования до JIT компиляции для метода Method2:

!u 0x00955263
Unmanaged code
00955263 call        003C3538        ;call to the jitted Method2()
00955268 add         eax,68040000h   ;ignore this and the rest 
                                     ;as !u thinks it as code

Теперь давайте запустим метод и дизассемблируем тот же адрес:

!u 0x00955263
Unmanaged code
00955263 jmp     02C633E8        ;call to the jitted Method2()
00955268 add     eax,0E8040000h  ;ignore this and the rest 
                                 ;as !u thinks it as code

Только первые 5 байт по этому адресу являются кодом; остальные содержат данные метода Method2 описателя методов. Команда »!u» не в курсе этого и генерирует бессмысленный код, то есть вы можете игнорировать всё после 5-и первых байт.

CodeOrIL до JIT компиляции содержит относительный виртуальный адрес(RVA) реализации метода в промежуточном языке (IL). Это поле устанавливается для индикации что это есть промежуточный код. CLR обновляет это это поле адресом JIT –компилированного кода после компиляции по требованию. Давайте выберем метод из тех что выведены и снимем дамп MethodDesc используя команду DumpMT до и после JIT компиляции:

!DumpMD 0x00955268
Method Name : [DEFAULT] [hasThis] Void MyClass.Method2()
MethodTable 9552a0
Module: 164008
mdToken: 06000006 
Flags : 400
IL RVA : 00002068

После компиляции, MethodDesc выглядит следующим образом:

!DumpMD 0x00955268
Method Name : [DEFAULT] [hasThis] Void MyClass.Method2()
MethodTable 9552a0
Module: 164008
mdToken: 06000006
Flags : 400
Method VA : 02c633e8

Поле флагов в описателе метода закодировано для хранения информации о типе метода, таким как статичный, экземплярный, интерфейсный метод или COM реализация.

Давайте посмотрим на другой сложный аспект таблицы методов: реализация интерфейсов. Она сделана так, чтобы смотреть просто на управляемую среду понимая все сложности в процесс представления. Далее, мы рассмотрим как интерфейсы размещаются и как распределение интерфейсных методов реально работает.

IVMap и Карта интерфейсов

По смещению 12 в таблице методов находится важный указатель, IVMap. Как показано на рисунке 9, IVMap указывает на таблицу сопоставлений уровня домена приложения, которая индексируется с помощью идентификатора интерфейса уровня процесса. Каждая реализация интерфейса будет иметь запись в IVMap. Если MyInterface1 реализован двумя классами, то будет две записи в таблице IVMap. Запись будет указывать назад на начало подчинённой таблицы встроенной в таблицу методов (MethodTable) MyClass, как показано на рисунке 9. Это референс с помощью которого происходит распределение интерфейсного метода. IVMap создаётся на основе информации карты интерфейса встроенной в таблице методов. Карта интерфейса создана на основе метаданных класса в процессе построения таблицы методов. Как только загрузка типа выполнена, только IVMap используется в методе распределения. Поле по смешению 28 в таблицы методов (Interface Map ) указывает на запись InterfaceInfo встроенную внутри таблицы методов. В нашем случае, присутствует две записи для каждого из двух интерфейсов реализованных классом MyClass. Первые 4 байта первой записи InterfaceInfo указывают на хендлер типа (TypeHandle) интерфейса MyInterface1 (смотри рисунок 9 и рисунок 10). Следующее слово (2 байта) занято флагами (где 0 наследован от родительского класса и 1 реализована в текущем классе). Следующим словом сразу после флагов идёт начальный слот, который используется загрузчиком классов для размещения подчинённой таблицы реализации интерфейса. Для MyInterface1 значение равно 4, которое значит что слоты 5 и 6 указывают на реализацию. Для интерфейса MyInterface2, значение равно 6, то есть слоты 7 и 8 указывают на реализацию. Загрузчик классов дуюлирует слоты если это необходимо для создания иллюзии, что каждый интерфейс получает свою собственную реализацию, хотя физически сопоставляется с тем же описателем метода. В классе MyClass метод MyInterface1.Method2 и метод MyInterface2.Method2 будут указывать на одну и ту же реализацию.

Распределение интерфейсного метода выполняется через IVMap, в то время как распределение прямых методов происходит через адрес MethodDesc сохранённый в соответствующем слоте. Как отмечено ранее, .NET Framework использует конвенцию вызовов fastcall. Первые два аргумента обычно передаются через регистры ECX и EDX, если возможно. Первый аргумент экземплярного метода всегда «this» указатель, который передаётся через регистр ECX, как показано инструкцией «mov ecx, esi»:

mi1.Method1();
mov    ecx,edi                 ;move "this" pointer into ecx        
mov    eax,dword ptr [ecx]     ;move "TypeHandle" into eax 
mov    eax,dword ptr [eax+0Ch] ;move IVMap address into eax at offset 12
mov    eax,dword ptr [eax+30h] ;move the ifc impl start slot into eax
call   dword ptr [eax]         ;call Method1

mc.Method1();
mov    ecx,esi                 ;move "this" pointer into ecx
cmp    dword ptr [ecx],ecx     ;compare and set flags
call   dword ptr ds:[009552D8h];directly call Method1

Этот дизассемблированный код демонстрирует что прямые вызовы экземплярных методов MyClass не используют смещение. JIT компилятор записывает адрес описателя метода непосредственно в коде. Распределение основанное на интерфейсе происходит через IVMap и требует несколько больше инструкций, чем прямое распределение. Одна из инструкций используется для получения адреса IVMap, а другая для получения начального слота реализации интерфейса в таблице слотов методов. Также, приведение экземпляра объекта к интерфейсу это просто копирование этого указателя в целевую переменную. На рисунке 2, «mi1 = mc;» использует одиночную инструкцию для копирования OBJECTREF из mc в mi1.

Виртуальное распределение

Давайте сейчас посмотрим на виртуальное распределение и сравним это с прямым и основанным на интерфейсах распределении. Здесь приведено дизассемблирование для вызова виртуального метода MyClass.Method3 из рисунка 3:

mc.Method3();
Mov    ecx,esi               ;move "this" pointer into ecx
Mov    eax,dword ptr [ecx]   ;acquire the MethodTable address
Call   dword ptr [eax+44h]   ;dispatch to the method at offset 0x44

Виртуальное распределение всегда происходит через фиксированный номер слота, независимо от от указателя в таблице методов в полученной иерархии реализации класса (типа). В процессе построения таблицы методов, загрузчик классов заменяет родительскую реализацию с переопределённой дочерней реализацией. Как результат, вызовы методов кодируются против родительского объекта распределённого к реализации дочернего объекта. Дизассемблирование демонстрирует что распределение происходит через слот номер 8 в окне памяти отладчика (как видно на рисунке 10) так же как и в выводе DumpMT.

Статические переменные

Статические переменные важная составляющая часть структуры данных таблицы методов. Они располагаются как часть таблицы методов сразу после массива слотов таблицы методов. Все примитивные статические типы встраиваемые, в то время как статические объекты значения, такие как структуры и ссылочные типы адресуются через OBJECTREF созданных в таблицах хендлеров. OBJECTREF в таблице методов указывает на экземпляр объекта созданный в куче. Однажды созданный, OBJECTREF в таблице хендлеров будет держать экземпляр объекта в куче невредимым, пока домен приложения не будет выгружен. На рисунке 9, статическая строковая переменная str, указывает на OBJECTREF в таблице хендлеров, который указывает на MyString в куче сборщика мусора.

EEClass

EEClass появляется до создания таблицы методов и в комбинации с таблицей методов является CLR версией объявления типа. На самом деле, EEClass и таблица методов логически явлются одной структурой данных (вместе они представляют один тип)и были разделены на основе частотыв использования. Поля используемые достаточно часто располагаются в таблице методов, а поля используемые не часто находятся в EEClass. Так что информация (такая как имена, поля и смещения)необходимая для JIT компиляции функций оказывается в EEClass, однако данные необходимые во время исполнения (такие как слоты vtable и информация сборщика мусора) располагается в таблице методов.

Для каждого типа загруженного в домен приложения будет создан один EEClass. Это включает интерфейсы, классы, абстрактные классы, массивы и структуры. Каждый EEClass это узел дерева отслеживаемый механизмом исполнения. CLR использует эту сеть для навигации через структуры EEClass для таких целей как загрузка класса, построение таблицы методов, проверка типа и приведения типов. Взаимосвязь дочернего элемента с родитель между EEClass устанавливается на основе иерархии наследования, в свою очередь взаимосвязи родителя с дочерним элементом устанавливаются на основе комбинации иерархии наследования и последовательности загрузки классов. Новые узлы EEClass добавляются, накладываются взаимосвязи между узлами и новые взаимосвязи устанавливаются в процессе выполнения управляемого кода. Существуют также горизонтальные связи с близнецами EEClass в сети. EEClass обладает тремя полями для управления взаимосвязями узлов между загруженными типами: Родительский класс ParentClass, Цепочка близнец SiblingChain и дочерняя цепочка ChildrenChain. Смотрите рисунок 13 для схематичного представления EEClass в контексте класса MyClass с рисунка 4.

Рисунок 13 показывает только несколько полей относящихся к этому обсуждению. Потому что мы пропустили некоторые поля в представлении, мы не показали смещения на этом рисунке. EEClass имеет циклические ссылки на таблицу методов. EEClass также указывает на блоки данных описателя метода, расположенных в куче частого доступа домена приложения по умолчанию. Референс на список объектов описателей полей расположенный на куче процесса обеспечивает информацию о размещении полей во время построения таблицы методов. EEClass располагается на куче с низкой частотой доступа домена приложения, так что операционная система может эффективнее управлять страницами памяти, и как следствие уменьшается рабочее пространство.


Рисунок 13 Представление EEClass

Другие поля, показанные на рисунке 13 являются второстепенными и не требуют объяснения в контексте MyClass (рисунок 3). Давайте посмотрим на фактическую физическую память создав дамп EEClass используя SOS. Запустим программу с рисунка 3 после установки точки останова на строке mc.Method1. Для начала получим адрес EEClass для MyClass используя команду Name2EE:

!Name2EE C:/Working/test/ClrInternals/Sample1.exe MyClass

MethodTable: 009552a0
EEClass: 02ca3508
Name: MyClass

Первый аргумент команды Name2EE это имя модуля, которое может быть получено из команды DumpDomain. Теперь мы знаем адрес EEClass, и получим дамп самого класса EEClass:

!DumpClass 02ca3508
Class Name : MyClass, mdToken : 02000004, Parent Class : 02c4c3e4 
ClassLoader : 00163ad8, Method Table : 009552a0, Vtable Slots : 8
Total Method Slots : a, NumInstanceFields: 0,
NumStaticFields: 2,FieldDesc*: 00955224

      MT    Field   Offset  Type           Attr    Value    Name
009552a0  4000001   2c      CLASS          static 00a8198c  str
009552a0  4000002   30      System.UInt32  static aaaaaaaa  ui 

Рисунок 13 и вывод DumpClass выглядят в основном одинаково. Токен метаданных (mdToken) представляет индекс MyClass в таблицах метаданных сопоставления памяти модуля PE файла, родительский класс указывает на System.Object. Цепочка близнец (Рисунок 13) демонстрирует, что это загружено как результат загрузки класса Program.

MyClass имеет восемь слотов vtable (методы что могут быть распределены виртуально). При том, что методы Method1 Method2 не являются виртуальными, они будут рассматриваться как виртуальные методы при выполнении распределения через интерфейсы и поэтому добавлены в список. Добавим .cctor и .ctor в список, и вы получите всего 10(0xA) методов. Класс имеет два статических поля приведенные в конце. MyClass не имеет экземплярных полей. Остальные поля не требуют объяснения.

Заключение

Закончился наш тур по некоторым самым важным внутренним компонентам CLR. Очевидно, осталось гораздо больше не рассмотренного, и того что следовало бы рассмотреть более глубоко, но мы надеемся это даст вам некоторое впечатление как это работает. Много информации представленной здесь возможно будет изменена в последующих выпусках CLR и .NET Framework. Но всё же, если структуры данных рассмотренные в этой статье могут быть изменены, концепции будут оставаться без изменений.

The .NET Framework is a software development framework developed by Microsoft that provides a runtime environment and a set of libraries and tools for building and running applications on Windows operating systems. The framework includes a variety of programming languages, such as C#, F#, and Visual Basic, and supports a range of application types, including desktop, web, mobile, and gaming applications.

  1. The .NET Framework includes two main components: the Common Language Runtime (CLR) and the .NET Framework Class Library. The CLR is responsible for managing the execution of code written in any of the supported languages, while the class library provides a large set of pre-built functions and classes that can be used to create a wide range of applications.
  2. One of the key advantages of the .NET Framework is its support for a variety of programming languages. This means that developers can choose the language that best fits their needs and expertise, while still being able to use the same set of libraries and tools provided by the framework.
  3. Another advantage of the .NET Framework is its support for a variety of application types. The framework includes libraries and tools for creating desktop, web, mobile, and gaming applications, which makes it a versatile choice for developers working on a wide range of projects.
  4. The .NET Framework also provides a number of features that help improve the security, reliability, and performance of applications. These include features such as code access security, automatic memory management, and just-in-time (JIT) compilation, which helps improve the speed of application execution.
  5. The .NET Framework is also designed to integrate with other Microsoft technologies, such as Microsoft SQL Server, Microsoft SharePoint, and Microsoft Office, which can make it easier to build applications that work seamlessly with other Microsoft products.

Overall, the .NET Framework is a powerful and versatile development platform that provides a wide range of tools and libraries for building and running applications on Windows operating systems.

.NET is a software framework that is designed and developed by Microsoft. The first version of the .Net framework was 1.0 which came in the year 2002. In easy words, it is a virtual machine for compiling and executing programs written in different languages like C#, VB.Net, etc. 

It is used to develop Form-based applications, Web-based applications, and Web services. There is a variety of programming languages available on the .Net platform, VB.Net and C# being the most common ones. It is used to build applications for Windows, phones, web, etc. It provides a lot of functionalities and also supports industry standards. 

.NET Framework supports more than 60 programming languages of which 11 programming languages are designed and developed by Microsoft. The remaining Non-Microsoft Languages are supported by .NET Framework but not designed and developed by Microsoft. 

There are three significant phases of the development of .NET technology.

  • OLE Technology
  • COM Technology
  • .NET Technology

OLE Technology: OLE (Object Linking and Embedding) is one of the technologies of Microsoft’s component document. Basically, its main purpose is to link elements from different applications with each other.

COM Technology: The technology of the Microsoft Windows family of the operating system, Microsoft COM (Common Object Model) enables various software components to communicate. COM is mostly used by developers for various purposes like creating reusable software components, linking components together to build applications, and also taking advantage of Windows services. The objects of COM can be created with a wide range of programming languages.

.NET Technology: .NET technology of collection or set of technologies to develop windows and web applications. The technology of .Net is developed by Microsoft and was launched in Feb. 2002, by basic definition, Microsoft’s new Internet Strategy. It was originally called NGWS (Next Generation Web Services). It is considered to be one of the most powerful, popular, and very useful Internet Technology available today.

11 Programming Languages which are designed and developed by Microsoft are: 

  • C#.NET
  • VB.NET
  • C++.NET
  • J#.NET
  • F#.NET
  • JSCRIPT.NET
  • WINDOWS POWERSHELL
  • IRON RUBY
  • IRON PYTHON
  • C OMEGA
  • ASML(Abstract State Machine Language)

Main Components of .NET Framework

Common Language Runtime(CLR): CLR is the basic and Virtual Machine component of the .NET Framework. It is the run-time environment in the .NET Framework that runs the codes and helps in making the development process easier by providing various services such as remoting, thread management, type safety, memory management, robustness, etc. Basically, it is responsible for managing the execution of .NET programs regardless of any .NET programming language. It also helps in the management of code, as code that targets the runtime is known as Managed Code, and code that doesn’t target to runtime is known as Unmanaged code. 

Framework Class Library(FCL): It is the collection of reusable, object-oriented class libraries and methods, etc that can be integrated with CLR. Also called the Assemblies. It is just like the header files in C/C++ and packages in java. Installing the .NET framework basically is the installation of CLR and FCL into the system. Below is the overview of the .NET Framework. 

.NET Framework Components

Is the .NET application platform dependent or platform independent?

The combination of Operating System Architecture and CPU Architecture is known as the platform. Platform-dependent means the programming language code will run only on a particular Operating System. A .NET application is platform-dependent because of the .NET framework which is only able to run on the Windows-based operating system. The .Net application is platform-independent also because of the Mono framework. Using the Mono framework the .Net application can run on any Operating System including windows. Mono framework is a third-party software developed by Novell Company which is now a part of Micro Focus Company. It is a paid framework. 

Release History of .NET Framework and its compatibility with the different Windows version 

.NET Version CLR Version

Development tool

Windows Support
1.0 1.0 Visual Studio .NET XP SP1
1.1 1.1 Visual Studio .NET 2003 XP SP2, SP3
2.0 2.0 Visual Studio 2005 N/A
3.0 2.0 Expression Blend Vista
3.5 2.0 Visual Studio 2008 7, 8, 8.1, 10
4.0 4 Visual Studio 2010 N/A
4.5 4 Visual Studio 2012 8
4.5.1 4 Visual Studio 2013 8.1
4.5.2 4 N/A N/A
4.6 4 Visual Studio 2015 10 v1507
4.6.1 4 Visual Studio 2015 Update 1 10 v1511
4.6.2 4 N/A 10 v1607
4.7 4 Visual Studio 2017 10 v1703
4.7.1 4 Visual Studio 2017 10 v1709
4.7.2 4 Visual Studio 2017 10v 1803
4.8 4 Visual Studio 2019 11
4.8.1 4 Visual Studio 2019 11
6   Visual Studio 2022 11

Important Points: 

  • Visual Studio is the development tool that is used to design and develop .NET applications. For using Visual Studio, the user has to first install the .NET framework on the system.
  • In the older version of Windows OS like XP SP1, SP2, or SP3, the .NET framework was integrated with the installation media.
  • Windows 8, 8.1, or 10 do not provide a pre-installed version 3.5 or later of .NET Framework. Therefore, a version higher than 3.5 must be installed either from a Windows installation media or from the Internet on demand. Windows update will give recommendations to install the .NET framework.

Advantages of .NET Framework:

  1. Multi-language support: The .NET Framework supports a variety of programming languages, including C#, F#, and Visual Basic, which allows developers to choose the language that best fits their needs and expertise.
  2. Cross-platform compatibility: The .NET Framework can run on multiple operating systems, including Windows, Linux, and macOS, which provides flexibility in developing and deploying applications.
  3. Large community: The .NET Framework has a large and active community of developers who have created a wide range of resources, including libraries, tools, and documentation.
  4. Security: The .NET Framework includes a variety of security features, such as code access security and digital signatures, which can help protect applications from malicious attacks.
  5. Productivity: The .NET Framework includes a large set of pre-built libraries and tools that can help developers save time and improve productivity.

Disadvantages of .NET Framework:

  1. Windows dependency: Although the .NET Framework can run on multiple operating systems, it was originally designed for use on Windows operating systems, which means that it may not be the best choice for cross-platform applications.
  2. Large footprint: The .NET Framework has a large installation footprint, which can make it difficult to deploy applications on systems with limited storage or bandwidth.
  3. Licensing: Some versions of the .NET Framework require a license, which can add to the cost of developing and deploying applications.
  4. Performance: While the .NET Framework provides good performance for most applications, it may not be the best choice for high-performance applications that require low-level access to hardware or complex algorithms.
  5. Learning curve: Although the .NET Framework is designed to be easy to use, it still has a learning curve, especially for developers who are new to the platform or to object-oriented programming in general.

Last Updated :
20 Feb, 2023

Like Article

Save Article

The .NET Framework is a set of libraries and a runtime, originally designed by Microsoft. All .NET programs compile to a bytecode called Microsoft Intermediate Language (MSIL). The MSIL is run by the Common Language Runtime (CLR).

Below you can find several examples of «Hello World» in various languages that support the .NET Framework. «Hello World» is a program that displays «Hello World» on the display device. It’s used for illustrating the basic syntax for constructing a working program. It can also be used as a sanity test to make sure that a language’s compiler, development environment, and runtime environment are all working correctly.

List of languages supported by .NET

Versions

.NET

Version Release Date
1.0 2002-02-13
1.1 2003-04-24
2.0 2005-11-07
3.0 2006-11-06
3.5 2007-11-19
3.5 SP1 2008-08-11
4.0 2010-04-12
4.5 2012-08-15
4.5.1 2013-10-17
4.5.2 2014-05-05
4.6 2015-07-20
4.6.1 2015-11-17
4.6.2 2016-08-02
4.7 2017-04-05

Compact Framework

Version Release Date
1.0 2000-01-01
2.0 2005-10-01
3.5 2007-11-19
3.7 2009-01-01
3.9 2013-06-01

Micro Framework

Version Release Date
4.2 2011-10-04
4.3 2012-12-04
4.4 2015-10-20

Hello World in Boo

print "Hello World"
 

Hello World in C#

using System;

class Program
{
    // The Main() function is the first function to be executed in a program
    static void Main()
    {
        // Write the string "Hello World to the standard out
        Console.WriteLine("Hello World");
    }
}
 

Console.WriteLine has several overloads. In this case, the string «Hello World» is the parameter, and it will output the «Hello World» to the standard out stream during execution. Other overloads may call the .ToString of the argument before writing to the stream. See the .NET Framework Documentation for more information.

Live Demo in Action at .NET Fiddle

Introduction to C#

Hello World in C++/CLI

using namespace System;

int main(array<String^>^ args)
{
    Console::WriteLine("Hello World");
}
 

Hello World in F#

open System

[<EntryPoint>]
let main argv = 
    printfn "Hello World" 
    0 
 

Live Demo in Action at .NET Fiddle

Introduction to F#

Hello World in IL

.class public auto ansi beforefieldinit Program
       extends [mscorlib]System.Object
{
  .method public hidebysig static void  Main() cil managed
  { 
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldstr      "Hello World"
    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000b:  nop
    IL_000c:  ret
  }

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  }

}
 

Hello World in Nemerle

System.Console.WriteLine("Hello World");
 

Hello World in Oxygene

namespace HelloWorld;

interface

type
  App = class
  public
    class method Main(args: array of String);
  end;

implementation

class method App.Main(args: array of String);
begin
  Console.WriteLine('Hello World');
end;

end.
 

Hello World in PowerShell

Write-Host "Hello World"
 

Introduction to PowerShell

Hello World in Python (IronPython)

print "Hello World"
 
import clr
from System import Console
Console.WriteLine("Hello World")
 

Hello World in Visual Basic .NET

Imports System

Module Program
    Public Sub Main()
        Console.WriteLine("Hello World")
    End Sub
End Module
 

Live Demo in Action at .NET Fiddle

Introduction to Visual Basic .NET

замечания

.NET Framework — это набор библиотек и среда выполнения, первоначально разработанная Microsoft. Все .NET-программы компилируются в байт-код под названием Microsoft Intermediate Language (MSIL). MSIL управляется Common Language Runtime (CLR).

Ниже вы можете найти несколько примеров «Hello World» на разных языках, поддерживающих .NET Framework. «Hello World» — это программа, которая отображает «Hello World» на устройстве отображения. Он используется для иллюстрации базового синтаксиса для построения рабочей программы. Его также можно использовать в качестве теста на здравомыслие, чтобы убедиться, что компилятор языка, среда разработки и среда выполнения работают правильно.

Список языков, поддерживаемых .NET.

Версии

.СЕТЬ

Версия Дата выхода
1,0 2002-02-13
1,1 2003-04-24
2,0 2005-11-07
3.0 2006-11-06
3,5 2007-11-19
3.5 SP1 2008-08-11
4,0 2010-04-12
4.5 2012-08-15
4.5.1 2013-10-17
4.5.2 2014-05-05
4,6 2015-07-20
4.6.1 2015-11-17
4.6.2 2016-08-02
4,7 2017-04-05

Компактная структура

Версия Дата выхода
1,0 2000-01-01
2,0 2005-10-01
3,5 2007-11-19
3,7 2009-01-01
3,9 2013-06-01

Микроструктура

Версия Дата выхода
4,2 2011-10-04
4,3 2012-12-04
4,4 2015-10-20

Привет, мир в C #

using System;

class Program
{
    // The Main() function is the first function to be executed in a program
    static void Main()
    {
        // Write the string "Hello World to the standard out
        Console.WriteLine("Hello World");
    }
}

Console.WriteLine имеет несколько перегрузок. В этом случае строка «Hello World» является параметром, и во время выполнения она выведет «Hello World» в стандартный поток. Другие перегрузки могут вызывать .ToString аргумента перед записью в поток. Дополнительную информацию см. В документации по .NET Framework .

Живая демонстрация в действии на .NET Fiddle

Введение в C #

Hello World в Visual Basic .NET

Imports System

Module Program
    Public Sub Main()
        Console.WriteLine("Hello World")
    End Sub
End Module

Живая демонстрация в действии на .NET Fiddle

Введение в Visual Basic .NET

Hello World in F #

open System

[<EntryPoint>]
let main argv = 
    printfn "Hello World" 
    0 

Живая демонстрация в действии на .NET Fiddle

Введение в F #

Hello World в C ++ / CLI

using namespace System;

int main(array<String^>^ args)
{
    Console::WriteLine("Hello World");
}

Привет, мир в PowerShell

Write-Host "Hello World"

Введение в PowerShell

Привет, мир в Nemerle

System.Console.WriteLine("Hello World");

Привет, мир в Oxygene

namespace HelloWorld;

interface

type
  App = class
  public
    class method Main(args: array of String);
  end;

implementation

class method App.Main(args: array of String);
begin
  Console.WriteLine('Hello World');
end;

end.

Привет, мир в Бу

print "Hello World"

Hello World в Python (IronPython)

print "Hello World"
import clr
from System import Console
Console.WriteLine("Hello World")

Привет мир в Ил

.class public auto ansi beforefieldinit Program
       extends [mscorlib]System.Object
{
  .method public hidebysig static void  Main() cil managed
  { 
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldstr      "Hello World"
    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000b:  nop
    IL_000c:  ret
  }

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  }

}

Понравилась статья? Поделить с друзьями:
  • Глазные капли противовирусные инструкция по применению взрослым
  • Триметазидин гидрохлорид инструкция по применению взрослым
  • Инструкция по правилам пользования комплексом технических средств охраны в школе
  • Грамурин инструкция по применению цена отзывы аналоги
  • Брустан инструкция по применению от чего помогает взрослым таблетки инструкция