Jamie888 Ответов: 2

Использование памяти резко возрастает при использовании RDLC


Привет, у меня есть отчетная RDLC, предназначенная для WinForms. Я заметил, что всякий раз, когда RDLC загружается данными, память взлетает примерно на 10%. 20 Мб на запрос из базы данных. Ниже приведен мой сценарий:
1. Откройте winform с RDLC в нем.
2. Нажмите кнопку Отправить, чтобы вызвать вызов БД для получения данных.
3. RDLC загружает данные отображения ti.(именно здесь память начала расти на 20 МБ).
4. Нажмите еще раз кнопку Отправить с тем же параметром, RDLC снова перезагрузится(память выстрелит еще на ~20 МБ)

Я очистил от любого источника до нагрузки отчетами. Ниже приведены мои коды:
protected Microsoft.Reporting.WinForms.ReportViewer rpvBaseReport;

rpvBaseReport.ProcessingMode = ProcessingMode.Local;
rpvBaseReport.LocalReport.SetBasePermissionsForSandboxAppDomain(new PermissionSet(PermissionState.Unrestricted));
rpvBaseReport.LocalReport.DataSources.Clear();

rpvBaseReport.LocalReport.DataSources.Add(reportDataSource);
//further logic here
rpvBaseReport.RefreshReport();


Любая идея будет приветствоваться. Спасибо.

Что я уже пробовал:

1. используется профилировщик производительности для отслеживания использования памяти.
2. Поиск в интернете для решения, пытались ReleaseSandBoxAppDomain(), метод Dispose (), но безрезультатно.

Mehdi Gholam

Как и следовало ожидать, не беспокойтесь об этом, если только ваше приложение не выйдет из строя или не будет работать неправильно.

Jamie888

Как сказать, сэр?

Mehdi Gholam

Как сказать что?

Jamie888

Как сказать, что это ожидается? Является ли это общей проблемой для RDLC?

2 Ответов

Рейтинг:
2

MetalKid007

Я довольно поздно пришел к этому, но у меня есть реальное решение, и я могу объяснить почему!

It turns out that LocalReport here is using .NET Remoting to dynamically create a sub appdomain and run the report in order to avoid a leak internally somewhere. We then notice that, eventually, the report will release all the memory after 10 to 20 minutes. For people with a lot of PDFs being generated, this isn't going to work. However, the key here is that they are using .NET Remoting. One of the key parts to Remoting is something called "Leasing". Leasing means that it will keep that Marshal Object around for a while since Remoting is usually expensive to setup and its probably going to be used more than once. LocalReport RDLC is abusing this.

По умолчанию время аренды составляет... 10 минут! Кроме того, если что-то делает различные звонки в него, это добавляет еще 2 минуты к времени ожидания! Таким образом, это может быть случайным образом от 10 до 20 минут в зависимости от того, как выстраиваются звонки. К счастью, вы можете изменить время ожидания. К сожалению, вы можете установить это только один раз для каждого домена приложения... Таким образом, если вам нужно удаленное взаимодействие, отличное от генерации PDF, вам, вероятно, потребуется создать другую службу, запускающую его, чтобы вы могли изменить значения по умолчанию. Для этого все, что вам нужно сделать, это запустить эти 4 строки кода при запуске:

LifetimeServices.LeaseTime = TimeSpan.FromSeconds(5);
LifetimeServices.LeaseManagerPollTime = TimeSpan.FromSeconds(5);
LifetimeServices.RenewOnCallTime = TimeSpan.FromSeconds(1);
LifetimeServices.SponsorshipTimeout = TimeSpan.FromSeconds(5);

Вы увидите, что использование памяти начинает расти, а затем в течение нескольких секунд вы должны увидеть, что память начинает возвращаться вниз. Мне потребовалось несколько дней с помощью профилировщика памяти, чтобы действительно отследить это и понять, что происходит.

Вы не можете обернуть ReportViewer в оператор using (Dispose аварийно завершает работу), но вы должны быть в состоянии это сделать, если используете LocalReport напрямую. После этого распоряжается, вы можете позвонить в ГК.Collect() если вы хотите быть вдвойне уверены, что делаете все возможное, чтобы освободить эту память.

Надеюсь, это поможет!


Рейтинг:
2

Jamie888

Привет, я могу выйти с обходным путем после многих тестов. Я добавил следующие коды в свое приложение, а также файл app.config:

            rpvBaseReport.LocalReport.ExecuteReportInCurrentAppDomain(AppDomain.CurrentDomain.Evidence);           
rpvBaseReport.LocalReport.AddTrustedCodeModuleInCurrentAppDomain("assembly name here");


Что касается app.config:
<runtime>
<NetFx40_LegacySecurityPolicy enabled="true" />
</runtime>


Как и всякий раз, когда запускается событие(например, нажмите кнопку Отправить, чтобы извлечь данные из БД и визуализировать их в RDLC), новый экземпляр будет создан в новом домене приложения. Таким образом, память будет продолжать расти, и хотя ресурс будет освобожден с помощью соответствующих кодов(например, Dispose ()), он просто избавится от последнего домена приложения, в то время как предыдущие созданные домены приложений все еще будут находиться в кэше приложений.
Принудительное обращение к RDLC с просьбой использовать только один домен приложения предотвратит создание нового домена приложения и, таким образом, сократит использование памяти, и он будет освобождаться каждый раз, когда форма закрывается.
Пожалуйста, не стесняйтесь поправлять меня или добавлять какие-либо пункты, которые я пропустил. Спасибо.