Автор: Ван Шу
Предыстория введение
- Строковый тип также является широко используемым типом. Из-за особенностей строк, в целях экономии памяти, одни и те же строковые переменные обычно совместно используют блок памяти. При подсчете ссылок несколько переменных помечаются для использования этой памяти.
- Однако после отслеживания GDB обнаруживается, что не все строки являются нормальными в подсчете ссылок на операции, с нормальным накоплением, иногда 0, иногда 1. Чтобы выяснить это, в этой статье просто анализируются различные ситуации с назначением.
Экологическая ситуация
- Версия системы: Ubuntu 16.04.3 LTS
- Версия PHP: PHP 7.1.0
- Версия GDB: GNU GDB (Ubuntu 7.11.1-0ubuntu 1 ~ 16.5) 7.11.1
1, Базовая переменная
Zval является основой всех переменных в PHP. (строка 121 из zend_type. H)
Где zend_value хранит конкретные данные, а структура показана на рисунке: (zend_type. Линия H 101)
- Zend_value-это объединение, которое занимает 8 байт в целом.
- U1-это объединение, которое хранит необходимые данные, требуемые типом, и занимает до 4 байт.
- Бит U2 – это объединение, в котором хранятся некоторые дополнительные данные, такие как следующая коллизия хэшей, которая занимает 4 байта.
Вся структура zval, занимающая 16 байт, поддерживает все типы PHP.
Php7 использует такой простой и гениальный zval для хранения всех типов данных, так как же строка неопределенной длины может храниться в 16-байтовом zval?
2, Строковая переменная
Благодаря отладке GDB вы можете увидеть:
Тип, по сравнению с определением типа, вы можете видеть, что тип IS_STRING (строка zend_type. H 303)
Поскольку длина нашей строки не обязательно равна, 16 байтов zval сами по себе не могут быть сохранены напрямую, поэтому мы указываем на адрес памяти реальной строки хранения через STR в значении. При печати мы видим, что тип адреса zend_string
1. Структура Zend_string
Сначала взгляните на его структуру данных, как показано на рисунке (строка 169 zend_type. H)
GC в структуре zend_string Первым заголовком является GC. Вы можете увидеть другие сложные типы. У головы есть GC. В чем его функция? Посмотрите на структуру данных GC, как показано на рисунке:
- Первый-это refcount, который записывает количество ссылок.
- Второй u-это консорциум, который очень похож на U1 zval. Ключ состоит в том, чтобы записать тип.
Так что лучше догадаться. Когда программа выполняет GC или другие операции, для любого сложного типа головкой указателя является GC, которая не только имеет счетчик ссылок, но также может определять реальный тип сложного типа с помощью u.v.типа.
H в структуре zend_string Из названия мы можем догадаться, что это хэш строки, идея пространства для времени, чтобы сохранить вычисленный хэш и повысить производительность.
В zend_string Очевидно, что в нем хранится длина строки.
Val [1] в zend_string Этот метод записи представляет собой гибкий массив на языке Си, в котором хранится вся строка. Таким образом, адрес памяти строки тесно связан с адресом памяти структуры, что сокращает время получения значения из другого блока памяти. (PS: оставьте небольшой вопрос, и GDB сможет его отследить. Занимает ли гибкий массив место в памяти? Какова структура строки Zend после выравнивания? Сколько она занимает в целом
2. Фактическое содержимое Zend_string
После понимания самой структуры вы можете распечатать содержимое, чтобы посмотреть, как показано на рисунке
То, что хранится в этом адресе, на самом деле является Hello world. Почему значение refcount 0 в GC?
Причины заключаются в следующем:
- Постоянная строка, фиксированная строка в PHP-коде, хранится в таблице глобальных переменных на этапе компиляции, также известной как литеральный масштаб. Он будет уничтожен только после завершения запроса, поэтому количество ссылок всегда равно 0.
- Временная строка, которая возникает, когда виртуальная машина выполняет код операции, хранится в области временных переменных и имеет обычный счетчик ссылок.
Измените код, посмотрите на временную строку
Выведите значение zval этой переменной, значение refcount равно 1, как показано на рисунке
3, Количество ссылок для строки
1. Прямое назначение временной строки
For temporary strings, it should be the reference count + 1 in zend_string for each variable assigned, and release this memory when the reference count is 0.
When the assignment of $a is completed, $a is in the first position on the stack. The type is 6, is_string. Take the STR in value and the address is * * 0x7ff4402c30 * *. You can see the content. The zend_string reference count is 1.
When the assignment of $B is completed, the second position of $B on the stack is type 6, is_string. The same address of STR in value is * * 0x7ff4402c30 * *, and the reference count of zend_string is 2.
The general quotation can be drawn as follows:
2. Задание ссылки
For direct variable assignment, the reference relationship has been drawn above. What about the reference type?
When the assignment of $a is completed, $a is in the first position on the stack. The type is 6, is_string. Take the STR in value and the address is * * 0x7ff4402c30 * *. You can see the content. The zend_string reference count is 1.
When $B is assigned as a reference type, $B is in the second position on the stack, the type is 10, is "reference, and you can see the content by taking ref in value.
Does the type of $a change at this time? Is it a string type? Just print $a and have a look. At this time, the type of $a becomes 10. Is "reference. Print ref in value. The address is the same as ref in $B!
When $B references $a, both $a and $B become reference types. The reference type points to a zval in, the type is 6, the STR in is string, the value points to a Zend string, and the Zend string reference count is 1
The general reference is shown in the figure:
4, Специальное значение строковой переменной
В нашем заключении, и $a, и $B принадлежат постоянным строкам.
Выведите строку zend_string $a, как показано на рисунке
Выведите строку zend_string $B, как показано на рисунке
Можно видеть, что $B соответствует ожиданиям, но $a опровергает вышеприведенную теорию. В чем проблема?
После отслеживания GDB вы можете увидеть, что a и B находятся в стеке, и оба имеют строковый тип. Однако адрес str по значению совсем другой. Давайте сначала посмотрим на переменную a В первой позиции стека, значение STR равно 0x11522c0
Во-вторых, переменная b На второй позиции в стеке, значение str равно 0x7ff4401880
Если вы знаете распределение памяти PHP, вы можете видеть, что строка B выделена на фрагменте 0x7ffff440000, который принадлежит первой странице, 0x7ff4401000
Строка a, очевидно, не является этим правилом. Он присваивается не куску, а совершенно особому адресу. Таким образом, строка не выделяется “malloc”.
Итак, глупым образом я включаю 0x11522c0 (zend_string *) 0x11522c0 и смотрю, когда в него вводится значение.
PHP версии 7.1.0 Первый узел: Строка 1345 в php_cli. C
sapi_module->startup(sapi_module)
Второй узел: Строка 424 в php_cli. C
php_module_startup(sapi_module, NULL, 0)
Третий узел: Строка 2123 в main. C
zend_startup(&zuf, NULL);
Четвертый узел: Строка 768 в Zend. C
zend_interned_strings_init();
Это закрывает пятый узел: 103 в zend_string. C
zend_intern_known_strings(known_strings, (sizeof(known_strings)
Распечатайте здесь, know_strings. Вы можете видеть, что файл, строка, функция, класс, объект и т.д. и строка инициализированы здесь!
Соответствующий объявленный адрес находится в строке zend_string. H 383
Литералы еще не были инициализированы, поэтому эти строки не совпадают с литералами.
Резюме
В PHP также существует множество различных вариантов строк.
- 1. Для непосредственно жестко закодированной строки в коде в буквальном масштабе количество ссылок всегда равно 0, и оно не будет уничтожено до тех пор, пока не будет выполнен весь сценарий.
- 2. Когда строка, временная строка и количество ссылок, рассчитанные на этапе выполнения, рассчитываются нормально, для каждой ссылки будет добавлено 1. Восстановите память, когда количество ссылок равно 0.
- 3. Строка ссылочного типа. Количество ссылок на несколько переменных вычисляется на zend_reference. На строку ссылается zend_reference с числом ссылок 1.
- 4. Специальные строки, созданные во время инициализации PHP, не будут уничтожены до тех пор, пока не будет выполнен весь скрипт. Количество ссылок всегда равно 1.