if include outside reference inside where predicate
memory not releases.
let's have list<object>
if write where predicate
:
list<object> mylist = new list<object>(); ... mylist.add(object); ... expression<func<object,bool>> predicate = p => mylist.contains(p);
even if make mylist = null
or predicate = null
, not releasing memory.
i have list<object> itemsource
binded datagrid
. make it's itemsource null, disposing datagrid, datagrid null. . have analyzed issue ants memory profiler 7.4. shows me because of wherepredicate
holding reference.
if change wherepredicate
in dispose()
, memory getting released.
expression<func<object,bool>> predicate = p => p.id == 0;
that means removing reference in wherepredicate
.
mmmh... interesting... expression<>
cause closure... didn't know...
the end result: predicate doesn't have reference mylist
i'll explain:
private static bool isdebug() { // taken http://stackoverflow.com/questions/2104099/c-sharp-if-then-directives-for-debug-vs-release object[] customattributes = assembly.getexecutingassembly().getcustomattributes(typeof(debuggableattribute), false); if ((customattributes != null) && (customattributes.length == 1)) { debuggableattribute attribute = customattributes[0] debuggableattribute; return (attribute.isjitoptimizerdisabled && attribute.isjittrackingenabled); } return false; } static void main(string[] args) { // check x86 or x64 console.writeline(intptr.size == 4 ? "x86" : "x64"); // check debug/release console.writeline(isdebug() ? "debug, useless benchmark" : "release"); // check if debugger attached console.writeline(system.diagnostics.debugger.isattached ? "debugger attached, useless benchmark!" : "debugger not attached"); console.writeline(); { long memory = gc.gettotalmemory(true); // big array, big enough can see allocation in // memory byte[] buffer = new byte[10000000]; console.writeline("just allocated array: {0}", gc.gettotalmemory(true) - memory); // list<>, containing reference buffer list<object> mylist = new list<object>(); mylist.add(buffer); console.writeline("added list<>: {0}", gc.gettotalmemory(true) - memory); // want sure buffer referenced @ least // point gc.keepalive(buffer); // setting buffer = null useless, because // list<> has anothe reference buffer = null; console.writeline("buffer = null: {0}", gc.gettotalmemory(true) - memory); // if clear() list<>, last reference buffer // removed, , buffer can freed mylist.clear(); console.writeline("mylist.clear(): {0}", gc.gettotalmemory(true) - memory); gc.keepalive(mylist); } console.writeline(); gc.collect(); { long memory = gc.gettotalmemory(true); // big array, big enough can see allocation in // memory byte[] buffer = new byte[10000000]; console.writeline("just allocated array: {0}", gc.gettotalmemory(true) - memory); // list<>, containing reference buffer list<object> mylist = new list<object>(); mylist.add(buffer); console.writeline("added list<>: {0}", gc.gettotalmemory(true) - memory); // want sure buffer referenced @ least // point gc.keepalive(buffer); // setting buffer = null useless, because // list<> has reference buffer = null; console.writeline("buffer = null: {0}", gc.gettotalmemory(true) - memory); // want sure list<> referenced @ least // point gc.keepalive(mylist); // if set null mylist, last reference mylist // , buffer removed mylist = null; console.writeline("mylist = null: {0}", gc.gettotalmemory(true) - memory); } console.writeline(); gc.collect(); { long memory = gc.gettotalmemory(true); // big array, big enough can see allocation in // memory byte[] buffer = new byte[10000000]; console.writeline("just allocated array: {0}", gc.gettotalmemory(true) - memory); // list<>, containing reference buffer list<object> mylist = new list<object>(); mylist.add(buffer); console.writeline("added list<>: {0}", gc.gettotalmemory(true) - memory); // predicate, containing reference mylist expression<func<object, bool>> predicate1 = p => mylist.contains(p); console.writeline("created predicate p => mylist.contains(p): {0}", gc.gettotalmemory(true) - memory); // second predicate, **not** containing reference // mylist expression<func<object, bool>> predicate2 = p => p.gethashcode() == 0; console.writeline("created predicate p => p.gethashcode() == 0: {0}", gc.gettotalmemory(true) - memory); // want sure buffer referenced @ least // point gc.keepalive(buffer); // setting buffer = null useless, because // list<> has reference buffer = null; console.writeline("buffer = null: {0}", gc.gettotalmemory(true) - memory); // want sure list<> referenced @ least // point gc.keepalive(mylist); // if set null mylist, interesting thing happens: // memory freed, if predicate1 still alive! mylist = null; console.writeline("mylist = null: {0}", gc.gettotalmemory(true) - memory); // want sure predicates referenced @ // least point gc.keepalive(predicate1); gc.keepalive(predicate2); try { // compile predicate1 func<object, bool> fn = predicate1.compile(); // , execute it! fn(5); } catch (nullreferenceexception) { console.writeline("predicate1 'pointing' null mylist"); } } }
this sample test in 3 parts: basic point big byte[]
array allocated, , through checking how memory allocated check if array still allocated in way. it important code executed in release mode without debugger (ctrl+f5). if don't it, you'll warning when program starts
the first 2 "parts" show list<>
keep "alive" items references (so byte[]
in case), , freeing list<>
or .clear()
ing lets gc collect byte[]
.
the third part more interesting: there both list<>
, expression<>
... both seems keep reference byte[]
, illusion. expression<>
written causes compiler generate "closure" around mylist<>
variable. using ilspy quite easy see:
program.<>c__displayclassb <>c__displayclassb = new program.<>c__displayclassb(); <>c__displayclassb.mylist = new list<object>(); <>c__displayclassb.mylist.add(buffer3); parameterexpression parameterexpression = expression.parameter(typeof(object), "p"); expression<func<object, bool>> predicate = expression.lambda<func<object, bool>>(expression.call(expression.field(expression.constant(<>c__displayclassb), fieldof(program.<>c__displayclassb.mylist)), methodof(list<object>.contains(!0)), new expression[] { parameterexpression }), new parameterexpression[] { parameterexpression });
(if don't have ilspy, can take @ code generated online compiler tryroslyn simpler sample)
an hidden class <>c__displayclassb
generated compiler, field mylist
. instead of having "local" variable mylist
, method has local variable <>c__displayclassb
has field mylist
. predicate1
doesn't directly keep reference mylist
, has reference variable <>c__displayclassb
(see expression.constant(<>c__displayclassb)
?), when
<>c__displayclassb.mylist = null;
the predicate1
still has reference <>c__displayclassb
, <>c__displayclassb.mylist
null
, there no more references mylist
.
Comments
Post a Comment