GC does not release unused memory to the system in WinCE 4.2

M

MS

I've read in several sources that during garbage collection, the GC will
release unused pages from the GC heap back to the OS.

Well this is not the case with my app.
Here is the code to reproduce the behaviour:
This sample code simply allocate about 2MB

private void btnCreateMemChunk_Click(object sender, System.EventArgs e) {
if (al==null)
al=new ArrayList();
int Count=10000;
ItemInfo ii=null;

Exception eeee=null;

GC.Collect();
long before=System.GC.GetTotalMemory(true);

this.tbMemo.Text+="Start
Mem(kb):"+((int)(before/1024)).ToString()+"\r\n";;

for (int i=0;i<Count;i++) {
try{
ii=new ItemInfo((i.ToString()),new string('T',i%17),new
string('1',i%19),new string('9',i%7));
al.Add(ii);
}catch(Exception ee){
eeee=ee;
break;
}

}
long after=System.GC.GetTotalMemory(true);
if (eeee!=null)
this.tbMemo.Text+="Exception: "+eeee.ToString()+"\r\n";
this.tbMemo.Text+="End Mem(kb):"+((int)(after/1024)).ToString()+"\r\n";

}

This code frees the array:
private void btnFree_Click(object sender, System.EventArgs e) {
if (this.al!=null)
this.al=null;
GC.Collect();

this.tbMemo.Text+="Allocated:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}



When I start the program , it takes about 7MB of program memory, out of 9MB
free at the beginning.

First time I press the CreateMemChunk the allocated memory stays the same,
second time allocated memory goes to nearly 9MB.
(If I press again I get the Out of memory dialog.)

So I've pressed the button 2 times. CG tells me it has allocated about 4.2
MB of memory, and all programm memory on the device is allocated also.

Now I press btnFree. CG tells me it has allocated about 0,3 MB of memory.

But all of the program memory on the device is still taken from the GC !!!

If I try to run some other application I get the OOM dialog....

But if I press the CreateMemChunk it runs(even twice), which means that
there is plenty of space in th GC heap!!!!

So what can I do to make the GC release the unused 4MB of heap to the
system?

I've tried sending WM_HIBERNATE to CLR :

int res=WinAPI.SendMessage(0xffff,0x03FF,0,0);

but this does not help.

please help me,

thanks in advance
 
G

Guest

You have a misconception about how the GC works. Calling Collect does not
instantly free memory in any measurable way. It walks the GC Heap marking
reachable objects and then frees those that are not reachable. The GC Heap
won't necessarily compact or shrink when you call it.

At first glance I would expect the GC Heap to shrink back to 1MB on the
first collection after you destroy your ArrayList (if this is CF 2.0
anyway).

This is not all of the code, so we can't exactly test it ourselves.

-Chris
 
M

MS

Thanks for you reply.

My code runs on CF 1.0 SP3
Here is the full code of a sample to replicate the problem:


using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;

namespace MemTest
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button btnHibernateAll;
private System.Windows.Forms.Button btnFree;
private System.Windows.Forms.Button btnCreateChunk;
private System.Windows.Forms.TextBox tbMemo;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button4;
public ArrayList al=null;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnHibernateAll = new System.Windows.Forms.Button();
this.btnFree = new System.Windows.Forms.Button();
this.btnCreateChunk = new System.Windows.Forms.Button();
this.tbMemo = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
//
// btnHibernateAll
//
this.btnHibernateAll.Location = new System.Drawing.Point(160, 0);
this.btnHibernateAll.Size = new System.Drawing.Size(80, 32);
this.btnHibernateAll.Text = "Hibernate";
this.btnHibernateAll.Click += new
System.EventHandler(this.btnHibernateAll_Click);
//
// btnFree
//
this.btnFree.Location = new System.Drawing.Point(80, 0);
this.btnFree.Size = new System.Drawing.Size(80, 32);
this.btnFree.Text = "Free";
this.btnFree.Click += new System.EventHandler(this.btnFree_Click);
//
// btnCreateChunk
//
this.btnCreateChunk.Size = new System.Drawing.Size(80, 32);
this.btnCreateChunk.Text = "Allocate";
this.btnCreateChunk.Click += new
System.EventHandler(this.btnCreateChunk_Click);
//
// tbMemo
//
this.tbMemo.Location = new System.Drawing.Point(0, 32);
this.tbMemo.Multiline = true;
this.tbMemo.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.tbMemo.Size = new System.Drawing.Size(232, 200);
this.tbMemo.Text = "";
//
// button1
//
this.button1.Location = new System.Drawing.Point(0, 264);
this.button1.Size = new System.Drawing.Size(80, 32);
this.button1.Text = "Close";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button4
//
this.button4.Location = new System.Drawing.Point(144, 232);
this.button4.Size = new System.Drawing.Size(88, 24);
this.button4.Text = "GC Collect";
this.button4.Click += new System.EventHandler(this.button4_Click);
//
// Form1
//
this.ClientSize = new System.Drawing.Size(234, 295);
this.Controls.Add(this.button4);
this.Controls.Add(this.button1);
this.Controls.Add(this.btnHibernateAll);
this.Controls.Add(this.btnFree);
this.Controls.Add(this.btnCreateChunk);
this.Controls.Add(this.tbMemo);
this.Text = "Memory test";

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>

static void Main()
{
Application.Run(new Form1());
}

private void btnHibernateAll_Click(object sender, System.EventArgs e) {

int res=WinAPI.SendMessage(0xffff,0x03FF,0,0);
tbMemo.Text+="Hibernate sent!"+res.ToString()+"\r\n";
}

private void btnCreateChunk_Click(object sender, System.EventArgs e) {
// System.Collections.ArrayList al=new ArrayList();
if (al==null)
al=new ArrayList();
int Count=10000;
ItemInfo ii=null;
Exception eeee=null;

GC.Collect();
long before=System.GC.GetTotalMemory(true);
this.tbMemo.Text+="Start GC Heap
Mem(kb):"+((int)(before/1024)).ToString()+"\r\n";;

for (int i=0;i<Count;i++) {
try{
ii=new ItemInfo((i.ToString()),new string('T',i%17),new
string('1',i%19),new string('9',i%7));
al.Add(ii);
}catch(Exception ee){
eeee=ee;
break;
}
}
long after=System.GC.GetTotalMemory(true);


if (eeee!=null)
this.tbMemo.Text+="Exception: "+eeee.ToString()+"\r\n";

this.tbMemo.Text+="End CG Heap
Mem(kb):"+((int)(after/1024)).ToString()+"\r\n";


long ss=System.Runtime.InteropServices.Marshal.SizeOf(ii);
ii=new ItemInfo("0","0","0","0");

ss=(after-before)/Count;
}

private void btnFree_Click(object sender, System.EventArgs e) {
if (this.al!=null)
this.al=null;

this.tbMemo.Text+="Current GC
Heap:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}

private void button1_Click(object sender, System.EventArgs e) {
Close();
}

private void button4_Click(object sender, System.EventArgs e) {
GC.Collect();
this.tbMemo.Text+="Current GC
Heap:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}

}

