Not enough memory or not enough pens?

I am working on a large scale project where a common (pretty good and robust) framework has been created and we have to use it to render forms and views.


There is an abstract StrategyEditor class (derived from some class in the framework) that is created every time a new StrategyForm is opened.

StrategyForm

(custom window frame) contains StrategyEditor

.
StrategyEditor

contains StrategyTab

.
StrategyTab

contains StrategyCanvas

.

This is a small part of the larger classes just to clarify that there are many objects that will be created if a single StrategyForm object is allocated in memory at runtime. My component owns all of the above classes, except StrategyForm

whose code is not under my control.


Now, at runtime, the user opens many strategy objects (which initiate the creation of a new StrategyForm object.) After creating approx. 44 strategy objects, we can see that the OBJECT USER GUIDE (I will use UOH from here onwards) generated by the application reaches roughly 20k +, whereas in the registry the default value for descriptors is 10k. Learn more about user objects here. Testing on different machines made it clear that the number of open strategy objects is different for pop-up messages - by one m / s, if it is 44, then it can be 40 on another.

When we see a pop-up message, it means that the application will respond slowly. A few more objects are degraded, and then a window box and subsequent objects are created.

At first we thought that this is not something that is a memory problem. But then reading more about new

in C #
it helped to understand that an exception would be thrown if the application ran out of memory. This is not a memory issue, I feel (the task manager also showed 1.5GB + available memory.)


Specifications M / C
Core 2 Duo 2 GHz +
RAM 4 GB
80 GB + free disk space for page file
Virtual memory set: 4000 - 6000


My questions


Q1. This looks like a memory problem and I am wrong that it is not?
Q2. Does this mean a question of running out of free UOH (as I think) and what causes the window handles to fail?
Q3. How can we avoid loading the object StrategyEditor

(over the threshold, keeping track of the current UOH usage)? (We already know how to select the number of UOHs to use, so don't go there.)
Be aware that the call new StrategyForm()

is out of my component's control.
Q4. I'm a little confused - what are Handles for custom objects? Is MSDN talking about any object we create, or just some specific objects like window handles, cursor pens, icons?
Q5. What exactly is causing the use of UOH? (almost the same as Q4)

I would be very grateful to anyone who can give me some knowledgeable answers. Many thanks!:)

[Update]
Based on Stakx's answer, please note that opened windows will only be closed by the user. It's kind of a situation with an MDI application where there are too many windows open for children. Thus, Dispose

it cannot be called whenever we want.

+1


source to share


1 answer


Q1

It looks like you are trying to create too many user interface controls at the same time. Even if the memory remains, you are out of pens. See below for a short but rather technical explanation.

Q4

I understand a user object as any object that is part of a GUI. At least prior to Windows XP, the Windows UI API was in USER.DLL

(one of the core DLLs in Windows). Basically, the user interface consists of "windows". All controls like buttons, text boxes, checkboxes are internally the same, namely "windows". To create them, you must call a Win32 API function CreateWindow

. This function will then return a handle to the created "window" (user interface element or "custom object").

So I am assuming the handle to the user object is the handle returned by this function. (Winforms is based on the old Win32 API and therefore uses a function CreateWindow

.)

Q2

Indeed, you cannot create as many UI controls as you want. All these descriptors received through CreateWindow

must be freed at some point. In Winforms, the easiest and safest way to do this is using a block using

or call Dispose

:

using (MyForm form = new MyForm())
{
    if (form.ShowDialog() == DialogResult.OK) ...
}    

      

Basically, everything System.Windows.Forms.Control

can be Dispose

d and should be removed. Sometimes this is done automatically for you, but you shouldn't rely on it. Always Dispose

your UI controls when you no longer need them.



Note for Dispose

modal and non-modal forms:

  • Modal forms (shown with ShowDialog

    ) are not automatically removed. You have to do it yourself as shown in the above code example.
  • The modeling forms (shown with Show

    ) are automatically deleted for you, since you have no control over it when the user is closed. No need to explicitly call Dispose

    !

Q5

Every time you create a UI object, Winforms makes calls internally CreateWindow

. This is how descriptors are allocated. And they are not released until the appropriate call is made DestroyWindow

. In Winforms, this call is triggered by Dispose

any method System.Windows.Forms.Control

. (Note: While I'm still pretty sure of this, I'm actually a little guessing. I may be 100% wrong. Looking at the internals of Winforms with Reflector reveals the truth.)

Q3

Assuming yours is StrategyEditor

creating a massive bunch of UI controls, I don't think you can do much. If you cannot simplify this control (in terms of the number of children to create) then it seems like you are stuck in the situation where you are. You simply cannot create infinitely many user interface controls.

However, you can keep track of how much is being StrategyEditor

opened at any given time (increment the counter on each instance and decrement it each time it is closed), you can keep track of the last one using the FormClosing

/ FormClosed

event of a form or in a Dispose

control method ). Then you can limit the number of open concurrently StrategyEditor

to a fixed number, say 5. If the limit is exceeded, you can throw an exception in the constructor so that no more instances are created. Of course, I can't tell if StrategyForm

the exception will be handled from your constructor StrategyEditor

...

public class StrategyEditor : ...
{
    public StrategyEditor()
    {
        InitializeComponent();

        if (numberOfLiveInstances >= maximumAllowedLiveInstances)
            throw ...;
        // not a nice solution IMHO, but if you've no other choice...
    }
}

      

Anyway, limiting the number of instances StrategyEditor

seems like a temporary fix to me and won't solve the real problem.

+2


source







All Articles