C# Processo de execução de um código no .NET Framework

Entenda as nuances do processo de execução de um código no .NET Framework.

Wladimilson M. Nascimento 12 de dezembro de 2016

Quando estamos desenvolvendo, o único momento em que pensamos na compilação do código é quando algum erro ocorre. Fora isso, essa é só mais uma etapa do processo de execução (que costuma passar despercebida).

Só que um bom programador precisa entender como este processo funciona, para criar códigos melhores, entender mais a linguagem que se está utilizando e compreender melhor os erros gerados.

No geral as linguagens possuem um processo de compilação parecido, mas aqui veremos o processo de compilação de uma aplicação desenvolvida para o .NET Framework.

Nele, separamos o processo de compilação em quatro etapas:

  • Escolhendo um compilador;
  • Compilando o código para a linguagem intermediária;
  • Compilando o código da linguagem intermediário para a nativa;
  • Executando o código.

Essas etapas são ilustradas na imagem abaixo:

C# (C Sharp) - Introdução ao ASP.NET Core
Curso de C# (C Sharp) - Introdução ao ASP.NET Core
CONHEÇA O CURSO

Escolhendo um compilador

O .NET Framework possui um importante recurso que permite a interoperabilidade das linguagens suportadas por ele, que é o Common Language Runtime
(CLR).

Seguindo as especificações definidas no padrão ECMA-335 (Obs: Esse link leva a um arquivo PDF) – que define as especificações do CLR – qualquer linguagem pode definir um compilador suportado pelo .NET. Com isso, é possível desenvolver aplicações para o .NET utilizando C#, F#, Perl, Cobol, Ruby etc.

Assim, na compilação, a primeira coisa que o .NET faz é selecionar o compilador definido para a linguagem do código que será compilado.

É função do compilador verificar o código que está sendo analisado. Ele verificará se algum token está sendo utilizado de forma errônea e se todas as regras da linguagem estão sendo obedecidas. Basicamente ele verificará se há algum erro de digitação no código.

Compilando o código para a linguagem intermediária

Agora que o .NET começa a fazer a sua “mágica”. Ao final do processo de compilação, o compilador converte o código-fonte para uma linguagem intermediária, que no .NET, é chamada de Microsoft Intermediate Language (MSIL), ou apenas Intermediate Language (IL).

O IL é um conjunto de instruções independente da CPU, que pode ser convertido de forma mais eficiente para código nativo.

Nele temos instruções para carregar, armazenar, iniciar e chamar métodos em objetos, bem como, instruções para operações lógicas e matemáticas, controle de fluxo, acesso direto a memória, manipulação de erros, entre outros recursos.

Antes deste código ser gerado, ele precisa ser convertido em tempo de execução para um código de máquina que é entendido pela CPU do computador. Normalmente este processo é realizado por um compilador Just-in-Time (JIT). Como o CLR fornece um ou mais compiladores de acordo com a arquitetura do computador, o mesmo conjunto de instruções IL pode ser compilado e executado em qualquer arquitetura suportada pelo .NET

Quando o código intermediário é produzido, também são gerados metadados. Esses metadados descrevem os tipos utilizados no código incluindo as suas definições, as assinaturas dos seus membros, as referências a outros códigos e qualquer coisa que será utilizada em tempo de execução.

O IL e os metadados são contidos em um arquivo executável portátil (PE – portable executable), que é baseado e que estende o Microsoft PE e Common Object File Format (COFF), historicamente utilizado para conteúdo executável. Este formato de arquivo, que possui o IL ou código nativo, bem como metadados, permite que o sistema operacional reconheça características do CLR. A presença dos metadados com o código intermediário permite que o código se descreva, o que significa que não há necessidade de incluir bibliotecas de tipos ou interfaces de definição da linguagem (IDL).

Durante a execução, o sistema localiza e extrai dos metadados as informações que necessitar.

Compilando o código da linguagem intermediária para a nativa

