Cách các đối tượng được lưu trữ trong bộ nhớ trong Python

Quản lý bộ nhớ là quá trình quản lý hiệu quả bộ nhớ máy tính [RAM]. Nó liên quan đến việc phân bổ một phần bộ nhớ trong thời gian chạy cho chương trình khi chương trình yêu cầu nó và giải phóng bộ nhớ được phân bổ để sử dụng lại khi chương trình không còn cần đến nó nữa.

Trong các ngôn ngữ như C hay Rust, quản lý bộ nhớ là trách nhiệm của lập trình viên. Lập trình viên phải cấp phát bộ nhớ theo cách thủ công trước khi chương trình có thể sử dụng và giải phóng bộ nhớ khi chương trình không còn cần đến nó nữa. Trong Python, quản lý bộ nhớ là tự động. Python tự động xử lý việc phân bổ và giải phóng bộ nhớ

Trong bài viết này, chúng ta sẽ thảo luận về nội bộ của quản lý bộ nhớ trong Python. Chúng tôi cũng sẽ đề cập đến cách các đơn vị cơ bản, chẳng hạn như các đối tượng, được lưu trữ trong bộ nhớ, các loại bộ cấp phát bộ nhớ khác nhau trong Python và cách trình quản lý bộ nhớ của Python quản lý hiệu quả bộ nhớ

Hiểu nội dung quản lý bộ nhớ trong Python giúp thiết kế các ứng dụng hiệu quả về bộ nhớ. Nó cũng giúp gỡ lỗi các vấn đề về bộ nhớ trong một ứng dụng dễ dàng hơn

Mục lục

  • Python như một đặc tả ngôn ngữ
  • CPython là gì
    • Các hàm
      # Increment a by 1
      a = a + 1
      
      3 và
      # Increment a by 1
      a = a + 1
      
      4 trong C là gì?
    • Các đối tượng trong Python
    • Biến trong Python
  • Quản lý bộ nhớ trong CPython
    • Cấp phát bộ nhớ
    • Cấp phát đối tượng
    • khối
    • hồ bơi
    • đấu trường
  • Quá trình Python có giải phóng bộ nhớ không?
  • Thu gom rác bằng Python
    • Số tham chiếu
    • Thu gom rác trên cơ sở Số lượng tham chiếu
    • Tham chiếu theo chu kỳ trong Python
    • Thu gom rác thế hệ
    • Mô-đun
      # Increment a by 1
      a = a + 1
      
      5 trong Python

Hãy bắt đầu với việc hiểu Python như một đặc tả ngôn ngữ và sau đó đi sâu vào CPython

Python như một đặc tả ngôn ngữ

Ngôn ngữ lập trình là một tập hợp các quy tắc và thông số kỹ thuật, như được định nghĩa trong tài liệu tham khảo

Python là một ngôn ngữ lập trình và đây là tài liệu tham khảo dành cho Python nêu rõ các quy tắc và thông số kỹ thuật cho ngôn ngữ Python

Ví dụ, đặc tả ngôn ngữ Python nói rằng để xác định một hàm, chúng ta phải sử dụng từ khóa

# Increment a by 1
a = a + 1
6. Đây chỉ là một đặc điểm kỹ thuật và chúng tôi phải làm cho máy tính hiểu rằng bằng cách sử dụng
# Increment a by 1
a = a + 1
7, chúng tôi dự định xác định một hàm có tên
# Increment a by 1
a = a + 1
8. Chúng ta phải viết một chương trình thực hiện các quy tắc và thông số kỹ thuật này

Các quy tắc và thông số kỹ thuật của ngôn ngữ Python được triển khai bởi nhiều ngôn ngữ lập trình khác nhau, chẳng hạn như C, Java và C#. Việc triển khai ngôn ngữ Python trong C được gọi là CPython, trong khi việc triển khai ngôn ngữ Python trong Java và C# lần lượt được gọi là Jython và IronPython

CPython là gì?

CPython là triển khai mặc định và được sử dụng rộng rãi nhất của ngôn ngữ Python. Khi chúng tôi nói Python, về cơ bản, điều đó có nghĩa là chúng tôi đang đề cập đến CPython. Khi bạn tải xuống Python từ python. org, về cơ bản, bạn tải xuống mã CPython. Do đó, CPython là một chương trình được viết bằng ngôn ngữ C, thực hiện tất cả các quy tắc và thông số kỹ thuật được xác định bởi ngôn ngữ Python

CPython là triển khai tham chiếu của ngôn ngữ lập trình Python. CPython có thể được định nghĩa là cả trình thông dịch và trình biên dịch, vì nó biên dịch mã Python thành mã byte trước khi diễn giải nó. -Wikipedia

Vì CPython là triển khai tham chiếu nên tất cả các quy tắc và thông số kỹ thuật mới của ngôn ngữ Python đều được triển khai đầu tiên bởi CPython

Trong bài viết này, chúng ta sẽ thảo luận về nội bộ quản lý bộ nhớ của CPython

Xin lưu ý. Các triển khai khác, chẳng hạn như Jython và IronPython, có thể triển khai quản lý bộ nhớ theo một cách khác

Vì CPython được triển khai bằng ngôn ngữ lập trình C, trước tiên hãy hiểu hai chức năng quan trọng liên quan đến quản lý bộ nhớ trong C.

# Increment a by 1
a = a + 1
3 và
# Increment a by 1
a = a + 1
4

Hàm
# Increment a by 1
a = a + 1
3 và
# Increment a by 1
a = a + 1
4 trong C là gì?

Đầu tiên,

# Increment a by 1
a = a + 1
3 là một phương thức được sử dụng trong ngôn ngữ lập trình C để yêu cầu một khối bộ nhớ từ hệ điều hành trong thời gian chạy. Khi chương trình cần bộ nhớ trong thời gian chạy, chương trình sẽ gọi phương thức
# Increment a by 1
a = a + 1
3 để lấy bộ nhớ cần thiết

Thứ hai,

# Increment a by 1
a = a + 1
4 là một phương pháp được sử dụng trong ngôn ngữ lập trình C để giải phóng hoặc giải phóng bộ nhớ được phân bổ cho chương trình trở lại hệ điều hành khi chương trình không còn cần đến nó nữa.

Khi một chương trình Python [CPython] cần bộ nhớ, CPython gọi nội bộ phương thức

# Increment a by 1
a = a + 1
3 để phân bổ nó. Khi chương trình không còn cần bộ nhớ, CPython gọi phương thức
# Increment a by 1
a = a + 1
4 để giải phóng nó

Tiếp theo, hãy xem cách bộ nhớ được phân bổ cho các đối tượng khác nhau trong Python

Các đối tượng trong Python

Mọi thứ trong Python đều là đối tượng. Các lớp, hàm và thậm chí các kiểu dữ liệu đơn giản, chẳng hạn như số nguyên, số float và chuỗi, là các đối tượng trong Python. Khi chúng ta định nghĩa một số nguyên trong Python, bên trong CPython sẽ tạo một đối tượng có kiểu số nguyên. Các đối tượng này được lưu trữ trong bộ nhớ heap

Mỗi đối tượng Python bao gồm ba trường

  • Giá trị
  • Loại hình
  • số tham chiếu

Hãy xem xét một ví dụ đơn giản

