Страница 1 из 2

Утечка в неуправляемой куче

Добавлено: 09 дек 2014, 16:33
walle93
Здравствуйте!
Не однократно поднимал вопрос по поводу расходования ОЗУ при построении отчетов. Использовал статьи приведенные на этом форуме, а так же три части статей по оптимизации отчетов. Но сегодня вскрылась одна особенность.
Есть код класса, реализующий работу со списком отчетов.

Код: Выделить всё

public class RegisteredReport 
        {
            public Stimulsoft.Report.StiReport mReport;
            public String mName;
            public String mReportCacheCompiledFolderName;
            public String mReportCacheCompiledFileName;
            public String mViolationTypes;
            public String mOriginalFoto;
            public DateTime mDateFrom;
            public DateTime mDateTo;
            public bool mLoadedFromAssembly = false;
            public int mCollate;

            public RegisteredReport(Stimulsoft.Report.StiReport report, String name, String violationTypes, String originalFoto, DateTime dateFrom, DateTime dateTo, int collate)
            {
                mReportCacheCompiledFolderName = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                mReportCacheCompiledFolderName = Path.Combine(mReportCacheCompiledFolderName, "Stimulsoft\\CompiledReports");
                mReportCacheCompiledFolderName = Path.Combine(mReportCacheCompiledFolderName, System.Runtime.InteropServices.RuntimeEnvironment.GetSystemVersion().ToString());
                mReportCacheCompiledFileName = Path.Combine(mReportCacheCompiledFolderName, report.GetReportAssemblyCacheName());
                if (File.Exists(mReportCacheCompiledFileName))
                {
                    File.Delete(mReportCacheCompiledFileName);
                }
                mReport = report;
                mName = name;
                mViolationTypes = violationTypes;
                mOriginalFoto = originalFoto;
                mDateFrom = dateFrom;
                mDateTo = dateTo;
                mCollate = collate;
            }

            public void ProcessReportBeforeUsage(DataTable Table)
            {
                if (File.Exists(mReportCacheCompiledFileName))
                {
                   // if (!mLoadedFromAssembly)
                    //{
                    mReport.Dictionary.Clear();
                    //mReport.CompiledReport.DataStore.Clear();
                    mReport.Dispose();
                    mReport =
                        Stimulsoft.Report.StiReport.GetReportFromAssembly(
                            mReportCacheCompiledFileName, false);
                    mLoadedFromAssembly = true;
                    //}
                }
                else //Compiled report does not exist
                {
                    mReport.RegData(Table);
                    if (!Directory.Exists(mReportCacheCompiledFolderName))
                        Directory.CreateDirectory(mReportCacheCompiledFolderName);
                    mReport.Compile(mReportCacheCompiledFileName);
                    mReport = mReport.CompiledReport;
                }
            }

        }
Метод ProcessReportBeforeUsage вызывается каждый раз, когда необходимо отрисовать отчет. К сведению, отчет представляет из себя один БЛАНК состоящий из 4-х страниц. То есть может быть цикл из 1000-100 000 итераций, и за каждую итерацию нужно отрисовать один БЛАНК и сохранять в файл.
Обнаружено, что после первых двух итераций(если убрать комментарии в указанном методе) в отчетах перестает отображаться статичная картинка(к примеру герб печати на одной из страниц), в то время как динамичные картинки(берущиеся из БД) отображаются корректно.
Если добавить закомментировать условие if (!mLoadedFromAssembly), которое в примере уже реализовано, то отчеты отображаются правильно, но происходит утечка памяти, и примерно уже к 1000 итерации выдается исключение OutOfMemory (размер достигает 1 ГБайт).
Получается, с комментариями код похож на тот, который рекомендуют здесь на форуме(то есть если первого прохода и сохранения в сборку каждый раз при отрисовке подгружать отчет из сохраненной сборки), а если с моей доработкой(после сохранения в сборку и однократной загрузки из нее) то пропадает изображение.

