Różnice między kodem bajtowym MSIL i kodem bajtowym Java?

Odpowiedzi:

75

Po pierwsze, pozwólcie mi powiedzieć, że nie sądzę, aby subtelne różnice między kodem bajtowym Javy a MSIL były czymś, co powinno przeszkadzać początkującym programistom .NET. Oba służą temu samemu celowi, definiując abstrakcyjną maszynę docelową, która jest warstwą nad maszyną fizyczną używaną na końcu.

Kod bajtowy MSIL i Java są bardzo podobne, w rzeczywistości istnieje narzędzie o nazwie Grasshopper, które tłumaczy MSIL na kod bajtowy Java. Byłem częścią zespołu programistów Grasshopper, więc mogę podzielić się odrobiną mojej (wyblakłej) wiedzy. Zwróć uwagę, że przestałem nad tym pracować, gdy pojawił się .NET framework 2.0, więc niektóre z tych rzeczy mogą już nie być prawdziwe (jeśli tak, zostaw komentarz, a ja go poprawię).

  • NET zezwala na typy zdefiniowane przez użytkownika, które mają semantykę wartości dołączoną do zwykłej semantyki odwołań ( struct).
  • .NET obsługuje typy bez znaku, dzięki czemu zestaw instrukcji jest nieco bogatszy.
  • Java zawiera specyfikację wyjątków metod w kodzie bajtowym. Chociaż specyfikacja wyjątku jest zwykle wymuszana tylko przez kompilator, może zostać wymuszona przez maszynę JVM, jeśli używany jest inny program ładujący klasy niż domyślny.
  • .NET typy generyczne są wyrażane w IL, podczas gdy typy generyczne Java używają tylko wymazywania typów .
  • Atrybuty .NET nie mają odpowiednika w Javie (czy to nadal prawda?).
  • .NET enumsto niewiele więcej niż otoki wokół typów całkowitych, podczas gdy Javaenums to prawie pełnoprawne klasy (dzięki Internet Friendowi za komentarz).
  • .NET ma outi refparametry.

Istnieją inne różnice językowe, ale większość z nich nie jest wyrażona na poziomie kodu bajtowego, na przykład, jeśli pamięć obsługuje nie staticwewnętrzne klasy Javy (które nie istnieją w .NET) nie są funkcją kodu bajtowego, kompilator generuje dodatkowy argument do konstruktor klasy wewnętrznej i przekazuje obiekt zewnętrzny. To samo dotyczy wyrażeń lambda .NET.

Motti
źródło
Odnośnie atrybutów - adnotacje Java można ustawić tak, aby pojawiały się również w kodzie bajtowym, więc istnieje odpowiednik.
Dąb
@ Dąb: adnotacje Java pozwalają tylko na przekazywanie danych, podczas gdy atrybuty .NET są klasami w pełni zasilanymi, które mogą mieć logikę i, co najważniejsze, implementować interfejsy.
Fyodor Soikin
Kod bajtowy ma również oddzielne instrukcje powrotu dla każdego typu zwrotu, nie wiem, czy faktycznie pomaga w bezpieczeństwie typów.
Zmywarka Cecil
1
Fakt, że typy wartości w .NET mogą być czasami przydzielane na stosie, ma trywialne znaczenie w porównaniu z faktem, że mają semantykę wartości ; każda lokalizacja pamięci typu wartościowego jest instancją. Z kolei każda lokalizacja pamięci w Javie jest albo prymitywnym, albo rozwiązłym odniesieniem do obiektu; nie ma innych typów.
supercat
1
Zastanawiałeś się, jak porównują wydajność? Czy MSIL jest szybszy do interpretacji niż kod bajtowy np.?
Luke T. O'Brien
23

CIL (właściwa nazwa dla MSIL) i kod bajtowy Java są bardziej takie same niż różne. Jest jednak kilka ważnych różnic:

1) CIL został zaprojektowany od początku, aby służyć jako cel dla wielu języków. W związku z tym obsługuje znacznie bogatszy system typów, w tym typy podpisane i niepodpisane, typy wartości, wskaźniki, właściwości, delegatów, zdarzenia, typy ogólne, system obiektowy z jednym korzeniem i więcej. CIL obsługuje funkcje, które nie są wymagane dla początkowych języków CLR (C # i VB.NET), takie jak funkcje globalne i optymalizacje wywołań końcowych . Dla porównania, kod bajtowy Java został zaprojektowany jako cel dla języka Java i odzwierciedla wiele ograniczeń występujących w samej Javie. Byłoby znacznie trudniej napisać C lub Scheme przy użyciu kodu bajtowego Java.

2) CIL został zaprojektowany w celu łatwej integracji z natywnymi bibliotekami i niezarządzanym kodem

3) Kod bajtowy Java został zaprojektowany do interpretacji lub kompilacji, podczas gdy CIL został zaprojektowany z założeniem tylko kompilacji JIT. To powiedziawszy, początkowa implementacja Mono wykorzystywała interpreter zamiast JIT.

