问题描述
我有一个消息记录到使用文本框在屏幕的应用程序。更新函数使用一些Win32函数,以确保盒自动滚动到结束,除非用户正观看另一行。这里是更新的功能:
I have an application that logs messages to the screen using a TextBox. The update function uses some Win32 functions to ensure that the box automatically scrolls to the end unless the user is viewing another line. Here is the update function:
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设置为假)。增加总是至少两倍我的期望,并(在我的例子),有时甚至更多。
Now the strange thing is that the text box consumes at least double the memory that I would expect it to. For example, when there are ~1MB of messages in the TextBox, the application can consume up to 6MB of memory (in addition to what it uses when the logToScreen is set to false). The increase is always at least double what I expect, and (as in my example) sometimes more.
什么是更奇怪的是,使用:
What is more strange is that using:
this.txtBoxLogging.Clear();
for (int i = 0; i < 3; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
不释放存储器(事实上,它增加了略)。
Does not free the memory (in fact, it increases slightly).
任何想法是把存储会为我记录这些消息?我不认为这有什么做的Win32调用,但我将它彻底。
Any idea where the memory is going as I'm logging these messages? I don't believe it has anything to do with the Win32 calls, but I've included it to be thorough.
编辑:
我得到的第一对夫妇的反应均与如何跟踪内存泄漏,所以我想我应该分享我的方法。我使用的WinDbg和性能监视器的组合来跟踪随着时间的推移内存使用(从几小时到几天)。字节的所有CLR堆的总人数不超过我预期增加,但私人字节的总数不断增加随着越来越多的消息都会被记录。这使得WinDbg的用处不大,因为它是工具(SOS)和命令(dumpheap,gcroot等)都是基于.NET的托管内存。
The first couple of responses I got were related to how to track a memory leak, so I thought I should share my methodology. I used a combination of WinDbg and perfmon to track the memory use over time (from a couple hours to days). The total number of bytes on all CLR heaps does not increase by more than I expect, but the total number of private bytes steadily increases as more messages are logged. This makes WinDbg less useful, as it's tools (sos) and commands (dumpheap, gcroot, etc.) are based on .NET's managed memory.
这可能是为什么GC.Collect的()不能帮助我,因为它是只想找一个在CLR堆上的空闲内存。我的泄漏似乎是在联合国管理的记忆。
This is likely why GC.Collect() can not help me, as it is only looking for free memory on the CLR heap. My leak appears to be in un-managed memory.
推荐答案
你是如何确定的内存使用情况?你不得不看的CLR内存使用情况的应用程序,而不是整个应用程序所使用的系统内存(可以使用性能监视器的那个)。也许你已经在使用监测的正确的方法。
How are you determining the memory usage? You'd have to watch the CLR memory usage for your application, not the memory used by the system for the whole application (you can use Perfmon for that). Perhaps you are already using the correct method of monitoring.
据您正在使用的StringBuilder
内部似乎对我。如果是这样,这可以解释的内存增加一倍,因为这是StringBuilder的内部工作方式。
It seems to me that you are using StringBuilder
internally. If so, that would explain the doubling of the memory, because that's the way StringBuilder works internally.
在 GC.Collect的()
可能无法做任何事情,如果要引用你的对象仍处于范围内,或者如果你的任何code使用静态变量。
The GC.Collect()
may not do anything if the references to your objects are still in scope, or if any of your code uses static variables.
编辑:的
我将离开上面,因为它可能仍然是事实,但我抬头一看 AppendText
的内部。它不追加(即,以一个StringBuilder),相反,它集 SelectedText
属性,该属性没有设置字符串,但发送一个Win32消息(字符串缓存在检索)。
I'll leave the above, because it may still be true, but I looked up AppendText
's internals. It does not append (i.e., to a StringBuilder), instead, it sets the SelectedText
property, which does not set a string but sends a Win32 message (string is cached on retrieval).
由于字符串是不可变的,这意味着,每一个字符串,将有三个副本:一个在调用应用程序,一个在基地的缓存控制
一个在实际的Win32文本框控件。每个字符是两个字节宽。这意味着,任何文本1MB,将消耗的内存6MB(我知道,这是一个有点简单化,但是这基本上是什么,似乎发生了)。
Because strings are immutable, this means, for every string, there will be three copies: one in the calling application, one in the "cache" of the base Control
and one in the actual Win32 textbox control. Each character is two bytes wide. This means that any 1MB of text, will consume 6MB of memory (I know, this is a bit simplistic, but that's basically what seems happening).
编辑2:的不知道这是否会做任何改变,但你可以考虑调用 SendMessage函数
自己。但可以肯定的开始看起来像你需要你自己的滚动算法,以及你自己自行绘制文本框来获取内存了。
EDIT 2: not sure if it'll make any change, but you can consider calling SendMessage
yourself. But it sure starts to look like you'll need your own scrolling algorithm and your own owner-drawn textbox to get the memory down.
这篇关于自动滚动文本框使用更多的内存比预期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!