# Increment a by 1
a = a + 1
5

Khi đoạn mã trên được thực thi, CPython tạo một đối tượng kiểu

# Increment a by 1
a = a + 1
68 và cấp phát bộ nhớ cho đối tượng này trên bộ nhớ heap

# Increment a by 1
a = a + 1
69 cho biết loại đối tượng trong CPython và trường
# Increment a by 1
a = a + 1
80, như tên cho thấy, lưu trữ giá trị của đối tượng [trong trường hợp này là ___181]. Chúng ta sẽ thảo luận về trường
# Increment a by 1
a = a + 1
82 sau trong bài viết

Biến trong Python

Các biến trong Python chỉ là tham chiếu đến đối tượng thực tế trong bộ nhớ. Chúng giống như tên hoặc nhãn trỏ đến đối tượng thực tế trong bộ nhớ. Họ không lưu trữ bất kỳ giá trị

Xem xét ví dụ sau

# Increment a by 1
a = a + 1
5

Như đã thảo luận trước đó, khi đoạn mã trên được thực thi, bên trong CPython sẽ tạo một đối tượng kiểu số nguyên. Biến

# Increment a by 1
a = a + 1
83 trỏ tới đối tượng số nguyên này như hình bên dưới

Chúng ta có thể truy cập đối tượng số nguyên trong chương trình Python bằng cách sử dụng biến

# Increment a by 1
a = a + 1
83

Hãy gán đối tượng số nguyên này cho một biến khác

# Increment a by 1
a = a + 1
85

# Increment a by 1
a = a + 1
5

Khi đoạn mã trên được thực thi, cả hai biến

# Increment a by 1
a = a + 1
83 và
# Increment a by 1
a = a + 1
85 đều trỏ đến cùng một đối tượng số nguyên, như hình bên dưới

Bây giờ hãy tăng giá trị của đối tượng số nguyên lên 1

# Increment a by 1
a = a + 1

Khi đoạn mã trên được thực thi, CPython tạo một đối tượng số nguyên mới với giá trị

# Increment a by 1
a = a + 1
88 và tạo biến
# Increment a by 1
a = a + 1
83 trỏ tới đối tượng số nguyên mới này. Biến
# Increment a by 1
a = a + 1
85 sẽ tiếp tục trỏ đến đối tượng số nguyên có giá trị
# Increment a by 1
a = a + 1
81, như hình bên dưới

Ở đây, chúng ta có thể thấy rằng thay vì ghi đè giá trị của

# Increment a by 1
a = a + 1
81 bằng
# Increment a by 1
a = a + 1
88, CPython tạo một đối tượng mới với giá trị
# Increment a by 1
a = a + 1
88 vì số nguyên trong Python là bất biến. Sau khi được tạo, chúng không thể được sửa đổi. Xin lưu ý rằng các kiểu dữ liệu chuỗi và float cũng không thay đổi trong Python

Hãy xem xét một chương trình Python đơn giản để giải thích thêm về khái niệm này

# Increment a by 1
a = a + 1
6

Đoạn mã trên xác định một vòng lặp

# Increment a by 1
a = a + 1
75 đơn giản làm tăng giá trị của biến
# Increment a by 1
a = a + 1
76 cho đến khi nó nhỏ hơn
# Increment a by 1
a = a + 1
81. Khi mã này được thực thi, với mỗi lần tăng của biến
# Increment a by 1
a = a + 1
76, CPython sẽ tạo một đối tượng số nguyên mới với giá trị tăng dần và đối tượng số nguyên cũ sẽ bị xóa [nói chính xác hơn, đối tượng này sẽ đủ điều kiện để xóa] khỏi bộ nhớ

CPython gọi phương thức

# Increment a by 1
a = a + 1
3 cho mỗi đối tượng mới để cấp phát bộ nhớ cho đối tượng đó. Nó gọi phương thức
# Increment a by 1
a = a + 1
4 để xóa đối tượng cũ khỏi bộ nhớ

Hãy chuyển đổi đoạn mã trên thành

# Increment a by 1
a = a + 1
3 và
# Increment a by 1
a = a + 1
4

# Increment a by 1
a = a + 1
8

Chúng ta có thể thấy rằng CPython tạo và xóa một số lượng lớn các đối tượng, ngay cả đối với chương trình đơn giản này. Nếu chúng ta gọi các phương thức

# Increment a by 1
a = a + 1
3 và
# Increment a by 1
a = a + 1
4 cho mỗi lần tạo và xóa đối tượng thì sẽ làm giảm hiệu suất thực thi của chương trình và làm cho chương trình chạy chậm

Do đó, CPython giới thiệu các kỹ thuật khác nhau để giảm số lần chúng ta phải gọi

# Increment a by 1
a = a + 1
3 và
# Increment a by 1
a = a + 1
4 cho mỗi lần tạo và xóa đối tượng nhỏ. Bây giờ hãy hiểu cách Python quản lý bộ nhớ

Quản lý bộ nhớ trong CPython

Quản lý bộ nhớ trong Python liên quan đến việc quản lý một đống riêng tư. Heap riêng là một phần bộ nhớ dành riêng cho quy trình Python. Tất cả các đối tượng Python và cấu trúc dữ liệu được lưu trữ trong heap riêng

Hệ điều hành không thể phân bổ phần bộ nhớ này cho một tiến trình khác. Kích thước của private heap có thể tăng và giảm dựa trên yêu cầu bộ nhớ của quy trình Python. Heap riêng được quản lý bởi trình quản lý bộ nhớ Python được xác định bên trong mã CPython

Đối với mục đích đại diện, heap riêng trong CPython có thể được chia thành nhiều phần như hình bên dưới

Xin lưu ý rằng ranh giới của từng phần này không cố định và có thể tăng hoặc giảm tùy theo yêu cầu

  1. Bộ nhớ phi đối tượng lõi Python. Phần bộ nhớ được phân bổ cho dữ liệu phi đối tượng lõi python
  2. Bộ đệm nội bộ. Phần bộ nhớ được phân bổ cho bộ đệm bên trong
  3. Bộ nhớ dành riêng cho đối tượng - Phần bộ nhớ được phân bổ cho các đối tượng có bộ cấp phát bộ nhớ dành riêng cho đối tượng
  4. Bộ nhớ đối tượng. Phần bộ nhớ được phân bổ cho các đối tượng

Khi chương trình yêu cầu bộ nhớ, CPython sử dụng phương pháp

# Increment a by 1
a = a + 1
3 để yêu cầu bộ nhớ đó từ hệ điều hành và heap riêng tăng kích thước

Để tránh gọi

# Increment a by 1
a = a + 1
3 và
# Increment a by 1
a = a + 1
4 cho mỗi lần tạo và xóa đối tượng nhỏ, CPython định nghĩa nhiều bộ cấp phát và bộ giải mã cho các mục đích khác nhau. Chúng ta sẽ thảo luận chi tiết từng vấn đề trong phần tiếp theo

Cấp phát bộ nhớ

Để tránh gọi các phương thức

# Increment a by 1
a = a + 1
3 và
# Increment a by 1
a = a + 1
4 thường xuyên, CPython định nghĩa một hệ thống cấp phát, như được hiển thị bên dưới

