1
0
Fork 0

garbage collection changes

This commit is contained in:
Jan Steemann 2015-12-21 16:35:19 +01:00
parent ade47c06dc
commit c0d5973adb
2 changed files with 54 additions and 22 deletions

View File

@ -7,6 +7,13 @@ v3.0.0 (XXXX-XX-XX)
v2.8.0 (XXXX-XX-XX) v2.8.0 (XXXX-XX-XX)
------------------- -------------------
* slightly adjusted V8 garbage collection strategy so that collection eventually
happens in all contexts that hold V8 external references to documents and
collections.
also adjusted default value of `--javascript.gc-frequency` from 10 seconds to
15 seconds, as less internal operations are carried out in JavaScript.
* fixes for AQL optimizer and traversal * fixes for AQL optimizer and traversal
* added `--create-collection-type` option to arangoimp * added `--create-collection-type` option to arangoimp
@ -311,9 +318,9 @@ v2.8.0-alpha1 (2015-12-03)
v2.7.4 (XXXX-XX-XX) v2.7.4 (XXXX-XX-XX)
------------------- -------------------
* randomly shuffle available V8 contexts when picking one for an HTTP or scheduler * slightly adjusted V8 garbage collection strategy so that collection eventually
task. This distributes requests more evenly across contexts so they can eventually happens in all contexts that hold V8 external references to documents and
be garbage-collected. collections.
* added the following attributes to the result of `collection.figures()` and the * added the following attributes to the result of `collection.figures()` and the
corresponding HTTP API at `PUT /_api/collection/<name>/figures`: corresponding HTTP API at `PUT /_api/collection/<name>/figures`:

View File

@ -297,7 +297,7 @@ ApplicationV8::ApplicationV8 (TRI_server_t* server,
_useActions(true), _useActions(true),
_frontendVersionCheck(true), _frontendVersionCheck(true),
_gcInterval(1000), _gcInterval(1000),
_gcFrequency(10.0), _gcFrequency(15.0),
_v8Options(""), _v8Options(""),
_startupLoader(), _startupLoader(),
_vocbase(nullptr), _vocbase(nullptr),
@ -442,9 +442,6 @@ ApplicationV8::V8Context* ApplicationV8::enterContext (TRI_vocbase_t* vocbase,
LOG_TRACE("found unused V8 context"); LOG_TRACE("found unused V8 context");
TRI_ASSERT(! _freeContexts.empty()); TRI_ASSERT(! _freeContexts.empty());
// randomly shuffle free contexts so that different get contexts get used (and garbage collected)
std::random_shuffle(_freeContexts.begin(), _freeContexts.end());
context = _freeContexts.back(); context = _freeContexts.back();
TRI_ASSERT(context != nullptr); TRI_ASSERT(context != nullptr);
@ -629,6 +626,7 @@ void ApplicationV8::collectGarbage () {
// to false again once all contexts have been cleaned up and there is nothing // to false again once all contexts have been cleaned up and there is nothing
// more to do // more to do
volatile bool useReducedWait = false; volatile bool useReducedWait = false;
bool preferFree = false;
// the time we'll wait for a signal // the time we'll wait for a signal
uint64_t const regularWaitTime = (uint64_t) (_gcFrequency * 1000.0 * 1000.0); uint64_t const regularWaitTime = (uint64_t) (_gcFrequency * 1000.0 * 1000.0);
@ -638,9 +636,11 @@ void ApplicationV8::collectGarbage () {
while (_stopping == 0) { while (_stopping == 0) {
V8Context* context = nullptr; V8Context* context = nullptr;
bool wasDirty = false;
{ {
bool gotSignal = false; bool gotSignal = false;
preferFree = ! preferFree;
CONDITION_LOCKER(guard, _contextCondition); CONDITION_LOCKER(guard, _contextCondition);
if (_dirtyContexts.empty()) { if (_dirtyContexts.empty()) {
@ -653,26 +653,39 @@ void ApplicationV8::collectGarbage () {
// the reduced wait time will allow to perfom GC for more contexts // the reduced wait time will allow to perfom GC for more contexts
useReducedWait = ! gotSignal; useReducedWait = ! gotSignal;
} }
if (preferFree && ! _freeContexts.empty()) {
context = pickFreeContextForGc();
}
if (! _dirtyContexts.empty()) { if (context == nullptr &&
! _dirtyContexts.empty()) {
context = _dirtyContexts.back(); context = _dirtyContexts.back();
_dirtyContexts.pop_back(); _dirtyContexts.pop_back();
useReducedWait = false; if (context->_numExecutions < 10 && ! context->_hasActiveExternals) {
// don't collect this one
_freeContexts.emplace_back(context);
context = nullptr;
}
else {
wasDirty = true;
}
} }
else if (! gotSignal && ! _freeContexts.empty()) {
if (context == nullptr &&
! preferFree &&
! gotSignal &&
! _freeContexts.empty()) {
// we timed out waiting for a signal, so we have idle time that we can // we timed out waiting for a signal, so we have idle time that we can
// spend on running the GC pro-actively // spend on running the GC pro-actively
// We'll pick one of the free contexts and clean it up // We'll pick one of the free contexts and clean it up
context = pickFreeContextForGc(); context = pickFreeContextForGc();
}
// there is no context to clean up, probably they all have been cleaned up // there is no context to clean up, probably they all have been cleaned up
// already. increase the wait time so we don't cycle too much in the GC loop // already. increase the wait time so we don't cycle too much in the GC loop
// and waste CPU unnecessary // and waste CPU unnecessary
useReducedWait = (context != nullptr); useReducedWait = (context != nullptr);
}
else {
useReducedWait = false;
}
} }
// update last gc time // update last gc time
@ -680,6 +693,7 @@ void ApplicationV8::collectGarbage () {
gc->updateGcStamp(lastGc); gc->updateGcStamp(lastGc);
if (context != nullptr) { if (context != nullptr) {
// LOG_TRACE("will collect now: %d, numExecutions: %d, hasActive: %d", (int) context->_id, (int) context->_numExecutions, (int) context->_hasActiveExternals);
LOG_TRACE("collecting V8 garbage"); LOG_TRACE("collecting V8 garbage");
bool hasActiveExternals = false; bool hasActiveExternals = false;
auto isolate = context->isolate; auto isolate = context->isolate;
@ -716,10 +730,18 @@ void ApplicationV8::collectGarbage () {
{ {
CONDITION_LOCKER(guard, _contextCondition); CONDITION_LOCKER(guard, _contextCondition);
_freeContexts.emplace_back(context); if (wasDirty) {
_freeContexts.emplace_back(context);
}
else {
_freeContexts.insert(_freeContexts.begin(), context);
}
guard.broadcast(); guard.broadcast();
} }
} }
else {
useReducedWait = false; // sanity
}
} }
_gcFinished = true; _gcFinished = true;
@ -1188,9 +1210,9 @@ ApplicationV8::V8Context* ApplicationV8::pickFreeContextForGc () {
int pickedContextNr = -1; // index of context with lowest GC stamp, -1 means "none" int pickedContextNr = -1; // index of context with lowest GC stamp, -1 means "none"
for (int i = 0; i < n; ++i) { for (int i = n - 1; i > 0; --i) {
// check if there's actually anything to clean up in the context // check if there's actually anything to clean up in the context
if (_freeContexts[i]->_numExecutions == 0 && ! _freeContexts[i]->_hasActiveExternals) { if (_freeContexts[i]->_numExecutions < 10 && ! _freeContexts[i]->_hasActiveExternals) {
continue; continue;
} }
@ -1253,6 +1275,7 @@ bool ApplicationV8::prepareV8Instance (size_t i, bool useActions) {
TRI_ASSERT(context->_locker == nullptr); TRI_ASSERT(context->_locker == nullptr);
// enter a new isolate // enter a new isolate
bool hasActiveExternals = false;
context->_id = i; context->_id = i;
context->isolate = isolate; context->isolate = isolate;
TRI_ASSERT(context->_locker == nullptr); TRI_ASSERT(context->_locker == nullptr);
@ -1343,6 +1366,8 @@ bool ApplicationV8::prepareV8Instance (size_t i, bool useActions) {
break; break;
} }
} }
TRI_GET_GLOBALS();
hasActiveExternals = v8g->hasActiveExternals();
// and return from the context // and return from the context
localContext->Exit(); localContext->Exit();
@ -1357,7 +1382,7 @@ bool ApplicationV8::prepareV8Instance (size_t i, bool useActions) {
// initialize garbage collection for context // initialize garbage collection for context
context->_numExecutions = 0; context->_numExecutions = 0;
context->_hasActiveExternals = true; context->_hasActiveExternals = hasActiveExternals;
context->_lastGcStamp = TRI_microtime() + randomWait; context->_lastGcStamp = TRI_microtime() + randomWait;
LOG_TRACE("initialized V8 context #%d", (int) i); LOG_TRACE("initialized V8 context #%d", (int) i);