[PHP] Geheugenlek in gebruik objecten

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Gamerkoe
  • Registratie: Maart 2003
  • Laatst online: 08-08 21:43
Ik heb een probleem waar ik niet uit komen. Hopelijk heeft iemand tips, alvast bedankt!

Via een webservices krijg ik een object terug, dit is een salesorder object. In dit object zitten op verschillende diepte niveaus informatie in verschillende nodes. Voorbeeldje:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
salesorder
|- CreateDateTime
|- OrderNumber
|- OrderPrice
|- SentPrice
|- Discount
|- User
 |- Id
 |- Discount_Percentage
|- Customer
 |- InvoiceAddress
  |- FirstName
  |- LastName
  |- Street
  |- HouseNumber
 |- ShippingAddress
  |- FirstName
  |- LastName
  |- Street
  |- HouseNumber
|- SalesOrderRows


En zo nog veel meer properties/nodes onder het SalesOrder object, maar ook in het InvoiceAddress en ShippingAddress node.
Om de informatie in de DB op te slaan wil ik graag een Flatten functie, die alles naar één niveau gaat verwerken, die vervolgens een MySQL::Insert functie in kan. Klein voorbeeldje van de Flatten functie:

code:
1
2
3
4
5
6
7
8
9
10
11
12
    private function Flatten($oOrder){
        $oOrder->UserId                     = (!isset($oOrder->User->Id) ? null : $oOrder->User->Id);
        $oOrder->UserDiscount_Percentage    = (!isset($oOrder->User->Discount_Percentage) ? null : $oOrder->User->Discount_Percentage);

        $oOrder->DiscountCoupon_Code                = (!isset($oOrder->DiscountCoupon->Code) ? null : $oOrder->DiscountCoupon->Code);
        $oOrder->DiscountCoupon_Discount            = (!isset($oOrder->DiscountCoupon->Discount) ? null : $oOrder->DiscountCoupon->Discount);
        $oOrder->DiscountCoupon_Type                = (!isset($oOrder->DiscountCoupon->Type) ? 

    KNIP

    return $oOrder;
}


Het probleem is dat deze functie een memory lek bevat, maar waar/waarom is mij onbekend 8)7 . Er worden zo'n 75 velden omgezet. Ik heb al geprobeerd om met unset ($oOrder->Discount) en resterende velden te werken, maar dit helpt niet. Ik weet dat PHP niet netjes het geheugen weer opruimt omtrent objecten en heb bijvoorbeeld gc_enable(); gebruikt. Ook al gezocht op andere object flatten functies, maar niet kunnen vinden. Hopelijk kan iemand mij tips geven _/-\o_ !

20x SF170s + SMA SB3000TL-21 | PV Output


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Hoe ben je achter de datalek gekomen?

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Definieer ünerhaupt "data lek" eens...

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • sig69
  • Registratie: Mei 2002
  • Laatst online: 00:00
Ik denk dat hij een memory leak bedoelt.

Roomba E5 te koop


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
sig69 schreef op woensdag 06 juli 2016 @ 22:34:
Ik denk dat hij een memory leak bedoelt.
Dan nog ben ik benieuwd hoe TS tot die conclusie komt; hij/zij heeft 't over één SalesOrder object met een stapeltje (nested) properties; niet bepaald heel spannend. Aangenomen dat er geen complete BLOBs in zitten ergens (afgaand op de structuur in de topicstart lijkt me dat sterk; het zijn vrijwel allemaal rechttoe-rechtaan velden) of geen arrays met duizenden elementen (SalesOrderRows bijv.) vraag ik me dan af hoe je tot de conclusie komt dat je een "data lek" (geheugenlek inderdaad) hebt.

[ Voor 8% gewijzigd door RobIII op 06-07-2016 23:04 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Gamerkoe
  • Registratie: Maart 2003
  • Laatst online: 08-08 21:43
Ik bedoelde inderdaad een memory leak, geen datalek. Kan een mod de titel aanpassen?

Wanneer ik de memory_get_usage(true) in het bestand laat zien, dan krijg ik een ophoging die niet meer omlaag gaat. Ik gebruik dus wel veelvuldig unset voor verschillende variabelen. Dit heeft wel iets geholpen, maar nog niet voldoende.

Onderstaande is een soort output scherm wat ik gebruik. Elk puntje is een order, daarna een telling en daarna de memory_get_usage output.
code:
1
2
3
4
5
6
7
8
.................................................................................................... 100 26.5MB
.................................................................................................... 200 35.5MB
.................................................................................................... 300 48.25MB
.................................................................................................... 400 66.5MB
.................................................................................................... 500 80.75MB
.................................................................................................... 600 86MB
.................................................................................................... 700 99.75MB
KNIP


Zonder de gebruikte 'flatten' functie die hierboven staat is het geheugen gebruik in het begin rond de 4MB. Hij stijgt dan nog wel iets, maar aanzienlijk minder.
Voor mijn gevoel 'verplaats' ik de data enkel van een diepere node naar een hogere en unset ik de diepere. Is node eigenlijk wel het juiste woord voor objecten?

Via de webservice krijg ik een array van 100 salesorder objecten, daarin zitten nog wel veel meer properties dan ik heb afgedrukt. Ook de salesorderrows zijn gevuld: dit is ook weer een array van salesorderrow objecten. Afhankelijk van welke orders er worden opgehaald verschilt de informatie en het aantal orderrows natuurlijk.

20x SF170s + SMA SB3000TL-21 | PV Output


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Zonder meer (relevante!) code is op basis van deze paar cijfertjes weinig zinnigs te zeggen. Ik zie sowieso niet waarom je een "Flatten" functie zou willen gebruiken; waarom niet gewoon 't SalesOrder object passen naar de functie die de insert gaat doen :?

code:
1
2
3
4
5
6
7
8
function SaveOrder($order) {
  $db->Execute(
    'insert into `orders` (x, y, z) values (@x, @y, @z)`,
    $order->X,
    $order->Foo->Y,
    $order->Foo->Bar->Z
  );
}


Daarbij vind ik je "Flatten" functie maar raar; waarom zou je het originele $oOrder object gaan 'verbouwen' door alle properties 'naar boven te tillen' i.p.v. een nieuw object/array te returnen? Dan kun je daarna 't originele $oOrder object in z'n totaliteit vernietigen:

code:
1
2
3
4
5
6
7
function Flatten($order) {
  return array(
    'x' => $order->X,
    'y' => $order->Foo->Y,
    'z' => $order->Foo->Bar->Z
  );
}


Verder zou ik dat hele $x = !isset($foo->bar->baz) ? null : $foo->bar->baz even in een functie wegstoppen; maakt 't veel leesbaarder:

PHP:
1
2
3
function GetValue(&$obj, $defaultValue = null) {
  return isset($obj) ? $obj : $defaultValue;
}

Ofwel deze puinhoop:
PHP:
1
2
3
4
5
6
$oOrder->UserId = (!isset($oOrder->User->Id) ? null : $oOrder->User->Id);
$oOrder->UserDiscount_Percentage = (!isset($oOrder->User->Discount_Percentage) ? null : $oOrder->User->Discount_Percentage);

$oOrder->DiscountCoupon_Code = (!isset($oOrder->DiscountCoupon->Code) ? null : $oOrder->DiscountCoupon->Code);
$oOrder->DiscountCoupon_Discount = (!isset($oOrder->DiscountCoupon->Discount) ? null : $oOrder->DiscountCoupon->Discount);
$oOrder->DiscountCoupon_Type = (!isset($oOrder->DiscountCoupon->Type) ? null : $oOrder->DiscountCoupon->Type;

Wordt:
PHP:
1
2
3
4
5
6
$flatorder->UserId                  = GetValue($oOrder->User->Id);
$flatorder->UserDiscount_Percentage = GetValue($oOrder->User->Discount_Percentage); 

$flatorder->DiscountCoupon_Code     = GetValue($oOrder->DiscountCoupon->Code);
$flatorder->DiscountCoupon_Discount = GetValue($oOrder->DiscountCoupon->Discount);
$flatorder->DiscountCoupon_Type     = GetValue($oOrder->DiscountCoupon->Type);

GetValue's tweede argument ($defaultValue) kun je mooi gebruiken om te 'defaulten' naar een value als een element/property niet geset is:

PHP:
1
$flatorder->UserDiscount_Percentage = GetValue($oOrder->User->Discount_Percentage, 0); // Return 0% discount if not specified

[ Voor 68% gewijzigd door RobIII op 08-07-2016 12:19 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Gamerkoe
  • Registratie: Maart 2003
  • Laatst online: 08-08 21:43
Ik gebruik generieke functies voor het opslaan (met een exist controle) van een object in de database. Daarbij gebruikt elk object platte objecten.

De GetValue functie zal ik eens mee stoeien, wellicht dat dit helpt.

Begrijpelijk dat het nu lastig feedback geven is omdat je niet alle code ziet. Dit is ook lastig, want ik kan moeilijk delen van alle verschillende bestanden hier posten omdat dit de overzichtelijkheid ook niet verbeterd.

20x SF170s + SMA SB3000TL-21 | PV Output


Acties:
  • 0 Henk 'm!

  • storeman
  • Registratie: April 2004
  • Laatst online: 22:36
Kan het een recursie probleem zijn? Als je in de flatten functie nu eens alles in een nieuw object stopt in plaats van in hetzelfde object, of zelfs even in een array? Ik heb namelijk het idee dat er een paar references niet goed doorkomen waardoor alle data gekopieerd wordt.

Bovendien, waarom zou je het op deze manier platter maken? Kun je het niet gewoon met een ReflectionClass doen en recursief alle properties detecteren? En waarom stop je hem niet serialized in de database? Ik neem aan dat je de data er ook weer uit wil halen?

"Chaos kan niet uit de hand lopen"


Acties:
  • 0 Henk 'm!

  • Gomez12
  • Registratie: Maart 2001
  • Laatst online: 17-10-2023
Gamerkoe schreef op vrijdag 08 juli 2016 @ 11:36:
Voor mijn gevoel 'verplaats' ik de data enkel van een diepere node naar een hogere en unset ik de diepere. Is node eigenlijk wel het juiste woord voor objecten?
Momenteel copieer je de data enkel naar een "hoger niveau"en unset je niets.
Als je ergens anders in je code dus "de diepere" unset dan blijven je hogere niveau staan want die unset je dan dus schijnbaar niet.

Als je niet te veel code wilt veranderen zou ik het simpel houden en je flatten functie gewoon een nieuw object laten retourneren (en het oude unsetten)

En sowieso zou ik ook even je "generieke" code nakijken want als die op dezelfde basis is dan vermoed ik dat je wel meer dingen in memory houdt dan je denkt.

Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Gamerkoe schreef op vrijdag 08 juli 2016 @ 11:36:
Ik bedoelde inderdaad een memory leak.

Wanneer ik de memory_get_usage(true) in het bestand laat zien, dan krijg ik een ophoging die niet meer omlaag gaat.
Dat is normaal, lees de handleiding nog maar eens.

Een "memory leak" is trouwens ook wat anders, dan een memory_get_usage waarde uitlezen.
Stel je hebt wat geheugen in gebruik genomen met malloc() maar hebt de pointer daar naartoe verwijderd en vergeten free() aan te roepen.
Het geheugen blokje blijft bezet maar niemand kan er bij.
Aangezien PHP een garbage collector heeft, die na het sluiten van het script de boel opruimt, weet je niet of het een "memory leak" is.

Om op te sporen of jouw PHP code geheugen opslokt gebruik je dus memory_get_usage(FALSE).
Je kan dan dus zien hoeveel het geheugen gebruik na het aanroepen van een functie is toegenomen.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$mem = memory_get_usage();
function testMemory()
{
    $a = str_repeat('Memory', 4242);

    echo (memory_get_usage() - $GLOBALS['mem']) . "\n";

    $a = null;

    echo (memory_get_usage() - $GLOBALS['mem']) . "\n";

    unset($a);

    echo (memory_get_usage() - $GLOBALS['mem']) . "\n";
}
testMemory();
echo (memory_get_usage() - $GLOBALS['mem']) . "\n";
Gamerkoe schreef op vrijdag 08 juli 2016 @ 11:36:
Onderstaande is een soort output scherm wat ik gebruik. Elk puntje is een order, daarna een telling en daarna de memory_get_usage output.
code:
1
2
99.75MB
KNIP
99.75MB voor een SalesOrders lijst 8)7
Een Forums index met bergen data gebruikt bij mij maar in totaal 2.34 MiB (gemeten met memory_get_peak_usage) en dat is inclusief alles (92 geladen php/template bestanden)!
En met een XML feed van 200 MB kom ik ook niet boven de 10 MiB uit.
Ik zou daarom als ik jou was even al je code nakijken.

[ Voor 36% gewijzigd door DJMaze op 09-07-2016 10:51 ]

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • Phoenix1337
  • Registratie: April 2009
  • Laatst online: 18:22
RobIII schreef op vrijdag 08 juli 2016 @ 11:50:
Verder zou ik dat hele $x = !isset($foo->bar->baz) ? null : $foo->bar->baz even in een functie wegstoppen; maakt 't veel leesbaarder:

PHP:
1
2
3
function GetValue(&$obj, $defaultValue = null) {
  return isset($obj) ? $obj : $defaultValue;
}
Waarom niet gewoon:

PHP:
1
$x = $foo->bar->baz ?: null


Daarnaast is ook je pass by reference overbodig, want objecten worden standaard al pass by reference gedaan.
storeman schreef op vrijdag 08 juli 2016 @ 12:49:
Kan het een recursie probleem zijn? Als je in de flatten functie nu eens alles in een nieuw object stopt in plaats van in hetzelfde object, of zelfs even in een array? Ik heb namelijk het idee dat er een paar references niet goed doorkomen waardoor alle data gekopieerd wordt.
Ik vermoed ook een recursion probleem. Dat zou echter wel betekenen dat het script nooit stopt met uitvoeren. Dat is iets waar alleen de TS antwoord op kan geven. Of heeft de TS de oplossing inmiddels al gevonden?

[ Voor 5% gewijzigd door Phoenix1337 op 20-07-2016 09:07 ]


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Phoenix1337 schreef op woensdag 20 juli 2016 @ 09:05:
Waarom niet gewoon:

PHP:
1
$x = $foo->bar->baz ?: null
a) Omdat ik niet wist dat er een shorthand van de ternary was :P (schijnbaar ook "pas" vanaf 5.3... nu was dat wel in 2009... kun je nagaan hoe oud mijn PHP ervaring is :P daarbij waren hosters pas veel later vaak over op 5.3+)
b) Omdat dat leuk is voor properties (schijnbaar geeft dit geen Null Reference als bar null is bijvoorbeeld *zie edit) maar niet opgaat bij, bijvoorbeeld: var_dump($_GET['someunsetvalue'] ?: null);
Phoenix1337 schreef op woensdag 20 juli 2016 @ 09:05:
Daarnaast is ook je pass by reference overbodig, want objecten worden standaard al pass by reference gedaan.
Zie bovenstaand; als je by-ref passed werkt dit wel:
PHP:
1
2
3
4
5
6
var_dump(GetValue($_GET['someunsetvalue']));

