Unity资源内存管理--webstream控制
一 使用前提
1,需要使用资源热更新
2,使用Assetbundle资源热更(AssetBundle是产生webstream的元凶)
二 为什么要用AssetBundle
AssetBundle本质上就是一个压缩算法,只不过比起zip等一些压缩多了一些信息,比如平台信息(Ios,android),依赖信息等,既然是压缩,那就很好理解了,AssetBundle就是为了减少包体的大小的。Assets/Resources目录其实也是一样的
三 怎么生成AssetBundle
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
public class PushAndPop
{
[MenuItem("Build/test")]
static void Test()
{
string path1 ="Assets/Resources/UI1.prefab";
List<string> deps1 = GetDepends(path1);
Execute(path1, deps1); string path2 = "Assets/Resources/UI2.prefab";
List<string> deps2 = GetDepends(path2);
Execute(path2, deps2); }
//获取path的依赖资源路径
static List<string> GetDepends(string path)
{
List<string> deps = AssetDatabase.GetDependencies(new string[] { path }).ToList<string>();
return deps;
}
//打包资源path成AssetBundle
static void Execute(string path, List<string> deps)
{
string SavePath = Application.streamingAssetsPath + "/"; BuildAssetBundleOptions buildOp = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets
| BuildAssetBundleOptions.DeterministicAssetBundle; //先打包依赖
BuildPipeline.PushAssetDependencies();
string bundleName = "";
foreach (var dep in deps)
{
Debug.Log(dep);
string ext = System.IO.Path.GetExtension(dep);
if (ext == ".png" || ext==".shader" || ext==".FBX" || ext==".ttf")
{ Object sharedAsset = AssetDatabase.LoadMainAssetAtPath(dep);
bundleName = sharedAsset.name.Replace('/', '_') + ext.Replace('.','_');
BuildPipeline.BuildAssetBundle(sharedAsset, null, SavePath + bundleName + ".assetbundle", buildOp, BuildTarget.StandaloneWindows);
}
} //再打包主资源
BuildPipeline.PushAssetDependencies();
Object mainAsset = AssetDatabase.LoadMainAssetAtPath(path);
bundleName = mainAsset.name.Replace('/', '_');
BuildPipeline.BuildAssetBundle(mainAsset, null, SavePath + mainAsset.name + ".assetbundle", buildOp, BuildTarget.StandaloneWindows);
BuildPipeline.PopAssetDependencies(); BuildPipeline.PopAssetDependencies();
AssetDatabase.Refresh();
} }
实例代码把Resources目录下的UI1和UI2打成了AssetBundle,并把他们放到了StreamingAssets目录,如下

其中myAtlas_png和Unlit_Transparent Colored_shader是UI1和UI2的依赖资源
四 加载AssetBundle并且实例化资源
加载要先依赖再主资源
using UnityEngine;
using System.Collections;
using System.Collections.Generic; public class AssetLoad : MonoBehaviour
{
void OnGUI()
{ //依赖加载按钮
if (GUI.Button(new Rect(0f, 30f, 100f, 20f), "Load Share Res"))
{
StartCoroutine(Load(@"file://" + Application.streamingAssetsPath + "/myAtlas_png.assetbundle"));
StartCoroutine(Load(@"file://" + Application.streamingAssetsPath + "/Unlit_Transparent Colored_shader.assetbundle"));
} if (GUI.Button(new Rect(0f, 60f, 100f, 20f), "Load UI1"))
{
StartCoroutine(LoadAndInstantiate(@"file://"+Application.streamingAssetsPath + "/UI1.assetbundle"));
} if (GUI.Button(new Rect(0f, 90f, 100f, 20f), "Load UI2"))
{
StartCoroutine(LoadAndInstantiate(@"file://" + Application.streamingAssetsPath + "/UI2.assetbundle"));
} } // 加载
IEnumerator Load(string url)
{
WWW www = new WWW(url);
yield return www;
AssetBundle ab = www.assetBundle;
//Object obj = ab.mainAsset;
ab.LoadAll();
Debug.Log("load" + url);
} // 加载并实例化
IEnumerator LoadAndInstantiate(string url)
{
WWW www = new WWW(url);
yield return www; if (!System.String.IsNullOrEmpty(www.error))
{
Debug.Log(www.error);
}
else
{
Object main = www.assetBundle.mainAsset;
GameObject.Instantiate(main);
}
} }
实例用www从StreamingAssets目录加载资源,加载完成后

