Data-block users and garbage collection¶
Blender uses a system based on reference-counting to decide when data-blocks have become unused and can get purged. In the short video below we show some of the details of this scheme:
The video shows the Orphan Data
outliner mode, but there are several modes that can be used to get detailed insight into the current state of Blender internals:
- The
Blender File
mode gives a high-level overview of a file's contents, including some of the more implicit data block types, such as Workspaces.
- The
Data API
mode provides an even more detailed view. It is actually a great way to inspect all the gory details of Blender's internal data structures. It will show all data-blocks by type and their attributes. Some attributes can be even be edited in this outliner mode.
- The
Orphan Data
mode shows data blocks that do not have any users and which will not be saved (unless they are marked to have a fake user). Some of the data-blocks you see here might not have been created by you, but are used by Blender internally, for example the Brushes.
Although the video only focused on materials, the way data-block lifetime is managed using the user counts is general to all types of data-blocks in Blender. But there are subtle differences in whether a data-block is really deleted or just has a link to it removed:
- Whenever the term "unlink" is used it means that a link to that data-block is removed and its user count decreased, but the data-block itself will still be in memory. An example of this is clicking the
✕
next to a mesh's material in the Material Properties.
- If the UI uses the term "delete" it means the data-block is deleted immediately from memory. Any data-blocks linked from the deleted data-block will have their user count decreased. An example of this is deleting a Camera object in the 3D view: the Camera object's data-block is deleted from memory, but the Camera object data data-block (containing the actual camera settings) is still in memory, which you can check in the Orphan Data mode of the outliner.
Python API support¶
The usage count of data-blocks can also be queried from Python:
# Two cube meshes using the same material
>>> bpy.context.scene.objects.values()
[bpy.data.objects['Cube'], bpy.data.objects['Cube.001']]
>>> bpy.data.materials['Material'].users
2
# Add a new material, set one of the cubes to use it
>>> bpy.data.materials['Material'].users
1
>>> bpy.data.materials['Material.001'].users
1
# <Delete Cube.001 object in the UI>
# Hmmm, still has a user?
>>> bpy.data.materials['Material.001'].users
1
# The reason is we deleted the Cube.001 *object*, but
# the Cube.001 *mesh* is still alive (as its usage count
# was merely decremented) and it still references the material
>>> bpy.data.objects['Cube.001']
Traceback (most recent call last):
File "<blender_console>", line 1, in <module>
KeyError: 'bpy_prop_collection[key]: key "Cube.001" not found'
>>> bpy.data.meshes['Cube.001']
bpy.data.meshes['Cube.001']
>>> bpy.data.meshes['Cube.001'].users
0
>>> bpy.data.meshes['Cube.001'].materials.values()
[bpy.data.materials['Material']]
The use_fake_user
attribute of a data block controls whether a Fake user is set, similar to the checkbox in the UI.
Really deleting objects through Python
In most cases you probably don't want to manually delete data blocks from a file and only use the normal UI operations for that. But it is possible for cases that need it. Truly purging a data block from Python can be done with the relevant remove()
method, e.g.
>>> bpy.context.scene.objects.values()
[bpy.data.objects['Cube']]
>>> o = bpy.context.active_object
>>> o
bpy.data.objects['Cube']
>>> m = o.data
>>> m
bpy.data.meshes['Cube']
# Remove the Mesh data-block from the file
>>> bpy.data.meshes.remove(m)
>>> bpy.data.meshes.values()
[]
>>> bpy.data.objects.values()
[]
Note that in the case of deleting object data (in this case a Mesh) any Objects referencing that object data also get removed!
A second thing to note is the above code does not actually update the current Blender file on disk. That only happens on an explicit save action (e.g. through the File
menu or using the relevant operator from Python).