Hal ini sangat penting di situs web dengan lalu lintas tinggi, di mana banyak eksekusi skrip bersamaan terjadi dan masalah terkait memori lebih mudah menyebabkan masalah
Sebelum pindah ke bagian selanjutnya, saya sarankan Anda melihat video singkat berikut, yang menjelaskan dengan baik bagaimana manajemen memori bekerja di PHP dan memberi Anda beberapa tips dasar untuk mengoptimalkan kode Anda
Beberapa jenis skrip PHP sangat berisiko konsumsi memori yang berlebihan
- skrip yang sangat kompleks, terutama jika menyertakan loop bersarang;
- skrip menggunakan sumber daya eksternal seperti file XML, kumpulan hasil basis data, dan koneksi jarak jauh;
- skrip yang selalu aktif seperti daemon PHP
Tentu saja, masalah memori juga dapat terjadi pada skrip jenis lain
Tetapi bagaimana Anda bisa memeriksa berapa banyak memori yang digunakan skrip Anda?
Mudah
library PHP standar memberi Anda dua fungsi yang memberi Anda informasi yang tepat. memory_get_usage() dan memory_get_peak_usage() .
Mari kita lihat cara kerjanya.
[tweet-mudah tweet="Tutorial (dengan contoh) tentang cara memeriksa penggunaan memori skrip PHP Anda" via="no" hashtags="PHP"]
memory_get_usage() mengembalikan jumlah byte yang digunakan oleh skrip saat fungsi dipanggil.
Demikian pula, memory_get_peak_usage() mengembalikan jumlah maksimum memori (sekali lagi, dalam byte) yang digunakan oleh skrip until the function is called.
Sebagai contoh
<?php $big_array = array(); for ($i = 0; $i < 1000000; $i++) { $big_array[] = $i; } echo 'After building the array.<br>'; print_mem(); unset($big_array); echo 'After unsetting the array.<br>'; print_mem(); function print_mem() { /* Currently used memory */ $mem_usage = memory_get_usage(); /* Peak memory usage */ $mem_peak = memory_get_peak_usage(); echo 'The script is now using: <strong>' . round($mem_usage / 1024) . 'KB</strong> of memory.<br>'; echo 'Peak usage: <strong>' . round($mem_peak / 1024) . 'KB</strong> of memory.<br><br>'; }
Di PC saya, saya mengerti
Seperti yang Anda lihat, setelah larik tidak disetel, penggunaan memori saat ini turun menjadi 370Kb, tetapi penggunaan puncaknya tetap sama
(Jika Anda ingin mempelajari cara menyiapkan lingkungan PHP lokal, lihat bab Memulai program pembelajaran PHP saya)
Kedua fungsi menggunakan argumen boolean opsional bernama $real_usage . Nilai standarnya salah.
Apa artinya dan kapan sebaiknya Anda menyetelnya ke true?
Ayo cari tahu
Apakah Anda ingin berbicara dengan saya dan pengembang lain tentang PHP dan pengembangan web? . Kafe Alex PHP
Sampai jumpa di sana 🙂
ARGUMEN $real_usage. BAGAIMANA CARA MENGGUNAKANNYA
Z
$real_usage adalah argumen boolean. Nilai defaultnya adalah false untuk memory_get_usage() dan memory_get_peak_usage().
Jika argumen ini disetel ke true, maka fungsi mengembalikan jumlah penuh dari memori yang dialokasikan . instead of the memory actually used by the script.
Apa bedanya…?
Begini Cara kerjanya
Penerjemah PHP secara dinamis mengalokasikan memori untuk skrip. Memori ini dialokasikan dalam potongan, bagian kecil dari jumlah memori tetap.
Ketika semua memori yang dialokasikan telah digunakan oleh skrip, juru bahasa mengalokasikan potongan lain
Jadi
- pertama , penafsir mencadangkan sebagian memori untuk digunakan skrip;
- lalu skrip secara bertahap mengisinya dengan datanya, sampai potongannya penuh;
- jika skrip membutuhkan lebih banyak memori, juru bahasa mengalokasikan potongan memori lainnya;
- ulang…
Oleh karena itu, jumlah yang dialokasikan memori selalu sedikit lebih besar daripada jumlah digunakan< . memory (how much bigger depends on many factors, including the PHP configuration and the operating system settings).
Untuk inilah $real_usage digunakan
- Jika $real_usage disetel ke false (default) , maka fungsi mengembalikan yang digunakan . only.
- Jika $real_usage disetel ke true, maka fungsi mengembalikan seluruh memori yang dialokasikan .
Contoh berikut menunjukkan bagaimana memori yang dialokasikan bertambah seiring dengan memori yang sebenarnya digunakan oleh skrip
<table align="center" valign="top"> <tr> <td>Used memory</td> <td>Allocated memory</td> <td>Difference</td> </tr> <?php $big_array = array(); for ($i = 0; $i < 100000; $i++) { $big_array[] = 'aaa' . $i; if (($i % 7000) == 0) { /* With $real_usage = false (the default value) */ $used_mem = round(memory_get_usage(false) / 1024); /* With $real_usage = true */ $alloc_mem = round(memory_get_usage(true) / 1024); echo '<tr><td style="color: green; border: 1px solid #888;">' . $used_mem . 'KB</td>'; echo '<td style="color: blue; border: 1px solid #888;">' . $alloc_mem . 'KB</td>'; echo '<td style="color: red; border: 1px solid #888;">' . ($alloc_mem - $used_mem) . 'KB</td></tr>'; } } _
Inilah hasilnya
Perhatikan bagaimana memori yang digunakan (kolom pertama) tumbuh secara bertahap, sedangkan memori yang dialokasikan (kolom kedua) tumbuh dalam potongan
Catatan
jika Anda mencoba menjalankan contoh ini dan mendapatkan hasil yang aneh, seperti memori yang dialokasikan lebih kecil dari memori yang digunakan, mungkin karena beberapa sistem caching
Cara untuk menghindari masalah tersebut adalah dengan memulai ulang server web tepat sebelum menjalankan skrip
Kapan Anda harus menyetel $real_usage ke true?
Inilah aturan praktisnya
jika Anda ingin melihat berapa banyak memori sistem yang digunakan skrip Anda, maka Anda harus menyetel . to true.
Mengapa?
Karena memori yang dialokasikan, meskipun tidak benar-benar digunakan oleh skrip, masih dicadangkan untuknya dan tidak tersedia untuk proses sistem lainnya
Dengan kata lain, ini adalah jumlah RAM sistem yang dicadangkan untuk proses PHP
Sebaliknya, jika Anda ingin membuat profil skrip PHP Anda , lebih baik biarkan false because you will see the memory usage in more detail.
Faktanya, dengan cara itu Anda dapat melihat berapa banyak memori yang digunakan oleh potongan kode tertentu terlepas dari berapa banyak memori yang tersedia untuk skrip secara keseluruhan
Mari kita lihat beberapa contoh cara melakukannya.
CARA MEMANTAU LOOP
Di dalam struktur loop penggunaan memori yang berlebihan dapat menimbulkan masalah, terutama jika penggunaan ini terus bertambah setelah setiap iterasi loop.
Jika Anda ingin memastikan loop tidak menggunakan terlalu banyak memori, sebaiknya buat profilnya
Bagaimana kamu melakukannya?
Pertama-tama, Anda harus mencetak penggunaan memori sebelum dan sesudah pengulangan untuk memeriksa perbedaannya.
Jika Anda melihat bahwa penggunaan memori terlalu tinggi, Anda juga harus memeriksa bagian dalam loop itu sendiri
Parameter $real_usage sebaiknya dibiarkan false, karena Anda ingin profil your code in detail more than your whole script.
Ini juga berguna untuk melihat berapa banyak memori maksimum yang tersedia sedang digunakan
Skrip PHP hanya dapat menggunakan memori dalam jumlah tertentu. Nilai ini diatur dalam variabel memory_limit dari file php.ini. file ini (file konfigurasi utama PHP)
Anda dapat mengambil nilai tersebut dengan fungsi ini_get() , mengubahnya menjadi byte dan membandingkannya dengan penggunaan memori skrip
Ini sebuah contoh
<?php $mem_usage = memory_get_usage(); echo 'Memory usage before the loop: <strong>' . round($mem_usage / 1024) . 'KB</strong><br>'; /* Get the memory limit in bytes. */ $mem_limit = get_memory_limit(); echo 'Memory limit: <strong>' . round($mem_limit / 1048576) . 'MB</strong><br>'; $array = array(); for ($i = 0; $i < 100000; $i++) { $array[] = $i; /* Check the memory usage every 10000 iterations. */ if (($i % 10000) == 0) { $mem_usage = memory_get_usage(); $limit_perc = $mem_usage * 100 / $mem_limit; echo 'Memory usage after iteration ' . $i . ': <strong>' . round($mem_usage / 1024) . 'KB</strong> '; echo '(' . round($limit_perc) . '% of the available memory for the script)<br>'; } } $mem_usage = memory_get_usage(); echo 'Memory usage after the loop: <strong>' . round($mem_usage / 1024) . 'KB</strong><br>'; /* Parse the memory_limit variable from the php.ini file. */ function get_memory_limit() { $limit_string = ini_get('memory_limit'); $unit = strtolower(mb_substr($limit_string, -1 )); $bytes = intval(mb_substr($limit_string, 0, -1), 10); switch ($unit) { case 'k': $bytes *= 1024; break 1; case 'm': $bytes *= 1048576; break 1; case 'g': $bytes *= 1073741824; break 1; default: break 1; } return $bytes; }
CARA MEMANTAU DAEMON PHP
Daemon PHP adalah skrip perulangan yang tidak pernah berakhir
Mereka sangat berguna untuk tugas pemeliharaan dan pemantauan, seperti sistem pemberitahuan email otomatis dan operasi pemeliharaan
Sama seperti aplikasi selalu aktif, daemon PHP juga dapat mengalami kebocoran memori.
Praktik yang baik adalah memeriksa penggunaan memori di awal setiap putaran dan melakukan sesuatu jika ternyata terlalu tinggi
Misalnya, Anda dapat membunuh atau menghentikan sementara . send a notification email (you will see how to do it in the next chapter).
Untuk tujuan ini, sebaiknya atur $real_usage ke true, karena Anda sekarang lebih tertarik pada berapa banyak memori yang benar-benar digunakan .
Tentu saja, jika masalah memori terjadi, Anda perlu membuat profil skrip dengan hati-hati untuk melihat dari mana asal masalah (untuk itu, akan lebih baik membiarkan $real_usage menjadi false)
Sebagai contoh, mari kita lihat cara menghentikan daemon jika memori yang digunakan lebih dari 50Mb
<?php /* Remove the execution time limit */ set_time_limit(0); /* Iteration interval in seconds */ $sleep_time = 30; /* Memory limit (50Mb) */ $mem_limit = 52428800; $array = array(); $iteration = 0; while (TRUE) { $iteration++; /* Memory check */ if (memory_get_usage(true) >= $mem_limit) { echo 'Memory limit exceeded after iteration ' . strval($iteration); die(); } /* Sleep for the iteration interval */ sleep($sleep_time); for ($i = 0; $i < 500000; $i++) { $array[] = $i; } }
Anda juga dapat menghentikan daemon ketika penggunaan memorinya melebihi persentase tertentu dari penggunaan maksimum yang diperbolehkan
Untuk melakukan ini, Anda perlu membandingkan penggunaan memori dengan nilai memory_limit, seperti yang Anda lihat pada contoh loop sebelumnya
Inilah contohnya
<?php /* Remove the execution time limit. */ set_time_limit(0); /* Get the memory limit in bytes. */ $mem_limit = get_memory_limit(); /* Iteration interval in seconds. */ $sleep_time = 5; /* Max memory usage: 10% of the memory_limit. */ $mem_perc_limit = 20; $array = array(); $iteration = 0; while (TRUE) { $iteration++; /* Memory check. */ $mem_usage = memory_get_usage(true); $perc_usage = $mem_usage * 100 / $mem_limit; if ($perc_usage >= $mem_perc_limit) { echo 'Memory limit exceeded after iteration ' . $iteration . ' (' . round($perc_usage) . '%)'; die(); } /* Sleep for the iteration interval */ sleep($sleep_time); for ($i = 0; $i < 500000; $i++) { $array[] = $i; } } /* Parse the memory_limit variable from the php.ini file. */ function get_memory_limit() { $limit_string = ini_get('memory_limit'); $unit = strtolower(mb_substr($limit_string, -1 )); $bytes = intval(mb_substr($limit_string, 0, -1), 10); switch ($unit) { case 'k': $bytes *= 1024; break 1; case 'm': $bytes *= 1048576; break 1; case 'g': $bytes *= 1073741824; break 1; default: break 1; } return $bytes; } _
PANAS UNTUK MENDAPATKAN PEMBERITAHUAN EMAIL KETIKA MEMORY TERLALU BANYAK DIGUNAKAN
Terkadang, men-debug dan membuat profil penggunaan memori skrip PHP bisa jadi sulit
Tidak selalu mudah untuk mengambil output dari memory_get_usage() dan memory_get_peak_usage(), especially when the memory usage depends on the request string values and other factors.
Dalam beberapa kasus, masalah memori mungkin muncul hanya dalam keadaan tertentu atau hanya setelah beberapa waktu (terutama dalam daemon PHP)
Hanya mengulangi penggunaan memori tidak selalu cukup baik
Anda tidak bisa hanya menatap layar Anda menunggu kebocoran memori terjadi, bukan?
Salah satu solusinya adalah log informasi penggunaan memori ke file log atau database.
Tetapi solusi ini juga tidak sempurna, karena Anda akan mendapatkan banyak log yang perlu Anda baca dan kelola
Solusi yang lebih cerdas adalah membuat PHP melakukan kerja keras
Alih-alih mencetak atau mencatat semuanya, cukup periksa apakah penggunaan memori terlalu tinggi
Jika tidak, lanjutkan eksekusi skrip. Jika tidak, kirimkan email pemberitahuan kepada Anda sendiri bersama dengan informasi debug yang berguna.
Prosedur sederhana ini menghilangkan kebutuhan Anda untuk secara aktif memeriksa skrip Anda
Faktanya, skrip itu sendiri akan memberi tahu Anda jika ada yang salah 😉
Jauh lebih baik, bukan?
Jadi, bagaimana ini bisa dilakukan dalam praktik?
Mari kembali ke contoh daemon
Sekarang, sebelum mematikan skrip ketika penggunaan memori terlalu tinggi, kami mengirim email agar kami tahu bahwa skrip ini telah dihentikan
Kami juga melampirkan file dengan jejak kembali debug dan satu dengan semua variabel yang ditentukan untuk memudahkan kami men-debug masalah
CATATAN. Anda perlu menginstal PHPMailer terlebih dahulu. Ikuti tutorial PHPMailer saya untuk mempelajari cara melakukannya.
Ini kodenya
<?php /* PHPMailer */ use \PHPMailer\PHPMailer\PHPMailer; use \PHPMailer\PHPMailer\Exception; require 'C:\xampp\htdocs\PHPMailer-master\src\Exception.php'; require 'C:\xampp\htdocs\PHPMailer-master\src\PHPMailer.php'; require 'C:\xampp\htdocs\PHPMailer-master\src\SMTP.php'; /* Remove the execution time limit */ set_time_limit(0); /* Iteration interval in seconds */ $sleep_time = 1; /* Memory limit (50Mb) */ $mem_limit = 52428800; $array = array(); $iteration = 0; while (TRUE) { $iteration++; /* Memory check */ if (memory_get_usage(true) >= $mem_limit) { send_debug_email(); echo 'This PHP script has been killed for using too much memory. An email has been sent.'; die(); } /* Sleep for the iteration interval */ sleep($sleep_time); for ($i = 0; $i < 500000; $i++) { $array[] = $i; } } function send_debug_email() { $mail = new PHPMailer(true); try { /* Source address */ $mail->setFrom('php@mysite'); /* Destination address */ $mail->addAddress('my_address@mysite'); /* Subject */ $mail->Subject = 'PHP script memory error'; /* Message */ $mail->Body = 'This PHP script has been killed for using too much memory.'; /* Add debug info as attachment */ $mail->addStringAttachment(print_r(debug_backtrace(), true), 'debug_backtrace.txt'); $mail->addStringAttachment(print_r(get_defined_vars(), true), 'defined_vars.txt'); /* Use SMTP. */ $mail->isSMTP(); /* SMTP server adrress. */ $mail->Host = 'mysmtp_server'; /* Use SMTP authentication. */ $mail->SMTPAuth = TRUE; /* Set the encryption system. */ $mail->SMTPSecure = 'tls'; /* SMTP authentication username. */ $mail->Username = 'smtp@mysite'; /* SMTP authentication password. */ $mail->Password = 'smtp_passwd'; /* Set the SMTP port. */ $mail->Port = 587; /* Send the mail. */ $mail->send(); } catch (Exception $e) { echo $e->errorMessage(); } catch (\Exception $e) { echo $e->getMessage(); } }
PERHATIAN. SUMBER DAYA
Ada satu masalah dengan memory_get_usage() dan memory_get_peak_usage()
mereka TIDAK memperhitungkan sumber daya .
Jenis resource PHP mencakup variabel yang ditangani oleh library eksternal atau sistem operasi, termasuk
- koneksi jarak jauh (HTTP, FTP...)
- File XML dibuat dengan SimpleXML
- koneksi basis data
- …dan banyak lagi
Memori sistem untuk jenis ini tidak ditangani oleh mesin inti PHP itu sendiri
Perpustakaan eksternal yang melakukan itu
Oleh karena itu, memory_get_[peak]_usage() tidak memasukkannya ke dalam hasilnya dan Anda perlu mengambilnya dengan cara lain
Posting dari drib tech ini dengan jelas menjelaskan situasi ini dan juga memberikan solusi
Dapatkan jumlah nyata dari memori yang dialokasikan oleh PHP
Solusi yang Anda temukan di posting di atas hanya berfungsi pada sistem Linux
Tapi jangan khawatir
jika Anda menggunakan Windows, Anda dapat menggunakan kode berikut yang bergantung pada alat jendela daftar tugas
<?php /* Load a big XML file to see the memory usage difference */ $xml_file = 'C:\xampp\htdocs\big_xml_file.xml'; $xml = simplexml_load_file($xml_file); if ($xml === false) { die('Unable to load and parse the XML file: "' . $xml_file . '"'); } /* Tasklist command to get info about the process ID running this script */ $cmd = 'tasklist /fi "pid eq ' . strval(getmypid()) . '"'; /* Get the output from tasklist */ $tasklist = trim(exec($cmd, $output)); /* Parse the output to get the memory usage value */ $mem_val = mb_strrchr($tasklist, ' ', TRUE); $mem_val = trim(mb_strrchr($mem_val, ' ', FALSE)); $mem_val = str_replace('.', '', $mem_val); echo 'Memory usage from <em>memory_get_usage()</em>: ' . round(memory_get_usage(true) / 1024) . 'KB<br>'; echo 'Memory usage from <em>tasklist</em>: ' . $mem_val . 'KB<br>'; _
KESIMPULAN
Sekarang Anda tahu mengapa penting untuk menjaga agar penggunaan memori skrip tetap terkendali, dan Anda tahu cara melakukannya
Anda mempelajari perbedaan antara memory_get_usage() dan memory_get_peak_usage() dan apa sebenarnya arti dari argumen $real_usage
Anda juga melihat bagaimana memasukkan jenis sumber daya eksternal dalam perhitungan, baik pada sistem Linux maupun Windows