Việc cấp phát và thu hồi bộ nhớ trong Python được thực hiện tự động. Người dùng sẽ không cần thiết phải tự mình cấp phát hay thu hồi bộ nhớ bằng cách sử dụng các câu lệnh xử lý bộ nhớ động, nhưng trong các ngôn ngữ chẳng hạn như C hay C++.

Python sử dụng hai chiến lược cấp phát bộ nhớ:

– Reference counting (đếm số tham chiếu)

– Garbage collection (trình thu gom bộ nhớ)

Trước phiên bản Python 2.0, trình thông dịch của Python (Python interpreter) chỉ sử dụng reference counting để quản lý bộ nhớ. Reference counting làm việc bằng cách đếm số lần mà một đối tượng được tham chiếu tới bởi các đối tượng khác trong hệ thống. Khi các tham chiếu đến một đối tượng không còn tồn tại nữa, reference count (số đếm tham chiếu) của đối tượng đó sẽ được giảm đi. Khi reference count giảm về 0, đối tượng sẽ được giải phóng, tức là phần bộ nhớ đã cấp phát cho đối tượng đó sẽ được thu hồi lại.


# Literal 9 is an object 
b = 9
  
# Reference count of object 9  
# becomes 0. 
b = 4

Trong ví dụ trên, giá trị 9 chính là một đối tượng. Reference count của đối tượng 9 

này sẽ tăng lên một đơn vị thành 1 ở dòng 1. Trong dòng 2, reference count của đối tượng 9 này sẽ trở thành 0, bởi vì tại đây, nó đã bị giảm đi. Do đó, garbage collector (trình thu gom bộ nhớ) sẽ giải phóng đối tượng này.

Một chu trình tham chiếu – reference cycle sẽ xuất hiện khi tồn tại một hoặc nhiều đối tượng tự tham chiếu đến chính bản thân nó hoặc tham chiếu tới nhau. Reference cycle thường xuất hiện khi sử dụng list, tuple, instance (các thể hiện của class), class, dictionary, và các function (hàm). Cách dễ nhất để tạo ra một reference cycle là tạo ra một đối tượng mà đối tượng này sẽ tham chiếu đến chính bản thân nó, như trong ví dụ dưới đây:


# -----------------------------------------------------------
#Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
#@author cafedevn
#Contact: cafedevn@gmail.com
#Fanpage: https://www.facebook.com/cafedevn
#Group: https://www.facebook.com/groups/cafedev.vn/
#Instagram: https://instagram.com/cafedevn
#Twitter: https://twitter.com/CafedeVn
#Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
#Pinterest: https://www.pinterest.com/cafedevvn/
#YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
# -----------------------------------------------------------


def create_cycle(): 
  
    # create a list x 
    x = [ ] 
  
    # A reference cycle is created 
    # here as x contains reference to 
    # to self. 
    x.append(x) 
   
create_cycle() 

Bởi vì hàm create_cycle() sẽ tạo ra một đối tượng x tham chiếu tới chính bản thân nó, nên đối tượng x sẽ không được tự động giải phóng khi hàm này trả về (returns). Điều này sẽ khiến cho phần bộ nhớ mà đối tượng x đang sử dụng bị giữ lại cho đến khi garbage collector của Python được gọi.

1. Các cách để tạo ra một đối tượng mà garbage collector có thể thu gom


x = [] 
x.append(l) 
x.append(2) 
  
# delete the list from memory or  
# assigning object x to None(Null) 
del x  
# x = None 

Giá trị reference count cho list x lúc này là 2. Tuy nhiên, bởi vì nó không thể được truy cập đến từ bên trong Python và không thể được sử dụng lại, nên nó được coi là garbage (rác). Trong phiên bản hiện tại của Python, list x sẽ không bao giờ được giải phóng.

2. Việc tự động thu hồi bộ nhớ đối với các chu trình tham chiếu

