I've did a test for performance issues caused by GC and memory consumption.
My test run on linux, node v0.8.5
the code:
var obj_num = 5000000;
var fill = {}
>
> for (var i = obj_num; i >= 0; i--) {
>
> fill["fill string*******************" + i] = "fill string" + i;
>
> };
>
> setInterval(function () {
>
> fill[0] = "something" + Date.now();
>
> }, 1);
>
>
if run by "node --trace_gc gc_test.js", the output is:
9407594 ms: Scavenge 849.0 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9418468 ms: Scavenge 849.0 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9429360 ms: Scavenge 849.0 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9440165 ms: Scavenge 849.0 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9450968 ms: Scavenge 849.0 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9453661 ms: Mark-sweep 848.1 (894.0) -> 848.1 (894.0) MB, 484 ms (+ 1385
ms in 3 steps since start of m
arking, biggest step 1152.330811 ms) [idle notification: finalize
incremental] [GC in old space requeste
d].
9455521 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 470 ms (+ 1388
ms in 3 steps since start of m
arking, biggest step 1148.824219 ms) [idle notification: finalize
incremental] [GC in old space requeste
d].
9457355 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 463 ms (+ 1360
ms in 3 steps since start of m
arking, biggest step 1132.174072 ms) [idle notification: finalize
incremental] [GC in old space requeste
d].
9459202 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 463 ms (+ 1372
ms in 3 steps since start of m
arking, biggest step 1139.920898 ms) [idle notification: finalize
incremental] [GC in old space requeste
d].
9461050 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 467 ms (+ 1371
ms in 3 steps since start of m
arking, biggest step 1136.500977 ms) [idle notification: finalize
incremental] [GC in old space requeste
d].
9464581 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 3521 ms [idle
notification: finalize idle rou
nd] [GC in old space requested].
9468118 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 3527 ms [idle
notification: finalize idle rou
nd] [GC in old space requested].
9478934 ms: Scavenge 849.0 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9489861 ms: Scavenge 849.0 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9500980 ms: Scavenge 849.0 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9511772 ms: Scavenge 849.0 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9522651 ms: Scavenge 849.0 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9524948 ms: Mark-sweep 848.1 (894.0) -> 848.1 (894.0) MB, 479 ms (+ 1350
ms in 3 steps since start of marking, biggest step 1121.382812 ms) [idle
notification: finalize incremental] [GC in old space requested].
9526773 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 462 ms (+ 1361
ms in 3 steps since start of marking, biggest step 1132.158936 ms) [idle
notification: finalize incremental] [GC in old space requested].
9528606 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 468 ms (+ 1353
ms in 3 steps since start of marking, biggest step 1121.354980 ms) [idle
notification: finalize incremental] [GC in old space requested].
9530459 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 467 ms (+ 1374
ms in 3 steps since start of marking, biggest step 1143.323975 ms) [idle
notification: finalize incremental] [GC in old space requested].
9532307 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 467 ms (+ 1370
ms in 3 steps since start of marking, biggest step 1139.680908 ms) [idle
notification: finalize incremental] [GC in old space requested].
9535905 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 3587 ms [idle
notification: finalize idle round] [GC in old space requested].
9539484 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 3569 ms [idle
notification: finalize idle round] [GC in old space requested].
9550287 ms: Scavenge 849.0 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9561110 ms: Scavenge 849.0 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
it indicated that gc used several seconds every tens of seconds. As you
know, In this period no user code is executed, and the cpu usage is about
100%.
if run by "node --trace_gc --nouse_idle_notification gc_test.js", the
output is like this:
9284693 ms: Scavenge 850.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9306457 ms: Scavenge 850.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9328158 ms: Scavenge 850.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9349766 ms: Scavenge 850.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9371736 ms: Scavenge 850.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9393432 ms: Scavenge 850.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9415037 ms: Scavenge 850.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9436695 ms: Scavenge 850.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
9458344 ms: Scavenge 850.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC]
No heavy gc is executed in 9458344ms, I didn't do any longer test.
if run by "node --trace_gc --nouse_idle_notification
--max_old_space_size=500 gc_test.js", we can see in the log of previous
case, user code needs more than 500MB. the output is like this:
Marking speed increased to 76
Speed up marking because of low space left
Postponing speeding up marking until marking starts
82169 ms: Mark-sweep 537.8 (577.0) -> 537.8 (578.0) MB, 705 ms (+ 888 ms
in 10 steps since start of m
arking, biggest step 664.453857 ms) [StackGuard GC request] [GC in old
space requested].
Speed up marking because of low space left
Marking speed increased to 3
Speed up marking because of low space left
Marking speed increased to 6
Speed up marking because of low space left
Marking speed increased to 10
Speed up marking because of low space left
Speed up marking because marker was not keeping up
Marking speed increased to 15
Speed up marking because of low space left
Speed up marking because marker was not keeping up
Marking speed increased to 22
Speed up marking because of low space left
Speed up marking because marker was not keeping up
Marking speed increased to 31
Speed up marking because of low space left
Speed up marking because marker was not keeping up
Marking speed increased to 42
Speed up marking because of low space left
Speed up marking because marker was not keeping up
Marking speed increased to 57
Speed up marking because of low space left
Speed up marking because marker was not keeping up
Marking speed increased to 76
Speed up marking because of low space left
Postponing speeding up marking until marking starts
83766 ms: Mark-sweep 538.6 (578.0) -> 538.6 (579.0) MB, 706 ms (+ 882 ms
in 10 steps since start of marking, biggest step 646.981934 ms) [StackGuard
GC request] [GC in old space requested].
Speed up marking because of low space left
Marking speed increased to 3
It indicated that the program is always busy with GC, and with cpu usage
100%, meanwhile there's little chance to execute user code. Default
max_old_space_size is about 1900MB, so if user code needs more memory than
this, will be blocked by gc.
So, the impact of --nouse_idle_notification is more than acceptable, with a
block to program for several seconds every tens of seconds. I read the
relative source code of node and v8, and found this:
// v8, heap.cc
bool Heap::IdleNotification(int hint) {
// Hints greater than this value indicate that
// the embedder is requesting a lot of GC work.
const int kMaxHint = 1000;
// Minimal hint that allows to do full GC.
const int kMinHintForFullGC = 100;
...
if (remaining_mark_sweeps <= 2 && hint >= kMinHintForFullGC) {
CollectAllGarbage(kReduceMemoryFootprintMask,
"idle notification: finalize idle round");
} else {
incremental_marking()->Start();
}
}
because there's "idle notification: finalize idle round" in the log, and
there's only one apperance in v8 src, so that CollectAllGarbage() is called
in IdleNotification. Pay attention to the param "hint", node called
V8::IdleNotification() with default param value 1000, which is the max,
means most aggressive. I changed the value to 1:
// in node.cc
static void Idle(uv_idle_t* watcher, int status) {
assert((uv_idle_t*) watcher == &gc_idle);
if (V8::IdleNotification(1)) {
uv_idle_stop(&gc_idle);
StopGCTimer();
}
}
rebuild node and run by "node --trace_gc gc_test.js", the output is:
7443012 ms: Scavenge 849.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
7453843 ms: Scavenge 849.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
7464650 ms: Scavenge 849.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
7475521 ms: Scavenge 849.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
7486301 ms: Scavenge 849.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
7489050 ms: Mark-sweep 848.2 (894.0) -> 848.1 (894.0) MB, 484 ms (+ 1375
ms in 70 steps since start of marking, biggest step 1140.266113 ms) [idle
notification: finalize incremental] [GC in old space requested].
7490932 ms: Mark-sweep 848.1 (894.0) -> 848.1 (894.0) MB, 491 ms (+ 1387
ms in 70 steps since start of marking, biggest step 1148.691895 ms) [idle
notification: finalize incremental] [GC in old space requested].
7492810 ms: Mark-sweep 848.1 (894.0) -> 848.1 (894.0) MB, 488 ms (+ 1386
ms in 70 steps since start of marking, biggest step 1149.963135 ms) [idle
notification: finalize incremental] [GC in old space requested].
7494681 ms: Mark-sweep 848.1 (894.0) -> 848.1 (894.0) MB, 482 ms (+ 1385
ms in 70 steps since start of marking, biggest step 1150.884033 ms) [idle
notification: finalize incremental] [GC in old space requested].
7496534 ms: Mark-sweep 848.1 (894.0) -> 848.1 (894.0) MB, 479 ms (+ 1370
ms in 70 steps since start of marking, biggest step 1136.897949 ms) [idle
notification: finalize incremental] [GC in old space requested].
7498415 ms: Mark-sweep 848.1 (894.0) -> 848.1 (894.0) MB, 485 ms (+ 1392
ms in 70 steps since start of marking, biggest step 1153.990967 ms) [idle
notification: finalize incremental] [GC in old space requested].
7500279 ms: Mark-sweep 848.1 (894.0) -> 848.1 (894.0) MB, 473 ms (+ 1386
ms in 70 steps since start of marking, biggest step 1149.422119 ms) [idle
notification: finalize incremental] [GC in old space requested].
7511092 ms: Scavenge 849.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
7521936 ms: Scavenge 849.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
7532793 ms: Scavenge 849.1 (894.0) -> 848.1 (894.0) MB, 0 ms
[Runtime::PerformGC].
In contrast with the default value version, there's no:
9464581 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 3521 ms [idle
notification: finalize idle rou
nd] [GC in old space requested].
9468118 ms: Mark-sweep 848.1 (894.0) -> 848.0 (894.0) MB, 3527 ms [idle
notification: finalize idle rou
nd] [GC in old space requested].
saved a lot of time. but [idle notification: finalize incremental] is still
a problem, and I've not find a simple way to change it.
In summary:
1. --nouse_idle_notification is useful, but is there any side effect?
2. If there's not enough for program, node will not crash, but continuously
busy with GC.
3. V8::IdleNotification(1) seems to be better for node, please any
developer pay attention to it.
--
Job Board: http://jobs.nodejs.org/
Posting guidelines:
https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en