V8 garbage collector callbacks for measuring GC activity

百般思念 提交于 2020-03-24 03:07:46

问题


I have a small question related to V8 6.7.240 GC behavior and AddGCPrologueCallback/AddGCEpilogueCallback callbacks.

A little story behind the problem: we launch custom JS code using V8 engine, and in order to limit execution by time we have a watchdog (separate thread with isolate->TerminateExecution() call) that monitors code and kills it in case of long running, but with respect to GC activity.

So, if code execution timeout equals to 200ms, GC activity takes 300ms and code takes 199ms we will be okay (199ms < 200ms, 300ms is not included).

On the other hand, if code execution timeout equals to 200ms, GC activity takes 300ms and code takes 201ms that will be execution timeout (201ms > 200ms, 300ms is not included).

As you can see, that's really important to have precise GC measurements, cuz in case GC activity takes more than callbacks indicates, that may lead to situation in which watchdog will notice that code runs too long and kill it, but in reality the GC activity (along with 'stop the world' approach) 'eating' time without any indication of that.

Seems like that's exactly what we have noticed during our testing and debugging. Lets see:

V8 side:

// init step
api,v8::Context::New
[api,v8::FunctionTemplate::New]

// a little bit fancy way of converting string to object
// like JSON:Parse call, but doing that using global JSON object
// no problem with that, just part of the log
api,v8::String::NewFromUtf8
api,v8::Object::Get
api,v8::String::NewFromUtf8
api,v8::Object::Get
api,v8::String::NewFromUtf8
timer-event-start,V8.GCIncrementalMarking,6406056058
timer-event-end,V8.GCIncrementalMarking,6406056581
api,v8::Function::Call

