Pamięciożerny WordPress – memory exhausted, zbyt mały memory_limit

WordPress ma to do siebie, że nie dba zbytnio o ograniczenie zużycia pamięci. Niestety, przy dodaniu większej ilości wtyczek (większej niż 5-7) zaczynają się problemy z wykorzystanym limitem pamięci.

Błędy pojawiają się najprędzej w panelu administracyjnym, co czynnie uniemożliwia poprawną pracę z systemem.
kokpit_2009-09-12

[11-Sep-2009 15:35:01] PHP Fatal error:  Allowed memory size of 33554432 bytes exhausted (tried to allocate 170031 bytes) in /home/.../wp-includes/wp-db.php on line 501

Plugin WP-Memory-Usage pokazuje ile z dostępnej pamięci jest w użyciu.

Przyczyny

Co może być przyczyną takiego stanu rzeczy, i czy możemy coś z tym zrobić?
Główną przyczyną jest funkcja inicjująca init oraz poprzedzające ją plugins_loaded i widgets_init. Funkcje te ładują do pamięci wtyczki oraz widgety, inicjują całą aplikację.
Aby sprawdzić jak wygląda ładowanie strony pod względem wykorzystania pamięci możemy posłużyć się wtyczką WP Tuner. Po zainstalowaniu wtyczki przechodzimy do panelu zarządzania wtyczką wybieramy jedno z ustawień Presets: np Analyze Timing. Następnie otwieramy dowolną stronę panelu i przewijamy stronę do samego dołu:
wp_tuner_2009-09-12
Jak widzimy na screenie ilość użytej pamięci skacze drastycznie do 26MB! po załadowaniu funkcji init do ponad 20MB tylko po załadowaniu pluginów! Dawno nie widziałem takiego marnotrawstwa pamięci 😉

Rozwiązania

Możliwości rozwiązania problemu mamy kilka.
Zacznijmy od najprostszych: zwiększenie limitu pamięci 🙂 Jednak w przypadku, gdy nasz wordpress nie jest postawiony na dedyku mamy małą szansę na możliwość ingerencji w to ustawienie. Możemy napisać @ do admina.
Jeśli natomiast mamy możliwość zmian konfiguracji możemy zastosować kilka wpisów:
php.ini:

;; set new memory limit
memory_limit = 64M

.htaccess:

# set new memory limit
php_value memory_limit 64M

lub w php:

 // set new memory limit
ini_set('memory_limit','64M');

Jak już wspomniałem w przypadku serwera wirtualnego takie zapisy zwykle nie zadziałają. Co nam pozostaje jeśli nie możemy zmienić wielkości limitu? Niewiele… Możemy co najwyżej odchudzić katalog wtyczek, wyłączyć i usunąć te niepotrzebne/nieużywane.

Aby odciążyć generowanie strony bloga możemy (powinniśmy) zainteresować się cache. Wtyczek do cache jest sporo. Najbardziej popularne to: WP Widget Cache oraz WP Super Cache. Pierwsza robi cache samych widgetów, druga natomiast (w zależności od trybu) może zrobić cache wszystkiego 😉

