Introduction:
JavaScript, the powerhouse behind many applications, sometimes faces limitations when it comes to memory in a Node.js environment. Today, let's dive into a common challenge faced by many applications - the default memory limit in Node.js. In this blog post, we'll explore how to break free from these limitations, boost your application's performance, and optimize memory usage.
Understanding the Challenge:
Node.js applications are confined by a default memory limit set by the runtime environment. This can be a bottleneck for memory-intensive applications. Fortunately, Node.js provides a solution by allowing developers to increase this fixed memory limit, paving the way for improved performance.
Checking Current Heap Size:
Before making any tweaks, it's crucial to grasp your Node.js application's current heap size. The code snippet below, saved in a file named `heapsize.js`, uses the V8 module to retrieve the system's current heap size:
const v8 =require('v8');
const totalHeapSize = v8.getHeapStatistics().total_available_size;
let totalHeapSizeinGB = (totalHeapSize /1024/1024/1024).toFixed(2);
console.log(`Total heap size: ${totalHeapSizeinGB} GB`);
Running this script (`node heapsize.js`) will provide insights into the current heap size of your Node.js application.
How-to run-on Azure App service?
- Navigate to the Advanced Tools Blade on the Azure portal and click on "Go" to access KUDU (SCM site).
- Click on SSH, leading you to the WebSSH session of your app service (`https://<WEBAPPNAME>.scm.azurewebsites.net/webssh/host`).
Please consider the below when you attempt to open SSH session:
- For web app for containers, you need to enable SSH from your Dockerfile. Steps to enable SSH for custom containers.
- For blessed images (Images developed by Microsoft), SSH would be enabled by default, however your app should be up and running without any issues.
- Under configuration blade -> General Settings on Azure app service. Ensure that SSH is "on" as shown below,
After successfully loading your SSH session, navigate to /home and create a file named heapsize.js using the command - touch heapsize.js. Edit the file using the vi editor with the command - vi heapsize.js.
Copy the code snippet above, then save the file by entering `!wq` and pressing Enter.
Run the file using `node heapsize.js`, and you will receive the following output.
Note: In case you encounter v8 module not found issues, attempt to resolve it by installing v8 from SSH using the command - `npm install v8`.
Adjusting Memory Limits:
To increase the memory limit for your Node.js application, use the `--max-old-space-size` flag when starting your script. The value following this flag denotes the maximum memory allocation in megabytes.
For instance, running the following command increases the heap size:
node --max-old-space-size=6000 heapsize.js
This modification results in an expanded total heap size, furnishing your application with more memory resources.
Testing on Azure app service:
In the section on how-to run-on Azure App Service, we've learned how to check the current heap size. Now, for the same file, let's attempt to increase the heap size and conduct a test.
Execute the command "node --max-old-space-size=6000 heapsize.js" in the WebSSH session. You can observe the difference with and without the argument --max-old-space-size=6000.
Automating Memory Adjustments with App Settings on Azure App Service:
For a more streamlined approach, consider adding the `--max-old-space-size` value directly as an app setting. This setting allows you to specify the maximum memory allocation for your Node.js application.
{
"appSettings": [
{
"name": "NODE_OPTIONS",
"value": "--max-old-space-size=<max-memory-in-MB>"
}
]
}
In the above snippet, replace `<max-memory-in-MB>` with the desired maximum memory allocation for your application.
Sample screenshot:
How to choose the value to which you can increase the heap size?
Selecting an appropriate heap size value is contingent upon the constraints of the app service plan. Consider a scenario where you're utilizing a 4-core CPU, and concurrently running three applications on the same app service plan. It's crucial to recognize that the CPU resources are shared among these applications as well as certain system processes.
In such cases, a prudent approach involves carefully allocating the heap size to ensure optimal performance for each application while taking into account the shared CPU resources and potential competition from system processes. Balancing these factors is essential for achieving efficient utilization of the available resources within the specified app service plan limits.
For example:
Let's consider an example where you have a 4-core CPU and a total of 32GB memory in your app service plan. We'll allocate memory to each application while leaving some headroom for system processes.
- Total Memory Available: 32GB
- System Processes Overhead: 4GB
Reserve 4GB for the operating system and other system processes. - Memory for Each Application: (32GB - 4GB) / 3 = 9.33GB per Application
Allocate approximately 9.33GB to each of the three applications running on the app service plan. - Heap Size Calculation for Each Node.js Application:
Suppose you want to allocate 70% of the allocated memory to Node.js heap.
Heap Size per Application = 9.33GB * 0.7 ≈ 6.53GB - Total Heap Size for All Applications: 3 * 6.53GB = 19.59GB
This is the combined heap size for all three Node.js applications running on the app service plan. - Remaining Memory for Other Processes: (32GB - 19.59GB) = 12.41GB
- The remaining memory can be utilized by other processes and for any additional requirements.
These calculations are approximate and can be adjusted based on the specific needs and performance characteristics of your applications. It's essential to monitor the system's resource usage and make adjustments accordingly.
Validating on Azure app Service:
To validate, you can check the docker.log present in the location - /home/Logfiles and check the docker run command as below and you can see the app setting NODE_OPTIONS getting appended to the docker run command.
docker run -d --expose=8080 --name xxxxxx_1_d6a4bcfd -e NODE_OPTIONS=--max-old-space-size=6000 appsvc/node:18-lts_20240207.3.tuxprod
Conclusion:
Optimizing memory limits is pivotal for ensuring the seamless operation of Node.js applications, especially those handling significant workloads. By understanding and adjusting the memory limits, developers can enhance performance and responsiveness. Regularly monitor your application's memory usage and adapt these settings to strike the right balance between resource utilization and performance. With these techniques, unleash the full potential of your JavaScript applications!