Скомпилированы ли деревья выражений?
В моем понимании код JIT-ed никогда не освобождается из памяти во время работы программы. Означает ли это, что повторное вызов .Compile()
в деревьях выражений утечки памяти?
Следствием этого было бы только скомпилировать деревья выражений в статических конструкторах или кэшировать их каким-либо другим способом, что может быть не так просто. Правильно?
Ответы
Ответ 1
Они, вероятно, GCed... LambdaExpression.Compile()
использует LambdaCompiler.Compile(LambdaExpression, DebugInfoGenerator)
, через один из конструкторов LambdaCompiler
использует DynamicMethod
, который из MSDN:
Определяет и представляет динамический метод, который может быть скомпилирован, выполнен и отброшен. Отброшенные методы доступны для сбора мусора.
Ответ 2
Я попытался проверить это, непрерывно генерируя деревья выражений в фоновом режиме, а затем собирать все мусор и контролировать используемое пространство в потоке графического интерфейса.
Казалось бы, использование памяти остается устойчивым примерно на 655000 байт через пару часов. Поэтому я бы сказал, что это безопасно, чтобы сходить с деревьями выражений.
![Использование памяти дерева выражений]()
Если кому-то нужен мой взломанный тестовый код, вот он:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading;
using System.Windows.Forms;
namespace Experiments
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Ensuring that always the same amount of memory is used for point storage.
bytesUsed = new Queue<long>(1000);
var points = chart1.Series[0].Points;
for (var i = 0; i < 1000; ++i)
{
bytesUsed.Enqueue(0);
points.Add(0);
}
thread = new Thread(ThreadMethod);
thread.Start();
timer1.Interval = 10000;
timer1.Enabled = true;
timer1_Tick(null, null);
}
private readonly Queue<long> bytesUsed;
private void timer1_Tick(object sender, EventArgs e)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
bytesUsed.Dequeue();
bytesUsed.Enqueue(GC.GetTotalMemory(false));
var points = chart1.Series[0].Points;
points.Clear();
foreach (var value in bytesUsed)
points.Add(value);
}
private Thread thread;
private volatile bool stopping;
private void ThreadMethod()
{
var random = new Random();
while (!stopping)
{
var constant = Expression.Constant(random.Next(), typeof(int));
var param = Expression.Parameter(typeof(int));
var mul = Expression.Multiply(param, constant);
var add = Expression.Multiply(mul, param);
var sub = Expression.Subtract(add, constant);
var lambda = Expression.Lambda<Func<int, int>>(sub, param);
var compiled = lambda.Compile();
}
}
protected override void Dispose(bool disposing)
{
stopping = true;
if (thread != null && disposing)
thread.Join();
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
}
}