Wysokie zuzycie pamieci szczytowej podczas ReadPdf na duzych plikach PDF
Wywolanie BarcodeReader.ReadPdf() raz dla calego duzego PDF renderuje kazda zadana strone do skanowania w skonfigurowanej rozdzielczosci DPI, zatem szczytowa pamiec skalowana jest z liczba stron i rozdzielczoscia zamiast pozostawac stala. W pliku o rozmiarze 248 stron, 80 MB przy 300 DPI, pamiec miescila sie miedzy 3 GB a 8 GB, z naglymi wzrostami do 12 GB, co wystarczalo na blad w srodowiskach z ograniczona pamiecia.
BarcodeReader.ReadPdf() przetwarza kazda strone przekazana mu w jednej operacji. Gdy cala gama stron duzego dokumentu o wysokim DPI zostaje odczytana naraz, wszystkie te strony sa przetrzymywane w pamieci razem, co sprawia, ze szczytowe zuzycie rosnie w zaleznosci od wielkosci zestawu stron. Odzwierciedla to sposob przetwarzania paczek odczytu, a nie potwierdzony defekt w IronBarcode.
Rozwiązanie
1. Odczytywanie w stalych segmentach stron
Podziel dokument na stale fragmenty stron za pomoca PdfBarcodeReaderOptions.PageNumbers, aby tylko jeden fragment byl w pamieci naraz. Mniejszy chunkSize zmniejsza szczytowa pamiec, ale dodaje wiecej przejsc, wiec dostosuj go do limitu pamieci srodowiska.
var barcodeMetadata = new List<BarcodeResult>();
var pageChunks = Enumerable.Range(1, pdf.PageCount)
.GroupBy(i => i / 50)
.Select(g => g.ToArray())
.ToList();
foreach (var chunk in pageChunks)
{
var readerOptions = new PdfBarcodeReaderOptions
{
ExpectMultipleBarcodes = true,
ExpectBarcodeTypes = IronBarCode.BarcodeEncoding.AllOneDimensional,
Speed = ReadingSpeed.Detailed,
ScanMode = BarcodeScanMode.OnlyDetectionModel,
DPI = 300,
PageNumbers = chunk
};
Console.WriteLine($"Processing pages: Pages {chunk.First()}-{chunk.Last()}");
var chunkResult = BarcodeReader.ReadPdf(pdf.Stream, readerOptions);
if (chunkResult != null && chunkResult.Count > 0)
{
barcodeMetadata.AddRange(chunkResult);
}
// Optional memory pressure relief
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine($"Total barcodes found: {barcodeMetadata.Count}");
var barcodeMetadata = new List<BarcodeResult>();
var pageChunks = Enumerable.Range(1, pdf.PageCount)
.GroupBy(i => i / 50)
.Select(g => g.ToArray())
.ToList();
foreach (var chunk in pageChunks)
{
var readerOptions = new PdfBarcodeReaderOptions
{
ExpectMultipleBarcodes = true,
ExpectBarcodeTypes = IronBarCode.BarcodeEncoding.AllOneDimensional,
Speed = ReadingSpeed.Detailed,
ScanMode = BarcodeScanMode.OnlyDetectionModel,
DPI = 300,
PageNumbers = chunk
};
Console.WriteLine($"Processing pages: Pages {chunk.First()}-{chunk.Last()}");
var chunkResult = BarcodeReader.ReadPdf(pdf.Stream, readerOptions);
if (chunkResult != null && chunkResult.Count > 0)
{
barcodeMetadata.AddRange(chunkResult);
}
// Optional memory pressure relief
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine($"Total barcodes found: {barcodeMetadata.Count}");
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports IronBarCode
Dim barcodeMetadata As New List(Of BarcodeResult)()
Dim pageChunks = Enumerable.Range(1, pdf.PageCount) _
.GroupBy(Function(i) i \ 50) _
.Select(Function(g) g.ToArray()) _
.ToList()
For Each chunk In pageChunks
Dim readerOptions As New PdfBarcodeReaderOptions With {
.ExpectMultipleBarcodes = True,
.ExpectBarcodeTypes = BarcodeEncoding.AllOneDimensional,
.Speed = ReadingSpeed.Detailed,
.ScanMode = BarcodeScanMode.OnlyDetectionModel,
.DPI = 300,
.PageNumbers = chunk
}
Console.WriteLine($"Processing pages: Pages {chunk.First()}-{chunk.Last()}")
Dim chunkResult = BarcodeReader.ReadPdf(pdf.Stream, readerOptions)
If chunkResult IsNot Nothing AndAlso chunkResult.Count > 0 Then
barcodeMetadata.AddRange(chunkResult)
End If
' Optional memory pressure relief
GC.Collect()
GC.WaitForPendingFinalizers()
Next
Console.WriteLine($"Total barcodes found: {barcodeMetadata.Count}")
Kazda iteracja odczytuje tylko strony wymienione w PageNumbers, wiec pamiec spada pomiedzy fragmentami zamiast kumulowac sie przez caly plik.
2. Buforuj strumien PDF i zresetuj jego pozycje
Odczytaj strumien do lokalnego przed petla, a nastepnie przewijaj go po kazdym fragmencie. Wlasciwosc Stream moze przydzielac na kazdym dostepie, wiec przechowuj ja w var pdfStream = pdf.Stream; i wywoluj pdfStream.Seek(0, SeekOrigin.Begin) przed kazdym odczytem fragmentu.
3. Zbuduj opcje czytnika raz
Skonstruuj PdfBarcodeReaderOptions poza petla i zmien tylko PageNumbers dla kazdego fragmentu, zamiast przydzielac nowy obiekt opcji za kazda iteracja.
4. Wstepnie odstaw liste wynikow
Zainicjuj liste wynikow z szacowana pojemnoscia, aby uniknac powtarzajacego sie podwajania wewnetrznej tablicy podczas dodawania wynikow. Rekord szacuje na poczatek, ze na dziesiec stron przypada jeden kod kreskowy.

