Diskuze: Problém s pamětí

PHP PHP Problém s pamětí American English version English version

Avatar
Martin Štěpánek (Enormyk):

Ahoj,
mám problém se pamětí. Hlásí mi to tuto chybu:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 65488 bytes) in C:\xampp\htdocs\php\miny\Tridy\Miny.php on line 183

Jen jako výzvu jsem si začal v PHP programovat vlastní hledání min. Když uživatel klikne na prázdné pole tak se odhalí i ta pole kolem něj. Pokud je nějaké pole kolem něj prázdné tak zase kolem něj atd... Logicky jsem to vyřešil rekurzí, nic jiného mě nenapadlo.
Když uživatel klikne zkontroluje se jestli klikl na prázdné pole (nedotýká se žádných min):

// pokud je zviditelněné pole 0, zviditelní pole kolem
if ($_SESSION['pole'][$x][$y] == 0) {
    $this->oznacPoleOkolo($x, $y);
}

Zde je kód metody oznacPoleOkolo:

//metoda projede všechna pole okolo
 if ($x != 0) { //podmínka jestli není na konci mapy
            $this->sectiCisla($x - 1, $y); //sečte počet min kolem pole
            $_SESSION['mapa'][$x - 1][$y] = true; //zviditelní pole
            if ($_SESSION['pole'][$x - 1][$y] == 0) { //pokud je zviditelnění pole prázdné
                $this->oznacPoleOkolo($x - 1, $y);//znovu označí vše kolem
            }
        }
        if ($x != $this->sirka - 1) { //zde se to opakuje u všech polí kolem toho, na které si kliklo
            $this->sectiCisla($x + 1, $y);
            $_SESSION['mapa'][$x + 1][$y] = true;
            if ($_SESSION['pole'][$x + 1][$y] == 0) {
                $this->oznacPoleOkolo($x + 1, $y);
            }
        }
        if ($y != 0) {
            $this->sectiCisla($x, $y - 1);
            $_SESSION['mapa'][$x][$y - 1] = true;
            if ($_SESSION['pole'][$x][$y - 1] == 0) {
                $this->oznacPoleOkolo($x, $y - 1);
            }
        }
        if ($y != $this->vyska - 1) {
            $this->sectiCisla($x, $y + 1);
            $_SESSION['mapa'][$x][$y + 1] = true;
            if ($_SESSION['pole'][$x][$y + 1] == 0) {
                $this->oznacPoleOkolo($x, $y + 1);
            }
        }
        if ($x != 0 && $y != 0) {
            $this->sectiCisla($x - 1, $y - 1);
            $_SESSION['mapa'][$x - 1][$y - 1] = true;
            if ($_SESSION['pole'][$x - 1][$y - 1] == 0) {
                $this->oznacPoleOkolo($x - 1, $y - 1);
            }
        }
        if ($x != 0 && $y != $this->vyska - 1) {
            $this->sectiCisla($x - 1, $y + 1);
            $_SESSION['mapa'][$x - 1][$y + 1] = true;
            if ($_SESSION['pole'][$x - 1][$y + 1] == 0) {
                $this->oznacPoleOkolo($x - 1, $y + 1);
            }
        }
        if ($x != $this->sirka - 1 && $y != 0) {
            $this->sectiCisla($x + 1, $y - 1);
            $_SESSION['mapa'][$x + 1][$y - 1] = true;
            if ($_SESSION['pole'][$x + 1][$y - 1] == 0) {
                $this->oznacPoleOkolo($x + 1, $y - 1);
            }
        }
        if ($x != $this->sirka - 1 && $y != $this->vyska - 1) {
            $this->sectiCisla($x + 1, $y + 1);
            $_SESSION['mapa'][$x + 1][$y + 1] = true;
            if ($_SESSION['pole'][$x + 1][$y + 1] == 0) {
                $this->oznacPoleOkolo($x + 1, $y + 1);
            }
        }

Ještě pro úplnost vkládám metodu sectiCisla:

