Hi,

I have a C# pdf library called PdfSharp which I use to create PDFs. I have a page (print.aspx) which instantiats the class which creates the pdf in the Page_Load() event handler. for some reason when I am running this through IIS, the process for the app pool (w3wp.exe) keeps using more and more memory.

Initially (on first run after a recycle of app pool) the memory usage is about 10mb, then I run print.aspx page and the memory usage goes up to around 70mb (which I assume most of is the .dlls being loaded into memory etc.?) and the pdf is outputted through print.aspx page using a Response.WriteBinary(). When i then refresh the page (i.e. run it again) to create another identical pdf, the memory usage goes up by about 300kb. so its now 70,300kb instead of 70,000kb before the refresh. The outputted pdf is about 190kb when i save it to my pc.

This is happening on our production server and until i added some application pool recycling rules, it was crashing the server after enough requests were made!

I think this must be of a memory leak, but could it be some form of caching? I am running pretty default IIS settings and using .NET 2.0

I am learning more and more about memory management in .net, but until about a week ago, i thought that i didnt need to worry about it, obviously i was wrong....

Any help would be greatly appreciated!

Thanks,

Mike

It sounds like the pdf library is using unmanaged resources and you aren't disposing of the object properly. Or they aren't disposing of the unmanaged resources. Does PDFDocument have a close() or dispose() method?

Does PDFDocument have a close() or dispose() method?

Yes it does and I am calling it at the point which it is saved:

private void SaveBrowserPDF(PdfDocument pdfDocument)
        {
            try
            {
                // Send PDF to browser
                string _tag = "";
                if (pdfDocument.Tag != null)
                {
                    _tag = pdfDocument.Tag.ToString();
                }
                string _companyName = "";
                string _oppName = "";
                if (_tag.Length > 0)
                {
                    _companyName = _tag.Substring(0, _tag.IndexOf("^"));
                    _oppName = _tag.Substring(_tag.IndexOf("^") + 1);
                }
                DateTime _dt = DateTime.Now;
                string _filename = _companyName + "-" + _oppName + " (" + _dt.Month + "-" + _dt.Day + "-" + _dt.Year + ") " + _dt.Millisecond;
                MemoryStream stream = new MemoryStream();
                if (pdfDocument.PageCount == 0)
                {
                    PdfSharp.Pdf.PdfPage _page = pdfDocument.AddPage();
                    _page.Size = PdfSharp.PageSize.A4;
                    PdfSharp.Drawing.XGraphics _gfx = PdfSharp.Drawing.XGraphics.FromPdfPage(_page);
                    PdfSharp.Drawing.XFont _font = new PdfSharp.Drawing.XFont("Arial", 10);
                    _gfx.DrawString("There was no information selected to print", _font, PdfSharp.Drawing.XBrushes.Black, new PdfSharp.Drawing.XPoint(50, 50));
                }
                pdfDocument.Save(stream, false);
                pdfDocument.Close();
                pdfDocument.Dispose();
                CommonData.Instance.DeleteByID(__GUID);
                
                Response.Clear();
                Response.ClearContent();
                Response.ClearHeaders();
                Response.Buffer = true;
                Response.Expires = 0;
                Response.ContentType = "application/pdf";
                Response.AddHeader("Content-Disposition", "attachment;filename=" + _filename + ".pdf");
                Response.AddHeader("Content-Length", stream.Length.ToString());
                Response.BinaryWrite(stream.ToArray());
                Response.Flush();
                stream.Close();
                Response.End();
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

        }

Is there a way i can easily identify the lines of code where un-managed resources are used? i.e. particular object definitions etc.?

Thanks,

Mike

Hi there.
For completeness I agree it would be good to find the reason.
Could you try instantiating the pdfDocument within the method? It gets disposed of here but the existing reference from the calling code could be putting a spanner in the works.
If this does not work then to help assess things could you try saving to a temporary file instead of using the MemoryStream (using something like Path.GetTempFileName() - deleting it afterwards) then write that to the response. It would take the MemoryStream out of the equation.

Nicholas

Hi,

Unfortunately I cant instantiate the pdfDocument object in this function as i manipulate it throughout the code and this function is the very last thing the code will execute, I suppose i could:

PdfDocument tempDoc = pdfDocument.Clone();

it, but that wouldnt make any difference would it?

As for the temp file, surely i would need a Stream of some description to then read the temp file back into once i had saved it out? to then do Response.WriteBinary(tempFileStream.ToArray());?

I am a little confused as ive not worked with temp files before, please could you provide a brief example of how you intend to skip a Stream of some type out of the equation?

Thanks,

Mike

Hi Mike,

In reply,
"but that wouldnt make any difference would it?"
possibly / probably not but in order to troubleshoot issolating things is a good approach.


To strip the memory stream out use a file stream. Write the pdf to a temporary file then use a stream to read it again.

To write the file use something like:

FileStream newFile = new FileStream(strPath, FileMode.Create);

FileStream inherits System.IO.Stream so will almost definately work with the pdfDocument.Save.

Taking a file and publishing it to the response, this has come from an old project of mine written in VB (you'll need to convert and modify it):

Dim MyFileStream As FileStream
        Dim FileSize As Long

        MyFileStream = New FileStream(Path.Combine(rw("PjPath"), rw("FiFileName")), FileMode.Open)
        FileSize = MyFileStream.Length

        Dim Buffer(CInt(FileSize)) As Byte
        MyFileStream.Read(Buffer, 0, CInt(FileSize))
        MyFileStream.Close()

        Response.Clear()
        Response.ContentType = rw("FiContentType")
        Response.AddHeader("Content-Disposition", "inline;filename=" + rw("FiFileName"))
        Response.OutputStream.Write(Buffer, 0, FileSize)
        Response.Flush()
        Response.Close()

Nicholas

Hi,

I have implememted it like this:

private void SaveBrowserPDF2(PdfDocument pdfDocument)
        {
            try
            {
                for (int i = 0; i < pdfDocument.PageCount; i++)
                {
                    pdfDocument.Pages[i].Close();
                }
                pdfDocument.Close();
                // Send PDF to browser
                string _tag = "";
                if (pdfDocument.Tag != null)
                {
                    _tag = pdfDocument.Tag.ToString();
                }
                string _companyName = "";
                string _oppName = "";
                if (_tag.Length > 0)
                {
                    _companyName = _tag.Substring(0, _tag.IndexOf("^"));
                    _oppName = _tag.Substring(_tag.IndexOf("^") + 1);
                }
                DateTime _dt = DateTime.Now;
                string _filename = _companyName + "-" + _oppName + " (" + _dt.Month + "-" + _dt.Day + "-" + _dt.Year + ") " + _dt.Millisecond + ".pdf";
                string _filePath = HttpContext.Current.Server.MapPath(_filename);
                
                if (pdfDocument.PageCount == 0)
                {
                    PdfSharp.Pdf.PdfPage _page = pdfDocument.AddPage();
                    _page.Size = PdfSharp.PageSize.A4;
                    PdfSharp.Drawing.XGraphics _gfx = PdfSharp.Drawing.XGraphics.FromPdfPage(_page);
                    PdfSharp.Drawing.XFont _font = new PdfSharp.Drawing.XFont("Arial", 10);
                    _gfx.DrawString("There was no information selected to print", _font, PdfSharp.Drawing.XBrushes.Black, new PdfSharp.Drawing.XPoint(50, 50));
                }
                pdfDocument.Save(_filePath);
                __PrintHandler.ResetAll();

                FileStream _fileStr = new FileStream(_filePath, FileMode.Open);
                Int32 _fileSize = (Int32)_fileStr.Length;
                Byte[] _buffer = new Byte[_fileSize];
                _fileStr.Read(_buffer, 0, _fileSize);
                _fileStr.Close();
                File.Delete(_filePath);
                Response.Clear();
                Response.ContentType = "application/pdf";
                Response.AddHeader("Content-Disposition", "attachment;filename=" + _filename);
                Response.AddHeader("Content-Length", _fileSize.ToString());
                Response.BinaryWrite(_buffer);
                GC.Collect();
                Response.Flush();
                Response.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine("MIKE - " + ex.Message);
            }

        }

And its still behaving basically the same. I added in the .Close on the pages and the document to see if this would help, but it doesnt seem to.

I think the leak must be elsewhere in the code, unless I have implemented what you suggest incorerectly?

Mike

Code looks fine to me.
I think Momerath may be right with the pdfDocument using unmanaged memory or the like.
My limited experience with memory issues resulting in implementing a
using( memoryheavyobject )
{

}
construct which ensures the object is defined only within the context and then marked for disposal directly afterwards.

I am afraid I can not offer any more info on this one.

N

This article has been dead for over six months. Start a new discussion instead.