Bởi vì việc tìm ra các các chu trình tham chiếu (reference cycles) cần đến nhiều công việc tính toán, nên garbage collection – việc thu gom bộ nhớ cần phải là một hoạt động được lập lịch. Python sẽ lập trinh cho việc garbage collection – việc thu gom bộ nhớ dựa trên một số ngưỡng, liên quan đến số lượng các hành động cấp phát đối tượng, và số lượng các hành động giải phóng đối tượng. Khi số lượng các hành động cấp phát trừ đi số lượng các hành động giải phóng ra kết quả mà lớn hơn số ngưỡng, garbage collector – trình thu gom bộ nhớ sẽ hoạt động. Có thể kiểm tra số ngưỡng dành cho các đối tượng mới (đối tượng trong Python còn được gọi là các đối tượng thế hệ 0) bằng cách import module gc và truy cập đến các ngưỡng thu hồi bộ nhớ (garbage collection thresholds).

# loading gc 
import gc 
  
# get the current collection  
# thresholds as a tuple 
print("Garbage collection thresholds:", 
                    gc.get_threshold()) 

Kết quả in ra là:

Garbage collection thresholds: (700, 10, 10) 

Ở ví dụ này, ngưỡng mặc định của hệ thống là 700. Điều này có nghĩa là khi số lượng các hành động cấp phát bộ nhớ trừ đi số lượng các hành động giải phóng bộ nhớ mà lớn hơn 700, thì garbage collector tự động sẽ chạy. Do đó, bất kỳ phần nào trong code của bạn mà giải phóng nhiều khối bộ nhớ lớn, thì đều là những ví dụ tốt về việc thực hiện thu hồi bộ nhớ thủ công.

3. Thu hồi bộ nhớ thủ công

Việc gọi garbage collector một cách thủ công trong quá trình thực thi chương trình có thể là một ý tưởng về cách xử lý vấn đề bộ nhớ bị tiêu tốn bởi các chu trình tham chiếu – reference cycles. Việc thu gom bộ nhớ có thể được gọi một cách thủ công như sau:

# -----------------------------------------------------------
#Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
#@author cafedevn
#Contact: cafedevn@gmail.com
#Fanpage: https://www.facebook.com/cafedevn
#Group: https://www.facebook.com/groups/cafedev.vn/
#Instagram: https://instagram.com/cafedevn
#Twitter: https://twitter.com/CafedeVn
#Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
#Pinterest: https://www.pinterest.com/cafedevvn/
#YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
# -----------------------------------------------------------


# Importing gc module 
import gc 
  
# Returns the number of 
# objects it has collected 
# and deallocated 
collected = gc.collect() 
  
# Prints Garbage collector  
# as 0 object 
print("Garbage collector: collected", 
          "%d objects." % collected) 

Trong trường hợp chỉ có một vài chu kỳ được tạo ra, việc thu gom bộ nhớ thủ công sẽ hoạt động như sau:


import gc 
i = 0 
  
# create a cycle and on each iteration x as a dictionary 
# assigned to 1 
def create_cycle(): 
    x = { } 
    x[i+1] = x 
    print x 
  
# lists are cleared whenever a full collection or  
# collection of the highest generation (2) is run 
collected = gc.collect() # or gc.collect(2) 
print "Garbage collector: collected %d objects." % (collected) 
  
print "Creating cycles..."
for i in range(10): 
    create_cycle() 
  
collected = gc.collect() 
  
print "Garbage collector: collected %d objects." % (collected) 

Kết quả in ra là:

Garbage collector: collected 0 objects.
Creating cycles...
{1: {...}}
{2: {...}}
{3: {...}}
{4: {...}}
{5: {...}}
{6: {...}}
{7: {...}}
{8: {...}}
{9: {...}}
{10: {...}}
Garbage collector: collected 10 objects.

Có hai cách để thực hiện thu gom bộ nhớ thủ công: Thu gom bộ nhớ dựa trên thời gian (time-based garbage collection) và thu gom bộ nhớ dựa trên sự kiện (event-based garbage collection). Thu gom bộ nhớ dựa trên thời gian rất đơn giản: Garbage collector – trình thu gom bộ nhớ sẽ được gọi sau một khoảng thời gian cố định. Còn thu gom bộ nhớ dựa trên sự kiện sẽ gọi garbage collector khi xảy ra sự kiện. Ví dụ, khi một người dùng thoát khỏi ứng dụng, hoặc khi ứng dụng chuyển sang trạng thái không hoạt động (idle state).

Nguồn và Tài liệu tiếng anh tham khảo:

Tài liệu từ cafedev:

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!