Đây là hệ thống cấp phát bộ nhớ từ cấp cơ sở

  • Bộ phân bổ mục đích chung [phương pháp
    # Increment a by 1
    a = a + 1
    
    3 của CPython]
  • Bộ cấp phát bộ nhớ thô [đối với các đối tượng lớn hơn 512 byte]
  • Bộ cấp phát đối tượng [đối với các đối tượng nhỏ hơn hoặc bằng 512 byte]
  • Bộ cấp phát dành riêng cho đối tượng [bộ cấp phát bộ nhớ cụ thể cho các loại dữ liệu cụ thể]

Ở cấp độ cơ sở là bộ cấp phát

# Increment a by 1
a = a + 1
63. Bộ cấp phát
# Increment a by 1
a = a + 1
63 là phương thức
# Increment a by 1
a = a + 1
3 của ngôn ngữ C cho CPython. Nó chịu trách nhiệm tương tác với trình quản lý bộ nhớ ảo của hệ điều hành và phân bổ bộ nhớ cần thiết cho quy trình Python. Đây là bộ cấp phát duy nhất giao tiếp với bộ quản lý bộ nhớ ảo của hệ điều hành

Ở đầu bộ cấp phát

# Increment a by 1
a = a + 1
63 là bộ cấp phát
# Increment a by 1
a = a + 1
67 của Python. Bộ cấp phát
# Increment a by 1
a = a + 1
67 cung cấp một sự trừu tượng cho bộ cấp phát
# Increment a by 1
a = a + 1
63 [i. e. , phương pháp
# Increment a by 1
a = a + 1
3]. Khi một quy trình Python cần bộ nhớ, bộ cấp phát
# Increment a by 1
a = a + 1
67 tương tác với bộ cấp phát
# Increment a by 1
a = a + 1
63 để cung cấp bộ nhớ cần thiết. Nó đảm bảo rằng có đủ bộ nhớ để lưu trữ tất cả dữ liệu của quy trình Python

Ngoài bộ cấp phát

# Increment a by 1
a = a + 1
67, chúng tôi có bộ cấp phát đối tượng. Bộ cấp phát này được sử dụng để cấp phát bộ nhớ cho các đối tượng nhỏ [nhỏ hơn hoặc bằng 512 byte]. Nếu một đối tượng cần nhiều hơn 512 byte bộ nhớ, trình quản lý bộ nhớ của Python sẽ gọi trực tiếp bộ cấp phát
# Increment a by 1
a = a + 1
67

Như đã thấy trong phần trình bày ở trên, chúng ta có các bộ cấp phát dành riêng cho đối tượng ở trên bộ cấp phát đối tượng. Các kiểu dữ liệu đơn giản, chẳng hạn như số nguyên, float, chuỗi và danh sách, có các bộ cấp phát dành riêng cho đối tượng tương ứng. Các bộ cấp phát dành riêng cho đối tượng này thực hiện các chính sách quản lý bộ nhớ theo yêu cầu của đối tượng. Ví dụ: bộ cấp phát dành riêng cho đối tượng cho số nguyên có cách triển khai khác với bộ cấp phát dành riêng cho đối tượng cho float

Cả bộ cấp phát dành riêng cho đối tượng và bộ cấp phát đối tượng đều hoạt động trên bộ nhớ đã được cấp phát bộ nhớ thô cấp phát cho quy trình Python. Các bộ cấp phát này không bao giờ yêu cầu bộ nhớ từ hệ điều hành. Họ hoạt động trên heap riêng. Nếu bộ cấp phát đối tượng hoặc bộ cấp phát dành riêng cho đối tượng cần thêm bộ nhớ, bộ cấp phát bộ nhớ thô của Python sẽ cung cấp nó bằng cách tương tác với bộ cấp phát mục đích chung

Hệ thống cấp phát bộ nhớ trong Python

Khi một đối tượng yêu cầu bộ nhớ và đối tượng có các bộ cấp phát dành riêng cho đối tượng được xác định, các bộ cấp phát dành riêng cho đối tượng được sử dụng để cấp phát bộ nhớ

Nếu đối tượng không có bộ cấp phát dành riêng cho đối tượng và hơn 512 byte bộ nhớ được yêu cầu, trình quản lý bộ nhớ Python sẽ gọi trực tiếp bộ cấp phát bộ nhớ thô để cấp phát bộ nhớ

Nếu kích thước bộ nhớ được yêu cầu nhỏ hơn 512 byte, bộ cấp phát đối tượng sẽ được sử dụng để cấp phát bộ nhớ đó

Cấp phát đối tượng

Bộ cấp phát đối tượng còn được gọi là

# Increment a by 1
a = a + 1
85. Nó được sử dụng để cấp phát bộ nhớ cho các đối tượng nhỏ có kích thước nhỏ hơn 512 byte

Cơ sở mã CPython mô tả bộ cấp phát đối tượng là

một bộ cấp phát bộ nhớ nhanh, có mục đích đặc biệt cho các khối nhỏ, được sử dụng trên một malloc có mục đích chung

Nó được gọi cho mọi phân bổ và hủy phân bổ đối tượng [PyObject_New/Del] trừ khi các bộ cấp phát dành riêng cho đối tượng triển khai sơ đồ phân bổ độc quyền [ví dụ:. ints sử dụng một danh sách miễn phí đơn giản]

Đây cũng là nơi mà bộ thu gom rác tuần hoàn hoạt động có chọn lọc trên các đối tượng chứa

Khi một đối tượng nhỏ yêu cầu bộ nhớ, thay vì chỉ cấp phát bộ nhớ cho đối tượng đó, bộ cấp phát đối tượng sẽ yêu cầu một khối bộ nhớ lớn từ hệ điều hành. Khối bộ nhớ lớn này sau đó được sử dụng để phân bổ bộ nhớ cho các đối tượng nhỏ khác

Bằng cách này, bộ cấp phát đối tượng tránh gọi

# Increment a by 1
a = a + 1
3 cho từng đối tượng nhỏ

Khối bộ nhớ lớn mà bộ cấp phát đối tượng phân bổ được gọi là

# Increment a by 1
a = a + 1
87. Đấu trường có kích thước 256 KB

Để sử dụng ________ 688 một cách hiệu quả, Python chia ________ 687 thành ________ 1500. Nhóm là 4 KB. Vì vậy, một lon

# Increment a by 1
a = a + 1
501 bao gồm 64 nhóm [256KB/4KB]

Các nhóm được chia thành

# Increment a by 1
a = a + 1
502

Tiếp theo, chúng ta sẽ thảo luận về từng thành phần này

khối

Các khối là đơn vị bộ nhớ nhỏ nhất mà bộ cấp phát đối tượng có thể cấp phát cho một đối tượng. Một khối chỉ có thể được phân bổ cho một đối tượng và một đối tượng chỉ có thể được phân bổ cho một khối. Không thể đặt các bộ phận của một đối tượng thành hai hoặc nhiều khối riêng biệt

Khối có sẵn trong các kích cỡ khác nhau. Kích thước nhỏ nhất của một khối là 8 byte, trong khi kích thước tối đa của một khối là 512 byte. Kích thước của một khối là bội số của 8, và do đó, kích thước khối có thể là 8, 16, 32,. , 504 hoặc 512 byte. Mỗi kích thước khối được gọi là Lớp kích thước. Có 64 lớp kích thước, như hình bên dưới

Như đã thấy trong bảng trên, khối kích thước lớp 0 có kích thước 8 byte, trong khi khối kích thước lớp 1 có kích thước 16 byte, v.v.

Các chương trình luôn được phân bổ một khối đầy đủ hoặc không có khối nào cả. Vì vậy, nếu một chương trình yêu cầu 14 byte bộ nhớ, nó sẽ được cấp phát một khối 16 byte. Tương tự, nếu một chương trình yêu cầu 35 byte bộ nhớ, thì một khối 40 byte sẽ được cấp phát.

hồ bơi

Một nhóm bao gồm các khối chỉ có một loại kích thước. Ví dụ: một nhóm có khối kích thước loại 0 không thể có khối thuộc bất kỳ loại kích thước nào khác

Kích thước của nhóm bằng với kích thước của trang bộ nhớ ảo. Đây là thuật ngữ trang bộ nhớ ảo nghĩa là gì

Trang, trang bộ nhớ hoặc trang ảo là một khối bộ nhớ ảo liền kề có độ dài cố định. Đây là đơn vị dữ liệu nhỏ nhất để quản lý bộ nhớ trong hệ điều hành bộ nhớ ảo. -Wikipedia

Trong hầu hết các trường hợp, kích thước của nhóm là 4 KB

Các nhóm chỉ được khắc từ Đấu trường khi không có nhóm nào khác có khối thuộc loại kích thước được yêu cầu

Một nhóm có thể ở một trong ba trạng thái

  1. Đã sử dụng. Một nhóm được cho là ở trạng thái

    # Increment a by 1
    a = a + 1
    
    503 nếu nó có sẵn các khối để phân bổ

  2. Đầy. Một nhóm được cho là ở trạng thái

    # Increment a by 1
    a = a + 1
    
    504 nếu tất cả các khối của nhóm được phân bổ

  3. Rỗng Một nhóm được cho là ở trạng thái

    # Increment a by 1
    a = a + 1
    
    505 nếu tất cả các khối của nhóm đều có sẵn để phân bổ. Một nhóm trống không có lớp kích thước được liên kết với nó. Nó có thể được sử dụng để gán các khối có kích thước bất kỳ

Các nhóm được xác định trong mã CPython như hình bên dưới

# Increment a by 1
a = a + 1
7

Thuật ngữ

# Increment a by 1
a = a + 1
506 cho biết loại kích thước của nhóm. Nếu
# Increment a by 1
a = a + 1
506 là 0 cho một nhóm, nó sẽ chỉ có các khối có kích thước loại 0 [i. e. , khối 8 byte]

Thuật ngữ

# Increment a by 1
a = a + 1
508 cho biết đấu trường mà hồ bơi thuộc về

Các nhóm có cùng kích thước lớp được liên kết với nhau bằng danh sách liên kết kép. Con trỏ ________ 1509 trỏ đến nhóm tiếp theo của cùng loại kích thước, trong khi con trỏ ________ 1510 trỏ đến nhóm trước đó của cùng loại kích thước

Đây là cách các nhóm có cùng kích thước lớp được kết nối

Con trỏ

# Increment a by 1
a = a + 1
511 trỏ đến điểm bắt đầu của danh sách các khối miễn phí được liên kết đơn trong nhóm. Khi một khối được phân bổ được giải phóng, nó sẽ được chèn vào trước con trỏ
# Increment a by 1
a = a + 1
511

Như đã thấy trong hình trên, các khối

# Increment a by 1
a = a + 1
513 được phân bổ cho các đối tượng, trong khi các khối
# Increment a by 1
a = a + 1
4 được phân bổ cho các đối tượng nhưng hiện tại miễn phí và có thể được phân bổ cho các đối tượng mới

Khi một yêu cầu được thực hiện cho bộ nhớ và không có nhóm nào có sẵn khối của lớp kích thước được yêu cầu, CPython sẽ tạo một nhóm mới từ Arena

Khi một nhóm mới được chạm khắc, toàn bộ nhóm không bị phân mảnh ngay lập tức thành các khối. Các khối được chạm khắc từ hồ bơi khi cần thiết. Vùng bóng mờ

# Increment a by 1
a = a + 1
515 của nhóm trong hình trên chỉ ra rằng các phần này của nhóm không bị phân mảnh thành các khối

Một đoạn trích từ cơ sở mã CPython đề cập đến việc khắc các khối từ nhóm như sau

Các khối có sẵn trong một nhóm không được liên kết tất cả với nhau khi một nhóm được khởi tạo. Thay vào đó, chỉ các khối "hai địa chỉ thấp nhất" đầu tiên được thiết lập, trả về khối đầu tiên như vậy và đặt pool->freeblock thành danh sách một khối giữ khối thứ hai như vậy. Điều này phù hợp với việc pymalloc cố gắng ở mọi cấp độ [đấu trường, nhóm và khối] không bao giờ chạm vào một phần bộ nhớ cho đến khi nó thực sự cần thiết

Ở đây, chúng ta có thể thấy rằng khi một bể mới được khoét từ một đấu trường, chỉ có hai khối đầu tiên được khoét từ bể. Một khối được phân bổ cho đối tượng yêu cầu bộ nhớ, trong khi khối còn lại trống hoặc chưa được chạm tới và con trỏ

# Increment a by 1
a = a + 1
511 trỏ tới khối này

CPython duy trì một mảng có tên là ________ 1517 để theo dõi các nhóm ở trạng thái ________ 1503 [các nhóm có sẵn để phân bổ] của tất cả các loại kích thước

Chỉ số của mảng

# Increment a by 1
a = a + 1
517 bằng với lớp kích thước của nhóm. Đối với mỗi chỉ mục
# Increment a by 1
a = a + 1
76 của mảng
# Increment a by 1
a = a + 1
517,
# Increment a by 1
a = a + 1
522 trỏ tới tiêu đề của nhóm kích thước lớp
# Increment a by 1
a = a + 1
76. Ví dụ:
# Increment a by 1
a = a + 1
524 trỏ đến tiêu đề của nhóm kích thước lớp
# Increment a by 1
a = a + 1
525 và
# Increment a by 1
a = a + 1
526 trỏ đến tiêu đề của nhóm kích thước lớp
# Increment a by 1
a = a + 1
527

Hình bên dưới chắc dễ hiểu hơn

Vì các nhóm có cùng loại kích thước được liên kết với nhau bằng cách sử dụng danh sách liên kết đôi, tất cả các nhóm ở trạng thái

# Increment a by 1
a = a + 1
503 của mỗi loại kích thước có thể được duyệt qua bằng cách sử dụng mảng
# Increment a by 1
a = a + 1
517

Nếu

# Increment a by 1
a = a + 1
522 trỏ đến
# Increment a by 1
a = a + 1
531, điều đó có nghĩa là không có nhóm quy mô loại
# Increment a by 1
a = a + 1
76 nào ở trạng thái
# Increment a by 1
a = a + 1
503. Nếu một đối tượng yêu cầu một khối có kích thước lớp
# Increment a by 1
a = a + 1
76, CPython sẽ khắc một nhóm mới có kích thước lớp
# Increment a by 1
a = a + 1
76 và cập nhật
# Increment a by 1
a = a + 1
522 để trỏ đến nhóm mới này

