Problems with memory at WTS/Citrix Servern

  • Thread starter Alexander Overmann
  • Start date
A

Alexander Overmann

Hello,

currently we encounter several huge memoryproblems running .NET C#
Applications under Windows Terminal Servers/Citrix. Sometimes the program
claims 100 MB which is not very much at a single PC but a pain in the ass at
a Terminalserver when 20-25 Users start the program (25*100 = 2500 MB).



We made some analysis:

So we build a new project which only contains 2 forms. The first form just
has one button which starts the second form:



private void button1_Click(object sender, System.EventArgs e)

{

Form2 frm = new Form2();

frm.ShowDialog();

}



This second form contains a listview with 10 columns and a button in order
to fill it. After pressing the button the listview is filled:



private void button1_Click(object sender, System.EventArgs e)

{

Random rd = new Random();

Cursor.Current = Cursors.WaitCursor;

ListViewItem item;

string[] s = new string[10];

for (int i=0;i<100000;i++)

{

s[0] = rd.NextDouble().ToString();

s[1] = rd.NextDouble().ToString();

s[2] = rd.NextDouble().ToString();

s[3] = rd.NextDouble().ToString();

s[4] = rd.NextDouble().ToString();

s[5] = rd.NextDouble().ToString();

s[6] = rd.NextDouble().ToString();

s[7] = rd.NextDouble().ToString();

s[8] = rd.NextDouble().ToString();

s[9] = rd.NextDouble().ToString();

item = new ListViewItem(s);

this.lvData.Items.Add(item);

}

}



During this the memoryusage rises till 180 MB. If I close the second form
and return to the first one the memory decreases to 100 MB. This value didn't
decrease any more. So we change the code in the first form which opens the
second one:



private void button1_Click(object sender, System.EventArgs e)

{

Form2 frm = new Form2();

frm.ShowDialog();

frm.Dispose(); // NEW Statement

GC.Collect(); // NEW Statement

}



Now the memory changes after closing the second form from 100 MB to 25 MB.
This is much better but still not enough! Why does s single application with
one form and one button still uses 25 MB? At the beginning it just had 8 MB.
Run this program at a WTS by 20 Users and you have 500 MB wasted by a single
form with just one button!



We have also read the 1150 pages of ScaleNet.pdf from MSDN. I was wondering
about this sentence: AVOID CALLING GC.COLLECT. But this was the only way to
reduces the memory in my sampleapplication!



How can we solve this problem? Is there any better chance to get rid of this
waste of memory? At a normal PC it does not matter but it is critical at a
Terminal Server.



Thank you very much
 
W

Willy Denoyette [MVP]

Please write a small real-world application and run this on a TS, your
conclusion based on this piece of code are not relevant. It's right, you
only have a few UI elements but you are using a lot of memory because you
are filling a listview with 100000 rows of 10 string values each. Also, the
way you fill the list is wrong, it must take ages to fill the list I guess.


Willy.


