资源管理
Q1:我想请教一下,下图这个函数中,每次我都申请了一个List temp = list();在这里存放6KB的数据,但是如果不做GC处理,这6KB是否就一直累加,直到做GC处理了才会释放掉,是这样么?如果调用次数很多,每次都调用一点点,也会推高内存占用吗?
A:是的,这个6KB堆内存会随着Update的执行一直分配内存,所累积的堆内存会在GC触发时进行销毁。一般来说,研发团队需要尽可能避免在高频次调用函数中进行堆内存的分配。
资源管理
Q2:我的UWA报告中看到几乎每次切换场景都会有UIPanel.LateUpdate()这个函数的堆内存开销,请问说明了什么问题,我是否还能优化?
UIPanel.LateUpdate持续分配较大量的堆内存,说明UI界面在制作上存在以下问题:
- Panel中Widgets数量过多,且存在频繁的变动,导致UIPanel需要进行大量的网格重建;
- 动静态UI元素没有分离。
建议研发团队对UI界面的制作进行进一步检测,尽可能将静态UI元素和动态UI元素分开,存放于不同的Panel下。同时,对于不同频率的动态元素也建议存放于不同的Panel中。
资源管理
Q3:我UWA报告中“渲染模块”界面中看到大部分场景中的三角面片数是正常的,但在某一帧时DrawCall、Traingle、蒙皮网格数骤然提升,请问这可能是什么原因导致的?这三个参数的变化曲线是否有规律?为什么我在切换场景的时候也会有400多的DrawCall呢?
从图中看,这种情况发生在场景切换处。这种情况的发生原因很可能为一次性动态加载大量GameObejct,然后再手动Deactive当前并不需要使用的GameObject。研发团队可以查看该峰值处的场景名称和相关截图,从而来进一步定位发生该问题的根本原因。
资源管理
Q4:关于贴图类型设置请问有什么好的建议呢?是否所有的贴图都设置为Advanced比较好?这种情况下的贴图内存是不是比较小?
Unity默认情况下会将绝大多数纹理设置为Texture模式。一般来说,Texture模式对于绝大多数纹理资源也都是合适的。上面的例子中,Advanced模式下之所以内存占用较小,是因为关闭了Mipmap选项,所以其内存下降了。其本质跟Advanced模式无关。Advanced模式较之Texture模式和其他模式,可以更大自由地对纹理资源进行控制。因此,如果你想对纹理资源进行更加自定义地设置,可以选择Advanced模式进行编辑。
资源管理
Q5:NGUI的图集在内存里存了多份,求问怎么清理?
游戏运行中,UI Mesh出现多份不同内存的情况,是正常的,因为随着UI widget使用的增加或减少,创建的UI Mesh是会随着变化的。同时,如果不同UIPanel中存在相同Atlas的Widgets,则也会出现上图中的情况。因此,建议大家遇到这种情况时,查看单帧中NGUI UI Mesh重名的是否有多份重名资源。如果存在,则说明相同Atlas中的资源被多个不同的UIPanel所使用,这种情况是需要尽可能避免的。
资源管理
Q6:Profiler中Not Saved是指什么?
Profiler中的Not Saved指的是项目中通过代码生成的各种资源记录。如上图所示,其Mesh均为NGUI插件通过脚本生成的UI界面Mesh资源。
资源管理
Q7:我已经下载了Shader,放到Unity里了,理应是不会自己引用,可是为什么还有如下这么多重复引用呢?
1. Mobile/Particles/Additive
2. Particles/Alpha Blended
3. Mobile/Particles/Alpha Blended
4. Default-Material
我们在Unity5.3.4版本上进行了测试,发现了以下两种情况:
.
- 对于Standard Shader来说,仅下载Shader确实是关联不上的。原因是Prefab上还用了Default-Material,这个也是Built-in资源,所以才会出现了Standard Shader的冗余问题。
. 上图可以直接看到这两个资源是同时出现,且均为冗余资源。对此,建议开发团队直接生成一个新的Material来代替Default-Material。
.
- 其他Shader关联不上的原因可能是:新下载Shader的GUID与项目中原始资源关联的GUID不一致,如下图所示。对此,大家需要自行对Meta文件中的相关GUID进行修改,将原始GUID替换为新下载Shader的GUID。最下方为一个批量处理该类问题的脚本代码,感谢QQ讨论群中网友wupei的提供!
.
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;
public class BuildScript {
[MenuItem("Build/RefreshMat", false, 501)]
static void RefreshMat() {
var guids = AssetDatabase.FindAssets("t:Material");
foreach (var guid in guids) {
var path = AssetDatabase.GUIDToAssetPath(guid);
if (path.ToLower().EndsWith("mat")) {
var mat = AssetDatabase.LoadAssetAtPath<Material>(path);
if (mat && mat.shader) {
Debugger.Log("{0}\n{1}\n{2}\n{3}\n", path, mat.shader.name,
mat.shader.GetInstanceID(),
Shader.Find(mat.shader.name).GetInstanceID());
mat.shader = Shader.Find(mat.shader.name);
}
}
}
}
}
资源管理
Q8: 我们测试了下发现,在名为A的MonoBehaviour中,有个数组来存放名为B的 MonoBehaviour对象的引用。当我们其他的逻辑去Destroy了B对象所在的GameObject后,在A对象中的数组里,遍历打印,它们(B的引用)都为Null,在Inspector面板上看是missing。而这时候进行GC,堆内存其实并未释放这些B对象。只有当A对象中的数组被清空后,再调用GC,才可释放这些对象所占内存。这种现象是否正常?为什么值为Null但却还是被引用着,无法通过GC释放呢?
首先这种现象是正常的。这是Unity中对Null的检测做了特殊的处理所致,在Unity中MonoBehaviour对象除了存在于Managed Heap中(作为“壳”),在Native内存中还会有一个相对应的“实体”,在调用Destroy时,真正被释放的正是这个“实体”。而在判断一个MonoBehaviour对象是否为Null时,Unity会首先检测“实体”是否已经被销毁,如果是则返回为true,但此时Managed Heap中的“壳”实际上依然是被引用的,从而就会出现对象的Null判断为true,但实际上还是被引用着,无法被GC释放的问题。
.
相关的细节可见官方blog对Unity中Null判断的解释:
http://blogs.unity3d.com/2014/05/16/custom-operator-should-we-keep-it/
资源管理
Q9:Optimize Game Objects对于老版本的Animation系统有没有作用呢?
没有作用。Optimize Game Objects功能是Unity4.3版本推出的功能,且仅对Mecanim动画系统起作用。如果可以的话,建议研发团队尽可能使用Mecanim来作为项目动画系统的解决方案。
资源管理
Q10:切换场景时,卸载上一个场景总感觉耗时,请问大家是否有什么推荐的方案?
A:这是Unity在切换场景时调用Resources.UnloadUnusedAssets这个函数造成的,通常情况下其开销都是会比较大。建议开发团队通过UWA性能检测,在加载模块中进一步定位卸载场景的元凶。