我有一个应用程序,使用TextBox将消息记录到屏幕上。更新函数使用一些Win32函数来确保该框自动滚动到末尾,除非用户正在查看另一行。以下是更新函数:
private bool logToScreen = true;
// Constants for extern calls to various scrollbar functions
private const int SB_HORZ = 0x0;
private const int SB_VERT = 0x1;
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
private const int SB_THUMBPOSITION = 4;
private const int SB_BOTTOM = 7;
private const int SB_OFFSET = 13;
[DllImport("user32.dll")]
static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
[DllImport("user32.dll")]
static extern bool GetScrollRange(IntPtr hWnd, int nBar, out int lpMinPos, out int lpMaxPos);
private void LogMessages(string text)
{
if (this.logToScreen)
{
bool bottomFlag = false;
int VSmin;
int VSmax;
int sbOffset;
int savedVpos;
// Make sure this is done in the UI thread
if (this.txtBoxLogging.InvokeRequired)
{
this.txtBoxLogging.Invoke(new TextBoxLoggerDelegate(LogMessages), new object[] { text });
}
else
{
// Win32 magic to keep the textbox scrolling to the newest append to the textbox unless
// the user has moved the scrollbox up
sbOffset = (int)((this.txtBoxLogging.ClientSize.Height - SystemInformation.HorizontalScrollBarHeight) / (this.txtBoxLogging.Font.Height));
savedVpos = GetScrollPos(this.txtBoxLogging.Handle, SB_VERT);
GetScrollRange(this.txtBoxLogging.Handle, SB_VERT, out VSmin, out VSmax);
if (savedVpos >= (VSmax - sbOffset - 1))
bottomFlag = true;
this.txtBoxLogging.AppendText(text + Environment.NewLine);
if (bottomFlag)
{
GetScrollRange(this.txtBoxLogging.Handle, SB_VERT, out VSmin, out VSmax);
savedVpos = VSmax - sbOffset;
bottomFlag = false;
}
SetScrollPos(this.txtBoxLogging.Handle, SB_VERT, savedVpos, true);
PostMessageA(this.txtBoxLogging.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * savedVpos, 0);
}
}
}
现在奇怪的是,文本框消耗的内存至少是我预期的两倍。例如,当TextBox中有约1MB的消息时,应用程序可以消耗多达6MB的内存(除了在logToScreen设置为false时使用的内存)。增加的内存总是至少是我所预期的两倍,并且(就像我的示例中一样)有时会更多。
更奇怪的是,使用以下代码:
this.txtBoxLogging.Clear();
for (int i = 0; i < 3; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
该函数不释放内存(实际上会略微增加内存占用)。
有没有想法,当我记录这些消息时内存去了哪里?我不认为这与Win32调用有任何关系,但我包括它以保证全面性。
编辑:
我得到的前几个回复与如何跟踪内存泄漏有关,因此我认为我应该分享我的方法。我使用WinDbg和perfmon的组合来跟踪随时间推移的内存使用情况(从几个小时到几天)。所有CLR堆上的字节数不会超出预期增加的总数,但是随着记录更多消息,私有字节数总体稳步增加。这使得WinDbg的工具(sos)和命令(dumpheap、gcroot等)基于.NET的管理内存变得不太有用。
这很可能是为什么GC.Collect()无法帮助我,因为它只在CLR堆上寻找可用内存。我的泄漏似乎在非托管内存中。