U3D开发:Mono和IL2CPP的区别

术语解释

C#

.NET Framework:是Windows的托管环境,可为其运行的应用提供各种服务。

主要包括两个主要组件:公共语言运行时(CLR),它是处理运行应用的执行引擎;.NET Framework类库,它提供开发人员可从其自己的应用中调试的已测试、可重用代码库。

MonoVM各平台运行时库,解决了C#不能跨平台的问题

组成组件:C#编译器、CLI虚拟机,以及核心类别程序库。

Mono的编译器负责生成符合公共语言规范的映射代码,即公共中间语言(Common Intermediate Language,CIL)。

工作流程

1. 通过C#编译器mcs,将C#编译器为IL(中间语言,byte code)

2. 通过Mono运行时中的编译器将IL编译成对应平台的原生码

三种转译方式

即时编译(Just in time,JIT):程序运行过程中,将CIL的byte code转译为目标平台的原生码。
提前编译(Ahead of time,AOT):程序运行之前,将.exe或.dll文件中的CIL的byte code部分转译为目标平台的原生码并且存储,程序运行中仍有部分CIL的byte code需要JIT编译。
完全静态编译(Full ahead of time,Full-AOT):程序运行前,将所有源码编译成目标平台的原生码。

JIT编译

        将IL代码转为对应平台原生码并且将原生码映射到虚拟内存中执行。JIT编译的时候IL是在依托Mono运行时,转为对应的原生码后在依托本地运行。

IL(Intermdediate Language)

        IL的全称是 Intermediate Language,很多时候还会看到CIL特指在.Net平台下的IL标准)。翻译过来就是中间语言。IL就是.net字节码
        它是一种属于通用语言架构和.NET框架的低阶的人类可读的编程语言。
        CIL类似一个面向对象的汇编语言,并且它是完全基于堆栈的,它运行在虚拟机上(.Net Framework, Mono VM)的语言。

IL代码如何生成:不管何种编程语言--->通过自身编译器--->生成IL中间代码

虚拟机是什么:解释执行IL代码的执行环境。游戏运行时--->加载到运行时库(Mono VM)

                        --->动态地编译成汇编代码JIT然后再执行

IL2CPP

解释:在得到中间语言IL后,使用IL2CPP将他们重新变回C++代码,然后再由各个平台的C++编译器直接编译成能执行的原生汇编代码

优点:可移植性强、尺寸小,运行速度快,相比Mono性能提升40%左右。

缺点:转换过程中不好调试、不直观;不支持热更新;

IL2CPP分为两个独立的部分:开发编译工具+runtime的环境库
1. AOT(静态编译)编译器:把IL中间语言(IL的字节码:.net字节码)转换成CPP文件。然后再利用平台的工具,来编译成本地的机器码。

        ex:

        IOS->IL2CPP->IL字节码->cpp代码->xcode来编译,生成本地机器码

        Android>IL2CPP->IL字节码->cpp代码->Android的NDK编译成本地机器码


2. cpp运行时库(也可以说是IL2CPP VM):例如垃圾回收、C#委托与事件、C#多线程的支撑和实现。也就是说,这个是为了支撑C#的语言特性,我们开发的一个运行时候的库,来对C#的一些特性来作支撑。

运行原理:

程序语言--->自身编译器--->IL--->IL2CPP--->C++Code--->Native C++ Compiler---> Native excutable asm ---> IL2CPP VM

Untiy为什么要推出这个技术

1.跨平台移植:如果是Mono--->我们需要每个平台实现mono的虚拟机--->不受unity控制;

2.Mono使用.net也是有版权在这里,受限于Mono;

3.Mono性能问题--->不如CPP直接--->编译成本地的机器码。

4.解决苹果上的编译问题:IOS不支持jit编译,机器码被禁止映射到内存,即封存了内存的可执行权限,变相的封锁了jit编译方式。

IL2CPP是如何处理C#中的反射机制

C#里面有反射(Type t = Type.GetType("TypeName");),但是C++里面没有反射。就需要把C#类型--->编译成C++类、为C++类做一个type描述的类型(类的名字、类的成员的函数指针)--->编译时的时候来生成这样的对象,或者对一个类来注册我们的类型描述

ex:调用反射的时候:对象实例.xxx方法--->获取这个类型描述对象,根据我们的方法的名字,静态转成我们的函数指针。

Func函数指针 = 类的成员--->成员函数的地址;

MethodInfo f = t.getMethodInfo("Test");就是从类的描述里面获取"Test"的函数指针--->编译时就确定的,就可以调用这个函数了。

IL2CPP是如何支持C#的GC的

C#本身就有GC,但是C++没有,需要IL2CPP的runtime库实现这个GC,采用的是标记+清除算法

1.遍历所有的对象,找出对象的引用关系,把这些正在使用的(引用对象)标记出来;

2.清除掉没有标记的对象;

但是这两步都会占用计算资源,我们就会卡住主线程,导致我们瞬间的fps下降,游戏卡了一下。

我们释放内存,还资源给OS的时候,有可能引发内存碎片--->可用内存变小--->就需要用缓存池,自己来定义我们的内存分配的方式,减少内存碎片

IL2CPP如何处理C#中的委托和事件

C#中,先定义一个委托的类型:delegate void OnClick(); OnClick委托变量->里面有n个回调函数(Click += this.click1),触发这个回调调用的时候,里面的n个函数都被触发。

需要定义一个数据结构,对象实例的指针、成员函数->函数指针地址。C++调用这个类型的这个方法,传递this对象,跳到对应的函数地址来执行就可以了。

Native:机器码 

Mono和IL2CPP的区别

        早期,C#是微软的,只会在Windows平台运行。为了解决这个问题,引入了Mono,它是开源跨平台的,其实是在各个平台实现了套Mono的虚拟机,这样你的代码就能运行在各个平台了。Mono的核心原理是,将C#代码,转化成IL 中间代码,然后通过各个平台的Mono虚拟机解释执行,在运行时,解释的过程中,最终转化成了机器码。

        而 IL2CPP,是在各个平台,先将 C# 转化成 C++ 代码,然后通过各个平台的C++编译器直接生成了机器码,也就是在你发包的时候已经是机器码了,所以很快。

        另外,L2CPP 生成出的项目无法反编译,这相当于将C#代码加密了;而 Mono生成出的项目,是可以ILSpy 反编译出来的。

  • 24
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值