Gestión de memoria en PHP 7

Muy a menudo, los desarrolladores de PHP necesitan lidiar con una gran cantidad de datos. Si bien grande es un término relativo, la memoria no lo es. Ciertas combinaciones de funciones y construcciones de lenguaje, cuando se usan de manera irresponsable, pueden obstruir la memoria de nuestro servidor en cuestión de segundos.


Probablemente la función más notoria es file_get_contents(). Esta función fácil de usar literalmente toma el contenido de un archivo completo y lo guarda en la memoria. Para comprender mejor el problema, echemos un vistazo al siguiente ejemplo:

<?php
$content = file_get_contents('users.csv');
$lines = explode("\r\n", $content);
foreach ($lines as $line) {
$user = str_getcsv($line);
// Do something with data from $user…
}

Si bien este código es perfectamente válido y funciona, es un cuello de botella de rendimiento potencial. La variable $content extraerá el contenido de todo el archivo users.csv en la memoria. Si bien esto podría funcionar para un tamaño de archivo pequeño, digamos un par de megabytes, el código no está optimizado para el rendimiento. En el momento en que users.csv comience a crecer, comenzaremos a experimentar problemas de memoria.
¿Qué podemos hacer para mitigar el problema? Podemos repensar nuestro enfoque para resolver un problema.
En el momento en que cambiamos nuestra mente al modo de rendimiento de optimización óptima, otras soluciones se vuelven claras. En lugar de leer el contenido de un archivo completo en la variable, podemos analizar la línea del archivo de la siguiente manera:

<?php
if (($users = fopen('users.csv', 'r')) !== false) {
while (($user = fgetcsv($users)) !== false) {
// Do something with data from $user…
}
fclose($users);
}

En lugar de usar file_get_contents() y str_getcsv(), nos enfocamos en usar otro conjunto de funciones, fopen() y fgetcsv(). El resultado final es absolutamente el mismo, con el beneficio adicional de ser totalmente amigable con el rendimiento. Al utilizar funciones con identificadores, en este caso específico, hemos asegurado efectivamente que las limitaciones de memoria no son un problema para nuestro script.

El uso irresponsable de bucles es otra causa común de memoria:

<?php
$conn = new PDO('mysql:host=localhost;dbname=eelgar_live_magento',
'root', 'mysql');
$stmt = $conn->query('SELECT * FROM customer_entity');
$users = $stmt->fetchAll();
foreach ($users as $user) {
if (strstr($user['email'], 'test')) {
// $user['entity_id']
// $user['email']
// Do something with data from $user…
}
}

Ahora, sigamos adelante y veamos un ejemplo modificado y amigable con la memoria con el mismo efecto:

<?php
$conn = new PDO('mysql:host=localhost;dbname=eelgar_live_magento',
'root', 'mysql');
$stmt = $conn->prepare('SELECT entity_id, email FROM customer_entity WHERE
email LIKE :email');
$stmt->bindValue(':email', '%test%');
$stmt->execute();
while ($user = $stmt->fetch(PDO::FETCH_ASSOC)) {
// $user['entity_id']
// $user['email']
// Do something with data from $user…
}

El método fetchAll() es ligeramente más rápido que fetch(), pero requiere más memoria.
Cuando PHP alcanza el límite de memoria, detiene la ejecución del script y arroja el siguiente error:

Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to
allocate 2348617 bytes) …

Afortunadamente, la directiva memory_limit nos permite controlar la cantidad de memoria disponible.
El valor predeterminado de memory_limit es 128M, lo que implica 128 megabytes de memoria. La directiva es PHP_INI_ALL cambiable, lo que significa que, además de configurarlo a través del archivo php.ini, podemos configurarlo en tiempo de ejecución usando ini_set (‘memory_limit’, ‘512M’) ;.

Además de ajustar la directiva memory_limit, PHP proporciona las siguientes dos funciones que devuelven información sobre el uso de la memoria:

  • memory_get_usage(): devuelve la cantidad de memoria asignada actualmente por su script PHP
  • memory_get_peak_usage(): Esto devuelve la cantidad máxima de memoria asignada por nuestro script PHP

Si bien podríamos sentir la tentación de aumentar este valor, debemos pensarlo dos veces antes de hacerlo.
El límite de memoria es por proceso, no por servidor. Los propios servidores web pueden activar varios procesos. El uso de valores límite de memoria grandes puede por lo tanto obstruir nuestro servidor. Aparte de eso, cualquier script que pueda consumir una gran cantidad de memoria es fácilmente un candidato para la optimización del rendimiento. La aplicación de técnicas simples y pensadas a nuestro código puede reducir en gran medida el uso de la memoria.
Cuando se trata de la administración de memoria real, las cosas están bastante automatizadas aquí. A diferencia del lenguaje C, donde podemos administrar la memoria nosotros mismos, PHP usa la recolección de basura en combinación con un mecanismo de conteo de referencias. Sin entrar en los entresijos del mecanismo en sí, es suficiente decir que las variables se liberan automáticamente cuando ya no se utilizan.

Para obtener más detalles sobre la recolección de basura, consulte http://php.net/manual/en/features.gc.php.

Comparte