CINXE.COM
PEP 454 – Add a new tracemalloc module to trace Python memory allocations | peps.python.org
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="color-scheme" content="light dark"> <title>PEP 454 – Add a new tracemalloc module to trace Python memory allocations | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0454/"> <link rel="stylesheet" href="../_static/style.css" type="text/css"> <link rel="stylesheet" href="../_static/mq.css" type="text/css"> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light"> <link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark"> <link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss"> <meta property="og:title" content='PEP 454 – Add a new tracemalloc module to trace Python memory allocations | peps.python.org'> <meta property="og:description" content="This PEP proposes to add a new tracemalloc module to trace memory blocks allocated by Python."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0454/"> <meta property="og:site_name" content="Python Enhancement Proposals (PEPs)"> <meta property="og:image" content="https://peps.python.org/_static/og-image.png"> <meta property="og:image:alt" content="Python PEPs"> <meta property="og:image:width" content="200"> <meta property="og:image:height" content="200"> <meta name="description" content="This PEP proposes to add a new tracemalloc module to trace memory blocks allocated by Python."> <meta name="theme-color" content="#3776ab"> </head> <body> <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all"> <title>Following system colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="9"></circle> <path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path> </svg> </symbol> <symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all"> <title>Selected dark colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path> </svg> </symbol> <symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all"> <title>Selected light colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> </symbol> </svg> <script> document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto" </script> <section id="pep-page-section"> <header> <h1>Python Enhancement Proposals</h1> <ul class="breadcrumbs"> <li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> » </li> <li><a href="../pep-0000/">PEP Index</a> » </li> <li>PEP 454</li> </ul> <button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())"> <svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg> <span class="visually-hidden">Toggle light / dark / auto colour theme</span> </button> </header> <article> <section id="pep-content"> <h1 class="page-title">PEP 454 – Add a new tracemalloc module to trace Python memory allocations</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Victor Stinner <vstinner at python.org></dd> <dt class="field-even">BDFL-Delegate<span class="colon">:</span></dt> <dd class="field-even">Charles-François Natali <cf.natali at gmail.com></dd> <dt class="field-odd">Status<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd> <dt class="field-even">Type<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd> <dt class="field-odd">Created<span class="colon">:</span></dt> <dd class="field-odd">03-Sep-2013</dd> <dt class="field-even">Python-Version<span class="colon">:</span></dt> <dd class="field-even">3.4</dd> <dt class="field-odd">Resolution<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2013-November/130491.html">Python-Dev message</a></dd> </dl> <hr class="docutils" /> <section id="contents"> <details><summary>Table of Contents</summary><ul class="simple"> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#proposal">Proposal</a></li> <li><a class="reference internal" href="#api">API</a><ul> <li><a class="reference internal" href="#functions">Functions</a></li> <li><a class="reference internal" href="#filter">Filter</a></li> <li><a class="reference internal" href="#frame">Frame</a></li> <li><a class="reference internal" href="#snapshot">Snapshot</a></li> <li><a class="reference internal" href="#statistic">Statistic</a></li> <li><a class="reference internal" href="#statisticdiff">StatisticDiff</a></li> <li><a class="reference internal" href="#trace">Trace</a></li> <li><a class="reference internal" href="#traceback">Traceback</a></li> </ul> </li> <li><a class="reference internal" href="#rejected-alternatives">Rejected Alternatives</a><ul> <li><a class="reference internal" href="#log-calls-to-the-memory-allocator">Log calls to the memory allocator</a></li> </ul> </li> <li><a class="reference internal" href="#prior-work">Prior Work</a></li> <li><a class="reference internal" href="#links">Links</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>This PEP proposes to add a new <code class="docutils literal notranslate"><span class="pre">tracemalloc</span></code> module to trace memory blocks allocated by Python.</p> </section> <section id="rationale"> <h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2> <p>Classic generic tools like Valgrind can get the C traceback where a memory block was allocated. Using such tools to analyze Python memory allocations does not help because most memory blocks are allocated in the same C function, in <code class="docutils literal notranslate"><span class="pre">PyMem_Malloc()</span></code> for example. Moreover, Python has an allocator for small objects called “pymalloc” which keeps free blocks for efficiency. This is not well handled by these tools.</p> <p>There are debug tools dedicated to the Python language like <code class="docutils literal notranslate"><span class="pre">Heapy</span></code> <code class="docutils literal notranslate"><span class="pre">Pympler</span></code> and <code class="docutils literal notranslate"><span class="pre">Meliae</span></code> which lists all alive objects using the garbage collector module (functions like <code class="docutils literal notranslate"><span class="pre">gc.get_objects()</span></code>, <code class="docutils literal notranslate"><span class="pre">gc.get_referrers()</span></code> and <code class="docutils literal notranslate"><span class="pre">gc.get_referents()</span></code>), compute their size (ex: using <code class="docutils literal notranslate"><span class="pre">sys.getsizeof()</span></code>) and group objects by type. These tools provide a better estimation of the memory usage of an application. They are useful when most memory leaks are instances of the same type and this type is only instantiated in a few functions. Problems arise when the object type is very common like <code class="docutils literal notranslate"><span class="pre">str</span></code> or <code class="docutils literal notranslate"><span class="pre">tuple</span></code>, and it is hard to identify where these objects are instantiated.</p> <p>Finding reference cycles is also a difficult problem. There are different tools to draw a diagram of all references. These tools cannot be used on large applications with thousands of objects because the diagram is too huge to be analyzed manually.</p> </section> <section id="proposal"> <h2><a class="toc-backref" href="#proposal" role="doc-backlink">Proposal</a></h2> <p>Using the customized allocation API from <a class="pep reference internal" href="../pep-0445/" title="PEP 445 – Add new APIs to customize Python memory allocators">PEP 445</a>, it becomes easy to set up a hook on Python memory allocators. A hook can inspect Python internals to retrieve Python tracebacks. The idea of getting the current traceback comes from the faulthandler module. The faulthandler dumps the traceback of all Python threads on a crash, here is the idea is to get the traceback of the current Python thread when a memory block is allocated by Python.</p> <p>This PEP proposes to add a new <code class="docutils literal notranslate"><span class="pre">tracemalloc</span></code> module, a debug tool to trace memory blocks allocated by Python. The module provides the following information:</p> <ul class="simple"> <li>Traceback where an object was allocated</li> <li>Statistics on allocated memory blocks per filename and per line number: total size, number and average size of allocated memory blocks</li> <li>Computed differences between two snapshots to detect memory leaks</li> </ul> <p>The API of the tracemalloc module is similar to the API of the faulthandler module: <code class="docutils literal notranslate"><span class="pre">enable()</span></code> / <code class="docutils literal notranslate"><span class="pre">start()</span></code>, <code class="docutils literal notranslate"><span class="pre">disable()</span></code> / <code class="docutils literal notranslate"><span class="pre">stop()</span></code> and <code class="docutils literal notranslate"><span class="pre">is_enabled()</span></code> / <code class="docutils literal notranslate"><span class="pre">is_tracing()</span></code> functions, an environment variable (<code class="docutils literal notranslate"><span class="pre">PYTHONFAULTHANDLER</span></code> and <code class="docutils literal notranslate"><span class="pre">PYTHONTRACEMALLOC</span></code>), and a <code class="docutils literal notranslate"><span class="pre">-X</span></code> command line option (<code class="docutils literal notranslate"><span class="pre">-X</span> <span class="pre">faulthandler</span></code> and <code class="docutils literal notranslate"><span class="pre">-X</span> <span class="pre">tracemalloc</span></code>). See the <a class="reference external" href="http://docs.python.org/3/library/faulthandler.html">documentation of the faulthandler module</a>.</p> <p>The idea of tracing memory allocations is not new. It was first implemented in the PySizer project in 2005. PySizer was implemented differently: the traceback was stored in frame objects and some Python types were linked the trace with the name of object type. PySizer patch on CPython adds an overhead on performances and memory footprint, even if the PySizer was not used. tracemalloc attaches a traceback to the underlying layer, to memory blocks, and has no overhead when the module is not tracing memory allocations.</p> <p>The tracemalloc module has been written for CPython. Other implementations of Python may not be able to provide it.</p> </section> <section id="api"> <h2><a class="toc-backref" href="#api" role="doc-backlink">API</a></h2> <p>To trace most memory blocks allocated by Python, the module should be started as early as possible by setting the <code class="docutils literal notranslate"><span class="pre">PYTHONTRACEMALLOC</span></code> environment variable to <code class="docutils literal notranslate"><span class="pre">1</span></code>, or by using <code class="docutils literal notranslate"><span class="pre">-X</span> <span class="pre">tracemalloc</span></code> command line option. The <code class="docutils literal notranslate"><span class="pre">tracemalloc.start()</span></code> function can be called at runtime to start tracing Python memory allocations.</p> <p>By default, a trace of an allocated memory block only stores the most recent frame (1 frame). To store 25 frames at startup: set the <code class="docutils literal notranslate"><span class="pre">PYTHONTRACEMALLOC</span></code> environment variable to <code class="docutils literal notranslate"><span class="pre">25</span></code>, or use the <code class="docutils literal notranslate"><span class="pre">-X</span> <span class="pre">tracemalloc=25</span></code> command line option. The <code class="docutils literal notranslate"><span class="pre">set_traceback_limit()</span></code> function can be used at runtime to set the limit.</p> <section id="functions"> <h3><a class="toc-backref" href="#functions" role="doc-backlink">Functions</a></h3> <p><code class="docutils literal notranslate"><span class="pre">clear_traces()</span></code> function:</p> <blockquote> <div>Clear traces of memory blocks allocated by Python.<p>See also <code class="docutils literal notranslate"><span class="pre">stop()</span></code>.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">get_object_traceback(obj)</span></code> function:</p> <blockquote> <div>Get the traceback where the Python object <em>obj</em> was allocated. Return a <code class="docutils literal notranslate"><span class="pre">Traceback</span></code> instance, or <code class="docutils literal notranslate"><span class="pre">None</span></code> if the <code class="docutils literal notranslate"><span class="pre">tracemalloc</span></code> module is not tracing memory allocations or did not trace the allocation of the object.<p>See also <code class="docutils literal notranslate"><span class="pre">gc.get_referrers()</span></code> and <code class="docutils literal notranslate"><span class="pre">sys.getsizeof()</span></code> functions.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">get_traceback_limit()</span></code> function:</p> <blockquote> <div>Get the maximum number of frames stored in the traceback of a trace.<p>The <code class="docutils literal notranslate"><span class="pre">tracemalloc</span></code> module must be tracing memory allocations to get the limit, otherwise an exception is raised.</p> <p>The limit is set by the <code class="docutils literal notranslate"><span class="pre">start()</span></code> function.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">get_traced_memory()</span></code> function:</p> <blockquote> <div>Get the current size and maximum size of memory blocks traced by the <code class="docutils literal notranslate"><span class="pre">tracemalloc</span></code> module as a tuple: <code class="docutils literal notranslate"><span class="pre">(size:</span> <span class="pre">int,</span> <span class="pre">max_size:</span> <span class="pre">int)</span></code>.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">get_tracemalloc_memory()</span></code> function:</p> <blockquote> <div>Get the memory usage in bytes of the <code class="docutils literal notranslate"><span class="pre">tracemalloc</span></code> module used to store traces of memory blocks. Return an <code class="docutils literal notranslate"><span class="pre">int</span></code>.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">is_tracing()</span></code> function:</p> <blockquote> <div><code class="docutils literal notranslate"><span class="pre">True</span></code> if the <code class="docutils literal notranslate"><span class="pre">tracemalloc</span></code> module is tracing Python memory allocations, <code class="docutils literal notranslate"><span class="pre">False</span></code> otherwise.<p>See also <code class="docutils literal notranslate"><span class="pre">start()</span></code> and <code class="docutils literal notranslate"><span class="pre">stop()</span></code> functions.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">start(nframe:</span> <span class="pre">int=1)</span></code> function:</p> <blockquote> <div>Start tracing Python memory allocations: install hooks on Python memory allocators. Collected tracebacks of traces will be limited to <em>nframe</em> frames. By default, a trace of a memory block only stores the most recent frame: the limit is <code class="docutils literal notranslate"><span class="pre">1</span></code>. <em>nframe</em> must be greater or equal to <code class="docutils literal notranslate"><span class="pre">1</span></code>.<p>Storing more than <code class="docutils literal notranslate"><span class="pre">1</span></code> frame is only useful to compute statistics grouped by <code class="docutils literal notranslate"><span class="pre">'traceback'</span></code> or to compute cumulative statistics: see the <code class="docutils literal notranslate"><span class="pre">Snapshot.compare_to()</span></code> and <code class="docutils literal notranslate"><span class="pre">Snapshot.statistics()</span></code> methods.</p> <p>Storing more frames increases the memory and CPU overhead of the <code class="docutils literal notranslate"><span class="pre">tracemalloc</span></code> module. Use the <code class="docutils literal notranslate"><span class="pre">get_tracemalloc_memory()</span></code> function to measure how much memory is used by the <code class="docutils literal notranslate"><span class="pre">tracemalloc</span></code> module.</p> <p>The <code class="docutils literal notranslate"><span class="pre">PYTHONTRACEMALLOC</span></code> environment variable (<code class="docutils literal notranslate"><span class="pre">PYTHONTRACEMALLOC=NFRAME</span></code>) and the <code class="docutils literal notranslate"><span class="pre">-X</span></code> <code class="docutils literal notranslate"><span class="pre">tracemalloc=NFRAME</span></code> command line option can be used to start tracing at startup.</p> <p>See also <code class="docutils literal notranslate"><span class="pre">stop()</span></code>, <code class="docutils literal notranslate"><span class="pre">is_tracing()</span></code> and <code class="docutils literal notranslate"><span class="pre">get_traceback_limit()</span></code> functions.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">stop()</span></code> function:</p> <blockquote> <div>Stop tracing Python memory allocations: uninstall hooks on Python memory allocators. Clear also traces of memory blocks allocated by Python<p>Call <code class="docutils literal notranslate"><span class="pre">take_snapshot()</span></code> function to take a snapshot of traces before clearing them.</p> <p>See also <code class="docutils literal notranslate"><span class="pre">start()</span></code> and <code class="docutils literal notranslate"><span class="pre">is_tracing()</span></code> functions.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">take_snapshot()</span></code> function:</p> <blockquote> <div>Take a snapshot of traces of memory blocks allocated by Python. Return a new <code class="docutils literal notranslate"><span class="pre">Snapshot</span></code> instance.<p>The snapshot does not include memory blocks allocated before the <code class="docutils literal notranslate"><span class="pre">tracemalloc</span></code> module started to trace memory allocations.</p> <p>Tracebacks of traces are limited to <code class="docutils literal notranslate"><span class="pre">get_traceback_limit()</span></code> frames. Use the <em>nframe</em> parameter of the <code class="docutils literal notranslate"><span class="pre">start()</span></code> function to store more frames.</p> <p>The <code class="docutils literal notranslate"><span class="pre">tracemalloc</span></code> module must be tracing memory allocations to take a snapshot, see the <code class="docutils literal notranslate"><span class="pre">start()</span></code> function.</p> <p>See also the <code class="docutils literal notranslate"><span class="pre">get_object_traceback()</span></code> function.</p> </div></blockquote> </section> <section id="filter"> <h3><a class="toc-backref" href="#filter" role="doc-backlink">Filter</a></h3> <p><code class="docutils literal notranslate"><span class="pre">Filter(inclusive:</span> <span class="pre">bool,</span> <span class="pre">filename_pattern:</span> <span class="pre">str,</span> <span class="pre">lineno:</span> <span class="pre">int=None,</span> <span class="pre">all_frames:</span> <span class="pre">bool=False)</span></code> class:</p> <blockquote> <div>Filter on traces of memory blocks.<p>See the <code class="docutils literal notranslate"><span class="pre">fnmatch.fnmatch()</span></code> function for the syntax of <em>filename_pattern</em>. The <code class="docutils literal notranslate"><span class="pre">'.pyc'</span></code> and <code class="docutils literal notranslate"><span class="pre">'.pyo'</span></code> file extensions are replaced with <code class="docutils literal notranslate"><span class="pre">'.py'</span></code>.</p> <p>Examples:</p> <ul class="simple"> <li><code class="docutils literal notranslate"><span class="pre">Filter(True,</span> <span class="pre">subprocess.__file__)</span></code> only includes traces of the <code class="docutils literal notranslate"><span class="pre">subprocess</span></code> module</li> <li><code class="docutils literal notranslate"><span class="pre">Filter(False,</span> <span class="pre">tracemalloc.__file__)</span></code> excludes traces of the <code class="docutils literal notranslate"><span class="pre">tracemalloc</span></code> module</li> <li><code class="docutils literal notranslate"><span class="pre">Filter(False,</span> <span class="pre">"<unknown>")</span></code> excludes empty tracebacks</li> </ul> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">inclusive</span></code> attribute:</p> <blockquote> <div>If <em>inclusive</em> is <code class="docutils literal notranslate"><span class="pre">True</span></code> (include), only trace memory blocks allocated in a file with a name matching <code class="docutils literal notranslate"><span class="pre">filename_pattern</span></code> at line number <code class="docutils literal notranslate"><span class="pre">lineno</span></code>.<p>If <em>inclusive</em> is <code class="docutils literal notranslate"><span class="pre">False</span></code> (exclude), ignore memory blocks allocated in a file with a name matching <code class="docutils literal notranslate"><span class="pre">filename_pattern</span></code> at line number <code class="docutils literal notranslate"><span class="pre">lineno</span></code>.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">lineno</span></code> attribute:</p> <blockquote> <div>Line number (<code class="docutils literal notranslate"><span class="pre">int</span></code>) of the filter. If <em>lineno</em> is <code class="docutils literal notranslate"><span class="pre">None</span></code>, the filter matches any line number.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">filename_pattern</span></code> attribute:</p> <blockquote> <div>Filename pattern of the filter (<code class="docutils literal notranslate"><span class="pre">str</span></code>).</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">all_frames</span></code> attribute:</p> <blockquote> <div>If <em>all_frames</em> is <code class="docutils literal notranslate"><span class="pre">True</span></code>, all frames of the traceback are checked. If <em>all_frames</em> is <code class="docutils literal notranslate"><span class="pre">False</span></code>, only the most recent frame is checked.<p>This attribute is ignored if the traceback limit is less than <code class="docutils literal notranslate"><span class="pre">2</span></code>. See the <code class="docutils literal notranslate"><span class="pre">get_traceback_limit()</span></code> function and <code class="docutils literal notranslate"><span class="pre">Snapshot.traceback_limit</span></code> attribute.</p> </div></blockquote> </section> <section id="frame"> <h3><a class="toc-backref" href="#frame" role="doc-backlink">Frame</a></h3> <p><code class="docutils literal notranslate"><span class="pre">Frame</span></code> class:</p> <blockquote> <div>Frame of a traceback.<p>The <code class="docutils literal notranslate"><span class="pre">Traceback</span></code> class is a sequence of <code class="docutils literal notranslate"><span class="pre">Frame</span></code> instances.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">filename</span></code> attribute:</p> <blockquote> <div>Filename (<code class="docutils literal notranslate"><span class="pre">str</span></code>).</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">lineno</span></code> attribute:</p> <blockquote> <div>Line number (<code class="docutils literal notranslate"><span class="pre">int</span></code>).</div></blockquote> </section> <section id="snapshot"> <h3><a class="toc-backref" href="#snapshot" role="doc-backlink">Snapshot</a></h3> <p><code class="docutils literal notranslate"><span class="pre">Snapshot</span></code> class:</p> <blockquote> <div>Snapshot of traces of memory blocks allocated by Python.<p>The <code class="docutils literal notranslate"><span class="pre">take_snapshot()</span></code> function creates a snapshot instance.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">compare_to(old_snapshot:</span> <span class="pre">Snapshot,</span> <span class="pre">group_by:</span> <span class="pre">str,</span> <span class="pre">cumulative:</span> <span class="pre">bool=False)</span></code> method:</p> <blockquote> <div>Compute the differences with an old snapshot. Get statistics as a sorted list of <code class="docutils literal notranslate"><span class="pre">StatisticDiff</span></code> instances grouped by <em>group_by</em>.<p>See the <code class="docutils literal notranslate"><span class="pre">statistics()</span></code> method for <em>group_by</em> and <em>cumulative</em> parameters.</p> <p>The result is sorted from the biggest to the smallest by: absolute value of <code class="docutils literal notranslate"><span class="pre">StatisticDiff.size_diff</span></code>, <code class="docutils literal notranslate"><span class="pre">StatisticDiff.size</span></code>, absolute value of <code class="docutils literal notranslate"><span class="pre">StatisticDiff.count_diff</span></code>, <code class="docutils literal notranslate"><span class="pre">Statistic.count</span></code> and then by <code class="docutils literal notranslate"><span class="pre">StatisticDiff.traceback</span></code>.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">dump(filename)</span></code> method:</p> <blockquote> <div>Write the snapshot into a file.<p>Use <code class="docutils literal notranslate"><span class="pre">load()</span></code> to reload the snapshot.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">filter_traces(filters)</span></code> method:</p> <blockquote> <div>Create a new <code class="docutils literal notranslate"><span class="pre">Snapshot</span></code> instance with a filtered <code class="docutils literal notranslate"><span class="pre">traces</span></code> sequence, <em>filters</em> is a list of <code class="docutils literal notranslate"><span class="pre">Filter</span></code> instances. If <em>filters</em> is an empty list, return a new <code class="docutils literal notranslate"><span class="pre">Snapshot</span></code> instance with a copy of the traces.<p>All inclusive filters are applied at once, a trace is ignored if no inclusive filters match it. A trace is ignored if at least one exclusive filter matches it.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">load(filename)</span></code> classmethod:</p> <blockquote> <div>Load a snapshot from a file.<p>See also <code class="docutils literal notranslate"><span class="pre">dump()</span></code>.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">statistics(group_by:</span> <span class="pre">str,</span> <span class="pre">cumulative:</span> <span class="pre">bool=False)</span></code> method:</p> <blockquote> <div>Get statistics as a sorted list of <code class="docutils literal notranslate"><span class="pre">Statistic</span></code> instances grouped by <em>group_by</em>:<table class="docutils align-default"> <thead> <tr class="row-odd"><th class="head">group_by</th> <th class="head">description</th> </tr> </thead> <tbody> <tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'filename'</span></code></td> <td>filename</td> </tr> <tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">'lineno'</span></code></td> <td>filename and line number</td> </tr> <tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'traceback'</span></code></td> <td>traceback</td> </tr> </tbody> </table> <p>If <em>cumulative</em> is <code class="docutils literal notranslate"><span class="pre">True</span></code>, cumulate size and count of memory blocks of all frames of the traceback of a trace, not only the most recent frame. The cumulative mode can only be used with <em>group_by</em> equals to <code class="docutils literal notranslate"><span class="pre">'filename'</span></code> and <code class="docutils literal notranslate"><span class="pre">'lineno'</span></code> and <code class="docutils literal notranslate"><span class="pre">traceback_limit</span></code> greater than <code class="docutils literal notranslate"><span class="pre">1</span></code>.</p> <p>The result is sorted from the biggest to the smallest by: <code class="docutils literal notranslate"><span class="pre">Statistic.size</span></code>, <code class="docutils literal notranslate"><span class="pre">Statistic.count</span></code> and then by <code class="docutils literal notranslate"><span class="pre">Statistic.traceback</span></code>.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">traceback_limit</span></code> attribute:</p> <blockquote> <div>Maximum number of frames stored in the traceback of <code class="docutils literal notranslate"><span class="pre">traces</span></code>: result of the <code class="docutils literal notranslate"><span class="pre">get_traceback_limit()</span></code> when the snapshot was taken.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">traces</span></code> attribute:</p> <blockquote> <div>Traces of all memory blocks allocated by Python: sequence of <code class="docutils literal notranslate"><span class="pre">Trace</span></code> instances.<p>The sequence has an undefined order. Use the <code class="docutils literal notranslate"><span class="pre">Snapshot.statistics()</span></code> method to get a sorted list of statistics.</p> </div></blockquote> </section> <section id="statistic"> <h3><a class="toc-backref" href="#statistic" role="doc-backlink">Statistic</a></h3> <p><code class="docutils literal notranslate"><span class="pre">Statistic</span></code> class:</p> <blockquote> <div>Statistic on memory allocations.<p><code class="docutils literal notranslate"><span class="pre">Snapshot.statistics()</span></code> returns a list of <code class="docutils literal notranslate"><span class="pre">Statistic</span></code> instances.</p> <p>See also the <code class="docutils literal notranslate"><span class="pre">StatisticDiff</span></code> class.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">count</span></code> attribute:</p> <blockquote> <div>Number of memory blocks (<code class="docutils literal notranslate"><span class="pre">int</span></code>).</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">size</span></code> attribute:</p> <blockquote> <div>Total size of memory blocks in bytes (<code class="docutils literal notranslate"><span class="pre">int</span></code>).</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">traceback</span></code> attribute:</p> <blockquote> <div>Traceback where the memory block was allocated, <code class="docutils literal notranslate"><span class="pre">Traceback</span></code> instance.</div></blockquote> </section> <section id="statisticdiff"> <h3><a class="toc-backref" href="#statisticdiff" role="doc-backlink">StatisticDiff</a></h3> <p><code class="docutils literal notranslate"><span class="pre">StatisticDiff</span></code> class:</p> <blockquote> <div>Statistic difference on memory allocations between an old and a new <code class="docutils literal notranslate"><span class="pre">Snapshot</span></code> instance.<p><code class="docutils literal notranslate"><span class="pre">Snapshot.compare_to()</span></code> returns a list of <code class="docutils literal notranslate"><span class="pre">StatisticDiff</span></code> instances. See also the <code class="docutils literal notranslate"><span class="pre">Statistic</span></code> class.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">count</span></code> attribute:</p> <blockquote> <div>Number of memory blocks in the new snapshot (<code class="docutils literal notranslate"><span class="pre">int</span></code>): <code class="docutils literal notranslate"><span class="pre">0</span></code> if the memory blocks have been released in the new snapshot.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">count_diff</span></code> attribute:</p> <blockquote> <div>Difference of number of memory blocks between the old and the new snapshots (<code class="docutils literal notranslate"><span class="pre">int</span></code>): <code class="docutils literal notranslate"><span class="pre">0</span></code> if the memory blocks have been allocated in the new snapshot.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">size</span></code> attribute:</p> <blockquote> <div>Total size of memory blocks in bytes in the new snapshot (<code class="docutils literal notranslate"><span class="pre">int</span></code>): <code class="docutils literal notranslate"><span class="pre">0</span></code> if the memory blocks have been released in the new snapshot.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">size_diff</span></code> attribute:</p> <blockquote> <div>Difference of total size of memory blocks in bytes between the old and the new snapshots (<code class="docutils literal notranslate"><span class="pre">int</span></code>): <code class="docutils literal notranslate"><span class="pre">0</span></code> if the memory blocks have been allocated in the new snapshot.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">traceback</span></code> attribute:</p> <blockquote> <div>Traceback where the memory blocks were allocated, <code class="docutils literal notranslate"><span class="pre">Traceback</span></code> instance.</div></blockquote> </section> <section id="trace"> <h3><a class="toc-backref" href="#trace" role="doc-backlink">Trace</a></h3> <p><code class="docutils literal notranslate"><span class="pre">Trace</span></code> class:</p> <blockquote> <div>Trace of a memory block.<p>The <code class="docutils literal notranslate"><span class="pre">Snapshot.traces</span></code> attribute is a sequence of <code class="docutils literal notranslate"><span class="pre">Trace</span></code> instances.</p> </div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">size</span></code> attribute:</p> <blockquote> <div>Size of the memory block in bytes (<code class="docutils literal notranslate"><span class="pre">int</span></code>).</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">traceback</span></code> attribute:</p> <blockquote> <div>Traceback where the memory block was allocated, <code class="docutils literal notranslate"><span class="pre">Traceback</span></code> instance.</div></blockquote> </section> <section id="traceback"> <h3><a class="toc-backref" href="#traceback" role="doc-backlink">Traceback</a></h3> <p><code class="docutils literal notranslate"><span class="pre">Traceback</span></code> class:</p> <blockquote> <div>Sequence of <code class="docutils literal notranslate"><span class="pre">Frame</span></code> instances sorted from the most recent frame to the oldest frame.<p>A traceback contains at least <code class="docutils literal notranslate"><span class="pre">1</span></code> frame. If the <code class="docutils literal notranslate"><span class="pre">tracemalloc</span></code> module failed to get a frame, the filename <code class="docutils literal notranslate"><span class="pre">"<unknown>"</span></code> at line number <code class="docutils literal notranslate"><span class="pre">0</span></code> is used.</p> <p>When a snapshot is taken, tracebacks of traces are limited to <code class="docutils literal notranslate"><span class="pre">get_traceback_limit()</span></code> frames. See the <code class="docutils literal notranslate"><span class="pre">take_snapshot()</span></code> function.</p> <p>The <code class="docutils literal notranslate"><span class="pre">Trace.traceback</span></code> attribute is an instance of <code class="docutils literal notranslate"><span class="pre">Traceback</span></code> instance.</p> </div></blockquote> </section> </section> <section id="rejected-alternatives"> <h2><a class="toc-backref" href="#rejected-alternatives" role="doc-backlink">Rejected Alternatives</a></h2> <section id="log-calls-to-the-memory-allocator"> <h3><a class="toc-backref" href="#log-calls-to-the-memory-allocator" role="doc-backlink">Log calls to the memory allocator</a></h3> <p>A different approach is to log calls to <code class="docutils literal notranslate"><span class="pre">malloc()</span></code>, <code class="docutils literal notranslate"><span class="pre">realloc()</span></code> and <code class="docutils literal notranslate"><span class="pre">free()</span></code> functions. Calls can be logged into a file or send to another computer through the network. Example of a log entry: name of the function, size of the memory block, address of the memory block, Python traceback where the allocation occurred, timestamp.</p> <p>Logs cannot be used directly, getting the current status of the memory requires to parse previous logs. For example, it is not possible to get directly the traceback of a Python object, like <code class="docutils literal notranslate"><span class="pre">get_object_traceback(obj)</span></code> does with traces.</p> <p>Python uses objects with a very short lifetime and so makes an extensive use of memory allocators. It has an allocator optimized for small objects (less than 512 bytes) with a short lifetime. For example, the Python test suites calls <code class="docutils literal notranslate"><span class="pre">malloc()</span></code>, <code class="docutils literal notranslate"><span class="pre">realloc()</span></code> or <code class="docutils literal notranslate"><span class="pre">free()</span></code> 270,000 times per second in average. If the size of log entry is 32 bytes, logging produces 8.2 MB per second or 29.0 GB per hour.</p> <p>The alternative was rejected because it is less efficient and has less features. Parsing logs in a different process or a different computer is slower than maintaining traces on allocated memory blocks in the same process.</p> </section> </section> <section id="prior-work"> <h2><a class="toc-backref" href="#prior-work" role="doc-backlink">Prior Work</a></h2> <ul class="simple"> <li><a class="reference external" href="http://www.softwareverify.com/python/memory/index.html">Python Memory Validator</a> (2005-2013): commercial Python memory validator developed by Software Verification. It uses the Python Reflection API.</li> <li><a class="reference external" href="http://pysizer.8325.org/">PySizer</a>: Google Summer of Code 2005 project by Nick Smallbone.</li> <li><a class="reference external" href="http://guppy-pe.sourceforge.net/">Heapy</a> (2006-2013): part of the Guppy-PE project written by Sverker Nilsson.</li> <li>Draft PEP: <a class="reference external" href="http://svn.python.org/projects/python/branches/bcannon-sandboxing/PEP.txt">Support Tracking Low-Level Memory Usage in CPython</a> (Brett Canon, 2006)</li> <li>Muppy: project developed in 2008 by Robert Schuppenies.</li> <li><a class="reference external" href="http://code.activestate.com/recipes/546530/">asizeof</a>: a pure Python module to estimate the size of objects by Jean Brouwers (2008).</li> <li><a class="reference external" href="http://www.scons.org/wiki/LudwigHaehne/HeapMonitor">Heapmonitor</a>: It provides facilities to size individual objects and can track all objects of certain classes. It was developed in 2008 by Ludwig Haehne.</li> <li><a class="reference external" href="http://code.google.com/p/pympler/">Pympler</a> (2008-2011): project based on asizeof, muppy and HeapMonitor</li> <li><a class="reference external" href="http://mg.pov.lt/objgraph/">objgraph</a> (2008-2012)</li> <li><a class="reference external" href="https://pypi.python.org/pypi/Dozer">Dozer</a>: WSGI Middleware version of the CherryPy memory leak debugger, written by Marius Gedminas (2008-2013)</li> <li><a class="reference external" href="https://pypi.python.org/pypi/meliae">Meliae</a>: Python Memory Usage Analyzer developed by John A Meinel since 2009</li> <li><a class="reference external" href="https://fedorahosted.org/gdb-heap/">gdb-heap</a>: gdb script written in Python by Dave Malcolm (2010-2011) to analyze the usage of the heap memory</li> <li><a class="reference external" href="https://pypi.python.org/pypi/memory_profiler">memory_profiler</a>: written by Fabian Pedregosa (2011-2013)</li> <li><a class="reference external" href="https://github.com/smartfile/caulk/">caulk</a>: written by Ben Timby in 2012</li> </ul> <p>See also <a class="reference external" href="http://pythonhosted.org/Pympler/related.html">Pympler Related Work</a>.</p> </section> <section id="links"> <h2><a class="toc-backref" href="#links" role="doc-backlink">Links</a></h2> <p>tracemalloc:</p> <ul class="simple"> <li><a class="reference external" href="http://bugs.python.org/issue18874">#18874: Add a new tracemalloc module to trace Python memory allocations</a></li> <li><a class="reference external" href="https://pypi.python.org/pypi/pytracemalloc">pytracemalloc on PyPI</a></li> </ul> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document has been placed in the public domain.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0454.rst">https://github.com/python/peps/blob/main/peps/pep-0454.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0454.rst">2023-09-09 17:39:29 GMT</a></p> </article> <nav id="pep-sidebar"> <h2>Contents</h2> <ul> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#proposal">Proposal</a></li> <li><a class="reference internal" href="#api">API</a><ul> <li><a class="reference internal" href="#functions">Functions</a></li> <li><a class="reference internal" href="#filter">Filter</a></li> <li><a class="reference internal" href="#frame">Frame</a></li> <li><a class="reference internal" href="#snapshot">Snapshot</a></li> <li><a class="reference internal" href="#statistic">Statistic</a></li> <li><a class="reference internal" href="#statisticdiff">StatisticDiff</a></li> <li><a class="reference internal" href="#trace">Trace</a></li> <li><a class="reference internal" href="#traceback">Traceback</a></li> </ul> </li> <li><a class="reference internal" href="#rejected-alternatives">Rejected Alternatives</a><ul> <li><a class="reference internal" href="#log-calls-to-the-memory-allocator">Log calls to the memory allocator</a></li> </ul> </li> <li><a class="reference internal" href="#prior-work">Prior Work</a></li> <li><a class="reference internal" href="#links">Links</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> <br> <a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0454.rst">Page Source (GitHub)</a> </nav> </section> <script src="../_static/colour_scheme.js"></script> <script src="../_static/wrap_tables.js"></script> <script src="../_static/sticky_banner.js"></script> </body> </html>