欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

java中间语言汇编语言_中间语言(IL) | 学步园

发布时间:2025/3/8 37 豆豆
生活随笔 收集整理的这篇文章主要介绍了 java中间语言汇编语言_中间语言(IL) | 学步园 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

一、IL与汇编语言

IL是微软.NET平台上衍生出的一门中间语言,.NET平台上的各种高级语言(如C#,VB,F#)的编译器会将各自的代码转化为IL。,其中包含了.NET平台上的各种元素,如“范型”,“类”、、“接口”、“模块”、“属性”等等。值得注意的是,各种高级语言本身可能根本没有这些“概念”在里头,如IronScheme是一个在.NET平台上的Scheme语言实现,其中根本没有前面提到的这些IL——亦或说是.NET平台上的名词。IL本身并不知道自己是由哪种高级语言转化而来的,哪种语言中有哪些特性,IL也根本不会关心。

各种语言的编译器将:高级语言=> IL。

汇编是让CPU直接使用的“语言”,请注意“直接”二字:一条汇编指令便是让CPU作一件事情(如寄存器的复制,从内存中读取数据等等),毫无二义。不同族CPU拥有不同的指令集,但是它们都有一样的特征:指令的数量相对较少,每个指令功能都简单之至。

由于CPU只认识汇编代码(机器码和汇编其实也是一一对应的,您可以这样理解:汇编是机器码的文字表现形式,提供了一些方便人们记忆的“助记符”),因此就算是IL也需要再次进行转化,才能被CPU执行。这次转化便由“JIT Compiler”(即时编译器)完成。CLR加载了IL之后,当每个方法——请注意这是IL中的概念——第一次被执行时,就会使用JIT将IL代码进行编译为机器码。与IL不同的是,CLR,JIT都是真正了解CPU的,对于同样的IL,JIT会把它为不同的CPU架构(如x86/IA64等等)生成不同的机器码。这也是Java/.NET中“Compile Once,Run Everywhere”这一口号的技术基础:它们为不同的CPU架构提供了不同的“IL转化器”,仅此而已。与高级语言到IL的转化类似,CPU也完全不知道自己在执行的指令是从哪里来的,可能是JIT从IL转化而来,可能是JVM从Java Bytecode转化而来,也有可能是C语言编译得来,也有可能是由MIT/GNU Scheme解释而来。

这就是.NET平台上的高级语言在机器上运行的第二次转化:IL =>汇编(机器码)。

因此,IL和汇编的区别是显著的。IL拥有各种高级特性,它知道什么是范型,什么是类和方法(以及它们的“名称”),什么是继承,什么是字符串,布尔值,什么是User对象。而CPU只知道寄存器,地址,内存,01010101。与汇编相比,IL简直太高级了,几乎完全是一个高级语言,比C语言还要高级。因此,您会看到.NET Reflector几乎可以把IL代码“一五一十”地反编译为可读性良好的C#代码,包括类,属性,方法等等;而从汇编只能勉勉强强地反编译为C语言——而且其中的“方法名”等信息已经完全不可恢复了,更别说“模块”等高级抽象的内容。您想要把汇编反编译成C#代码?相信在将来这是可行的,不过现在这还是天方夜谭。

二、IL并不是万能的,CLR还有很多内容IL都无法看到

示例一:探究泛型在某些情况下的性能问题

namespace TestConsole

{

public class MyArrayList

{

public MyArrayList(int length)

{

this.m_items = new object[length];

}

private object[] m_items;

public object this[int index]

{

[MethodImpl(MethodImplOptions.NoInlining)]

get

{

return this.m_items[index];

}

[MethodImpl(MethodImplOptions.NoInlining)]

set

{

this.m_items[index] = value;

}

}

}

public class MyList

{

public MyList(int length)

{

this.m_items = new T[length];

}

private T[] m_items;

public T this[int index]

{

[MethodImpl(MethodImplOptions.NoInlining)]

get

{

return this.m_items[index];

}

[MethodImpl(MethodImplOptions.NoInlining)]

set

{

this.m_items[index] = value;

}

}

}

class Program

{

static void Main(string[] args)

{

MyArrayList arrayList = new MyArrayList(1);

arrayList[0] = arrayList[0] ?? new object();

MyList list = new MyList(1);

list[0] = list[0] ?? new object();

Console.WriteLine("Here comes the testing code.");

var a = arrayList[0];

var b = list[0];

Console.ReadLine();

}

}

}

示例目的是证明“.NET中,就算在使用Object作为泛型类型的时候,也不会比直接使用Object类型性能差”。类MyList泛型容器,类MyArrayList直接使用Object类型的容器。在Main方法中将对MyList和MyArrayList的下标索引进行访问。至此,便出现了一些疑问,为泛型容器使用Object类型,是否比直接使用Object类型性能要差?看MyArrayList.get_Item和MyList.get_Item两个方法的IL代码get操作:

// MyArrayList的get_Item方法

.method public hidebysig specialname instance object get_Item(int32 index) cil managed noinlining

{

.maxstack 8

L_0000: ldarg.0

L_0001: ldfld object[] TestConsole.MyArrayList::m_items

L_0006: ldarg.1

L_0007: ldelem.ref

L_0008: ret

}

// MyList的get_Item方法

.method public hidebysig specialname instance !T get_Item(int32 index) cil managed noinlining

{

.maxstack 8

L_0000: ldarg.0

L_0001: ldfld !0[] TestConsole.MyList`1::m_items

L_0006: ldarg.1

L_0007: ldelem.any !T

L_000c: ret

}

这两个方法的区别只在于红色的两句。我们“默认”ldfld指令的功能在两段代码中产生的效果完全相同(毕竟是相同的指令嘛),但是您觉得ldelem.ref指令和ldelem.any两条指令的效果如何,它们是一样的吗?我们通过查阅一些资料可以了解到说,ldelem.any的作用是加载一个泛型向量或数组中的元素。不过它的性能如何?您能得出结果说,它就和ldelem.ref指令一样吗?

除非您了解到JIT对待这两个指令的具体方式,否则您是无法得出其中性能高低的。因为IL还是过于高级,您看到了一条IL指令,您可以知道它的作用,但是您还是不知道它最终造成了何种结果。您还是无法证明“Object泛型集合的性能不会低于直接存放Object的非泛型集合”。因此,比较MyArrayList.get_Item方法和MyList.get_Item方法的汇编代码,最后得出结果是“毫无二致”。由于汇编代码和机器代码一一对应,因此观察汇编代码就可以完全了解CPU是如何执行这两个方法的。汇编代码一模一样,就意味着CPU对待这两个方法的方式一模一样,它们的性能怎么会有不同呢?

结论:.NET的Object泛型容器的性能不会低于直接使用Object的容器,因为CLR在处理Object泛型的时候,会生成与直接使用Object类型时一模一样的类型,因此性能是不会降低的。但是您是通过学习IL可以了解这些吗?显然不是,如果您只是学习了IL,最终还是要“听别人说”才能知道这些,而即使您不学IL,在“听别人说”了之后您也了解了这些——同时也不会因为不了解IL而变得“易忘”等等。

同样道理,IL的call指令和callvirt指令的区别是什么呢?“别人会告诉你”call指令直接就去调用了那个方法,而callvirt还需要去虚方法表里去“寻找”那个真正的方法;“别人可能还会告诉你”,查找虚方法是靠方法表地址加偏移量;《Essential .NET》还会将方法表的实现结构告诉给你,而这些都是IL不会告诉您的。您就算了解再多IL,也不如“别人告诉你”的这些来得重要。您要了解“别人告诉你”的东西,也不需要了解多少IL。

示例二:只有经过调用的方法才能获得其汇编代码吗?

许多资料都告诉我们,在一个方法被第一次调用之前,它是不会被JIT的。也就是说,直到第一次调用时它才会被转化为机器码。不过,这个真是这样吗?我们还是准备一段简单的C#代码:

总结

以上是生活随笔为你收集整理的java中间语言汇编语言_中间语言(IL) | 学步园的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。