public class ItemInfo {
public string TruckNo="";
public string ID="";
public string Code128="";
public DateTime Time;
public string Name="";
public static string delimiter=";";

/// <summary>
/// 0-áÒÔÉËÕÌßÔ Å ÚÁÒÅÄÅÎ ÏÔ ÂÁÚÁ
/// 1-áÒÔÉËÕÌßÔ Å ÚÁÒÅÄÅÎ ÏÔ ÕÓÔÒÏÊÓÔ×Ï, ÎÑÍÁ ÇÏ × ÂÁÚÁÔÁ
/// 2-áÒÔÉËÕÌßÔ Å ÉÚÔÒÉÔ!!!
/// </summary>
public int Status=0;

public ItemInfo(string truckNo, string id, string code128, string name) {
TruckNo=truckNo;
ID=id;
Code128=code128;
Name=name;
Time=DateTime.Now;
}

/// <summary>
/// óßÚÄÁ×Á ÄÁÎÉ ÚÁ ÁÒÔÉËÕÌ ÏÔ ÒÅÄ
/// </summary>
/// <param name="Row"></param>
public ItemInfo(string Row) {
string str=Row;
string cell;
int col=0;
int endpos=0,startpos=0;

while(((endpos = str.IndexOf(delimiter,startpos)) >= 0)&& (col<(5+1)) ) {
cell=str.Substring(startpos,endpos-startpos);
cell=cell.Trim();
startpos=endpos+1;
switch (col) {
case 0: ID=cell; break;
case 1: Code128=cell; break;
case 2: TruckNo=cell; break;
case 3:
try {
Time=DateTime.ParseExact(cell,"dd.MM.yy",System.Globalization.CultureInfo.CurrentCulture);
}
catch{}
break;
}

col++;
}
//ðÏÓÌÅÄÎÁÔÁ ËÏÌÏÎÁ
try {
cell=str.Substring(startpos);
DateTime
dTime=DateTime.ParseExact(cell,"hh:mm:ss",System.Globalization.CultureInfo.CurrentCulture);
Time=Time.Add(new TimeSpan(dTime.Hour,dTime.Minute,dTime.Second));

}
catch{}

}
public override string ToString() {
if ((Code128!=null)&&(Code128.Length>0))
return Code128;
if ((ID!=null)&&(ID.Length>0))
return ID;
return ID;
//return base.ToString ();
}



public string ToStringDB() {
return
ID+delimiter+Code128+delimiter+TruckNo+delimiter+Time.ToString("dd.MM.yy")+delimiter+Time.ToString("hh:mm:ss");
}


//òÁ×ÎÉ ÓÁ ÓÁÍÏ ÁËÏ ÉÍ Å ÒÁ×ÅÎ ËÏÄ128 É
public override bool Equals(object a) {
if ( (((ItemInfo) a).Code128.Length>0) &&
(this.Code128.Length>0) &&
(this.Code128==((ItemInfo) a).Code128))
return true;
return false;
}




}

public class WinAPI{

[System.Runtime.InteropServices.DllImport("coredll.dll")]
public static extern int SendMessage(int hWnd, uint Msg, uint WParam, uint
LParam ) ;

[System.Runtime.InteropServices.DllImport("coredll.dll")]
public static extern int FindWindowW(String lpClassName,String
lpWindowName );

// public extern static int SendMessage(IntPtr hwnd,uint msg, uint wParam,
uint lParam);
}


}

//----------------------------------------------------------------------------------------------------------

Steps to do:
1.Start the program.(initially I get about 9mb free program memory)
2. Press twice the "Allocate" button.(You will see there are 4MB in the GC
heap)
3. Press "Free"
4. Press GC Collect(You will see there are 0,3MB in the GC heap))
NOW:
5A. Try to start Word or IExplorer. I get the out of memory box
OR
5B: Press twice the "Allocate" button. The memory would be allocated inside
the GC heap.

For me this means that GC simply does not release the memory it uses, even
in a OOM situation.

Thanks in advance.






----- Original Message -----
From: "<ctacke/>" <ctacke_AT_OpenNETCF_com>
Newsgroups: microsoft.public.dotnet.framework.compactframework
Sent: Thursday, April 20, 2006 10:01 PM
Subject: Re: GC does not release unused memory to the system in WinCE 4.2
 
G

Guest

What kind of device are you running on? I tried on a PPC 2003 and followed
your directions and both pocket word and IE launch just fine with no
modifications to your code. In fact I was able to allocate 10 times without
failure.

-Chris



MS said:
Thanks for you reply.

My code runs on CF 1.0 SP3
Here is the full code of a sample to replicate the problem:


using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;

namespace MemTest
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button btnHibernateAll;
private System.Windows.Forms.Button btnFree;
private System.Windows.Forms.Button btnCreateChunk;
private System.Windows.Forms.TextBox tbMemo;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button4;
public ArrayList al=null;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnHibernateAll = new System.Windows.Forms.Button();
this.btnFree = new System.Windows.Forms.Button();
this.btnCreateChunk = new System.Windows.Forms.Button();
this.tbMemo = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
//
// btnHibernateAll
//
this.btnHibernateAll.Location = new System.Drawing.Point(160, 0);
this.btnHibernateAll.Size = new System.Drawing.Size(80, 32);
this.btnHibernateAll.Text = "Hibernate";
this.btnHibernateAll.Click += new
System.EventHandler(this.btnHibernateAll_Click);
//
// btnFree
//
this.btnFree.Location = new System.Drawing.Point(80, 0);
this.btnFree.Size = new System.Drawing.Size(80, 32);
this.btnFree.Text = "Free";
this.btnFree.Click += new System.EventHandler(this.btnFree_Click);
//
// btnCreateChunk
//
this.btnCreateChunk.Size = new System.Drawing.Size(80, 32);
this.btnCreateChunk.Text = "Allocate";
this.btnCreateChunk.Click += new
System.EventHandler(this.btnCreateChunk_Click);
//
// tbMemo
//
this.tbMemo.Location = new System.Drawing.Point(0, 32);
this.tbMemo.Multiline = true;
this.tbMemo.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.tbMemo.Size = new System.Drawing.Size(232, 200);
this.tbMemo.Text = "";
//
// button1
//
this.button1.Location = new System.Drawing.Point(0, 264);
this.button1.Size = new System.Drawing.Size(80, 32);
this.button1.Text = "Close";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button4
//
this.button4.Location = new System.Drawing.Point(144, 232);
this.button4.Size = new System.Drawing.Size(88, 24);
this.button4.Text = "GC Collect";
this.button4.Click += new System.EventHandler(this.button4_Click);
//
// Form1
//
this.ClientSize = new System.Drawing.Size(234, 295);
this.Controls.Add(this.button4);
this.Controls.Add(this.button1);
this.Controls.Add(this.btnHibernateAll);
this.Controls.Add(this.btnFree);
this.Controls.Add(this.btnCreateChunk);
this.Controls.Add(this.tbMemo);
this.Text = "Memory test";

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>

static void Main()
{
Application.Run(new Form1());
}

private void btnHibernateAll_Click(object sender, System.EventArgs e) {

int res=WinAPI.SendMessage(0xffff,0x03FF,0,0);
tbMemo.Text+="Hibernate sent!"+res.ToString()+"\r\n";
}

private void btnCreateChunk_Click(object sender, System.EventArgs e) {
// System.Collections.ArrayList al=new ArrayList();
if (al==null)
al=new ArrayList();
int Count=10000;
ItemInfo ii=null;
Exception eeee=null;

GC.Collect();
long before=System.GC.GetTotalMemory(true);
this.tbMemo.Text+="Start GC Heap
Mem(kb):"+((int)(before/1024)).ToString()+"\r\n";;

for (int i=0;i<Count;i++) {
try{
ii=new ItemInfo((i.ToString()),new string('T',i%17),new
string('1',i%19),new string('9',i%7));
al.Add(ii);
}catch(Exception ee){
eeee=ee;
break;
}
}
long after=System.GC.GetTotalMemory(true);


if (eeee!=null)
this.tbMemo.Text+="Exception: "+eeee.ToString()+"\r\n";

this.tbMemo.Text+="End CG Heap
Mem(kb):"+((int)(after/1024)).ToString()+"\r\n";


long ss=System.Runtime.InteropServices.Marshal.SizeOf(ii);
ii=new ItemInfo("0","0","0","0");

ss=(after-before)/Count;
}

