SharpDX を使用してタイル張りの 2D 画像をレンダリングする .NET 3.5 アプリケーションに取り組んでいます。
テクスチャ (Texture2D) はオンデマンドでキャッシュに読み込まれ、マネージド プールに作成されます。
テクスチャは不要になると破棄され、Dispose() が正しく呼び出されることを確認しました。SharpDX オブジェクト トラッキングは、ファイナライズ中のテクスチャがないことを示します。
問題は、テクスチャによって使用される大量のアンマネージ ヒープ メモリが、破棄後も引き続き予約されることです。このメモリは新しいテクスチャをロードするときに再利用されるため、メモリ リークは発生しません。
ただし、アプリケーションの別の部分でも、新しい画像を処理するためにかなりの量のメモリが必要です。これらのヒープはまだ存在しているため、テクスチャが破棄されても、別のイメージをロードするための十分な連続メモリがありません (数百 MB になる可能性があります)。
を使用してアンマネージ メモリを割り当てるAllocHGlobal
と、結果のヒープ メモリは を呼び出した後に完全に消失しますFreeHGlobal
。
VMMap は、アプリケーションを頻繁に使用した後、管理されていないヒープ (赤) を示します。
この時点で実際にコミットされているのは約 20MB だけですが、管理されていないヒープが約 380MB を占めていることがわかります。
長期的には、アプリケーションは 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;
}
}
}
}
したがって、私の質問は - (どのように) テクスチャが破棄された後、これらのアンマネージ ヒープによって消費されたメモリを再利用できますか?