php - punteros - ¿Tiene(cadena) ''copia dura'' una cadena?
punteros en php (3)
PHP utiliza un sistema de copia en modificación.
Hace $a = (string) $a;
($ a es una cadena ya) ¿Modificar y copiar algo?
Especialmente, este es mi problema:
El parámetro 1 está mixed
/ quiero permitir pasar cadenas y convertirlas en cadenas.
Pero a veces estas cuerdas son muy grandes. Así que quiero omitir la copia de un parámetro, que ya es una cadena.
¿Puedo usar la versión Foo
o tengo que usar la Bar
versiones?
class Foo {
private $_foo;
public function __construct($foo) {
$this->_foo = (string) $foo;
}
}
class Bar {
private $_bar;
public function __construct($bar) {
if (is_string($bar)) {
$this->_bar = $bar;
} else {
$this->_bar = (string) $bar;
}
}
}
La respuesta es que sí, sí copia la cadena. Algo así como ... en realidad no. Bueno, depende de tu definición de "copia" ...
> = 5.4
Para ver qué está pasando, echemos un vistazo a la fuente. El ejecutor maneja una variable emitida en 5.5 aquí .
zend_make_printable_zval(expr, &var_copy, &use_copy);
if (use_copy) {
ZVAL_COPY_VALUE(result, &var_copy);
// if optimized out
} else {
ZVAL_COPY_VALUE(result, expr);
// if optimized out
zendi_zval_copy_ctor(*result);
}
Como puede ver, la llamada utiliza zend_make_printable_zval()
que solo cortocircuita si el zval ya es una cadena.
Así que el código que se ejecuta para hacer la copia es (la rama else):
ZVAL_COPY_VALUE(result, expr);
Ahora, veamos la definición de ZVAL_COPY_VALUE
:
#define ZVAL_COPY_VALUE(z, v) /
do { /
(z)->value = (v)->value; /
Z_TYPE_P(z) = Z_TYPE_P(v); /
} while (0)
Tenga en cuenta lo que está haciendo. La cadena en sí NO se copia (que se almacena en el bloque de ->value
del zval). Solo se hace referencia (el puntero permanece igual, por lo que el valor de la cadena es el mismo, no hay copia). Pero está creando una nueva variable (la parte zval que envuelve el valor).
Ahora, entramos en la llamada zendi_zval_copy_ctor
. Que internamente hace algunas cosas interesantes por su cuenta. Nota:
case IS_STRING:
CHECK_ZVAL_STRING_REL(zvalue);
if (!IS_INTERNED(zvalue->value.str.val)) {
zvalue->value.str.val = (char *) estrndup_rel(zvalue->value.str.val, zvalue->value.str.len);
}
break;
Básicamente, eso significa que si es una cadena internada, no se copiará. pero si no lo es, se copiará ... Entonces, ¿qué es una cadena interna y qué significa eso?
<= 5.3
En 5.3, las cadenas internas no existían. Así que la cadena siempre se copia. Esa es realmente la única diferencia ...
Tiempo de referencia:
Bueno, en un caso como este:
$a = "foo";
$b = (string) $a;
Ninguna copia de la cadena ocurrirá en 5.4, pero en 5.3 se producirá una copia.
Pero en un caso como este:
$a = str_repeat("a", 10);
$b = (string) $a;
Se producirá una copia para todas las versiones. Eso es porque en PHP, no todas las cadenas están internadas ...
http://3v4l.org/HEelW en un punto de referencia: http://3v4l.org/HEelW
$a = "foobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisout";
$b = str_repeat("a", 300);
echo "Static Var/n";
testCopy($a);
echo "Dynamic Var/n";
testCopy($b);
function testCopy($var) {
echo memory_get_usage() . "/n";
$var = (string) $var;
echo memory_get_usage() . "/n";
}
Resultados:
5.4 - 5.5 alfa 1 (sin incluir otros alfas, ya que las diferencias son lo suficientemente pequeñas como para no hacer una diferencia fundamental)
Static Var 220152 220200 Dynamic Var 220152 220520
Así que la var estática aumentó en 48 bytes, y la var dinámica aumentó en 368 bytes.
5.3.11 a 5.3.22:
Static Var 624472 625408 Dynamic Var 624472 624840
La var estática aumentó en 936 bytes, mientras que la var dinámica aumentó en 368 bytes.
Tenga en cuenta que en 5.3, se copiaron las variables estáticas y dinámicas. Así que la cadena siempre está duplicada.
Pero en 5.4 con cadenas estáticas, solo se copió la estructura zval. Lo que significa que la cadena, que fue internada, sigue siendo la misma y no se copia ...
Otra cosa
Otra cosa a tener en cuenta es que todo lo anterior es discutible. Estás pasando la variable como un parámetro a la función. Entonces estás echando dentro de la función. Así que la copia en escritura será activada por su línea. Entonces, ejecutar eso siempre (bueno, en el 99.9% de los casos) desencadenará una copia variable. Entonces, en el mejor de los casos (cadenas internas), se trata de una duplicación de zval y una sobrecarga asociada. En el peor de los casos, estás hablando de una duplicación de cadena ...
Sorprendentemente, crea una copia:
$string = "TestMe";
debug_zval_dump($string);
$string2 = $string;
debug_zval_dump($string);
$string3 = $string;
debug_zval_dump($string);
$string4 = (string) $string;
debug_zval_dump($string);
$string5 = (string) $string;
debug_zval_dump($string);
Salida:
string(6) "TestMe" refcount(2)
string(6) "TestMe" refcount(3)
string(6) "TestMe" refcount(4)
string(6) "TestMe" refcount(4)
string(6) "TestMe" refcount(4)
Otra prueba:
echo memory_get_usage(), PHP_EOL;
$s = str_repeat(''c'', 100000);
echo memory_get_usage(), PHP_EOL;
$s1 = $s;
echo memory_get_usage(), PHP_EOL;
$s2 = (string) $s;
echo memory_get_usage(), PHP_EOL;
Salida:
627496
727664
727760 # small increase, new allocated object, but no string copy
827928 # oops, we copied the string...
Tu código en realidad no lo hace:
$a = (string)$a;
Es más parecido a esto, debido a que la semántica de copia en escritura se aplica cuando la cadena se pasa como un argumento de función:
$b = (string)$a;
Hay una gran diferencia entre esas dos afirmaciones. El primero no tendrá ningún impacto en la memoria, mientras que el segundo sí ... normalmente.
El siguiente código hace aproximadamente lo que su código haría; se pasa alguna cadena y usted la convierte y la asigna a otra variable. Hace un seguimiento de los aumentos en la memoria.
<?php
$x = 0;
$y = 0;
$x = memory_get_usage();
$s = str_repeat(''c'', 1200);
$y = memory_get_usage();
echo $y - $x, PHP_EOL;
$s1 = (string)$s;
$x = memory_get_usage();
echo $x - $y, PHP_EOL;
1360
1360
1368
1368
La asignación básicamente copia todo el valor de la cadena.
Usando cadenas literales
Cuando se usa una cadena literal, el comportamiento depende de la versión:
<?php
$x = 0;
$y = 0;
$x = memory_get_usage();
$s = ''cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'';
$y = memory_get_usage();
echo $y - $x, PHP_EOL;
$s1 = (string)$s;
$x = memory_get_usage();
echo $x - $y, PHP_EOL;
152
136
1328
1328
La razón es que los literales de una cadena son tratados de manera diferente por el motor, como se puede leer en la respuesta de ircmaxell .