// actually function run
// script->Run(context);
timer-event-start,V8.Execute,6406057062
timer-event-start,V8.GCIncrementalMarking,6406057179
timer-event-end,V8.GCIncrementalMarking,6406059180
timer-event-start,V8.GCIncrementalMarking,6406060424
timer-event-end,V8.GCIncrementalMarking,6406062569
timer-event-start,V8.GCIncrementalMarking,6406063674
timer-event-end,V8.GCIncrementalMarking,6406065864
timer-event-start,V8.GCIncrementalMarking,6406066891
timer-event-end,V8.GCIncrementalMarking,6406068970
timer-event-start,V8.GCIncrementalMarking,6406069912
timer-event-end,V8.GCIncrementalMarking,6406070711
timer-event-start,V8.GCIncrementalMarking,6406071368
timer-event-end,V8.GCIncrementalMarking,6406073392
timer-event-start,V8.GCIncrementalMarking,6406074204
timer-event-end,V8.GCIncrementalMarking,6406076411
timer-event-start,V8.GCIncrementalMarking,6406077223
timer-event-end,V8.GCIncrementalMarking,6406079326
timer-event-start,V8.GCIncrementalMarking,6406080096
timer-event-end,V8.GCIncrementalMarking,6406082253
timer-event-start,V8.GCIncrementalMarking,6406083041
timer-event-end,V8.GCIncrementalMarking,6406085169
timer-event-start,V8.GCIncrementalMarking,6406085754
timer-event-end,V8.GCIncrementalMarking,6406087852
timer-event-start,V8.GCIncrementalMarking,6406088753
timer-event-end,V8.GCIncrementalMarking,6406090888
timer-event-start,V8.GCIncrementalMarking,6406091704
timer-event-end,V8.GCIncrementalMarking,6406093860
timer-event-start,V8.GCIncrementalMarking,6406094638
timer-event-end,V8.GCIncrementalMarking,6406096819
timer-event-start,V8.GCIncrementalMarking,6406097737
timer-event-end,V8.GCIncrementalMarking,6406099851
timer-event-start,V8.GCIncrementalMarking,6406100651
timer-event-end,V8.GCIncrementalMarking,6406102158
timer-event-start,V8.GCIncrementalMarking,6406102830
timer-event-end,V8.GCIncrementalMarking,6406102949
timer-event-start,V8.GCIncrementalMarkingFinalize,6406103476
timer-event-end,V8.GCIncrementalMarkingFinalize,6406103720
timer-event-start,V8.GCIncrementalMarking,6406103781
timer-event-end,V8.GCIncrementalMarking,6406103805
timer-event-start,V8.GCFinalizeMC,6406106106
markcompact,begin,37,733899,1573929817539
sfi-move,0x26c8c6b42258,0x39727ab446f0
sfi-move,0x26c8c6b423d0,0x39727ab44858
code-move,0x26c8c6b424a8,0x39727ab44920
code-move,0x26c8c6b42990,0x39727ab44e08
[delete,MemoryChunk,0x20803a500000]
markcompact,end,37,750515,1573929817560
timer-event-end,V8.GCFinalizeMC,6406126613
[delete,MemoryChunk,0x2fcf14300000]
timer-event-start,V8.GCIncrementalMarkingStart,
timer-event-end,V8.GCIncrementalMarkingStart,6406148920
timer-event-start,V8.GCIncrementalMarking,6406148975
timer-event-end,V8.GCIncrementalMarking,6406150059
timer-event-start,V8.GCIncrementalMarking,6406151014
timer-event-end,V8.GCIncrementalMarking,6406152657
timer-event-start,V8.GCIncrementalMarking,6406153356
timer-event-end,V8.GCIncrementalMarking,6406154995
timer-event-start,V8.GCIncrementalMarking,6406155703
timer-event-end,V8.GCIncrementalMarking,6406157341
new,MemoryChunk,0x24eee1900000,524288
timer-event-start,V8.GCIncrementalMarking,6406158486
timer-event-end,V8.GCIncrementalMarking,6406160149
new,MemoryChunk,0x7f310200000,524288
timer-event-start,V8.GCIncrementalMarking,6406161218
timer-event-end,V8.GCIncrementalMarking,6406162914
new,MemoryChunk,0x178aad500000,524288
timer-event-start,V8.GCIncrementalMarking,6406163990
timer-event-end,V8.GCIncrementalMarking,6406165681
new,MemoryChunk,0x34d7b2580000,524288
timer-event-start,V8.GCIncrementalMarking,6406166748
timer-event-end,V8.GCIncrementalMarking,6406168439
new,MemoryChunk,0x225fec080000,524288
timer-event-start,V8.GCIncrementalMarking,6406169481
timer-event-end,V8.GCIncrementalMarking,6406171229
new,MemoryChunk,0x502e7380000,524288
timer-event-start,V8.GCIncrementalMarking,6406172280
timer-event-end,V8.GCIncrementalMarking,6406174003
new,MemoryChunk,0x208b2af00000,524288
timer-event-start,V8.GCIncrementalMarking,6406175047
timer-event-end,V8.GCIncrementalMarking,6406176787
new,MemoryChunk,0x39ffd8400000,524288
timer-event-start,V8.GCIncrementalMarking,6406177851
timer-event-end,V8.GCIncrementalMarking,6406179600
new,MemoryChunk,0x10408d480000,524288
timer-event-start,V8.GCIncrementalMarking,6406180631
timer-event-end,V8.GCIncrementalMarking,6406182384
new,MemoryChunk,0x25c069e80000,524288
timer-event-start,V8.GCIncrementalMarking,6406183415
timer-event-end,V8.GCIncrementalMarking,6406185165
new,MemoryChunk,0x20cb51c80000,524288
timer-event-start,V8.GCIncrementalMarking,6406186210
timer-event-end,V8.GCIncrementalMarking,6406186919
new,MemoryChunk,0xe774a300000,524288
timer-event-start,V8.GCIncrementalMarking,6406187940
timer-event-end,V8.GCIncrementalMarking,6406188387
new,MemoryChunk,0x23a0a1180000,524288
timer-event-start,V8.GCIncrementalMarking,6406189172
timer-event-end,V8.GCIncrementalMarking,6406189924
new,MemoryChunk,0x16bb70600000,524288
timer-event-start,V8.GCIncrementalMarking,6406190724
timer-event-end,V8.GCIncrementalMarking,6406192029
new,MemoryChunk,0x18b8a0200000,524288
timer-event-start,V8.GCIncrementalMarking,6406192894
timer-event-end,V8.GCIncrementalMarking,6406194253
new,MemoryChunk,0x11c87c180000,524288
timer-event-start,V8.GCIncrementalMarking,6406195137
timer-event-end,V8.GCIncrementalMarking,6406196462
new,MemoryChunk,0x353b80280000,524288
timer-event-start,V8.GCIncrementalMarking,6406197381
timer-event-end,V8.GCIncrementalMarking,6406198513
new,MemoryChunk,0x991e5a80000,524288
timer-event-start,V8.GCIncrementalMarking,6406199347
timer-event-end,V8.GCIncrementalMarking,6406200659
new,MemoryChunk,0x77559500000,524288
timer-event-start,V8.GCIncrementalMarking,6406201554
timer-event-end,V8.GCIncrementalMarking,6406202952
new,MemoryChunk,0x51fcc580000,524288
timer-event-start,V8.GCIncrementalMarking,6406203842
timer-event-end,V8.GCIncrementalMarking,6406205229
new,MemoryChunk,0x11134ca80000,524288
timer-event-start,V8.GCIncrementalMarking,6406206124
timer-event-end,V8.GCIncrementalMarking,6406207561
new,MemoryChunk,0x17b32eb00000,524288
timer-event-start,V8.GCIncrementalMarking,6406208473
timer-event-end,V8.GCIncrementalMarking,6406209913
new,MemoryChunk,0xa7929400000,524288
timer-event-start,V8.GCIncrementalMarking,6406210795
timer-event-end,V8.GCIncrementalMarking,6406212249
new,MemoryChunk,0x1f76e7200000,524288
timer-event-start,V8.GCIncrementalMarking,6406213115
timer-event-end,V8.GCIncrementalMarking,6406214629
new,MemoryChunk,0x4bb9cd00000,524288
timer-event-start,V8.GCIncrementalMarking,6406215548
timer-event-end,V8.GCIncrementalMarking,6406216079
new,MemoryChunk,0x26fd04080000,524288
timer-event-start,V8.GCIncrementalMarking,6406216911
timer-event-end,V8.GCIncrementalMarking,6406217106
timer-event-start,V8.GCIncrementalMarkingFinalize,6406217606
timer-event-end,V8.GCIncrementalMarkingFinalize,6406217921
new,MemoryChunk,0x3e8b51000000,524288
timer-event-start,V8.GCIncrementalMarking,6406218332
timer-event-end,V8.GCIncrementalMarking,6406218348
timer-event-start,V8.GCFinalizeMC,6406218897
markcompact,begin,38,36462,1573929817653
markcompact,end,38,54306,1573929817670
timer-event-end,V8.GCFinalizeMC,6406237199
new,MemoryChunk,0x3e70a1a00000,524288
new,MemoryChunk,0x14c9e4180000,524288
new,MemoryChunk,0x335947c80000,524288
timer-event-start,V8.GCIncrementalMarkingStart,6406263779
timer-event-end,V8.GCIncrementalMarkingStart,6406264145
new,MemoryChunk,0x1bf6fd00000,524288
timer-event-start,V8.GCIncrementalMarking,6406264575
timer-event-end,V8.GCIncrementalMarking,6406266175
new,MemoryChunk,0x109d6f780000,524288
timer-event-start,V8.GCIncrementalMarking,6406267498
timer-event-end,V8.GCIncrementalMarking,6406269125
new,MemoryChunk,0xcf3b9200000,524288
timer-event-start,V8.GCIncrementalMarking,6406270226
timer-event-end,V8.GCIncrementalMarking,6406271868
new,MemoryChunk,0x33eb2c880000,524288
timer-event-start,V8.GCIncrementalMarking,6406272919
timer-event-end,V8.GCIncrementalMarking,6406274608
new,MemoryChunk,0x10c056a80000,524288
timer-event-start,V8.GCIncrementalMarking,6406275660
timer-event-end,V8.GCIncrementalMarking,6406277318
new,MemoryChunk,0x1846c1880000,524288
timer-event-start,V8.GCIncrementalMarking,6406278406
timer-event-end,V8.GCIncrementalMarking,6406280119
new,MemoryChunk,0x21eed5900000,524288
timer-event-start,V8.GCIncrementalMarking,6406281176
timer-event-end,V8.GCIncrementalMarking,6406282888
new,MemoryChunk,0x323ac6b80000,524288
timer-event-start,V8.GCIncrementalMarking,6406283954
timer-event-end,V8.GCIncrementalMarking,6406285682
new,MemoryChunk,0x3c2009d00000,524288
timer-event-start,V8.GCIncrementalMarking,6406286739
timer-event-end,V8.GCIncrementalMarking,6406288466
new,MemoryChunk,0x3d504cd00000,524288
timer-event-start,V8.GCIncrementalMarking,6406289535
timer-event-end,V8.GCIncrementalMarking,6406291291
new,MemoryChunk,0x235494980000,524288
timer-event-start,V8.GCIncrementalMarking,6406292373
timer-event-end,V8.GCIncrementalMarking,6406294127
new,MemoryChunk,0xecfc3600000,524288
timer-event-start,V8.GCIncrementalMarking,6406295191
timer-event-end,V8.GCIncrementalMarking,6406296997
new,MemoryChunk,0x1a2eae600000,524288
timer-event-start,V8.GCIncrementalMarking,6406298083
timer-event-end,V8.GCIncrementalMarking,6406299894
timer-event-end,V8.Execute,6406300603

