Skip to content

Release the underlying PDFPage object when a page is closed - #1396

Open
afonsojanu wants to merge 1 commit into
jsvine:developfrom
afonsojanu:fix/close-releases-page-obj
Open

afonsojanu wants to merge 1 commit into
jsvine:developfrom
afonsojanu:fix/close-releases-page-obj

Conversation

@afonsojanu

Copy link
Copy Markdown

Fixes #1395.

page.close() clears the derived caches via flush_cache() and clears the get_textmap LRU cache, but it never lets go of page.page_obj, the raw pdfminer PDFPage. That object carries its own resource/content-stream data, so anyone processing many pages one at a time (extract text, flush, move on, rather than holding the whole document open) ends up retaining most of that memory regardless of flush_cache().

I didn't touch flush_cache() itself for this, since a couple of properties (layout, annots) still read page_obj mid-lifecycle and calling flush_cache() doesn't mean a page is done being used. close() is the one spot where nothing should touch the page again afterward, so it's the safe place to release it.

Measured the actual effect with a small script against one of the repo's own multi-page test PDFs, reopening and reprocessing it 20 times and tracking peak memory with tracemalloc: clearing page_obj alongside the existing caches roughly halved peak retained memory in that run.

Added a regression test in tests/test_issues.py confirming page.page_obj is None after close() (fails on unmodified develop, passes with the fix). Ran the full suite: everything passes except the six pre-existing test_repair.py failures from Ghostscript not being installed in my sandbox, unrelated to this change and failing identically on unmodified develop. black, isort, and flake8 are clean on the two files I touched (there's a pre-existing isort complaint on page.py's import block that predates this change).

flush_cache() clears the derived caches (_layout, _rect_edges, etc.)
but leaves page.page_obj, the raw pdfminer PDFPage, referenced. That
object holds resource and content-stream data of its own, so a caller
who iterates over many pages calling extract_text() then flush_cache()
per page (rather than holding the whole document open) ends up
retaining most of that memory anyway.

page.close() is the one place nothing should read from the page
afterward, so it's the safe spot to drop page_obj too, unlike
flush_cache() itself, which other properties like layout and annots
may still need mid-lifecycle. Confirmed with a small benchmark against
one of the repo's own multi-KB test PDFs, reopening and reprocessing
it 20 times: peak traced memory roughly halved once page_obj is
cleared alongside the existing caches.

Fixes jsvine#1395.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant