首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >SharpDX内存碎片

SharpDX内存碎片
EN

Stack Overflow用户
提问于 2015-07-02 13:33:56
回答 1查看 675关注 0票数 2

我正在开发一个.NET 3.5应用程序,它使用SharpDX来呈现平铺的2D图像。

纹理(Texture2D)按需加载到缓存中,并在托管池中创建.

纹理将在不再需要时被处理,并且我已经验证了that ()是否被正确调用。SharpDX对象跟踪表明没有最终确定纹理。

问题是,纹理使用的大量非托管堆内存在处理后继续保留。此内存在加载新纹理时被重用,因此内存不会泄漏。

然而,应用程序的另一部分也需要大量内存来处理新图像。因为这些堆仍然存在,即使已经处理了纹理,也没有足够的连续内存来加载另一个映像(可以是数百MB)。

如果我使用AllocHGlobal分配非托管内存,则生成的堆内存在调用FreeHGlobal后再次完全消失。

VMMap显示应用程序大量使用后的非托管堆(red)。

我们在这里可以看到,非托管堆占了~380 at,尽管此时实际上只提交了~20 at。

从长远来看,该应用程序正在移植到64位。但是,由于非托管依赖关系,这并不简单。而且,不是所有的用户都在64位的机器上。

编辑:--我已经为这个问题做了演示--创建一个WinForms应用程序,并通过Nuget安装SharpDX 2.6.3。

Form1.cs:

代码语言:javascript
运行
复制
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
using SharpDX.Direct3D9;

namespace SharpDXRepro {
    public partial class Form1 : Form {
        private readonly SharpDXRenderer renderer;
        private readonly List<Texture> textures = new List<Texture>();

        public Form1() {
            InitializeComponent();

            renderer = new SharpDXRenderer(this);

            Debugger.Break(); // Check VMMap here

            LoadTextures();

            Debugger.Break(); // Check VMMap here

            DisposeAllTextures();

            Debugger.Break(); // Check VMMap here

            renderer.Dispose();

            Debugger.Break(); // Check VMMap here
        }

        private void LoadTextures() {
            for (int i = 0; i < 1000; i++) {
                textures.Add(renderer.LoadTextureFromFile(@"D:\Image256x256.jpg"));
            }
        }

        private void DisposeAllTextures() {
            foreach (var texture in textures.ToArray()) {
                texture.Dispose();
                textures.Remove(texture);
            }
        }
    }
}

SharpDXRenderer.cs:

代码语言:javascript
运行
复制
using System;
using System.Linq;
using System.Windows.Forms;
using SharpDX.Direct3D9;

namespace SharpDXRepro {
    public class SharpDXRenderer : IDisposable {
        private readonly Control parentControl;

        private Direct3D direct3d;
        private Device device;
        private DeviceType deviceType = DeviceType.Hardware;
        private PresentParameters presentParameters;
        private CreateFlags createFlags = CreateFlags.HardwareVertexProcessing | CreateFlags.Multithreaded;

        public SharpDXRenderer(Control parentControl) {
            this.parentControl = parentControl;

            InitialiseDevice();
        }

        public void InitialiseDevice() {
            direct3d = new Direct3D();
            AdapterInformation defaultAdapter = direct3d.Adapters.First();

            presentParameters = new PresentParameters {
                Windowed = true,
                EnableAutoDepthStencil = true,
                AutoDepthStencilFormat = Format.D16,
                SwapEffect = SwapEffect.Discard,
                PresentationInterval = PresentInterval.One,
                BackBufferWidth = parentControl.ClientSize.Width,
                BackBufferHeight = parentControl.ClientSize.Height,
                BackBufferCount = 1,
                BackBufferFormat = defaultAdapter.CurrentDisplayMode.Format,
            };

            device = new Device(direct3d, direct3d.Adapters[0].Adapter, deviceType,
                parentControl.Handle, createFlags, presentParameters);
        }

        public Texture LoadTextureFromFile(string filename) {
            using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read)) {
                return Texture.FromStream(device, stream, 0, 0, 1, Usage.None, Format.Unknown, Pool.Managed, Filter.Point, Filter.None, 0);
            }
        }

        public void Dispose() {
            if (device != null) {
                device.Dispose();
                device = null;
            }

            if (direct3d != null) {
                direct3d.Dispose();
                direct3d = null;
            }
        }
    }
}

因此,我的问题是-(如何)在纹理被释放之后,我如何回收这些非托管堆所消耗的内存?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-07-03 14:37:38

给出问题的内存似乎是由nVidia驱动程序分配的。据我所知,所有的去分配方法都是正确调用的,所以这可能是驱动程序中的一个bug。环顾互联网,可以看到一些似乎与此相关的问题,尽管这还不够严重,不值得参考。我不能在ATi卡上测试这个(我已经有十年没见过了)。

看来你的选择是:

  • 确保你的纹理足够大,不能在“共享”堆上分配。这使得内存泄漏进行得慢得多--尽管它仍然是未释放的内存,但它不会像您所经历的那样严重地导致内存碎片。你说的是绘制瓷砖--这在历史上是用分蘖做的,这给了你更好的处理能力(尽管它们也有缺点)。在我的测试中,简单地避免微小的纹理几乎消除了这个问题--很难判断它是隐藏的还是完全消失的(两者都是非常可能的)。
  • 在单独的进程中处理处理。您的主应用程序将在需要时启动另一个进程,当助手进程退出时,内存将被正确地回收。当然,这只有在您编写一些处理应用程序时才有意义--如果您正在制作一些实际显示纹理的东西,这不会有帮助(或者至少设置起来会非常棘手)。
  • 不要处理纹理。Managed纹理池为您处理从设备到设备的分页操作,它甚至允许您使用优先级等,以及刷新整个设备上(托管)内存。这意味着纹理将保留在您的进程内存中,但与当前的方法相比,您仍然可以获得更好的内存使用:)
  • 可能,这些问题可能只与DirectX 9上下文有关。您可能需要使用一个较新的接口进行测试,比如DX10或DXGI。这并不一定限制您使用DX10+ GPUs -但您将失去对Windows的支持(无论如何,它不再受支持)。
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31186093

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档