Утечку искал при помощи системного монитора и ссылки http://msdn.microsoft.com/ru-ru/magazine/cc163491.aspx

Прошу помощи!

Re: Утечка в неуправляемой куче

Добавлено: 11 дек 2014, 13:05
HighAley
Здравствуйте.

Нам необходимо дополнительное время для анализа проблемы.
Мы сообщим, когда получим какие-либо результаты.

Спасибо.

Re: Утечка в неуправляемой куче

Добавлено: 11 дек 2014, 14:05
walle93
Благодарю!
Очень надеюсь на вашу команду!

Re: Утечка в неуправляемой куче

Добавлено: 12 дек 2014, 16:38
Aleksey
Здравствуйте.

Всегда рады помочь.
Мы сообщим, когда получим какие-либо результаты.

Спасибо.

Re: Утечка в неуправляемой куче

Добавлено: 15 дек 2014, 15:32
Ivan
Здравствуйте.
walle93 писал(а):Если добавить закомментировать условие if (!mLoadedFromAssembly), которое в примере уже реализовано, то отчеты отображаются правильно, но происходит утечка памяти, и примерно уже к 1000 итерации выдается исключение OutOfMemory (размер достигает 1 ГБайт).
Это происходит потому, что вы указали в методе GetReportFromAssembly() второй параметр false, при этом сборка на диске не лочится, но считывается в память каждый раз и остаётся там до окончания работы приложения (особенность Net Framework). Вам надо установить этот параметр в true.
walle93 писал(а): Получается, с комментариями код похож на тот, который рекомендуют здесь на форуме(то есть если первого прохода и сохранения в сборку каждый раз при отрисовке подгружать отчет из сохраненной сборки), а если с моей доработкой(после сохранения в сборку и однократной загрузки из нее) то пропадает изображение.
Не совсем понятно, зачем отчет каждый раз заново загружать из сборки.
Загрузка из сборки обычно используется для того, чтобы каждый раз не перекомпилировать отчет, т.к. компиляция может занимать несколько секунд времени.

Мы немного изменили ваш метод, попробуйте использовать изменённый вариант и сообщите нам о результатах.

Код: Выделить всё

           public void ProcessReportBeforeUsage(DataTable Table)
            {
                if (mReport == null)
                {
                    if (File.Exists(mReportCacheCompiledFileName))
                    {
                        mReport = Stimulsoft.Report.StiReport.GetReportFromAssembly(mReportCacheCompiledFileName, true);
                        mLoadedFromAssembly = true;
                    }
                    else //Compiled report does not exist
                    {
                        if (!Directory.Exists(mReportCacheCompiledFolderName))
                            Directory.CreateDirectory(mReportCacheCompiledFolderName);
                        mReport.Compile(mReportCacheCompiledFileName);
                        mReport = mReport.CompiledReport;
                    }
                }

                mReport.RegData(Table);
            }
Спасибо.

Re: Утечка в неуправляемой куче

Добавлено: 15 дек 2014, 15:44
walle93
Спасибо за подробный ответ!!!
Вопрос: а конструкция if (mReport == null) зачем?
Ведь репорт никогда не будет равен НУЛЛ. Я его либо из файла еще в конструкторе загружу, либо из переданного элемента.

Re: Утечка в неуправляемой куче

Добавлено: 16 дек 2014, 13:19
Ivan
Здравствуйте.

Предыдущий код был немного некорректен.
Попробуйте использовать следующий доработанный вариант вашего класса.
Сейчас реализовано следующее: в конструктор класса RegisteredReport передаётся ваш загруженный отчет. По ReportGuid определяется имя сборки отчета, и если эта сборка существует - отчет грузится из этой сборки (т.е. без компиляции), если сборки нет - отчет компилируется в сборку с этим именем. Далее в методе ProcessReportBeforeUsage() просто регистрируются новые данные для отчета.

