Transcript Introduction to Microsoft .NET
Introduzione al CLR/MSIL
Alfredo Paolillo e Marco Servetto
Vocabolario
IL: Intermediate Language, Standard ECMA del 1997 MSIL: Microsoft IL, Implementazione Microsoft di IL
Introduzione
Perché .NET
Ambiente di esecuzione Common Language Runtime
Perché .NET
Difficile effettuare sviluppo omogeneo Molto tempo viene dedicato a far comunicare i vari “strati” Serve un salto qualitativo per semplificare lo scenario
Evoluzione Codice nativo Codice interpretato Codice MSIL
Codici
Sorgenti
Codice nativo
Compilatore Codice nativo (.EXE) Output
Sorgenti
Codice interpretato
Interprete Output
Sorgenti
Codice MSIL
Compilatore .NET
Codice MSIL (Assembly)
.EXE/.DLL
Compilatore JIT Codice nativo Output
Sorgenti Compilatore JIT
Codice MSIL
Compilatore .NET
Codice + metadati Codice nativo Codice MSIL (Assembly)
.EXE/.DLL
Output
Sorgenti
Codice MSIL
Compilatore .NET
Codice MSIL (Assembly)
.EXE/.DLL
Compilatore JIT Codice nativo Output
Motori JIT
Inizialmente previsti 4 motori: Motore JIT Descrizione OptiJit FastJit Codice più ottimizzato Esecuzione JIT più veloce Native (Pre-Jit) Compilazione preventiva, assembly compilato salvato in GAC Dove si trova Attuale implementazione Non implementato .NET Compact Framework NGEN.EXE
JIT – Just in Time Compiler
In teoria, come con Java, è possibile compilare MSIL ed eseguirlo (interpretato) in qualsiasi ambiente che supporti l’esecuzione La compilazione di un’applicazione da un tipo di codice assembly quale MSIL verso un codice eseguibile sulla macchina nativa dovrebbe appesantire le prestazioni dell’applicazione È quello che succede?
JIT – Just in Time Compiler
Il codice non viene caricato tutto in memoria il compilatore JIT compila solo il codice necessario, quindi memorizza nella cache il codice nativo compilato per riutilizzarlo L’overhead è una lieve differenza che, nella maggior parte dei casi, non verrà rilevata
JIT – Just in Time Compiler
Quando viene caricata una classe, il caricatore aggiunge uno stub a ogni metodo della classe La prima volta che viene chiamato il metodo, il codice stub cede il controllo al compilatore JIT, che compila MSIL nel codice nativo.
Lo stub viene quindi modificato per puntare al codice nativo appena creato, affinché le chiamate successive passino direttamente al codice nativo
Indipendenza dalla piattaforma
.NET è un’implementazione di CLI Common Language Infrastructure CLI è uno standard ECMA ECMA-334, ECMA-335 Esistono già altre implementazioni di CLI: SSCLI (Microsoft, per Windows, FreeBSD e Macintosh) Mono (per Linux) DotGNU Intel OCL (Open CLI Library) …
Codice IL
Tutto questo assomiglia a qualcosa di già visto?
Forse Java?
Ci sono delle differenze Un compilatore Java crea bytecode, che in fase di esecuzione viene interpretato tramite JVM .NET crea un codice nativo
Codice IL
Un vantaggio rilevante offerto da .NET Framework rispetto a Java e JVM è la scelta del linguaggio di programmazione JVM solo Java .NET Multilinguaggio (VB.net, C#, J# etc…) Vediamo un esempio di IL
Assembly
Modulo
(file PE)
Codice IL Metadati Manifest Assembly
Metadati
Concetto chiave in .NET
Informazioni sui tipi di un assembly Generati automaticamente dai compilatori Estendibili da terze parti Formato binario rappresentabile con XML: XML Schema (XSD) Serializzazione e deserializzazione oggetti a runtime in XML
Metadati
Descrizione di un assembly Identità: nome, versione, cultura [, pubblic key] Tipi esportati Assembly da cui dipende Descrizione dei tipi Nome, visibilità, classe base, interfacce implementate Attributi custom Definiti dall’utente Definiti dal compilatore
Codice IL
Proviamo adesso a scrivere e compilare dei semplici programmi in C# e proviamo ad analizzarli
Codice IL
namespace testUno { Esempio 1 public class esempioUno { public esempioUno() { } static void Main(string[] args) { int primaVariabile = 0x1234; int secondaVariabile = 0xabcdef; } } }
Codice IL
Il file eseguibile è costituito da due parti: la prima è il codice MSIL, utilizzato per generare il codice nativo la seconda è rappresentata dai metadati Con un tool in dotazione con l’SDK possiamo Diassemblare il file ottenuto dalla compilazione Otterremo il seguente output Tralasceremo comunque alcuni dettagli come il codice del costruttore di classe
Codice IL
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint
// Code size 13 (0xd) .maxstack 1 .locals init (int32 V_0, int32 V_1) IL_0000: ldc.i4 0x1234 IL_0005: stloc.0
IL_0006: ldc.i4 0xabcdef IL_000b: stloc.1
IL_000c: ret } // end of method esempioUno::Main
Codice IL – istruzioni principali
.entrypoint
Specifies that this method is the entry point to the application (only one such method is allowed).
.maxstack
int32 specifies the maximum number of elements on the evaluation stack during the execution of the method
.locals [init]
Defines a set of local variables for this method.
ldc.i4:
Description Push num of type int32 onto the stack as int32.
stloc.0:
Description: Pop value from stack into local variable 0.
ret:
Description: return from method, possibly returning a value
Codice IL – Metainformazioni
ScopeName : testUno.exe
MVID : {F01C8E38-E942-43D9-9D71-95D37789D357} =========================================================== Global functions ------------------------------------------------------ Global fields ------------------------------------------------------ Global MemberRefs ------------------------------------------------------ TypeDef #1 ------------------------------------------------------ TypDefName: testUno.esempioUno (02000002) Flags : [Public] [AutoLayout] [Class] [AnsiClass] (00100001) Extends : 01000001 [TypeRef] System.Object
Method #1 ------------------------------------------------------ MethodName: .ctor (06000001) Flags : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor] (00001886) RVA : 0x00002050 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments.
Method #2 [ENTRYPOINT] ------------------------------------------------------ MethodName: Main (06000002) Flags : [Private] [Static] [HideBySig] [ReuseSlot] (00000091) RVA : 0x00002064 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] ReturnType: Void 1 Arguments Argument #1: SZArray String 1 Parameters (1) ParamToken : (08000001) Name : args flags: [none] (00000000)
Codice IL – Metainformazioni
TypeRef #1 (01000001) ------------------------------------------------------ Token: 0x01000001 ResolutionScope: 0x23000001 TypeRefName: System.Object
MemberRef #1 ------------------------------------------------------ Member: (0a000002) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments.
TypeRef #2 (01000002) ------------------------------------------------------ Token: 0x01000002 ResolutionScope: 0x23000001 TypeRefName: System.Diagnostics.DebuggableAttribute
MemberRef #1 ------------------------------------------------------ Member: (0a000001) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 2 Arguments Argument #1: Boolean Argument #2: Boolean
Codice IL – Metainformazioni
Signature #1 (0x11000001) ------------------------------------------------------ CallCnvntn: [LOCALSIG] 2 Arguments Argument #1: I4 Argument #2: I4 Assembly ------------------------------------------------------ Token: 0x20000001 Name : testUno Public Key : Hash Algorithm : 0x00008004 Major Version: 0x00000000 Minor Version: 0x00000000 Build Number: 0x00000000 Revision Number: 0x00000000 Locale:
Codice IL – Metainformazioni
AssemblyRef #1 ------------------------------------------------------ Token: 0x23000001 Public Key or Token: b7 7a 5c 56 19 34 e0 89 Name: mscorlib Major Version: 0x00000001 Minor Version: 0x00000000 Build Number: 0x00001388 Revision Number: 0x00000000 Locale:
Codice IL
I metadati vengono organizzati in tabelle, in cui fondamentalmente viene descritto ciò che il codice definisce e a cui fa riferimento Prestiamo attenzione a questa parte di codice: CallCnvntn: [LOCALSIG] 2 Arguments Argument #1: I4 Argument #2: I4
Codice C#
Proviamo adesso a compilare il seguente codice FILE:esempioDueB namespace testDue { public class esempioDueB { static void Main(string[] args) { esempioDueA variabile = new esempioDueA(); variabile.printString(); } } }
Codice C#
FILE: esempioDueA using System; namespace testDue { public class esempioDueA { public esempioDueA() { } public void printString() { string s = "Hello!!!!"; Console.Write(s); } } }
Codice IL
Disassembliamo: A differenza di prima dovremo analizzare due codici
Codice IL
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint
// Code size 13 (0xd) .maxstack 1 .locals init ( class testDue.esempioDueA V_0 ) IL_0000: IL_0007: newobj instance void testDue.esempioDueA::.ctor() IL_0005: stloc.0
IL_0006: ldloc.0
callvirt instance void testDue.esempioDueA::printString() IL_000c: ret } // end of method esempioDueB::Main
Codice IL
.method public hidebysig instance void printString() cil managed { // Code size 13 (0xd) .maxstack 1 .locals init ( string V_0 ) IL_0000: ldstr "Hello!!!!" IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: call void [mscorlib]System.Console::Write(string) IL_000c: ret } // end of method esempioDueA::printString
Codice IL
Principali differenze rispetto al codice precedente:
Newobj:
Assembli format: newobjctor
Call:
Description: allocate an uninitialized object or value type and call ctor Assembli format: call method Description: Call method described by method
Callvirt:
Assembli format: callvirt method Description: Call a method associated with obj
Codice IL
Andiamo nuovamente a riesaminare le meta-informazioni: Signature #2 (0x11000002) (EsempioDueB) ------------------------------------------------------ CallCnvntn: [LOCALSIG] 1 Arguments Argument #1: Class testDue.esempioDueA
Signature #1 (0x11000001) (EsempioDueA) ------------------------------------------------------ CallCnvntn: [LOCALSIG] 1 Arguments Argument #1: String
Codice IL – Metainformazioni
Method #2 (definizione del metodo invocato dalla call) ------------------------------------------------------ MethodName: printString (06000002) Flags : [Public] [HideBySig] [ReuseSlot] (00000086) RVA : 0x00002064 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments.
..................
User Strings (costante) ------------------------------------------------------ 70000001 : ( 9) L"Hello!!!!”
Codice C#
namespace testTre { Passaggio di parametri: public class esempioTreA { static void Main(string[] args) { string s = ("HELLO!!!!!!!!!!!!!!!!!!!!!!!!!!"); esempioTreB variabile = new esempioTreB(); variabile.printString(s); } } }
Esempio C#
Using system; public class esempioTreB { public esempioTreB() { } public void printString(string s) { Console.Write(s); } }
Codice IL
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint
// Code size 20 (0x14) .maxstack 2 .locals init (string V_0, class testTre.esempioTreB V_1) IL_0000: ldstr "HELLO!!!!!!!!!!!!!!!!!!!!!!!!!!" IL_0005: stloc.0
IL_0006: newobj instance void testTre.esempioTreB::.ctor() IL_000b: stloc.1
IL_000c: ldloc.1
IL_000d: ldloc.0
IL_000e: callvirt instance void testTre.esempioTreB:: printString(string) IL_0013: ret } // end of method esempioTreA::Main
Codice IL
.method public hidebysig instance void printString(string s) cil managed { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.1
IL_0001: call void [mscorlib]System.Console::Write(string) IL_0006: ret } // end of method esempioTreB::printString
Codice IL
ldarg.1
Assembli format: ldarg.1
Esistono anche delle varianti, ad esempio: ldarg
num
Assembli format: ldarg num Description: Load argument 1 onto stack Description: Load argument numbered num onto stack.
Codice IL – Metainformazioni
TypeDef #1 ------------------------------------------------------ TypDefName: testTre.esempioTreB (02000002) Flags : [Public] [AutoLayout] [Class] [AnsiClass] (00100001) Extends : 01000001 [TypeRef] System.Object
Method #1 ------------------------------------------------------ MethodName: .ctor (06000001) Flags : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor] (00001886) RVA : 0x00002050 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments.
Method #2 ------------------------------------------------------ MethodName: printString (06000002) Flags : [Public] [HideBySig] [ReuseSlot] (00000086) RVA : 0x00002064 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String 1 Parameters (1) ParamToken : (08000001) Name : s flags: [none] (00000000)
Codice IL – Metainformazioni
Signature #1 (0x11000001) ------------------------------------------------------ CallCnvntn: [LOCALSIG] 2 Arguments Argument #1: String Argument #2: Class testTre.esempioTreB
Assenza di Signature #2 La classe su cui viene invocato il metodo printString non ha dichiarazioni locali
Garbage Collector
Gli oggetti vengono distrutti automaticamente quando non sono più referenziati Algoritmo Mark-and-Compact
Garbage Collector - fase 1: Mark
Root set NextObjPtr Oggetti “vivi” Oggetti non raggiungibili Spazio libero
Root set
Garbage Collector - fase 2: Compact
Spazio recuperato NextObjPtr Oggetti “vivi” Spazio libero
GC e distruzione deterministica
In alcuni casi serve un comportamento di finalizzazione deterministica: Riferimenti a oggetti non gestiti Utilizzo di risorse che devono essere rilasciate appena termina il loro utilizzo Non si possono usare i finalizzatori, che non sono richiamabili direttamente Implementare l’interfaccia IDisposable
Common Type System
Tutto è un oggetto Due categorie di tipi: Tipi reference (riferimento) Allocati su heap gestito Tipi value (valore) Allocati su stack o in oggetti gestiti (reference) Tutti i tipi value possono essere visti come tipi reference Boxing
Tipi value e reference in memoria
public struct Size { public int height; public int weight; } public class CSize { public int height; public int weight; } void Main() { Size v; // v istanza di Size v.height = 100; // ok CSize r; // r è un reference r.height = 100; // illegale, r non assegnato r = new CSize(); // r fa riferimento a un CSize r.height = 100; // ok, r inizializzata }
v.height
v.width
r Stack height width Heap Class CSize
Equivalenza e identità
Il confronto tra oggetti può essere: di equivalenza
Object.Equals
: oggetti con stesso tipo e uguale contenuto di identità
Object.ReferenceEquals
: stessa istanza o entrambi null
==
: dipende dal tipo (come ReferenceEquals o altro)
Object.GetHashCode
: rappresentazione univoca istanza
r1 height width r2=r1; r2 Stack Heap Class CSize
a b c d
Equivalenza e identità
“Teo” 19 “Ugo” 38
a b c .Equals(d) false true true ==d false false true
“Ugo” 38
Boxing
I tipi value si possono sottoporre a “boxing” per supportare le funzionalità tipiche degli oggetti Un tipo value “boxed” è un clone indipendente Un tipo value “boxed” può tornare ad essere value ( unboxing ) System.Object
è il tipo universale
Stack
i 123 int i = 123; o object o = i; k 123 int j = (int)o;
Boxing
int i = 123; object o = i; int k = (int)o;
Boxing Unboxing Heap
int 123
Conclusioni
Evoluzione della macchina virtuale Si cerca di trovare il miglior compromesso tra sicurezza, flessibilità e prestazioni Non tutto è documentato Scarsa documentazione per quanto riguarda i metadati
Altre Informazioni
Dove posso ottenere maggiori informazioni www.microsoft.com/msdn/italy/studenti www.ugidotnet.org
www.gotdotnet.com
www.ecma-international.org
Developer resources Microsoft Visual Studio.NET
Microsoft .NET Framework SDK Microsoft Developer Network