Application side:

(1) // kGCTypeIncrementalMarking
[18:43:37.536(1573929817536752)][DEBUG]: GCPrologueCallback type: 4
[18:43:37.536(1573929817536955)][DEBUG]: GCEpilogCallback type: 4
Total: 203 micro


(2) // kGCTypeMarkSweepCompact
[18:43:37.539(1573929817539368)][DEBUG]: GCPrologueCallback type: 2
[18:43:37.559(1573929817559840)][DEBUG]: GCEpilogCallback type: 2
Total: 20472 micro


(3) // kGCTypeIncrementalMarking
[18:43:37.650(1573929817650874)][DEBUG]: GCPrologueCallback type: 4
[18:43:37.651(1573929817651154)][DEBUG]: GCEpilogCallback type: 4
Total: 280 micro


(4) // kGCTypeMarkSweepCompact
[18:43:37.652(1573929817652160)][DEBUG]: GCPrologueCallback type: 2
[18:43:37.670(1573929817670422)][DEBUG]: GCEpilogCallback type: 2
Total: 18262 micro

So, 39217 microseconds or 40ms in total.

Read V8 log in that way is too complicated. So I did some post-processing on it by measuring time between timer-event-start/timer-event-end events.

(V8.GCIncrementalMarking, 523)
(V8.GCIncrementalMarking, 2001)
(V8.GCIncrementalMarking, 2145)
(V8.GCIncrementalMarking, 2190)
(V8.GCIncrementalMarking, 2079)
(V8.GCIncrementalMarking, 799)
(V8.GCIncrementalMarking, 2024)
(V8.GCIncrementalMarking, 2207)
(V8.GCIncrementalMarking, 2103)
(V8.GCIncrementalMarking, 2157)
(V8.GCIncrementalMarking, 2128)
(V8.GCIncrementalMarking, 2098)
(V8.GCIncrementalMarking, 2135)
(V8.GCIncrementalMarking, 2156)
(V8.GCIncrementalMarking, 2181)
(V8.GCIncrementalMarking, 2114)
(V8.GCIncrementalMarking, 1507)
(V8.GCIncrementalMarking, 119)
(V8.GCIncrementalMarkingFinalize, 244)        (1)
(V8.GCIncrementalMarking, 24)
(V8.GCFinalizeMC, 20507)                      (2)
(V8.GCIncrementalMarkingStart, 428)
(V8.GCIncrementalMarking, 1084)
(V8.GCIncrementalMarking, 1643)
(V8.GCIncrementalMarking, 1639)
(V8.GCIncrementalMarking, 1638)
(V8.GCIncrementalMarking, 1663)
(V8.GCIncrementalMarking, 1696)
(V8.GCIncrementalMarking, 1691)
(V8.GCIncrementalMarking, 1691)
(V8.GCIncrementalMarking, 1748)
(V8.GCIncrementalMarking, 1723)
(V8.GCIncrementalMarking, 1740)
(V8.GCIncrementalMarking, 1749)
(V8.GCIncrementalMarking, 1753)
(V8.GCIncrementalMarking, 1750)
(V8.GCIncrementalMarking, 709)
(V8.GCIncrementalMarking, 447)
(V8.GCIncrementalMarking, 752)
(V8.GCIncrementalMarking, 1305)
(V8.GCIncrementalMarking, 1359)
(V8.GCIncrementalMarking, 1325)
(V8.GCIncrementalMarking, 1132)
(V8.GCIncrementalMarking, 1312)
(V8.GCIncrementalMarking, 1398)
(V8.GCIncrementalMarking, 1387)
(V8.GCIncrementalMarking, 1437)
(V8.GCIncrementalMarking, 1440)
(V8.GCIncrementalMarking, 1454)
(V8.GCIncrementalMarking, 1514)
(V8.GCIncrementalMarking, 531)
(V8.GCIncrementalMarking, 195)
(V8.GCIncrementalMarkingFinalize, 315)        (3)
(V8.GCIncrementalMarking, 16)
(V8.GCFinalizeMC, 18302)                      (4)
(V8.GCIncrementalMarkingStart, 366)
(V8.GCIncrementalMarking, 1600)
(V8.GCIncrementalMarking, 1627)
(V8.GCIncrementalMarking, 1642)
(V8.GCIncrementalMarking, 1689)
(V8.GCIncrementalMarking, 1658)
(V8.GCIncrementalMarking, 1713)
(V8.GCIncrementalMarking, 1712)
(V8.GCIncrementalMarking, 1728)
(V8.GCIncrementalMarking, 1727)
(V8.GCIncrementalMarking, 1756)
(V8.GCIncrementalMarking, 1754)
(V8.GCIncrementalMarking, 1806)
(V8.GCIncrementalMarking, 1811)
(Total, 135996)

As you can see we can match only 4 cases, but lots of GCIncrementalMarking activity was unnoticed. And the true total time equals to 136ms. A lot more than 40ms!, that we were able to measure on application side..

I'm aware of Orinoco and parallel approach, but I've no idea whether calls to GCIncrementalMarking blocks code execution or not.

We've noticed same situation a couple of times and in all cases the reason to code termination was incorrect GC measurement on application side in comparison to V8 log file.

Is it intentional and we cannot trust to GC callbacks so much ? Or I've missed something ? As a temporary solution, we've downgraded V8 to 5.6.316 and it works okay on it..

6.7.240 has new GC approach, can it be the root of that problem ?


PS. --single-threaded-gc for 6.7.240 doesn't help either


PPS. some code related to V8 and GC logic:

void cnode::V8Runner::init() {    
    v8::V8::InitializeICU();

    v8::Platform *platform = v8::platform::CreateDefaultPlatform();
    v8::V8::InitializePlatform(platform);
    v8::V8::Initialize();

    auto flags = "--log --log-all --logfile=/tmp/v8.log --nolazy";
    auto isolate = cnode::V8Runner::getIsolate();

    isolate->AddGCPrologueCallback(cnode::V8Runner::_GCPrologue);
    isolate->AddGCEpilogueCallback(cnode::V8Runner::_GCEpilog);
}

void cnode::V8Runner::_GCPrologue(v8::Isolate *isolate, v8::GCType type,
                                  v8::GCCallbackFlags flags) {    
    cnode::V8Runner::_GCPrologueTimePoint = std::chrono::high_resolution_clock::now();
    cnode::V8Runner::_GCEpilogTimePoint = std::chrono::high_resolution_clock::now();

    LOG_DEBUG("GCPrologueCallback type: %d", type);
}

void cnode::V8Runner::_GCEpilog(v8::Isolate *isolate, v8::GCType type,
                                v8::GCCallbackFlags flags) {    
    cnode::V8Runner::_GCEpilogTimePoint = std::chrono::high_resolution_clock::now();

    std::chrono::milliseconds _GCPrologueTimePointMs = std::chrono::duration_cast<std::chrono::milliseconds>(
            cnode::V8Runner::_GCPrologueTimePoint.time_since_epoch());
    std::chrono::milliseconds _GCEpilogTimePointMs = std::chrono::duration_cast<std::chrono::milliseconds>(
            cnode::V8Runner::_GCEpilogTimePoint.time_since_epoch());

    const int diff = _GCEpilogTimePointMs.count() - _GCPrologueTimePointMs.count();

    cnode::V8Runner::_currentGCActionMs += diff;

    cnode::V8Runner::_GCPrologueTimePoint = std::chrono::high_resolution_clock::now();
    cnode::V8Runner::_GCEpilogTimePoint = std::chrono::high_resolution_clock::now();

    LOG_DEBUG("GCEpilogCallback type: %d", type);
}

PPPS. After this post I decided to recheck this using manual call to Isolate::LowMemoryNotification() after each code execution.

Raw V8 Log:

api,v8::Context::New
api,v8::FunctionTemplate::New
api,v8::ObjectTemplate::New
[api,v8::FunctionTemplate::New]
api,v8::String::NewFromUtf8
api,v8::JSON::Parse
[new,MemoryChunk,0x1940b7d00000,524288]
timer-event-start,V8.GCIncrementalMarkingStart,4351782723
timer-event-end,V8.GCIncrementalMarkingStart,4351783066
new,MemoryChunk,0x106dca200000,524288
timer-event-start,V8.GCIncrementalMarking,4351783463
timer-event-end,V8.GCIncrementalMarking,4351785131
new,MemoryChunk,0x225019c00000,524288
timer-event-start,V8.GCIncrementalMarking,4351786485
timer-event-end,V8.GCIncrementalMarking,4351788194
new,MemoryChunk,0xdfc50c80000,524288
timer-event-start,V8.GCIncrementalMarking,4351789202
timer-event-end,V8.GCIncrementalMarking,4351790879
new,MemoryChunk,0x26861a780000,524288
timer-event-start,V8.GCIncrementalMarking,4351791905
timer-event-end,V8.GCIncrementalMarking,4351793611
new,MemoryChunk,0x44086a00000,524288
timer-event-start,V8.GCIncrementalMarking,4351794615
timer-event-end,V8.GCIncrementalMarking,4351796319
new,MemoryChunk,0x4debd600000,524288
timer-event-start,V8.GCIncrementalMarking,4351797315
timer-event-end,V8.GCIncrementalMarking,4351799066
new,MemoryChunk,0x2db69c500000,524288
timer-event-start,V8.GCIncrementalMarking,4351800060
timer-event-end,V8.GCIncrementalMarking,4351801814
new,MemoryChunk,0x1f1825000000,524288
timer-event-start,V8.GCIncrementalMarking,4351802804
timer-event-end,V8.GCIncrementalMarking,4351804535
new,MemoryChunk,0x86246080000,524288
timer-event-start,V8.GCIncrementalMarking,4351805573
timer-event-end,V8.GCIncrementalMarking,4351807383
new,MemoryChunk,0xfc3bb580000,524288
timer-event-start,V8.GCIncrementalMarking,4351808377
timer-event-end,V8.GCIncrementalMarking,4351810192
new,MemoryChunk,0x10451e500000,524288
timer-event-start,V8.GCIncrementalMarking,4351811223
timer-event-end,V8.GCIncrementalMarking,4351813019
new,MemoryChunk,0x16e643c00000,524288
timer-event-start,V8.GCIncrementalMarking,4351814010
timer-event-end,V8.GCIncrementalMarking,4351815842
new,MemoryChunk,0x2a4379600000,524288
timer-event-start,V8.GCIncrementalMarking,4351816837
timer-event-end,V8.GCIncrementalMarking,4351818621
new,MemoryChunk,0x18436f400000,524288
timer-event-start,V8.GCIncrementalMarking,4351819650
timer-event-end,V8.GCIncrementalMarking,4351821491
new,MemoryChunk,0x3b377700000,524288
timer-event-start,V8.GCIncrementalMarking,4351822503
timer-event-end,V8.GCIncrementalMarking,4351824007
new,MemoryChunk,0x1f2f22600000,524288
timer-event-start,V8.GCIncrementalMarking,4351825011
timer-event-end,V8.GCIncrementalMarking,4351825827
new,MemoryChunk,0x1cd733a80000,524288
timer-event-start,V8.GCIncrementalMarking,4351826871
timer-event-end,V8.GCIncrementalMarking,4351827291
new,MemoryChunk,0x3cf039a80000,524288
timer-event-start,V8.GCIncrementalMarking,4351828058
timer-event-end,V8.GCIncrementalMarking,4351828500
new,MemoryChunk,0x31ae7100000,524288
timer-event-start,V8.GCIncrementalMarking,4351829286
timer-event-end,V8.GCIncrementalMarking,4351830586    
new,MemoryChunk,0x2265e4c00000,524288
timer-event-start,V8.GCIncrementalMarking,4351831462
timer-event-end,V8.GCIncrementalMarking,4351832806
new,MemoryChunk,0x34586ae00000,524288
timer-event-start,V8.GCIncrementalMarking,4351833700
timer-event-end,V8.GCIncrementalMarking,4351835122
new,MemoryChunk,0x21c0aa580000,524288    
timer-event-start,V8.GCIncrementalMarking,4351836000
timer-event-end,V8.GCIncrementalMarking,4351837409
new,MemoryChunk,0x2d29cda00000,524288
timer-event-start,V8.GCIncrementalMarking,4351838280
timer-event-end,V8.GCIncrementalMarking,4351839701
new,MemoryChunk,0x216cbbb80000,524288
timer-event-start,V8.GCIncrementalMarking,4351840569
timer-event-end,V8.GCIncrementalMarking,4351842015
new,MemoryChunk,0x1fb82e480000,524288
timer-event-start,V8.GCIncrementalMarking,4351842910
timer-event-end,V8.GCIncrementalMarking,4351844380
new,MemoryChunk,0x329529f00000,524288
timer-event-start,V8.GCIncrementalMarking,4351845255
timer-event-end,V8.GCIncrementalMarking,4351846654    
new,MemoryChunk,0x957ccf80000,524288
timer-event-start,V8.GCIncrementalMarking,4351847553
timer-event-end,V8.GCIncrementalMarking,4351848991
new,MemoryChunk,0x2df008e00000,524288
timer-event-start,V8.GCIncrementalMarking,4351849864
timer-event-end,V8.GCIncrementalMarking,4351851358
new,MemoryChunk,0x8be2be80000,524288
timer-event-start,V8.GCIncrementalMarking,4351852237
timer-event-end,V8.GCIncrementalMarking,4351853720
new,MemoryChunk,0x146846980000,524288
timer-event-start,V8.GCIncrementalMarking,4351854588
timer-event-end,V8.GCIncrementalMarking,4351856080
new,MemoryChunk,0x367cfe280000,524288
timer-event-start,V8.GCIncrementalMarking,4351856969
timer-event-end,V8.GCIncrementalMarking,4351858491
new,MemoryChunk,0x6ed38180000,524288
timer-event-start,V8.GCIncrementalMarking,4351859382
timer-event-end,V8.GCIncrementalMarking,4351860900
new,MemoryChunk,0xfbd5f600000,524288
timer-event-start,V8.GCIncrementalMarking,4351861805
timer-event-end,V8.GCIncrementalMarking,4351863367
new,MemoryChunk,0x1a5e01680000,524288
timer-event-start,V8.GCIncrementalMarking,4351864277
timer-event-end,V8.GCIncrementalMarking,4351865092
new,MemoryChunk,0x24120d700000,524288
timer-event-start,V8.GCIncrementalMarking,4351865984
timer-event-end,V8.GCIncrementalMarking,4351866048
new,MemoryChunk,0x125d39480000,524288
timer-event-start,V8.GCIncrementalMarking,4351866808
timer-event-end,V8.GCIncrementalMarking,4351866824
timer-event-start,V8.GCIncrementalMarkingFinalize,4351867246
timer-event-end,V8.GCIncrementalMarkingFinalize,4351867419
new,MemoryChunk,0x2f31d9400000,524288
timer-event-start,V8.GCIncrementalMarking,4351867767
timer-event-end,V8.GCIncrementalMarking,4351867774
timer-event-start,V8.GCFinalizeMC,4351869855
markcompact,begin,7143,825650,1575032707066
markcompact,end,7143,846265,1575032707086
timer-event-end,V8.GCFinalizeMC,4351890363
[new,MemoryChunk,0x19b52bb00000,524288]
timer-event-start,V8.GCLowMemoryNotification,4351984850
timer-event-start,V8.GCCompactor,4351984910
markcompact,begin,7143,949018,1575032707181
[delete,MemoryChunk,0xd202f700000]
markcompact,end,7144,188469,1575032707268
timer-event-end,V8.GCCompactor,4352073048
[delete,MemoryChunk,0x1c887fa80000]    
timer-event-start,V8.GCCompactor,4352073343
markcompact,begin,7144,192691,1575032707270
new,MemoryChunk,0x33c543500000,524288
new,MemoryChunk,0xd4241800000,524288
[sfi-move,0x8b6f5421390,0xd424181d470]
[delete,MemoryChunk,0x1455b1780000]
markcompact,end,7144,453893,1575032707371
timer-event-end,V8.GCCompactor,4352175676
delete,MemoryChunk,0x25fae9b80000
delete,MemoryChunk,0x2590e9700000
timer-event-end,V8.GCLowMemoryNotification,4352175844
api,v8::ObjectTemplate::New
api,v8::FunctionTemplate::New
api,v8::String::NewFromUtf8

Application log:

(1) // kGCTypeIncrementalMarking
[13:05:07.063(1575032707063270)][DEBUG]: GCPrologueCallback type: 4
[13:05:07.063(1575032707063418)][DEBUG]: GCEpilogCallback type: 4
Total: 148micro

(2) // kGCTypeMarkSweepCompact
[13:05:07.065(1575032707065876)][DEBUG]: GCPrologueCallback type: 2
[13:05:07.086(1575032707086356)][DEBUG]: GCEpilogCallback type: 2
Total: 20480 micro

(3) // kGCTypeMarkSweepCompact
[13:05:07.180(1575032707180925)][DEBUG]: GCPrologueCallback type: 2
[13:05:07.269(1575032707269036)][DEBUG]: GCEpilogCallback type: 2

(4) // kGCTypeMarkSweepCompact
[13:05:07.269(1575032707269366)][DEBUG]: GCPrologueCallback type: 2
[13:05:07.371(1575032707371667)][DEBUG]: GCEpilogCallback type: 2

V8 Log after post-processing:

(V8.GCIncrementalMarkingStart, 343)          (1)?
(V8.GCIncrementalMarking, 1668)
(V8.GCIncrementalMarking, 1709)
(V8.GCIncrementalMarking, 1677)
(V8.GCIncrementalMarking, 1706)
(V8.GCIncrementalMarking, 1704)
(V8.GCIncrementalMarking, 1751)
(V8.GCIncrementalMarking, 1754)
(V8.GCIncrementalMarking, 1731)
(V8.GCIncrementalMarking, 1810)
(V8.GCIncrementalMarking, 1815)
(V8.GCIncrementalMarking, 1796)
(V8.GCIncrementalMarking, 1832)
(V8.GCIncrementalMarking, 1784)
(V8.GCIncrementalMarking, 1841)
(V8.GCIncrementalMarking, 1504)
(V8.GCIncrementalMarking, 816)
(V8.GCIncrementalMarking, 420)
(V8.GCIncrementalMarking, 442)
(V8.GCIncrementalMarking, 1300)
(V8.GCIncrementalMarking, 1344)
(V8.GCIncrementalMarking, 1422)
(V8.GCIncrementalMarking, 1409)
(V8.GCIncrementalMarking, 1421)
(V8.GCIncrementalMarking, 1446)
(V8.GCIncrementalMarking, 1470)
(V8.GCIncrementalMarking, 1399)
(V8.GCIncrementalMarking, 1438)
(V8.GCIncrementalMarking, 1494)
(V8.GCIncrementalMarking, 1483)
(V8.GCIncrementalMarking, 1492)
(V8.GCIncrementalMarking, 1522)
(V8.GCIncrementalMarking, 1518)
(V8.GCIncrementalMarking, 1562)
(V8.GCIncrementalMarking, 815)
(V8.GCIncrementalMarking, 64)
(V8.GCIncrementalMarking, 16)
(V8.GCIncrementalMarkingFinalize, 173)
(V8.GCIncrementalMarking, 7)
(V8.GCFinalizeMC, 20508)                  (2)
// call to LowMemoryNotification
(V8.GCCompactor, 88138)                   (3)
(V8.GCCompactor, 102333)                  (4)
(V8.GCLowMemoryNotification, 190994)
(Total, 452871)

When I saw this, my first idea was - V8.GCLowMemoryNotification (190994) includes V8.GCCompactor(88138) and V8.GCCompactor(102333)..after all (88138 + 102333) < 190994, it makes sense, right ?

So, probably V8.GCIncrementalMarking is also included into V8.GCFinalizeMC. But it turns out it's not the case - 20508 is much smaller than the amount of GCIncrementalMarkings values (50555).

The final result of this story is the same - execution timeout. Time limit for code equals to 200ms.

Call to LowMemoryNotification IS NOT included to the watchdog monitoring. So 452871 - 190994 = 261877.

And remember, we respect GC activity, so 261877 - 20508 = 241369. Still larger than 200ms.

BUT, if we will start to count all V8.GCIncrementalMarking activities (50555microseconds), the final result will be 241369 - 50555 = 190814. 190ms - not a timeout!

I’m still thinking that V8 (6.7.240) does not provide enough accuracy in terms of GC activity.


回答1:


V8 developer here. If I understand this question correctly, then the answer is: GC prologue/epilogue callbacks are invoked at the beginning/end of a major GC cycle, which consists of (possibly hundreds of) incremental marking steps and a somewhat longer finalization phase in the end, all intermingled with regular program execution. The callbacks are not invoked around every incremental marking step, also not invoked around minor GC cycles ("scavenges", which are curiously missing entirely from your various logs above) and are not intended to provide precise timing information. Sorry.

Ideally, you wouldn't have to do any of this. For most programs, time spent in GC is somewhere between 1% and 10% of overall execution time, so shouldn't matter much.

If you do insist on tracking main-thread time spent on major GCs, then your best bet is probably to turn off incremental marking (flag --noincremental-marking). Note that this will make the user experience choppier (instead of hundreds of sub-1ms pauses you'll get a few second-long pauses), but maybe in your scenario you don't care. I don't have a suggestion for covering minor GCs.

That said, if you allow a more general comment, I have doubts on the overall approach. GC activity is triggered by allocations and can be seen as part of the cost of allocating. If your goal is to treat programs "fairly" (maybe even in a competitive sense?), then I would absolutely include GC time in each program's time budget. GC is not something that "randomly strikes well-behaved code out of the blue" -- having to spend time on GC activity is what happens to code that allocates a lot; if code wants to stay in a certain time budget then it better try to allocate as little as possible, which is just one aspect of being fast/efficient.

Maybe the underlying mental model is one of a very simple memory management strategy, where e.g. many snippets of code can run without any GC activity and allocate a couple of megabytes each, and then one of them is the unlucky straw that breaks the camel's back, crosses some threshold, and runs in a major slowdown that cleans up the garbage that everyone else left behind? In such a VM your approach would make total sense, but this is far from how V8 works.

Also, GC is not the only interruption of the main thread, there is also (the foreground parts of) function optimization, which doesn't have any embedder-controlled callbacks around it. If you wanted to exclude all interruptions, you'd have to hack V8 to create a way to exclude those too. On the other hand, my argument from above applies here too: optimization doesn't happen randomly, it happens to long-running code and is usually beneficial to overall performance (otherwise we wouldn't do it), so I think it would be fair/appropriate to include it in the budget just like GC time.



来源:https://stackoverflow.com/questions/59093671/v8-garbage-collector-callbacks-for-measuring-gc-activity

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!