Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Differences between MSIL and Java bytecode?

I'm new to .Net and I'm trying to understand the basics first. What is the difference between MSIL and Java bytecode?

like image 291
user18055 Avatar asked Sep 18 '08 18:09

user18055


People also ask

How is bytecode different from MSIL?

They are essentially doing the same thing, MSIL is Microsoft's version of Java bytecode. The main differences internally are: MSIL has more explicit delineation between value and reference types.

What is Java bytecode?

Bytecodes are non-runnable codes that rely on the availability of an interpreter, this is where JVM comes into play. It is a machine-level language code that runs on the JVM. It adds portability to Java which resonates with the saying, “write once, read anywhere”.

What is JVM and bytecode in Java?

Bytecode is the compiled format for Java programs. Once a Java program has been converted to bytecode, it can be transferred across a network and executed by Java Virtual Machine (JVM). Bytecode files generally have a . class extension.


2 Answers

First off let me say that I don't think that the subtle differences between Java bytecode and MSIL is something that should bother a novice .NET developer. They both serve the same purpose of defining an abstract target machine which is a layer above the physical machine being used in the end.

MSIL and Java bytecode are very similar, in fact there is a tool called Grasshopper which translates MSIL to Java bytecode, I was part of the development team for Grasshopper so I can share a bit of my (faded) knowledge. Please note that I stopped working on this around when .NET framework 2.0 came out so some of these things may not be true any more (if so please leave a comment and I'll correct it).

  • .NET allows user defined types that have value semantics as apposed to the regular reference semantics (struct).
  • .NET supports unsigned types, this makes the instruction set a bit richer.
  • Java includes the exception specification of methods in the bytecode. Although exception specification is usually only enforced by the compiler, it may be enforced by the JVM if a class loader other than the default one is used.
  • .NET generics are expressed in IL while Java generics only use type erasure.
  • .NET attributes have no equivalent in Java (is this still true?).
  • .NET enums are not much more than wrappers around integer types while Java enums are pretty much fully fledged classes (thanks to Internet Friend for commenting).
  • .NET has out and ref parameters.

There are other language differences but most of them are not expressed at the byte code level, for example if memory serves Java's non-static inner classes (which do not exist in .NET) are not a bytecode feature, the compiler generates an additional argument to the inner class's constructor and passes the outer object. The same is true for .NET lambda expressions.

like image 145
Motti Avatar answered Oct 06 '22 01:10

Motti


CIL (the proper name for MSIL) and Java bytecode are more the same than they are different. There are some important differences though:

1) CIL was designed from the beginning to serve as a target for multiple languages. As such, it supports a much richer type system including signed and unsigned types, value types, pointers, properties, delegates, events, generics, an object-system with a single root, and more. CIL supports features not required for the initial CLR languages (C# and VB.NET) such as global functions and tail-call optimizations. In comparision, Java bytecode was designed as a target for the Java language and reflects many of the constraints found in Java itself. It would be a lot harder to write C or Scheme using Java bytecode.

2) CIL was designed to integrate easily into native libraries and unmanaged code

3) Java bytecode was designed to be either interpreted or compiled while CIL was designed assuming JIT compilation only. That said, the initial implementation of Mono used an interpreter instead of a JIT.

4) CIL was designed (and specified) to have a human readable and writable assembly language form that maps directly to the bytecode form. I believe that Java bytecode was (as the name implies) meant to be only machine readable. Of course, Java bytecode is relatively easily decompiled back to the original Java and, as shown below, it can also be "disassembled".

I should note that the JVM (most of them) is more highly optimized than the CLR (any of them). So, raw performance might be a reason to prefer targeting Java bytecode. This is an implementation detail though.

Some people say that the Java bytecode was designed to be multi-platform while CIL was designed to be Windows only. This is not the case. There are some "Windows"isms in the .NET framework but there are none in CIL.

As an example of point number 4) above, I wrote a toy Java to CIL compiler a while back. If you feed this compiler the following Java program:

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 ;     } } 

my compiler will spit out the following 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    } } 

This is a valid CIL program that can be fed into a CIL assembler like ilasm.exe to create an executable. As you can see, CIL is a fully human readable and writable language. You can easily create valid CIL programs in any text editor.

You can also compile the Java program above with the javac compiler and then run the resulting class files through the javap "disassembler" to get the following:

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 } 

The javap output is not compilable (to my knowledge) but if you compare it to the CIL output above you can see that the two are very similar.

like image 35
Justin Avatar answered Oct 06 '22 01:10

Justin