五 AssetBundle的内存占用
我们先看看加进来的AssetBudle都使用了哪些内存,点击unity的菜单栏Window->Profiler,打开如下窗口

点击到Memory那一栏,在点击Simple旁边的下三角,选择Detailed

点击other,看到下面有一栏WebStream

这就是我们加进来的4个AssetBundle的占用的内存,后面带有各自占用内存
但是还没完,我们点开Assets这一栏

我们看到Texture2D下面有一张我们用到的图片,Shader下面也有

那么问题来了,为什么加进来的AssetBundle占有两处内存(WebStream和Asset),其实也很好理解,我们上面知道AssetBundle和压缩一样,这就好比我们在网站下载了一个zip的压缩文件,如果我们不解压是看不了里面的内容的,这就相当于WebStream下面的内存,Unity是识别不了的。这时候你用压缩软件把zip文件解压了,并且把文件解压在另外的目录,这个解压的操作和unity的ab.loadAll()操作是一样一样的,有木有,有木有,加载出来的Asset资源,Unity就能识别了。
六 AssetBundle内存释放
上面我们说过AssetBundle的内存占用分为WebStream和Asset,那么他们分别是怎么释放的,在什么时候释放,这是个问题
首先,我们释放WebStream的内存,调用ab.Unload(false)函数,可以释放AssetBundle的WebStream的内存
修改Load函数
// 加载
List<AssetBundle> ablist = new List<AssetBundle>();
IEnumerator Load(string url)
{
WWW www = new WWW(url);
yield return www;
AssetBundle ab = www.assetBundle;
//Object obj = ab.mainAsset;
ab.LoadAll();
//缓存ab
ablist.Add(ab);
Debug.Log("load" + url);
} // 加载并实例化
IEnumerator LoadAndInstantiate(string url)
{
WWW www = new WWW(url);
yield return www; if (!System.String.IsNullOrEmpty(www.error))
{
Debug.Log(www.error);
}
else
{
Object main = www.assetBundle.mainAsset;
GameObject.Instantiate(main);
}
//释放WebStream
foreach(var ab in ablist)
{
if(ab != null)
{
ab.Unload(false);
}
}
}
运行我们在看看

只剩下两个ui主资源的Webstream了,把他俩的ab加入释放列表,也是可以释放的。
unload(false)释放了Webstream的内存,我们再看看怎么释放Asset的内存,就是类似Texture2D下的图片内存等
释放Asset的内存使用Resources.UnloadAsset(obj) obj就是LoadAll加载出来的资源
我们再次修改我们的加载函数
// 加载
List<AssetBundle> ablist = new List<AssetBundle>();
List<Object> objlist = new List<Object>();
IEnumerator Load(string url)
{
WWW www = new WWW(url);
yield return www;
AssetBundle ab = www.assetBundle;
//Object obj = ab.mainAsset;
Object[] objs = ab.LoadAll();
//缓存ab
ablist.Add(ab);
objlist.AddRange(objs.ToList<Object>());
Debug.Log("load" + url);
}
添加一个按钮作为释放的操作
if(GUI.Button(new Rect(0f, 120f, 100f, 20f), "Unload"))
{
foreach (var obj in objlist)
{
Resources.UnloadAsset(obj);
}
}
当点击Unload的时候释放了png和shader的Asset内存,我们发现UI也不可见了,所以这个释放操作是在所有引用到这个依赖的GameObject都Destroy的时候调用的,不然会出现资源丢失的情况
七 WebStream的释放时机
我们知道Webstream是用unlaod(false)释放的,但是我们发现,如果UI1和UI2都引用了png,当你实例化UI1后释放png的Webstream,在实例化UI2就不成功了,因为UI2依赖png,所以Webstream和Asset内存一样,也要在所有引用到这个依赖的GameObject都Destroy的时候才能释放。
最后修改的代码如下
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class AssetLoad : MonoBehaviour
{ void OnGUI()
{ //依赖加载按钮
if (GUI.Button(new Rect(0f, 30f, 100f, 20f), "Load Share Res"))
{
StartCoroutine(Load(@"file://" + Application.streamingAssetsPath + "/myAtlas_png.assetbundle"));
StartCoroutine(Load(@"file://" + Application.streamingAssetsPath + "/Unlit_Transparent Colored_shader.assetbundle"));
} if (GUI.Button(new Rect(0f, 60f, 100f, 20f), "Load UI1"))
{
StartCoroutine(LoadAndInstantiate(@"file://"+Application.streamingAssetsPath + "/UI1.assetbundle"));
} if (GUI.Button(new Rect(0f, 90f, 100f, 20f), "Load UI2"))
{
StartCoroutine(LoadAndInstantiate(@"file://" + Application.streamingAssetsPath + "/UI2.assetbundle"));
} if (GUI.Button(new Rect(0f, 120f, 100f, 20f), "Detroy"))
{
GameObject go = GameObject.Find("UI1(Clone)");
GameObject.Destroy(go);
go = GameObject.Find("UI2(Clone)");
GameObject.Destroy(go);
} if (GUI.Button(new Rect(0f, 150f, 100f, 20f), "Unload"))
{
//释放WebStream
foreach (var ab in ablist)
{
if (ab != null)
{
ab.Unload(false);
}
}
//释放Asset
foreach (var obj in objlist)
{
Resources.UnloadAsset(obj);
}
}
} // 加载
List<AssetBundle> ablist = new List<AssetBundle>();
List<Object> objlist = new List<Object>();
IEnumerator Load(string url)
{
WWW www = new WWW(url);
yield return www;
AssetBundle ab = www.assetBundle;
//Object obj = ab.mainAsset;
Object[] objs = ab.LoadAll();
//缓存ab
ablist.Add(ab);
objlist.AddRange(objs.ToList<Object>());
Debug.Log("load" + url);
} // 加载并实例化
IEnumerator LoadAndInstantiate(string url)
{
WWW www = new WWW(url);
yield return www; if (!System.String.IsNullOrEmpty(www.error))
{
Debug.Log(www.error);
}
else
{
AssetBundle ab = www.assetBundle;
ablist.Add(ab);
Object main = ab.mainAsset;
GameObject.Instantiate(main);
} } }
八 游戏项目中的内存解决方案
一般在游戏中,游戏资源很多,依赖关系很更复杂,有可能一个png有很多个GameObject引用,如果不及时的释放没有引用的资源,游戏很可能因为内存不足变得卡顿,甚至闪退,
一个很好的办法就是关联主资源和所有实例化的GameObject,并且给主资源引用的依赖计数,每当有一个主资源引用到依赖,依赖引用计数就+1,每当实例化一个GameObject,主资源的计数+1,当删除一个GamObject的时候,主资源计数-1,当主资源计数为0,主资源依赖计数分别-1,如果依赖的计数为0,释放这个依赖的Webstream和Asset内存。
Unity资源内存管理--webstream控制的更多相关文章
- 从Profile中窥探Unity的内存管理
刨根问底U3D---从Profile中窥探Unity的内存管理 这篇文章包含哪些内容 这篇文章从Unity的Profile组件入手,来探讨一下Unity在开发环境和正式环境中的内存使用发面的一些区别, ...
- 刨根问底U3D---从Profile中窥探Unity的内存管理
这篇文章包含哪些内容 这篇文章从Unity的Profile组件入手,来探讨一下Unity在开发环境和正式环境中的内存使用发面的一些区别, 并且给出了最好控制内存的方法(我想你已经知道了...Prefa ...
- Unity游戏开发中的内存管理_资料
内存是手游的硬伤——Unity游戏Mono内存管理及泄漏http://wetest.qq.com/lab/view/135.html 深入浅出再谈Unity内存泄漏http://wetest.qq.c ...
- 【转载】Unity 优雅地管理资源,减少占用内存,优化游戏
转自:星辰的<Unity3D占用内存太大的解决方法> 最近网友通过网站搜索Unity3D在手机及其他平台下占用内存太大. 这里写下关于Unity3D对于内存的管理与优化. Unity3D ...
- [转]全面理解Unity加载和内存管理
[转]全面理解Unity加载和内存管理 最近一直在和这些内容纠缠,把心得和大家共享一下: Unity里有两种动态加载机制:一是Resources.Load,一是通过AssetBundle,其实两者本质 ...
- Unity 全面理解加载和内存管理
最近一直在和这些内容纠缠,把心得和大家共享一下: Unity里有两种动态加载机制:一是Resources.Load,一是通过AssetBundle,其实两者本质上我理解没有什么区别.Resources ...
- Unity 3D中的内存管理
本文欢迎转载,但烦请保留此行出处信息:http://www.onevcat.com/2012/11/memory-in-unity3d/ Unity3D在内存占用上一直被人诟病,特别是对于面向移动设备 ...
- Unity动态加载和内存管理(三合一)
原址:http://game.ceeger.com/forum/read.php?tid=4394#info 最近一直在和这些内容纠缠,把心得和大家共享一下: Unity里有两种动态加载机制:一是Re ...
- 理解Unity加载和内存管理
转自:http://game.ceeger.com/forum/read.php?tid=4394#info Unity里有两种动态加载机制:一是Resources.Load,一是通过AssetBun ...
随机推荐
- TCGA样本命名详解
在TCGA中,一个患者可能会对应多个样本,如TCGA-A6-6650可以得到3个样本数据: TCGA-A6-6650-01A-11R-1774-07TCGA-A6-6650-01A-11R-A278- ...
- 《Clean Code》阅读笔记
Chapter 2 命名 命名要表现意图 避免歧义和误导,增强区分 命名可读性:便于发音,增强印象,便于交流 命名可查性:增强区分,便于搜索 类和对象的命名:名词或名词短语 方法的命名:动词或动词短 ...
- Unity3d外包公司|UE4外包公司:谷歌首款Daydream VR设备上手
这款售价仅为79美元(约合人民币525元)的产品内含“够用”的手柄和一台头戴设备,只要你有一台支持月日,10月5日,dream平台的手机(未来将成为安卓平台的标配),就能体验VR的乐趣. 即使该产品最 ...
- 蚂蚁金服ATEC城市峰会上海举行,三大发布迎接金融科技2019
2019年1月4日,蚂蚁金服ATEC城市峰会以“数字金融新原力(The New Force of Digital Finance)”为主题在上海举办.稠州银行副行长程杰.蚂蚁金服副总裁刘伟光.蚂蚁金服 ...
- [python]Git
Git 修改默认编辑器 git config –global core.editor vim 提交发生变化得文件 # 提交新文件(new)和被修改(modified)文件,不包括被删除(deleted ...
- erlang下lists模块sort(排序)方法源码解析(二)
上接erlang下lists模块sort(排序)方法源码解析(一),到目前为止,list列表已经被分割成N个列表,而且每个列表的元素是有序的(从大到小) 下面我们重点来看看mergel和rmergel ...
- Python连接MySQL数据库的多种方式
上篇文章分享了windows下载mysql5.7压缩包配置安装mysql 后续可以选择 ①在本地创建一个数据库,使用navicat工具导出远程测试服务器的数据库至本地,用于学习操作,且不影响测试服务器 ...
- SWUST OJ(599)
拉丁方阵 #include <iostream> #include <cstdlib> using namespace std; int main() { int n; cin ...
- MySQL 8 配置文件
包括功能: 端口,是否启用bin log , 指定目录, InnoDB是否启用压缩,MySQL使用旧的密码验证方式. 说明,建表的时候要添加必要的参数才会启用表数据压缩存储,以下为例: CREATE ...
- JavaJ2EE相关知识整理
1.Servlet的生命周期 在Web容器中,Servlet主要经历4个阶段 ①.加载Servlet:当Tomcat第一次访问Servlet的时候,Tomcat会负责创建Servle ...