Whenever you run a PHP application or any other software, it consumes memory. Basically, all applications require memory to run. With all these apps competing for resources, the computer has to allocate memory efficiently. This brings us to the memory management process. What is it?
Memory management involves freeing, allocating, organizing, and optimizing available computer memory to ensure programs run well. Most of the time, the computer does memory management automatically, but as a PHP developer, you have a duty to manage your app's memory footprint.
Join us in this article as we discuss the techniques you can use to lower your PHP application's memory footprint.
Why is memory management important?
PHP applications usually have a memory limit of about 128 MB. If your application surpasses this limit, you will experience the following fatal error:
Fatal error: Allowed memory size of 94371840 bytes exhausted (tried to allocate 5072 bytes)
The above error will cause your application to crash, which can be quite frustrating. In certain cases, you may experience lags and freezes, ultimately affecting the overall user experience.
Although you can extend your PHP app's memory limit, it can be a long and complicated process that should only be the last resort. A good alternative is reviewing your application and correcting areas that may result in high memory consumption.
You can find the amount of memory your PHP application consumes and its peak usage using the following methods:
// Current memory usage
$memory_usage = memory_get_usage();
// Peak memory usage
$memory_peak = memory_get_peak_usage();
Next, let's look at some common causes of high memory usage in PHP applications.
Common causes of high memory usage
Here are some common causes of high memory usage in PHP applications.
Memory leaks
A memory leak happens when developers allocate a certain percentage of memory to their application but forget to free it once a particular task is complete.
If your application is running on a local server, you will notice a decrease in performance, especially if you are running a time-intensive task. This is because the available memory is quite small, and the computer must still allocate it to other applications.
PHP libraries and extensions
PHP libraries are important because they allow us to add functionality to our project without writing much code. However, overlying on PHP libraries has its drawbacks. For instance, some extensions may end up consuming more memory. The worse thing is that you have little control over them since the code is not open-source.
Failure to update the PHP version
In some cases, your PHP application may consume more memory simply because you are using an old version of PHP. Such versions may have bugs or inefficient code, impacting your app's performance.
Running too many scripts
Websites executing too many scripts concurrently will record high memory usage, especially if they contain inefficient code. Some PHP scripts feature endless loops or timers, meaning they will keep running for a long time. There's a high likelihood of such websites crashing. Additionally, noticeable lags in the user interface will cause a bad user experience.
Now that we know the common causes of high memory consumption in PHP applications, let's proceed and discuss how to reduce your app's memory usage in the following section.
How to reduce your PHP app's memory footprint
As you create or deploy your PHP application, here are tips to reduce its memory footprint.
Fix memory leaks
A PHP app can consume more memory simply because certain sections of your code are still running and consuming resources, yet they're no longer required. As your application grows, such sections can slow performance and cause fatal crashes due to high memory usage.
The first step in fixing memory leaks is to identify which scripts are consuming exceptionally high memory than normal. You can generate a log of your scripts using the following code.
auto_append_file
memory_get
Once the logs are generated, review them and determine which ones consume more memory.
You can also determine the exact amount of memory the scripts consume using the following methods:
//Currently used memory
memory_get_usage();
//Peak memory usage
memory_get_peak_usage();
For example, the following script to calculate the Fibonacci sequence only consumes 389KB
of memory. However, note that the amount of memory may increase as the Fibonacci sequence grows.
$previousValues=array(0,1);
function fibonacci($n) {
global $previousValues;
if(array_key_exists($n, $previousValues) ){
return $previousValues[$n];
}
else {
if($n > 1) {
$result = fibonacci($n-1) + fibonacci($n-2);
$previousValues[$n] = $result;
return $result;
}
return $n;
}
}
$number = 9;
echo fibonacci($number);
/* Displaying used memory */
echo '<br>Memory consumed: ' . round(memory_get_usage() / 1024) . 'KB<br>';
/* Displaing peak memory usage */
echo 'Peak usage: ' . round(memory_get_peak_usage() / 1024) . 'KB of memory.<br><br>';
When you determine that a script is indeed consuming more memory, go through the code and try to identify the responsible sections (memory leaks). You can comment out certain sections of your code (that you suspect to be memory-intensive) and execute the code again. Optimize the functions responsible for the high memory leaks and run your tests again.
Reading files and database records
In some PHP applications, you may need to read, create, modify, and delete files. Although these operations are important, they may lead to high memory usage in your application, especially when incorrectly implemented.
One tip to save memory while handling large files is to read their contents line by line rather than saving the whole file in memory. Let's see why.
The file_get_contents
method is used to retrieve the contents of a file and store it in a string
. This method is executed quickly when a small file is involved, but as the size grows, your application may require more memory and finally crash when the limit is reached.
Here is an illustration of the file_get_contents
method:
//loading a large text file (1GB) using the file_get_contents method
$myfile = file_get_contents('myverylargefile.txt');
echo $myfile;
When you run the above code, you will experience the following error:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 529084788 bytes) in index.php
The above error occurred because PHP attempted to load a file of about 1GB in memory, but the allowed limit for the script is about 128MB.
We can avoid the above error and lower our memory foot my simply reading large files line-by-line using fopen
, fgets
, and feof
methods, as demonstrated below.
//File path.
$file = 'myverylargefile.log';
//Opening the file in "reading only" mode.
$fileHandler = fopen($file, "r");
//We throw an exception in case if we do find the file.
if($fileHandler === false){
throw new Exception('Cannot find: ' . $file);
}
//We use a while loop to go through the file
while(!feof($fileHandler)) {
//We read the current line
$line = fgets($fileHandler);
//We can save line in an array or run any operation.
}
//Close the file handler when done to avoid file corruption
fclose($fileHandler);
The same principle applies when retrieving records from a database. Rather than fetching all entries at once, loop through the database and retrieve records individually.
In the following example, about 642kbs
of memory will be consumed when all data is retrieved from the database simultaneously:
// Fetching all records at once
$results = $stmt->fetchAll();
foreach ($results as $r) { print_r($r); }
Output:
PEAK USAGE - 642kbs
In contrast, 626kbs
of memory will be consumed when you fetch records individually from the database using the following code. Although it may seem minor, it can give your application a huge performance boost, especially when lots of data are involved.
while ($r = $databasestatement->fetch()) {
print_r($r);
}
Output:
PEAK USAGE - 626kbs
Here is the entire code for retrieving MySQL database records. Note that the inefficient code for retrieving all database records at once has been commented out.
//Database connection details
$db_host = "localhost";
$db_name = "products";
$db_charset = "utf8mb4";
$db_user = "root";
$db_password = "";
// Establishing a database connection
$conn = new PDO(
"mysql:host=".$db_host.";dbname=".$db_name.";charset=".$db_charset,
$db_user, $db_password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
// Preparing SQL statements
$databasestatement = $conn->prepare("SELECT * FROM `products`");
$databasestatement->execute();
// Unrecommended method - Fetching all records
// $results = $stmt->fetchAll();
// foreach ($results as $r) { print_r($r); }
// Preferred method - Fetch records line by line
while ($r = $databasestatement->fetch()) {
print_r($r);
}
// Once done retrieving records, close the database connection
if ($databasestatement !== null) { $databasestatement = null; }
if ($conn !== null) { $conn = null; }
// (G) PEAK MEMORY USED
echo "PEAK USAGE - " . round(memory_get_peak_usage() / 1024). "kbs" ;
Use the latest PHP version
Always ensure that you are using the latest version of PHP to build applications. The latest version not only makes your website more memory-efficient but also improves overall security by resolving bugs.
If you have an old website, you can upgrade your existing codebase to use the latest PHP version.
Disable unused plugins
As mentioned, over-reliance on PHP libraries may cause your app to consume more memory. This is because you have little control over them. Therefore, it’s recommended to go through your project and identify and disable plugins you don't need.
You should also research and find out what other developers say about different plugins before incorporating them into your project.
Conclusion
Lowering your PHP app's memory usage can facilitate a better user experience. Not only will your scripts and web pages load faster, but also the likelihood of the website crashing, freezing, or lagging is low.
Reduce your app's memory footprint today by finding and eliminating memory leaks, reading files effectively, and disabling unused scripts and libraries.