private void btnFree_Click(object sender, System.EventArgs e) {
if (this.al!=null)
this.al=null;

this.tbMemo.Text+="Current GC
Heap:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}

private void button1_Click(object sender, System.EventArgs e) {
Close();
}

private void button4_Click(object sender, System.EventArgs e) {
GC.Collect();
this.tbMemo.Text+="Current GC
Heap:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}

}

public class ItemInfo {
public string TruckNo="";
public string ID="";
public string Code128="";
public DateTime Time;
public string Name="";
public static string delimiter=";";

/// <summary>
/// 0-áÒÔÉËÕÌßÔ Å ÚÁÒÅÄÅÎ ÏÔ ÂÁÚÁ
/// 1-áÒÔÉËÕÌßÔ Å ÚÁÒÅÄÅÎ ÏÔ ÕÓÔÒÏÊÓÔ×Ï, ÎÑÍÁ ÇÏ × ÂÁÚÁÔÁ
/// 2-áÒÔÉËÕÌßÔ Å ÉÚÔÒÉÔ!!!
/// </summary>
public int Status=0;

public ItemInfo(string truckNo, string id, string code128, string name) {
TruckNo=truckNo;
ID=id;
Code128=code128;
Name=name;
Time=DateTime.Now;
}

/// <summary>
/// óßÚÄÁ×Á ÄÁÎÉ ÚÁ ÁÒÔÉËÕÌ ÏÔ ÒÅÄ
/// </summary>
/// <param name="Row"></param>
public ItemInfo(string Row) {
string str=Row;
string cell;
int col=0;
int endpos=0,startpos=0;

while(((endpos = str.IndexOf(delimiter,startpos)) >= 0)&& (col<(5+1)) )
{
cell=str.Substring(startpos,endpos-startpos);
cell=cell.Trim();
startpos=endpos+1;
switch (col) {
case 0: ID=cell; break;
case 1: Code128=cell; break;
case 2: TruckNo=cell; break;
case 3:
try {

Time=DateTime.ParseExact(cell,"dd.MM.yy",System.Globalization.CultureInfo.CurrentCulture);
}
catch{}
break;
}

col++;
}
//ðÏÓÌÅÄÎÁÔÁ ËÏÌÏÎÁ
try {
cell=str.Substring(startpos);
DateTime
dTime=DateTime.ParseExact(cell,"hh:mm:ss",System.Globalization.CultureInfo.CurrentCulture);
Time=Time.Add(new TimeSpan(dTime.Hour,dTime.Minute,dTime.Second));

}
catch{}

}
public override string ToString() {
if ((Code128!=null)&&(Code128.Length>0))
return Code128;
if ((ID!=null)&&(ID.Length>0))
return ID;
return ID;
//return base.ToString ();
}



public string ToStringDB() {
return
ID+delimiter+Code128+delimiter+TruckNo+delimiter+Time.ToString("dd.MM.yy")+delimiter+Time.ToString("hh:mm:ss");
}


//òÁ×ÎÉ ÓÁ ÓÁÍÏ ÁËÏ ÉÍ Å ÒÁ×ÅÎ ËÏÄ128 É
public override bool Equals(object a) {
if ( (((ItemInfo) a).Code128.Length>0) &&
(this.Code128.Length>0) &&
(this.Code128==((ItemInfo) a).Code128))
return true;
return false;
}




}

public class WinAPI{

[System.Runtime.InteropServices.DllImport("coredll.dll")]
public static extern int SendMessage(int hWnd, uint Msg, uint WParam,
uint
LParam ) ;

[System.Runtime.InteropServices.DllImport("coredll.dll")]
public static extern int FindWindowW(String lpClassName,String
lpWindowName );

// public extern static int SendMessage(IntPtr hwnd,uint msg, uint
wParam,
uint lParam);
}


}

//----------------------------------------------------------------------------------------------------------

Steps to do:
1.Start the program.(initially I get about 9mb free program memory)
2. Press twice the "Allocate" button.(You will see there are 4MB in the GC
heap)
3. Press "Free"
4. Press GC Collect(You will see there are 0,3MB in the GC heap))
NOW:
5A. Try to start Word or IExplorer. I get the out of memory box
OR
5B: Press twice the "Allocate" button. The memory would be allocated
inside
the GC heap.

For me this means that GC simply does not release the memory it uses, even
in a OOM situation.

Thanks in advance.






----- Original Message -----
From: "<ctacke/>" <ctacke_AT_OpenNETCF_com>
Newsgroups: microsoft.public.dotnet.framework.compactframework
Sent: Thursday, April 20, 2006 10:01 PM
Subject: Re: GC does not release unused memory to the system in WinCE 4.2

You have a misconception about how the GC works. Calling Collect does
not instantly free memory in any measurable way. It walks the GC Heap
marking reachable objects and then frees those that are not reachable.
The GC Heap won't necessarily compact or shrink when you call it.

At first glance I would expect the GC Heap to shrink back to 1MB on the
first collection after you destroy your ArrayList (if this is CF 2.0
anyway).

This is not all of the code, so we can't exactly test it ourselves.

-Chris
 
M

MS

I'm running on Windows CE 4.2 device, in fact I've tested this code on 4
different platforms.
1.Symbol's MC3000,
2.MC1000,
3.PPT8800(WinCE 4.1)
4.and on standart WindowsCE 4.2 emulator.

on all of these I get similar results.(It depends when on the initial
settings of the storage_to_program memory slider in the Control Panel)

On PPC2003 the behaviour of the operating systme in OOM case is different.
There, when you reach the maximum of the availabla program memory, the
device allocated some more from the available
storage. And since I suppose you were testing on device with 64MB RAM, it's
perfectly possible that you've allocated 20MB or more memory.
But even on that device, please, take a look that when you release these
20MB of data, not all of the program memory is released back to the system.

I've tested tha same program with CF2.0 on WinCE5.0, and everything works
perfectly(i.e
the memory is released back to the system), but unfortunately there is still
no CF2.0 for WinCE4.2.

I'm really desperate,
We are trying to port a big application running on hundreds of PPC2003
devices, but this stops our whole project.

Thanks in advance.


----- Original Message -----
From: "<ctacke/>" <ctacke_AT_OpenNETCF_com>
Newsgroups: microsoft.public.dotnet.framework.compactframework
Sent: Friday, April 21, 2006 5:07 PM
Subject: Re: GC does not release unused memory to the system in WinCE 4.2

What kind of device are you running on? I tried on a PPC 2003 and
followed your directions and both pocket word and IE launch just fine with
no modifications to your code. In fact I was able to allocate 10 times
without failure.

-Chris



MS said:
Thanks for you reply.

My code runs on CF 1.0 SP3
Here is the full code of a sample to replicate the problem:


using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;