4) CIL został zaprojektowany ( i określony ) tak, aby miał czytelną dla człowieka i zapisywalną formę języka asemblera, która jest odwzorowywana bezpośrednio na postać kodu bajtowego. Uważam, że kod bajtowy Java (jak sama nazwa wskazuje) miał być przeznaczony tylko do odczytu maszynowego. Oczywiście kod bajtowy Javy jest stosunkowo łatwo dekompilowany z powrotem do oryginalnej Javy i, jak pokazano poniżej, można go również „zdemontować”.

Powinienem zauważyć, że JVM (większość z nich) jest bardziej zoptymalizowany niż CLR (którykolwiek z nich). Tak więc nieprzetworzona wydajność może być powodem, aby preferować celowanie w kod bajtowy Java. Jest to jednak szczegół implementacji.

Niektórzy mówią, że kod bajtowy Javy został zaprojektowany jako wieloplatformowy, podczas gdy CIL został zaprojektowany tylko dla systemu Windows. Nie o to chodzi. W środowisku .NET jest kilka izmów „Windows”, ale nie ma ich w CIL.

Jako przykład punktu 4) powyżej, jakiś czas temu napisałem zabawkową Javę do kompilatora CIL. Jeśli zasilasz ten kompilator następującym programem Java:

class Factorial{
    public static void main(String[] a){
    System.out.println(new Fac().ComputeFac(10));
    }
}

class Fac {
    public int ComputeFac(int num){
    int num_aux ;
    if (num < 1)
        num_aux = 1 ;
    else 
        num_aux = num * (this.ComputeFac(num-1)) ;
    return num_aux ;
    }
}

mój kompilator wypluje następujący CIL:

.assembly extern mscorlib { }
.assembly 'Factorial' { .ver  0:0:0:0 }
.class private auto ansi beforefieldinit Factorial extends [mscorlib]System.Object
{
   .method public static default void main (string[] a) cil managed
   {
      .entrypoint
      .maxstack 16
      newobj instance void class Fac::'.ctor'()
      ldc.i4 3
      callvirt instance int32 class Fac::ComputeFac (int32)
      call void class [mscorlib]System.Console::WriteLine(int32)
      ret
   }
}

.class private Fac extends [mscorlib]System.Object
{
   .method public instance default void '.ctor' () cil managed
   {
      ldarg.0
      call instance void object::'.ctor'()
      ret
   }

   .method public int32 ComputeFac(int32 num) cil managed
   {
      .locals init ( int32 num_aux )
      ldarg num
      ldc.i4 1
      clt
      brfalse L1
      ldc.i4 1
      stloc num_aux
      br L2
   L1:
      ldarg num
      ldarg.0
      ldarg num
      ldc.i4 1
      sub
      callvirt instance int32 class Fac::ComputeFac (int32)
      mul
      stloc num_aux
   L2:
      ldloc num_aux
      ret
   }
}

Jest to poprawny program CIL, który można wprowadzić do asemblera CIL, tak jak w ilasm.execelu utworzenia pliku wykonywalnego. Jak widać, CIL jest językiem w pełni czytelnym i zapisywalnym przez człowieka. Możesz łatwo tworzyć prawidłowe programy CIL w dowolnym edytorze tekstu.

Możesz również skompilować powyższy program Java za pomocą javackompilatora, a następnie uruchomić wynikowe pliki klas za pomocą javap„dezasemblera”, aby uzyskać następujące informacje:

class Factorial extends java.lang.Object{
Factorial();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   new #3; //class Fac
   6:   dup
   7:   invokespecial   #4; //Method Fac."<init>":()V
   10:  bipush  10
   12:  invokevirtual   #5; //Method Fac.ComputeFac:(I)I
   15:  invokevirtual   #6; //Method java/io/PrintStream.println:(I)V
   18:  return

}

class Fac extends java.lang.Object{
Fac();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public int ComputeFac(int);
  Code:
   0:   iload_1
   1:   iconst_1
   2:   if_icmpge   10
   5:   iconst_1
   6:   istore_2
   7:   goto    20
   10:  iload_1
   11:  aload_0
   12:  iload_1
   13:  iconst_1
   14:  isub
   15:  invokevirtual   #2; //Method ComputeFac:(I)I
   18:  imul
   19:  istore_2
   20:  iload_2
   21:  ireturn
}

Wynik javapnie jest kompilowalny (o ile mi wiadomo), ale jeśli porównasz go z powyższym wynikiem CIL, zobaczysz, że są one bardzo podobne.

Justin
źródło
2
Okazuje się, że były próby stworzenia czytelnego / zapisywalnego języka asemblera Java. Dwa, które znalazłem, to Jasmin i Java Bytecode Assembler
Justin
2
Napisałem tutaj jeden, który jest znacznie lepszy. W przeciwieństwie do Jasmin, jest przeznaczony do demontażu i ponownego złożenia dowolnego prawidłowego pliku klasowego. github.com/Storyyeller/Krakatau . Myślę, że dokładniejsze byłoby stwierdzenie, że Microsoft dostarcza standardowy asembler, podczas gdy programiści Java muszą tworzyć własne.
Antymon
22