| Hello,
|
| currently we encounter several huge memoryproblems running .NET C#
| Applications under Windows Terminal Servers/Citrix. Sometimes the program
| claims 100 MB which is not very much at a single PC but a pain in the ass
at
| a Terminalserver when 20-25 Users start the program (25*100 = 2500 MB).
|
|
|
| We made some analysis:
|
| So we build a new project which only contains 2 forms. The first form just
| has one button which starts the second form:
|
|
|
| private void button1_Click(object sender, System.EventArgs e)
|
| {
|
| Form2 frm = new Form2();
|
| frm.ShowDialog();
|
| }
|
|
|
| This second form contains a listview with 10 columns and a button in order
| to fill it. After pressing the button the listview is filled:
|
|
|
| private void button1_Click(object sender, System.EventArgs e)
|
| {
|
| Random rd = new Random();
|
| Cursor.Current = Cursors.WaitCursor;
|
| ListViewItem item;
|
| string[] s = new string[10];
|
| for (int i=0;i<100000;i++)
|
| {
|
| s[0] = rd.NextDouble().ToString();
|
| s[1] = rd.NextDouble().ToString();
|
| s[2] = rd.NextDouble().ToString();
|
| s[3] = rd.NextDouble().ToString();
|
| s[4] = rd.NextDouble().ToString();
|
| s[5] = rd.NextDouble().ToString();
|
| s[6] = rd.NextDouble().ToString();
|
| s[7] = rd.NextDouble().ToString();
|
| s[8] = rd.NextDouble().ToString();
|
| s[9] = rd.NextDouble().ToString();
|
| item = new ListViewItem(s);
|
| this.lvData.Items.Add(item);
|
| }
|
| }
|
|
|
| During this the memoryusage rises till 180 MB. If I close the second form
| and return to the first one the memory decreases to 100 MB. This value
didn't
| decrease any more. So we change the code in the first form which opens the
| second one:
|
|
|
| private void button1_Click(object sender, System.EventArgs e)
|
| {
|
| Form2 frm = new Form2();
|
| frm.ShowDialog();
|
| frm.Dispose(); // NEW Statement
|
| GC.Collect(); // NEW Statement
|
| }
|
|
|
| Now the memory changes after closing the second form from 100 MB to 25 MB.
| This is much better but still not enough! Why does s single application
with
| one form and one button still uses 25 MB? At the beginning it just had 8
MB.
| Run this program at a WTS by 20 Users and you have 500 MB wasted by a
single
| form with just one button!
|
|
|
| We have also read the 1150 pages of ScaleNet.pdf from MSDN. I was
wondering
| about this sentence: AVOID CALLING GC.COLLECT. But this was the only way
to
| reduces the memory in my sampleapplication!
|
|
|
| How can we solve this problem? Is there any better chance to get rid of
this
| waste of memory? At a normal PC it does not matter but it is critical at a
| Terminal Server.
|
|
|
| Thank you very much
|
|
|
|
 
A

Alexander Overmann

Dear Willy,
sure the listview costs a lot of memory but this is at form2. If I close
form2 and return to form1 the memory does not get become free completely.

But maybe my "wrong way" of filling the listview is the reason? Tell me how
you would fill it please and I post the results of ths.
Thanx
Alex

btw: Our real-world application currently uses 60MB-150MB for each(!) user
and we also used all those "hints" found in the web:
stringbuilder, do not use boxing/unboxing to often, set ref-types to null,
GC.COLLECT etc.
Thankx

Willy Denoyette said:
Please write a small real-world application and run this on a TS, your
conclusion based on this piece of code are not relevant. It's right, you
only have a few UI elements but you are using a lot of memory because you
are filling a listview with 100000 rows of 10 string values each. Also,
the
way you fill the list is wrong, it must take ages to fill the list I
guess.


Willy.