public function sectiCisla($x, $y) {
    if (!$_SESSION['mapa'][$x][$y]) {
        if ($x != 0) { //pokud to není kraj mapy
            if ($_SESSION['pole'][$x - 1][$y] . '' == $this->mina) { //pokud je u něj mina
                $_SESSION['pole'][$x][$y] ++; //přičte číslo
            }
        }
        if ($x != $this->vyska - 1) {
            if ($_SESSION['pole'][$x + 1][$y] . '' == $this->mina) {
                $_SESSION['pole'][$x][$y] ++;
            }
        }
        if ($y != 0) {
            if ($_SESSION['pole'][$x][$y - 1] . '' == $this->mina) {
                $_SESSION['pole'][$x][$y] ++;
            }
        }
        if ($y != $this->sirka - 1) {
            if ($_SESSION['pole'][$x][$y + 1] . '' == $this->mina) {
                $_SESSION['pole'][$x][$y] ++;
            }
        }
        if ($x != 0 && $y != 0) {
            if ($_SESSION['pole'][$x - 1][$y - 1] . '' == $this->mina) {
                $_SESSION['pole'][$x][$y] ++;
            }
        }
        if ($x != 0 && $y != $this->sirka - 1) {
            if ($_SESSION['pole'][$x - 1][$y + 1] . '' == $this->mina) {
                $_SESSION['pole'][$x][$y] ++;
            }
        }
        if ($x != $this->vyska - 1 && $y != 0) {
            if ($_SESSION['pole'][$x + 1][$y - 1] . '' == $this->mina) {
                $_SESSION['pole'][$x][$y] ++;
            }
        }
        if ($x != $this->vyska - 1 && $y != $this->sirka - 1) {
            if ($_SESSION['pole'][$x + 1][$y + 1] . '' == $this->mina) {
                $_SESSION['pole'][$x][$y] ++;
            }
        }
    }
}

Vím, že by to šlo napsat ne tak dlouze, ale tohle bylo to nejednoduší, co mě napadlo.
Mohli by jste mi prosím, jak tento problém vyřešit? Upravení php.ini (memory_limit) beru jako tu nejhorší možnost, pokud se jiná nenajde.
Předem děkuji za pomoc. :-)

Odpovědět 6. července 21:01
Nesnáším, když někdo u if nepoužívá {}, byť se jedná o jeden řádek.
Avatar
Odpovídá na Martin Štěpánek (Enormyk)
Dominik Gavrecký:

Možno sa mýlim ale je hlúposť písať takúto hru v PHP ... Sú na to omnoho krajšie jazyky ale ako študijnú výzvu to beriem

Nahoru Odpovědět  -1 6. července 22:01
Hlupák nie je ten kto niečo nevie, hlupákom sa stávaš v momente keď sa na to bojíš opýtať.
Avatar
David Hynek
Redaktor
Avatar
Odpovídá na Martin Štěpánek (Enormyk)
David Hynek:

nemáš to prostě jen někde zacyklený? Generování mapy nemůže být nic složitého. Do pole náhodně rozmístit miny a pak v okolí 1 pole načteš +1 tam kde jsou miny vedle sebe se pak automaticky změní z 1 na 2 a výše. A ty když pak hledáš v prozkoumáváš jen okolí 1 pole kolem sebe se pak zobrazí čísla (ale je asi zbytečné rozepisovat princip hry) koukni na podmínky, tam bude zakopaný pes.

Nahoru Odpovědět 6. července 22:12
Čím víc vím, tím víc věcí nevím.
Avatar
David Hynek
Redaktor
Avatar
David Hynek:

Jo ale teď koukám, že to možná bude v SESSION ty nejsou tak velké jak si představuješ... myslím si že je to kolem 4kB Možná ještě méně... já chtěl jednou přes session přenášet velký formulář a vždy se mi dařilo přečíst jen půlku, tak dlouho jsem nad tím špekuloval, až jsem si uvědomil. že se mi vrací vždy konkrétní velikost formuláře. Tedy, změň způsob ukládání mapy.

Nahoru Odpovědět 6. července 22:18
Čím víc vím, tím víc věcí nevím.
Avatar
Odpovídá na David Hynek
Martin Štěpánek (Enormyk):

