小记-Unity手动GC,解决资源加载引发内存溢出问题
Published in:2025-07-20 |
Words: 855 | Reading time: 3min |

Unity手动GC,解决资源加载引发内存溢出问题

玩游戏的时候爆内存了,发现罪魁祸首是播放器内存溢出,在此记录一下解决方案 :-)

什么是内存溢出?

内存溢出(OutOfMemoryError,简称OOM)是指程序在申请内存时,系统没有足够的空闲内存空间供其使用,导致程序运行失败的现象。这是软件开发中常见的严重错误之一。

为什么会存在内存溢出?

程序运行需要内存(如创建对象、加载数据),但当发生以下情况时会触发内存溢出:

  1. 内存泄漏(Memory Leak)
    • 程序申请内存后未释放(如未被回收的对象持续累积)。
    • 例如:静态集合类持续添加数据、未关闭数据库连接、未注销事件监听
  2. 数据量过大
    • 一次性加载超大规模数据(如大文件、海量数据库查询结果)。
  3. 内存设置不足
    • 应用所需内存超过JVM堆上限(如未调整默认的JVM堆大小)。
  4. 无限递归/循环
    • 代码缺陷导致无限创建对象或调用栈过深。

本文主要解决的是由于内存泄漏导致的内存溢出问题,而解决这个问题也很简单,在合适的时机手动触发GC,回收掉内存即可很好的避免内存泄漏。


接下来将以我的播放器为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public IEnumerator LoadAndPlayMusic(string path)
{
using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip("file://" + path, AudioType.UNKNOWN))
{
yield return www.SendWebRequest();

if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
{
Debug.LogError("加载音频时发生错误: " + www.error);
}
else
{
audioSource.clip = DownloadHandlerAudioClip.GetContent(www);
audioSource.Play();
isPlaying = true;
Debug.Log("开始播放: " + path);
PlayBarHandel.GetComponent<Animator>().SetTrigger("IntoPlaying");
CtrlButton.SetTrigger("Pause");
}
}
}

这段代码看似没有问题,实际运行下来会发现:

内存泄漏

为什么播放器内存占用会这么大?实际上这就是在读取音频文件后一直没有释放掉导致的

找到问题所在,那么解决起来就很容易了,只需要在每次读取新的音频之前手动GC一次,回收掉所有加载的音频就能解决了,为此封装一个简单的内存回收功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private IEnumerator ReleasePreviousClip()
{
if (audioSource.clip != null)
{
audioSource.Stop();

// 卸载 PCM 数据,释放内存
audioSource.clip.UnloadAudioData();

// 销毁 AudioClip 对象
Destroy(audioSource.clip);
audioSource.clip = null;

// 触发资源回收
var unloadOp = Resources.UnloadUnusedAssets();
yield return unloadOp;

// 触发托管内存回收
System.GC.Collect();
}
}

接下来在每次加载新的音频前调用,回收资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public IEnumerator LoadAndPlayMusic(string path)
{
yield return StartCoroutine(ReleasePreviousClip());
using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip("file://" + path, AudioType.UNKNOWN))
{
yield return www.SendWebRequest();

if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
{
Debug.LogError("加载音频时发生错误: " + www.error);
}
else
{
audioSource.clip = DownloadHandlerAudioClip.GetContent(www);
audioSource.Play();
isPlaying = true;
Debug.Log("开始播放: " + path);
PlayBarHandel.GetComponent<Animator>().SetTrigger("IntoPlaying");
CtrlButton.SetTrigger("Pause");
}
}
}

[!CAUTION]

对于这种通过WWW方式获取的音频资源,在执行回收时会被释放,如果在加载之后再执行回收,会因为刚加载音频资源被回收导致无法播放,因此对于部分方式加载的资源需要格外注意GC的时机,避免加载出错!

修复后

到这里,内存溢出的问题已解决!


Prev:
Introduction to WebSocket-Based Remote Control Application
Next:
Media测试页