Tell me, please, is it possible to manually manage memory in C #? For example, to delete objects when you need it, but not when the garbage collector likes.
3 answers
It is possible , but to a limited extent, but generally not necessary. If you use classes that implement IDisposable
, do not forget about Dispose()
(or about the using
construct)
- And the overhead projector on the big turns? - Merlin
- oneDepends on your expectations. Have your tests already shown that the bottleneck is a garbage collector? - nitrocaster
- I just read the article (section "Memory shortage") rsdn.ru/article/dotnet/GC.xml , the test took 2 seconds to c ++, and as many as 20 minutes in C #, is it really that sad? - Merlin
- one@Merlin, you need to use the right tools for the right goals. If you are faced with the situation described in the article (they occupied almost all the free memory), then you will most likely have to use an unmanaged heap. - nitrocaster
I reread the article , which points to the vehicle. The article is dated 2006 year, since those times the garbage collection algorithms have changed a lot. I compiled and launched projects on a test 32-bit machine.
(Note that a 64-bit machine has much more available address space, and the garbage collector will work much less. If you have a 64-bit machine at hand, check, please compare and write down!)
C ++: In the source code I fixed 10,000 on PermanentObjectsCount
on line 102, there was an obvious error. Compiled Release, running from the command line 5 times, with different options.
Test parameters: PermanentObjectsCount
: the number of “dead” objects that only occupy memory and interfere with allocation algorithms
QUICK_HEAP_OPERATOR_NEW
: a macro connecting a custom memory allocator
Results:
PermanentObjectsCount
= 1,QUICK_HEAP_OPERATOR_NEW
enabled:
Timestamp: 2.332243 / 2.285392 / 2.310033 / 2.329071 / 2.362159, average 2,3237796
ObjCount = 10001001
PermanentObjectsCount
= 1,QUICK_HEAP_OPERATOR_NEW
off
Timestamp: 3.123940 / 3.130930 / 3.113295 / 3.139964 / 3.104077, average 3.1224412
ObjCount = 10001001
PermanentObjectsCount
= 10 * 1000 * 1000,QUICK_HEAP_OPERATOR_NEW
included:
Timestamp: 7.175451 / 7.234413 / 7.241763 / 7.197331 / 7.226205, average 7.2150326
ObjCount = 20001000
PermanentObjectsCount
= 10 * 1000 * 1000,QUICK_HEAP_OPERATOR_NEW
disabled:
Timestamp: 8.823886 / 8.751605 / 8.908837 / 8.825099 / 8.806487, average 8.8231828
ObjCount = 20001000
Now C #:
Reconfigured the project to use .NET 4.5. Compiled Release, running from the command line 5 times, with different options.
Test parameters:
PermanentObjectsCount
: the number of "dead" objects that only occupy memory and interfere with the garbage collector
The default settings for the garbage collector in app.config
( <gcConcurrent enabled="false"/>
disabled):
PermanentObjectsCount
= 1
00: 00: 01.6406347 / 1.5898657 / 1.6479692 / 1.6528195 / 1.5796367, average 1.62218516
The number of created objects: 10001001
Number of generation garbage collection 0: 571
Number of generation 1 garbage collection: 145
Number of generation 2: 3 garbage collections
PermanentObjectsCount
= 10 * 1000 * 1000
00: 00: 15.5065372 / 16.4615645 / 16.5447826 / 15.4274263 / 16.3169154, average 16.0514452
The number of created objects: 20001000
Number of generation garbage collections 0: 758
Number of garbage collection generation 1: 256
The number of garbage collections generation 2: 10
Requested the server garbage collector in app.config
( <gcServer enabled="true"/>
):
PermanentObjectsCount
= 1
00: 00: 01.9130864 / 1.8731294 / 1.8649931 / 1.6231794 / 1.8787851, average 1.83063468
The number of created objects: 10001001
Number of generation garbage collections 0: 243
Number of garbage collection generation 1: 43
Number of generation 2: 3 garbage collections
PermanentObjectsCount
= 10 * 1000 * 1000
00: 00: 14.2030724 / 13.8427775 / 14.0393846 / 14.5345637 / 14.2312238, average 14.1702044
The number of created objects: 20001000
Number of generation garbage collections 0: 249
Number of generation 1 garbage collection: 54
Number of generation 2 garbage collections: 5
Findings:
The garbage collector then the garbage collector now - different things.
- In the case of the presence of 10 million static objects, which is quite rare for real programs, the .NET garbage collector has significantly accelerated, and now it is not doubled from the native allocator 86 times. The server allocator is a little better at handling the load.
- In the case when the work of memory management is not difficult, the .NET allocator wins more than one and a half times.
A custom allocator for native code speeds up the work, but I wouldn’t use an untested allocator in serious code: it would be better than C ++ embedded, the compiler developers would no doubt replace their allocator with this one.
- How much free memory was on your system during the test? - nitrocaster
- @Flammable: tried with empty memory and loaded (2 instances of Visual Studio, MS Word + MS Excel, Paint.NET and 50 large pictures, VirtualBox). The results differ slightly. --- However, here we are already talking about the performance of the virtual memory subsystem. On the machine where the measurements took place, the fastest screw. System - 32-bit Windows 7. - VladD
- @VladD, a drop in GC performance in the test was associated with ~ 90% memory usage. As I understand it, in your case there was enough free memory so that the GC does not brush the heap too often. - nitrocaster
- @Flammable: Hardly. Process Explorer showed almost full memory load. The number of passes of the 0th generation garbage collection, by the way, is not so different. The lower number for older generations is fully explained by the increased quality of the GC and the improved jit-optimizer. - VladD
If you work with COM objects, they need to call the Marshal.ReleaseComObject method.
IDisposable
is enough, plus the possible manual launch ofGC.Collect
. - VladD