¿Diferencias entre MSIL y el código de bytes de Java?

Respuestas:

75

En primer lugar, déjeme decir que no creo que las sutiles diferencias entre el código de bytes de Java y MSIL sean algo que deba molestar a un desarrollador de .NET novato. Ambos tienen el mismo propósito de definir una máquina objetivo abstracta que es una capa por encima de la máquina física que se utiliza al final.

MSIL y el código de bytes de Java son muy similares, de hecho, hay una herramienta llamada Grasshopper que traduce MSIL al código de bytes de Java, yo era parte del equipo de desarrollo de Grasshopper, así que puedo compartir un poco de mi conocimiento (desvaído). Tenga en cuenta que dejé de trabajar en esto cuando salió .NET framework 2.0, por lo que es posible que algunas de estas cosas ya no sean ciertas (si es así, deje un comentario y lo corregiré).

  • .NET permite tipos definidos por el usuario que tienen una semántica de valor en oposición a la semántica de referencia regular ( struct).
  • .NET admite tipos sin firmar, esto hace que el conjunto de instrucciones sea un poco más rico.
  • Java incluye la especificación de excepción de métodos en el código de bytes. Aunque la especificación de excepciones generalmente solo la aplica el compilador, la JVM puede hacerla cumplir si se usa un cargador de clases diferente al predeterminado.
  • Los genéricos .NET se expresan en IL, mientras que los genéricos de Java solo usan borrado de tipo .
  • Los atributos .NET no tienen equivalente en Java (¿sigue siendo así?).
  • .NET enumsno son mucho más que envoltorios de tipos de enteros, mientras que Javaenums son clases bastante completas (gracias a Internet Friend por sus comentarios).
  • .NET tiene outy refparámetros.

Hay otras diferencias de lenguaje, pero la mayoría de ellas no se expresan a nivel de código de bytes, por ejemplo, si la memoria sirve a las staticclases no internas de Java (que no existen en .NET) no son una característica de código de bytes , el compilador genera un argumento adicional para el constructor de la clase interna y pasa el objeto externo. Lo mismo ocurre con las expresiones lambda de .NET.

Motti
fuente
Con respecto a los atributos, las anotaciones de Java se pueden configurar para que aparezcan también en el código de bytes, por lo que hay un equivalente.
Oak
@Oak: las anotaciones de Java solo permiten pasar datos, mientras que los atributos .NET son clases totalmente potenciadas, que pueden tener lógica y, lo más importante, implementar interfaces.
Fyodor Soikin
Bytecode también tiene instrucciones de devolución separadas para cada tipo de devolución, no sé si realmente ayuda en la seguridad de los tipos.
Cecil Lavavajillas
1
El hecho de que los tipos de valor en .NET a veces puedan asignarse en la pila es de importancia trivial en comparación con el hecho de que tienen semántica de valor ; cada ubicación de almacenamiento de tipo valor es una instancia. Por el contrario, cada ubicación de almacenamiento en Java es una referencia de objeto primitiva o promiscua; no hay otros tipos.
supercat
1
¿Se preguntaba cómo se comparan en términos de rendimiento? ¿Es MSIL más rápido de interpretar que el bytecode, por ejemplo?
Luke T O'Brien
23

CIL (el nombre propio de MSIL) y el código de bytes de Java son más iguales que diferentes. Sin embargo, existen algunas diferencias importantes:

1) CIL fue diseñado desde el principio para servir como un objetivo para múltiples idiomas. Como tal, es compatible con un sistema de tipos mucho más rico que incluye tipos firmados y sin firmar, tipos de valor, punteros, propiedades, delegados, eventos, genéricos, un sistema de objetos con una sola raíz y más. CIL admite funciones que no son necesarias para los lenguajes CLR iniciales (C # y VB.NET), como funciones globales y optimizaciones de llamadas finales . En comparación, el código de bytes de Java se diseñó como un objetivo para el lenguaje Java y refleja muchas de las limitaciones que se encuentran en el propio Java. Sería mucho más difícil escribir C o Scheme usando el código de bytes de Java.

2) CIL fue diseñado para integrarse fácilmente en bibliotecas nativas y código no administrado

3) El código de bytes de Java se diseñó para ser interpretado o compilado, mientras que CIL se diseñó asumiendo únicamente la compilación JIT. Dicho esto, la implementación inicial de Mono utilizó un intérprete en lugar de un JIT.

4) CIL fue diseñado ( y especificado ) para tener una forma de lenguaje ensamblador legible y de escritura que se asigna directamente a la forma de código de bytes. Creo que el código de bytes de Java (como su nombre lo indica) estaba destinado a ser solo legible por máquina. Por supuesto, el código de bytes de Java se descompila con relativa facilidad en el Java original y, como se muestra a continuación, también se puede "desensamblar".

Debo señalar que el JVM (la mayoría de ellos) está más optimizado que el CLR (cualquiera de ellos). Por lo tanto, el rendimiento en bruto podría ser una razón para preferir apuntar al código de bytes de Java. Sin embargo, este es un detalle de implementación.

Algunas personas dicen que el código de bytes de Java fue diseñado para ser multiplataforma, mientras que CIL fue diseñado solo para Windows. Este no es el caso. Hay algunos ismos de "Windows" en el marco .NET pero no hay ninguno en CIL.

Como ejemplo del punto número 4) anterior, escribí un compilador de Java para CIL de juguete hace un tiempo. Si alimenta este compilador con el siguiente programa 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 ;
    }
}

mi compilador escupirá el siguiente 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
   }
}

Este es un programa CIL válido que puede introducirse en un ensamblador CIL como ilasm.exepara crear un ejecutable. Como puede ver, CIL es un lenguaje completamente legible y de escritura. Puede crear fácilmente programas CIL válidos en cualquier editor de texto.

También puede compilar el programa Java anterior con el javaccompilador y luego ejecutar los archivos de clase resultantes a través del javap"desensamblador" para obtener lo siguiente:

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
}

El javapresultado no es compilable (que yo sepa) pero si lo compara con el resultado de CIL anterior, puede ver que los dos son muy similares.

Justin
fuente
2
Resulta que ha habido intentos de crear un lenguaje ensamblador Java legible / escribible por humanos. Dos que he encontrado son Jasmin y Java Bytecode Assembler
Justin
2
He escrito uno aquí que es mucho mejor. A diferencia de Jasmin, está diseñado para poder desmontar y volver a montar cualquier archivo de clase válido. github.com/Storyyeller/Krakatau . Creo que sería más exacto decir que Microsoft proporciona un ensamblador estándar, mientras que los codificadores de Java tienen que crear el suyo propio.
Antimony
22

Básicamente, están haciendo lo mismo, MSIL es la versión de Microsoft del código de bytes de Java.

Las principales diferencias a nivel interno son:

  1. Bytecode se desarrolló tanto para la compilación como para la interpretación, mientras que MSIL se desarrolló explícitamente para la compilación JIT
  2. MSIL se desarrolló para admitir varios lenguajes (C # y VB.NET, etc.) en lugar de Bytecode que se escribe solo para Java, lo que hace que Bytecode sea más similar a Java sintácticamente que IL a cualquier lenguaje .NET específico
  3. MSIL tiene una delimitación más explícita entre los tipos de valor y referencia

Se puede encontrar mucha más información y una comparación detallada en este artículo de K John Gough (documento posdata)

Guy Starbuck
fuente
"1. El código de bytes se desarrolló tanto para la compilación como para la interpretación, mientras que MSIL se desarrolló explícitamente para la compilación JIT". Esto se refiere a cómo el código Java se compila en código de bytes Y ese código de bytes se interpreta. ¿Estoy en lo correcto? ¿MSIL no se interpreta para ejecutarse?
Abdul
2

CIL, también conocido como MSIL, está destinado a ser legible por humanos. El código de bytes de Java no lo es.

Piense en el código de bytes de Java como un código de máquina para hardware que no existe (pero que emulan las JVM).

CIL se parece más al lenguaje ensamblador: un paso del código de máquina, sin dejar de ser legible por humanos.

Delgado
fuente
Bytecode es realmente muy legible siempre que tenga un editor hexadecimal. Es un lenguaje basado en pilas bastante simple con extensiones para la representación directa de clases y métodos. Pensé que MSIL era de nivel inferior (por ejemplo, registros).
Daniel Spiewak
en.wikibooks.org/wiki/… en.wikibooks.org/wiki/… Uno es el CIL sin procesar . El otro es un código de bytes desmontado . El código de bytes puede ser razonablemente legible si asimila hexadecimal, pero ese no es un objetivo de diseño.
delgado
"Desmontado" es realmente la palabra incorrecta. "Descodificado" tal vez. Bytecode es ilegible en los archivos .class simplemente por compacidad. A diferencia de la página de manual de javap, no hay ningún desmontaje involucrado en la producción de un código de bytes legible a partir de una clase compilada.
Daniel Spiewak
2

No hay tantas diferencias. Ambos son formatos intermedios del código que escribió. Cuando se ejecutan, las máquinas virtuales ejecutarán el lenguaje intermedio administrado, lo que significa que la máquina virtual controla las variables y las llamadas. Incluso hay un lenguaje que no recuerdo en este momento que se puede ejecutar en .Net y Java de la misma manera.

Básicamente, es solo otro formato para lo mismo

Editar: Encontré el idioma (además de Scala): Es FAN ( http://www.fandev.org/ ), parece muy interesante, pero aún no hay tiempo para evaluar

GHad
fuente
Scala se puede compilar para apuntar a JVM o CLR, generando bytecode o MSIL respectivamente.
Daniel Spiewak
Es bueno saberlo, pero encontré otro idioma hace aproximadamente un mes cuando leí DZone: ¡Lo encontré! Ver edición de mi publicación
GHad
1

De acuerdo, las diferencias son lo suficientemente pequeñas como para ignorarlas como principiantes. Si desea aprender .Net a partir de los conceptos básicos, le recomiendo que consulte Common Language Infrastructure y Common Type System.

Amigo de Internet
fuente
1

Creo que MSIL no debería compararse con el código de bytes de Java, sino "la instrucción que comprende los códigos de bytes de Java".

No hay ningún nombre de código de bytes Java desensamblado. "Java Bytecode" debería ser un alias no oficial, ya que no puedo encontrar su nombre en el documento oficial. El desensamblador de archivos de clase Java dice

Imprime el código desensamblado, es decir, las instrucciones que comprenden los códigos de bytes de Java, para cada uno de los métodos de la clase. Estos están documentados en la Especificación de máquina virtual de Java.

Tanto las "instrucciones Java VM" como "MSIL" se ensamblan en código de bytes .NET y código Java, que no son legibles por humanos.

Dennis C
fuente