Auto-scrolling-text-Feld verwendet mehr Speicher als erwartet
Ich habe eine Anwendung, die protokolliert Meldungen auf dem Bildschirm mit einem Textfeld. Die update-Funktion verwendet einige Win32-Funktionen, um sicherzustellen, dass die box automatisch einen Bildlauf an das Ende, es sei denn, der Benutzer ist anzeigen eine weitere Zeile. Hier ist die update-Funktion:
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);
}
}
}
Nun ist die seltsame Sache ist, dass das Textfeld verbraucht mindestens das doppelte der Speicher, ich würde es erwarten. Zum Beispiel, wenn dort sind ~1MB von Nachrichten in das Textfeld, kann die Anwendung verbrauchen bis zu 6MB Arbeitsspeicher (zusätzlich zu dem, was es nutzt, wenn die logToScreen ist auf false gesetzt). Die Erhöhung ist mindestens immer das doppelte, was ich erwarte, und (wie in meinem Beispiel), manchmal auch mehr.
Was ist mehr seltsam ist, dass die Verwendung von:
this.txtBoxLogging.Clear();
for (int i = 0; i < 3; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
Nicht den Speicher frei (in der Tat, es ist leicht erhöht).
Irgendeine Idee, wo der Speicher wird, wie ich bin-Protokollierung diese Nachrichten? Ich glaube nicht, dass es hat etwas zu tun mit der Win32-Aufrufe, aber ich habe es gründlich durchgeführt werden.
EDIT:
Den ersten paar Antworten, die ich bekam, waren, wie zu verfolgen ein Speicherleck, so dass ich dachte, ich sollte mein Anteil an Methodik. Ich habe eine Kombination von WinDbg und perfmon verfolgen Sie die Speichernutzung über die Zeit (von einigen Stunden bis zu Tagen). Die Gesamtanzahl der bytes auf alle CLR-Haufen nicht mehr als ich erwarte, aber die Gesamtzahl der privaten bytes, die stetig zunimmt, wenn mehr Nachrichten protokolliert werden. Dies macht WinDbg weniger nützlich, als es die tools (sos) und Befehle (dumpheap, gcroot, etc.) sind basierend auf .NET-verwalteten Speicher.
Dies ist wahrscheinlich, warum GC.Collect() kann mir nicht helfen, da Sie nur auf der Suche nach freien Speicher auf der CLR heap. Mein Leck zu sein scheint in der un-verwalteten Speicher.
- Sie, sir, sind mein Retter! Endlich, TextboxBase Anhängen mit scroll-lock, das funktioniert !
- Interessante Anmerkung: die Verwendung der richTextBox.Text+=str statt richTextBox-Steuerelement.AppendText(str) entfällt das flackern, verursacht aber massive Verlangsamung, wenn der string wird groß
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wie sind Sie mit der Bestimmung der Speichernutzung? Man müsste beobachten Sie die CLR-Speicher-Nutzung für Ihre Anwendung, nicht der Speicher der vom system für die gesamte Anwendung (können Sie Perfmon verwenden, für, die). Vielleicht sind Sie bereits über die richtige Methode der überwachung.
Scheint es mir, dass Sie mit
StringBuilder
intern. Wenn ja, das wäre eine Erklärung für die Verdoppelung des Speichers, denn das ist der Weg, der StringBuilder arbeitet intern.Den
GC.Collect()
kann nichts tun, wenn die Referenzen auf deine Objekte sind noch im Rahmen, oder wenn Sie Ihren code mit statischen Variablen.EDIT:
Ich lasse die oben genannten, denn es kann immer noch wahr sein, aber ich sah
AppendText
's-Interna. Es nicht Anhängen (z.B., ein "StringBuilder"), stattdessen legt es dieSelectedText
Eigenschaft, die nicht einen string, sondern sendet eine Win32-message (string zwischengespeichert wird auf Abruf).Da Zeichenfolgen unveränderlich sind, das heißt, für jede saite, es werden drei Exemplare: eines in der aufrufenden Anwendung, eine im "cache" von der Basis
Control
und eine in der tatsächlichen Win32 textbox-Steuerelement. Jedes Zeichen zwei Byte breit. Dies bedeutet, dass jede 1MB text, verbraucht 6 MB Arbeitsspeicher (ich weiß, das ist ein bisschen vereinfacht, aber das ist im Grunde das, was scheint geschehen).EDIT 2: nicht sicher, ob es irgendwelche änderungen vornehmen, aber Sie können Sie erwägen den Aufruf
SendMessage
selbst. Aber sicher, es beginnt zu schauen, wie Sie benötigen Ihren eigenen scrolling-Algorithmus und Ihre eigenen Besitzer-gezeichnet textbox, um die Speicher nach unten.Tracking, die Menge des Speichers verwendet durch eine Anwendung, insbesondere eine von der garbage Collection eingesammelt Sprache, ist ein schwieriges Unterfangen. Menschen, die oft verwendet den gesamten Speicher zählen, für eine Anwendung zu bestimmen, die Objekte, die noch in Gebrauch (z.B. über task-manager). Dies ist fraglich wirksam für die native Anwendung, sondern wird sehr irreführende Ergebnisse für verwaltete Anwendungen.
Um richtig zu bestimmen, wie viel Speicher von der CLR-Objekte verwenden, benötigen Sie ein tool, das speziell auf die Messung, die. Zum Beispiel, ich finde der beste Weg ist die Verwendung einer Kombination von WinDbg und sos.dll zur Messung der aktuellen verwurzelt Objekte. Dies wird sowohl Ihnen sagen, die Größe der verwalteten Objekte und zeigen, was Objekte eigentlich sind, wobei die zusätzlichen Speicher.
Hier ist ein netter Artikel zu dem Thema