Já právě ty čísla počítám až při kliknutí, ale to si nemyslím, že by mělo ovlivnit paměť (jestli počítat na začátku nebo při hře).
S těmi SESSION jsem si myslel taky, ale já už je při vytváření pole zaplňuji takže pokud by byl problém s tím, tak to bude hlásit chybu už při vytváření mapy...

    // vytvoří pole podle nastavení a zakryje ho
    for ($i = 0; $i < $this->vyska; $i++) {
        for ($j = 0; $j < $this->sirka; $j++) {
            $_SESSION['pole'][$i][$j] = 0; // obsahuje 0 (prázdné) nebo miny
            $_SESSION['mapa'][$i][$j] = false; // překrytí - pokud je false je zakryté, pokud true, odkryté
            $_SESSION['oznaceni'][$i][$j] = ''; // zde se ukládá umístění vlajek (označení min)
        }
    }
    $_SESSION['vlajky'] = $this->pocet;

    // náhodně vloží bomby
    for ($i = 0; $i < $this->pocet; $i++) {
        $this->vlozMinu();
    }

private function vlozMinu() {
    $x = rand(0, $this->vyska - 1); //j
    $y = rand(0, $this->sirka - 1); //i
    if ($_SESSION['pole'][$x][$y] . '' == $this->mina) {
        $this->vlozMinu(); // pokud mina na daném poli již je, vloží ji znovu
    } else {
        $_SESSION['pole'][$x][$y] = $this->mina;
    }
}
Nahoru Odpovědět 7. července 8:26
Nesnáším, když někdo u if nepoužívá {}, byť se jedná o jeden řádek.
Avatar
Odpovídá na Dominik Gavrecký
Martin Štěpánek (Enormyk):

Máš pravdu, například Java nebo C#, jedná se o studijní výzvu. ;-)
Co se týče vzhledu, vše řeším přes ajax takže to alespoň působí jako hra než web. :-)

Nahoru Odpovědět 7. července 8:28
Nesnáším, když někdo u if nepoužívá {}, byť se jedná o jeden řádek.
Avatar
Odpovídá na David Hynek
Martin Štěpánek (Enormyk):

Pro jistotu jsem vyzkoušel i výpočet čísel už při generování mapy, ale i přesto je pořád problém s pamětí pokud se klikne na na prázdné pole. Jiná pole se odkryjí v pořádku.

Nahoru Odpovědět 7. července 8:49
Nesnáším, když někdo u if nepoužívá {}, byť se jedná o jeden řádek.
Avatar
Odpovídá na David Hynek
Martin Štěpánek (Enormyk):

A na řádku, který hlásí chyba (183) se nachází právě rekurze:

if ($x != 0) { //podmínka jestli není na konci mapy
           $this->sectiCisla($x - 1, $y); //sečte počet min kolem pole
           $_SESSION['mapa'][$x - 1][$y] = true; //zviditelní pole
           if ($_SESSION['pole'][$x - 1][$y] == 0) { //pokud je zviditelnění pole prázdné
               $this->oznacPoleOkolo($x - 1, $y);//znovu označí vše kolem ------- řádek 183, zde má být chyba podle PHP
           }
       }
Nahoru Odpovědět 7. července 8:51
Nesnáším, když někdo u if nepoužívá {}, byť se jedná o jeden řádek.
Avatar
David Hynek
Redaktor
Avatar
Odpovídá na Martin Štěpánek (Enormyk)
David Hynek:

v tom případě jsi to prostě zacyklil a do zblbnutí ti to zaplňuje paměť až do naplnění a pak to spadne. Obávám se, že bez nakouknutí do kodu se lepší odpovědi nedočkáš...

Nahoru Odpovědět 7. července 10:15
Čím víc vím, tím víc věcí nevím.
Avatar
katrincsak
Člen
Avatar
Odpovídá na Martin Štěpánek (Enormyk)
katrincsak:

To zda je lepší řešení, nebo není by asi bylo na lepší prostudování.

Každopádně v nastavení php.ini si nastav větší alokaci paměti a problém máš vyřešený. Pakliže to nenajdeš, pošli mi zprávu a napíšu ti přesně o které položky se jedná. (Nejsem teď na PC kde bych to komfortně zjistil).

Lze nastavovat velikost session a dalších funkcí. Osobně jsem ukládal dost velké data do session i bitovou kopii obrázků v řádově desítkách MB.

 
Nahoru Odpovědět  -3 12. července 15:27
Avatar
katrincsak
Člen
Avatar
Odpovídá na Martin Štěpánek (Enormyk)
katrincsak:

Pakliže nemáš možnost se dostat k php.ini což na webhostingu se nedostaneš, tak bych doporučil zjistit jakou kapacitu ti hosting umožňuje. Standardně by mělo být dnes 32MB a víc, ale to je na hostingu. V opačném případě to vyžaduje buď změnu hostingu a nebo optimalizovat tvojí "aplikaci".

 
Nahoru Odpovědět 12. července 15:30
Avatar
Odpovídá na katrincsak
Martin Štěpánek (Enormyk):

Jak jsem již psal, upravení php.ini bych bral jako poslední možnost.
Podle mě se to pravděpodobně opravdu někde zacykluje, bohužel nedokáži přijít na to kde.

Nahoru Odpovědět 12. července 19:00
Nesnáším, když někdo u if nepoužívá {}, byť se jedná o jeden řádek.
Avatar
katrincsak
Člen
Avatar
Odpovídá na Martin Štěpánek (Enormyk)
katrincsak:

Když budu mít čas a budu to stíhat, zkusím udělat také miny. Každopádně mi přijde zbytečné tohle vše ukládat ;-)
Osobně mám představu, že si dle navolené velikosti vytvořím čtverec, který bude mít xxx čtverečků a budou očíslované. A tak si budu muset pamatovat velikost. Následně náhodně dle čísel dosadit miny, to si určitě zapamatovat. A každou na kliknutou pozici bych si taky pamatoval a zbytek již jen dopočítal. Nepotřebuješ si pamatovat úplně všechno.

Aspoň takhle to vidím já, možná až to budu programovat, najdu chybu v mé teorii ;-)

Data můžeš ukládat do neviditelného inputu, nebo i do souboru... Možností je spousta.

Editováno 12. července 23:50
 
Nahoru Odpovědět 12. července 23:50
Avatar
Odpovídá na Martin Štěpánek (Enormyk)
grygerek.tomas:

Řekl bych, že zacyklení je v odkrývání prázdného pole a rekurze. Jedno pole vyvolá odkrývání toho vedle a to vedle vyvolá odkrývání původního..? Zas tak poctivě jsem to nečetl, ale toto mne napadlo.

Nahoru Odpovědět  +2 13. července 4:12
Není proč se klanět.
Avatar
Richard
Člen
Avatar
Richard:

katrincsak to už je po několikáté co tu radíš hovadiny, proč to děláš?
David Hynek to si asi pleteš s cookies, sessions ve standartu nemají limit, jsou limitované pouze maximální alokovanou pamětí skriptu

Do toho limitu co to překročilo by se vešlo několik běžících eshopů, zvyšování je hovadina, vzhledem k tomu že to je zacyklený tak to ani nemůže pomoct.

Autor to musí prijet debuggerem, případně ručně dumpovat obsah a zjistit důvod zacyklení, jiné řešení není.

Nahoru Odpovědět  +1 13. července 11:15
$action = $_GET['Life']; | Když dáš mínus, napiš proč!
Avatar
Odpovídá na Martin Štěpánek (Enormyk)
Luboš Běhounek (Satik):

Jak píše grygerek.tomas

Do metody oznacPoleOkolo si musíš přidat kontrolu, zda to pole už není "zviditelněné" (abych použil tvoji terminologii :) )

Takhle se oznacPoleokolo pořád volá dokola na stejná pole, až dojde pamět, php asi nemá nijak omezený stack (prostě pro něj používá celou paměť), takže místo stackoverflow dostaneš nedostatek paměti.

Nahoru Odpovědět 13. července 14:42
:)
Avatar
Odpovídá na grygerek.tomas
Martin Štěpánek (Enormyk):

Nad tím jsem se teď zapřemýšlel, ale v metodě sectiCisla hned na začátku mám ověření, že se čísla sečtou jen v případě, že je pole zakryté (není ještě zviditelněné):