Nếu một khối được giải phóng khỏi một nhóm ở trạng thái

# Increment a by 1
a = a + 1
504, thì trạng thái nhóm sẽ thay đổi từ
# Increment a by 1
a = a + 1
504 thành
# Increment a by 1
a = a + 1
503. CPython thêm nhóm này vào phía trước danh sách được liên kết đôi của các nhóm thuộc lớp kích thước của nó

Như đã thấy trong hình trên,

# Increment a by 1
a = a + 1
540 có kích thước loại
# Increment a by 1
a = a + 1
525 và nó ở trạng thái
# Increment a by 1
a = a + 1
504. Khi một khối được giải phóng khỏi
# Increment a by 1
a = a + 1
540, trạng thái của nó sẽ thay đổi từ
# Increment a by 1
a = a + 1
504 thành
# Increment a by 1
a = a + 1
503. Khi trạng thái của
# Increment a by 1
a = a + 1
540 trở thành
# Increment a by 1
a = a + 1
503, CPython thêm nhóm này vào trước danh sách liên kết đôi của các nhóm có kích thước lớp
# Increment a by 1
a = a + 1
525 và
# Increment a by 1
a = a + 1
524 sẽ bắt đầu trỏ đến nhóm này

đấu trường

Đấu trường là các khối bộ nhớ lớn được sử dụng để cấp phát bộ nhớ cho các đối tượng nhỏ. Chúng được cấp phát bởi bộ cấp phát bộ nhớ thô với kích thước 256 KB

Khi một đối tượng nhỏ yêu cầu bộ nhớ, nhưng không có đấu trường hiện có để xử lý yêu cầu này, thay vì chỉ yêu cầu bộ nhớ cho đối tượng nhỏ này, bộ cấp phát bộ nhớ thô sẽ yêu cầu một khối bộ nhớ lớn [256 KB] từ hệ điều hành. Những khối bộ nhớ lớn này được gọi là đấu trường

Các hồ bơi [kích thước 4 KB] được chạm khắc từ các đấu trường khi cần thiết

Hãy xem xét

# Increment a by 1
a = a + 1
550, như được định nghĩa trong cơ sở mã CPython

# Increment a by 1
a = a + 1
25

Con trỏ

# Increment a by 1
a = a + 1
551 trỏ đến danh sách các nhóm miễn phí và các nhóm miễn phí không có bất kỳ khối nào được phân bổ

Thuật ngữ

# Increment a by 1
a = a + 1
552 cho biết số lượng nhóm miễn phí trong đấu trường

CPython duy trì một danh sách liên kết kép có tên là

# Increment a by 1
a = a + 1
553 để theo dõi tất cả các đấu trường có nhóm
# Increment a by 1
a = a + 1
554

Nhóm

# Increment a by 1
a = a + 1
555 ở trạng thái
# Increment a by 1
a = a + 1
505 hoặc
# Increment a by 1
a = a + 1
503

Con trỏ

# Increment a by 1
a = a + 1
558 trỏ đến đấu trường có thể sử dụng tiếp theo, trong khi con trỏ
# Increment a by 1
a = a + 1
559 trỏ đến đấu trường có thể sử dụng trước đó trong danh sách liên kết kép
# Increment a by 1
a = a + 1
553. Danh sách liên kết kép được sắp xếp theo thứ tự tăng dần của giá trị
# Increment a by 1
a = a + 1
552

Như được hiển thị ở trên,

# Increment a by 1
a = a + 1
553 được sắp xếp trên cơ sở của
# Increment a by 1
a = a + 1
552. Đấu trường có 0 bể bơi miễn phí là mục đầu tiên, tiếp theo là đấu trường có 1 bể bơi miễn phí, v.v. Nó có nghĩa là danh sách được sắp xếp với các đấu trường được phân bổ nhiều nhất trước tiên. Chúng tôi sẽ giải thích lý do tại sao phân loại này được nhập trong phần tiếp theo của bài viết

Danh sách đấu trường có thể sử dụng được sắp xếp dựa trên danh sách nào có nhiều phân bổ nhất, vì vậy khi có yêu cầu cấp phát bộ nhớ, nó sẽ được phục vụ từ đấu trường có nhiều phân bổ nhất

Bộ nhớ phát hành quy trình Python có không?

Khi một khối được phân bổ trong nhóm được giải phóng, CPython không trả lại bộ nhớ cho hệ điều hành. Bộ nhớ này tiếp tục thuộc về quy trình Python và CPython sử dụng khối này để cấp phát bộ nhớ cho các đối tượng mới

Ngay cả khi tất cả các khối trong nhóm được giải phóng, CPython không trả lại bất kỳ bộ nhớ nào từ nhóm cho hệ điều hành. CPython giữ bộ nhớ của toàn bộ nhóm dành riêng cho mục đích sử dụng của chính nó

Python giải phóng bộ nhớ trở lại hệ điều hành ở cấp độ đấu trường, không phải ở cấp độ khối hoặc nhóm. Ngoài ra, xin lưu ý rằng CPython giải phóng bộ nhớ của toàn bộ đấu trường cùng một lúc

Vì bộ nhớ chỉ có thể được giải phóng ở cấp đấu trường, CPython chỉ tạo đấu trường từ phần bộ nhớ mới khi thực sự cần thiết. Nó luôn cố gắng phân bổ bộ nhớ từ các khối và nhóm được chạm khắc trước đó

Đây là lý do

# Increment a by 1
a = a + 1
553 được sắp xếp theo thứ tự giảm dần của
# Increment a by 1
a = a + 1
552. Yêu cầu bộ nhớ tiếp theo sẽ được phân bổ từ các đấu trường có nhiều phân bổ nhất. Điều này cho phép các đấu trường có ít dữ liệu nhất trở nên tự do nếu các đối tượng mà chúng chứa bị xóa và bộ nhớ do các đấu trường này chiếm giữ có thể được giải phóng cho hệ điều hành

Thu gom rác bằng Python

Thu gom rác được định nghĩa là quá trình thu hồi hoặc giải phóng bộ nhớ được cấp phát khi chương trình không còn cần đến nó nữa.

Bộ sưu tập rác [GC] là một hình thức quản lý bộ nhớ tự động. Trình thu gom rác cố gắng lấy lại bộ nhớ đã được cấp phát bởi chương trình, nhưng không còn được tham chiếu nữa - còn được gọi là rác. -Wikipedia

Trong các ngôn ngữ như C, lập trình viên phải giải phóng bộ nhớ theo cách thủ công cho các đối tượng không sử dụng [bộ sưu tập rác của các đối tượng không sử dụng], trong khi bộ sưu tập rác trong Python được ngôn ngữ tự động xử lý.

Python sử dụng hai phương pháp để thu gom rác tự động

  1. Thu gom rác trên cơ sở đếm tham chiếu
  2. thu gom rác thế hệ

Trước tiên, hãy giải thích số tham chiếu là gì, sau đó chúng ta sẽ tìm hiểu thêm về thu gom rác trên cơ sở đếm tham chiếu

Số tham chiếu

Như chúng ta đã thấy trước đó, CPython tạo nội bộ các thuộc tính

# Increment a by 1
a = a + 1
69 và
# Increment a by 1
a = a + 1
567 cho mỗi đối tượng

