2011-06-30 13 views
5

Mam skrypt, który uruchamiam, który wykonuje wiele zadań i przechodzi przez około 21k razy. Problem polega na tym, że każdy indeks robię kilka różnych rzeczy, każdy indeks jest produktem w naszej bazie danych, aktualizuję cenę przez pobieranie danych z API i zapisywanie produktu i tak dalej. Mam kilka obszarów, w których dzwoniłem do memory_get_usage() przed i po prawie każdym wywołaniu metody, a każdy, kogo mam, wydaje się zwiększać pamięć. Nie ma takiego, który robi więcej niż inni, albo nie jest tak zauważalny.Wykorzystanie pamięci PHP w pętli For stale rośnie

Próbowałem usunąć wszystkie moje zmienne w dolnej części pętli, a także próbowałem po prostu ustawić je na wartość null, ale bez względu na to, jaki limit pamięci ciągle podnosi się przez każdą iterację.

Czy jest coś, co mogę zrobić, aby wyczyścić tę pamięć, myślę, że rozregulowanie zmiennych miało na celu zwolnienie pamięci, ale wydaje się, że tak nie jest?

EDYTOWANIE: Zapomniałem podać powód, dla którego zacząłem badać to, że dostaję błędy limitu pamięci na serwerze. Nie zawsze dzieje się to w tym samym momencie, a nawet za każdym razem, gdy jest uruchamiany. Dlatego próbowałem to zbadać.

Uruchomienie skryptu zajmuje około godziny, uruchamiam go rano, kiedy nic się nie dzieje, a teraz jest tylko na serwerze testowym, więc tak naprawdę nie ma nikogo, kto trafiłby na serwer.

mogę pisać kod, ale jego dość duży

<?php 


if(!function_exists('memory_get_usage')){ 
    include('function.php'); 
} 
echo "At the start we're using (in bytes): ", 
    memory_get_usage() , "\n\n"; 

$path = realpath(dirname(__FILE__) . '/../../../../Mage.php'); 
require_once($path); 
Mage::app(); 
require_once '/lib/ProductUpdate.php'; 
echo "Starting product update process \n\n"; 
$productUpdate = new ProductUpdate(); 
$dealerStoreId = 3; 
$volumeDiscountGroupId = 4; 
$retailGroupId = Mage_Customer_Model_Group::CUST_GROUP_ALL; 
$wholesaleGroupId = 2; 


echo "Grabbing all products \n\n"; 
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID); 

// get the products from the InOrder stored procedure qty since datetime and don't pass a date to get all products, also pass the id of the cron job 
$ioProducts = $productUpdate->getProductUpdateProducts('WEB2'); 
echo "---------------------------\n\n"; 
echo "Begin Updating Products \n\n"; 
echo "---------------------------\n\n"; 
$productCount = 0; 
$productUpdate->saveScriptStarted(2); 
echo "Before we go into the initial loop we are using (in bytes): ", 
    memory_get_usage() , "\n\n"; 
foreach ($ioProducts as $ioProduct) { 
    $updateProduct = false; 
    $updateTierPrice = false; 
    $sku = trim($ioProduct['inp_short_item_number']) . trim($ioProduct['isc_SIZE']) . trim($ioProduct['isc_COLOR']); 
    echo "Checking item number " . $sku . " \n\n"; 
    echo "Before Loading Product " . $sku . " we are using (in bytes): ", 
    memory_get_usage() , "\n\n"; 
    $product = $productUpdate->getProduct(); 
    $productId = $product->getIdBySku($sku); 
    echo "After Getting Id from sku " . $sku . " we are using (in bytes): ", 
    memory_get_usage() , "\n\n"; 
    if ($productId) { 
     //$product = $productUpdate->getProduct()->load($productId); 
     echo "After Loading Product " . $sku . " we are using (in bytes): ", 
      memory_get_usage() , "\n\n"; 
     echo "WE HAVE A PRODUCT!: " . $product->getName() . "\n\n"; 

     try { 
      echo "Before Getting Additional Info from InOrder for Product " . $sku . " we are using (in bytes): ", 
      memory_get_usage() , "\n\n"; 

      // Since the product is same for parent products as it is for children you should just be able to get the price of the parent and use that. 
      $additionalInfo = $productUpdate->getItemDetails($ioProduct['inp_short_item_number'], 'WEB2'); 

      echo "After Getting Additional Info from InOrder for Product " . $sku . " we are using (in bytes): ", 
      memory_get_usage() , "\n\n"; 

      echo "Before Getting Extra Charges from InOrder for Product " . $sku . " we are using (in bytes): ", 
      memory_get_usage() , "\n\n"; 

      $oversizeCharge = $productUpdate->getExtraCharges($ioProduct['inp_short_item_number']); 

      echo "After Getting Extra Charges from InOrder for Product " . $sku . " we are using (in bytes): ", 
      memory_get_usage() , "\n\n"; 
     } catch (Exception $e) { 
      echo $e->getMessage() . "\n\n"; 
      continue; 
     } 

     if (is_array($additionalInfo) && count($additionalInfo) > 0) { 
      if (isset($oversizeCharge[0]['Shipping Unit Charge']) && $product->getOversizeCharge() != $oversizeCharge[0]['Shipping Unit Charge']) { 
       $product->setOversizeCharge($oversizeCharge[0]['Shipping Unit Charge']); 
       $updateProduct = true; 
       unset($oversizeCharge); 
      } 
      if ($product->getPrice() != $additionalInfo[0]['pri_current_price']) { 
       $product->setPrice($additionalInfo[0]['pri_current_price']); 
       $updateProduct = true; 
       unset($additionalInfo); 
      } 
      echo "Before Setting Stock Status for Product " . $sku . " we are using (in bytes): ", 
      memory_get_usage() , "\n\n"; 

      $product = $productUpdate->setStockStatus($product, $ioProduct); 

      echo "After Setting Stock Status for Product " . $sku . " we are using (in bytes): ", 
      memory_get_usage() , "\n\n"; 

      if ($product->getNeedsUpdate()) { 
       $updateProduct = true; 
      } 

      if ($updateProduct) { 
       try{ 
        echo "Before Saving Product " . $sku . " we are using (in bytes): ", 
        memory_get_usage() , "\n\n"; 

        $productUpdate->saveProduct($product, $ioProduct); 

        echo "After Saving Product " . $sku . " we are using (in bytes): ", 
        memory_get_usage() , "\n\n"; 
       }catch (Exception $e){ 
        echo $e->getMessage() . "\n\n"; 
        continue; 
       } 
      } 


      // Go through and do the same thing for the other 2 web classes to set pricing for the Dealer and Volume wholesale customers 
      $updateProduct = false; 
      try { 
       echo "Before getting Tier Price info for " . $sku . " we are using (in bytes): ", 
       memory_get_usage() , "\n\n"; 

       $product = $productUpdate->getProduct()->setStoreId($dealerStoreId)->load($productId); 
       $additionalInfo = $productUpdate->getItemDetails($ioProduct['inp_short_item_number'], 'WEB3'); 
       //$additionalTierInfo = $productUpdate->getItemDetails($ioProduct['inp_short_item_number'], 'WEB4'); 

       // Get Real Tier Prices based on Customer Type 
       $retailPriceBreaks = $productUpdate->getItemPriceBreaks($ioProduct['inp_short_item_number'], Mage::getStoreConfig(Lancaster_InOrder_Helper_Data::XML_PATH_RETAIL_PRICE_LIST)); 
       $wholesalePriceBreaks = $productUpdate->getItemPriceBreaks($ioProduct['inp_short_item_number'], Mage::getStoreConfig(Lancaster_InOrder_Helper_Data::XML_PATH_WHOLESALE_PRICE_LIST)); 
       $volumeWholesalePriceBreaks = $productUpdate->getItemPriceBreaks($ioProduct['inp_short_item_number'], Mage::getStoreConfig(Lancaster_InOrder_Helper_Data::XML_PATH_VOLUME_WHOLESALE_PRICE_LIST)); 

       echo "After getting Tier Price infor for " . $sku . " we are using (in bytes): ", 
       memory_get_usage() , "\n\n"; 
      } catch (Exception $e) { 
       echo $e->getMessage() . "\n\n"; 
       continue; 
      } 


      if ($product->getPrice() != $additionalInfo[0]['pri_current_price']) { 
       $product->setPrice($additionalInfo[0]['pri_current_price']); 
       $updateProduct = true; 
      } 

      //The only way to setup multiple price for one website is to set a tier price so we set it to a specific group and the dealer site then go through and set all the other real tier prices 
      $tierPriceInfo = $product->getData('tier_price'); 
      if (!empty($tierPriceInfo)) { 
       echo "Before looping through Tier Price infor for " . $sku . " we are using (in bytes): ", 
       memory_get_usage() , "\n\n"; 
       foreach ($tierPriceInfo as $tierPrice) { 
        if ($tierPrice["website_id"] == $dealerStoreId && 
         $tierPrice["cust_group"] == $volumeDiscountGroupId && 
         $tierPrice["price_qty"] == '1' && 
         $tierPrice["price"] != $additionalTierInfo[0]['pri_current_price']) { 
         $updateTierPrice = true; 
        } 
        //todo need to do some refinement to the following, was rushed to put out the logic need to fix so it doesn't update everytime 
        // need to find if any of the tier prices do not match price as well if there is a price break in InOrder but not in Magento 

        if (!$updateTierPrice) { 

         $updateRetail = isUpdateTierPrices($retailPriceBreaks, $tierPrice, $retailGroupId); 
         $updateWholesale = isUpdateTierPrices($wholesalePriceBreaks, $tierPrice, $wholesaleGroupId); 
         $updateVolWholesale = isUpdateTierPrices($volumeWholesalePriceBreaks, $tierPrice, $volumeDiscountGroupId); 
         if (
          (count($retailPriceBreaks) > 0 && !$updateRetail['priceTierExists']) && 
          (count($wholesalePriceBreaks) > 0 && !$updateWholesale['priceTierExists']) && 
          (count($volumeWholesalePriceBreaks) > 0 && !$updateVolWholesale['priceTierExists'])) { 
          $updateTierPrice = true; 
         } 

         if(($updateRetail['updateTierPrice'] || $updateWholesale['updateTierPrice'] || $updateVolWholesale['updateTierPrice'])){ 
          $updateTierPrice = true; 
         } 
        } 
       } 
       unset($tierPriceInfo); 
       echo "After looping through Tier Price infor for " . $sku . " we are using (in bytes): ", 
       memory_get_usage() , "\n\n"; 
      } 
      else { 
       $updateTierPrice = true; 
      } 
      if ($updateTierPrice) { 
       echo "Before setting whether we update Tier Price for " . $sku . " we are using (in bytes): ", 
       memory_get_usage() , "\n\n"; 
       //construct the tier price 
       $website_id = Mage::getModel('core/store')->load($dealerStoreId)->getWebsiteId(); 
       $tierPrices = array(array(
             'website_id' => $website_id, 
             'cust_group' => $volumeDiscountGroupId, 
             'price_qty' => '1', 
             'price' => $additionalTierInfo[0]['pri_current_price'] 
            )); 

       updateTierPrices($retailPriceBreaks, $retailGroupId, $tierPrices); 
       updateTierPrices($wholesalePriceBreaks, $wholesaleGroupId, $tierPrices); 
       updateTierPrices($volumeWholesalePriceBreaks, $volumeDiscountGroupId, $tierPrices); 

       $product->setData('tier_price', $tierPrices); 
       $updateProduct = true; 
       unset($website_id); 
       echo "After setting whether we update Tier Price for " . $sku . " we are using (in bytes): ", 
       memory_get_usage() , "\n\n"; 
      } 

      if ($updateProduct) { 
       try{ 
        echo "Before saving product for Tiered Pricing for " . $sku . " we are using (in bytes): ", 
        memory_get_usage() , "\n\n"; 
        // $productUpdate->saveProduct($product, $ioProduct); 
        echo "After saving product for Tiered Pricing for " . $sku . " we are using (in bytes): ", 
        memory_get_usage() , "\n\n"; 
       }catch (Exception $e){ 
        echo $e->getMessage() . "\n\n"; 
        continue; 
       } 

      } 
     } 
    } 
    $retailPriceBreaks = null; 
    $wholesalePriceBreaks = null; 
    $volumeWholesalePriceBreaks = null; 
    $oversizeCharge = null; 
    $additionalTierInfo = null; 
    $additionalInfo = null; 
    $product = null; 
    $productCount++; 
    echo $productCount . " Products have been proceessed \n\n"; 
} 
echo "After running through all products we are using (in bytes): ", 
        memory_get_usage() , "\n\n"; 