if (!$_SESSION['mapa'][$x][$y]) {
Nahoru Odpovědět 14. července 15:18
Nesnáším, když někdo u if nepoužívá {}, byť se jedná o jeden řádek.
Avatar
katrincsak
Člen
Avatar
Odpovídá na Richard
katrincsak:

Můžeš mi přesněji vysvětlit co píšu za hovadinu ? A možná by mě zajímalo i v jakém dalším případě, vzhledem k tomu že bych se rád napravil z nějaké chyby ;-)

  • Navýšením limitu by mu problém vyřešil i když to není samozřejmě ideální řešení, ale těžko poradit konkrétně kromě toho, že to má někde zacyklené.
  • Pakliže je to kvůli ukládání fotografiíí do session, tak ano opravdu je ukládám a běží to v ostrém provozu.

A vůbec nebudu překvapený, když odpověď na to nedostanu :/

 
Nahoru Odpovědět  -1 15. července 21:08
Avatar
Richard
Člen
Avatar
Odpovídá na katrincsak
Richard:

Samozřejmě že dostaneš.
Jak konkrétně se vyřeší problém zacyklený rekurze, která nikdy nekončí a při každým průchodu zabírá další pamět zvýšením limitu paměti? Nijak, jenom mu to reportne chybu s větší hodnotou alokované paměti, případně pokud tý paměti na serveru má opravdu hodně, tak to narazí na time limit.
A druhý bod taky sedí. Jsi si vědom toho jak a kde se obsah session ukládá, kdy a proč? Pokud ano, tak by tě taková věc nenapadla.

Další případy si teď nevybavím, ale mám tvůj nick spojený s tagama "php, hovadiny".
PS. Dáš link na ten projekt v ostrým provozu kde používáš tyhle techniky?

Editováno 15. července 21:16
Nahoru Odpovědět  +2 15. července 21:15
$action = $_GET['Life']; | Když dáš mínus, napiš proč!
Avatar
katrincsak
Člen
Avatar
Odpovídá na Richard
katrincsak:

Odpovědi si určitě cením ;-)

  • V tomhle rozhodně máš pravdu a zřejmě to rada nebyla úplně ideální, ale i když ty cykly má hezky napsané, tak se v tom vyznat není nic lehkého a tak jsem poradil tu nejsnazší věc, která by mohla dočasně problém řešit. Ale jak píšeš, mohlo by se číslo jen zvýšit dalším a dalším klikáním na miny.
  • Osobně session používám, tam kde je skutečně potřeba a případně tam kde již znalosti neznají případně jiné možnosti.

Ano, mohu dát link. Je to web, který jsem psal od A do Z a prakticky se na tom naučil OOP. Grafika není ideální, ale hold nejsem grafik. I tak to považuji zatím za svou nejlepší a nejčistší práci .
Každopádně budu rád za feedback ;-)

Proces na ukládání fotek do session najdeš v přidání nového inzerátu.
http://ebazar.eu

 
Nahoru Odpovědět 15. července 22:04
Avatar
Marian Benčat
Redaktor
Avatar
Marian Benčat:

Jednoduchý sortící algoritmus na 10 000 000 prvků, ti v PHP sežere 2,5GB paměti, zatímco v jiném nedebiilním jazyku 80MB, To říkám proto, abych ti dal naději, že si to nenaimplementoval špatně, ale pouze autoři PHP již 10 let přemýšlejí pr**lí - Chci říct.. že to, že ti něco dost primitivního permamentně v PHP memory leakuje, nemusí být tvoje chyba, ale hodně špatného nástroje, který používáš.

Editováno 15. července 22:12
 
Nahoru Odpovědět  +2 15. července 22:11
Avatar
katrincsak
Člen
Avatar
Odpovídá na Richard
katrincsak:

A jiné řešení jsem pro ukládání nenašel, potřebuji, aby se fotka zmenšila již na straně klienta a zároveň mít 100% přehled o tom co se nahrává a dostat informace tam kam potřebuji. ;-)

foreach ($_SESSION['fotky']['baseID1'] as $foto){

$filteredData=substr($foto, strpos($foto, ",")+1);
$decocedData = base64_decode($filteredData);

        $fp = fopen('b'.$i++.'.png', 'wb');
        fwrite($fp, $decocedData);
        fclose($fp );
}
 
Nahoru Odpovědět 15. července 22:15
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 22 zpráv z 22.