All about Memory Management in Python.

All about Memory Management in Python.

RAM and ROM, Process in OS. Garbage Collector. Allocate memory using a Dynamic array data structure.

Before we start, Maybe you have some questions in your mind.

Why do we need a memory Allocator?

What is the difference between RAM and ROM?

Why do we need more ram and a suitable PC configuration for playing Games?

Is there any difference between Python and C++ memory allocators?

What is a Python Garbage collector, heap and stack allocator?

So today we define what that stuff is!

Computer memory is just like the human brain. it is used to store data, information and instructions. and for processes that data, also we need memory. the process is simply a program under execution

You all played various games alright! game is the program, and when we want to play a game we start the game by just clicking one icon. easy! but in the background, it's run by process and the Operating system(OS) handles all the computer processes. so the process is a program under execution.

There are three types of memory -

Primary memory.

Secondary Memory.

Cache memory.

primary Memory is also known as the main memory of the computer system. It is used to store data and programs or instructions during computer operations. It is also called semiconductor memory.

RAM(Random Access Memory) - It is also volatile Memory. Volatile Memory stores information based on your power supply.means if you turn off your device all memory will be lost. RAM Temporarily stores Programs, and data which has to be executed by the processor.

ROM(Read Only Memory) - It is Non-Volatile memory. Non-volatile memory stores information even power supply is off

The difference is RAM and ROM. RAM Store is Temporary memory and ROM stores Permeant memory

Cache Memory - It is High-speed semiconductor memory that can help the CPU run faster. Between the CPU and the main memory. It is faster than the main memory and takes less time to access. but it's expensive so it's usually small compared to other memory.

How Does RAM Play Into Gaming?

RAM is important because your system can access data in it more quickly than it can retrieve info from your main storage disk. You have the entire game's data stored on your hard drive or solid-state drive, but constantly pulling it from it is inefficient. Thus, your computer moves the game information it will need to RAM to quickly load it.

With low RAM, your computer won't be able to store all the game info it needs to run properly, leading to choppy frame rates and poor performance. An extreme lack of RAM could even prevent the game from working.

Python Memory Management -

Python is the Master language, we can do everything in Python nothing is impossible!

Python provides you with automatic memory Management. Python has a Garbage collector.

another question what is heap memory?

There is a Heap Data structure in Python but Heap memory is different like there is a difference in Java and javascript.

It is called a heap because it is a pile of memory space available to programmers to allocate and de-allocate. and memory allocates in the stack. so there are two allocations of memory stack and heap. Right?

What is a Python garbage collector?

Garbage collection is a process in which the interpreter frees up the memory when not in use to make it available for other objects. Assume a case You created an object. but you don't use that object so it is not in use, Right? so the virtual machine has a garbage collector that automatically deletes that object from the heap memory.

Python’s memory allocation and deallocation method is automatic. The user does not have to preallocate or deallocate memory similar to using dynamic memory allocation in languages such as C or C++.

That is a difference between C++ and Python memory management.

Simple Data Structure for Memory in Python.

You all know about Array Data structure. simply array is a collection of items of the same data type stored at a contiguous memory location. Contiguous means an object that is adjacent to another object.

the array is a Contiguous chunk of Random access memory in a computer. and we can access individual cells of the array as a[n] where n is the size of the array. reading and writing to memory element takes O(1) time means constant time.

Out today's task begins.

Reading and writing to a particular index j where 1 <= j <= n.

Adding a new element at the end of the array. the size of the array will become n+ 1.

when a process needs memory and how os allocates memory to the process to run the program?

The program can request a contiguous chunk of K memory cells using an allocation function. and as you know This function setup is different in various programming languages.

a= [0] * K

but the great thing is Python lists are already a dynamic array. so it is implemented in the same manner that we are going to describe here.

so curious readers may ask about deallocation or freeing memory. programming languages like C and C++ which required telling the operating system to that particular chunk of memory that was previously allocated is no longer needed. but in Python, there is a Garbage collector so runtime manages memory and decides that a chunk of memory is no longer needed.

#allowcate a new memory of size

def allowcateMemory(size):

assert size > =1:

return [0]*size

# copies the contents of the old list into new

def CopyInto(old,new):

assert len(old) <= len(new) , 'Not enough space to copy into'

m = len(old)

for i in range (m):

new[i] = old[i]

we will now implement the DynamicArray data structure as a Python class. It will have three fields.

Array: The overall memory that has been allocated.

allocated_size: how much is the allocated size?

size: what is the actual size of the array?

the allocated size is always larger than the actual size.

class DynamicArray: 

    def __init__(self, initial_size=16, initial_fill=0, debug=False):
        self.allocated_size = initial_size 
        self.size = 0
        self.array = [initial_fill] * initial_size
        self.debug = debug

    # This allows us to directly access d[idx]
    def __getitem__(self, idx):
        assert idx >= 0 and idx < self.size 
        return self.array[idx]

    # This allows us to write d[idx] = val 
    def __setitem__(self, idx, val):
        assert idx >= 0 and idx < self.size 
        self.array[idx] = val

    def append(self, x):
        # Do we have enough allocated size to just append x to the array?
        if self.size >= self.allocated_size:
            if self.debug: 
                print(f'Ran out of memory: old allocated size: {self.allocated_size}, new allocated size is {2*self.allocated_size}')
            # No, we have run out of pre-allocated memory
            # Double the size of the array 
            # Double the size of the allocated memory
            self.allocated_size = 2 * self.allocated_size
            old_array = self.array
            # allocate and copy.
            new_array = allocateMemory(self.allocated_size)
            copyInto(old_array, new_array)
            # update the array.
            self.array = new_array
        # Append the element to the end
        self.array[self.size] = x
        # Update its size.
        self.size = self.size + 1
l = DynamicArray(initial_size=1, initial_fill=0, debug=True)
for j in range(1000):
    l.append(j)
print(f'l[5] = {l[5]}')
l[0] = 30
print(f'l[0] = {l[0]}')

Ran out of memory: old allocated size: 1, new allocated size is 2 Ran out of memory: old allocated size: 2, new allocated size is 4 Ran out of memory: old allocated size: 4, new allocated size is 8 Ran out of memory: old allocated size: 8, newly allocated size is 16 Ran out of memory: old allocated size: 16, newly allocated size is 32 Ran out of memory: old allocated size: 32, newly allocated size is 64 Ran out of memory: old allocated size: 64, new allocated size is 128 Ran out of memory: old allocated size: 128, new allocated size is 256 Ran out of memory: old allocated size: 256, new allocated size is 512 Ran out of memory: old allocated size: 512, new allocated size is 1024 l[5] = 5 l[0] = 30

So this is how can we allocate memory in Python. If there is a mistake in the information please let me know. Thank you.

@wemakedevs #wemakedevs #Python