namespace MemTest
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button btnHibernateAll;
private System.Windows.Forms.Button btnFree;
private System.Windows.Forms.Button btnCreateChunk;
private System.Windows.Forms.TextBox tbMemo;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button4;
public ArrayList al=null;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnHibernateAll = new System.Windows.Forms.Button();
this.btnFree = new System.Windows.Forms.Button();
this.btnCreateChunk = new System.Windows.Forms.Button();
this.tbMemo = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
//
// btnHibernateAll
//
this.btnHibernateAll.Location = new System.Drawing.Point(160, 0);
this.btnHibernateAll.Size = new System.Drawing.Size(80, 32);
this.btnHibernateAll.Text = "Hibernate";
this.btnHibernateAll.Click += new
System.EventHandler(this.btnHibernateAll_Click);
//
// btnFree
//
this.btnFree.Location = new System.Drawing.Point(80, 0);
this.btnFree.Size = new System.Drawing.Size(80, 32);
this.btnFree.Text = "Free";
this.btnFree.Click += new System.EventHandler(this.btnFree_Click);
//
// btnCreateChunk
//
this.btnCreateChunk.Size = new System.Drawing.Size(80, 32);
this.btnCreateChunk.Text = "Allocate";
this.btnCreateChunk.Click += new
System.EventHandler(this.btnCreateChunk_Click);
//
// tbMemo
//
this.tbMemo.Location = new System.Drawing.Point(0, 32);
this.tbMemo.Multiline = true;
this.tbMemo.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.tbMemo.Size = new System.Drawing.Size(232, 200);
this.tbMemo.Text = "";
//
// button1
//
this.button1.Location = new System.Drawing.Point(0, 264);
this.button1.Size = new System.Drawing.Size(80, 32);
this.button1.Text = "Close";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button4
//
this.button4.Location = new System.Drawing.Point(144, 232);
this.button4.Size = new System.Drawing.Size(88, 24);
this.button4.Text = "GC Collect";
this.button4.Click += new System.EventHandler(this.button4_Click);
//
// Form1
//
this.ClientSize = new System.Drawing.Size(234, 295);
this.Controls.Add(this.button4);
this.Controls.Add(this.button1);
this.Controls.Add(this.btnHibernateAll);
this.Controls.Add(this.btnFree);
this.Controls.Add(this.btnCreateChunk);
this.Controls.Add(this.tbMemo);
this.Text = "Memory test";

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>

static void Main()
{
Application.Run(new Form1());
}

private void btnHibernateAll_Click(object sender, System.EventArgs e) {

int res=WinAPI.SendMessage(0xffff,0x03FF,0,0);
tbMemo.Text+="Hibernate sent!"+res.ToString()+"\r\n";
}

private void btnCreateChunk_Click(object sender, System.EventArgs e) {
// System.Collections.ArrayList al=new ArrayList();
if (al==null)
al=new ArrayList();
int Count=10000;
ItemInfo ii=null;
Exception eeee=null;

GC.Collect();
long before=System.GC.GetTotalMemory(true);
this.tbMemo.Text+="Start GC Heap
Mem(kb):"+((int)(before/1024)).ToString()+"\r\n";;

for (int i=0;i<Count;i++) {
try{
ii=new ItemInfo((i.ToString()),new string('T',i%17),new
string('1',i%19),new string('9',i%7));
al.Add(ii);
}catch(Exception ee){
eeee=ee;
break;
}
}
long after=System.GC.GetTotalMemory(true);


if (eeee!=null)
this.tbMemo.Text+="Exception: "+eeee.ToString()+"\r\n";

this.tbMemo.Text+="End CG Heap
Mem(kb):"+((int)(after/1024)).ToString()+"\r\n";


long ss=System.Runtime.InteropServices.Marshal.SizeOf(ii);
ii=new ItemInfo("0","0","0","0");

ss=(after-before)/Count;
}

private void btnFree_Click(object sender, System.EventArgs e) {
if (this.al!=null)
this.al=null;

this.tbMemo.Text+="Current GC
Heap:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}

private void button1_Click(object sender, System.EventArgs e) {
Close();
}

private void button4_Click(object sender, System.EventArgs e) {
GC.Collect();
this.tbMemo.Text+="Current GC
Heap:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}

}

public class ItemInfo {
public string TruckNo="";
public string ID="";
public string Code128="";
public DateTime Time;
public string Name="";
public static string delimiter=";";

/// <summary>
/// 0-áÒÔÉËÕÌßÔ Å ÚÁÒÅÄÅÎ ÏÔ ÂÁÚÁ
/// 1-áÒÔÉËÕÌßÔ Å ÚÁÒÅÄÅÎ ÏÔ ÕÓÔÒÏÊÓÔ×Ï, ÎÑÍÁ ÇÏ × ÂÁÚÁÔÁ
/// 2-áÒÔÉËÕÌßÔ Å ÉÚÔÒÉÔ!!!
/// </summary>
public int Status=0;

public ItemInfo(string truckNo, string id, string code128, string name)
{
TruckNo=truckNo;
ID=id;
Code128=code128;
Name=name;
Time=DateTime.Now;
}

/// <summary>
/// óßÚÄÁ×Á ÄÁÎÉ ÚÁ ÁÒÔÉËÕÌ ÏÔ ÒÅÄ
/// </summary>
/// <param name="Row"></param>
public ItemInfo(string Row) {
string str=Row;
string cell;
int col=0;
int endpos=0,startpos=0;

while(((endpos = str.IndexOf(delimiter,startpos)) >= 0)&& (col<(5+1)) )
{
cell=str.Substring(startpos,endpos-startpos);
cell=cell.Trim();
startpos=endpos+1;
switch (col) {
case 0: ID=cell; break;
case 1: Code128=cell; break;
case 2: TruckNo=cell; break;
case 3:
try {

Time=DateTime.ParseExact(cell,"dd.MM.yy",System.Globalization.CultureInfo.CurrentCulture);
}
catch{}
break;
}

col++;
}
//ðÏÓÌÅÄÎÁÔÁ ËÏÌÏÎÁ
try {
cell=str.Substring(startpos);
DateTime
dTime=DateTime.ParseExact(cell,"hh:mm:ss",System.Globalization.CultureInfo.CurrentCulture);
Time=Time.Add(new TimeSpan(dTime.Hour,dTime.Minute,dTime.Second));

}
catch{}

}
public override string ToString() {
if ((Code128!=null)&&(Code128.Length>0))
return Code128;
if ((ID!=null)&&(ID.Length>0))
return ID;
return ID;
//return base.ToString ();
}



public string ToStringDB() {
return
ID+delimiter+Code128+delimiter+TruckNo+delimiter+Time.ToString("dd.MM.yy")+delimiter+Time.ToString("hh:mm:ss");
}


//òÁ×ÎÉ ÓÁ ÓÁÍÏ ÁËÏ ÉÍ Å ÒÁ×ÅÎ ËÏÄ128 É
public override bool Equals(object a) {
if ( (((ItemInfo) a).Code128.Length>0) &&
(this.Code128.Length>0) &&
(this.Code128==((ItemInfo) a).Code128))
return true;
return false;
}




}

public class WinAPI{

[System.Runtime.InteropServices.DllImport("coredll.dll")]
public static extern int SendMessage(int hWnd, uint Msg, uint WParam,
uint
LParam ) ;

[System.Runtime.InteropServices.DllImport("coredll.dll")]
public static extern int FindWindowW(String lpClassName,String
lpWindowName );

// public extern static int SendMessage(IntPtr hwnd,uint msg, uint
wParam,
uint lParam);
}


}

//----------------------------------------------------------------------------------------------------------