| Hello,
|
| currently we encounter several huge memoryproblems running .NET C#
| Applications under Windows Terminal Servers/Citrix. Sometimes the
program
| claims 100 MB which is not very much at a single PC but a pain in the
ass
at
| a Terminalserver when 20-25 Users start the program (25*100 = 2500 MB).
|
|
|
| We made some analysis:
|
| So we build a new project which only contains 2 forms. The first form
just
| has one button which starts the second form:
|
|
|
| private void button1_Click(object sender, System.EventArgs e)
|
| {
|
| Form2 frm = new Form2();
|
| frm.ShowDialog();
|
| }
|
|
|
| This second form contains a listview with 10 columns and a button in
order
| to fill it. After pressing the button the listview is filled:
|
|
|
| private void button1_Click(object sender, System.EventArgs e)
|
| {
|
| Random rd = new Random();
|
| Cursor.Current = Cursors.WaitCursor;
|
| ListViewItem item;
|
| string[] s = new string[10];
|
| for (int i=0;i<100000;i++)
|
| {
|
| s[0] = rd.NextDouble().ToString();
|
| s[1] = rd.NextDouble().ToString();
|
| s[2] = rd.NextDouble().ToString();
|
| s[3] = rd.NextDouble().ToString();
|
| s[4] = rd.NextDouble().ToString();
|
| s[5] = rd.NextDouble().ToString();
|
| s[6] = rd.NextDouble().ToString();
|
| s[7] = rd.NextDouble().ToString();
|
| s[8] = rd.NextDouble().ToString();
|
| s[9] = rd.NextDouble().ToString();
|
| item = new ListViewItem(s);
|
| this.lvData.Items.Add(item);
|
| }
|
| }
|
|
|
| During this the memoryusage rises till 180 MB. If I close the second
form
| and return to the first one the memory decreases to 100 MB. This value
didn't
| decrease any more. So we change the code in the first form which opens
the
| second one:
|
|
|
| private void button1_Click(object sender, System.EventArgs e)
|
| {
|
| Form2 frm = new Form2();
|
| frm.ShowDialog();
|
| frm.Dispose(); // NEW Statement
|
| GC.Collect(); // NEW Statement
|
| }
|
|
|
| Now the memory changes after closing the second form from 100 MB to 25
MB.
| This is much better but still not enough! Why does s single application
with
| one form and one button still uses 25 MB? At the beginning it just had 8
MB.
| Run this program at a WTS by 20 Users and you have 500 MB wasted by a
single
| form with just one button!
|
|
|
| We have also read the 1150 pages of ScaleNet.pdf from MSDN. I was
wondering
| about this sentence: AVOID CALLING GC.COLLECT. But this was the only way
to
| reduces the memory in my sampleapplication!
|
|
|
| How can we solve this problem? Is there any better chance to get rid of
this
| waste of memory? At a normal PC it does not matter but it is critical at
a
| Terminal Server.
|
|
|
| Thank you very much
|
|
|
|
 
W

Willy Denoyette [MVP]

| Dear Willy,
| sure the listview costs a lot of memory but this is at form2. If I close
| form2 and return to form1 the memory does not get become free completely.
|

That's right, this is due to the fact that after you ran the Form2, the CLR
has loaded the framework code for the windows ListView stuff and a buch of
other classes and static variables like strings that are getting interned
(~3MB) but the remaining 25MB is not the problem, the problem is your actual
memory consumption when filling the list. Note that on V2 of the framework
this starts with ~13MB and ends with ~21MB (after a second GC run).

You should never fill a ListView or whatever UI container with that number
of elements.
Why? Well no-one likes to scroll through such a list, it's bad UI design.
Second, memory overhead; you create 1000000 strings objects of 52 bytes each
= 52000000 bytes.
1000000 Listview SubItems of 28 bytes each = 28000000 bytes.
100000 Listview items of 64 bytes each = 6400000
add to that the hashtables used for the listview indexes and you have a
total of 106MB for a single ListView. Note that I'm not counting the randow
doubles(1000000) aand the objects added by the framework classes itself.
This accounts for ~200MB workingset usage, imagine this on a TS with 100
users - 200Mb * 100 = 20 GB, or 5GB for 25 users that would be a real
problem.

That's one thing, next the way you fill the list takes ages (>20 minutes) on
v2 of the framework, while you keep the CPU saturated and this on the UI
thread. This is because you fill the list row per row (using Items.Add(..)),
while you should use AddRange. And as a side effect you prevent the GC to
clean-up the garbage in a timely fashion, that's not a problem when you have
plenty of memory, but on a TS that becomes a problem as you noticed.

Willy.



