Avatar billede KlausEC Nybegynder
09. april 2010 - 09:49 Der er 5 kommentarer og
1 løsning

memory limit i 5.2.6

Hej eksperter!

Jeg sidder og roder med en løsning på Debian Lenny.
Da jeg benytter mig af frameworket Akelos (pt. version 0.9) kan jeg ikke tillade mig at opgradere php til 5.3.x, da Akelos pt. ikke understøtter det.
Det giver mig et problem, da jeg har et par scripts, der under eksekvering æder en del RAM. i php 5.3.x kan jeg bare garbage collecte skidtet undervejs, og så er den potte ude!
Hvad gør man i 5.2.x (5.2.6 her)?
Jeg har et mindre eksempel her, jeg bruger til memory tests:

<?php
class Foo
{
    public $var = '3.1415962654';
}

$baseMemory = memory_get_usage();

for ( $i = 0; $i <= 100000; $i++ )
{
    $a = new Foo();
    $a->self = $a;
    if ( $i % 500 === 0 )
    {
        echo sprintf( '%8d: ', $i ), memory_get_usage() - $baseMemory, "\n";
    }
}
?>

Jeg så kalder min test således:
time php -dmemory_limit=-1 -n test.php

og her kan jeg så se, hvor meget memory scriptet bruger.
Jeg ville her prøve at indsætte en unset($a) for at lade scriptet tømme variablen for hver iteration, men det lader ikke til at give mig noget.

Nogle gode ideer?
Avatar billede KlausEC Nybegynder
09. april 2010 - 10:36 #1
Noget andet er, at jeg har sat memory_limit til 256M i min /etc/php5/apache2/php.ini (og restartet apache).

Hvis jeg så kalder min test uden option'en -dmemory_limit=-1, dør den når den overskrider 128M - ikke 256.

Jeg får denne fejl:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 39 bytes) in /my_path/test.php on line 11
Avatar billede intenz Novice
10. april 2010 - 12:48 #2
Jeg sad og legede lidt med det, og fik skrabet ca. 20% af forbruget ved at tvinge den til at kalde nogle __destruct og sætte din $a værdi ind i sin egen klasse.


class Foo
{
    public $testvar = '3.1415962654';
   
    public function __destruct() {
        unset($this->testvar);
    }
}

class Test
{
    public $a;
    public function __construct() {
        $a = new Foo();
        $a->self = $a;
        $this->a = $a;
        return $a;
    }
   
    public function __destruct() {
        $this->a->__destruct();
    }
}

$baseMemory = memory_get_usage();

for ( $i = 0; $i <= 100000; $i++ )
{
    $a = new Test();
    unset($a);
    if ( $i % 500 === 0 )
    {
        echo sprintf( '%8d: ', $i ), memory_get_usage() - $baseMemory, "<br>";
    }
}


Men det løser selvfølgelig ikke dit problem helt. Dit bedste bud er nok at lave et check på dit memory forbrug, og når det går over en hvis grænse, så starte en ny process der forsætter hvor du kom til, og så lukke den process du har gang i.
Avatar billede KlausEC Nybegynder
12. april 2010 - 09:26 #3
"Dit bedste bud er nok at lave et check på dit memory forbrug, og når det går over en hvis grænse, så starte en ny process der forsætter hvor du kom til, og så lukke den process du har gang i."

Det lyder som en fornuftig løsning - har du et eksempel på, hvordan jeg kan gøre det?
Rent faktisk har jeg bare et script, der udfører en del mysql kald (oprydning), og jeg har erfaring om at det kan overskride min memory limit.
Jeg kan sagtens udvide scriptet med kontrol af memory usage, men så at afslutte nuværende process ud fra det, og så starte en ny process af samme script igen? Hvordan gør man det?
Avatar billede intenz Novice
12. april 2010 - 12:00 #4
Det hele ville jo være nemmere hvis du bare i stedet for at have én process, så kunne dele dine kald op i flere filer, som hver for sig ikke ville bruge al memory. Jeg ved ikke om det er mulighed. Hvis det ikke er, kan du se lille overblik over hvordan jeg ville gøre det. Det bliver kun lidt overfladisk beskrevet, da det ellers bliver et _meget_ langt indlæg :)

Hvor nemt der er at implementere afhænger af hvor nemt du kan adskille dine kald, så de ikke behøver at blive kørt i sammenhæng. At du kan gøre det (i tilstrækkelig grad) er kravet for at du kan lave sådan en løsning.

Umiddelbart skal flowet i din process være noget i stil med:

1. kører en anden lignende process allerede -> afslut.

2. Marker denne process som startet (gem evt. i en tekstfil eller database)

3. Hent evt. state fra en tidligere process, så vi ved hvor vi skal starte fra.

4. Loop. Kør dine mysql kald, tjek hvornår memory usage når et kritisk punkt.

5. Memory usage er nået et kritisk punkt -> gem processens 'state', så den næste process kan se hvor den skal starte fra.

6. Marker processen som afsluttet (samme måde som du markerede den som startet i 2).

7. afslut processen

Der skal selvfølgelig også være et sted hvor du markerer, at hele scriptet er afsluttet. Så den ikke behøver at forsøge at starte nye processer hvis det hele er gennemført.

Du skal så have en controller process der kan forsøge at starte processer indtil det er afsluttet.

Den letteste måde til det, er nok bare at forsøge at køre cronjobs indtil processen er slut. Det er dog også den mest primitive måde.

Den bedre, men mere avancerede opsætning kunne være, at du starter en daemon process der bliver kørt en gang fra cron, som så er ansvarlig for at starte nye processer indtil scriptet er gennemført, hvorefter daemon processen afsluttes.

Det kunne være en kombination af en daemon og en multi-thread class.
F.eks.

Daemon: http://www.phpclasses.org/package/5956-PHP-Manage-the-execution-of-PHP-daemon-scripts.html

Multi-thread: http://www.phpclasses.org/package/3953-PHP-Emulate-threads-using-separate-HTTP-requests.html
Avatar billede KlausEC Nybegynder
12. april 2010 - 12:45 #5
... nu var det mere basalt end det, jeg havde tænkt mig - process states bliver slet ikke nødvendigt her, så det var bare rent teknisk:
Hvordan afslutter jeg processen?
Hvordan starter jeg en ny (af samme slags)?
Avatar billede intenz Novice
12. april 2010 - 13:19 #6
Afslut process:
exit;

Start ny:
Kald filen igen...

Hvis du vil gøre det automatisk, kan du bruge:
http://php.net/manual/en/function.pcntl-fork.php
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Vi tilbyder markedets bedste kurser inden for webudvikling

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester