Re: [PHP-DEV] Memory leak in eval()'d code

This is only part of a thread. view whole thread
  106116
June 29, 2019 21:51 nikita.ppv@gmail.com (Nikita Popov)
On Sat, Jun 29, 2019 at 11:25 PM Benjamin Morel morel@gmail.com>
wrote:

> > The problem here is that OP is *creating* many classes (the fact that > they are anonymous ultimately doesn't matter) by eval'ing code. That is > what causes the leak. > > I still don't understand why there is no leak when *not* using eval(), > though. > > Ben >
Without eval you are creating many objects of the same anonymous class. With eval you are creating a single object each for many anonymous classes. In the first case there is only a single class, in the second there are as many classes as there are eval()s. That's the theory. In practice we have a long-standing bug with RTD key collisions -- this means that the names generated for anonymous classes (and closures for that matter) are not actually unique. For anonymous classes the current workaround is to reuse the existing class definition if an RTD key collision occurs. For your original code (and I should say that this is very specific to the code because it is susceptible to memory layout details) it happens that the RTD key for all those anonymous classes is the same, so in the end you end up with just one class definition also in the eval() case. The leaked memory is an allocated but unused class entry. We could plug that particular leak -- but I should emphasize that the current behavior is incorrect and once the key collision issue is resolved this will create a new class for each eval. Nikita
  106117
June 29, 2019 21:56 dave@mudsite.com (David Walker)
On Sat, Jun 29, 2019 at 15:52 Nikita Popov ppv@gmail.com> wrote:

> On Sat, Jun 29, 2019 at 11:25 PM Benjamin Morel morel@gmail.com> > wrote: > > The leaked memory is an allocated but unused class > entry. We could plug that particular leak -- but I should emphasize that > the current behavior is incorrect and once the key collision issue is > resolved this will create a new class for each eval.
To clarify; you’re saying that the behavior seen in the non scaled case is kind of “wrong” in the sense that it’s exposing the big in naming of the anon class. Once the bug is resolved, the two behaviors shown should mimic each other, wherein the memory footprint would grow as in the example eval() case. — Dave
> >
  106118
June 29, 2019 22:04 nikita.ppv@gmail.com (Nikita Popov)
On Sat, Jun 29, 2019 at 11:56 PM David Walker <dave@mudsite.com> wrote:

> > > On Sat, Jun 29, 2019 at 15:52 Nikita Popov ppv@gmail.com> wrote: > >> On Sat, Jun 29, 2019 at 11:25 PM Benjamin Morel morel@gmail.com >> > >> wrote: >> >> The leaked memory is an allocated but unused class >> entry. We could plug that particular leak -- but I should emphasize that >> the current behavior is incorrect and once the key collision issue is >> resolved this will create a new class for each eval. > > > To clarify; you’re saying that the behavior seen in the non scaled case is > kind of “wrong” in the sense that it’s exposing the big in naming of the > anon class. Once the bug is resolved, the two behaviors shown should mimic > each other, wherein the memory footprint would grow as in the example > eval() case. >
What is the "non scaled case"? To repeat what I said earlier: Without eval there is no and will not be a leak. The bug is that eval() currently reuses an existing class instead of creating a new one each time. Calling get_class(eval("return new class {};")) should be returning a new string every time, while for OPs script it currently does not. tl;dr: while (1) { get_class(new class {}); // should always return the same get_class(eval("return new class {};")); // should always return different, currently may return same due to bug } Nikita
  106119
June 29, 2019 22:19 benjamin.morel@gmail.com (Benjamin Morel)
> > What is the "non scaled case"? To repeat what I said earlier: Without eval > there is no and will not be a leak. > The bug is that eval() currently reuses an existing class instead of > creating a new one each time. Calling get_class(eval("return new class > {};")) should be returning a new string every time, while for OPs script it > currently does not. > tl;dr: > while (1) { > get_class(new class {}); // should always return the same > get_class(eval("return new class {};")); // should always return > different, currently may return same due to bug > }
Ok thank you, anyway the good news is that I found a workaround for now (creating classes of a given type once then cloning). And if I understand correctly, once the issue you mention is resolved (and get_class() returns a distinct class every time), I should have another alternative: ``` $object = eval('return new class (...) {};'); $class = get_class($object); $anotherObjectOfSameClass = new $class(); ``` And I do understand that my use case is border line, and does not deserve too much attention. It's very useful to be able to perform instanceof on objects that dynamically implement an arbritary set of interfaces, though. Ben