Я работаю над приложением .NET 3.5, которое использует SharpDX для рендеринга черепичных 2D-изображений.Осколок памяти SharpDX
Текстуры (Texture2D) загружаются в кеш по требованию и создаются в управляемом пуле.
Текстуры удаляются, когда больше не требуется, и я проверил, что Dispose() вызывается правильно. Отслеживание объектов SharpDX указывает, что текстуры не завершены.
Проблема заключается в том, что большое количество неуправляемой памяти кучи, используемой текстурами, продолжает сохраняться после удаления. Эта память используется повторно при загрузке новой текстуры, поэтому память не просачивается.
Однако в другой части приложения также требуются значительные куски памяти для обработки новых изображений. Поскольку эти кучи все еще присутствуют, хотя текстуры были расположены, недостаточно загруженной памяти для загрузки другого изображения (может быть сотни МБ).
Если я выделил неуправляемый мясник, используя AllocHGlobal
, итоговая память кучи полностью исчезнет после звонка FreeHGlobal
.
VMMap показывает неуправляемую кучу (красный) после интенсивного использования приложения.
Мы видим здесь, что неуправляемые счета кучи для ~ 380MB, даже если только ~ 20MB действительно совершается в этой точке.
Долгосрочное приложение переносится на 64-разрядный. Однако это не является тривиальным из-за неуправляемых зависимостей. Кроме того, не все пользователи находятся на 64-битных машинах.
EDIT: Я собрал демонстрацию проблемы - создайте приложение WinForms и установите SharpDX 2.6.3 через Nuget.
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, 0, 1, Usage.None, Format.Unknown, Pool.Managed, Filter.Point, Filter.None, 0);' Где поток в настоящее время всегда MemoryStream. –
Я добавил упрощенный образец кода, который воспроизводит проблему. –