Hohe Spitzenarbeitsspeicher während ReadPdf auf großen PDFs
Ein Aufruf von BarcodeReader.ReadPdf() für ein ganzes großes PDF rendert jede angeforderte Seite für das Scannen bei der konfigurierten DPI, sodass der maximale Speicherverbrauch mit der Seitenanzahl und der Auflösung skaliert, anstatt konstant zu bleiben. Bei einer 248-seitigen, 80 MB großen Datei bei 300 DPI lag der Arbeitsspeicher zwischen 3 GB und 8 GB mit Spitzen von bis zu 12 GB, was in speicherbegrenzten Umgebungen zum Scheitern führen kann.
BarcodeReader.ReadPdf() verarbeitet jede ihm übergebene Seite in einem einzigen Vorgang. Wenn die gesamte Seitenreihe eines großen, hochauflösenden Dokuments auf einmal gelesen wird, werden alle diese Seiten zusammen im Arbeitsspeicher gehalten, sodass die Spitzenverwendung mit der Größe des Seitensatzes wächst. Dies spiegelt wider, wie die Lektüre in Stapeln erfolgt, nicht jedoch ein bestätigter Defekt in IronBarcode.
Lösung
1. Lesen in festen Seitenblöcken
Teilen Sie das Dokument mit PdfBarcodeReaderOptions.PageNumbers in feste Seitenabschnitte, sodass jeweils nur ein Abschnitt im Speicher ist. Ein kleinerer chunkSize senkt den maximalen Speicherbedarf, fügt jedoch mehr Durchläufe hinzu. Stimmen Sie es also auf das Speicherlimit der Umgebung ab.
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}")
Jede Iteration liest nur die in PageNumbers genannten Seiten, sodass der Speicher zwischen den Abschnitten herunterfährt, anstatt im gesamten Dokument zuzunehmen.
2. Cache der PDF-Stream und Rücksetzen der Position
Lesen Sie den Stream einmal vor der Schleife in eine lokale Variable und spulen Sie ihn dann für jeden Abschnitt zurück. Die Stream-Eigenschaft kann bei jedem Zugriff eine Zuordnung vornehmen, speichern Sie sie daher in var pdfStream = pdf.Stream; und rufen Sie pdfStream.Seek(0, SeekOrigin.Begin) vor jedem Abschnitts-Read auf.
3. Erstellen Sie die Leseroptionen einmal
Erstellen Sie PdfBarcodeReaderOptions außerhalb der Schleife und ändern Sie pro Abschnitt nur PageNumbers, anstatt bei jeder Iteration ein neues Optionsobjekt zuzuweisen.
4. Vorerstellen der Ergebnisliste
Initialisieren Sie die Ergebnisliste mit einer geschätzten Kapazität, um wiederholtes internes Verdoppeln des Arrays zu vermeiden, wenn Ergebnisse hinzugefügt werden. Die Wiederholung schätzt ungefähr einen Barcode pro zehn Seiten als Ausgangszahl.

