Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Внутри CPython гид по интерпретатору Python.pdf
Скачиваний:
6
Добавлен:
07.04.2024
Размер:
8.59 Mб
Скачать

Управление памятью

Два важнейших компонента вашего компьютера — память и процессор. Один компонент не может работать без другого. Они должны использоваться правильно и эффективно.

При разработке языка программирования его создатели решают, как пользователь будет управлять памятью компьютера. Это зависит от множества факторов: насколько простым должен быть интерфейс в представлении создателей, должен ли язык быть кроссплатформенным и ставят ли его создатели производительность выше стабильности.

Авторы Python приняли эти решения за вас, но оставили некоторые дополнительные варианты, которые вы сможете выбрать самостоятельно.

В этой главе вы узнаете, как в С организовано управление памятью, так как CPython написан на C. Мы рассмотрим два важнейших аспекта управления памятью в Python:

1.Подсчет ссылок.

2.Сборка мусора.

Кконцу этой главы вы поймете, как CPython выделяет память в операционной системе, как выделяется и освобождается память для объектов и как CPython разбирается с утечкой памяти.

ВЫДЕЛЕНИЕ ПАМЯТИ В C

Чтобы переменные можно было использовать в C, сначала необходимо получить для них память от операционной системы. В C существуют три механизма выделения памяти:

Книги для программистов: https://t.me/booksforits

164    Управление памятью

1.Статическое выделение памяти: требования к памяти вычисляются во время компиляции, а память выделяется исполняемым файлом при запуске.

2.Автоматическое выделение памяти: память для области видимости выделяется из стека вызовов при входе в кадр и освобождается при завершении кадра.

3.Динамическое выделение памяти: память запрашивается и выделяется динамически во время выполнения через вызов API выделения памяти.

Статическое выделение памяти в C

Типы в языке C имеют фиксированный размер. Компилятор вычисляет требования к памяти для всех статических и глобальных переменных, а затем компилирует эти требования в приложение:

static int number = 0;

Для проверки размера типа в C используется функция sizeof(). В моей системе — 64-разрядной macOS с GCC — размер int составляет 4 байта. Базовые типы в C могут иметь разные размеры в зависимости от архитектуры и компилятора.

Массивы определяются статически. Возьмем массив из 10 целых чисел:

static int numbers[10] = {0,1,2,3,4,5,6,7,8,9};

Компилятор C преобразует эту команду в выделение sizeof(int) * 10 байт памяти.

Компилятор C использует системные функции для выделения памяти. Речь идет о системных сервисах, зависящих от операционной системы; это низко­ уровневые функции ядра, выделяющие страницы системной памяти.

Автоматическое выделение памяти в C

По аналогии со статическим выделением памяти, механизм автоматического выделения памяти вычисляет требования к памяти во время компиляции.

Приложение из следующего примера преобразует 100 градусов по Фаренгейту к значению по шкале Цельсия:

Книги для программистов: https://t.me/booksforits

Выделение памяти в C    165

cpython-book-samples 32 automatic.c

#include <stdio.h>

static const double five_ninths = 5.0/9.0;

double celsius(double fahrenheit) {

double c = (fahrenheit - 32) * five_ninths; return c;

}

int main() {

double f = 100;

printf("%f F is %f C\n", f, celsius(f)); return 0;

}

В этом примере используется как статическое, так и автоматическое выделение памяти:

zz Память для константы five_ninths выделяется статически, потому что она объявлена с ключевым словом static.

zz Память для переменной cwithin celsius() выделяется автоматически при вызове celsius() и освобождается при завершении celsius().

zz Память для переменной f в main() выделяется автоматически при вызове main() и освобождается при завершении main().

zz Память для результата celsius(f) неявно выделяется автоматически­.

zz Память, автоматически выделяемая для main(), освобождается при завершении функции.

Динамическое выделение памяти в C

Во многих случаях как статического, так и автоматического выделения памяти оказывается недостаточно. Например, в некоторых случаях программа не может вычислить требования к памяти во время компиляции, потому что они определяются пользовательским вводом.

В таких случаях память выделяется динамически. Динамическое выделение памяти основано на вызове API выделения памяти языка C. Операционные системы резервируют часть системной памяти для динамического выделения процессам. Эта часть памяти называется кучей (heap).

Книги для программистов: https://t.me/booksforits

166    Управление памятью

В следующем примере память выделяется динамически для массива со значениями температуры по Фаренгейту и Цельсию. Приложение вычисляет значения по шкале Цельсия, соответствующие заданным пользователем значениям по Фаренгейту:

cpython-book-samples 32 dynamic.c

#include <stdio.h> #include <stdlib.h>

static const double five_ninths = 5.0/9.0;

double celsius(double fahrenheit) {

double c = (fahrenheit - 32) * five_ninths; return c;

}

int main(int argc, char** argv) { if (argc != 2)

return -1;

int number = atoi(argv[1]);

double* c_values = (double*)calloc(number, sizeof(double)); double* f_values = (double*)calloc(number, sizeof(double)); for (int i = 0 ; i < number ; i++ ){

f_values[i] = (i + 10) * 10.0 ; c_values[i] = celsius((double)f_values[i]);

}

for (int i = 0 ; i < number ; i++ ){

printf("%f F is %f C\n", f_values[i], c_values[i]);

}

free(c_values); free(f_values);

return 0;

}

Если выполнить эту программу с аргументом 4, она выведет следующие значения:

100.000000 F is 37.777778 C

110.000000 F is 43.333334 C

120.000000 F is 48.888888 C

130.000000 F is 54.444444 C

Пример использует механизм динамического выделения блока памяти из кучи. Затем, когда надобность в блоке памяти отпадет, он возвращается в кучу. Если динамически выделенная память не будет освобождена, произойдет

утечка памяти.

Книги для программистов: https://t.me/booksforits