Код: Выделить всё

        public class RegisteredReport
        {
            public Stimulsoft.Report.StiReport mReport;
            public String mName;
            public String mReportCacheCompiledFolderName;
            public String mReportCacheCompiledFileName;
            public String mViolationTypes;
            public String mOriginalFoto;
            public DateTime mDateFrom;
            public DateTime mDateTo;
            public bool mLoadedFromAssembly = false;
            public int mCollate;

            public RegisteredReport(Stimulsoft.Report.StiReport report, String name, String violationTypes, String originalFoto, DateTime dateFrom, DateTime dateTo, int collate)
            {
                mReportCacheCompiledFolderName = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                mReportCacheCompiledFolderName = Path.Combine(mReportCacheCompiledFolderName, "Stimulsoft\\CompiledReports");
                mReportCacheCompiledFolderName = Path.Combine(mReportCacheCompiledFolderName, System.Runtime.InteropServices.RuntimeEnvironment.GetSystemVersion().ToString());
                mReportCacheCompiledFileName = Path.Combine(mReportCacheCompiledFolderName, report.GetReportAssemblyCacheName());

                if (File.Exists(mReportCacheCompiledFileName))
                {
                    mReport = Stimulsoft.Report.StiReport.GetReportFromAssembly(mReportCacheCompiledFileName, true);
                    mLoadedFromAssembly = true;
                }
                else //Compiled report does not exist
                {
                    if (!Directory.Exists(mReportCacheCompiledFolderName))
                        Directory.CreateDirectory(mReportCacheCompiledFolderName);
                    report.Compile(mReportCacheCompiledFileName);
                    mReport = report.CompiledReport;
                }

                mName = name;
                mViolationTypes = violationTypes;
                mOriginalFoto = originalFoto;
                mDateFrom = dateFrom;
                mDateTo = dateTo;
                mCollate = collate;
            }

            public void ProcessReportBeforeUsage(DataTable Table)
            {
                mReport.RegData(Table);
            }
        }
Ниже приведён пример использования класса:

Код: Выделить всё

            StiReport report = new StiReport();
            report.Load(@"d:\report.mrt");

            DataSet ds = new DataSet();
            ds.ReadXmlSchema("demo.xsd");
            ds.ReadXml("demo.xml");

            RegisteredReport regrep = new RegisteredReport(report, "MyReport", "violation", "foto", DateTime.Now, DateTime.Now, 1);

            for (int index = 0; index < 100; index++)
            {
                regrep.ProcessReportBeforeUsage(ds.Tables["Categories"]);
                regrep.mReport.Render();
                regrep.mReport.ExportDocument(StiExportFormat.Pdf, index.ToString() + ".pdf");
            }
В этом варианте отчет компилируется только один раз при первом обращении к нему.
При любом изменении отчета в дизайнере изменяется его ReportGuid, поэтому будет создаваться новая сборка.
Каждый раз при переходе на новую версию наших сборок необходимо очищать папку "Stimulsoft\\CompiledReports", т.к. отчет компилируется под конкретную версию наших сборок.

Спасибо.

Re: Утечка в неуправляемой куче

Добавлено: 17 дек 2014, 10:24
walle93
Спасибо огромное! Помогли в этом не легком деле)))
Успехов вам!

Re: Утечка в неуправляемой куче

Добавлено: 17 дек 2014, 11:27
Aleksey
Здравствуйте.

Всегда рады помочь.

Спасибо.

Re: Утечка в неуправляемой куче

Добавлено: 27 янв 2015, 14:51
walle93
В продолжение темы.
Нарисовалась странная особенность, раньше не смогли заметить. На одной странице из отчета(3-х листового), помимо данных имеется статичный рисунок (оттиск герба). В первом сохраненном отчете рисунок отображается, но в остальных всех пустое место. Использую класс, который вы мне помогли "допилить", в моей версию такое тоже наблюдалось.
В чем может быть причина? Или какие либо параметры задал не верные? Не придется ли мне эту картинку к каждой строке данных приписывать и использовать в отчете как часть данных?