| But maybe my "wrong way" of filling the listview is the reason? Tell me
how
| you would fill it please and I post the results of ths.
| Thanx
| Alex
|
| btw: Our real-world application currently uses 60MB-150MB for each(!) user
| and we also used all those "hints" found in the web:
| stringbuilder, do not use boxing/unboxing to often, set ref-types to null,
| GC.COLLECT etc.
| Thankx
|
Newsbeitrag
| | > Please write a small real-world application and run this on a TS, your
| > conclusion based on this piece of code are not relevant. It's right, you
| > only have a few UI elements but you are using a lot of memory because
you
| > are filling a listview with 100000 rows of 10 string values each. Also,
| > the
| > way you fill the list is wrong, it must take ages to fill the list I
| > guess.
| >
| >
| > Willy.
| >
| >
| > | > | Hello,
| > |
| > | currently we encounter several huge memoryproblems running .NET C#
| > | Applications under Windows Terminal Servers/Citrix. Sometimes the
| > program
| > | claims 100 MB which is not very much at a single PC but a pain in the
| > ass
| > at
| > | a Terminalserver when 20-25 Users start the program (25*100 = 2500
MB).
| > |
| > |
| > |
| > | We made some analysis:
| > |
| > | So we build a new project which only contains 2 forms. The first form
| > just
| > | has one button which starts the second form:
| > |
| > |
| > |
| > | private void button1_Click(object sender, System.EventArgs e)
| > |
| > | {
| > |
| > | Form2 frm = new Form2();
| > |
| > | frm.ShowDialog();
| > |
| > | }
| > |
| > |
| > |
| > | This second form contains a listview with 10 columns and a button in
| > order
| > | to fill it. After pressing the button the listview is filled:
| > |
| > |
| > |
| > | private void button1_Click(object sender, System.EventArgs e)
| > |
| > | {
| > |
| > | Random rd = new Random();
| > |
| > | Cursor.Current = Cursors.WaitCursor;
| > |
| > | ListViewItem item;
| > |
| > | string[] s = new string[10];
| > |
| > | for (int i=0;i<100000;i++)
| > |
| > | {
| > |
| > | s[0] = rd.NextDouble().ToString();
| > |
| > | s[1] = rd.NextDouble().ToString();
| > |
| > | s[2] = rd.NextDouble().ToString();
| > |
| > | s[3] = rd.NextDouble().ToString();
| > |
| > | s[4] = rd.NextDouble().ToString();
| > |
| > | s[5] = rd.NextDouble().ToString();
| > |
| > | s[6] = rd.NextDouble().ToString();
| > |
| > | s[7] = rd.NextDouble().ToString();
| > |
| > | s[8] = rd.NextDouble().ToString();
| > |
| > | s[9] = rd.NextDouble().ToString();
| > |
| > | item = new ListViewItem(s);
| > |
| > | this.lvData.Items.Add(item);
| > |
| > | }
| > |
| > | }
| > |
| > |
| > |
| > | During this the memoryusage rises till 180 MB. If I close the second
| > form
| > | and return to the first one the memory decreases to 100 MB. This value
| > didn't
| > | decrease any more. So we change the code in the first form which opens
| > the
| > | second one:
| > |
| > |
| > |
| > | private void button1_Click(object sender, System.EventArgs e)
| > |
| > | {
| > |
| > | Form2 frm = new Form2();
| > |
| > | frm.ShowDialog();
| > |
| > | frm.Dispose(); // NEW Statement
| > |
| > | GC.Collect(); // NEW Statement
| > |
| > | }
| > |
| > |
| > |
| > | Now the memory changes after closing the second form from 100 MB to 25
| > MB.
| > | This is much better but still not enough! Why does s single
application
| > with
| > | one form and one button still uses 25 MB? At the beginning it just had
8
| > MB.
| > | Run this program at a WTS by 20 Users and you have 500 MB wasted by a
| > single
| > | form with just one button!
| > |
| > |
| > |
| > | We have also read the 1150 pages of ScaleNet.pdf from MSDN. I was
| > wondering
| > | about this sentence: AVOID CALLING GC.COLLECT. But this was the only
way
| > to
| > | reduces the memory in my sampleapplication!
| > |
| > |
| > |
| > | How can we solve this problem? Is there any better chance to get rid
of
| > this
| > | waste of memory? At a normal PC it does not matter but it is critical
at
| > a
| > | Terminal Server.
| > |
| > |
| > |
| > | Thank you very much
| > |
| > |
| > |
| > |
| >
| >
|
|
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top