<jupyter_start><jupyter_text>Garbage Collection<jupyter_code>import ctypes
import gc<jupyter_output><empty_output><jupyter_text>We use the same function that we used in the lesson on reference counting to calculate the number of references to a specified object (using its memory address to avoid creating an extra reference)<jupyter_code>def ref_count(address):
    return ctypes.c_long.from_address(address).value<jupyter_output><empty_output><jupyter_text>We create a function that will search the objects in the GC for a specified id and tell us if the object was found or not:<jupyter_code>def object_by_id(object_id):
    for obj in gc.get_objects():
        if id(obj) == object_id:
            return "Object exists"
    return "Not found"<jupyter_output><empty_output><jupyter_text>Next we define two classes that we will use to create a circular referenceClass A's constructor will create an instance of class B and pass itself to class B's constructor that will then store that reference in some instance variable.<jupyter_code>class A:
    def __init__(self):
        self.b = B(self)
        print('A: self: {0}, b:{1}'.format(hex(id(self)), hex(id(self.b))))
class B:
    def __init__(self, a):
        self.a = a
        print('B: self: {0}, a: {1}'.format(hex(id(self)), hex(id(self.a))))<jupyter_output><empty_output><jupyter_text>We turn off the GC so we can see how reference counts are affected when the GC does not run and when it does (by running it manually).<jupyter_code>gc.disable()<jupyter_output><empty_output><jupyter_text>Now we create an instance of A, which will, in turn, create an instance of B which will store a reference to the calling A instance.<jupyter_code>my_var = A()<jupyter_output>B: self: 0x1a5a075cdf0, a: 0x1a5a075cf70
A: self: 0x1a5a075cf70, b:0x1a5a075cdf0<jupyter_text>As we can see A and B's constructors ran, and we also see from the memory addresses that we have a circular reference.In fact `my_var` is also a reference to the same A instance:<jupyter_code>print(hex(id(my_var)))<jupyter_output>0x1a5a075cf70<jupyter_text>Another way to see this:<jupyter_code>print('a: \t{0}'.format(hex(id(my_var))))
print('a.b: \t{0}'.format(hex(id(my_var.b))))
print('b.a: \t{0}'.format(hex(id(my_var.b.a))))
a_id = id(my_var)
b_id = id(my_var.b)<jupyter_output><empty_output><jupyter_text>We can see how many references we have for `a` and `b`:<jupyter_code>print('refcount(a) = {0}'.format(ref_count(a_id)))
print('refcount(b) = {0}'.format(ref_count(b_id)))
print('a: {0}'.format(object_by_id(a_id)))
print('b: {0}'.format(object_by_id(b_id)))<jupyter_output>refcount(a) = 2
refcount(b) = 1
a: Object exists
b: Object exists<jupyter_text>As we can see the A instance has two references (one from `my_var`, the other from the instance variable `b` in the B instance)The B instance has one reference (from the A instance variable `a`) Now, let's remove the reference to the A instance that is being held by `my_var`:<jupyter_code>my_var= None
print('refcount(a) = {0}'.format(ref_count(a_id)))
print('refcount(b) = {0}'.format(ref_count(b_id)))
print('a: {0}'.format(object_by_id(a_id)))
print('b: {0}'.format(object_by_id(b_id)))<jupyter_output>refcount(a) = 1
refcount(b) = 1
a: Object exists
b: Object exists<jupyter_text>As we can see, the reference counts are now both equal to 1 (a pure circular reference), and reference counting alone did not destroy the A and B instances - they're still around. If no garbage collection is performed this would result in a memory leak. Let's run the GC manually and re-check whether the objects still exist:<jupyter_code>gc.collect()
print('refcount(a) = {0}'.format(ref_count(a_id)))
print('refcount(b) = {0}'.format(ref_count(b_id)))
print('a: {0}'.format(object_by_id(a_id)))
print('b: {0}'.format(object_by_id(b_id)))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12405525149856658, 'fraction_numerical': 0.016158457127964557, 'mean_word_length': 6.107407407407408, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '88097', 'license': 'Apache-2.0', 'n_tokens_mistral': 1382, 'n_tokens_neox': 1348, 'n_words': 407, 'path': 'dd_1/Part 1/Section 03 - Variables and Memory/03 - Garbage Collection.ipynb', 'repo_name': 'rebekka-halal/bg', 'size': '3837'}