详解Unity-Profiler内存分析问题

在使用Unity开发游戏的过程中,借助Profiler来分析内存使用状况是至关重要的。但许多开发者可能还对Profiler中各项数据表示的含义不甚明确,今天我们Unity官方的技术工程师柳振东,将针对Profiler内存分析相关的问题及解答,与大家进行分享。

要想完全发挥Profiler内存分析的威力,首先要做的就是了解Profiler展示的数据所表达的含义,以及到底哪些模块所使用的内存才会被统计到Unity的Profiler中。Profiler涉及到的知识点还有很多,今天先从中挑选一些大家常有的疑问来作解答。

在Unity的Profiler中看到的总内存使用和我使用其它工具看到的系统内存占用不太一样,这是为什么呢?

Profiler中看到的内存是通过Unity自身引擎的内存分配,凡是引擎分配和释放的内存,引擎均有记录,所以我们可以给出确切的引擎内存占用信息。但是,仍有其他内存我们是无法获知的。

比如,如果引擎使用了第三方库,那么库分配的内存我们是无法进行统计的。另外,在移动设备上大家看到的内存,其实都要比Profiler大很多,这是因为不管是通过Xcode的Instrument还是通过Android的USS,其记录的真实物理内存都包括两部分,一个是真实使用(Used)的物理内存,另一个则是缓存的(Cached)的物理内存。

这是由系统所决定的,Android和iOS系统在资源不使用时均不会立即将其进行回收,而是将其放在缓存的物理内存中,以便下次再用时,可以快速地加载。当系统发现App的内存不够用时,才会在底层调用一个Memory
Killer线程来轮询缓存物理内存,进而为App清理内存。

而Unity
Profiler记录的则是目前真实使用的物理内存,即上述所说的第一部分。因此,当游戏运行时间越长,Profiler分配内存和通过其他软件获得的系统内存差距会越大。

因此,只要所使用的第三方库不存在内存泄露问题,我们一般都建议只需要查看Profiler即可,只要Profiler中的内存可以保证正常升高和回落,那么引擎这边分配的内存就是没有问题的。

我们做项目的时候发现有时Profiler中System.ExecutableAndDlls这项占用很多,有什么解决办法吗?

“System.ExecutableAndDlls”该项显示的是执行文件和所调用的库(物理、渲染、IO等系统库)的总和。请不用担心该选项的数值,因为很多Application均在共用这些库,并且它对于真实游戏的内存压力非常小,而且也不会导致系统因为该内存来Kill掉游戏。

为什么在Profiler中的Simple模式下,Used Total的数值不等于其右边各项Unity, Mono, GfxDriver,
FMOD与Profiler总和呢?

其实在Unity中,Used Total的计算公式为Used Total = Unity + Mono + GfxDriver + Profiler +
additionalUsedMemory。公式中的additionalUsedMemory项在Profiler中并没有显示,因为这一项一般情况下都为0,只在某些特殊发布平台下才会有数值(一般Android,PC和IOS都为0)。因此一般情况下Used
Total的值就是除FMOD之外各项的总和了。当然,这个规则对于Reserved Total是同样适用的。

我们项目的资源主要使用AssetBundle动态加载资源,发现Profiler中Detailed模式下PersistentManager.Remapper一项占用时多时少,这一项主要是做什么的呢?

Remapper主要提供文件的持久化存储,包括各种序列化的Asset,项目的设置文件等,维护文件系统的中的文件与内存中数据的对应关系。

那么如果项目大量使用AssetBundle的话,在对AssetBundle进行Unload之前都会需要占用Remapper的内存的。而Remapper本身的实现使用内存池,其数值只会增大,那么为了使Remapper占用的内存保持在一个稳定的数值上,我们需要每次在加载一定数量的AssetBundle之后进行Unload操作,而不要一次性把所有AssetBundle都加载后才调用Unload。(这样的操作对维持整个Mono
Heap的大小也是至关重要的,因为Mono Heap本身也是只增大不减小的。)

