Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:107565 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 87808 invoked from network); 17 Oct 2019 03:54:58 -0000 Received: from unknown (HELO php-smtp3.php.net) (208.43.231.12) by pb1.pair.com with SMTP; 17 Oct 2019 03:54:58 -0000 Received: from php-smtp3.php.net (localhost [127.0.0.1]) by php-smtp3.php.net (Postfix) with ESMTP id CA0F52CC929 for ; Wed, 16 Oct 2019 18:39:29 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp3.php.net X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS15169 209.85.128.0/17 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) Received: from mail-qk1-f175.google.com (mail-qk1-f175.google.com [209.85.222.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp3.php.net (Postfix) with ESMTPS for ; Wed, 16 Oct 2019 18:39:29 -0700 (PDT) Received: by mail-qk1-f175.google.com with SMTP id u184so420647qkd.4 for ; Wed, 16 Oct 2019 18:39:29 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:reply-to :from:date:message-id:subject:to:cc; bh=0wRFtSwmk2Ai/lasivDxATXKn8LtI0Op8cHIch8bvV4=; b=AANRjHloqkGK1dyZGFv9bjwoWUxAKdhA6kBmvnQgkw7hiKBPsAF0f7I0moEO1y+J/C EwuIhd3VuOgvjnoOnoHTpWNnju+ar4K2mBhA+xzIO5LCJBMO1o+gQ4zGmJx75RK/oDQO ulEBSKskIVJKnywY62+fP7wNgHx/La/rf24K9SDLjmLJ3O3GXgo9/GQXjGUrjoNI1SjB Iem4mSER7iQsu/JraJTGevGSLm01pDW7pra1X+RXN4wVkyV/4dz0guCYLLd/UT9aui2Q QCuKe/3P1Rr9Tb4/KeUgdpu8DV/Jk/IwWOeNG0imqSyR14W0v/fFWSqj3XV8RuNg7n1x SsyQ== X-Gm-Message-State: APjAAAVULZUZvkW7DjiBLSh9wNy6sMuOrnlDlu/anFszHJ7HXijHKOcP NvxEumxB8NuBc+pBvpkWul6Vul1Zdo41Yqilg/I= X-Google-Smtp-Source: APXvYqwmb8t9+uIAcM2uPNnKKfgjykBr8m6yMSHiG1KMnAui1g+EAwATJxKxPc0axyqdpGirOfPwNrxerD+81RkTVak= X-Received: by 2002:a37:a1d8:: with SMTP id k207mr1014928qke.182.1571276368252; Wed, 16 Oct 2019 18:39:28 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: Reply-To: bishop@php.net Date: Wed, 16 Oct 2019 21:39:02 -0400 Message-ID: To: Peter Stalman Cc: "internals@lists.php.net" Content-Type: multipart/alternative; boundary="000000000000c611f605951149ca" X-Envelope-From: Subject: Re: [PHP-DEV] Shutdown Memory Allowance (aka Soft Memory Limit) From: bishop@php.net (Bishop Bettini) --000000000000c611f605951149ca Content-Type: text/plain; charset="UTF-8" On Tue, Sep 24, 2019 at 5:39 AM Peter Stalman wrote: > On Tue, Sep 24, 2019 at 12:01 AM Bishop Bettini wrote: > > > > On Tue, Sep 24, 2019 at 2:26 AM Peter Stalman > 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 --000000000000c611f605951149ca--