多线程环境下任务陷入无限循环并耗尽内存:两段PDF处理代码的诱因排查
排查PDF处理代码中的无限循环与内存耗尽问题
嘿,我来帮你拆解这两段PDF处理代码里可能触发无限循环和内存耗尽的点——尤其在多线程环境下,这些问题很容易因为资源竞争或者不当的对象复用冒出来。
第一段代码的潜在问题
先看这段合并PDF的代码:
Document document = new Document(); using (FileStream newFileStream = new FileStream(tempFile, FileMode.Create, FileAccess.ReadWrite)) { PdfCopy writer = new PdfCopy(document, newFileStream); document.Open(); foreach (var page in pages) { PdfReader reader = new PdfReader(Path.Combine(pdfInputPath, page.PdfFileName)); reader.ConsolidateNamedDestinations(); PdfImportedPage importedPage = writer.GetImportedPage(reader, page.Page); writer.AddPage(importedPage); reader.Close(); } writer.Close(); document.Close(); }
可能的诱因:
- 非线程安全集合的并发修改:如果
pages是普通的List<T>这类非线程安全集合,在多线程环境下,当这段代码遍历pages的同时,其他线程对pages执行了添加/删除操作,会导致枚举器失效,极端情况下可能陷入无限遍历。 - 资源未正确释放导致的内存累积:
Document和PdfCopy没有用using语句包裹,虽然手动调用了Close(),但如果遍历过程中抛出异常,这两个对象的资源可能无法正常释放。多次执行后内存占用持续攀升,最终触发OOM,而GC的频繁运行可能让程序看起来像是陷入了无限循环。 - 无效页码的隐性循环(概率较低):如果
page.Page的值超过了对应PDF的总页数,GetImportedPage会抛出异常,但如果外层有未显示的重试逻辑(比如循环重试直到成功),就可能导致无限循环。
第二段代码的潜在问题
再看这段给PDF添加内容的代码:
var reader = new PdfReader(pdfFile); using (var outputPdfStream = new FileStream(finalPDFPath, FileMode.Create, FileAccess.Write, FileShare.None)) { var stamper = new PdfStamper(reader, outputPdfStream); int i = 1; foreach (var page in pages) { var pdfContentByte = stamper.GetOverContent(i++); string value = GetValue(); if (!string.IsNullOrEmpty(value)) { var img = GetPage(Information, page, layout, pdfContentByte, value, X_DPI, Y_DPI); //var img = ImgRender(Information, page); pdfContentByte.AddImage(img); pdfContentByte.ClosePath(); } } logger.Info("Number of pages after generation :" + reader.NumberOfPages); stamper.Close(); reader.Close(); }
可能的诱因:
GetPage方法内部的无限循环:你注释掉了ImgRender换成了GetPage,如果这个自定义方法在处理某些特定输入(比如多次调用后数据异常、图片资源损坏)时,内部存在逻辑漏洞(比如循环条件永远为真),会直接导致整个程序陷入无限循环。pages集合与PDF页码不匹配+重试逻辑:如果pages的元素数量大于目标PDF的总页数,i++会超过reader.NumberOfPages,GetOverContent会抛出异常。如果外层有未显示的重试逻辑,就会不断循环重试。- 线程不安全对象的复用:
PdfReader是在using外部创建的,如果多线程环境下多个请求复用了同一个PdfReader实例,会导致PdfStamper的内部状态混乱,可能触发异常或隐性的循环逻辑。 - 资源泄漏导致的内存耗尽:
PdfReader和PdfStamper没有用using包裹,异常情况下资源无法释放,多次执行后内存耗尽,GC频繁运行导致程序假死,看起来像无限循环。
另外,多线程环境下一定要确保每个请求都使用独立的Document、PdfCopy、PdfReader、PdfStamper实例——这些iText对象都不是线程安全的,共用实例会导致各种不可预测的问题,包括循环和内存泄漏。
内容的提问来源于stack exchange,提问作者Anisse
相关产品推荐
相关产品推荐

