On Tue, Sep 24, 2019 at 5:39 AM Peter Stalman <sarke...@gmail.com> wrote:

> On Tue, Sep 24, 2019 at 12:01 AM Bishop Bettini <bis...@php.net> wrote:
> >
> > On Tue, Sep 24, 2019 at 2:26 AM Peter Stalman <sarke...@gmail.com>
> wrote:
> >>
> >> When PHP runs out of memory, a fatal error is triggered and whatever
> shutdown
> >> functions or error handlers take over.
> >>
> >> However, in the case of error logging, or just logging in general,
> there often
> >> needs to be additional memory used to accommodate the final logging
> process.
> >>
> >> This can sort of be accomplished in userland a few ways:
> >>
> >> 1. Pre-allocating memory in a variable, such as the Yii2 error handler
> >> (http://bit.ly/2kLnpd2), but this requires wasting memory on every
> request.
> >>
> >> 2. Continuously checking the memory usage, but this increases code
> complexity
> >> needlessly and also wastes resources with constant checking.
> >>
> >> 3. A second process with its own memory allowance, but this also
> increases
> >> complexity and transferring the required data for logging would require
> >> serialization without using additional memory.  I'm not sure how this
> would be
> >> accomplished.
> >>
> >> So I would like to suggest an option for setting a shutdown memory
> allowance,
> >> which would be the amount of additional memory allowed to be used by any
> >> registered error handlers or shutdown functions.
> >>
> >> I think a C implementation of this in PHP would be far more efficient
> than the
> >> userland implementionations I mentioned.
> >
> >
> > Memory parachutes help when against a hard limit, but I'm not sure
> they're applicable in the soft limit scenario you describe. When the OOM
> condition fires, PHP resets its soft tracking of memory. You are free to
> allocate as much on the stack of the shutdown function, up to the soft
> limit again.
> >
> > Here[1] we see the shutdown function called because of OOM, then we
> allocate a local variable that consumes less than the soft limit (which is
> ok), while here[2] we see the shutdown function itself is constrained to
> the soft limit (it's killed b/c of OOM).
> >
> > Perhaps I am misunderstanding the scenario. Could you elaborate further,
> perhaps provide a concrete example demonstrating where a parachute would be
> needed?
> >
> > [1]:https://3v4l.org/bXMlX
> > [2]:https://3v4l.org/HDkRc
>
> Hi Bishop,
>
> Thanks for your reply.  However, I do not think your statement that the
> memory
> limit resets is correct.  At least, not in a practical real-world sense,
> as the
> logger (or perhaps other shutdown functions as well) need to be able to
> access
>
> Your examples do not really work for a few reasons.  I don't think 2048 is
> a
> valid value for the memory limit, as the output in your examples show it is
> 2 mb (which I believe is the minimum).  The docs also state the number
> is in bytes.
>
> Even so, this is not a real-world number, and the way PHP handles it's
> memory I
> don't think using only one variable will give us a real-world example
> either.
> As you can see from your first example, PHP tries to assign an additional
> 2 mb
> just to add another element to the array.  I believe there is dome doubling
> going on with the memory allocation, when increasing the array size, so it
> doesn't have to be done on every append.  Since that additional assignment
> failed, the actual memory usage goes back to what it was before the
> assignment,
> which means any subsequent memory usage can be up to the 2 mb that you
> tried to
> assign.  That's why the smaller loop in the shutdown function works, as the
> original variable is probably only about 1 mb in size.
>
> The fact that your second example tries to assign only 516 kb when it
> fails at
> the memory limit supports this, and also suggests that that array was much
> smaller when it hit the limit.  If it could assign as much as the
> original, then
> it would have tried to increase the memory allocation by about the same
> amount.
>
> I've put together my own example as you suggested, using an array of
> strings
> instead.  This would reduce the incremental memory allocations as there is
> no
> longer a single giant variable being doubled.  It fails when trying to
> assign
> 416 kb.
>
> https://3v4l.org/BqfvU
>
> If you are correct then the shutdown function should be able to recreate
> the
> original array and not run out of memory.  However, as you can see, it
> fails
> after only assigning 2 of the 100 elements of the original array, which
> suggests
> the shutdown function is using the same memory and it's limit.
>

Right, so, the ini_set(..., 2048) value I chose was just to coerce the
engine to use its smallest heap limit [1], which is the size of one page,
or 2MB [2]. My choice of value was poor. 3am will do that. ini_set(..., 1)
would have been more obvious. Apologies!

Also, you're correct. Looking through the code ([2], [3], and [4]), when
the engine emits the familiar "Allowed memory size..." error, it goes into
a heap overflow state [4] heading toward shutdown. While in the overflow
state, the engine does not make this soft memory limit check. I think this
is what my (faulty 3am) memory was recalling and claiming was a "reset",
because it's a special state -- but it's just temporary.

Anyway, to Dan's point, shutdown functions and destructors are the time to
cleanup and free resources, more so than create new resources/make
allocations. In the case of logging, wouldn't you already have your logger
and the resources it needs? Even if resources (eg diagnostic arrays) are
needing allocation, wouldn't this be application specific and better-suited
for each application to reserve a parachute that's sized for its needs?

But let's say a parachute is definitely needed. Why not just raise the
memory limit in the shutdown function, to accommodate your parachute needs?
I've done this in your example [5] and it seems to perform as expected: the
outer loop runs out of heap, the shutdown function is called and quadruples
the limit, then the loop runs to completion. It's also very simple code,
and easy to understand.

What do you think?

[1]: https://github.com/php/php-src/blob/master/Zend/zend_alloc.c#L2662
[2]: https://github.com/php/php-src/blob/master/Zend/zend_alloc_sizes.h#L22
[3]: https://github.com/php/php-src/blob/master/Zend/zend_alloc.c#L951
[4]: https://github.com/php/php-src/blob/master/Zend/zend_alloc.c#L379
[5]: https://3v4l.org/Ug1pq

Reply via email to