Hãy xem xét một ví dụ để hiểu rõ hơn về thuộc tính

# Increment a by 1
a = a + 1
567

# Increment a by 1
a = a + 1
6

Khi đoạn mã trên được thực thi, CPython tạo một đối tượng

# Increment a by 1
a = a + 1
569 kiểu
# Increment a by 1
a = a + 1
570, như hình bên dưới

Trường

# Increment a by 1
a = a + 1
567 cho biết số lượng tham chiếu đến đối tượng. Chúng tôi biết rằng trong Python, các biến chỉ là tham chiếu đến các đối tượng. Trong ví dụ trên, biến
# Increment a by 1
a = a + 1
83 là tham chiếu duy nhất đến đối tượng chuỗi
# Increment a by 1
a = a + 1
569. Do đó, giá trị
# Increment a by 1
a = a + 1
567 của đối tượng chuỗi
# Increment a by 1
a = a + 1
569 là
# Increment a by 1
a = a + 1
527

Chúng ta có thể lấy số lượng tham chiếu của bất kỳ đối tượng nào trong Python bằng phương thức

# Increment a by 1
a = a + 1
577

Hãy lấy số tham chiếu của đối tượng chuỗi

# Increment a by 1
a = a + 1
569

# Increment a by 1
a = a + 1
8

Đầu ra của đoạn mã trên là

# Increment a by 1
a = a + 1
579. Điều này chỉ ra rằng đối tượng chuỗi
# Increment a by 1
a = a + 1
569 đang được tham chiếu bởi hai biến. Tuy nhiên, chúng ta đã thấy trước đó rằng đối tượng
# Increment a by 1
a = a + 1
569 chỉ được tham chiếu bởi biến
# Increment a by 1
a = a + 1
83

Tại sao giá trị đếm tham chiếu là

# Increment a by 1
a = a + 1
579 cho đối tượng chuỗi
# Increment a by 1
a = a + 1
569 khi sử dụng phương thức
# Increment a by 1
a = a + 1
577?

Để hiểu điều này, hãy xem xét định nghĩa của phương thức

# Increment a by 1
a = a + 1
577

# Increment a by 1
a = a + 1
50

Ghi chú. Định nghĩa

# Increment a by 1
a = a + 1
577 ở trên chỉ nhằm mục đích giải thích

Ở đây, khi chúng ta truyền biến

# Increment a by 1
a = a + 1
83 cho phương thức
# Increment a by 1
a = a + 1
577 thì đối tượng
# Increment a by 1
a = a + 1
569 cũng được tham chiếu bởi tham số
# Increment a by 1
a = a + 1
591 của phương thức
# Increment a by 1
a = a + 1
577. Do đó, số tham chiếu của đối tượng
# Increment a by 1
a = a + 1
569 là
# Increment a by 1
a = a + 1
579

Do đó, bất cứ khi nào chúng ta sử dụng phương pháp

# Increment a by 1
a = a + 1
577 để lấy số tham chiếu của một đối tượng, số tham chiếu sẽ luôn nhiều hơn 1 so với số tham chiếu thực tế của đối tượng

Hãy tạo một biến khác,

# Increment a by 1
a = a + 1
85, trỏ đến cùng một đối tượng chuỗi
# Increment a by 1
a = a + 1
569

# Increment a by 1
a = a + 1
5

Các biến

# Increment a by 1
a = a + 1
83 và
# Increment a by 1
a = a + 1
85 đều trỏ đến đối tượng chuỗi
# Increment a by 1
a = a + 1
569. Do đó, số lượng tham chiếu của đối tượng chuỗi
# Increment a by 1
a = a + 1
569 sẽ là 2 và đầu ra của phương thức
# Increment a by 1
a = a + 1
577 sẽ là 3

# Increment a by 1
a = a + 1
52

Giảm số lượng tham chiếu

Để giảm số lượng tham chiếu, chúng ta phải xóa các tham chiếu đến biến. Đây là một ví dụ

# Increment a by 1
a = a + 1
53

Biến

# Increment a by 1
a = a + 1
85 không còn trỏ đến đối tượng chuỗi
# Increment a by 1
a = a + 1
569. Do đó, số lượng tham chiếu của đối tượng chuỗi
# Increment a by 1
a = a + 1
569 cũng sẽ giảm

# Increment a by 1
a = a + 1
54

Giảm số lượng tham chiếu bằng từ khóa
# Increment a by 1
a = a + 1
506

Chúng ta cũng có thể sử dụng từ khóa

# Increment a by 1
a = a + 1
506 để giảm số lượng tham chiếu của một đối tượng

Nếu chúng ta gán

# Increment a by 1
a = a + 1
508 cho biến
# Increment a by 1
a = a + 1
85 [
# Increment a by 1
a = a + 1
510], thì
# Increment a by 1
a = a + 1
85 không còn trỏ đến đối tượng chuỗi
# Increment a by 1
a = a + 1
569. Từ khóa
# Increment a by 1
a = a + 1
506 hoạt động theo cách tương tự và được sử dụng để xóa tham chiếu của đối tượng, do đó làm giảm số lượng tham chiếu của nó

Xin lưu ý rằng từ khóa

# Increment a by 1
a = a + 1
506 không xóa đối tượng

Xem xét ví dụ sau

# Increment a by 1
a = a + 1
55

Ở đây, chúng tôi chỉ xóa tham chiếu của

# Increment a by 1
a = a + 1
85. Đối tượng chuỗi
# Increment a by 1
a = a + 1
569 không bị xóa

Bây giờ chúng ta hãy lấy số lượng tham chiếu của đối tượng chuỗi

# Increment a by 1
a = a + 1
569

# Increment a by 1
a = a + 1
56

Hoàn hảo. Số tham chiếu của

# Increment a by 1
a = a + 1
85 hiện là 2

Hãy tóm tắt lại những gì chúng ta đã học về số lượng tham chiếu

Số lượng tham chiếu của một đối tượng tăng lên nếu chúng ta gán cùng một đối tượng cho một biến mới. Khi chúng tôi hủy đăng ký đối tượng bằng cách sử dụng từ khóa

# Increment a by 1
a = a + 1
506 hoặc bằng cách trỏ nó tới
# Increment a by 1
a = a + 1
508, số lượng tham chiếu của đối tượng đó sẽ giảm

Bây giờ chúng ta đã hiểu rõ hơn về khái niệm số tham chiếu trong Python, hãy cùng tìm hiểu cách hoạt động của bộ sưu tập rác trên cơ sở số tham chiếu

Thu gom rác trên cơ sở số lượng tham chiếu

Thu gom rác trên cơ sở số tham chiếu sử dụng số tham chiếu của đối tượng để giải phóng hoặc lấy lại bộ nhớ. Khi số lượng tham chiếu của đối tượng bằng 0, bộ sưu tập rác của Python sẽ khởi động và xóa đối tượng khỏi bộ nhớ

Khi một đối tượng bị xóa khỏi bộ nhớ, nó có thể kích hoạt việc xóa các đối tượng khác

Xem xét ví dụ sau

# Increment a by 1
a = a + 1
57

Bỏ qua sự gia tăng số lượng tham chiếu do phương thức

