Skip to content

Commit b75139c

Browse files
committed
final version of talk
1 parent 9b65aa4 commit b75139c

File tree

9 files changed

+385
-208
lines changed

9 files changed

+385
-208
lines changed

_downloads/CircularReferenceExample.ipynb

Lines changed: 119 additions & 128 deletions
Large diffs are not rendered by default.

_downloads/simple_circular.py

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
"""
66

77
import sys
8-
import di
9-
import gc
108

119
l1 = [1,]
1210
l2 = [2,]
@@ -23,29 +21,18 @@
2321
l2.append(l1)
2422

2523
print "ref counts after the circular reference is added"
26-
print "l1:", sys.getrefcount(l1)
27-
print "l2:", sys.getrefcount(l2)
24+
print "l1:", l1, "refcount:", sys.getrefcount(l1)
25+
print "l2:", l2, "refcount:", sys.getrefcount(l2)
2826

2927
print "delete one"
3028
del l1
31-
print "l1:", sys.getrefcount(l2)
29+
print "l2:", sys.getrefcount(l2)
3230

3331
print "delete the other"
3432
del l2
3533

36-
print "are they still there?"
37-
print "l1's refcount:", di.ref_by_id(l1_id)
38-
print "l2's refcount:", di.ref_by_id(l2_id)
39-
print "-- yes they are!"
40-
41-
print "now run the garbage collector"
42-
print "It collected:", gc.collect(), "objects"
43-
44-
print "ref counts now???"
45-
print "l1's refcount:", di.ref_by_id(l1_id)
46-
print "l2's refcount:", di.ref_by_id(l2_id)
34+
print "but are they still there???"
4735

48-
print "yup -- they'll get cleaned up"
4936

5037

5138

_downloads/simple_circular_di.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env python
2+
3+
"""
4+
Simple contrived example of circular reference counting
5+
6+
This one does some testing with di.ref_by_id
7+
"""
8+
9+
import sys
10+
import di
11+
import gc
12+
13+
l1 = [1,]
14+
l2 = [2,]
15+
16+
l1_id = id(l1)
17+
l2_id = id(l2)
18+
19+
print "initial ref counts:"
20+
print "l1:", sys.getrefcount(l1)
21+
print "l2:", sys.getrefcount(l2)
22+
23+
print "now add the circular references:"
24+
l1.append(l2)
25+
l2.append(l1)
26+
27+
print "ref counts after the circular reference is added"
28+
print "l1:", sys.getrefcount(l1)
29+
print "l2:", sys.getrefcount(l2)
30+
31+
print "delete one"
32+
del l1
33+
print "l1:", sys.getrefcount(l2)
34+
35+
print "delete the other"
36+
del l2
37+
38+
print "are they still there?"
39+
print "l1's refcount:", di.ref_by_id(l1_id)
40+
print "l2's refcount:", di.ref_by_id(l2_id)
41+
print "-- yes they are!"
42+
43+
print "now run the garbage collector"
44+
print "It collected:", gc.collect(), "objects"
45+
46+
print "ref counts now???"
47+
print "l1's refcount:", di.ref_by_id(l1_id)
48+
print "l2's refcount:", di.ref_by_id(l2_id)
49+
50+
print "yup -- they'll get cleaned up"
51+
52+
53+
54+

_sources/weak_references.txt

Lines changed: 108 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ The Power of Reference Counting
173173

174174
* Objects get deleted right away
175175

176-
. They get "cleaned up" (files, for instance)
176+
. They can "clean up" on deletion (files, for instance) -- and it will happen right away.
177177

178178
* Performance is predictable
179179

@@ -187,7 +187,7 @@ The Limits of Reference Counting
187187

188188
Circular references
189189

190-
If a python object references another object that references the first
190+
If a python object somehow references itself -- i.e. it references another object that references the first
191191
object:
192192

193193
You have a circular reference ...
@@ -196,42 +196,41 @@ You have a circular reference ...
196196
Circular References
197197
===================
198198

199-
Circular Reference Example
200-
--------------------------
199+
.. rst-class:: left
201200

202-
.. code-block:: ipython
201+
.. code-block:: ipython
203202

204-
In [8]: l1 = [1,] ; l2 = [2,]
203+
In [8]: l1 = [1,] ; l2 = [2,]
205204

206-
In [9]: l1.append(l2); l2.append(l1)
205+
In [9]: l1.append(l2); l2.append(l1)
207206

208-
In [10]: l1
209-
Out[10]: [1, [2, [...]]]
207+
In [10]: l1
208+
Out[10]: [1, [2, [...]]]
210209

211-
In [11]: l2
212-
Out[11]: [2, [1, [...]]]
210+
In [11]: l2
211+
Out[11]: [2, [1, [...]]]
213212

214-
In [12]: l1[1]
215-
Out[12]: [2, [1, [...]]]
213+
In [12]: l1[1]
214+
Out[12]: [2, [1, [...]]]
216215

217-
In [13]: l2[1][1][1]
218-
Out[13]: [1, [2, [...]]]
216+
In [13]: l2[1][1][1]
217+
Out[13]: [1, [2, [...]]]
219218

220-
In [16]: sys.getrefcount(l1)
221-
Out[16]: 12
219+
(demo) -- :download:`simple_circular.py <../code/weak_references/simple_circular.py>`
222220

223221

224222
The Garbage Collector
225-
-----------------------
223+
----------------------
226224

227225
As of Python 2.0 -- a garbage collector was added.
228226

227+
- (https://docs.python.org/2/library/gc.html)
228+
229229
It can find and clean up "unreachable" references.
230230

231231
It is turned on by default::
232232

233233
In [1]: import gc
234-
235234
In [2]: gc.isenabled()
236235
Out[2]: True
237236

@@ -242,49 +241,124 @@ or you can force it::
242241

243242
But it can be slow, and doesn't always work!
244243

245-
Examples
244+
.. nextslide::
245+
246+
How does the garbage collector work?
247+
248+
* Not a full "mark and sweep" type.
249+
250+
It searches for reference cycles -- then cleans those up.
251+
252+
* It doesn't have to bother checking non-container types (ints, strings, etc.)
253+
254+
* Faster, and not as dependent on having a clear "root" namespace.
255+
256+
Details here:
257+
258+
http://arctrix.com/nas/python/gc/ (or in the source!)
259+
260+
Big issue: classes that define a ``__del__`` method are not cleaned up.
261+
262+
* ``__del__`` methods often act on references that may no be there if
263+
they are cleaned up in the wrong order.
264+
265+
NOTE: you can work with gc.garbage() -- but tricky and messy
266+
267+
=====
268+
Tools
269+
=====
270+
271+
.. rst-class:: left
272+
273+
If these objects are no longer "reachable" -- how do you find out what's going on?
274+
275+
We saw ``sys.getrefcount()`` -- but you need a reference to the object to use it.
276+
277+
You can see what the refcount is before you delete the last reference, but that isn't always easy.
278+
279+
280+
Process Memory Use
281+
-------------------
282+
283+
A really coarse way to find a memory leak is to see if the process memory
284+
is growing.
285+
286+
It can be subtle --python (and the OS) do tricks to re-use memory, etc.
287+
288+
But if you have a "real" leak -- you'll see it. (Example to follow)
289+
290+
:download:`mem_check.py <../code/weak_references/mem_check.py>`
291+
292+
provides functions that report the memory use of the current running process.
293+
294+
(\*nix and Windows code)
295+
296+
id checks
246297
----------
247298

248-
A really simple example:
299+
As it happens, the Python ``id()`` function returns a memory address.
249300

250-
:download:`simple_circular.py <../code/weak_references/simple_circular.py>`
301+
It's really dangerous, but that means we can examine an object if we know
302+
its `id`, even if we don't hold a reference to it.
251303

304+
Bill Bumgarner wrote a nifty extension module that returns the python
305+
object pointed to by an id (memory address) -- "di":
252306

253-
More real example in iPython notebook:
307+
http://www.friday.com/bbum/2007/08/24/python-di/
254308

255-
:download:`CircularReferenceExample.ipynb <../code/weak_references/CircularReferenceExample.ipynb>`
309+
I added a function that returns the reference count of an object from its id.
256310

257-
If you don't have the notebook running:
311+
https://github.com/PythonCHB/di_refcount
258312

259-
:download:`circular.py <../code/weak_references/circular.py>`
313+
NOTE: it would be a really bad idea to use these in production code!
260314

261-
And :download:`memcount.py <../code/weak_references/memcount.py>` is a test
262-
file that show memory growth if circular references are not cleaned up.
315+
Examples
316+
----------
317+
318+
:download:`simple_circular_di.py <../code/weak_references/simple_circular_di.py>`
263319

320+
uses the ref_by_id() function to see what's going on with a circular
321+
reference and garbage collection.
322+
323+
More real examples in iPython notebook:
324+
325+
:download:`CircularReferenceExample.ipynb <../code/weak_references/CircularReferenceExample.ipynb>`
326+
327+
Or: :download:`circular.py <../code/weak_references/circular.py>`
328+
329+
:download:`memcount.py <../code/weak_references/memcount.py>` is a test
330+
file that show memory growth if circular references are not cleaned up.
264331

265332
( :download:`mem_check.py <../code/weak_references/mem_check.py>` )
266333
is code that reports process memory use.
267334

335+
You can find this code in the main repo here:
336+
337+
https://github.com/PythonCHB/PythonTopics/tree/master/Sources/code/weak_references
338+
268339

269340
Weak References
270341
-----------------
271342

272343
For times when you don't want to keep objects alive, Python provides
273-
"weak references".
344+
"weak references" -- we saw this in the examples.
274345

275-
You saw this in the examples.
346+
(https://docs.python.org/2/library/weakref.html)
276347

277-
Three ways to use them:
348+
1. The built-in containers:
278349

279-
* The built-in containers:
280350
- ``WeakKeyDictionary``
351+
281352
- ``WeakValueDictionary``
353+
282354
- ``WeakSet``
283355

284-
* ``Proxy`` objects
356+
2. ``Proxy`` objects
357+
285358
- act much like regular references -- client code doesn't know the difference
286359

287-
* ``WeakRef`` objects
360+
3. ``WeakRef`` objects
361+
288362
- When you want to control what happens when the referenced object is gone.
289363

290364
=========

genindex.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
<li class="toctree-l1"><a class="reference internal" href="weak_references.html">Python Memory Management and Weak References</a><ul>
6161
<li class="toctree-l2"><a class="reference internal" href="weak_references.html#memory-management">Memory Management</a></li>
6262
<li class="toctree-l2"><a class="reference internal" href="weak_references.html#circular-references">Circular References</a></li>
63+
<li class="toctree-l2"><a class="reference internal" href="weak_references.html#tools">Tools</a></li>
6364
<li class="toctree-l2"><a class="reference internal" href="weak_references.html#exercise">Exercise</a></li>
6465
</ul>
6566
</li>

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
<li class="toctree-l1"><a class="reference internal" href="weak_references.html">Python Memory Management and Weak References</a><ul>
6161
<li class="toctree-l2"><a class="reference internal" href="weak_references.html#memory-management">Memory Management</a></li>
6262
<li class="toctree-l2"><a class="reference internal" href="weak_references.html#circular-references">Circular References</a></li>
63+
<li class="toctree-l2"><a class="reference internal" href="weak_references.html#tools">Tools</a></li>
6364
<li class="toctree-l2"><a class="reference internal" href="weak_references.html#exercise">Exercise</a></li>
6465
</ul>
6566
</li>

search.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
<li class="toctree-l1"><a class="reference internal" href="weak_references.html">Python Memory Management and Weak References</a><ul>
6060
<li class="toctree-l2"><a class="reference internal" href="weak_references.html#memory-management">Memory Management</a></li>
6161
<li class="toctree-l2"><a class="reference internal" href="weak_references.html#circular-references">Circular References</a></li>
62+
<li class="toctree-l2"><a class="reference internal" href="weak_references.html#tools">Tools</a></li>
6263
<li class="toctree-l2"><a class="reference internal" href="weak_references.html#exercise">Exercise</a></li>
6364
</ul>
6465
</li>

searchindex.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)