我们在Editor中调试项目的时候发现纹理的内存大小是其本身大小的两倍,是因为Unity把内存和显存的大小都计算进去了吗?

其实并不是这个原因,因为Editor本身会保有纹理的一份内存,在Editor下进行Profiler会把Editor本身所使用的纹理大小也计算进去,因此会有内存变为两倍的情况。我们官方并不建议在Editor下对项目进行性能调试,而是务必要在真机上跑编译好的项目,然后连接Profiler进行调试,只有这样才能得到真正精确的测试数据。

总结 :一般情况下只需Unity
Profiler在手,就能窥测到游戏中各项内存使用状况,当然前提是需要对Profiler各项数据所表达的含义了然于心。并且尽量使用真机调试结合Profiler,这样才能了解到游戏运行的真实内存占用数据。

Unity3D如何减少安装包大小

译官方文档:http://docs.unity3d.com/Manual/ReducingFilesize.html

PDF文档:http://www.rukawa.cn/Uploads/Attachment/ReducingFilesize/ReducingFilesize.pdf

原文地址:http://www.rukawa.cn/index.php?s=/home/article/detail/id/27.html

需要这么做的目的和好处就不多说了。

第一步要做的就是:看看哪些文件是最占空间的,那么它们就是首选优化对象了。

你可以在刚刚完成一次build之后在“Editor Log”中找到这些信息。

如何打开Editor Log:

在Mac上看起来就是这样的了:

可以看出这份log提供了一份资源总括:各种类型资源的总大小,以及所占百分比。同时还降序列出了单个文件的大小。

顺带一提,资源类型中的“File
headers”它们并不是资源本身,而是加在原始资源上,用来存储“引用”与“配置信息”的额外数据。通常可以忽略这些数据的大小,但如果在你的“Resources”文件夹里有着十分庞大的资源文件的话,这些数据也可能会很大。。

这份log可以帮助你鉴定哪些文件是你或许想要删掉或者进行优化的。

不过在开工之前,还需要先了解几点:

1、Unity再编码会把资源导成它自己的内部格式,所以资源源文件的类型是不相干的。比如你有一个多图层的PS纹理,那么在build之前它就会被拼接、压缩。所以刻意把这份纹理转成PNG格式其实对减少包大小并没有帮助。在开发时还是用最方便的格式就好了。

2、Unity会在build时去掉那些你的项目中没有使用到的资源,所以不需要我们手动找出来删掉了。但是脚本是不会被删掉的(不过它们不占资源),还有“Resources”文件夹里的全部资源,也是不会自动帮你删除的(因为Unity无法判断这里面哪些是需要用到的)。所以我们要确保“Resources”文件夹里的都是我们真正要用到的。当然,你还可以通过动态加载AssetBundles的方式来代替“Resources”文件夹里的资源,以减少包大小。

一些建议:

纹理(Textures)

纹理通常会占据大部分空间。第一步要做的就是选用经压缩的纹理格式(DXT 或者
PVRTC)。如果这样没有减少它的所占用空间,那么试试缩小纹理的尺寸吧。你不需要对资源本身进行修改,只要在 Project 下选中纹理,然后在
Inspectpr 下设置Max Size就行了。

有一个好办法就是:在场景中找到使用了该纹理的object,放大画面,然后一边降低 Max Size
,一边看场景中的object,直到它看起来比较糟糕,就可以知道怎样是最合适的了。。

由以上几张图对比来看,我们就能发现,选择512甚至256的Max Size,效果都是可以的,而且资源大小也能有显著的减少。修改纹理的Max
Size并不会影响到资源本身,只是改变了它在游戏里的分辨率。下表列出了不同图片格式所占用空间的大小(单位bpp:字节/像素)

一张图片的大小计算公式:width * height * bpp,如果你使用的是mipmap贴图,那么其大小将是普通图片的3倍左右。

Unity默认在导入纹理时就会进行压缩,为了在开发时节约时间,我们可以在偏好设置里手动关掉此功能

而在build时,Unity不管你有没有勾选这一项,都会对纹理进行压缩。