//of:

$myarray = ['foo'=>'bar'];
var_dump(GetValue($myarray['someunsetvalue']));

Doe je dat niet dan krijg je een Notice: Undefined index.

Nu ben ik geen PHP'er dus er zijn vast mooiere oplossingen (en of je nou "mijn" GetValue gebruikt of ?:... de strekking was dat 't redelijk onleesbare code was zoals TS het had).



Edit:

PHP:
1
2
3
4
5
6
7
8
class foo {
  public $bar;
}

$x = new foo();

var_dump($x->bar->baz ?: null); // Notice: Trying to get property of non-object
var_dump(GetValue($x->bar->baz )); // null

[ Voor 17% gewijzigd door RobIII op 20-07-2016 16:36 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
RobIII schreef op woensdag 20 juli 2016 @ 16:17:
PHP:
1
2
3
4
5
6
7
8
class foo {
  public $bar;
}

$x = new foo();

var_dump($x->bar->baz ?: null); // Notice: Trying to get property of non-object
var_dump(GetValue($x->bar->baz )); // null
var_dump(GetValue($x->bar->baz )); geeft ook "Notice: Trying to get property of non-object" hoor.
Zie: https://3v4l.org/mFSoW

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Waar haal je die GetValue vandaan? ;) Het ging om deze namelijk ;)

https://3v4l.org/oesQP

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Phoenix1337
  • Registratie: April 2009
  • Laatst online: 18:22
RobIII schreef op woensdag 20 juli 2016 @ 16:17:
[...]

a) Omdat ik niet wist dat er een shorthand van de ternary was :P (schijnbaar ook "pas" vanaf 5.3... nu was dat wel in 2009... kun je nagaan hoe oud mijn PHP ervaring is :P daarbij waren hosters pas veel later vaak over op 5.3+)
Klopt, alleen zijn we inmiddels al bij PHP 7.0 aangekomen ;)
RobIII schreef op woensdag 20 juli 2016 @ 16:17:
b) Omdat dat leuk is voor properties (schijnbaar geeft dit geen Null Reference als bar null is bijvoorbeeld *zie edit) maar niet opgaat bij, bijvoorbeeld: var_dump($_GET['someunsetvalue'] ?: null);
Superglobals moet je sowieso nooit direct aanroepen, daar is http://php.net/filter_input voor bedacht, welke je in combinatie met de ternary operator of een default value optie (argument 4) gebruikt. Of je gebruikt een library die dat allemaal voor je regelt.

In PHP 7.0 is tegenwoordig ook de null coalese operator beschikbaar. Deze controleert echter alleen op null en niet op lege waardes (string met lengte 0 bijvoorbeeld), wat de ternary operator wel doet.

PHP:
1
$x = $foo->bar->baz ?? 'default';


Ik weet alleen niet of dit nog steeds een notice of warning throwt. Je kan notices natuurlijk ook uitzetten 8)7
RobIII schreef op woensdag 20 juli 2016 @ 16:17:
Nu ben ik geen PHP'er dus er zijn vast mooiere oplossingen (en of je nou "mijn" GetValue gebruikt of ?:... de strekking was dat 't redelijk onleesbare code was zoals TS het had).
Eens.
Pagina: 1