Memory Usage and Resource Disposal
Desktop applications often run all day, and every document they open, merge or convert passes through the memory of one process. TX Text Control releases what an instance holds when the instance is disposed, so most memory problems come from instances that are never disposed, and on Windows those show up as a limit on handles before they show up as a lack of memory. Find out what each part of TX Text Control keeps, how to release it and how to watch for instances your code leaves behind.
What Holds Memory
| Part | Holds | Released when |
|---|---|---|
A TextControl on a form | Its document, the undo buffer and what it renders for the screen | The control is disposed, together with its form |
A ServerTextControl | Its document, without undo buffer and without rendering | Your code disposes it |
A MailMerge | No document of its own; it works on the document of its TextComponent | Your code or the form disposes it |
| Byte arrays and copies your code keeps | Documents in the TX Text Control format, PDF files and other data | Your code removes the last reference |
Each TextControl has its own document: a form with two editors keeps two independent documents. A ServerTextControl keeps no history for undo and redo and renders no images, which is why Text Control names a smaller memory footprint as one of its advantages over the visible control. Work the user doesn't see belongs there, as Using ServerTextControl in Desktop Applications describes.
Dispose Every ServerTextControl
A ServerTextControl must be disposed explicitly: create it in a using block, call Create first and check its return value, as Create and Dispose Each Instance shows.
Warning
Undisposed Instances. Instances that are never disposed accumulate until
Createfails. The most common reason for a failingCreateis that no new window handle can be created: Windows limits a process to 10,000 USER handles and 10,000 GDI handles. An instance whoseCreatereturnedfalseexists but can't be used; loading a document into it fails with the message "The TXTextControl object must have been completely loaded to use this method." Text Control reports that such failures often appear after an application has run for several days, and sporadically, and that the cause is usually instances that aren't disposed. Restarting the application releases the handles, but disposing the instances is the fix.
The same rule applies to a MailMerge that your code creates: it is a component, and a using block disposes it, as the samples on Mail Merge Performance do.
Count Instances and Handles
Text Control's diagnostics sample counts the ServerTextControl instances that aren't disposed yet, together with the USER and GDI handles of the process. The following class is based on it. Add it to your project as ServerTextControlFactory.cs:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using TXTextControl;
// Creates ServerTextControl instances and counts those that aren't disposed yet
public static class ServerTextControlFactory
{
private static int liveInstances;
[]
private static extern int GetGuiResources(IntPtr hProcess, int uiFlags);
public static int LiveInstances => liveInstances;
// 0 counts GDI objects, 1 counts USER objects
public static int GdiHandles => GetGuiResources(Process.GetCurrentProcess().Handle, 0);
public static int UserHandles => GetGuiResources(Process.GetCurrentProcess().Handle, 1);
public static ServerTextControl Create()
{
ServerTextControl tx = new ServerTextControl();
if (!tx.Create())
{
tx.Dispose();
throw new InvalidOperationException("ServerTextControl could not be created.");
}
Interlocked.Increment(ref liveInstances);
tx.Disposed += (sender, e) => Interlocked.Decrement(ref liveInstances);
return tx;
}
}
Create instances through the factory, and show the counters, for example in a Label named statusLabel after each task:
private void convertButton_Click(object sender, EventArgs e)
{
using (ServerTextControl tx = ServerTextControlFactory.Create())
{
tx.Load("contract.docx", StreamType.WordprocessingML);
tx.Save("contract.pdf", StreamType.AdobePDF);
}
statusLabel.Text = $"Instances: {ServerTextControlFactory.LiveInstances}, " +
$"USER handles: {ServerTextControlFactory.UserHandles}, " +
$"GDI handles: {ServerTextControlFactory.GdiHandles}";
}
The code works as follows:
ServerTextControlis a component and raises theDisposedevent when it's disposed. The handler decreases the counter;Interlockedkeeps it correct when several threads create instances.GetGuiResourcesfrom User32.dll returns the number of GDI or USER objects of a process, the two counts Windows limits.- After the
usingblock, the instance counter is back where it was. A counter or a handle count that grows while the application runs points to code that doesn't dispose its instances.
The blog article Diagnostics: When Calling ServerTextControl.Create() Failed contains the complete sample, which also keeps a list of the live instances and can dispose all of them for debugging.
Keep the Undo Buffer Small
The editor records every change for undo and redo. AllowUndo switches this off; the reference recommends it when a TextControl is used for tasks such as generating reports, to save time and memory. Switch it on again before the user edits. After your code has loaded or created a document, ClearUndo empties the buffer. Undo and Redo shows both.
When Memory Runs Out
- Errors while loading or saving. A FilterException with the Reason
OutOfMemorymeans that TX Text Control couldn't allocate enough memory, andFileTooBigthat the input file is too big. Exceptions and Error Handling shows how to catch them. - 32-bit processes. A 64-bit application has more memory available and can open and save much larger documents, as 32-bit and 64-bit Applications explains.
- Copies your code keeps. A byte array with a document stays in memory as long as your code refers to it, for example in a list of recent documents. Keep such copies only as long as you need them, or store them in a file or a database.
Next Steps
- Large Documents — build and process long documents efficiently.
- UI Threading and Background Operations — one
ServerTextControlper thread. - Using ServerTextControl in Desktop Applications — create and dispose instances.
- Performance Best Practices — all measures at a glance.