Mmmh ... ilginç ... hatta Expression<>
neden kapanması ... ben bilmiyordum ...
Sonuç: yüklem myList
başvuru yok Ben anlatacağım:
private static bool IsDebug()
{
// Taken from 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] as 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 is attached
Console.WriteLine(System.Diagnostics.Debugger.IsAttached ? "Debugger attached, USELESS BENCHMARK!" : "Debugger not attached");
Console.WriteLine();
{
long memory = GC.GetTotalMemory(true);
// A big array, big enough that we can see its allocation in
// memory
byte[] buffer = new byte[10000000];
Console.WriteLine("Just allocated the array: {0}", GC.GetTotalMemory(true) - memory);
// A List<>, containing a reference to the buffer
List<object> myList = new List<object>();
myList.Add(buffer);
Console.WriteLine("Added to the List<>: {0}", GC.GetTotalMemory(true) - memory);
// We want to be sure that buffer is referenced at least up to
// this point
GC.KeepAlive(buffer);
// But clearly setting buffer = null is useless, because the
// List<> has anothe reference
buffer = null;
Console.WriteLine("buffer = null: {0}", GC.GetTotalMemory(true) - memory);
// If I Clear() the List<>, the last reference to the buffer
// is removed, and now the buffer can be freed
myList.Clear();
Console.WriteLine("myList.Clear(): {0}", GC.GetTotalMemory(true) - memory);
GC.KeepAlive(myList);
}
Console.WriteLine();
GC.Collect();
{
long memory = GC.GetTotalMemory(true);
// A big array, big enough that we can see its allocation in
// memory
byte[] buffer = new byte[10000000];
Console.WriteLine("Just allocated the array: {0}", GC.GetTotalMemory(true) - memory);
// A List<>, containing a reference to the buffer
List<object> myList = new List<object>();
myList.Add(buffer);
Console.WriteLine("Added to the List<>: {0}", GC.GetTotalMemory(true) - memory);
// We want to be sure that buffer is referenced at least up to
// this point
GC.KeepAlive(buffer);
// But clearly setting buffer = null is useless, because the
// List<> has another reference
buffer = null;
Console.WriteLine("buffer = null: {0}", GC.GetTotalMemory(true) - memory);
// We want to be sure that the List<> is referenced at least
// up to this point
GC.KeepAlive(myList);
// If I set to null myList, the last reference to myList
// and to buffer are removed
myList = null;
Console.WriteLine("myList = null: {0}", GC.GetTotalMemory(true) - memory);
}
Console.WriteLine();
GC.Collect();
{
long memory = GC.GetTotalMemory(true);
// A big array, big enough that we can see its allocation in
// memory
byte[] buffer = new byte[10000000];
Console.WriteLine("Just allocated the array: {0}", GC.GetTotalMemory(true) - memory);
// A List<>, containing a reference to the buffer
List<object> myList = new List<object>();
myList.Add(buffer);
Console.WriteLine("Added to the List<>: {0}", GC.GetTotalMemory(true) - memory);
// A predicate, containing a reference to myList
Expression<Func<object, bool>> predicate1 = p => myList.Contains(p);
Console.WriteLine("Created a predicate p => myList.Contains(p): {0}", GC.GetTotalMemory(true) - memory);
// A second predicate, **not** containing a reference to
// myList
Expression<Func<object, bool>> predicate2 = p => p.GetHashCode() == 0;
Console.WriteLine("Created a predicate p => p.GetHashCode() == 0: {0}", GC.GetTotalMemory(true) - memory);
// We want to be sure that buffer is referenced at least up to
// this point
GC.KeepAlive(buffer);
// But clearly setting buffer = null is useless, because the
// List<> has another reference
buffer = null;
Console.WriteLine("buffer = null: {0}", GC.GetTotalMemory(true) - memory);
// We want to be sure that the List<> is referenced at least
// up to this point
GC.KeepAlive(myList);
// If I set to null myList, an interesting thing happens: the
// memory is freed, even if the predicate1 is still alive!
myList = null;
Console.WriteLine("myList = null: {0}", GC.GetTotalMemory(true) - memory);
// We want to be sure that the predicates are referenced at
// least up to this point
GC.KeepAlive(predicate1);
GC.KeepAlive(predicate2);
try
{
// We compile the predicate1
Func<object, bool> fn = predicate1.Compile();
// And execute it!
fn(5);
}
catch (NullReferenceException)
{
Console.WriteLine("predicate1 is 'pointing' to a null myList");
}
}
}
Bu üç bölümden örnek testtir: temel nokta büyük byte[]
dizi tahsis edilir olması ve tahsis ne kadar bellek kontrol yoluyla biz dizi kontrol hala bir şekilde tahsis edilir. kod hata ayıklayıcı (CTRL + F5) olmadan yayın modunda çalıştırıldığında çok önemlidir. Eğer bunu yapmazsanız programı
ilk iki "bölümlerini" başladığında, bir uyarı alırsınız bir List<>
başvurduğu öğeleri (şimdiye byte[]
yılında "canlı" tutmak olmadığını göstermek için sadece Bu durumda) ve GC byte[]
toplamak sağlar ing List<>
veya .Clear()
kurtararak. hem List<>
ve Expression<>
... Hem byte[]
bir başvuru tutmak gibi görünüyor vardır, ancak bu bir yanılsama:
üçüncü bölümü daha ilginç. Yazılı olarak Expression<>
, derleyicinin myList<>
değişkeninin etrafında bir "kapanma" oluşturmasına neden olur. ILSpy kullanma bunu görmek oldukça kolaydır:
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
});
bir gizli (Eğer ILSpy yoksa daha basit ve hızlı numune için çevrimiçi derleyici TryRoslyn tarafından üretilen koda bir göz atabilirsiniz) myList
alanı ile derleyici tarafından <>c__DisplayClassb
sınıfı oluşturulur. Dolayısıyla, bir "yerel" değişkeni myList
yerine, yöntem myList
numaralı bir alana sahip <>c__DisplayClassb
yerel değişkenine sahiptir. predicate1
, myList
referansını doğrudan tutmaz, ancak <>c__DisplayClassb
değişkenine bir referansı vardır (bkz. Expression.Constant(<>c__DisplayClassb)
?), Bu yüzden
<>c__DisplayClassb.myList = null;
predicate1
hala <>c__DisplayClassb
referansı olan, ancak <>c__DisplayClassb.myList
null
, yani myList
için daha fazla referanslar olduğunda.
Lütfen sorunu gösteren kısa ama eksiksiz bir programı gösterin. Şüpheliyim * kullandığınız profilerle ilgili bir sorun olabilir ya da yanlış anlıyor olabilirsiniz. –
(Açıklamak için isteklere cevap vermeyecekseniz bir soru üzerine bir ödül koymanın pek bir anlamı yok ...) –
Hey john ... Projemde nasıl kullanıldığımı merak ettim ... Bu konuyu Ants bellek profiler ile analiz ettik .. Ama bana anonim tipler gösterir ... –