Je commence à étudier la gestion de la mémoire en Python et je me demande comment le garbage collector intervient concrètement. Au-delà du comptage de références, quels sont les critères qui déclenchent le ramassage des objets cycliques ? Quelle est la différence entre les générations et comment cela influence la fréquence du nettoyage ? Des explications simples ou des analogies seraient les bienvenues, vous avez des conseils ?
Comment le garbage collector de Python gère‑t‑il la libération de mémoire ?
👁️ 81 görüntüleme💬 1 cevap❤️ 0 beğeni
1 Cevap
When I first started profiling a long‑running Flask service, I ran into the classic “memory keeps growing” surprise. The app was mostly pure Python objects, and reference‑counting was doing its job, but a handful of data‑structures formed cycles (think a linked list where each node also kept a back‑pointer to the list object). Those cycles never hit zero reference count, so they sat in memory forever—until the cyclic GC kicked in. In CPython the collector runs a separate pass that looks for objects that are only reachable through cycles; it does this by periodically walking the object graph and marking generations.
The trigger isn’t a fixed timer but a heuristic based on allocation activity. Every time a new container object (list, dict, set, etc.) is created, a counter for the current generation increments. When that counter exceeds a threshold (configurable via `gc.set_threshold`), the collector sweeps that generation. Objects that survive a sweep get promoted to the next older generation, which is checked less frequently. So generation 0 is the “young” pool—checked often, ideal for short‑lived objects. Generation 1 and 2 are “older” pools, inspected only when the lower‑generation thresholds are repeatedly exceeded. This tiered approach keeps the overhead low for most objects while still eventually catching long‑living cycles.
In practice, I tweaked the thresholds after noticing that my service’s memory plateaued at about 200 MB and then spiked slowly. By lowering the generation‑0 threshold a bit, the collector ran more often and reclaimed those hidden cycles before they became a problem. If you suspect cycles, you can always inspect `gc.get_objects()` and `gc.get_stats()` to see how many objects are in each generation and how many collections have occurred. Turning on `gc.set_debug(gc.DEBUG_LEAK)` helped me spot the exact objects that were stuck in a cycle, which made it easy to refactor the code to break the reference loop or use weak references where appropriate.