如何在Unity中释放内存? [英] How to release memory in Unity?

查看:109
本文介绍了如何在Unity中释放内存?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看到,当我在 Unity 中每帧 (30fps) 创建网格和纹理时,感觉 Unity 在使用后不会从内存中释放这些数据.

I see that when I create mesh and textures in Unity on each frame (30fps) feels like Unity doesn't release these data from memory after the usage.

这是我的代码

private bool UpdateFrame(int frameIdx)
    {
        bool result = true;
        int readyBuffSize = DecoderAPI.stream_get_ready_buffer_size(m_stream);

        if (m_currMeshFrameIndex != frameIdx
            && readyBuffSize > 0)
        {
            m_currMeshFrameIndex = frameIdx;

            IntPtr frame = DecoderAPI.stream_get_next_frame_obj(m_stream);

            if (frame == IntPtr.Zero)
            {
                result = false;
            }
            else
            {
                long sequentialFrameIdx = DecoderAPI.get_sequential_number(frame);
                DebugMethod("UNITY UpdateFrame", $"readyBuffSize :: {readyBuffSize}");
                DebugMethod("UNITY UpdateFrame", $"sequentialFrameIdx :: {sequentialFrameIdx}");

                Mesh releaseFormer = m_meshFilter.mesh;
                m_meshFilter.mesh = CrteateMesh(frame);
                Texture2D texture = CreateTexture(frame);
                m_meshRenderer.material.SetTexture("_MainTex", texture);

                DecoderAPI.stream_release_frame_obj(m_stream, frame);
                Destroy(releaseFormer);  // does not seem to help: even when there are no more allocations in C++ the process grows endlessly
            }
        }

        return result;
    }

private Mesh CrteateMesh(IntPtr frame)
    {
        Mesh mesh = new Mesh();

        //Vertices***
        int vertexCount = DecoderAPI.frame_get_vertex_count(frame);
        byte[] xyzBytes = new byte[vertexCount * 3 * 4];
        IntPtr xyz = DecoderAPI.frame_get_vertex_xyz(frame);
        Vector3[] vertices = new Vector3[vertexCount];

        GCHandle handle = GCHandle.Alloc(vertices, GCHandleType.Pinned);
        IntPtr pointer = handle.AddrOfPinnedObject();
        Marshal.Copy(xyz, xyzBytes, 0, xyzBytes.Length);
        Marshal.Copy(xyzBytes, 0, pointer, xyzBytes.Length);
        handle.Free();

        mesh.vertices = vertices;
        //***

        //Faces***
        int faceCount = DecoderAPI.frame_face_count(frame);
        int trisArrSize = faceCount * 3;
        int[] tris = new int[trisArrSize];
        IntPtr indices = DecoderAPI.frame_face_indices(frame);
        Marshal.Copy(indices, tris, 0, trisArrSize);
        mesh.triangles = tris;
        //***

        mesh.RecalculateNormals();

        //UV***
        int uvCount = DecoderAPI.frame_get_uv_count(frame);
        IntPtr uvData = DecoderAPI.frame_get_uv_data(frame);
        int uvArrSize = uvCount * 2;
        float[] uvArr = new float[uvArrSize];
        Vector2[] uv = new Vector2[uvCount];
        Marshal.Copy(uvData, uvArr, 0, uvArrSize);

        for (int i = 0; i < uvCount; i++)
        {
            Vector2 result = new Vector2(uvArr[i * 2], uvArr[i * 2 + 1]) * new Vector2(1, -1);
            uv[i] = result;
        }

        mesh.uv = uv;
        //***

        if (vertexCount != uvCount)
        {
            long frameId = DecoderAPI.get_sequential_number(frame);
            DebugMethod("UNITY CrteateMesh", $"HERE : in frame id :: {frameId}, vertexCount : {vertexCount}, uvCount : {uvCount}");
        }

        return mesh;
    }