Antes de executar o código IL, é necessário compilá-lo para a linguagem nativa, suportada pela arquitetura da máquina alvo. Para fazer este processo, o .NET fornece duas formas de conversão:

  • Um compilador Just-in-Time (JIT);
  • O Ngen.exe (Native Image Generator).

Compilação com um compilador JIT

A compilação JIT converte o código intermediário em código nativo em tempo de execução. Conforme um código IL for requisitado, o JIT o converte para código nativo.

Como o CLR fornece um compilador JIT para cada arquitetura suportada pelo .NET, um código IL pode ser compilado pelo JIT e executado em diferentes arquiteturas. No entanto, se o código chamar APIs nativas de uma plataforma, ou algum recurso específico, o código IL só poderá ser executado nesta plataforma.

A compilação JIT leva em conta a possibilidade de algum código nunca ser chamado durante a execução. Assim, em vez de usar tempo e memória para converter todo o código, em um arquivo PE nativo, ele converte apenas o que for necessário para a execução, e armazena este código nativo gerado, na memória, para ele ficar acessível para as chamadas subsequentes.

Por exemplo, na primeira vez que um método é invocado durante a execução, o JIT irá convertê-lo para código nativo e armazená-lo na memória. Quando este código for chamado novamente, o JIT irá diretamente na memória obter o código nativo, em vez de tentar convertê-lo novamente.

Compilação com o Ngen.exe

Como o compilador JIT gera o código nativo em tempo de execução, isso pode impactar um pouco na performance da aplicação. Na maioria dos casos, esta perda é aceitável ou irrisória. Além disso, o código gerado pelo compilador JIT fica vinculado ao processo que desencadeou a compilação, assim ele não pode ser compartilhado entre vários processos.

Para permitir que o código gerado possa ser compartilhado entre múltiplas chamadas ou entre vários processos que compartilham um conjunto de códigos nativos, o .NET possui o compilador Ngen.exe, também chamado de compilador Ahead-of-Time (AOT).

Este compilador gera o código nativo de uma forma muito parecida que o JIT, mas eles se diferenciam em três pontos:

  • O código IL é convertido antes do arquivo ser executado, no lugar de convertê-lo durante a execução.
  • Todo o código é convertido de uma única vez.
  • O código convertido é salvo no disco, e não na memória.

Verificação de código

Se não for desabilitado, durante a compilação para o código nativo, tanto com JIT ou com o AOT, o código IL passa por uma verificação que analisa se o código é “type safe”, o que significa que o código só acessa locais da memória que ele está autorizado a acessar.

Isso ajuda a isolar um objeto do outro e ajuda a protegê-lo contra corrupção indevida ou mal-intencionada. Ele também garante que as restrições de segurança podem ser aplicadas de forma confiável.

Para verificar se o código é “type safe”, a verificação se baseia em três afirmações (que precisam ser verdadeiras):

  • Uma referência a um tipo é estritamente compatível com o tipo a ser referenciado;
  • Somente operações apropriadamente definidas são invocadas em um objeto;
  • As identidades são de quem afirmam ser.

Se o requisito “type safe” estiver ativo e o código não passar na verificação acima, é gerando um erro de execução.

Executando o código

O processo final de execução, é a execução do código propriamente dito. Como já dito nos tópicos anteriores, durante esta execução o CLR e os metadados irão fornecer as informações que forem necessárias para a execução da aplicação.

Lendo assim, é possível notar que o processo não é simples, mas não é tão complexo, por isso que é importante o seu entendimento.

C# (C Sharp) - Introdução ao ASP.NET Core
Curso de C# (C Sharp) - Introdução ao ASP.NET Core
CONHEÇA O CURSO
Deixe seu comentário

Conheça o autor desse artigo

  • Foto Autor Wladimilson M. Nascimento
    Wladimilson M. Nascimento

    Instrutor, nerd, cinéfilo e desenvolvedor nas horas vagas. Graduado em Ciências da Computação pela Universidade Metodista de São Paulo.

    Posts desse Autor

Artigos relacionados