2011-06-30 30 views
5

我有一個腳本,我運行了很多任務,並且經歷了大約21k次。問題在於我爲每個索引做了幾件不同的事情,每個索引都是數據庫中的產品,我通過從API中獲取數據並保存產品等來更新價格。在幾乎每個方法調用之前和之後,我都有幾個區域撥打memory_get_usage(),而我所有的人似乎都在增加內存。沒有一個比別人做得更多,或者沒有那麼明顯。For循環中的PHP內存使用率不斷增長

我試圖取消設置循環底部的所有變量以及試圖將它們設置爲null,但無論內存限制只是通過每次迭代持續提高。

有什麼我可以做的,以清除這個記憶了,我會認爲unsetting變量是爲了釋放內存,但它似乎並沒有這樣做?

編輯: 我忘了提到我開始調查這個問題的原因是,我得到了服務器上的內存限制錯誤。它並不總是在同一時刻發生,甚至每次發生時都會發生。這就是我試圖調查它的原因。

腳本需要大約一個小時才能運行,我在早上運行它時沒有其他任何事情發生,現在它只能在臨時服務器上運行,因此實際上沒有任何人訪問服務器。

我可以張貼代碼,但在PHP其相當大的

<?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

幾乎不可能幫助你,除非你發佈你的代碼。 – Cfreak

+0

更新了我的代碼,其大雖然 –

回答

5

增加內存的使用情況是正常的。取消設置變量不會立即釋放它正在使用的內存,而只是將其標記爲可用於重新使用。在某些時候,PHP會決定垃圾回收器應該運行,這就是內存真正釋放的時候。

除非您真的遇到「內存不足」的致命錯誤,否則無需擔心。 PHP盡最大努力防止OOM的發生,但每次您取消設置變量時,都不會執行非常昂貴的垃圾收集運行。如果這種情況發生,性能會從字面上戛然而止。

+0

還要注意內存應在腳本/線程完成時釋放,否則PHP的垃圾收集將照顧它。 – Brian

+1

對不起,我忘了提及,我將更新我的帖子,我在整個腳本中解決內存問題。這並不總是發生,並不總是發生在同一個地方。因此,我開始將獲取的使用調用看看它來自哪裏,並注意到它正在增加。 –

+0

您使用的是哪個版本的PHP? 5.3有一個改進的垃圾回收器,可以打破循環引用,這在以前的版本中導致了許多內存泄漏。 –

0

沒有看到您的代碼,我的猜測是PHP的垃圾回收(釋放未使用的內存)不會在腳本運行時運行。

點的故事,這種行爲是可以接受的和預期的。只要你沒有得到任何內存不足的錯誤,你應該沒問題。

+0

我更新了我的代碼,是的,我收到錯誤 –