我正在使用SharpDX渲染平鋪的2D圖像的.NET 3.5應用程序。SharpDX內存碎片
紋理(Texture2D)按需加載到緩存中,並在管理的池中創建。
紋理在不再需要時被處置,並且我已驗證Dispose()被正確調用。 SharpDX對象跟蹤表明沒有紋理正在定型。
問題在於處理後紋理使用的大量非託管堆內存繼續保留。加載新紋理時會重用此內存,所以內存不會泄漏。
但是,應用程序的另一部分還需要大量的內存來處理新圖像。由於這些堆仍然存在,即使紋理已經處理完畢,也沒有足夠的連續內存來加載另一個圖像(可能爲幾百MB)。
如果我使用AllocHGlobal
分配未管理的存儲,產生的 堆內存在調用FreeHGlobal
後完全消失。
的VMMap顯示應用程序的大量使用後的非託管堆(紅色)。
我們可以在這裏看到非託管堆佔〜380MB,雖然只有大約20MB的實際上是在這一點上犯。
長期來看,應用程序正在移植到64位。但是,由於非託管依賴性,這不是微不足道的。另外,並非所有的用戶都在64位機器上。
編輯:我已經彙總了這個問題的演示 - 創建一個WinForms應用程序並通過Nuget安裝SharpDX 2.6.3。
Form1.cs中:
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:
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;
}
}
}
}
我的問題,因此是 - (怎麼)我可以回收利用後的這些非託管堆消耗的內存紋理已經處理?
你是如何加載紋理? – Luaan
'Texture texture = Texture.FromStream(device,stream,0,1,0,1, Usage.None,Format.Unknown,Pool.Managed,Filter.Point,Filter.None,0);' 其中stream是當前總是一個MemoryStream。 –
我已經添加了一個簡化的代碼示例,它重現了這個問題。 –