.NET Forum / .NET Framework / Compact Framework / April 2006
GC does not release unused memory to the system in WinCE 4.2
|
|
Thread rating:  |
MS - 20 Apr 2006 18:38 GMT 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
<ctacke/> - 20 Apr 2006 20:01 GMT 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. [quoted text clipped - 75 lines] > > thanks in advance MS - 21 Apr 2006 09:59 GMT 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 [quoted text clipped - 90 lines] >> >> thanks in advance <ctacke/> - 21 Apr 2006 15:07 GMT 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. > [quoted text clipped - 414 lines] >>> >>> thanks in advance MS - 22 Apr 2006 10:13 GMT 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 [quoted text clipped - 424 lines] >>>> >>>> thanks in advance <ctacke/> - 22 Apr 2006 11:52 GMT CF 2.0 for CE 4.2:
http://www.microsoft.com/downloads/details.aspx?FamilyID=6548dd53-a418-42d9-a481 -19ba3ceca1a6&displaylang=en%20
-Chris
> I'm running on Windows CE 4.2 device, in fact I've tested this code on 4 > different platforms. [quoted text clipped - 461 lines] >>>>> >>>>> thanks in advance MS - 22 Apr 2006 12:12 GMT 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: > [quoted text clipped - 469 lines] >>>>>> >>>>>> thanks in advance TDC - 25 Apr 2006 16:38 GMT MS, did this fix your problem?
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|