предисловие
Когда я недавно просматривал материалы по сборке мусора PHP7, некоторые примеры кода в Интернете показывали разные результаты при работе в локальной среде, что на некоторое время озадачило меня. Если хорошенько подумать, найти проблему несложно: большинство этих статей относятся к эпохе PHP5.x, а после выпуска PHP7 принята новая структура zval, и релевантной информации относительно мало, поэтому я объединил некоторую информацию, чтобы сделать резюме,Основное внимание уделяется объяснению механизма подсчета ссылок в новом контейнере zval., Если есть какие-либо ошибки, пожалуйста, сообщите.
Новая структура zval в PHP7
Люди Мин не говорят секретных слов, сначала посмотрите на код!
struct _zval_struct {
union {
zend_long lval; /* long value */
double dval; /* double value */
zend_refcounted *counted;
zend_string *str;
zend_array *arr;
zend_object *obj;
zend_resource *res;
zend_reference *ref;
zend_ast_ref *ast;
zval *zv;
void *ptr;
zend_class_entry *ce;
zend_function *func;
struct {
uint32_t w1;
uint32_t w2;
} ww;
} value;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type, /* active type */
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved) /* call info for EX(This) */
} v;
uint32_t type_info;
} u1;
union {
uint32_t var_flags;
uint32_t next; /* hash collision chain */
uint32_t cache_slot; /* literal cache slot */
uint32_t lineno; /* line number (for ast nodes) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
uint32_t fe_iter_idx; /* foreach iterator index */
} u2;
};
За подробным описанием структуры вы можете обратиться к статье Брата Берда в конце статьи.Она написана очень подробно.Я не буду играть большими мечами перед Гуан Гонгом.Здесь я лишь предлагаю несколько ключевых моментов:
- Переменные в PHP7 делятся наимя переменнойа такжеПеременнаядве части, соответствующие
zval_struct
и объявлено в немvalue
-
zval_struct.value
серединаzend_long
,double
обепростой тип данных, которые могут напрямую хранить определенные значения, в то время как другие сложные типы данных хранят указатель на другие структуры данных.указатель - В PHP7 счетчики ссылок хранятся в
value
вместоzval_struct
-
NULL,логическийпринадлежитневажнотип данных (где передается логическое значение
IS_FALSE
а такжеIS_TRUE
две константы), естественно счетчика ссылок нет - Цитировать(REFERENCE) стал структурой данных, а не просто битом флага, и его структура выглядит следующим образом:
struct _zend_reference {
zend_refcounted_h gc;
zval val;
}
-
zend_reference
в видеzval_struct
один из включенныхvalue
тип, тоже есть свойval
значение, это значение указывает наzval_struct.value
из. у всех своисчетчик ссылок.
Счетчик ссылок используется для отслеживания того, сколько в настоящее время
zval
указать на то жеzend_value
.
Для шестого пункта см. следующий код:
$a = 'foo';
$b = &$a;
$c = $a;
Структура данных на данный момент выглядит следующим образом:
$a и $b имеют по одномуzval_struct
контейнер, иvalue
оба указывают на одно и то жеzend_reference
структура,zend_reference
встроенныйval
структура, указывающая на то же самоеzend_string
,содержимое строкихранится в нем.
И $c также имеетzval_struct
, и его значение может прямо указывать на указанное вышеzend_string
, чтобы при копировании не возникало дублирования.
Поговорим о новомzval
В структуре будут появляться различные явления и причины этих явлений.
вопрос
1. Почему начальное значение счетчика ссылок некоторых переменных равно 0
Феномен
$var_int = 233;
$var_float = 233.3;
$var_str = '233';
xdebug_debug_zval('var_int');
xdebug_debug_zval('var_float');
xdebug_debug_zval('var_str');
/** 输出 **
var_int:
(refcount=0, is_ref=0)int 233
var_float:
(refcount=0, is_ref=0)float 233.3
var_str:
(refcount=0, is_ref=0)string '233' (length=3)
**********/
причина
В PHP7 при присвоении значения переменной операция состоит из двух частей:
- Подать заявку на символическое количество (т.е. имя переменной)
zval_struct
структура - сохранить значение переменной в
zval_struct.value
середина заzval
существуетvalue
Значения, которые могут быть сохранены в поле, не будут считаться ссылками на них,Вместо этого назначьте его непосредственно при копировании, типы этой детали:
- IS_LONG
- IS_DOUBLE
то есть наш в PHPпластика такжеплавающая точка.
Так почему же счетчик ссылок var_str также равен 0?
Это включает в себя два типа строк в PHP:
-
interned string
Внутренние строки (имена функций, имена классов, имена переменных, статические строки):
$str = '233'; // 静态字符串
- Обычная строка:
$str = '233' . time();
завнутренняя строкаЧто касается содержимого строки, то оно уникально и неизменно, что эквивалентно строке, определенной в области статических переменных языка C.Их жизненный цикл существует в течение всего периода запроса, и они будут уничтожены и освобождены после выполнения запроса., и естественно нет необходимости в управлении памятью через подсчет ссылок.
2. Почему значение счетчика напрямую изменяется на 2 при присвоении ссылки на целочисленную переменную, переменную с плавающей запятой и статическую строковую переменную?
Феномен
$var_int_1 = 233;
$var_int_2 = &var_int;
xdebug_debug_zval('var_int_1');
/** 输出 **
var_int:
(refcount=2, is_ref=1)int 233
**********/
причина
Помните, что мы сказали в началеzval_struct
серединаvalue
структура данных, при присвоении переменнойпластик,плавающая точкаилистатическая строказначение типа, тип данных значенияzend_long
,double
илиzend_string
, то значение может быть сохранено непосредственно в value. При копировании по значению создается новыйzval_struct
Значения сохраняются в value того же типа данных таким же образом, поэтому значение refcount всегда будет равно 0.
но при использовании&
Иная ситуация, когда оператор делает копию ссылки:
- PHP это
&
Переменная, управляемая оператором, применяется дляzend_reference
структура - будет
zend_reference.value
указать на оригиналzval_struct.value
-
zval_struct.value
Тип данных будет изменен наzend_refrence
- будет
zval_struct.value
Укажите на только что примененный и инициализированныйzend_reference
- подать заявку на новые переменные
zval_struct
структуру, будет ли егоvalue
указывает на только что созданныйzend_reference
На данный момент: и $var_int_1, и $var_int_2 имеютzval_struct
структуры и ихzval_struct.value
все указывают на одно и то жеzend_reference
структура, поэтому счетчик ссылок для этой структуры равен 2.
Отступление: zend_reference указывает на целое число или значение с плавающей запятой.Если указанный тип значения — zend_string, значение счетчика ссылок на значение равно 1. Счетчик ссылок из xdebug показывает значение счетчика zend_reference (т.е. 2)
3. Почему значение счетчика ссылок исходного массива равно 2
Феномен
$var_empty_arr = [1, 2, '3'];
xdebug_debug_zval('var_empty_arr');
/** 输出 **
var_arr:
(refcount=2, is_ref=0)
array (size=3)
0 => (refcount=0, is_ref=0)int 1
1 => (refcount=0, is_ref=0)int 2
2 => (refcount=1, is_ref=0)string '3' (length=1)
**********/
причина
Это включает в себя другую концепцию в PHP7, называемуюimmutable array
(неизменяемый массив).
For arrays the not-refcounted variant is called an "immutable array". If you use opcache, then constant array literals in your code will be converted into immutable arrays. Once again, these live in shared memory and as such must not use refcounting. Immutable arrays have a dummy refcount of 2, as it allows us to optimize certain separation paths.
Неизменяемый массивдаopcache
Тип массива, оптимизированный по расширению. Проще говоря, все массивы, результаты которых остаются постоянными после нескольких компиляций, будут оптимизированы какНеизменяемый массив, ниже приведен встречный пример:
$array = [1, 2, time()];
PHP не может знать во время компиляцииtime()
возвращаемое значение функции, поэтому $array здесьизменяемый массив.
Неизменяемый массивкак мы упоминали вышевнутренняя строкато же самое, обаНе используйте подсчет ссылок, но разница в том, что значение счетчика внутренней строки всегда равно 0, а неизменяемый массив будет использоватьПсевдосчетное значение2.
Суммировать
- простой тип данных
- Целое число (без подсчета ссылок)
- float (не использует подсчет ссылок)
- логический (не использует подсчет ссылок)
- NULL (не использовать подсчет ссылок)
- сложные типы данных
- нить
- обычная строка (использует подсчет ссылок, начальное значение равно 1)
- Внутренняя строка (счетчик ссылок не используется, значение счетчика ссылок всегда равно 0)
- множество
- Обычный массив (с использованием подсчета ссылок, начальное значение равно 1)
- Неизменяемые массивы (не используйте подсчет ссылок, используйте значение псевдосчетчика, равное 2)
- Объект (со счетчиком ссылок, начальное значение равно 1)
- нить
использованная литература
- «Анализ ядра PHP7» (Цинь Пэн)
- php7-internal
- Confusion about PHP 7 refcount