# Increment a by 1
a = a + 1
577, số lượng tham chiếu của đối tượng chuỗi
# Increment a by 1
a = a + 1
522 là 2 [được tham chiếu bởi biến
# Increment a by 1
a = a + 1
523 ​​và được tham chiếu từ danh sách
# Increment a by 1
a = a + 1
85] và số lượng tham chiếu của đối tượng mảng là
# Increment a by 1
a = a + 1
527

Tiếp theo, hãy xóa biến

# Increment a by 1
a = a + 1
523

# Increment a by 1
a = a + 1
58

Số tham chiếu của đối tượng chuỗi

# Increment a by 1
a = a + 1
522 là 1 do đối tượng mảng
# Increment a by 1
a = a + 1
528, như hình bên dưới

Chúng ta vẫn có thể truy cập đối tượng chuỗi

# Increment a by 1
a = a + 1
522 với sự trợ giúp của biến
# Increment a by 1
a = a + 1
85

# Increment a by 1
a = a + 1
59

Hãy xóa biến

# Increment a by 1
a = a + 1
85

# Increment a by 1
a = a + 1
55

Khi đoạn mã trên được thực thi, số tham chiếu của đối tượng mảng trở thành 0 và trình thu gom rác sẽ xóa đối tượng mảng

Xóa đối tượng mảng

# Increment a by 1
a = a + 1
528 cũng sẽ xóa tham chiếu
# Increment a by 1
a = a + 1
523 ​​của đối tượng chuỗi
# Increment a by 1
a = a + 1
522 khỏi đối tượng mảng. Điều này sẽ làm cho số tham chiếu của đối tượng
# Increment a by 1
a = a + 1
522 bằng 0 và kết quả là đối tượng
# Increment a by 1
a = a + 1
522 cũng sẽ được thu thập. Do đó, việc thu gom rác của một đối tượng cũng có thể kích hoạt việc thu gom rác của các đối tượng mà đối tượng đó tham chiếu tới.

Thu gom rác dựa trên số lượng tham chiếu là thời gian thực

Bộ sưu tập rác được kích hoạt ngay khi số lượng tham chiếu của đối tượng trở thành 0. Đây là thuật toán thu gom rác chính của Python và do đó, nó không thể bị vô hiệu hóa

Thu gom rác trên cơ sở số lượng tham chiếu không hoạt động nếu có tham chiếu theo chu kỳ. Để giải phóng hoặc lấy lại bộ nhớ của các đối tượng có tham chiếu theo chu kỳ, Python sử dụng thuật toán thu gom rác thế hệ

Tiếp theo, chúng ta sẽ thảo luận về các tham chiếu theo chu kỳ và sau đó đi sâu hơn để hiểu thêm về thuật toán thu gom rác thế hệ

Tham chiếu theo chu kỳ trong Python

Tham chiếu tuần hoàn hoặc tham chiếu vòng là điều kiện trong đó một đối tượng tự tham chiếu hoặc khi hai đối tượng khác nhau tham chiếu lẫn nhau

Chỉ có thể tham chiếu theo chu kỳ với các đối tượng vùng chứa, chẳng hạn như danh sách, dict và các đối tượng do người dùng xác định. Không thể với các kiểu dữ liệu không thay đổi, chẳng hạn như số nguyên, số float hoặc chuỗi

Xem xét ví dụ sau

# Increment a by 1
a = a + 1
51

Như đã thấy trong biểu diễn ở trên, đối tượng mảng

# Increment a by 1
a = a + 1
85 đang tham chiếu chính nó

Hãy xóa tài liệu tham khảo

# Increment a by 1
a = a + 1
85

# Increment a by 1
a = a + 1
55

Khi chúng tôi xóa tham chiếu

# Increment a by 1
a = a + 1
85, đối tượng mảng sẽ không thể truy cập được từ mã Python, nhưng nó sẽ tiếp tục tồn tại trong bộ nhớ, như thể hiện trong hình trên

Vì đối tượng mảng đang tự tham chiếu, số tham chiếu của đối tượng mảng sẽ không bao giờ trở thành 0 và nó sẽ không bao giờ được thu thập bởi trình thu gom rác đếm tham chiếu

Hãy xem xét một ví dụ khác

# Increment a by 1
a = a + 1
53

Ở đây, chúng ta đã định nghĩa hai đối tượng,

# Increment a by 1
a = a + 1
540 và
# Increment a by 1
a = a + 1
541, của các lớp
# Increment a by 1
a = a + 1
542 và
# Increment a by 1
a = a + 1
543, tương ứng.
# Increment a by 1
a = a + 1
540 được tham chiếu bởi biến
# Increment a by 1
a = a + 1
545 và
# Increment a by 1
a = a + 1
541 được tham chiếu bởi biến
# Increment a by 1
a = a + 1
547

# Increment a by 1
a = a + 1
540 có thuộc tính
# Increment a by 1
a = a + 1
549 trỏ đến
# Increment a by 1
a = a + 1
541. Tương tự, đối tượng
# Increment a by 1
a = a + 1
541 có thuộc tính
# Increment a by 1
a = a + 1
552 trỏ đến
# Increment a by 1
a = a + 1
540

Có thể truy cập

# Increment a by 1
a = a + 1
540 và
# Increment a by 1
a = a + 1
541 trong mã Python với sự trợ giúp của các biến lần lượt là
# Increment a by 1
a = a + 1
545 và
# Increment a by 1
a = a + 1
547

Hãy xóa các biến

# Increment a by 1
a = a + 1
545 và
# Increment a by 1
a = a + 1
547

# Increment a by 1
a = a + 1
54

Sau khi chúng tôi xóa các biến

# Increment a by 1
a = a + 1
545 và
# Increment a by 1
a = a + 1
547, thì sẽ không thể truy cập
# Increment a by 1
a = a + 1
540 và
# Increment a by 1
a = a + 1
541 từ cơ sở mã Python, nhưng chúng sẽ tiếp tục tồn tại trong bộ nhớ

Số lượng tham chiếu của các đối tượng này sẽ không bao giờ trở thành 0, vì chúng trỏ đến nhau [theo thuộc tính

# Increment a by 1
a = a + 1
552 và
# Increment a by 1
a = a + 1
549]. Do đó, các đối tượng này sẽ không bao giờ được thu thập bởi trình thu gom rác đếm tham chiếu

Vì số tham chiếu của các đối tượng có tham chiếu tuần hoàn không bao giờ trở thành 0, phương thức thu gom rác đếm tham chiếu sẽ không bao giờ xóa các đối tượng này. Đối với những trường hợp như vậy, Python cung cấp một thuật toán thu gom rác khác có tên là Generational Garbage Collection

Bộ sưu tập rác thế hệ trong Python

Thuật toán thu gom rác thế hệ giải quyết vấn đề đối tượng thu gom rác có tham chiếu vòng tròn. Vì các tham chiếu vòng chỉ có thể thực hiện được với các đối tượng vùng chứa, nên nó quét tất cả các đối tượng vùng chứa, phát hiện các đối tượng có tham chiếu vòng và loại bỏ chúng nếu chúng có thể được thu gom rác

Để giảm số lượng đối tượng được quét, bộ sưu tập rác thế hệ cũng bỏ qua các bộ chỉ chứa các loại bất biến [e. g. , int và chuỗi]

