How to recover from a fatal error "Allowed memory size exhausted"
Asked Answered
S

8

13

Do you know any solution to recover from the PHP fatal error : "Allowed memory size ... exhausted"

I have a shutdown function that is called when a fatal error appear. This function create an ErrorException from it, and logs it.

The problem is : when there is no more memory available, it can't log the error (I log in Firebug, via FirePHP, with Zend Framework).

So what i mean by "how to recover from it", is how to perform basic error log, and let Zend Framework send the Headers, so that the error is logged (in Firebug in my case) as any other error ?

Thanks

Sextuple answered 23/2, 2010 at 14:32 Comment(0)
I
8

This error is a fatal error - that means you cannot recover from it. If PHP has hit it's memory limit, it won't be able to allocate any more memory to create your exception and any other memory it needs to carry on its execution.

There is another type of error - "catchable fatal error" which as the name suggests, can be caught in a try/catch, but unfortunately the memory size allocation is not one of them.

Individuality answered 23/2, 2010 at 14:43 Comment(8)
Is that possible to expand the memory limit during the execution ? (just to get enough memory to finish the rendering of the site) I suppose this is not possible to list all the objects in memory, and "kill" the one that is too big, like I could with processes ? (I know this is asking for the moon, but who knows)Sextuple
Don't believe so. If it's one particular thing that's running into this problem, then you might just have to avoid the issue and raise the memory limit (for that script). Which isn't what you're wanting, but I don't believe you have any other choice.Yingyingkow
No 'fraid not. Just out of curiosity, how much is your current memory limit, and what sort of website is it? I'm not sure what the recommended amount is, but I tend to set it to 1/4 of the RAM on the machine. Perhaps at key points in your script you could call memory_get_usage and if it's getting high, increase the memory_limit setting before it trips? Not sure without knowing your script's layout and purpose.Individuality
Looking at the example on the manpage (php.net/manual/en/function.memory-get-usage.php) it looks like calling that function triggers the garbage collector, so maybe unsetting some un-needed variables before calling that function may help?Individuality
The limit is actually 128M. But currently I have about 10 Exceptions (that I know of, and that will be fixed, but until then they have no impact on the website) that are logged to Firebug via an exception handler. So maybe this is what is causing the memory usage (an Exception seems to be a very big object). If that is possible to extend the memory limit, i'll do so ? (just a bit, to flush the headers). The garbage collector seems a good idea too.Sextuple
Please refer to gordons answerKaikaia
@MatthieuNapoli @BlairMcMillan ini_set('memory_limit', '512M') can be sent mid-execution to quadruple the default memory limit.Ritaritardando
Gotta love the "you can't" answers.Desiredesirea
B
9
if((memory_get_usage() / 1024 /1024) < 70)

I simply divide the memory_get_usage by 1024 squared to compare it to a 'normal' megabyte value of '70'.