网格与动画(Meshes and Animations)

网格与导入的动画(Animation Clips)都可以被压缩。在选中一个模型之后,就可以在Inspector中进行设置了。

不过对它们进行压缩,是可能造成误差的。所以最好先弄清楚什么程度的压缩是可接受范围内的。另外,mesh的压缩仅仅是减少数据文件的大小,并不会减少运行时的内存消耗。而减少动画关键帧,则会让两者都有所减少,一般情况下我们都应该开启。

动态链接库(DLLs)

默认情况下,Unity只会在build时包含以下dll:

我们应该避免对 System.dll 或者 System.Xml.dll 有所引用,否则还是会在build时包含进来,而它们也会占用数M的空间。

如果在游戏中确实需要解析XML,那么可以使用“Mono.Xml.zip”来代替系统级的dll。此外,大多数泛型容器都已经包含在标准库中,只有少数几个在System.dll里,所以可能的话,也应该避免使用到它。

减少手机上使用的 .NET 库的大小

Unity为移动设备提供了两套 .NET 的API:.NET 2.0 和.NET 2.0 Subset

.NET 2.0
提供了几乎整套的API功能,但是很多时候游戏都用不上那么多,导致大量多余的代码占用了宝贵的空间资源。为了避免浪费,我们就可以用Unity提供的 .NET
2.0 Subset(相当于.NET
2.0的一个子集)。为节省资源,这里面很多一般用不到的例程库都被移除了,所以这一优化也会是很有用的,只是需要确保我们的代码能够正常工作。

可在“Player Settings”中进行设置

移除无用的代码

目前我们的项目比较适合使用Strip ByteCode选项,并配合link.xml使用,link.xml放到项目的Assetes目录下:

对于android平台,如果包实在太大,可以使用 Split Application Binary 功能

在Player Setting > Publishing Settings里,

关于Split Application Binary<http://www.ceeger.com/Manual/android-
OBBsupport.html>

unity图片压缩格式和内存计算

例子1:使用RGBA 32bit真彩(Truecolor),占用内存 = 4Bytes512512 = 1MB;

例子2:使用RGB ETC 4bit压缩,占用内存 = 0.5Bytes512512 = 128KB

一、2的N次方大小的图片会得到引擎更大的支持,包括压缩比率,内存消耗,打包压缩大小,而且支持的力度非常大。

二、减小图片的占用大小和内存方式有:图片大小变化(Maxsize),色彩位数变化(16位,32位),压缩(PVRC)。

三、U3D对于图片的格式是自己生成的,而并不是你给他什么格式,他就用什么格式,一张10241024图在无压缩格式下,它会被U3D以无压缩文件形式存放,也就是说U3D里的Texture
Preview里显示的占用大小*
MB不只是内存占用大小,还是空间占用大小

U3D的内部机制为自动生成图片类型来替换我们给的图片,在图片的压缩方式上需要进行谨慎的选择。

压缩格式在U3D的Component Reference里有介绍我就不再详细介绍,只介绍几个重点的:

RGBA32格式为无压缩最保真格式,但也是最浪费内存和空间的格式。Automatic Turecolor和它一个意思。

RGBA16格式为无压缩16位格式,比32位节省一半的空间和内存。Automatic 16bits和它一个意思。

RGBA Compressed PVRTC
4bits格式为PVRTC图片格式,它相当于把图片更改了压缩方式新生成了一个图片来替换原来的我们给的图片格式(比如我们给的是PNG格式)。

注意:U3D所有图片的压缩格式都会以另一种方式来存储,不会以你给的方式来存储,只有你指定了某种格式,它才会转成你要的格式。而且压缩格式在Android里并不一定有效,因为Android的机型多,GPU的渲染方式也不一样,有的是Nvidia,有的是PowerVR,最最好的在安卓机子上启用RGBA16方式,因为这个是适应所有机型的,并且比32位占用量少一半,但也需要因项目而异,只是推荐使用的格式,可以多用,但要权衡内存和显示效果。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×