Steps to do:
1.Start the program.(initially I get about 9mb free program memory)
2. Press twice the "Allocate" button.(You will see there are 4MB in the
GC
heap)
3. Press "Free"
4. Press GC Collect(You will see there are 0,3MB in the GC heap))
NOW:
5A. Try to start Word or IExplorer. I get the out of memory box
OR
5B: Press twice the "Allocate" button. The memory would be allocated
inside
the GC heap.

For me this means that GC simply does not release the memory it uses,
even
in a OOM situation.

Thanks in advance.






----- Original Message -----
From: "<ctacke/>" <ctacke_AT_OpenNETCF_com>
Newsgroups: microsoft.public.dotnet.framework.compactframework
Sent: Thursday, April 20, 2006 10:01 PM
Subject: Re: GC does not release unused memory to the system in WinCE 4.2

You have a misconception about how the GC works. Calling Collect does
not instantly free memory in any measurable way. It walks the GC Heap
marking reachable objects and then frees those that are not reachable.
The GC Heap won't necessarily compact or shrink when you call it.

At first glance I would expect the GC Heap to shrink back to 1MB on the
first collection after you destroy your ArrayList (if this is CF 2.0
anyway).

This is not all of the code, so we can't exactly test it ourselves.

-Chris




I've read in several sources that during garbage collection, the GC
will release unused pages from the GC heap back to the OS.

Well this is not the case with my app.
Here is the code to reproduce the behaviour:
This sample code simply allocate about 2MB

private void btnCreateMemChunk_Click(object sender, System.EventArgs e)
{
if (al==null)
al=new ArrayList();
int Count=10000;
ItemInfo ii=null;

Exception eeee=null;

GC.Collect();
long before=System.GC.GetTotalMemory(true);

this.tbMemo.Text+="Start
Mem(kb):"+((int)(before/1024)).ToString()+"\r\n";;

for (int i=0;i<Count;i++) {
try{
ii=new ItemInfo((i.ToString()),new string('T',i%17),new
string('1',i%19),new string('9',i%7));
al.Add(ii);
}catch(Exception ee){
eeee=ee;
break;
}

}
long after=System.GC.GetTotalMemory(true);
if (eeee!=null)
this.tbMemo.Text+="Exception: "+eeee.ToString()+"\r\n";
this.tbMemo.Text+="End
Mem(kb):"+((int)(after/1024)).ToString()+"\r\n";

}

This code frees the array:
private void btnFree_Click(object sender, System.EventArgs e) {
if (this.al!=null)
this.al=null;
GC.Collect();

this.tbMemo.Text+="Allocated:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}



When I start the program , it takes about 7MB of program memory, out of
9MB free at the beginning.

First time I press the CreateMemChunk the allocated memory stays the
same, second time allocated memory goes to nearly 9MB.
(If I press again I get the Out of memory dialog.)

So I've pressed the button 2 times. CG tells me it has allocated about
4.2 MB of memory, and all programm memory on the device is allocated
also.

Now I press btnFree. CG tells me it has allocated about 0,3 MB of
memory.

But all of the program memory on the device is still taken from the GC
!!!

If I try to run some other application I get the OOM dialog....

But if I press the CreateMemChunk it runs(even twice), which means that
there is plenty of space in th GC heap!!!!

So what can I do to make the GC release the unused 4MB of heap to the
system?

I've tried sending WM_HIBERNATE to CLR :

int res=WinAPI.SendMessage(0xffff,0x03FF,0,0);

but this does not help.

please help me,

thanks in advance
 
G

Guest

CF 2.0 for CE 4.2:

http://www.microsoft.com/downloads/...3-a418-42d9-a481-19ba3ceca1a6&displaylang=en

-Chris


MS said:
I'm running on Windows CE 4.2 device, in fact I've tested this code on 4
different platforms.
1.Symbol's MC3000,
2.MC1000,
3.PPT8800(WinCE 4.1)
4.and on standart WindowsCE 4.2 emulator.

on all of these I get similar results.(It depends when on the initial
settings of the storage_to_program memory slider in the Control Panel)

On PPC2003 the behaviour of the operating systme in OOM case is different.
There, when you reach the maximum of the availabla program memory, the
device allocated some more from the available
storage. And since I suppose you were testing on device with 64MB RAM,
it's perfectly possible that you've allocated 20MB or more memory.
But even on that device, please, take a look that when you release these
20MB of data, not all of the program memory is released back to the
system.

I've tested tha same program with CF2.0 on WinCE5.0, and everything works
perfectly(i.e
the memory is released back to the system), but unfortunately there is
still no CF2.0 for WinCE4.2.

I'm really desperate,
We are trying to port a big application running on hundreds of PPC2003
devices, but this stops our whole project.

Thanks in advance.


----- Original Message -----
From: "<ctacke/>" <ctacke_AT_OpenNETCF_com>
Newsgroups: microsoft.public.dotnet.framework.compactframework
Sent: Friday, April 21, 2006 5:07 PM
Subject: Re: GC does not release unused memory to the system in WinCE 4.2

What kind of device are you running on? I tried on a PPC 2003 and
followed your directions and both pocket word and IE launch just fine
with no modifications to your code. In fact I was able to allocate 10
times without failure.

-Chris



MS said:
Thanks for you reply.

My code runs on CF 1.0 SP3
Here is the full code of a sample to replicate the problem:


using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;

namespace MemTest
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button btnHibernateAll;
private System.Windows.Forms.Button btnFree;
private System.Windows.Forms.Button btnCreateChunk;
private System.Windows.Forms.TextBox tbMemo;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button4;
public ArrayList al=null;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnHibernateAll = new System.Windows.Forms.Button();
this.btnFree = new System.Windows.Forms.Button();
this.btnCreateChunk = new System.Windows.Forms.Button();
this.tbMemo = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
//
// btnHibernateAll
//
this.btnHibernateAll.Location = new System.Drawing.Point(160, 0);
this.btnHibernateAll.Size = new System.Drawing.Size(80, 32);
this.btnHibernateAll.Text = "Hibernate";
this.btnHibernateAll.Click += new
System.EventHandler(this.btnHibernateAll_Click);
//
// btnFree
//
this.btnFree.Location = new System.Drawing.Point(80, 0);
this.btnFree.Size = new System.Drawing.Size(80, 32);
this.btnFree.Text = "Free";
this.btnFree.Click += new System.EventHandler(this.btnFree_Click);
//
// btnCreateChunk
//
this.btnCreateChunk.Size = new System.Drawing.Size(80, 32);
this.btnCreateChunk.Text = "Allocate";
this.btnCreateChunk.Click += new
System.EventHandler(this.btnCreateChunk_Click);
//
// tbMemo
//
this.tbMemo.Location = new System.Drawing.Point(0, 32);
this.tbMemo.Multiline = true;
this.tbMemo.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.tbMemo.Size = new System.Drawing.Size(232, 200);
this.tbMemo.Text = "";
//
// button1
//
this.button1.Location = new System.Drawing.Point(0, 264);
this.button1.Size = new System.Drawing.Size(80, 32);
this.button1.Text = "Close";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button4
//
this.button4.Location = new System.Drawing.Point(144, 232);
this.button4.Size = new System.Drawing.Size(88, 24);
this.button4.Text = "GC Collect";
this.button4.Click += new System.EventHandler(this.button4_Click);
//
// Form1
//
this.ClientSize = new System.Drawing.Size(234, 295);
this.Controls.Add(this.button4);
this.Controls.Add(this.button1);
this.Controls.Add(this.btnHibernateAll);
this.Controls.Add(this.btnFree);
this.Controls.Add(this.btnCreateChunk);
this.Controls.Add(this.tbMemo);
this.Text = "Memory test";

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>

static void Main()
{
Application.Run(new Form1());
}

private void btnHibernateAll_Click(object sender, System.EventArgs e) {

int res=WinAPI.SendMessage(0xffff,0x03FF,0,0);
tbMemo.Text+="Hibernate sent!"+res.ToString()+"\r\n";
}

private void btnCreateChunk_Click(object sender, System.EventArgs e) {
// System.Collections.ArrayList al=new ArrayList();
if (al==null)
al=new ArrayList();
int Count=10000;
ItemInfo ii=null;
Exception eeee=null;

GC.Collect();
long before=System.GC.GetTotalMemory(true);
this.tbMemo.Text+="Start GC Heap
Mem(kb):"+((int)(before/1024)).ToString()+"\r\n";;

for (int i=0;i<Count;i++) {
try{
ii=new ItemInfo((i.ToString()),new string('T',i%17),new
string('1',i%19),new string('9',i%7));
al.Add(ii);
}catch(Exception ee){
eeee=ee;
break;
}
}
long after=System.GC.GetTotalMemory(true);


if (eeee!=null)
this.tbMemo.Text+="Exception: "+eeee.ToString()+"\r\n";

this.tbMemo.Text+="End CG Heap
Mem(kb):"+((int)(after/1024)).ToString()+"\r\n";


long ss=System.Runtime.InteropServices.Marshal.SizeOf(ii);
ii=new ItemInfo("0","0","0","0");

ss=(after-before)/Count;
}

private void btnFree_Click(object sender, System.EventArgs e) {
if (this.al!=null)
this.al=null;

this.tbMemo.Text+="Current GC
Heap:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}

private void button1_Click(object sender, System.EventArgs e) {
Close();
}

private void button4_Click(object sender, System.EventArgs e) {
GC.Collect();
this.tbMemo.Text+="Current GC
Heap:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}

}

