Large PDF Memory Management#
Processing large or multi-page PDFs in Docling can exhaust system memory due to how the C++ docling-parse backend accumulates native memory across pages, bypassing Python's per-page cleanup. This can surface as std::bad_alloc errors mid-conversion or — with the threaded backend — as silent failures with zero output . The issue is documented in docling-parse#227 and docling#3345.
Root Cause#
The DoclingParseDocumentBackend (default: docling_parse) holds internal C++ state that grows page-by-page and is not released between pipeline stages. For scanned/image-heavy PDFs, this grows rapidly: a 184-page scanned grayscale PDF reliably triggers std::bad_alloc starting around page 80+ . Python GC cannot reclaim this memory; releasing it requires explicit action at the C++ level.
PDF Backend Options and Trade-offs#
Docling supports multiple backends, selected via pdf_backend in pipeline options or --pdf-backend on the CLI :
| Backend | Memory behavior | Known issues |
|---|---|---|
docling_parse (default) | Accumulates C++ memory across all pages | std::bad_alloc on large/image-heavy PDFs |
threaded_docling_parse | Periodic native memory release supported | Silently drops tables; known data-loss bug (see below) |
pypdfium2 | No native memory accumulation | Fragmented sub-word table cells; lower table extraction quality |
dlparse_v4 | Same accumulation behavior as docling_parse | Same OOM failure mode |
Mitigation Strategies#
1. Switch to pypdfium2 (simplest CLI workaround)#
For scanned/OCR-heavy documents where native text extraction quality is less important, pypdfium2 avoids C++ memory accumulation entirely. The main trade-off is table cell quality: pypdfium2 produces fragmented sub-word cells vs. the cleaner hierarchical cells from docling-parse .
docling --pdf-backend pypdfium2 large_doc.pdf
2. Use threaded_docling_parse with periodic memory release ⚠️#
The ThreadedDoclingParseDocumentBackend supports the release_native_memory_every_n_pages option, which triggers a C++ memory flush after every N pages . It is configured via ThreadedDoclingParseBackendOptions:
- Default: 128 pages
- Range: any integer ≥ 0; set to
0to disable - Python API:
ThreadedDoclingParseBackendOptions(release_native_memory_every_n_pages=20) - CLI:
--release-native-memory-every-n-pages 20(only when--pdf-backend threaded_docling_parse)
⚠️ Known bug:
ThreadedDoclingParseDocumentBackendsilently drops tables that the serial backend detects. This reproduces even atparser_threads=1, so it is not a concurrency issue — the threaded backend feeds different cell/layout data into the table-structure stage . Do not use this backend in production if table extraction accuracy is required.
⚠️
--release-native-memory-every-n-pagesis silently ignored when used with any backend other thanthreaded_docling_parse.
Additional CLI levers to reduce peak memory when using the threaded backend:
--num-threads 1(default 4)--page-batch-size 1(default 4)
3. Page-range batching via Python API (most reliable)#
Process a large document in chunks by passing page_range=(start, end) to DocumentConverter.convert() . Critically, create a new DocumentConverter instance per batch and call del converter afterward to force the C++ backend to release memory :
from docling.document_converter import DocumentConverter
results = []
for start in range(1, total_pages, chunk_size):
converter = DocumentConverter()
result = converter.convert(path, page_range=(start, start + chunk_size - 1))
results.append(result)
del converter # forces C++ memory release
The page_range parameter is defined in DocumentLimits and flows through convert() → convert_all() → InputDocument.limits → backend . There is no --page-range flag on the local CLI docling convert command (it exists only for convert-remote) .
⚠️ Tables that span a batch boundary will be split across chunks.
Key Source Files#
| File | Purpose |
|---|---|
docling/backend/docling_parse_backend.py | ThreadedDoclingParseDocumentBackend and _make_docling_parse_decode_config |
docling/datamodel/backend_options.py | ThreadedDoclingParseBackendOptions (incl. release_native_memory_every_n_pages) |
docling/datamodel/settings.py | PageRange type and DocumentLimits |
docling/document_converter.py | convert() / convert_all() with page_range param |
docling/cli/main.py | --release-native-memory-every-n-pages CLI flag |
Open Issues#
- docling#3671 —
std::bad_allocon large scanned PDFs; covers all three failure modes - docling#3512 —
ThreadedDoclingParseDocumentBackendsilently drops tables - docling#3345 — Original OOM bug report; introduced threaded backend as mitigation
- docling-parse#227 — Upstream C++ memory accumulation tracking issue