Zasadniczo robią to samo, MSIL to wersja kodu bajtowego Java firmy Microsoft.

Główne różnice wewnętrzne to:

  1. Kod bajtowy został opracowany zarówno do kompilacji, jak i interpretacji, podczas gdy MSIL został opracowany bezpośrednio do kompilacji JIT
  2. MSIL został opracowany w celu obsługi wielu języków (C # i VB.NET itp.) W porównaniu z kodem bajtowym napisanym tylko dla języka Java, w wyniku czego kod bajtowy jest bardziej podobny składniowo do języka Java niż IL do dowolnego konkretnego języka .NET
  3. MSIL ma bardziej wyraźne rozgraniczenie między typami wartości i odwołań

O wiele więcej informacji i szczegółowe porównanie można znaleźć w artykule K John Gough (dokument postscriptowy)

Guy Starbuck
źródło
„1.Kod bajtowy został opracowany zarówno do kompilacji, jak i do interpretacji, podczas gdy MSIL został opracowany specjalnie do kompilacji JIT” - To mówi o tym, jak kod Java jest kompilowany do kodu bajtowego ORAZ ten kod bajtowy jest interpretowany. Mam rację? Czy MSIL nie jest interpretowany w celu wykonania?
Abdul
2

CIL aka MSIL ma być czytelny dla człowieka. Kod bajtowy Java nie jest.

Pomyśl o kodzie bajtowym Java jako o kodzie maszynowym sprzętu, który nie istnieje (ale który emuluje maszyny JVM).

CIL jest bardziej podobny do języka asemblera - jeden krok od kodu maszynowego, a jednocześnie jest czytelny dla człowieka.

szczupły
źródło
Kod bajtowy jest w rzeczywistości bardzo czytelny, o ile masz edytor szesnastkowy. Jest to dość prosty język oparty na stosie z rozszerzeniami do bezpośredniej reprezentacji klas i metod. Myślałem, że MSIL był niższego poziomu (np. Rejestry)?
Daniel Śpiewak
en.wikibooks.org/wiki/ ... en.wikibooks.org/wiki/ ... Jeden z nich to surowy CIL. Drugi to zdemontowany kod bajtowy. Kod bajtowy może być czytelny, jeśli grokujesz hex, ale nie jest to celem projektowym.
slim
„Zdemontowany” to naprawdę niewłaściwe określenie. Może „zdekodowane”. Kod bajtowy jest nieczytelny w plikach .class tylko ze względu na zwartość. W przeciwieństwie do strony podręcznika javap, nie ma dezasemblacji związanej z tworzeniem czytelnego kodu bajtowego ze skompilowanej klasy.
Daniel Spiewak
2

Nie ma tak dużych różnic. Oba są formatami pośrednimi napisanego przez Ciebie kodu. Po uruchomieniu maszyny wirtualne będą wykonywać zarządzany język pośredni, co oznacza, że ​​maszyna wirtualna kontroluje zmienne i wywołania. Jest nawet język, którego w tej chwili nie pamiętam, który może działać na .Net i Javie w ten sam sposób.

Zasadniczo jest to po prostu inny format tego samego

Edycja: Znalazłem język (oprócz Scali): To FAN ( http://www.fandev.org/ ), wygląda bardzo interesująco, ale nie ma jeszcze czasu na ocenę

GHad
źródło
Scala może zostać skompilowana tak, aby była przeznaczona dla JVM lub CLR, generując odpowiednio kod bajtowy lub MSIL.
Daniel Śpiewak
Dobrze wiedzieć, ale jakiś miesiąc temu znalazłem inny język, czytając DZone: Znalazłem go! Zobacz edycję mojego postu
GHad
1

Zgoda, różnice są na tyle niewielkie, że można je przyjąć jako początkujący. Jeśli chcesz nauczyć się .Net zaczynając od podstaw, polecam przyjrzenie się Common Language Infrastructure i Common Type System.

Przyjaciel Internetu
źródło
1

Myślę, że MSIL nie powinien być porównywany z kodem bajtowym Javy, ale „instrukcją zawierającą kody bajtowe Javy”.

Nie ma nazwy zdemontowanego kodu bajtowego Java. „Kod bajtowy Java” powinien być nieoficjalnym aliasem, ponieważ nie mogę znaleźć jego nazwy w oficjalnym dokumencie. Mówi dezasembler klas Java

Wyświetla zdezasemblowany kod, tj. Instrukcje zawierające kody bajtowe języka Java, dla każdej metody w klasie. Są one udokumentowane w specyfikacji wirtualnej maszyny języka Java.

Zarówno „Instrukcje Java VM”, jak i „MSIL” są składane w kodzie bajtowym .NET i kodzie Java, które nie są czytelne dla człowieka.

Dennis C
źródło