echo "Peak memory usage for product update scrip (in bytes): ", 
        memory_get_peak_usage() , "\n\n"; 
+0

Niemożliwe, aby Ci pomóc, chyba że napiszesz swój kod. – Cfreak

+0

zaktualizowany moim kodem, ale duży, –

Odpowiedz

5

Rosnące zużycie pamięci w PHP jest normalne. wyłączenie zmiennej nie powoduje natychmiastowego zwolnienia pamięci, która została wykorzystana, po prostu zaznacza ją jako dostępną do ponownego wykorzystania. W pewnym momencie PHP podejmie decyzję, że garbage collector powinien zostać uruchomiony i wtedy naprawdę pamięć zostaje uwolniona.

Chyba że rzeczywiście masz do czynienia z fatalnymi błędami "z pamięci", nie ma się czym martwić. PHP dokłada wszelkich starań, aby nie dopuścić do wystąpienia OOM, ale nie będzie przeprowadzać bardzo kosztownych operacji usuwania pamięci za każdym razem, gdy usuniesz zmienną. Wydajność dosłownie zmieniłaby się do zatrzymania, gdyby tak się działo.

+0

Należy również pamiętać, że pamięć powinna zostać wydana po ukończeniu skryptu/wątku, w przeciwnym razie zajmie się to zbiorem śmieci PHP. – Brian

+1

Przepraszam, że zapomniałem wspomnieć, że będę aktualizować mój post, wychodzę z problemów z pamięcią w całym skrypcie. Nie zawsze tak się dzieje i nie zawsze ma miejsce w tym samym miejscu. Zacząłem więc wykorzystywać wywołania dotyczące wykorzystania, aby zobaczyć, skąd pochodzi i zauważyłem, że wzrastało ono wszędzie. –

+0

Którą wersję php masz? 5.3 ma ulepszony garbage collector, który może zerwać odwołania kołowe, co spowodowało wiele wycieków pamięci w poprzednich wersjach. –

0

Nie widząc twojego kodu, domyślam się, że odśmiecanie PHP (zwalnianie nieużywanej pamięci) nie jest uruchamiane w chwili uruchomienia skryptu.

Punkt opowiadania, to zachowanie jest dopuszczalne i oczekiwane. Dopóki nie otrzymujesz żadnych błędów pamięci, powinieneś być w porządku.

+0

Zaktualizowałem swój kod i tak, otrzymuję błędy –