Oczywiście możemy sami zająć się utworzeniem mini-systemu cache. Może mieć to zastosowanie w przypadku używania własnych pluginów. Pokażę na przykładzie.
W stopce mam 2 utworzone przeze mnie kolumny: Czytuję oraz RSS. Czytuję polega na pobraniu odnośników w wybranych kategoriach. Dzieje się to dynamicznie, przy każdym załadowaniu strony. Jednak po co? Przecież nowe linki nie są dodawane tak często. Zatem własny mini-system cache może być jak najbardziej na miejscu.
Domyślnie tworzenie kategorii RSS odbywało się tak:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
< ?php
function rss_footer($rss_channels=array()){
    if (!empty($rss_channels)){
        foreach ($rss_channels as $channel){
            if(function_exists('fetch_feed')){
                $limit = ($channel['limit']) ? intval($channel['limit']): 3;
                $feed = fetch_feed($channel['uri']);
                printf($feed->get_title());
                for($i=0;$i< $limit;$i++){
                    $item = $feed->get_item($i);
                    printf('
	<li><a href="http://blog.vokiel.com/">%s</a></li>
',$item->get_permalink(), $item->get_title());
                }
            }
        }
    }
}
?>

Sama funkcja wywoływana w stopce:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<ul>
	<li>
<h2>< ?php _e('RSS'); ?></h2>
<ul>
    < ?php 
        $rss_channels = array(
            array('uri'=>'http://forum.php.pl/index.php?act=rssout&id=1','limit'=>3),
            array('uri'=>'http://webhosting.pl/feed/rss/n/8','limit'=>3),
            array('uri'=>'http://feeds.feedburner.com/nettuts','limit'=>3)
        );
        @include(TEMPLATEPATH . '/rss_footer.php'); 
        if (function_exists('rss_footer')) { rss_footer($rss_channels); }
    ?></ul>
</li>
</ul>

Aby nasz system nie pobierał kanałów rss za każdym przeładowaniu strony zrobimy małe cachowanie wyników do pliku:
Schemat działania:
1. Sprawdzenie czy istnieje plik cache
2. Jeśli plik istnieje – sprawdzenie czy jest aktualny
3a. Jeśli plik jest aktualny – wczytanie i wyświetlenie
3b. Jeśli plik nie jest aktualny, lub nie istnieje – utworzenie go i wyświetlenie
Zatem do dzieła!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
< ?php
function rss_footer($rss_channels=array()){
    // ścieżka wraz z nazwą do pliku cache    
    $cache_file_name = dirname(__FILE__).'/../../cache/rss_footer.cache';
    $cache_time = 21600; // limit czasu ważności cache
    if (!empty($rss_channels)){
        // sprawdzenie czy plik cache istnieje i czy jest aktualny
        if (file_exists($cache_file_name) && filemtime($cache_file_name ) > (time() - $cache_time)){
            //plik aktualny - pobranie
            echo file_get_contents($cache_file_name);
        }else{
            //pobranie wyników
            foreach ($rss_channels as $channel){
                if(function_exists('fetch_feed')){
                    $limit = ($channel['limit']) ? intval($channel['limit']): 3;
                    $feed = fetch_feed($channel['uri']);
                    //dopisanie do cache
                    $cache .= trim($feed->get_title());
                    for($i=0;$i< $limit;$i++){
                        $item = $feed->get_item($i);
                        // dopisanie do cache
                        $cache .= '
	<li><a href="'.trim($item->get_permalink()).'">'.trim($item->get_title()).'</a></li>
';
                    }
                }
            }
            $cache .= "\n".'<!-- Cache created: '.date('Y-m-d H:i:s').'-->';
            // zapis do pliku cache
            file_put_contents($cache_file_name,$cache);
            // wyświetlenie cache
            echo $cache;
        }
    }
}
?>

Zastosowanie powyższego systemu cache dla RSS i Czytuję zmniejszyło użycie pamięci z 31.5 MB do 26.6MB! Co moim zdaniem, przy takim nakładzie pracy jest wartym uwagi 🙂

 

Przeczytaj także

Komentarze: 6

Dodaj komentarz »

 
 
 

A co jeśli ktoś nie może zwiększyć limitu, a do zrobienia cache ma za małą wiedzę? Co pozostaje? Tylko powyłączać przydatne pluginy?

 

Reply

 

Jeśli sam nie stworzysz mini-cache, to możesz wykorzystać WP Super Cache.
Jeśli to nie pomoże, pozostaje przeniesienie bloga na inny serwer – taki, który umożliwi ustawienie limitu pamięci. Zużycie na poziomie 30MB jeszcze nie jest straszne, niektórzy mają większe 😉
Jestem w trakcie pisania kolejnego posta na temat optymalizacji WordPress’a, zajmę się tam też bardziej front-endem.

 

Reply

 

Hey, great blog…but I don’t understand how to add your site in my rss reader. Can you Help me, please 🙂

 

Reply

 

Hi
Just click on the RSS icon in the head section, or use this link: http://blog.vokiel.com/feed

 

Reply

 

Super-Duper site! I am loving it!! Will come back again – taking your feeds too now, Thanks. 🙂

 

Reply

 

Świetnie, dzięki za pożyteczne treści do wykorzystania.

 

Reply

 

Dodaj komentarz

 
(nie będzie publikowany)
 
 
Komentarz
 
 

Dozwolone tagi XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
© 2009 - 2016 Vokiel.com
WordPress Theme by Arcsin modified by Vokiel