public class ItemInfo {
public string TruckNo="";
public string ID="";
public string Code128="";
public DateTime Time;
public string Name="";
public static string delimiter=";";

/// <summary>
/// 0-áÒÔÉËÕÌßÔ Å ÚÁÒÅÄÅÎ ÏÔ ÂÁÚÁ
/// 1-áÒÔÉËÕÌßÔ Å ÚÁÒÅÄÅÎ ÏÔ ÕÓÔÒÏÊÓÔ×Ï, ÎÑÍÁ ÇÏ × ÂÁÚÁÔÁ
/// 2-áÒÔÉËÕÌßÔ Å ÉÚÔÒÉÔ!!!
/// </summary>
public int Status=0;

public ItemInfo(string truckNo, string id, string code128, string name)
{
TruckNo=truckNo;
ID=id;
Code128=code128;
Name=name;
Time=DateTime.Now;
}

/// <summary>
/// óßÚÄÁ×Á ÄÁÎÉ ÚÁ ÁÒÔÉËÕÌ ÏÔ ÒÅÄ
/// </summary>
/// <param name="Row"></param>
public ItemInfo(string Row) {
string str=Row;
string cell;
int col=0;
int endpos=0,startpos=0;

while(((endpos = str.IndexOf(delimiter,startpos)) >= 0)&&
(col<(5+1)) ) {
cell=str.Substring(startpos,endpos-startpos);
cell=cell.Trim();
startpos=endpos+1;
switch (col) {
case 0: ID=cell; break;
case 1: Code128=cell; break;
case 2: TruckNo=cell; break;
case 3:
try {

Time=DateTime.ParseExact(cell,"dd.MM.yy",System.Globalization.CultureInfo.CurrentCulture);
}
catch{}
break;
}

col++;
}
//ðÏÓÌÅÄÎÁÔÁ ËÏÌÏÎÁ
try {
cell=str.Substring(startpos);
DateTime
dTime=DateTime.ParseExact(cell,"hh:mm:ss",System.Globalization.CultureInfo.CurrentCulture);
Time=Time.Add(new TimeSpan(dTime.Hour,dTime.Minute,dTime.Second));

}
catch{}

}
public override string ToString() {
if ((Code128!=null)&&(Code128.Length>0))
return Code128;
if ((ID!=null)&&(ID.Length>0))
return ID;
return ID;
//return base.ToString ();
}



public string ToStringDB() {
return
ID+delimiter+Code128+delimiter+TruckNo+delimiter+Time.ToString("dd.MM.yy")+delimiter+Time.ToString("hh:mm:ss");
}


//òÁ×ÎÉ ÓÁ ÓÁÍÏ ÁËÏ ÉÍ Å ÒÁ×ÅÎ ËÏÄ128 É
public override bool Equals(object a) {
if ( (((ItemInfo) a).Code128.Length>0) &&
(this.Code128.Length>0) &&
(this.Code128==((ItemInfo) a).Code128))
return true;
return false;
}




}

public class WinAPI{

[System.Runtime.InteropServices.DllImport("coredll.dll")]
public static extern int SendMessage(int hWnd, uint Msg, uint WParam,
uint
LParam ) ;

[System.Runtime.InteropServices.DllImport("coredll.dll")]
public static extern int FindWindowW(String lpClassName,String
lpWindowName );

// public extern static int SendMessage(IntPtr hwnd,uint msg, uint
wParam,
uint lParam);
}


}

//----------------------------------------------------------------------------------------------------------

Steps to do:
1.Start the program.(initially I get about 9mb free program memory)
2. Press twice the "Allocate" button.(You will see there are 4MB in the
GC
heap)
3. Press "Free"
4. Press GC Collect(You will see there are 0,3MB in the GC heap))
NOW:
5A. Try to start Word or IExplorer. I get the out of memory box
OR
5B: Press twice the "Allocate" button. The memory would be allocated
inside
the GC heap.

For me this means that GC simply does not release the memory it uses,
even
in a OOM situation.

Thanks in advance.






----- Original Message -----
From: "<ctacke/>" <ctacke_AT_OpenNETCF_com>
Newsgroups: microsoft.public.dotnet.framework.compactframework
Sent: Thursday, April 20, 2006 10:01 PM
Subject: Re: GC does not release unused memory to the system in WinCE
4.2


You have a misconception about how the GC works. Calling Collect does
not instantly free memory in any measurable way. It walks the GC Heap
marking reachable objects and then frees those that are not reachable.
The GC Heap won't necessarily compact or shrink when you call it.

At first glance I would expect the GC Heap to shrink back to 1MB on the
first collection after you destroy your ArrayList (if this is CF 2.0
anyway).

This is not all of the code, so we can't exactly test it ourselves.

-Chris




I've read in several sources that during garbage collection, the GC
will release unused pages from the GC heap back to the OS.

Well this is not the case with my app.
Here is the code to reproduce the behaviour:
This sample code simply allocate about 2MB

private void btnCreateMemChunk_Click(object sender, System.EventArgs
e) {
if (al==null)
al=new ArrayList();
int Count=10000;
ItemInfo ii=null;

Exception eeee=null;

GC.Collect();
long before=System.GC.GetTotalMemory(true);

this.tbMemo.Text+="Start
Mem(kb):"+((int)(before/1024)).ToString()+"\r\n";;

for (int i=0;i<Count;i++) {
try{
ii=new ItemInfo((i.ToString()),new string('T',i%17),new
string('1',i%19),new string('9',i%7));
al.Add(ii);
}catch(Exception ee){
eeee=ee;
break;
}

}
long after=System.GC.GetTotalMemory(true);
if (eeee!=null)
this.tbMemo.Text+="Exception: "+eeee.ToString()+"\r\n";
this.tbMemo.Text+="End
Mem(kb):"+((int)(after/1024)).ToString()+"\r\n";

}

