Memory not getting released after creating a large mail merge

2 posts, 0 answers
  1. Pavel
    Pavel avatar
    32 posts
    Member since:
    Sep 2016

    Posted 25 Jan 2017 Link to this post

    I am running into a memory issue when trying to perform a mail merge. I need to do two things:
    1. Generate individual PDF files to be stored on a file share
    2. generate a single PDF so it could be sent to a printer as a single job.

    I am noticing, that after my large PDF file is generated, the memory usage remains very large. When I re-run the process, it starts climbing up, but then falls back down to an acceptable level during the generation of single document, only to climb back up during the final single PDF. I am pasting my code below. For some odd reason my random data isn't actually making it into the files, but since that's not my problem at this moment, I am not concerned about it.

    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.IO;
    using System.Linq;
    using System.Threading.Tasks;
    using Telerik.WinForms.Documents.FormatProviders.OpenXml.Docx;
    using Telerik.WinForms.Documents.FormatProviders.Pdf;
    using Telerik.WinForms.Documents.Model;
     
    namespace MailMergeMemoryBenchmark
    {
        public partial class RadForm1 : Telerik.WinControls.UI.RadForm
        {
            List<DummyItem> dataSource;
            public RadForm1()
            {
                //get the rest of the string at http://pastebin.com/i6PZiJ1c
                byte[] template = Convert.FromBase64String(@"UEsDBBQ...[TRUNCATED]");
                dataSource = new List<DummyItem>();
                for (int i = 0; i < 1000; i++)
                {
                    dataSource.Add(DummyItem.GetRandom());
                }
                InitializeComponent();
                var provider = new DocxFormatProvider();
                radRichTextEditor1.Document = provider.Import(template);
                radRichTextEditor1.ScaleFactor = new Telerik.WinForms.Documents.Model.SizeF(.4f, .4f);
                radRichTextEditor1.ChangeAllFieldsDisplayMode(FieldDisplayMode.Code);
                radRichTextEditor1.Document.ShowMergeFieldsHighlight = true;
                radRichTextEditor1.Document.MailMergeDataSource.ItemsSource = dataSource;
            }
     
            private async void btnPerformMailMerge_Click(object sender, EventArgs e)
            {
                btnPerformMailMerge.Enabled = false;
                var progress = new Progress<string>(value => UpdateProgress(value));
                await Go(progress);
                btnPerformMailMerge.Enabled = true;
            }
     
            private void UpdateProgress(string value)
            {
                radLabel1.Text = value + Environment.NewLine;
            }
     
            private Task<bool> Go(IProgress<string> progress)
            {
                return Task.Run(() =>
                {
                    var pdfProvider = new PdfFormatProvider();
     
                    PdfExportSettings pdfExportSettings = new PdfExportSettings()
                    {
                        ContentsCompressionMode = PdfContentsCompressionMode.Automatic,
                        ContentsDeflaterCompressionLevel = 9,
                        FloatingUIContainersExportMode = PdfInlineUIContainersExportMode.Image,
                        ImagesCompressionMode = PdfImagesCompressionMode.Automatic,
                        ImagesDeflaterCompressionLevel = 9,
                        InlineUIContainersExportMode = PdfInlineUIContainersExportMode.Image,
                    };
                    pdfProvider.ExportSettings = pdfExportSettings;
                    byte[] letterBytes;
                    radRichTextEditor1.Document.MailMergeDataSource.MoveToFirst();
                    int i = 0;
                    do
                    {
                        using (var document = radRichTextEditor1.Document.MailMergeCurrentRecord())
                        {
                            letterBytes = pdfProvider.Export(document);
                            File.WriteAllBytes($"{i}-{DateTime.Now.Ticks}.pdf", letterBytes);
                        }
                        letterBytes = null;
                        progress.Report($"Merged {i + 1} out of {dataSource.Count()}");
                        i++;
                    } while (radRichTextEditor1.Document.MailMergeDataSource.MoveToNext());
                    progress.Report($"Starting large PDF creation");
                    using (var document = radRichTextEditor1.MailMerge(true))
                    {
                        letterBytes = pdfProvider.Export(document);
                        File.WriteAllBytes($"ALL RECORDS-{DateTime.Now.Ticks}.pdf", letterBytes);
                    }
                    progress.Report($"Done with large PDF creation");
                    letterBytes = null;
                    pdfProvider = null;
                    pdfExportSettings = null;
                    GC.Collect();
                    return true;
                });
            }
     
     
        }
     
        internal class DummyItem
        {
            public string ContactName;
            public string CompanyName;
            public string Address;
            public static DummyItem GetRandom()
            {
                return new DummyItem()
                {
                    ContactName = RandomStringGenerator.RandomString(20, 40),
                    CompanyName = RandomStringGenerator.RandomString(20, 40),
                    Address = RandomStringGenerator.RandomString(100, 200)
                };
            }
        }
     
        internal static class RandomStringGenerator
        {
            private const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ";
            private static Random random = new Random();
            public static string RandomString(int minLength, int maxLength)
            {
                var length = random.Next(minLength, maxLength);
                return new string(Enumerable.Repeat(chars, length)
                  .Select(s => s[random.Next(s.Length)]).ToArray());
            }
        }
    }
  2. Hristo
    Admin
    Hristo avatar
    1508 posts

    Posted 26 Jan 2017 Link to this post

    Hi Pavel,

    Thank you for writing. 

    I performed memory profiling of the described scenario and according to the provided code snippet and we could not isolate an issue with the consumed memory. Please check the attached screenshot and video showing the result on my end.

    As it is seen, the memory consumption goes up and down while creating the mail merge and the pdf files. A considerable increase is observed while generating the large pdf file at the end. Once the job finishes and you force the Garbage Collector the memory is released.

    Please also note that disposing of an object will not automatically release the memory it has consumed. The garbage collector is indeterministic in its nature and one cannot know when exactly it will release all objects marked as disposed. You can also check the following articles:
    I hope this information is useful. Should you have further questions please do not hesitate to write back.

    Regards,
    Hristo
    Telerik by Progress
    Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
Back to Top