![]() | |
![]() |
| | Thread Tools | Search this Thread | Display Modes |
#1
| |||
| |||
|
#2
| |||
| |||
|
|
I'm seeing some very strange behavior with the GC under C#. I'm seeing it in two different applications that have nothing in common except that they both use and release a lot of memory in BYTE or CHAR arrays. The first one is easier to describe, but the behavior is similar in both. WinXP on a machine with 512Meg and NO swap file (for performance). I'm using a winForms app as a testing harness against some networking code that stores results in a MemoryStream. The results are processed, then all objects are released. Watching system memory in Task Manager shows that memory is getting eaten up, but never freed. Because there's no swap file, Windows can't increase the memory, so the application will eventually crash with an out-of-memory error. Interestingly enough, it will actually do this even though it has plenty of memory waiting to be collected. The MemoryStream class increases capacity as needed by allocating a new buffer of double the existing size and copying the data to the new buffer. I'm starting it at one 1MB, so it goes 1,2,3,4,8,16,.... When it gets to the point where it needs to allocate a 256 meg block, it will fail with a memory error because there is only about 200MB of system memory left. The annoyance is that it actually has enough memory, if it would just collect the old buffers. You see, at this point, the MemoryStream is using a 128MB buffer, but has 127MB in discarded buffers (1+2+4+8+16+32+64). If I throw in calls to GC.Collect(), it works fine. I had first thought that the GC just wasn't collecting because I was using all of the CPU, but it just never seems to collect. After a smaller run, I left the testing harness up for 30 minutes after everything has been freed, and it just doesn't collect, however, if I put a call to GC.GetTotalMemory(true) on a button, it instantly frees all of the memory. GC.GetTotalMemory() reports 83Meg used then GC.GetTotalMemory(true) shows 461K. I have spread calls to GC.Collect through the code so solve the problem, but it seems foolish to me. Any thoughts? By the way, this happens inside and outside of VS as well as in code compiled for Release. And, of course, I already have a call to IDisposable.Dispose() on the MemoryStream. |
#3
| |||
| |||
|
|
#1: having no swap file isn't the best idea in the world, as the swap file is also used for a backing store for various operations. Windows will not swap unless it thinks it needs it. |
|
#2: are you sure that you are releasing the reference to the memorystream object after each run? If the instance of the class is still sitting around holding a reference that could be a problem. Alternately, your app may simply have a memory usage pattern that isn't well-recognized by the GC, although the GC is definitely supposed to collect more often as system memory runs out. So in this case, perhaps there is a bug. |
#4
| |||
| |||
|
|
I've been running without swap files (WinXP and Win2003) or with very small swap files (Win2K server) for a couple of years now. The performance improvements have been significant on all machines, especially servers that are already doing database or file I/O. Laptops also see a huge performance improvement because 2.5-inch drives are so slow. I've never seen a problem, but I'm open to suggestions. |
|
Attached you will find a test app that shows what's going on. Just compile it to a console application. Note that I'm using Framework 1.1, but it should compile to 1.0 just fine. In fact, I'm curious to see how it works on 1.0. It should be pretty clear. I've set it to stop when the MemoryStream capacity jumps to 128Meg. You can change the constants if you want to watch your swap file fill forever. You'll note that I have a 5-second sleep in there. This was to give the machine some time to think about collecting. I've yet to see it do so. |
I see the memory use go down much![]() |
| Thread Tools | Search this Thread |
| Display Modes | |
| |