This code frees the array:
private void btnFree_Click(object sender, System.EventArgs e) {
if (this.al!=null)
this.al=null;
GC.Collect();

this.tbMemo.Text+="Allocated:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}



When I start the program , it takes about 7MB of program memory, out
of 9MB free at the beginning.

First time I press the CreateMemChunk the allocated memory stays the
same, second time allocated memory goes to nearly 9MB.
(If I press again I get the Out of memory dialog.)

So I've pressed the button 2 times. CG tells me it has allocated about
4.2 MB of memory, and all programm memory on the device is allocated
also.

Now I press btnFree. CG tells me it has allocated about 0,3 MB of
memory.

But all of the program memory on the device is still taken from the GC
!!!

If I try to run some other application I get the OOM dialog....

But if I press the CreateMemChunk it runs(even twice), which means
that there is plenty of space in th GC heap!!!!

So what can I do to make the GC release the unused 4MB of heap to the
system?

I've tried sending WM_HIBERNATE to CLR :

int res=WinAPI.SendMessage(0xffff,0x03FF,0,0);

but this does not help.

please help me,

thanks in advance
 
M

MS

Thank you very much!

I'll give it a try tomorrow!


----- Original Message -----
From: "<ctacke/>" <ctacke_AT_OpenNETCF_com>
Newsgroups: microsoft.public.dotnet.framework.compactframework
Sent: Saturday, April 22, 2006 1:52 PM
Subject: Re: GC does not release unused memory to the system in WinCE 4.2

CF 2.0 for CE 4.2:

http://www.microsoft.com/downloads/...3-a418-42d9-a481-19ba3ceca1a6&displaylang=en

-Chris


MS said:
I'm running on Windows CE 4.2 device, in fact I've tested this code on 4
different platforms.
1.Symbol's MC3000,
2.MC1000,
3.PPT8800(WinCE 4.1)
4.and on standart WindowsCE 4.2 emulator.

on all of these I get similar results.(It depends when on the initial
settings of the storage_to_program memory slider in the Control Panel)

On PPC2003 the behaviour of the operating systme in OOM case is
different.
There, when you reach the maximum of the availabla program memory, the
device allocated some more from the available
storage. And since I suppose you were testing on device with 64MB RAM,
it's perfectly possible that you've allocated 20MB or more memory.
But even on that device, please, take a look that when you release these
20MB of data, not all of the program memory is released back to the
system.

I've tested tha same program with CF2.0 on WinCE5.0, and everything works
perfectly(i.e
the memory is released back to the system), but unfortunately there is
still no CF2.0 for WinCE4.2.

I'm really desperate,
We are trying to port a big application running on hundreds of PPC2003
devices, but this stops our whole project.

Thanks in advance.


----- Original Message -----
From: "<ctacke/>" <ctacke_AT_OpenNETCF_com>
Newsgroups: microsoft.public.dotnet.framework.compactframework
Sent: Friday, April 21, 2006 5:07 PM
Subject: Re: GC does not release unused memory to the system in WinCE 4.2

What kind of device are you running on? I tried on a PPC 2003 and
followed your directions and both pocket word and IE launch just fine
with no modifications to your code. In fact I was able to allocate 10
times without failure.

-Chris



Thanks for you reply.

My code runs on CF 1.0 SP3
Here is the full code of a sample to replicate the problem:


using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;

namespace MemTest
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button btnHibernateAll;
private System.Windows.Forms.Button btnFree;
private System.Windows.Forms.Button btnCreateChunk;
private System.Windows.Forms.TextBox tbMemo;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button4;
public ArrayList al=null;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnHibernateAll = new System.Windows.Forms.Button();
this.btnFree = new System.Windows.Forms.Button();
this.btnCreateChunk = new System.Windows.Forms.Button();
this.tbMemo = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
//
// btnHibernateAll
//
this.btnHibernateAll.Location = new System.Drawing.Point(160, 0);
this.btnHibernateAll.Size = new System.Drawing.Size(80, 32);
this.btnHibernateAll.Text = "Hibernate";
this.btnHibernateAll.Click += new
System.EventHandler(this.btnHibernateAll_Click);
//
// btnFree
//
this.btnFree.Location = new System.Drawing.Point(80, 0);
this.btnFree.Size = new System.Drawing.Size(80, 32);
this.btnFree.Text = "Free";
this.btnFree.Click += new System.EventHandler(this.btnFree_Click);
//
// btnCreateChunk
//
this.btnCreateChunk.Size = new System.Drawing.Size(80, 32);
this.btnCreateChunk.Text = "Allocate";
this.btnCreateChunk.Click += new
System.EventHandler(this.btnCreateChunk_Click);
//
// tbMemo
//
this.tbMemo.Location = new System.Drawing.Point(0, 32);
this.tbMemo.Multiline = true;
this.tbMemo.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.tbMemo.Size = new System.Drawing.Size(232, 200);
this.tbMemo.Text = "";
//
// button1
//
this.button1.Location = new System.Drawing.Point(0, 264);
this.button1.Size = new System.Drawing.Size(80, 32);
this.button1.Text = "Close";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button4
//
this.button4.Location = new System.Drawing.Point(144, 232);
this.button4.Size = new System.Drawing.Size(88, 24);
this.button4.Text = "GC Collect";
this.button4.Click += new System.EventHandler(this.button4_Click);
//
// Form1
//
this.ClientSize = new System.Drawing.Size(234, 295);
this.Controls.Add(this.button4);
this.Controls.Add(this.button1);
this.Controls.Add(this.btnHibernateAll);
this.Controls.Add(this.btnFree);
this.Controls.Add(this.btnCreateChunk);
this.Controls.Add(this.tbMemo);
this.Text = "Memory test";

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>

static void Main()
{
Application.Run(new Form1());
}

private void btnHibernateAll_Click(object sender, System.EventArgs e)
{

int res=WinAPI.SendMessage(0xffff,0x03FF,0,0);
tbMemo.Text+="Hibernate sent!"+res.ToString()+"\r\n";
}

private void btnCreateChunk_Click(object sender, System.EventArgs e) {
// System.Collections.ArrayList al=new ArrayList();
if (al==null)
al=new ArrayList();
int Count=10000;
ItemInfo ii=null;
Exception eeee=null;

GC.Collect();
long before=System.GC.GetTotalMemory(true);
this.tbMemo.Text+="Start GC Heap
Mem(kb):"+((int)(before/1024)).ToString()+"\r\n";;

for (int i=0;i<Count;i++) {
try{
ii=new ItemInfo((i.ToString()),new string('T',i%17),new
string('1',i%19),new string('9',i%7));
al.Add(ii);
}catch(Exception ee){
eeee=ee;
break;
}
}
long after=System.GC.GetTotalMemory(true);


if (eeee!=null)
this.tbMemo.Text+="Exception: "+eeee.ToString()+"\r\n";

this.tbMemo.Text+="End CG Heap
Mem(kb):"+((int)(after/1024)).ToString()+"\r\n";


long ss=System.Runtime.InteropServices.Marshal.SizeOf(ii);
ii=new ItemInfo("0","0","0","0");

ss=(after-before)/Count;
}

private void btnFree_Click(object sender, System.EventArgs e) {
if (this.al!=null)
this.al=null;

this.tbMemo.Text+="Current GC
Heap:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}

private void button1_Click(object sender, System.EventArgs e) {
Close();
}

private void button4_Click(object sender, System.EventArgs e) {
GC.Collect();
this.tbMemo.Text+="Current GC
Heap:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}

}

