【问题标题】:What causes Unity Memory Leaks?什么导致 Unity 内存泄漏?
【发布时间】:2018-03-16 08:18:28
【问题描述】:

我在使用 Unity 时出现内存泄漏,代码如下:

行为:在运行时,Unity 最终使用 > 64GB 的 RAM 并崩溃(这就是这台机器的全部)

场景:我的场景中只有一架飞机,代码如下。

我该如何解决这个问题?我不认为我想要一个弱引用,(或者至少在 C++ 中我不会在这种情况下使用 weak_ptr,也许是 unique_ptr)。我只想在对象超出范围时释放内存。

void Start () {
    frameNumber_ = 0;

    // commented code leaks memory even if update() is empty
    //WebCamDevice[] devices = WebCamTexture.devices;
    //deviceName = devices[0].name;
    //wct = new WebCamTexture(deviceName, 1280, 720, 30);
    //GetComponent<Renderer>().material.mainTexture = wct;
    //wct.Play();

    wct = new WebCamTexture(WebCamTexture.devices[0].name, 1280, 720, 30);
    Renderer renderer = GetComponent<Renderer>();
    renderer.material.mainTexture = wct;
    wct.Play();
}

// Update is called once per frame
// This code in update() also leaks memory
void Update () {
    if (wct.didUpdateThisFrame == false)
        return;

    ++frameNumber_;

    Texture2D frame = new Texture2D(wct.width, wct.height);
    frame.SetPixels(wct.GetPixels());
    frame.Apply();

    return;

【问题讨论】:

  • 我不擅长团结,但我已经看到了一些可疑的东西。您正在创建新的Texture2D 每一帧。那肯定会堆积垃圾

标签: c# unity3d


【解决方案1】:

看这个:

Texture2D frame = new Texture2D(wct.width, wct.height);

您几乎在每一帧或didUpdateThisFrame 为真时都在创建新的Texture2D。这是非常昂贵,而且大部分时间甚至没有被 Unity 释放。您只需要创建一个Texture2D,然后在Update 函数中重新使用它。将 frame 变量设为全局变量,然后在 Start 函数中将其初始化一次。


这样做,当相机图像高度/宽度发生变化导致frame.SetPixels(wct.GetPixels()); 失败并出现异常时,您可能会遇到新问题。要解决此问题,请检查相机纹理大小何时更改,然后自动调整 frame 纹理的大小。此项检查应在 frame.SetPixels(wct.GetPixels()); 之前的 Update 函数中完成。

if (frame.width != wct.width || frame.height != wct.height)
{
    frame.Resize(wct.width, wct.height);
}

您的最终代码应如下所示:

int frameNumber_ = 0;
WebCamTexture wct;
Texture2D frame;

void Start()
{
    frameNumber_ = 0;

    wct = new WebCamTexture(WebCamTexture.devices[0].name, 1280, 720, 30);
    Renderer renderer = GetComponent<Renderer>();
    renderer.material.mainTexture = wct;
    wct.Play();

    frame = new Texture2D(wct.width, wct.height);
}

// Update is called once per frame
// This code in update() also leaks memory
void Update()
{
    if (wct.didUpdateThisFrame == false)
        return;

    ++frameNumber_;

    //Check when camera texture size changes then resize your frame too
    if (frame.width != wct.width || frame.height != wct.height)
    {
        frame.Resize(wct.width, wct.height);
    }

    frame.SetPixels(wct.GetPixels());
    frame.Apply();
}

【讨论】:

  • 这对我有用,但是,我确实觉得很奇怪,统一在超出范围时不会释放纹理。托管语言的意义不是不必担心内存分配成本吗? (在 C++ 中,您可以使用自定义内存分配器,例如带有 std::vector 的内存池,分配内存变得非常便宜)
  • 这是一个纹理,而不仅仅是一个简单的对象。 Unity 创建和释放纹理的成本非常高。你有责任做这两件事。我认为Resources.UnloadUnusedAssets() 和DestroyImmediate 应该有助于释放纹理。如果 Unity 为您执行此操作,则游戏可能会由于 GC 打嗝而变得如此缓慢或偶尔卡住。最好把它留给程序员。如果他们没有这样做,您就不会知道每帧都创建新纹理是个坏主意,但是当您完成游戏制作后,您会意识到在移动设备上玩是不可能的。
  • @aCuria 托管语言仅自动释放托管资源。 存在非托管资源(例如此纹理或 Web 套接字等)的原因有很多,包括与非托管代码的向后兼容性,例如 C++ DirectX 库。 CLR 不能有效地管理这些资源,因此释放内存的决定留给了开发人员——他们需要了解什么时候这样做是合适的。 .NET 提供了IDisposable 接口来表示此类资源,但 Unity 尚未为此类实现该接口。
  • 你怎么知道一个对象是否被管理? C# 似乎没有表明这一点 - 您在所有非 POD 类型上使用“new”,因此不清楚您对生命周期负责的对象?
【解决方案2】:

古老的话题,但我发现了一些有趣的东西。我曾经在我的代码中有这个:

            downloadedTexture = DownloadHandlerTexture.GetContent(www);
        m_PictureScreen.GetComponent<RawImage>().texture = downloadedTexture;

这导致了内存泄漏的巨大问题,应用程序每次迭代都会超过 8gb。我的情况是,每天可能会增加数千个文件。 我之前通过使用此行解决了它:

Texture2D.Destroy(m_PictureScreen.GetComponent<RawImage>().texture);

不确定它是如何工作的,而且现在不存在内存泄漏。

【讨论】:

    猜你喜欢
    • 2021-11-12
    • 2013-07-12
    • 1970-01-01
    • 1970-01-01
    • 2017-02-13
    • 1970-01-01
    • 1970-01-01
    • 2018-02-04
    相关资源
    最近更新 更多