private Texture2D CreateTexture(IntPtr frame)
    {
        IntPtr textureObj = DecoderAPI.frame_get_texture_obj(frame);
        DecoderAPI.TextureInfo textureInfo = DecoderAPI.texture_get_info(textureObj);

        int width = textureInfo.width;
        int height = textureInfo.height;
        int channels = textureInfo.channels;
        int stride = textureInfo.stride;

        //DecoderAPI.ColorType colorType = textureInfo.color_type;

        IntPtr pixels = textureInfo.pixels;
        Texture2D texture = new Texture2D(width, height, TextureFormat.RGB24, false);
        //Texture2D texture = new Texture2D(width, height, TextureFormat.DXT5, false);
        texture.LoadRawTextureData(pixels, width * channels * height);
        texture.Apply();

        return texture;
    }

所以,我所做的是 - 我为每一帧使用它创建一个网格和纹理,然后我希望 Unity 应该在使用后从内存中释放它们,但没有.好的,我发现这种方法 Destroy(releaseFormer) 应该会有所帮助,但无论如何,我在 TaskManager 中看到的内存无休止地增长...

So, what I do is - I create a mesh and texture for each frame use it and then I expect that Unity should release them from memory after the usage, but no. Ok, I found like this method Destroy(releaseFormer) should help, but anyway it is the same I see in TaskManager that memory grows endlessly...

为了测试,我已经尝试过 ->我开始我的 C++ 代码生成(假设 100 帧)然后我停止它(所以我的 C++ 没有分配任何东西),我仍然看到内存增长到最后.我期望的是 - 好吧,即使 Unity 不释放我不需要的数据,我加载了 100 帧就是这样,为什么内存继续增长?

For test I have tried -> I start my c++ code generate (let's say 100 frames) then I stop it (so my c++ doesn't allocate nothing) and I still see that memory grows up to the end. What I expect is - ok even if Unity doesn't release data that I don't need more, I loaded 100 frames that is it, why memory continues to grow?

问题是 - 如何从内存中释放所有我不需要的帧?

Question is - how to release from memory all that frames that I don't need?

编辑

我已经改变了这个方法,以正确的顺序添加了Destroy

I have changed this method, added Destroy in proper order

private bool UpdateFrame(int frameIdx)
    {
        bool result = true;
        int readyBuffSize = -1;

        if (m_stream != IntPtr.Zero)
        {
            readyBuffSize = DecoderAPI.stream_get_ready_buffer_size(m_stream);
        }

        if (m_currMeshFrameIndex != frameIdx
            && readyBuffSize > 0)
        {
            m_currMeshFrameIndex = frameIdx;

            IntPtr frame = DecoderAPI.stream_get_next_frame_obj(m_stream);

            if (frame == IntPtr.Zero)
            {
                result = false;
            }
            else
            {
                long sequentialFrameIdx = DecoderAPI.frame_get_sequential_number(frame);
                DebugMethod("UNITY UpdateFrame", $"readyBuffSize :: {readyBuffSize}");
                DebugMethod("UNITY UpdateFrame", $"sequentialFrameIdx :: {sequentialFrameIdx}");

                if (m_meshFilter.mesh != null)
                {
                    Destroy(m_meshFilter.mesh);
                }

                m_meshFilter.mesh = CrteateMesh(frame);

                if (m_texture != null)
                {
                    Destroy(m_texture);
                }

                m_texture = CreateTexture(frame);
                m_meshRenderer.material.SetTexture("_MainTex", m_texture);

                if (m_stream != IntPtr.Zero)
                {
                    DecoderAPI.stream_release_frame_obj(m_stream, frame);
                }
            }
        }

        return result;
    }

推荐答案

releaseFormer 是网格吗?您是否尝试对纹理对象本身调用 Destroy?

releaseFormer is the mesh right? Did you try calling Destroy on the texture object itself?

另一个线程建议 Resources.UnloadUnusedAssets()

Another thread suggested Resources.UnloadUnusedAssets()

就我个人而言,我会尝试使用 RenderTexture 来执行此操作,尤其是在纹理大小不经常更改的情况下,尽管可能不适用于您的用例

Personally I'd be trying to do this with a RenderTexture especially if the texture size doesn't change too often, though might not be possible for your use case

这篇关于如何在Unity中释放内存?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