Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(212)

Unified Diff: src/global-handles.cc

Issue 7062004: Move young independent handles to the end of the global handle's list. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« src/global-handles.h ('K') | « src/global-handles.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/global-handles.cc
diff --git a/src/global-handles.cc b/src/global-handles.cc
index e4bbc9582e6d33e755e67a352c1d9679241ad394..7e59d9c5b3cd0c227ea94abf1c336290842441ea 100644
--- a/src/global-handles.cc
+++ b/src/global-handles.cc
@@ -41,213 +41,97 @@ ObjectGroup::~ObjectGroup() {
}
-class GlobalHandles::Node : public Malloced {
- public:
-
- void Initialize(Object* object) {
- // Set the initial value of the handle.
- object_ = object;
- class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
- independent_ = false;
- state_ = NORMAL;
- parameter_or_next_free_.parameter = NULL;
- callback_ = NULL;
- }
-
- Node() {
- state_ = DESTROYED;
- }
-
- explicit Node(Object* object) {
- Initialize(object);
- // Initialize link structure.
- next_ = NULL;
- }
-
- ~Node() {
- if (state_ != DESTROYED) Destroy(Isolate::Current()->global_handles());
+GlobalHandles::Node::~Node() {
+ if (state_ != DESTROYED) Destroy(Isolate::Current()->global_handles());
#ifdef DEBUG
- // Zap the values for eager trapping.
- object_ = NULL;
- next_ = NULL;
- parameter_or_next_free_.next_free = NULL;
+ // Zap the values for eager trapping.
+ object_ = NULL;
+ next_ = NULL;
+ prev_ = NULL;
+ parameter_or_next_free_.next_free = NULL;
#endif
- }
-
- void Destroy(GlobalHandles* global_handles) {
- if (state_ == WEAK || IsNearDeath()) {
- global_handles->number_of_weak_handles_--;
- if (object_->IsJSGlobalObject()) {
- global_handles->number_of_global_object_weak_handles_--;
- }
- }
- state_ = DESTROYED;
- }
-
- // Accessors for next_.
- Node* next() { return next_; }
- void set_next(Node* value) { next_ = value; }
- Node** next_addr() { return &next_; }
-
- // Accessors for next free node in the free list.
- Node* next_free() {
- ASSERT(state_ == DESTROYED);
- return parameter_or_next_free_.next_free;
- }
- void set_next_free(Node* value) {
- ASSERT(state_ == DESTROYED);
- parameter_or_next_free_.next_free = value;
- }
+}
- // Returns a link from the handle.
- static Node* FromLocation(Object** location) {
- ASSERT(OFFSET_OF(Node, object_) == 0);
- return reinterpret_cast<Node*>(location);
- }
- // Returns the handle.
- Handle<Object> handle() { return Handle<Object>(&object_); }
-
- // Make this handle weak.
- void MakeWeak(GlobalHandles* global_handles, void* parameter,
- WeakReferenceCallback callback) {
- LOG(global_handles->isolate(),
- HandleEvent("GlobalHandle::MakeWeak", handle().location()));
- ASSERT(state_ != DESTROYED);
- if (state_ != WEAK && !IsNearDeath()) {
- global_handles->number_of_weak_handles_++;
- if (object_->IsJSGlobalObject()) {
- global_handles->number_of_global_object_weak_handles_++;
- }
- }
- state_ = WEAK;
- set_parameter(parameter);
- callback_ = callback;
- }
- void ClearWeakness(GlobalHandles* global_handles) {
- LOG(global_handles->isolate(),
- HandleEvent("GlobalHandle::ClearWeakness", handle().location()));
- ASSERT(state_ != DESTROYED);
- if (state_ == WEAK || IsNearDeath()) {
- global_handles->number_of_weak_handles_--;
- if (object_->IsJSGlobalObject()) {
- global_handles->number_of_global_object_weak_handles_--;
- }
+bool GlobalHandles::Node::PostGarbageCollectionProcessing(
+ Isolate* isolate, GlobalHandles* global_handles) {
+ if (state_ != Node::PENDING) return false;
+ LOG(isolate, HandleEvent("GlobalHandle::Processing", handle().location()));
+ WeakReferenceCallback func = callback();
+ if (func == NULL) {
+ Destroy(global_handles);
+ return false;
+ }
+ void* par = parameter();
+ state_ = NEAR_DEATH;
+ set_parameter(NULL);
+
+ v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle());
+ {
+ // Forbid reuse of destroyed nodes as they might be already deallocated.
+ // It's fine though to reuse nodes that were destroyed in weak callback
+ // as those cannot be deallocated until we are back from the callback.
+ global_handles->set_first_free(NULL);
+ if (global_handles->first_deallocated()) {
+ global_handles->first_deallocated()->set_next(global_handles->head());
}
- state_ = NORMAL;
- set_parameter(NULL);
- }
-
- void MarkIndependent(GlobalHandles* global_handles) {
- LOG(global_handles->isolate(),
- HandleEvent("GlobalHandle::MarkIndependent", handle().location()));
- ASSERT(state_ != DESTROYED);
- independent_ = true;
- }
-
- bool IsNearDeath() {
- // Check for PENDING to ensure correct answer when processing callbacks.
- return state_ == PENDING || state_ == NEAR_DEATH;
- }
-
- bool IsWeak() {
- return state_ == WEAK;
- }
+ // Check that we are not passing a finalized external string to
+ // the callback.
+ ASSERT(!object_->IsExternalAsciiString() ||
+ ExternalAsciiString::cast(object_)->resource() != NULL);
+ ASSERT(!object_->IsExternalTwoByteString() ||
+ ExternalTwoByteString::cast(object_)->resource() != NULL);
+ // Leaving V8.
+ VMState state(isolate, EXTERNAL);
+ func(object, par);
+ }
+ // Absense of explicit cleanup or revival of weak handle
+ // in most of the cases would lead to memory leak.
+ ASSERT(state_ != NEAR_DEATH);
+ return true;
+}
- bool CanBeRetainer() {
- return state_ != DESTROYED && state_ != NEAR_DEATH;
- }
- void SetWrapperClassId(uint16_t class_id) {
- class_id_ = class_id;
+void GlobalHandles::Node::MakeWeak(GlobalHandles* global_handles,
+ void* parameter,
+ WeakReferenceCallback callback) {
+ LOG(global_handles->isolate(),
+ HandleEvent("GlobalHandle::MakeWeak", handle().location()));
+ ASSERT(state_ != DESTROYED);
+ if (state_ != WEAK && !IsNearDeath()) {
+ global_handles->number_of_weak_handles_++;
+ if (object_->IsJSGlobalObject()) {
+ global_handles->number_of_global_object_weak_handles_++;
+ }
}
+ state_ = WEAK;
+ set_parameter(parameter);
+ callback_ = callback;
+}
- // Returns the id for this weak handle.
- void set_parameter(void* parameter) {
- ASSERT(state_ != DESTROYED);
- parameter_or_next_free_.parameter = parameter;
- }
- void* parameter() {
- ASSERT(state_ != DESTROYED);
- return parameter_or_next_free_.parameter;
- }
- // Returns the callback for this weak handle.
- WeakReferenceCallback callback() { return callback_; }
-
- bool PostGarbageCollectionProcessing(Isolate* isolate,
- GlobalHandles* global_handles) {
- if (state_ != Node::PENDING) return false;
- LOG(isolate, HandleEvent("GlobalHandle::Processing", handle().location()));
- WeakReferenceCallback func = callback();
- if (func == NULL) {
- Destroy(global_handles);
- return false;
- }
- void* par = parameter();
- state_ = NEAR_DEATH;
- set_parameter(NULL);
-
- v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle());
- {
- // Forbid reuse of destroyed nodes as they might be already deallocated.
- // It's fine though to reuse nodes that were destroyed in weak callback
- // as those cannot be deallocated until we are back from the callback.
- global_handles->set_first_free(NULL);
- if (global_handles->first_deallocated()) {
- global_handles->first_deallocated()->set_next(global_handles->head());
- }
- // Check that we are not passing a finalized external string to
- // the callback.
- ASSERT(!object_->IsExternalAsciiString() ||
- ExternalAsciiString::cast(object_)->resource() != NULL);
- ASSERT(!object_->IsExternalTwoByteString() ||
- ExternalTwoByteString::cast(object_)->resource() != NULL);
- // Leaving V8.
- VMState state(isolate, EXTERNAL);
- func(object, par);
+void GlobalHandles::Node::ClearWeakness(GlobalHandles* global_handles) {
antonm 2011/05/23 19:44:19 do we want to persist independence related state a
Vyacheslav Egorov (Chromium) 2011/05/23 20:08:15 I think independence is more a property of an obje
antonm 2011/05/24 10:58:38 I am more concerned with is_in_independent_tail_
+ LOG(global_handles->isolate(),
+ HandleEvent("GlobalHandle::ClearWeakness", handle().location()));
+ ASSERT(state_ != DESTROYED);
+ if (state_ == WEAK || IsNearDeath()) {
+ global_handles->number_of_weak_handles_--;
+ if (object_->IsJSGlobalObject()) {
+ global_handles->number_of_global_object_weak_handles_--;
}
- // Absense of explicit cleanup or revival of weak handle
- // in most of the cases would lead to memory leak.
- ASSERT(state_ != NEAR_DEATH);
- return true;
}
+ state_ = NORMAL;
+ set_parameter(NULL);
+}
- // Place the handle address first to avoid offset computation.
- Object* object_; // Storage for object pointer.
-
- uint16_t class_id_;
-
- // Transition diagram:
- // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
- enum State {
- NORMAL, // Normal global handle.
- WEAK, // Flagged as weak but not yet finalized.
- PENDING, // Has been recognized as only reachable by weak handles.
- NEAR_DEATH, // Callback has informed the handle is near death.
- DESTROYED
- };
- State state_ : 4; // Need one more bit for MSVC as it treats enums as signed.
-
- bool independent_ : 1;
-
- private:
- // Handle specific callback.
- WeakReferenceCallback callback_;
- // Provided data for callback. In DESTROYED state, this is used for
- // the free list link.
- union {
- void* parameter;
- Node* next_free;
- } parameter_or_next_free_;
-
- // Linkage for the list.
- Node* next_;
-
- public:
- TRACK_MEMORY("GlobalHandles::Node")
-};
+
+void GlobalHandles::Node::MarkIndependent(GlobalHandles* global_handles) {
+ LOG(global_handles->isolate(),
+ HandleEvent("GlobalHandle::MarkIndependent", handle().location()));
+ ASSERT(state_ != DESTROYED);
+ independent_ = true;
+}
class GlobalHandles::Pool {
@@ -312,7 +196,7 @@ GlobalHandles::GlobalHandles(Isolate* isolate)
: isolate_(isolate),
number_of_weak_handles_(0),
number_of_global_object_weak_handles_(0),
- head_(NULL),
+ young_independent_anchor_(&anchor_node_),
first_free_(NULL),
first_deallocated_(NULL),
pool_(new Pool()),
@@ -339,11 +223,15 @@ Handle<Object> GlobalHandles::Create(Object* value) {
result = first_deallocated();
set_first_deallocated(result->next_free());
ASSERT(result->next() == head());
+ if (head() != NULL) head()->set_prev(result);
antonm 2011/05/23 19:44:19 nit: mixed style: it's (first_free()) in old code
+ result->set_prev(NULL);
set_head(result);
} else {
// Allocate a new node.
result = pool_->Allocate();
+ if (head() != NULL) head()->set_prev(result);
result->set_next(head());
+ result->set_prev(NULL);
set_head(result);
}
result->Initialize(value);
@@ -375,7 +263,27 @@ void GlobalHandles::ClearWeakness(Object** location) {
void GlobalHandles::MarkIndependent(Object** location) {
- Node::FromLocation(location)->MarkIndependent(this);
+ Node* node = Node::FromLocation(location);
+
+ if (node->independent_) return;
+
+ node->MarkIndependent(this);
+
+ if (node->is_in_independent_tail_) {
antonm 2011/05/23 19:44:19 is it possible for this to be true? if node wasn'
Vyacheslav Egorov (Chromium) 2011/05/23 20:08:15 Yes it is. If the handle is destroyed and reused w
+ return;
+ }
+
+ if (isolate_->heap()->InNewSpace(node->object_)) {
+ if (node == young_independent_anchor_) {
+ ASSERT(node->prev() != NULL);
+ young_independent_anchor_ = node->prev();
+ } else {
+ UnlinkNode(node);
+ LinkAfter(young_independent_anchor_, node);
+ }
+ node->is_in_independent_tail_ = true;
+ ASSERT(FirstYoungIndependent() == node);
+ }
}
@@ -397,21 +305,7 @@ void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) {
void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
// Traversal of GC roots in the global handle list that are marked as
// WEAK, PENDING or NEAR_DEATH.
- for (Node* current = head_; current != NULL; current = current->next()) {
- if (current->state_ == Node::WEAK
- || current->state_ == Node::PENDING
- || current->state_ == Node::NEAR_DEATH) {
- v->VisitPointer(&current->object_);
- }
- }
-}
-
-
-void GlobalHandles::IterateWeakIndependentRoots(ObjectVisitor* v) {
- // Traversal of GC roots in the global handle list that are independent
- // and marked as WEAK, PENDING or NEAR_DEATH.
- for (Node* current = head_; current != NULL; current = current->next()) {
- if (!current->independent_) continue;
+ for (Node* current = head(); current != NULL; current = current->next()) {
if (current->state_ == Node::WEAK
|| current->state_ == Node::PENDING
|| current->state_ == Node::NEAR_DEATH) {
@@ -423,7 +317,7 @@ void GlobalHandles::IterateWeakIndependentRoots(ObjectVisitor* v) {
void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f,
WeakReferenceCallback callback) {
- for (Node* current = head_; current != NULL; current = current->next()) {
+ for (Node* current = head(); current != NULL; current = current->next()) {
if (current->IsWeak() && current->callback() == callback) {
f(current->object_, current->parameter());
}
@@ -432,7 +326,7 @@ void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f,
void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
- for (Node* current = head_; current != NULL; current = current->next()) {
+ for (Node* current = head(); current != NULL; current = current->next()) {
if (current->state_ == Node::WEAK) {
if (f(&current->object_)) {
current->state_ = Node::PENDING;
@@ -444,9 +338,28 @@ void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
}
+void GlobalHandles::IterateWeakIndependentRoots(ObjectVisitor* v) {
+ // Traversal of GC roots in the global handle list that are independent
+ // and marked as WEAK, PENDING or NEAR_DEATH.
+ for (Node* current = FirstYoungIndependent();
+ current != NULL;
+ current = current->next()) {
+ if (!current->independent_) continue;
antonm 2011/05/23 19:44:19 shouldn't it always hold: ASSERT(current->independ
Vyacheslav Egorov (Chromium) 2011/05/23 20:08:15 It was an assertion initially but... see above :-)
+ if (current->state_ == Node::WEAK
+ || current->state_ == Node::PENDING
+ || current->state_ == Node::NEAR_DEATH) {
+ v->VisitPointer(&current->object_);
+ }
+ }
+}
+
+
void GlobalHandles::IdentifyWeakIndependentHandles(WeakSlotCallbackWithHeap f) {
- for (Node* current = head_; current != NULL; current = current->next()) {
- if (current->state_ == Node::WEAK && current->independent_) {
+ for (Node* current = FirstYoungIndependent();
+ current != NULL;
+ current = current->next()) {
+ if (!current->independent_) continue;
antonm 2011/05/23 19:44:19 ditto
Vyacheslav Egorov (Chromium) 2011/05/23 20:08:15 ditto :-)
+ if (current->state_ == Node::WEAK) {
if (f(isolate_->heap(), &current->object_)) {
current->state_ = Node::PENDING;
LOG(isolate_,
@@ -457,6 +370,72 @@ void GlobalHandles::IdentifyWeakIndependentHandles(WeakSlotCallbackWithHeap f) {
}
+void GlobalHandles::IterateStrongAndDependentRoots(ObjectVisitor* v) {
+ Node* first_young_independent = FirstYoungIndependent();
+ for (Node* current = head();
+ current != first_young_independent;
+ current = current->next()) {
+ if (current->state_ != Node::DESTROYED) {
+ v->VisitPointer(&current->object_);
+ }
+ }
+
+ for (Node* current = first_young_independent;
+ current != NULL;
+ current = current->next()) {
+ if ((current->independent_ && current->state_ == Node::NORMAL) ||
+ (!current->independent_ && current->state_ != Node::DESTROYED)) {
+ v->VisitPointer(&current->object_);
+ }
+ }
+}
+
+
+void GlobalHandles::RemoveFromIndependentTail(Node* node) {
+ if (FirstYoungIndependent() != node) {
+ UnlinkNode(node);
+ LinkAfter(young_independent_anchor_, node);
+ }
+ young_independent_anchor_ = node;
+ node->is_in_independent_tail_ = false;
+}
+
+
+void GlobalHandles::UnlinkNode(Node* node) {
+ Node* next = node->next();
+ Node* prev = node->prev();
+
+ if (next != NULL) next->set_prev(prev);
+
+ prev->set_next(next);
+
+ if (prev == &anchor_node_) {
+ if (first_deallocated()) first_deallocated()->set_next(next);
+ }
+
+ if (young_independent_anchor_ == node) {
+ young_independent_anchor_ = prev;
+ }
+
+ node->is_in_independent_tail_ = false;
antonm 2011/05/23 19:44:19 is it mandatory here? apparently you overwrite it
Vyacheslav Egorov (Chromium) 2011/05/23 20:08:15 there is one callsite that calls UnlinkNode direct
antonm 2011/05/24 10:58:38 Maybe it should go to this callsite then, but it's
+}
+
+
+void GlobalHandles::LinkAfter(Node* prev, Node* node) {
+ ASSERT(prev != NULL);
+ Node* next = prev->next();
+ prev->set_next(node);
+ node->set_prev(prev);
+
+ node->set_next(next);
+ if (next != NULL) next->set_prev(node);
+
+ if (prev == &anchor_node_) {
+ if (first_deallocated()) first_deallocated()->set_next(node);
+ }
+}
+
+
bool GlobalHandles::PostGarbageCollectionProcessing(
GarbageCollector collector) {
// Process weak global handle callbacks. This must be done after the
@@ -466,17 +445,23 @@ bool GlobalHandles::PostGarbageCollectionProcessing(
ASSERT(isolate_->heap()->gc_state() == Heap::NOT_IN_GC);
const int initial_post_gc_processing_count = ++post_gc_processing_count_;
bool next_gc_likely_to_collect_more = false;
- Node** p = &head_;
- while (*p != NULL) {
+
+ Node* next_node = NULL;
+ for (Node* node = (collector == SCAVENGER) ? FirstYoungIndependent() : head();
+ node != NULL;
+ node = next_node) {
+ next_node = node->next();
+ ASSERT(collector == MARK_COMPACTOR || node->is_in_independent_tail_);
+
// Skip dependent handles. Their weak callbacks might expect to be
// called between two global garbage collection callbacks which
// are not called for minor collections.
- if (collector == SCAVENGER && !(*p)->independent_) {
- p = (*p)->next_addr();
+ if (collector == SCAVENGER && !node->independent_) {
+ RemoveFromIndependentTail(node);
continue;
}
- if ((*p)->PostGarbageCollectionProcessing(isolate_, this)) {
+ if (node->PostGarbageCollectionProcessing(isolate_, this)) {
if (initial_post_gc_processing_count != post_gc_processing_count_) {
// Weak callback triggered another GC and another round of
// PostGarbageCollection processing. The current node might
@@ -485,18 +470,17 @@ bool GlobalHandles::PostGarbageCollectionProcessing(
break;
}
}
- if ((*p)->state_ == Node::DESTROYED) {
- // Delete the link.
- Node* node = *p;
- *p = node->next(); // Update the link.
- if (first_deallocated()) {
- first_deallocated()->set_next(node);
- }
+
+ if (node->state_ == Node::DESTROYED) {
+ UnlinkNode(node);
+
+ if (first_deallocated()) first_deallocated()->set_next(node);
node->set_next_free(first_deallocated());
set_first_deallocated(node);
next_gc_likely_to_collect_more = true;
- } else {
- p = (*p)->next_addr();
+ } else if (node->is_in_independent_tail_ &&
+ !isolate_->heap()->InNewSpace(node->object_)) {
+ RemoveFromIndependentTail(node);
}
}
set_first_free(NULL);
@@ -510,7 +494,7 @@ bool GlobalHandles::PostGarbageCollectionProcessing(
void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) {
// Traversal of global handles marked as NORMAL.
- for (Node* current = head_; current != NULL; current = current->next()) {
+ for (Node* current = head(); current != NULL; current = current->next()) {
if (current->state_ == Node::NORMAL) {
v->VisitPointer(&current->object_);
}
@@ -519,7 +503,7 @@ void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) {
void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
- for (Node* current = head_; current != NULL; current = current->next()) {
+ for (Node* current = head(); current != NULL; current = current->next()) {
if (current->state_ != Node::DESTROYED) {
v->VisitPointer(&current->object_);
}
@@ -527,18 +511,8 @@ void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
}
-void GlobalHandles::IterateStrongAndDependentRoots(ObjectVisitor* v) {
- for (Node* current = head_; current != NULL; current = current->next()) {
- if ((current->independent_ && current->state_ == Node::NORMAL) ||
- (!current->independent_ && current->state_ != Node::DESTROYED)) {
- v->VisitPointer(&current->object_);
- }
- }
-}
-
-
void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) {
- for (Node* current = head_; current != NULL; current = current->next()) {
+ for (Node* current = head(); current != NULL; current = current->next()) {
if (current->class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId &&
current->CanBeRetainer()) {
v->VisitEmbedderReference(&current->object_, current->class_id_);
@@ -562,7 +536,7 @@ void GlobalHandles::RecordStats(HeapStats* stats) {
*stats->pending_global_handle_count = 0;
*stats->near_death_global_handle_count = 0;
*stats->destroyed_global_handle_count = 0;
- for (Node* current = head_; current != NULL; current = current->next()) {
+ for (Node* current = head(); current != NULL; current = current->next()) {
*stats->global_handle_count += 1;
if (current->state_ == Node::WEAK) {
*stats->weak_global_handle_count += 1;
@@ -585,7 +559,7 @@ void GlobalHandles::PrintStats() {
int near_death = 0;
int destroyed = 0;
- for (Node* current = head_; current != NULL; current = current->next()) {
+ for (Node* current = head(); current != NULL; current = current->next()) {
total++;
if (current->state_ == Node::WEAK) weak++;
if (current->state_ == Node::PENDING) pending++;
@@ -604,7 +578,7 @@ void GlobalHandles::PrintStats() {
void GlobalHandles::Print() {
PrintF("Global handles:\n");
- for (Node* current = head_; current != NULL; current = current->next()) {
+ for (Node* current = head(); current != NULL; current = current->next()) {
PrintF(" handle %p to %p (weak=%d)\n",
reinterpret_cast<void*>(current->handle().location()),
reinterpret_cast<void*>(*current->handle()),
« src/global-handles.h ('K') | « src/global-handles.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698