Reputation: 1556
I'm running a dockerised node.js application on my server, using the --max-old-space-size
option to limit the applications heap size. The following output is given by htop:
PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command
10158 root 20 0 4284 720 644 S 0.0 0.0 0:00.00 sh -c node --max-old-space-size=512 ./dist/www.js
10159 root 20 0 1841M 929M 29592 S 0.0 3.0 1h31:16 node --max-old-space-size=512 ./dist/www.js
10160 root 20 0 1841M 929M 29592 S 0.0 3.0 0:00.00 node --max-old-space-size=512 ./dist/www.js
10161 root 20 0 1841M 929M 29592 S 0.0 3.0 7:27.13 node --max-old-space-size=512 ./dist/www.js
10162 root 20 0 1841M 929M 29592 S 0.0 3.0 7:26.96 node --max-old-space-size=512 ./dist/www.js
10163 root 20 0 1841M 929M 29592 S 0.0 3.0 7:26.99 node --max-old-space-size=512 ./dist/www.js
10164 root 20 0 1841M 929M 29592 S 0.0 3.0 7:26.64 node --max-old-space-size=512 ./dist/www.js
You can see my applications resident memory (929M) is way above my max-old-space-size
value (512MB) so why is this being seen? Shouldn't the application have aborted by this point?
System Info
Docker version: 19.03.5
node image version: 11.13.0
uname -v
#31~18.04.1-Ubuntu SMP Tue Nov 17 10:48:34 UTC 2020
Upvotes: 3
Views: 1435
Reputation: 5537
The RES that you can see on the top
is the non-swapped region of the virtual memory of a linux process (see man top
for more details). Here is short snipped of the explanation
22. RES -- Resident Memory Size (KiB)
A subset of the virtual address space (VIRT) representing the non-swapped
physical memory a task is currently using. It is also the sum of the
RSan, RSfd and RSsh fields.
It can include private anonymous pages, private pages mapped to files
(including program images and shared libraries) plus shared anonymous pages.
All such memory is backed by the swap file represented separately under SWAP.
The output of top
should not be confused with the node.js
memory model elements, since this output is giving the kernel view of the process, while internal memory organizations (like in this case of node.js
) are not visible.
If you want to get more details on the structure (the memory map) of the process, you can use pmap -x <PID>
and you will see which memory segments are currently in the RES, however this segments are also not to be confused with the node.js
segments (although, some of them can be directly mapped to the node.js
segments).
node.js
memory map, which is the subset of the whole process memory allocated, is called Resident Set
and should not be confused with the Resident Memory Size
- RES
. In fact, the Resident Set
of node.js
is more closer to the value that you can see in VIRT
. The difference between VIRT
and the Resident Set
will be attributed to the unix shared libraries (not to be confused with node.js
modules) that are loaded by the node.js
, files that the process had opened, etc.).
The Resident Set
is further divided in the Code Segment
, Stack Segment
and the Heap Segment
. The Code Segment
, as the name suggests, contains the executable code. The Stack Segment
contains the stack informations of the currently running threads in the process. This blog provides good overview of the memory organization.
What remains from all of this is the Heap Segment
. And here is where the configuration parameter max-old-space-size
has a role to play. The heap of node.js
follows memory model that is very similar to Java garbage collection memory model. The parameter is telling the process how much objects are allowed to survive consecutive garbage collection cycles. However, this doesn't mean that this objects are un-swappable, with other words, it's not given that all of the 512MB that are set will reside in RES
. This is a subsegment of the Heap
. The Old Space
is a sub-segment of the Heap
.
So, what you read in RES
had no direct link to the Old Space Sub-Segment
of the Heap Segment
. In applications where there is huge garbage left from the short leaving applications contexts (variable is the context that are not retained by the application), the Old Generation
will be very small, however the RES
will grow rapidly and the setting will not help you to manage the memory of the process.
Upvotes: -1
Reputation: 40511
V8 developer here. The --max-old-space-size
flag does not directly control the overall process' memory consumption; it puts a limit on one part (the biggest part) of V8's managed (i.e. garbage-collected) heap, which is the chunk of memory where all the JavaScript objects are put. Aside from this "old space", V8 also has a (much smaller) "new space"; and besides V8's managed heap, there are a bunch of other consumers of memory in the process: for example, within V8, parser and compiler use memory outside the managed heap; and then aside from V8, Node puts a bunch of things in memory too. Depending on what exactly the embedder (i.e. Node) and the executing code do, large strings and ArrayBuffers can also live outside of the managed heap.
In short, --max-old-space-size
gives you one knob to affect how much memory will be used, but the limit you set there is not a limit for the process' overall memory consumption. (For comparison, in Chrome, typically about a third of a renderer process' memory is V8's managed heap, though depending on what websites do there are significant outliers in both directions. I don't know typical numbers for Node.)
Upvotes: 7