Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Как работает сборщик мусора в CPython во всех м...
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
BestDoctor
December 18, 2019
Programming
71
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Как работает сборщик мусора в CPython во всех мучительных деталях
BestDoctor
December 18, 2019
More Decks by BestDoctor
See All by BestDoctor
Как работает наш сервис распознавания сущностей
bestdoctor
0
46
Data-driven motivation: как собраться и довести свой план до конца
bestdoctor
0
49
ZDD: как устроена разработка в BestDoctor
bestdoctor
0
270
Other Decks in Programming
See All in Programming
Web Framework Benchmark Battle 2026, Any%
a_matsuda
2
2k
Apple Intelligence を用いた個人情報誤送信防止、及びユーザーリクエスト体験の改善について
yukiny
0
350
HTML-in-Canvasで作る未来のWeb UI表現
clockmaker
5
5.1k
FactoryBotをやめればRSpecは速くなるのか?
yuta_onishi_97
0
290
RAG の “R” を Swift で覗いてみる 〜「意味から探す」検索の仕組み〜
nao_randd
0
160
Workers Cache を知る
syumai
0
360
コードレビューのボトルネックを"する側"と"される側"の両面から解消する
yub0n
2
1.6k
Railsのためのパスキー実践入門 〜マルチテナントSaaSでの意思決定〜
utakaha
2
550
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
500
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
15
8.5k
作れる、でも消せるか?──安全に「枯らして消す」機能削除術 / Drain, Then Delete
chobishiba
2
640
AWS CDKのカスタムリソースでContinuum(旧Security Agent)を実装した話 | Implementing Continuum (formerly Security Agent) via AWS CDK Custom Resource
akihisaikeda
2
150
Featured
See All Featured
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
330
30 Presentation Tips
portentint
PRO
1
430
Paper Plane (Part 1)
katiecoart
PRO
2
12k
A Modern Web Designer's Workflow
chriscoyier
699
190k
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
1
570
Building a Scalable Design System with Sketch
lauravandoore
464
34k
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
520
Art, The Web, and Tiny UX
lynnandtonic
304
22k
WENDY [Excerpt]
tessaabrams
14
40k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
530
Technical Leadership for Architectural Decision Making
baasie
3
600
Speed Design
sergeychernyshev
33
2.2k
Transcript
Как работает сборщик мусора в CPython во всех мучительных деталях
Злата Обуховская
char *src = malloc(len); char *dst = malloc(len); src =
scanf("%s", len); dst = memcpy(dst, src, len); ... free(src); free(dst); def foo(): src = input() dst = src
int PyByteArray_Resize(PyObject *self, Py_ssize_t requested_size) { ... sval = PyObject_Malloc(alloc);
memcpy(sval, PyByteArray_AS_STRING(self), Py_MIN((size_t)requested_size, (size_t)Py_SIZE(self))); PyObject_Free(obj->ob_bytes); obj->ob_bytes = obj->ob_start = sval;
_____ ______ ______ ________ [ int ] [ dict ]
[ list ] ... [ string ] Python core | +3 | <----- Object-specific memory -----> | <-- Non-object memory --> | _______________________________ | | [ Python's object allocator ] | | +2 | ####### Object memory ####### | <------ Internal buffers ------> | ______________________________________________________________ | [ Python's raw memory allocator (PyMem_ API) ] | +1 | <----- Python memory (under PyMem manager's control) ------> | | __________________________________________________________________ [ Underlying general-purpose allocator (ex: C library malloc) ] 0 | <------ Virtual memory allocated for the python process -------> |
_____ ______ ______ ________ [ int ] [ dict ]
[ list ] ... [ string ] Python core | +3 | <----- Object-specific memory -----> | <-- Non-object memory --> | _______________________________ | | [ Python's object allocator ] | | +2 | ####### Object memory ####### | <------ Internal buffers ------> | ______________________________________________________________ | [ Python's raw memory allocator (PyMem_ API) ] | +1 | <----- Python memory (under PyMem manager's control) ------> | | __________________________________________________________________ [ Underlying general-purpose allocator (ex: C library malloc) ] 0 | <------ Virtual memory allocated for the python process -------> |
Зачем используют аллокаторы?
Гитхаб сына маминой подруги
static void * _PyObject_Malloc(void *ctx, size_t nbytes) { void* ptr;
if (pymalloc_alloc(ctx, &ptr, nbytes)) { _Py_AllocatedBlocks++; return ptr; } ptr = PyMem_RawMalloc(nbytes); if (ptr != NULL) { _Py_AllocatedBlocks++; } return ptr; }
static int pymalloc_alloc(void *ctx, void **ptr_p, size_t nbytes) { ...
if (nbytes > SMALL_REQUEST_THRESHOLD) { return 0; } ... ...
Внутри питона очень много маленьких объектов
None
* Request in bytes Size of allocated block Size class
idx * ---------------------------------------------------------------- * 1-8 8 0 * 9-16 16 1 * 17-24 24 2 * 25-32 32 3 * 33-40 40 4 * 41-48 48 5 * 49-56 56 6 * 57-64 64 7 * 65-72 72 8 * ... ... ... * 497-504 504 62 * 505-512 512 63
None
None
Освобождение памяти
typedef struct _object { _PyObject_HEAD_EXTRA Py_ssize_t ob_refcnt; struct _typeobject *ob_type;
} PyObject;
Когда увеличивается счетчик ссылок?
TARGET(LOAD_CONST) { PyObject *value = GETITEM(consts, oparg); Py_INCREF(value); PUSH(value); FAST_DISPATCH();
} TARGET(POP_TOP) { PyObject *value = POP(); Py_DECREF(value); FAST_DISPATCH(); }
#define Py_DECREF(op) do { PyObject *_py_decref_tmp = (PyObject *)(op); if
(_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA --(_py_decref_tmp)->ob_refcnt != 0) _Py_CHECK_REFCNT(_py_decref_tmp) else _Py_Dealloc(_py_decref_tmp); } while (0)
Py_DECREF не приведет к удалению lst = [] lst.append(lst) del
lst
Проблема: ссылки бывают циклическими
None
Выявление циклов — дорогая операция
Mark-and-sweep
None
Проблема всех GC на свете: Stop the world
В CPython используется generational GC
PyObject * _PyObject_GC_Alloc(int use_calloc, size_t basicsize) { ... size =
sizeof(PyGC_Head) + basicsize; g = (PyGC_Head *)PyObject_Malloc(size); … state->generations[0].count++; /* number of allocated GC objects */ if (state->generations[0].count > state->generations[0].threshold { ... collect_generations(state); ... } ... op = FROM_GC(g); return op; }
typedef union _gc_head { struct { union _gc_head *gc_next; union
_gc_head *gc_prev; Py_ssize_t gc_refs; } gc; double dummy; /* force worst-case alignment */ } PyGC_Head;
PyObject * _PyObject_GC_Alloc(int use_calloc, size_t basicsize) { size = sizeof(PyGC_Head)
+ basicsize; g = (PyGC_Head *)PyObject_Malloc(size); … struct _gc_runtime_state *state = &_PyRuntime.gc; state->generations[0].count++; /* number of allocated GC objects */ if (state->generations[0].count > state->generations[0].threshold { ... collect_generations(state); ... } ... op = FROM_GC(g); return op; }
Порог: 700 объектов
static Py_ssize_t collect(struct _gc_runtime_state *state, int generation, Py_ssize_t *n_collected, Py_ssize_t
*n_uncollectable, int nofail) { … untrack_tuples(young); /* Move reachable objects to next generation. */ if (young != old) { if (generation == NUM_GENERATIONS - 2) { state->long_lived_pending += gc_list_size(young); } gc_list_merge(young, old); } else { /* We only untrack dicts in full collections, to avoid quadratic dict build-up. See issue #14775. */ untrack_dicts(young); state->long_lived_pending = 0;
Порог: 700 объектов Порог: 10 объектов
Порог: 700 объектов Порог: 10 объектов Порог: 10 объектов
При использовании сильно вложенных объектов память все равно будет фрагментироваться
Сборка мусора оптимизирована под работу с коротко живущими объектами
Future GC 3.9 Multiple interpreters, PEP-554 https://www.python.org/dev/peps/pep-0554/
void PyObject_GC_Del(void *op) { PyGC_Head *g = AS_GC(op); if (IS_TRACKED(op))
gc_list_remove(g); if (_PyRuntime.gc.generations[0].count > 0) _PyRuntime.gc.generations[0].count--; } PyObject_FREE(g); }