我在用C#编写的XNA 4.0应用程序中发现内存泄漏。该程序需要运行很长时间(天),但它会在几个小时的时间内耗尽内存并崩溃。打开任务管理器并观察内存占用,每隔一秒20-30 KB的内存分配给我的程序,直到它用完为止。我相信当我设置BasicEffect.Texture
属性时会发生内存泄漏,因为这是最终抛出OutOfMemory
异常的语句。
该程序有大约300个大(512px)纹理作为Texture2D对象存储在内存中。纹理不是正方形或甚至是2的幂 - 例如可以是512x431 - 一边总是512px。这些对象仅在初始化时创建,因此我相信它不是由动态创建/销毁Texture2D对象引起的。有些界面元素会创建自己的纹理,但只能在构造函数中创建,并且这些界面元素永远不会从程序中删除。
我正在渲染纹理贴图三角形。在使用三角形呈现每个对象之前,我将BasicEffect.Texture
属性设置为已创建的Texture2D
对象,将BasicEffect.TextureEnabled
属性设置为true
。我使用BasicEffect
在每个调用之间应用BasicEffect.CurrentTechnique.Passes[0].Apply()
- 我知道我正在调用Apply()
两倍,但是代码包含在只要Apply()
的任何属性发生变化,就会调用BasicEffect
的助手类。
我在整个应用程序中使用了一个BasicEffect
类,我更改了它的属性,并在每次渲染对象时调用Apply()
。
首先,是否可能更改BasicEffect.Texture
属性并多次调用Apply()
泄漏内存?
其次,这是渲染具有不同纹理的三角形的正确方法吗?例如。使用单个BasicEffect
并更新其属性?
此代码来自帮助程序类,所以我删除了所有的漏洞,只包括相关的XNA调用:
//single BasicEffect object for entire application
BasicEffect effect = new BasicEffect(graphicsDevice);
// loaded from file at initialization (before any Draw() is called)
Texture2D texture1 = new Texture2D("image1.jpg");
Texture2D texture2 = new Texture2D("image2.jpg");
// render object 1
if(effect.Texture != texture1) // effect.Texture eventually throws OutOfMemory exception
effect.Texture = texture1;
effect.CurrentTechnique.Passes[0].Apply();
effect.TextureEnabled = true;
effect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices1, 0, numVertices1, indices1, 0, numTriangles1);
// render object 2
if(effect.Texture != texture2)
effect.Texture = texture2;
effect.CurrentTechnique.Passes[0].Apply();
effect.TextureEnabled = true;
effect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices2, 0, numVertices2, indices2, 0, numTriangles2);
这是一个XNA应用程序,所以每秒60次我调用我的Draw方法,它渲染我所有的各种界面元素。这意味着我可以在每帧100-200个纹理之间绘制,并且绘制的纹理越多,内存耗尽的速度就越快,即使我没有在更新/绘制循环中的任何位置调用new
。我对OpenGL比DirectX更有经验,所以很明显在幕后发生的事情正在创造我不知道的非托管内存。
答案 0 :(得分:0)
我唯一能提出的建议就是将纹理分组到地图集而不是逐个。如果您每帧渲染大量纹理,它将加快渲染时间并减少GPU上的负载。这样做的原因是因为GPU不需要交换纹理来经常渲染,这是一项昂贵的操作。我正在使用我对OpenGL的了解,但我的猜测是XNA基于DirectX而我假设它们以类似的方式加载纹理(除非你使用的是Monogame,它允许你使用OpenGL)
那就是说,你没有提供太多信息。内存泄漏可能来自纹理切换,但它也可能来自其他地方。你的大部分记忆都被纹理所占据,这可能就是你在那里而不是在其他地方发生崩溃的原因。我猜这里发生了一些事情:
再一次,如果我对你的代码知之甚少,很难弄清楚这里有什么。但就我个人而言,我有一些建议:
我给你的建议?如果您对OpenGL非常熟悉,而且您正在做的事情相当简单,那么我会查看OpenTK。它是一个瘦的链接器层,它将OpenGL和“端口”转换为C#。所有的命令都是完全相同的,你可以灵活地使用整个.NET库来获得额外的烦恼。
我希望这有帮助!