Vì quét các đối tượng và phát hiện các chu trình là một quá trình tốn thời gian, thuật toán thu gom rác thế hệ không phải là thời gian thực. Nó được kích hoạt định kỳ. Khi thuật toán thu gom rác thế hệ được kích hoạt, mọi thứ khác sẽ dừng lại. Do đó, để giảm số lần kích hoạt bộ sưu tập rác thế hệ, CPython phân loại các đối tượng vùng chứa thành nhiều thế hệ và xác định ngưỡng cho từng thế hệ này. Bộ sưu tập rác thế hệ được kích hoạt nếu số lượng đối tượng trong một thế hệ nhất định vượt quá ngưỡng đã xác định

CPython phân loại các đối tượng thành ba thế hệ [thế hệ 0, 1 và 2]. Khi một đối tượng mới được tạo, nó thuộc thế hệ đầu tiên. Nếu đối tượng này không được thu thập khi CPython chạy thuật toán thu gom rác thế hệ, nó sẽ chuyển sang thế hệ thứ hai. Nếu đối tượng không được thu thập khi CPython chạy lại bộ sưu tập rác thế hệ, nó sẽ được chuyển sang thế hệ thứ ba. Đây là thế hệ cuối cùng và đối tượng sẽ vẫn ở đó

Nói chung, hầu hết các đối tượng được thu thập trong thế hệ đầu tiên

Khi bộ sưu tập rác thế hệ được kích hoạt cho một thế hệ nhất định, nó cũng thu thập tất cả các thế hệ trẻ hơn. Ví dụ: nếu bộ sưu tập rác được kích hoạt cho thế hệ 1, thì nó cũng sẽ thu thập các đối tượng có trong thế hệ 0

Trường hợp cả ba thế hệ [0, 1 và 2] được thu thập rác được gọi là thu thập đầy đủ. Vì bộ sưu tập

# Increment a by 1
a = a + 1
504 liên quan đến việc quét và phát hiện các chu kỳ trong một số lượng lớn đối tượng, CPython cố gắng tránh bộ sưu tập
# Increment a by 1
a = a + 1
504 càng nhiều càng tốt

Chúng ta có thể thay đổi hành vi của trình thu gom rác thế hệ bằng cách sử dụng mô-đun

# Increment a by 1
a = a + 1
5 do Python cung cấp. Bộ sưu tập rác thế hệ có thể bị vô hiệu hóa bằng cách sử dụng mô-đun
# Increment a by 1
a = a + 1
5. Do đó, nó còn được gọi là bộ sưu tập rác tùy chọn

Tiếp theo, chúng tôi sẽ giải thích một số phương pháp quan trọng của mô-đun

# Increment a by 1
a = a + 1
5

Mô-đun
# Increment a by 1
a = a + 1
5 trong Python

Chúng ta có thể sử dụng phương pháp sau để xác định ngưỡng cho mỗi thế hệ

# Increment a by 1
a = a + 1
55

Đầu ra ở trên chỉ ra rằng ngưỡng cho thế hệ đầu tiên là 700, trong khi đó là 10 cho thế hệ thứ hai và thứ ba

Nếu có hơn 700 đối tượng trong thế hệ đầu tiên, bộ sưu tập rác thế hệ sẽ được kích hoạt cho tất cả các đối tượng trong thế hệ đầu tiên. Nếu có hơn 10 đối tượng trong thế hệ thứ 2, bộ sưu tập rác thế hệ sẽ được kích hoạt cho cả thế hệ 1 và 0

Chúng ta cũng có thể kiểm tra số lượng đối tượng trong mỗi thế hệ, như hình bên dưới

# Increment a by 1
a = a + 1
56

Ở đây, số lượng đối tượng trong thế hệ đầu tiên là 679, trong khi số lượng đối tượng trong thế hệ thứ hai là 8 và số lượng đối tượng trong thế hệ thứ ba là 0

Chúng tôi cũng có thể chạy thuật toán thu gom rác thế hệ theo cách thủ công, như được hiển thị bên dưới

# Increment a by 1
a = a + 1
57

# Increment a by 1
a = a + 1
572 sẽ kích hoạt bộ sưu tập rác thế hệ. Theo mặc định, nó chạy một bộ sưu tập đầy đủ. Để chạy bộ sưu tập rác thế hệ cho thế hệ thứ nhất, chúng ta có thể gọi phương thức này như hình bên dưới

# Increment a by 1
a = a + 1
58

Hãy kiểm tra số đối tượng còn sống sau quá trình thu thập,

# Increment a by 1
a = a + 1
59

Chúng tôi cũng có thể cập nhật ngưỡng thế hệ cho từng thế hệ, như được hiển thị bên dưới

# Increment a by 1
a = a + 1
0

Chúng ta có thể vô hiệu hóa bộ sưu tập rác thế hệ bằng lệnh sau

# Increment a by 1
a = a + 1
1

Ngược lại, chúng ta có thể kích hoạt lại bằng lệnh sau

# Increment a by 1
a = a + 1
2

Sự kết luận

Trong bài viết này, chúng ta đã biết rằng Python là một tập hợp các quy tắc và thông số kỹ thuật và CPython là triển khai tham chiếu của Python trong C. Chúng ta cũng đã tìm hiểu về các bộ cấp phát bộ nhớ khác nhau, chẳng hạn như *Bộ cấp phát đối tượng*, Bộ cấp phát dành riêng cho đối tượng, Bộ cấp phát bộ nhớ thô và Bộ cấp phát mục đích chung, mà Python sử dụng để quản lý hiệu quả bộ nhớ. Sau đó, chúng ta đã tìm hiểu về Đấu trường, Nhóm và Khối mà trình quản lý bộ nhớ Python sử dụng để tối ưu hóa việc phân bổ/khử phân bổ bộ nhớ cho các đối tượng nhỏ [kích thước nhỏ hơn hoặc bằng 512 byte] trong một chương trình. Chúng tôi cũng đã tìm hiểu về các thuật toán thu gom rác, chẳng hạn như đếm tham chiếu và thu gom rác tạo

Các đối tượng được lưu trữ trong bộ nhớ như thế nào?

Một ngăn xếp và một đống được sử dụng để cấp phát bộ nhớ trong Java. Tuy nhiên, ngăn xếp được sử dụng cho các kiểu dữ liệu nguyên thủy, biến tạm thời, địa chỉ đối tượng, v.v. Đống được sử dụng để lưu trữ các đối tượng trong bộ nhớ .

Các đối tượng Python được biểu diễn trong bộ nhớ như thế nào?

Python lưu trữ đối tượng trong bộ nhớ heap và tham chiếu của đối tượng trong ngăn xếp. Các biến, hàm được lưu trữ trong ngăn xếp và đối tượng được lưu trữ trong heap.

Các đối tượng Python được lưu trữ trong bộ nhớ ở đâu?

Cấp phát bộ nhớ trong Python . heap memory.

Python lưu trữ dữ liệu trong bộ nhớ như thế nào?

Có thể lưu trữ trạng thái của đối tượng Python dưới dạng luồng byte trực tiếp vào tệp hoặc luồng bộ nhớ và truy xuất về trạng thái ban đầu. This process is called serialization and de-serialization. Python's built in library contains various modules for serialization and deserialization process.

Chủ Đề