0
# Object Discovery and Access
1
2
Functions for finding specific objects by type or memory address, and identifying objects that may indicate memory management issues. These tools help locate objects for further analysis and investigation.
3
4
## Capabilities
5
6
### Type-Based Object Discovery
7
8
Find all objects of a specific type tracked by the garbage collector.
9
10
```python { .api }
11
def by_type(typename, objects=None):
12
"""
13
Return objects tracked by the garbage collector with a given class name.
14
15
Args:
16
typename (str): Class name, can be short ('MyClass') or fully qualified ('mymodule.MyClass')
17
objects (list, optional): Custom object collection to search instead of gc.get_objects()
18
19
Returns:
20
list: List of objects matching the type name
21
22
Note:
23
The garbage collector does not track simple objects like int or str.
24
"""
25
```
26
27
Usage examples:
28
29
```python
30
import objgraph
31
32
# Find all list objects
33
all_lists = objgraph.by_type('list')
34
print(f"Found {len(all_lists)} list objects")
35
36
# Find custom class instances
37
my_objects = objgraph.by_type('MyClass')
38
for obj in my_objects:
39
print(f"MyClass instance: {obj}")
40
41
# Use fully qualified names to be specific
42
specific_objects = objgraph.by_type('mymodule.MyClass')
43
44
# Search within a specific collection
45
leaking_objects = objgraph.get_leaking_objects()
46
leaking_dicts = objgraph.by_type('dict', leaking_objects)
47
48
# Combine with other analysis
49
large_lists = [lst for lst in objgraph.by_type('list') if len(lst) > 1000]
50
print(f"Found {len(large_lists)} large lists")
51
```
52
53
### Address-Based Object Access
54
55
Retrieve objects using their memory addresses, useful for tracking specific object instances.
56
57
```python { .api }
58
def at(addr):
59
"""
60
Return an object at a given memory address.
61
62
Args:
63
addr (int): Memory address from id(obj)
64
65
Returns:
66
object or None: Object at the address, or None if not found
67
68
Note:
69
Only works on objects tracked by the garbage collector.
70
The reverse of id(obj): at(id(obj)) is obj should be True.
71
"""
72
```
73
74
Usage examples:
75
76
```python
77
import objgraph
78
79
# Basic address lookup
80
my_list = [1, 2, 3]
81
address = id(my_list)
82
found_obj = objgraph.at(address)
83
print(f"Found same object: {found_obj is my_list}")
84
85
# Use with object tracking
86
objects_to_track = []
87
for i in range(10):
88
new_dict = {'index': i}
89
objects_to_track.append(id(new_dict))
90
91
# Later retrieve the objects
92
for addr in objects_to_track:
93
obj = objgraph.at(addr)
94
if obj is not None:
95
print(f"Object at {addr}: {obj}")
96
else:
97
print(f"Object at {addr} was garbage collected")
98
```
99
100
### Bulk Address Resolution
101
102
Retrieve multiple objects from a set of memory addresses efficiently.
103
104
```python { .api }
105
def at_addrs(address_set):
106
"""
107
Return a list of objects for a given set of memory addresses.
108
109
Args:
110
address_set (set): Set of memory addresses from id() calls
111
112
Returns:
113
list: List of objects found at those addresses, in arbitrary order
114
115
Note:
116
Objects are returned in arbitrary order.
117
Only works on objects tracked by the garbage collector.
118
"""
119
```
120
121
Usage examples:
122
123
```python
124
import objgraph
125
126
# Use with get_new_ids for object tracking
127
objgraph.get_new_ids(limit=0) # Establish baseline
128
129
# Create some objects
130
a = [1, 2, 3]
131
b = [4, 5, 6]
132
c = {'key': 'value'}
133
134
# Get IDs of newly created objects
135
new_ids = objgraph.get_new_ids(limit=0)
136
137
# Retrieve the actual list objects
138
new_lists = objgraph.at_addrs(new_ids.get('list', set()))
139
print(f"Created {len(new_lists)} new lists")
140
print(f"Our list 'a' is in new lists: {a in new_lists}")
141
print(f"Our list 'b' is in new lists: {b in new_lists}")
142
143
# Combine with filtering
144
large_new_lists = [lst for lst in new_lists if len(lst) > 5]
145
146
# Save addresses for later analysis
147
important_object_ids = {id(obj) for obj in some_important_objects}
148
# ... later ...
149
still_alive = objgraph.at_addrs(important_object_ids)
150
print(f"{len(still_alive)} of {len(important_object_ids)} objects still exist")
151
```
152
153
### Leak Detection
154
155
Identify objects that have no referrers, which may indicate reference-counting bugs or orphaned objects.
156
157
```python { .api }
158
def get_leaking_objects(objects=None):
159
"""
160
Return objects that do not have any referrers.
161
162
Args:
163
objects (list, optional): Custom object collection to analyze instead of gc.get_objects()
164
165
Returns:
166
list: List of objects without referrers
167
168
Note:
169
These could indicate reference-counting bugs in C code or be legitimate.
170
The garbage collector does not track simple objects like int or str.
171
Calls gc.collect() automatically.
172
"""
173
```
174
175
Usage examples:
176
177
```python
178
import objgraph
179
180
# Find potentially leaking objects
181
leaking = objgraph.get_leaking_objects()
182
print(f"Found {len(leaking)} objects without referrers")
183
184
# Analyze leaking objects by type
185
if leaking:
186
leak_stats = objgraph.typestats(leaking)
187
print("Leaking object types:")
188
for obj_type, count in sorted(leak_stats.items(), key=lambda x: x[1], reverse=True):
189
print(f" {obj_type}: {count}")
190
191
# Investigate specific leaking objects
192
if leaking:
193
for obj in leaking[:5]: # Look at first 5
194
print(f"Leaking {type(obj).__name__}: {repr(obj)[:100]}")
195
196
# Try to find what might be keeping references
197
# (though by definition, these objects have no referrers)
198
objgraph.show_backrefs([obj], max_depth=2)
199
200
# Filter leaking objects
201
large_leaking = [obj for obj in leaking
202
if hasattr(obj, '__len__') and len(obj) > 100]
203
204
# Combine with custom object collection
205
my_objects = objgraph.by_type('MyClass')
206
leaking_my_objects = objgraph.get_leaking_objects(my_objects)
207
if leaking_my_objects:
208
print(f"Found {len(leaking_my_objects)} leaking MyClass instances")
209
```
210
211
## Common Workflows
212
213
### Object Lifecycle Tracking
214
215
```python
216
import objgraph
217
import weakref
218
219
class TrackedObject:
220
def __init__(self, name):
221
self.name = name
222
223
def __repr__(self):
224
return f"TrackedObject({self.name})"
225
226
# Create some objects to track
227
objects = [TrackedObject(f"obj_{i}") for i in range(5)]
228
object_ids = {id(obj) for obj in objects}
229
230
print(f"Created {len(objects)} objects")
231
232
# Clear some references
233
del objects[2:4] # Remove references to obj_2 and obj_3
234
235
# Check which objects still exist
236
surviving_objects = objgraph.at_addrs(object_ids)
237
print(f"{len(surviving_objects)} objects survived")
238
239
# Force garbage collection and check again
240
import gc
241
gc.collect()
242
surviving_after_gc = objgraph.at_addrs(object_ids)
243
print(f"{len(surviving_after_gc)} objects survived after GC")
244
```
245
246
### Memory Leak Investigation
247
248
```python
249
import objgraph
250
251
# Identify potential problem objects
252
leaking = objgraph.get_leaking_objects()
253
if leaking:
254
# Group by type
255
leak_by_type = {}
256
for obj in leaking:
257
obj_type = type(obj).__name__
258
leak_by_type.setdefault(obj_type, []).append(obj)
259
260
# Focus on most numerous type
261
most_common_type = max(leak_by_type.keys(), key=lambda t: len(leak_by_type[t]))
262
problem_objects = leak_by_type[most_common_type]
263
264
print(f"Most leaking type: {most_common_type} ({len(problem_objects)} instances)")
265
266
# Analyze a few examples
267
for obj in problem_objects[:3]:
268
print(f"Investigating: {repr(obj)[:100]}")
269
# Even though these have no referrers, show the object's contents
270
objgraph.show_refs([obj], max_depth=2)
271
```