<div class="gmail_quote">On Fri, Jun 15, 2012 at 3:06 PM, Nam Nguyen <span dir="ltr">&lt;<a href="mailto:bitsink@gmail.com" target="_blank">bitsink@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
If I recall correctly, CPython memory management does not free memory.<br>
Once it has allocated a slab, it will not release that slab. The<br>
garbage collector makes room for CPython allocated objects in all the<br>
heap spaces that CPython allocated.<br>
<span class="HOEnZb"><font color="#888888">Nam<br>
</font></span><div class="HOEnZb"><div class="h5"><br>
On Fri, Jun 15, 2012 at 2:44 PM, David Lawrence &lt;<a href="mailto:david@bitcasa.com">david@bitcasa.com</a>&gt; wrote:<br>
&gt; On Fri, Jun 15, 2012 at 2:41 PM, Bob Ippolito &lt;<a href="mailto:bob@redivi.com">bob@redivi.com</a>&gt; wrote:<br>
&gt;&gt;<br>
&gt;&gt; On Fri, Jun 15, 2012 at 5:32 PM, David Lawrence &lt;<a href="mailto:david@bitcasa.com">david@bitcasa.com</a>&gt; wrote:<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; On Fri, Jun 15, 2012 at 2:22 PM, Bob Ippolito &lt;<a href="mailto:bob@redivi.com">bob@redivi.com</a>&gt; wrote:<br>
&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt; On Fri, Jun 15, 2012 at 4:15 PM, David Lawrence &lt;<a href="mailto:david@bitcasa.com">david@bitcasa.com</a>&gt;<br>
&gt;&gt;&gt;&gt; wrote:<br>
&gt;&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt;&gt; When I load the file into json, pythons memory usage spike to about<br>
&gt;&gt;&gt;&gt;&gt; 1.8GB and I can&#39;t seem to get that memory to be released.  I put together a<br>
&gt;&gt;&gt;&gt;&gt; test case that&#39;s very simple:<br>
&gt;&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt;&gt; with open(&quot;test_file.json&quot;, &#39;r&#39;) as f:<br>
&gt;&gt;&gt;&gt;&gt;     j = json.load(f)<br>
&gt;&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt;&gt; I&#39;m sorry that I can&#39;t provide a sample json file, my test file has a<br>
&gt;&gt;&gt;&gt;&gt; lot of sensitive information, but for context, I&#39;m dealing with a file in<br>
&gt;&gt;&gt;&gt;&gt; the order of 240MB.  After running the above 2 lines I have the<br>
&gt;&gt;&gt;&gt;&gt; previously mentioned 1.8GB of memory in use.  If I then do &quot;del j&quot; memory<br>
&gt;&gt;&gt;&gt;&gt; usage doesn&#39;t drop at all.  If I follow that with a &quot;gc.collect()&quot; it still<br>
&gt;&gt;&gt;&gt;&gt; doesn&#39;t drop.  I even tried unloading the json module and running another<br>
&gt;&gt;&gt;&gt;&gt; gc.collect.<br>
&gt;&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt;&gt; I&#39;m trying to run some memory profiling but heapy has been churning<br>
&gt;&gt;&gt;&gt;&gt; 100% CPU for about an hour now and has yet to produce any output.<br>
&gt;&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt;&gt; Does anyone have any ideas?  I&#39;ve also tried the above using cjson<br>
&gt;&gt;&gt;&gt;&gt; rather than the packaged json module.  cjson used about 30% less memory but<br>
&gt;&gt;&gt;&gt;&gt; otherwise displayed exactly the same issues.<br>
&gt;&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt;&gt; I&#39;m running Python 2.7.2 on Ubuntu server 11.10.<br>
&gt;&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt;&gt; I&#39;m happy to load up any memory profiler and see if it does better then<br>
&gt;&gt;&gt;&gt;&gt; heapy and provide any diagnostics you might think are necessary.  I&#39;m<br>
&gt;&gt;&gt;&gt;&gt; hunting around for a large test json file that I can provide for anyone else<br>
&gt;&gt;&gt;&gt;&gt; to give it a go.<br>
&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt; It may just be the way that the allocator works. What happens if you<br>
&gt;&gt;&gt;&gt; load the JSON, del the object, then do it again? Does it take up 3.6GB or<br>
&gt;&gt;&gt;&gt; stay at 1.8GB? You may not be able to &quot;release&quot; that memory to the OS in<br>
&gt;&gt;&gt;&gt; such a way that RSS gets smaller... but at the same time it&#39;s not really a<br>
&gt;&gt;&gt;&gt; leak either.<br>
&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt; GC shouldn&#39;t really take part in a JSON structure, since it&#39;s guaranteed<br>
&gt;&gt;&gt;&gt; to be acyclic… ref counting alone should be sufficient to instantly reclaim<br>
&gt;&gt;&gt;&gt; that space. I&#39;m not at all surprised that gc.collect() doesn&#39;t change<br>
&gt;&gt;&gt;&gt; anything for CPython in this case.<br>
&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt; $ python<br>
&gt;&gt;&gt;&gt; Python 2.7.2 (default, Jan 23 2012, 14:26:16)<br>
&gt;&gt;&gt;&gt; [GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on<br>
&gt;&gt;&gt;&gt; darwin<br>
&gt;&gt;&gt;&gt; Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; import os, subprocess, simplejson<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; def rss(): return subprocess.Popen([&#39;ps&#39;, &#39;-o&#39;, &#39;rss&#39;, &#39;-p&#39;,<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; str(os.getpid())],<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; stdout=subprocess.PIPE).communicate()[0].splitlines()[1].strip()<br>
&gt;&gt;&gt;&gt; ...<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; rss()<br>
&gt;&gt;&gt;&gt; &#39;7284&#39;<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; l = simplejson.loads(simplejson.dumps([x for x in xrange(1000000)]))<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; rss()<br>
&gt;&gt;&gt;&gt; &#39;49032&#39;<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; del l<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; rss()<br>
&gt;&gt;&gt;&gt; &#39;42232&#39;<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; l = simplejson.loads(simplejson.dumps([x for x in xrange(1000000)]))<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; rss()<br>
&gt;&gt;&gt;&gt; &#39;49032&#39;<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; del l<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; rss()<br>
&gt;&gt;&gt;&gt; &#39;42232&#39;<br>
&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt; $ python<br>
&gt;&gt;&gt;&gt; Python 2.7.2 (default, Jan 23 2012, 14:26:16)<br>
&gt;&gt;&gt;&gt; [GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on<br>
&gt;&gt;&gt;&gt; darwin<br>
&gt;&gt;&gt;&gt; Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; import os, subprocess, simplejson<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; def rss(): return subprocess.Popen([&#39;ps&#39;, &#39;-o&#39;, &#39;rss&#39;, &#39;-p&#39;,<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; str(os.getpid())],<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; stdout=subprocess.PIPE).communicate()[0].splitlines()[1].strip()<br>
&gt;&gt;&gt;&gt; ...<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; l = simplejson.loads(simplejson.dumps(dict((str(x), x) for x in<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; xrange(1000000))))<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; rss()<br>
&gt;&gt;&gt;&gt; &#39;288116&#39;<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; del l<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; rss()<br>
&gt;&gt;&gt;&gt; &#39;84384&#39;<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; l = simplejson.loads(simplejson.dumps(dict((str(x), x) for x in<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; xrange(1000000))))<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; rss()<br>
&gt;&gt;&gt;&gt; &#39;288116&#39;<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; del l<br>
&gt;&gt;&gt;&gt; &gt;&gt;&gt; rss()<br>
&gt;&gt;&gt;&gt; &#39;84384&#39;<br>
&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt; -bob<br>
&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; It does appear that deleting the object and running the example again the<br>
&gt;&gt;&gt; memory stays static at about 1.8GB.  Could you provide a little more detail<br>
&gt;&gt;&gt; on what your examples are meant to demonstrate.  One shows a static memory<br>
&gt;&gt;&gt; footprint and the other shows the footprint fluctuating up and down.  I<br>
&gt;&gt;&gt; would expect the static footprint in the first example just from my<br>
&gt;&gt;&gt; understanding of python free lists of integers.<br>
&gt;&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt; Both examples show the same thing, but with different data structures<br>
&gt;&gt; (list of int, dict of str:int). The only thing missing is that I left out<br>
&gt;&gt; the baseline in the second example, it would be the same as the first<br>
&gt;&gt; example.<br>
&gt;&gt;<br>
&gt;&gt; The other suggestions are spot on. If you want the memory to really be<br>
&gt;&gt; released, you have to do it in a transient subprocess, and/or you could<br>
&gt;&gt; probably have lower overhead if you&#39;re using a streaming parser (if there&#39;s<br>
&gt;&gt; something you can do with it incrementally).<br>
&gt;&gt;<br>
&gt;&gt; -bob<br>
&gt;&gt;<br>
&gt;<br>
&gt; Thank you all for the help.  Multiprocessing with a Queue and blocking get()<br>
&gt; calls looks like it will work well.<br>
&gt;<br>
</div></div><div class="HOEnZb"><div class="h5">&gt; _______________________________________________<br>
&gt; Baypiggies mailing list<br>
&gt; <a href="mailto:Baypiggies@python.org">Baypiggies@python.org</a><br>
&gt; To change your subscription options or unsubscribe:<br>
&gt; <a href="http://mail.python.org/mailman/listinfo/baypiggies" target="_blank">http://mail.python.org/mailman/listinfo/baypiggies</a><br>
</div></div></blockquote></div><br><div><br></div><div>Lots of people have raised this idea in my hunt for answers.  However, releasing memory to the OS appears to be objects dependent.  I assume this is because different types use different memory allocators. Does anyone have a deeper understanding of this?</div>