45

What is a good way to find all of the references to an object in python?

The reason I ask is that it looks like we have a "memory leak". We are uploading image files to the server from a web browser. Each time we do this, the memory usage on the server goes up proportionately to the size of the file that was just uploaded. This memory is never getting released by the python garbage collection, so I'm thinking that there are probably stray references pointing to the image data that are not getting deleted or going out of scope, even at the end of each request.

I figure it would be nice to be able to ask python: "What references are still pointing to this memory?" so that I can figure out what is keeping the garbage collection from freeing it.

Currently we are running Python and Django on a Heroku server.

2

3 Answers 3

91

Python's gc module has several useful functions, but it sounds like gc.get_referrers() is what you're looking for. Here's an example:

import gc


def foo():
    a = [2, 4, 6]
    b = [1, 4, 7]

    l = [a, b]
    d = dict(a=a)
    return l, d

l, d = foo()
r1 = gc.get_referrers(l[0])
r2 = gc.get_referrers(l[1])

print r1
print r2

When I run that, I see the following output:

[[[2, 4, 6], [1, 4, 7]], {'a': [2, 4, 6]}]
[[[2, 4, 6], [1, 4, 7]]]

You can see that the first line is l and d, and the second line is just l.

In my brief experiments, I've found that the results are not always this clean. Interned strings and tuples, for example, have more referrers than you would expect.

Sign up to request clarification or add additional context in comments.

2 Comments

This appears to be the simplest and best answer.
... and actually "an answer".
11

Python's standard library has gc module containing garbage collector API. One of the function you possible want to have is

gc.get_objects()

This function returns list of all objects currently tracked by garbage collector. The next step is to analyze it.

If you know the object you want to track you can use sys module's getrefcount function:

>>> x = object()
>>> sys.getrefcount(x)
2
>>> y = x
>>> sys.getrefcount(x)
3

3 Comments

sys.getrefcount() is useful, but is there any way of seeing what those references are?
In realpython.com/python-gil it reads: Python uses reference counting for memory management. It means that objects created in Python have a reference count variable that keeps track of the number of references that point to the object. When this count reaches zero, the memory occupied by the object is released. [...]brief code example to demonstrate how reference counting works: >>> import sys >>> a = [] >>> b = a >>> sys.getrefcount(a) 3 [look at the next comment]
[...continuation of the above comment] In the above example, the reference count for the empty list object [] was 3. The list object was referenced by a, b and the argument passed to sys.getrefcount().
3

If you're finding the output of gc.get_referrers not that helpful for diagnosing memory leaks, you can try using the referrers package, which I wrote.

This package helps to to answer the question "what is holding a reference to this object?" by trying to assign a meaningful name to each reference to an object, and returning a graph of references (including indirect references).

Here's how to use it, based on the example from another answer:

import referrers

def foo():
    a = [2, 4, 6]
    b = [1, 4, 7]

    l = [a, b]
    d = dict(a=a)
    print(referrers.get_referrer_graph(a))
    print(referrers.get_referrer_graph(b))

foo()

When this code is run, the output will be something like this:

╙── list instance (id=4521562304)
    ├─╼ list[0] (id=4521951040)
    │   └─╼ foo.l (local) (id=4521951040)
    ├─╼ dict[a] (id=4522090880)
    │   └─╼ foo.d (local) (id=4522090880)
    └─╼ foo.a (local) (id=4521562304)

╙── list instance (id=4521560448)
    ├─╼ list[1] (id=4521951040)
    │   └─╼ foo.l (local) (id=4521951040)
    └─╼ foo.b (local) (id=4521560448)

2 Comments

I came across this post when I was looking for the exact functionality, and I gave your package referrers a try. I just want to say this is working beautifully and has helped me in finding out a subtle memory leak in my code. Without your package I can't imagine how I could find it in a convenient way. Thank you and well done!
@bingung Thanks for your feedback. I really appreciate it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.