Uso de memoria en PHP
Erase una vez un programador web realizando un script relativamente complicado… Lo prueba en su maquina, funciona de maravilla. Lo sube al servidor de producción.
Craaash. Y ni siquiera un rastro de error. Revisa que los errores están habilitados, revisa los logs, y nada. Busca algún rastro y nada….
Mis poderes de psÃquico me dicen que es un error de memoria.
Estos errores son tÃpicos cuando se esta realizando un reporte con miles de filas, o mandando un mail con attachments pesados via php. De hecho lo que me motivo a realizar este post es que detecte un problema con cierto script famoso para enviar correos. EspecÃficamente encontré un error en una la función chunk_split en la que al trabajar con una cadena demasiado larga simplemente se niega a seguir trabajando y muere sin mandar ningun error.
Usualmente, PHP captura esos errores y nos lanza el ya tÃpico error “Allowed memory size of x bytes exhausted”. Esto es debido a que por precaución contra malos programadores, y especialmente en servidores compartidos, es posible limitar la memoria ocupada por cada script.
Para lidiar con estos problemas, he aquà unos tips básicos que harán tu experiencia un poco menos difÃcil.
- Revisa el uso de memoria de tus scripts
La función memory_get_usage te regresa el numero de bytes que tu script esta ocupando. Sin embargo esta función no esta disponible en todos los sistemas, ya que hay que compilar php con el parametro –enable-memory-limit. Si en tu servidor no existe esta función puedes emularla de la siguiente manera.
if( !function_exists('memory_get_usage') ){Es similar a la incluida en php, aunque no es tan exacta, pero te puedes dar una buena idea de lo que esta sucediendo. Requiere privilegios de ejecución, ya que utiliza el comando ps.
function memory_get_usage() {
exec("ps -orss -p ".getmypid(), $output);
return $output[1];
}
} - Cuando termines, no te olvides de limpiar
Si ya no utilizas una variable que contenga una fuerte cantidad de memoria, destrúyela con unset($variable). Esto liberara inmediatamente la memoria ocupada.
- DÃ no a las globales
Esta es una extensión de la anterior. Ten cuidado al usar variables globales. Cuando usas unset en una variable global adentro de una función solamente se destruye la variable local, no la global. Asà que una razón mas para NO usar variables globales. (como si hicieran falta
) - No confies en el barrendero
En general he notado que al finalizar una función, esta desactiva las variables locales. Sin embargo, no siempre es el caso o bien no se libera la memoria que estas variables usaron, por lo que cuando uses variables pesadas dentro de una función, desactivalas con unset antes de terminar el script si ya no haces uso de ellas.
- Olvida tan pronto como puedas
Si vas a utilizar por ejemplo, un archivo de muchas lineas, evita mantenerlo en memoria. Esto ultimo es lo mas facil pero puede provocar errores a la larga. En su lugar ve procesando el archivo linea por linea y escribe el resultado en un archivo. tmpfile y las funciones fopen, fclose y similares son tus amigos. Este consejo es el que mas te va a salvar la vida en estos casos, créeme.
- No ignores los avisos
Como recomendación general, cuando estés debuggeando scripts utiliza error_reporting con E_STRICT. Esto te dará un poco mas de información que E_ALL, aunque con el tiempo y conforme vayas mejorando tus habilidades no notaras la diferencia.
- Fuerza bruta
Como ultimo recurso, si nada de lo anterior funciona (que no he encontrado caso en que no funcione) es posible aumentar la memoria disponible para tu script, mediante la directiva ini_set(“memory_limit”, “16M”) la cual elevarÃa a 16 MB la capacidad de tu script. Esta directiva esta disponible en cualquier servidor que permita la ejecución de la funcion ini_set. Aunque se cuidadoso, especialmente si tienes un servidor compartido, se buen vecino y no acabes con el servidor. No te busques problemas gratis
Estos son solo unos cuantos consejos básicos para para esos casos difÃciles. Si este articulo tiene éxito, en algún tiempo pondré algunas técnicas un poquito mas a fondo sobre este tema.
Espero sus comentarios y sugerencias.
Si deseas contactarme puedes hacerlo en contacto@masio.com.mx o deja comentario en alguna de las entradas del blog. Tambien me encuentras en twitter en 
o deja comentario en alguna de las entradas del blog. Tambien me encuentras en twitter en
Leperpika
3 Apr, 2007
Muy interesante el post. Espero que sigas incluyendo muchos más. La verdad es que son pequeñas tonterÃas, pero hay veces que uno las desconoce o no piensa que el error pueda ser tan grande.
abur!
Roberto
11 Apr, 2007
Tengo un problema justo con el uso de memoria.
Al utilizar la funcion createimagefromjpeg() con el argumento de la ruta a una imagen de apenas 2M me da el siguiente mensaje:
Fatal error: Allowed memory size of 16777216 bytes exhausted (tried to allocate 9448 bytes) in /var/www/htdocs/pruebas.php on line 10
El limite de memoria que tiene asignado mi PHP es de 16M y en el script no hay otra lÃnea más que la del imagecreatefromjpeg() con cualquier otra imagen de menor tamaño funciona a la perfección.
Si tienen algún comentario para solucionar este problema será bienvenido.
Saludos