public class ItemInfo {
public string TruckNo="";
public string ID="";
public string Code128="";
public DateTime Time;
public string Name="";
public static string delimiter=";";

/// <summary>
/// 0-áÒÔÉËÕÌßÔ Å ÚÁÒÅÄÅÎ ÏÔ ÂÁÚÁ
/// 1-áÒÔÉËÕÌßÔ Å ÚÁÒÅÄÅÎ ÏÔ ÕÓÔÒÏÊÓÔ×Ï, ÎÑÍÁ ÇÏ × ÂÁÚÁÔÁ
/// 2-áÒÔÉËÕÌßÔ Å ÉÚÔÒÉÔ!!!
/// </summary>
public int Status=0;

public ItemInfo(string truckNo, string id, string code128, string
name) {
TruckNo=truckNo;
ID=id;
Code128=code128;
Name=name;
Time=DateTime.Now;
}

/// <summary>
/// óßÚÄÁ×Á ÄÁÎÉ ÚÁ ÁÒÔÉËÕÌ ÏÔ ÒÅÄ
/// </summary>
/// <param name="Row"></param>
public ItemInfo(string Row) {
string str=Row;
string cell;
int col=0;
int endpos=0,startpos=0;

while(((endpos = str.IndexOf(delimiter,startpos)) >= 0)&&
(col<(5+1)) ) {
cell=str.Substring(startpos,endpos-startpos);
cell=cell.Trim();
startpos=endpos+1;
switch (col) {
case 0: ID=cell; break;
case 1: Code128=cell; break;
case 2: TruckNo=cell; break;
case 3:
try {

Time=DateTime.ParseExact(cell,"dd.MM.yy",System.Globalization.CultureInfo.CurrentCulture);
}
catch{}
break;
}

col++;
}
//ðÏÓÌÅÄÎÁÔÁ ËÏÌÏÎÁ
try {
cell=str.Substring(startpos);
DateTime
dTime=DateTime.ParseExact(cell,"hh:mm:ss",System.Globalization.CultureInfo.CurrentCulture);
Time=Time.Add(new TimeSpan(dTime.Hour,dTime.Minute,dTime.Second));

}
catch{}

}
public override string ToString() {
if ((Code128!=null)&&(Code128.Length>0))
return Code128;
if ((ID!=null)&&(ID.Length>0))
return ID;
return ID;
//return base.ToString ();
}



public string ToStringDB() {
return
ID+delimiter+Code128+delimiter+TruckNo+delimiter+Time.ToString("dd.MM.yy")+delimiter+Time.ToString("hh:mm:ss");
}


//òÁ×ÎÉ ÓÁ ÓÁÍÏ ÁËÏ ÉÍ Å ÒÁ×ÅÎ ËÏÄ128 É
public override bool Equals(object a) {
if ( (((ItemInfo) a).Code128.Length>0) &&
(this.Code128.Length>0) &&
(this.Code128==((ItemInfo) a).Code128))
return true;
return false;
}




}

public class WinAPI{

[System.Runtime.InteropServices.DllImport("coredll.dll")]
public static extern int SendMessage(int hWnd, uint Msg, uint WParam,
uint
LParam ) ;

[System.Runtime.InteropServices.DllImport("coredll.dll")]
public static extern int FindWindowW(String lpClassName,String
lpWindowName );

// public extern static int SendMessage(IntPtr hwnd,uint msg, uint
wParam,
uint lParam);
}


}

//----------------------------------------------------------------------------------------------------------

Steps to do:
1.Start the program.(initially I get about 9mb free program memory)
2. Press twice the "Allocate" button.(You will see there are 4MB in the
GC
heap)
3. Press "Free"
4. Press GC Collect(You will see there are 0,3MB in the GC heap))
NOW:
5A. Try to start Word or IExplorer. I get the out of memory box
OR
5B: Press twice the "Allocate" button. The memory would be allocated
inside
the GC heap.

For me this means that GC simply does not release the memory it uses,
even
in a OOM situation.

Thanks in advance.






----- Original Message -----
From: "<ctacke/>" <ctacke_AT_OpenNETCF_com>
Newsgroups: microsoft.public.dotnet.framework.compactframework
Sent: Thursday, April 20, 2006 10:01 PM
Subject: Re: GC does not release unused memory to the system in WinCE
4.2


You have a misconception about how the GC works. Calling Collect does
not instantly free memory in any measurable way. It walks the GC Heap
marking reachable objects and then frees those that are not reachable.
The GC Heap won't necessarily compact or shrink when you call it.

At first glance I would expect the GC Heap to shrink back to 1MB on
the first collection after you destroy your ArrayList (if this is CF
2.0 anyway).

This is not all of the code, so we can't exactly test it ourselves.

-Chris




I've read in several sources that during garbage collection, the GC
will release unused pages from the GC heap back to the OS.

Well this is not the case with my app.
Here is the code to reproduce the behaviour:
This sample code simply allocate about 2MB

private void btnCreateMemChunk_Click(object sender, System.EventArgs
e) {
if (al==null)
al=new ArrayList();
int Count=10000;
ItemInfo ii=null;

Exception eeee=null;

GC.Collect();
long before=System.GC.GetTotalMemory(true);

this.tbMemo.Text+="Start
Mem(kb):"+((int)(before/1024)).ToString()+"\r\n";;

for (int i=0;i<Count;i++) {
try{
ii=new ItemInfo((i.ToString()),new string('T',i%17),new
string('1',i%19),new string('9',i%7));
al.Add(ii);
}catch(Exception ee){
eeee=ee;
break;
}

}
long after=System.GC.GetTotalMemory(true);
if (eeee!=null)
this.tbMemo.Text+="Exception: "+eeee.ToString()+"\r\n";
this.tbMemo.Text+="End
Mem(kb):"+((int)(after/1024)).ToString()+"\r\n";

}

This code frees the array:
private void btnFree_Click(object sender, System.EventArgs e) {
if (this.al!=null)
this.al=null;
GC.Collect();

this.tbMemo.Text+="Allocated:"+GC.GetTotalMemory(false).ToString()+"\r\n";
}



When I start the program , it takes about 7MB of program memory, out
of 9MB free at the beginning.

First time I press the CreateMemChunk the allocated memory stays the
same, second time allocated memory goes to nearly 9MB.
(If I press again I get the Out of memory dialog.)

So I've pressed the button 2 times. CG tells me it has allocated
about 4.2 MB of memory, and all programm memory on the device is
allocated also.

Now I press btnFree. CG tells me it has allocated about 0,3 MB of
memory.

But all of the program memory on the device is still taken from the
GC !!!

If I try to run some other application I get the OOM dialog....

But if I press the CreateMemChunk it runs(even twice), which means
that there is plenty of space in th GC heap!!!!

So what can I do to make the GC release the unused 4MB of heap to the
system?

I've tried sending WM_HIBERNATE to CLR :

int res=WinAPI.SendMessage(0xffff,0x03FF,0,0);

but this does not help.

please help me,

thanks in advance
 

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