I ran into memory problems with php inside a for loop and wrote this simple if statement to prevent my script from setting off a fatal error. In addition, the server I was operating on didn't allow me to modify the memory limit (this is often the case in some cloud offerings like openshift or large web hosts like dreamhost.) I didn't really notice any serious performance degradations (in php 5.3 which may handles functions such as this slightly differently than php 4.x or 5.x... at any rate the performance implication of a script giving a fatal error outweighs any overhead the function call may force. And would also prevent a runaway script from consuming all available ram.

Many may argue; oh hey your software isn't optimized. Yes. You're probably right; but with complex data sets you can only squeeze so much performance out before you need to throw more memory at it; and hunting down memory errors in an ajax flow can be very frustrating; especially when you aren't sure where your log files are.

Botzow answered 20/2, 2013 at 23:38 Comment(2)
great answer! just what i was looking for... should have focused less on telling a personal story an more on the actual solution though. Curious to know what you put in the conditional.Coady
this doesn't work for me in php 7. It's always 60mbForfend
I
8

This error is a fatal error - that means you cannot recover from it. If PHP has hit it's memory limit, it won't be able to allocate any more memory to create your exception and any other memory it needs to carry on its execution.

There is another type of error - "catchable fatal error" which as the name suggests, can be caught in a try/catch, but unfortunately the memory size allocation is not one of them.

Individuality answered 23/2, 2010 at 14:43 Comment(8)
Is that possible to expand the memory limit during the execution ? (just to get enough memory to finish the rendering of the site) I suppose this is not possible to list all the objects in memory, and "kill" the one that is too big, like I could with processes ? (I know this is asking for the moon, but who knows)Sextuple
Don't believe so. If it's one particular thing that's running into this problem, then you might just have to avoid the issue and raise the memory limit (for that script). Which isn't what you're wanting, but I don't believe you have any other choice.Yingyingkow
No 'fraid not. Just out of curiosity, how much is your current memory limit, and what sort of website is it? I'm not sure what the recommended amount is, but I tend to set it to 1/4 of the RAM on the machine. Perhaps at key points in your script you could call memory_get_usage and if it's getting high, increase the memory_limit setting before it trips? Not sure without knowing your script's layout and purpose.Individuality
Looking at the example on the manpage (php.net/manual/en/function.memory-get-usage.php) it looks like calling that function triggers the garbage collector, so maybe unsetting some un-needed variables before calling that function may help?Individuality
The limit is actually 128M. But currently I have about 10 Exceptions (that I know of, and that will be fixed, but until then they have no impact on the website) that are logged to Firebug via an exception handler. So maybe this is what is causing the memory usage (an Exception seems to be a very big object). If that is possible to extend the memory limit, i'll do so ? (just a bit, to flush the headers). The garbage collector seems a good idea too.Sextuple
Please refer to gordons answerKaikaia
@MatthieuNapoli @BlairMcMillan ini_set('memory_limit', '512M') can be sent mid-execution to quadruple the default memory limit.Ritaritardando
Gotta love the "you can't" answers.Desiredesirea
J
5

The regular way to customize error handling is through

set_error_handler — Sets a user-defined error handler function

The docs for this function state (emphasis mine):

The following error types cannot be handled with a user defined function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and most of E_STRICT raised in the file where set_error_handler() is called.

So, it won't work regularly, but you can try

As of PHP7, Errors and Exceptions are Throwables, so you can try/catch them:

Jenevajeni answered 23/2, 2010 at 14:51 Comment(4)
register_shutdown_function is the solution I am currently using, and it works fineSextuple
@Matthieu Cool to know. Happy I could help.Jenevajeni
register_shutdown_function does not work fine. It only allows you to run your cleanup functions. If you decide to try to continue execution within register_shutdown_function and you hit another fatal error php will terminate straight (most likely with a 500 internal server error).Abandoned
#8440939Kaikaia
S
3

This worked fine for me:

try {
    ini_set('memory_limit', (ini_get('memory_limit')+1).'M');
} catch(Exception $e) {}

This assumes your memory limit is in the format 123M.

Shack answered 23/11, 2011 at 14:28 Comment(6)
When do you execute this code? Is it when you get the fatal error?Sextuple
In the shutdown function, right after determinig that error_get_last() is not empty.Shack
why do you append the 'M' on ini_get() method?Bullheaded
memory_limit is a string that typically has an integer part and a character at the end which specifies the units (like K or M). This code assumes you know the unit is M; doing +1 on it will cause an implicit conversion to integer (ie. the M at the end is dropped) so I needed to re-add it. If you want to handle all possible memory limit values, the code will be a bit more complex (see the php.net example).Shack
Are you tested this solution at a real project that use more memory while shutdown? (i think it may cant do it because of ini_get,plus,concat needs more memory)Embryo
@Embryo only a trivial amount. If you are concerned about it, you can reserve some buffer like done e.g. by MediaWiki.Shack
L
2

PHP errors are sent by default to your apache error log /path/to/apache/logs/error.log and you can see it there.

Larainelarboard answered 23/2, 2010 at 14:36 Comment(2)
Please forgive me; I appreciate your help, but I believe I was not clear in my question. I log to various destinations, I can read the error, I can do a var_dump, but I don't want to, this is a professional website. I want to be able to log the error and flush the headers once this error appears.Sextuple
"The problem is : when there is no more memory available, it can't log the error" It sounded like you wanted to log the error.Larainelarboard
T
2

Got an idea for an untested trick and I'd be happy to know if it helped. Allocate some global variable when you first register the shutdown function and release it when the shutdown function's code is first executed. You may then have sufficient memory to create the Exception object. Let me know if it worked and please publish the code here.

Taper answered 22/4, 2010 at 17:28 Comment(2)
this does work, just use get_peak_memory_usage before and after your exception call to figure out how much memory you need to allocate and set a variable of that size in $GLOBALS, then unset it at the top of the shutdown functionContraposition
@profitphp: the correct syntax is memory_get_peak_usage() :) see php.net/manual/en/function.memory-get-peak-usage.phpToritorie
O
1

One I can think of is that you when you're doing your memory intensive operation you manually query memory_get_usage() on a regular basis (e.g. every loop iteration) and dump out your headers/error when it goes over some failsafe value which is below the script limit. It will slow your script down a lot, but at least you'll get something back.

Or, and you may not be able to do this, run the memory intensive stuff as a CLI-based script called from inside your web-based stuff by using exec. The CLI part might fall over, but the web part will be able to report on it.

Obaza answered 23/2, 2010 at 15:5 Comment(2)
Thanks, I will check out the get_memory_usage() function, but I want a generic solution for this problem, this is not about a specific page that requires a lot of memory (I use CLI scripts, as you said, for that). So I can't check get_memory_usage() on a regular basis.Sextuple
Oops, it's memory_get_usage(), has been ages since I used that one.Obaza
F
0

I just have found, for my case, if you fill an array with big int keys (83253143401 for example), cast them to String. Otherwise PHP will init empty array with 83253143400 first elements, and even 2Gb of memory will be not enough.

Forfend answered 23/7, 2024 at 21:16 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.