ClearOutputPreprocessor vs AsyncIO Server Mode for Mitigating Jupyter Notebook Memory Leak
0 reputation · 15 Aug 2021, 02:44 UTC
0 reputation · 15 Aug 2021, 02:44 UTC
Goal: Keep RSS stable when repeatedly executing cells that produce large outputs (e.g., 10 MB images) in a live Jupyter Notebook session without restarting the kernel.
Constraint: The nbconvert ClearOutputPreprocessor only removes output metadata when the notebook is saved, while enabling Jupyter Server’s asyncio‑based tornado loop alters thread‑local storage handling and can postpone garbage collection of output objects, potentially making the leak more noticeable.
Uncertainty: It is unclear whether applying ClearOutputPreprocessor on each cell execution (e.g., via a pre‑save hook) offsets the asyncio‑induced delay, or if one approach alone provides sufficient memory stabilization under typical editing workflows.
Questions: Does running ClearOutputPreprocessor before each save prevent RSS growth in live server memory? Does enabling asyncio mode increase leak severity despite the preprocessor? Which combination of settings yields the lowest RSS increase over 200 iterations of large‑output cells?
33275 reputation · 15 Aug 2021, 14:00 UTC
ClearOutputPreprocessor or switching server modes alone will not prevent memory growth during the session.
### Why Preprocessors Fail Live Sessions
The ClearOutputPreprocessor is a component of the nbconvert library. It is designed to process notebook files on disk. Running it via a pre-save hook removes metadata from the ipynb file when it is saved, but it does not interact with the running Python kernel. The kernel retains the output of every executed cell in memory to allow for interactive scrolling and re-rendering; therefore, the RSS will remain high regardless of the preprocessor's configuration.
Enabling Jupyter Server’s asyncio-based Tornado loop changes how the server handles concurrent requests and callbacks. While this improves responsiveness, it can make memory issues appear more noticeable. Because asyncio relies on an event loop, objects involved in callbacks or pending message buffers may stay in scope longer than in a strictly synchronous thread-local environment. This is not a "leak" per se, but it delays the reclamation of large byte buffers.
### Recommended Strategy for Lowest RSSTo achieve the lowest RSS increase over 200+ iterations without restarting the kernel, follow these steps:
IPython.display.clear_output(wait=True) at the end of your processing cell. This signals the kernel to release the reference to the previous output._history_-variable. For massive iterations, manually clear this or limit the history settings to prevent the kernel from accumulating every object ever generated.gc.collect() after clearing outputs to force the Python interpreter to reclaim memory from deleted large images or arrays.This analysis assumes you are using a standard IPython kernel. If memory growth persists after calling clear_output(), the leak likely exists within the library generating the images (e.g., a C-extension failing to free memory) rather than the Jupyter infrastructure itself. To verify, run the following in a cell:
import psutil, os
process = psutil.Process(os.getpid())
print(f"Current RSS: {process.memory_info().rss / 1024**2:.2f} MB")
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.