找工作之余又开始扯淡了,讲内存优化其实跟unity好像并没有半毛钱的关系,纯粹是一些.net框架的事,先将几个概念有助于我们理解unity和.net.Monos就像是一个神奇的酱汁,,混合成Unity平台这个食谱,并且赋予了它跨平台的能力。 Mono是一个开源的项目,基于API(应用程序编程接口),说明书和来自微软.NET框架的通用库工具建立起了它自己(Mono)的框架和库.但是却几乎不能对源代码进行访问。注意,尽管Mono库建立在开源的微软娱乐基础NET类库上,但是它(Mono)完全的兼容了基础的微软库。Mono项目的目标是提供使用NET框架作为通用层,提供一个允许跨平台兼容的框架.它将允许使用一门普通编程语言编写的应用程序,却能运行在许多不同的硬件平台上,包括Linux,OS X,Windows,ARM,个人电脑甚至更多不同设备.Mono也支持许多不同的编程语言,不仅仅是C#,Boo和UnityScripts,甚至是更多我们所熟悉的编程语言都可以支持。 DotNET框架的纯通用中间语言(CLR-稍后描述)也能充分的整合在Mono平台上,这包括C#,但是也包括类似于F#,Java,VB.NET,PythonNet和IronPython。大家可能觉得unity引擎是建立在mono上的,这是一个普遍的错误。mono在一方面并不参与处理某些比如导入游戏资源的任务,比如导入audio,rendering,physics等。所以得出的结论就是unity技术是建立在纯在的c++背后,目的是为了提高运行速度,并且允许用户使用mono作为脚本交口来控制unit引擎。同样mono只不过是unity引擎的一部分而已。一些渲染,动画,资源管理等重要任务当然还是c++来实现的,而mono只是提供一种脚本语言来实现游戏逻辑。本机代码仅仅意味着直接编译目标操作系统的代码,并执行运行时环境的复杂性中不包括额外的层。这使得管理成本较低,但是牺牲了以更直接的方式需要管理内存和其他任务中的代码。脚本语言通常抽象的以自动收集内存垃圾来管理复杂的内存,并且提供各种安全特性,这简化了编程的运行时的开销,有些脚本语言也能动态的运行时解析,这意味着他们不需要被编译之前执行。原始的指令转化为动态的机器码和执行指令的那一刻,他们是在运行时读取的(这里说的可不是编译期)。讲了这么多概念我都觉的自己在扯淡了。给大家补充一下运行环境java运行环境是jre c# vb.net,c++.net运行环境是clr,linux上运行环境是mono,这些就当是个基础知识了解就行。Unity引擎的内存空间在本质上可以分割成三个不同的内存域.每个内存域存储着不同的数据类型和托管着不同的任务。
第一个区域是本地域,这是Unity引擎中基础的基础,这个域是用C++语言编写并且根据目标平台编译成对应的机器码。这个区域负责分配内存空间,比如Asset数据,贴图材质,网格信息等.内存空间分为各种子系统,比如渲染系统,物理系统,输入系统等等.最后,它还包括了本机最重要的代表:游戏对象,比如Gameobject和Component组件. 这区域是很多基础组件保存他们的数据的地方,比如说Transform组件,Rigidbody组件等。
第二个内存区域叫做托管域,这个域是monop平台工作的地方,也是垃圾回收器工作的内存域。任何脚本对象和自定义容器都存储在这片区域。另外它还包括包装为存储在本地域内的同一个对象,这就是mono代码和机器嗲吗之间沟通的桥梁。任何域对于相同的一个实体对象都有属于该域的代表来表示这个实体,但是2个域之间的桥梁过多会造成一些伤害我们游戏的显著的性能。当一个新的游戏对象或者组件被实例化,这个实例化过程将需要内存分配在托管域和本地域。这允许如物理系统和渲染系统等子系统,通过获取本地域的transform数据来控制和渲染对象,而transform组件从我们的脚本代码中获取,仅仅是一种通过桥梁来进入本地内存空间和改变对象位置信息的引用,来回穿越桥梁应该越少越好,就是在改变位置和旋转信息的时候,先用临时变量存储,等发生变化后再对其赋值操作。
最后一个内存域是那些在本地或者外部引用的DLL(动态链接库),比如DirectX,OpenGL,和其他我们导入到项目中的DLL. 从Mono 中的C#代码引用其他DLL将会造成类似于内存空间中本地域和托管域之间的桥梁通信过渡。我们可以通过性能分析器看到所有的内存分配情况。
下面就逐个讲解:
1 我们总是容易忘记数据组织在内存中的重要性,但是恰当的组织好数据,可以得到相当大的性能提升。缓存未命中应该尽可能的避免,这意味着在大多数情况下,数组的数据在内存中是连续的迭代顺序,而不是其他任何迭代的风格。这意味着数据布局对垃圾收集也很重要,因为我们可以节省大量的迭代时间。它以迭代的方式完成,如果我们可以找到垃圾收集器跳过有问题的地方,那么我们可以节省大量的迭代时间。 在本质上,我们希望保持大量的引用类型和大量的值类型直接进行分离,哪怕是在值类型中只夹杂着一个引用类型,比如一个结构中,那么垃圾回收器会遍历整个对象和所有的数据成员,间接可引用对象. 当它到了标记-清除的时候,在此(mark - and - sweep)之前它必须验证对象的所有字段. 但是,如果我们用不同的数组分离了各种不同的类型,那么可以使垃圾收集器跳过大部分数据, 例如,如果我们有一个包含数据的结构体类型数组,那么垃圾回收器需要迭代结构体中的每一个成员变量,这造成了相当多的时间浪费。
public struct MyStruct {
int myInt;
float myFloat;
bool myBool;
string myString;
}
MyStruct[] arrayOfStructs = new MyStruct[1000];
int[] myInts = new int[1000];
float[] myFloats = new float[1000];
bool[] myBools = new bool[1000];
string[] myStrings = new string[1000];
GetComponents<T>(); // (T[])
Mesh.vertices; // (Vector3[])
Camera.allCameras; // (Camera[])
foreach (Transform child in transform) {
// do stuff with 'child'
}
for (int i = 0; i < transform.childCount; ++i) {
Transform child = transform.GetChild(i);
// do stuff with 'child'
}
第一个需求是允许池内对象当需要的时候如何回收本身,下列接口实现了需求:
public interface IPoolableObject
{
void New();
void Respawn();
}
public void WeakRef()
{
MyClass mc=new MyClass();
WeakReference wr=new WeakReference(mc);
mc=null;
if(wr.IsAlive)
{
mc=wr.Target as MyClass;
}
else
{
mc=new MyClass();
}
}
//容量动态增加一倍
a1.add("Three");
5 尽量在子类中重写方法
ToString()是System.Object提供的一个公有的虚方法,.net中任何类型都可继承System.Object类型提供的实现方法,默认为访问类型全路径名称。在自定义类或结构中重写ToString方法,除了可以有效控制输出结果,还能有效减少装箱操作的发生。
6 以is/as模式进行类型兼容性检查。以is和as操作符可以用于判断对象类型的兼容性,以is来实现类型判断,以as实现安全的类型转换是值得推荐的方法。这样能避免不必要的异常抛出。从而实现一种安全,灵活的转换控制。
const是编译时常量,readonly是运行时常量,所以const高效,readonly灵活。在实际的应用中,推荐一static readonly来代替const,及解决const可能引起的程序集引用不一致的问题,还有代来更多的灵活性控制。
淡扯完了,接下给大家分享一本c#的书籍吧,或许对大家有用吧。http://pan.baidu.com/s/1boVkwxx。如果有朋友有问题的话,大家可以加qq讨论,qq:1850761495