Index: src/global-handles.cc |
diff --git a/src/global-handles.cc b/src/global-handles.cc |
index 88ebe31647dc954fa21f0f5deca99dbf2ccc819b..1dd1cfd171fdc900807f0cdfa5ee68eb96244f70 100644 |
--- a/src/global-handles.cc |
+++ b/src/global-handles.cc |
@@ -56,7 +56,9 @@ class GlobalHandles::Node { |
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. |
+ NEAR_DEATH, // Callback has informed the handle is near death. |
+ |
+ LAST_STATE = NEAR_DEATH |
Michael Starzinger
2013/08/01 12:29:02
Let just rename this to STATE_COUNT or NUMBER_OF_S
|
}; |
// Maps handle location (slot) to the containing node. |
@@ -93,13 +95,12 @@ class GlobalHandles::Node { |
} |
#endif |
- void Initialize(int index, Node** first_free) { |
+ void Initialize(int index, Node* first_free) { |
index_ = static_cast<uint8_t>(index); |
ASSERT(static_cast<int>(index_) == index); |
set_state(FREE); |
set_in_new_space_list(false); |
- parameter_or_next_free_.next_free = *first_free; |
- *first_free = this; |
+ parameter_or_next_free_.next_free = first_free; |
} |
void Acquire(Object* object) { |
@@ -111,7 +112,6 @@ class GlobalHandles::Node { |
set_state(NORMAL); |
parameter_or_next_free_.parameter = NULL; |
weak_reference_callback_ = NULL; |
- IncreaseBlockUses(); |
} |
void Release() { |
@@ -125,7 +125,7 @@ class GlobalHandles::Node { |
set_partially_dependent(false); |
weak_reference_callback_ = NULL; |
#endif |
- DecreaseBlockUses(); |
+ ReleaseFromBlock(); |
} |
// Object slot accessors. |
@@ -204,10 +204,6 @@ class GlobalHandles::Node { |
} |
void clear_partially_dependent() { set_partially_dependent(false); } |
- // Callback accessor. |
- // TODO(svenpanne) Re-enable or nuke later. |
- // WeakReferenceCallback callback() { return callback_; } |
- |
// Callback parameter accessors. |
void set_parameter(void* parameter) { |
ASSERT(state() != FREE); |
@@ -276,8 +272,7 @@ class GlobalHandles::Node { |
private: |
inline NodeBlock* FindBlock(); |
inline GlobalHandles* GetGlobalHandles(); |
- inline void IncreaseBlockUses(); |
- inline void DecreaseBlockUses(); |
+ inline void ReleaseFromBlock(); |
// Storage for object pointer. |
// Placed first to avoid offset computation. |
@@ -319,66 +314,287 @@ class GlobalHandles::NodeBlock { |
public: |
static const int kSize = 256; |
- explicit NodeBlock(GlobalHandles* global_handles, NodeBlock* next) |
- : next_(next), |
- used_nodes_(0), |
- next_used_(NULL), |
- prev_used_(NULL), |
- global_handles_(global_handles) {} |
- |
- void PutNodesOnFreeList(Node** first_free) { |
+ explicit NodeBlock(GlobalHandles* global_handles) |
+ : used_nodes_(0), |
+ first_free_(NULL), |
+ next_block_(NULL), |
+ prev_block_(NULL), |
+ global_handles_(global_handles) { |
+ // Initialize nodes |
+ Node* first_free = first_free_; |
for (int i = kSize - 1; i >= 0; --i) { |
nodes_[i].Initialize(i, first_free); |
+ first_free = &nodes_[i]; |
} |
+ first_free_ = first_free; |
+ // Link into global_handles |
+ ASSERT(global_handles->first_non_full_block_ == NULL); |
+ global_handles->first_non_full_block_ = this; |
+ global_handles->number_of_blocks_++; |
+ if (global_handles->first_block_ == NULL) { |
+ ASSERT(global_handles->last_block_ == NULL); |
+ global_handles->first_block_ = this; |
+ global_handles->last_block_ = this; |
+ return; |
+ } |
+ ASSERT(global_handles->last_block_ != NULL); |
+ prev_block_ = global_handles->last_block_; |
+ prev_block_->next_block_ = this; |
+ global_handles->last_block_ = this; |
} |
- Node* node_at(int index) { |
- ASSERT(0 <= index && index < kSize); |
- return &nodes_[index]; |
- } |
- |
- void IncreaseUses() { |
+ Node* Acquire(Object* o) { |
ASSERT(used_nodes_ < kSize); |
- if (used_nodes_++ == 0) { |
- NodeBlock* old_first = global_handles_->first_used_block_; |
- global_handles_->first_used_block_ = this; |
- next_used_ = old_first; |
- prev_used_ = NULL; |
- if (old_first == NULL) return; |
- old_first->prev_used_ = this; |
+ ASSERT(first_free_ != NULL); |
+ ASSERT(global_handles_->first_non_full_block_ == this); |
+ // Remove from free list |
+ Node* node = first_free_; |
+ first_free_ = node->next_free(); |
+ // Increment counters |
+ global_handles_->isolate()->counters()->global_handles()->Increment(); |
+ global_handles_->number_of_global_handles_++; |
+ // Initialize node with value |
+ node->Acquire(o); |
+ // Move block out of first_non_full_block_ if it becomes full |
+ bool now_full = ++used_nodes_ == kSize; |
+ if (now_full) { |
+ global_handles_->first_non_full_block_ = next_block_; |
} |
+ return node; |
} |
- void DecreaseUses() { |
+ void Release(Node* node) { |
ASSERT(used_nodes_ > 0); |
- if (--used_nodes_ == 0) { |
- if (next_used_ != NULL) next_used_->prev_used_ = prev_used_; |
- if (prev_used_ != NULL) prev_used_->next_used_ = next_used_; |
- if (this == global_handles_->first_used_block_) { |
- global_handles_->first_used_block_ = next_used_; |
+ // Add to free list |
+ node->set_next_free(first_free_); |
+ first_free_ = node; |
+ // Decrement counters |
+ global_handles_->isolate()->counters()->global_handles()->Decrement(); |
+ global_handles_->number_of_global_handles_--; |
+ // Move this block to first_non_full_block_ if necessary |
+ bool was_full = used_nodes_-- == kSize; |
+ if (!was_full) return; |
+ ASSERT(first_free_->next_free() == NULL); |
+ ASSERT(global_handles_->first_non_full_block_ != this); |
+ // Simple case: last block becomes non full. Just mark. |
+ if (next_block_ == NULL) { |
+ ASSERT(global_handles_->last_block_ == this); |
+ ASSERT(global_handles_->first_non_full_block_ == NULL); |
+ global_handles_->first_non_full_block_ = this; |
+ return; |
+ } |
+ ASSERT(global_handles_->last_block_ != this); |
+ // Unlink prev_block_ and next_block_ |
+ if (prev_block_ == NULL) { |
+ // First block becomes non full |
+ ASSERT(global_handles_->first_block_ == this); |
+ global_handles_->first_block_ = next_block_; |
+ next_block_->prev_block_ = NULL; |
+ } else { |
+ ASSERT(global_handles_->first_block_ != this); |
+ prev_block_->next_block_ = next_block_; |
+ next_block_->prev_block_ = prev_block_; |
+ } |
+ // Relink into first_non_full_block_ position |
+ if (global_handles_->first_non_full_block_ == NULL) { |
+ // Link into last position. |
+ next_block_ = NULL; |
+ prev_block_ = global_handles_->last_block_; |
+ ASSERT(prev_block_->next_block_ == NULL); |
+ prev_block_->next_block_ = this; |
+ global_handles_->last_block_ = this; |
+ } else { |
+ next_block_ = global_handles_->first_non_full_block_; |
+ prev_block_ = next_block_->prev_block_; |
+ next_block_->prev_block_ = this; |
+ if (prev_block_ == NULL) { |
+ // link into first position |
+ ASSERT(global_handles_->first_block_ == next_block_); |
+ global_handles_->first_block_ = this; |
+ } else { |
+ ASSERT(global_handles_->first_block_ != next_block_); |
+ prev_block_->next_block_ = this; |
} |
} |
+ // Update first_non_full_block_ |
+ global_handles_->first_non_full_block_ = this; |
} |
- GlobalHandles* global_handles() { return global_handles_; } |
+ static void SortBlocks(GlobalHandles* global_handles); |
+ static void PruneBlocks(GlobalHandles* global_handles); |
+ // Needed for quicksort |
+ static int CompareBlocks(const void* a, const void* b); |
+ |
+ Node* node_at(int index) { |
+ ASSERT(0 <= index && index < kSize); |
+ return &nodes_[index]; |
+ } |
+ |
+#ifdef DEBUG |
+ int LengthOfFreeList() { |
+ int count = 0; |
+ Node* node = first_free_; |
+ while (node != NULL) { |
+ count++; |
+ node = node->next_free(); |
+ } |
+ return count; |
+ } |
+#endif |
- // Next block in the list of all blocks. |
- NodeBlock* next() const { return next_; } |
+ bool IsUnused() { return used_nodes_ == 0; } |
- // Next/previous block in the list of blocks with used nodes. |
- NodeBlock* next_used() const { return next_used_; } |
- NodeBlock* prev_used() const { return prev_used_; } |
+ GlobalHandles* global_handles() { return global_handles_; } |
+ int used_nodes() const { return used_nodes_; } |
+ NodeBlock* next_block() const { return next_block_; } |
+ NodeBlock* prev_block() const { return prev_block_; } |
private: |
Node nodes_[kSize]; |
- NodeBlock* const next_; |
int used_nodes_; |
- NodeBlock* next_used_; |
- NodeBlock* prev_used_; |
+ Node* first_free_; |
+ NodeBlock* next_block_; |
+ NodeBlock* prev_block_; |
GlobalHandles* global_handles_; |
}; |
+#ifdef DEBUG |
+void GlobalHandles::VerifyBlockInvariants() { |
+ int number_of_blocks = 0; |
+ int number_of_handles = 0; |
+ NodeBlock* first_non_full_block = NULL; |
+ NodeBlock* block = first_block_; |
+ NodeBlock* prev_block = NULL; |
+ while (block != NULL) { |
+ number_of_blocks++; |
+ int used_nodes = block->used_nodes(); |
+ number_of_handles += used_nodes; |
+ int unused_nodes = block->LengthOfFreeList(); |
+ ASSERT_EQ(used_nodes + unused_nodes, NodeBlock::kSize); |
+ if (first_non_full_block != NULL) { |
+ ASSERT_LT(used_nodes, NodeBlock::kSize); |
+ } else if (used_nodes < NodeBlock::kSize) { |
+ first_non_full_block = block; |
+ } else { |
+ ASSERT_EQ(used_nodes, NodeBlock::kSize); |
+ } |
+ prev_block = block; |
+ block = block->next_block(); |
+ } |
+ ASSERT_EQ(prev_block, last_block_); |
+ ASSERT_EQ(number_of_handles, number_of_global_handles_); |
+ ASSERT_EQ(number_of_blocks, number_of_blocks_); |
+ ASSERT_EQ(first_non_full_block, first_non_full_block_); |
+} |
+#endif |
+ |
+ |
+int GlobalHandles::NodeBlock::CompareBlocks(const void* a, const void* b) { |
+ const NodeBlock* block_a = |
+ *reinterpret_cast<const NodeBlock**>(reinterpret_cast<uintptr_t>(a)); |
+ const NodeBlock* block_b = |
+ *reinterpret_cast<const NodeBlock**>(reinterpret_cast<uintptr_t>(b)); |
+ if (block_a->used_nodes_ > block_b->used_nodes_) return -1; |
+ if (block_a->used_nodes_ == block_b->used_nodes_) return 0; |
+ return 1; |
+} |
+ |
+ |
+void GlobalHandles::NodeBlock::SortBlocks(GlobalHandles* global_handles) { |
+ int number_of_blocks = global_handles->number_of_blocks_; |
+ // Always sort at least 2 blocks |
+ if (number_of_blocks <= 1) return; |
+ // Build array of blocks |
+ ScopedVector<NodeBlock*> blocks(number_of_blocks); |
+ { |
+ NodeBlock* block = global_handles->first_block_; |
+ for (int i = 0; block != NULL; i++) { |
+ blocks[i] = block; |
+ block = block->next_block(); |
+ } |
+ } |
+ // Sort blocks |
+ qsort(blocks.start(), number_of_blocks, sizeof(blocks[0]), CompareBlocks); |
+ // Relink blocks |
+ NodeBlock* first_non_full_block = NULL; |
+ { |
+ NodeBlock* first_block = blocks[0]; |
+ global_handles->first_block_ = first_block; |
+ first_block->prev_block_ = NULL; |
+ first_block->next_block_ = blocks[1]; |
+ if (first_block->used_nodes_ < kSize) first_non_full_block = first_block; |
+ } |
+ for (int i = 1; i < number_of_blocks-1; i++) { |
+ NodeBlock* block = blocks[i]; |
+ block->prev_block_ = blocks[i-1]; |
+ block->next_block_ = blocks[i+1]; |
+ if (first_non_full_block == NULL && block->used_nodes_ < kSize) { |
+ first_non_full_block = block; |
+ } |
+ } |
+ { |
+ NodeBlock* last_block = blocks[number_of_blocks-1]; |
+ global_handles->last_block_ = last_block; |
+ last_block->prev_block_ = blocks[number_of_blocks-2]; |
+ last_block->next_block_ = NULL; |
+ if (first_non_full_block == NULL && last_block->used_nodes_ < kSize) { |
+ first_non_full_block = last_block; |
+ } |
+ } |
+ global_handles->first_non_full_block_ = first_non_full_block; |
+} |
+ |
+ |
+void GlobalHandles::NodeBlock::PruneBlocks(GlobalHandles* global_handles) { |
+ static const double kUnusedPercentage = 0.30; |
+ static const double kUsedPercentage = 1.30; |
+ ASSERT(global_handles->last_block_ != NULL); |
+ // Have at least one block available for deletion. |
+ int total_slots = global_handles->number_of_blocks_*NodeBlock::kSize; |
Michael Starzinger
2013/08/01 12:29:02
nit: Whitespace around operators. Here and several
|
+ const int total_used = global_handles->number_of_global_handles_; |
+ const int target_unused = static_cast<int>(Max( |
+ total_used*kUsedPercentage, |
+ total_slots*kUnusedPercentage)); |
+ NodeBlock* block = global_handles->last_block_; |
+ int blocks_deleted = 0; |
+ while (block->IsUnused()) { |
+ // Not worth deleting |
+ if (total_slots - total_used < target_unused) break; |
+ // Don't delete only remaining block |
+ if (block->prev_block() == NULL) break; |
+ // Delete |
+ NodeBlock* tmp = block; |
+ block = block->prev_block(); |
+ total_slots -= NodeBlock::kSize; |
+ delete tmp; |
+ blocks_deleted++; |
+ } |
+ if (blocks_deleted == 0) return; |
+ block->next_block_ = NULL; |
+ global_handles->last_block_ = block; |
+ global_handles->number_of_blocks_ -= blocks_deleted; |
+} |
+ |
+ |
+void GlobalHandles::SortBlocks(bool shouldPrune) { |
+#ifdef DEBUG |
+ VerifyBlockInvariants(); |
+#endif |
+ // Nothing to sort or prune |
+ if (first_non_full_block_ == NULL || first_block_ == last_block_) return; |
+ NodeBlock::SortBlocks(this); |
+#ifdef DEBUG |
+ VerifyBlockInvariants(); |
+#endif |
+ if (!shouldPrune) return; |
+ NodeBlock::PruneBlocks(this); |
+#ifdef DEBUG |
+ VerifyBlockInvariants(); |
+#endif |
+} |
+ |
+ |
GlobalHandles* GlobalHandles::Node::GetGlobalHandles() { |
return FindBlock()->global_handles(); |
} |
@@ -393,31 +609,18 @@ GlobalHandles::NodeBlock* GlobalHandles::Node::FindBlock() { |
} |
-void GlobalHandles::Node::IncreaseBlockUses() { |
- NodeBlock* node_block = FindBlock(); |
- node_block->IncreaseUses(); |
- GlobalHandles* global_handles = node_block->global_handles(); |
- global_handles->isolate()->counters()->global_handles()->Increment(); |
- global_handles->number_of_global_handles_++; |
-} |
- |
- |
-void GlobalHandles::Node::DecreaseBlockUses() { |
- NodeBlock* node_block = FindBlock(); |
- GlobalHandles* global_handles = node_block->global_handles(); |
- parameter_or_next_free_.next_free = global_handles->first_free_; |
- global_handles->first_free_ = this; |
- node_block->DecreaseUses(); |
- global_handles->isolate()->counters()->global_handles()->Decrement(); |
- global_handles->number_of_global_handles_--; |
+void GlobalHandles::Node::ReleaseFromBlock() { |
+ FindBlock()->Release(this); |
} |
class GlobalHandles::NodeIterator { |
public: |
explicit NodeIterator(GlobalHandles* global_handles) |
- : block_(global_handles->first_used_block_), |
- index_(0) {} |
+ : block_(global_handles->first_block_), |
+ index_(0) { |
+ SkipUnused(); |
+ } |
bool done() const { return block_ == NULL; } |
@@ -430,10 +633,19 @@ class GlobalHandles::NodeIterator { |
ASSERT(!done()); |
if (++index_ < NodeBlock::kSize) return; |
index_ = 0; |
- block_ = block_->next_used(); |
+ block_ = block_->next_block(); |
+ SkipUnused(); |
} |
+ static const int kCountArraySize = Node::LAST_STATE + 1; |
Michael Starzinger
2013/08/01 12:29:02
This constant is obsolete, see comment at the top
|
+ typedef int CountArray[kCountArraySize]; |
+ static int CollectStats(GlobalHandles* global_handles, CountArray counts); |
+ |
private: |
+ inline void SkipUnused() { |
+ while (block_ != NULL && block_->IsUnused()) block_ = block_->next_block(); |
+ } |
+ |
NodeBlock* block_; |
int index_; |
@@ -441,12 +653,33 @@ class GlobalHandles::NodeIterator { |
}; |
+int GlobalHandles::NodeIterator::CollectStats(GlobalHandles* global_handles, |
+ CountArray counts) { |
+ for (int i = 0; i < kCountArraySize; i++) { |
+ counts[i] = 0; |
+ } |
+ int total = 0; |
+ for (NodeIterator it(global_handles); !it.done(); it.Advance()) { |
+ total++; |
+ Node::State state = it.node()->state(); |
+ ASSERT(state >= 0 && state < kCountArraySize); |
+ counts[state]++; |
+ } |
+ // NodeIterator skips empty blocks |
+ int skipped = global_handles->number_of_blocks_ * NodeBlock::kSize - total; |
+ total += skipped; |
+ counts[Node::FREE] += total; |
+ return total; |
+} |
+ |
+ |
GlobalHandles::GlobalHandles(Isolate* isolate) |
: isolate_(isolate), |
+ number_of_blocks_(0), |
number_of_global_handles_(0), |
first_block_(NULL), |
- first_used_block_(NULL), |
- first_free_(NULL), |
+ last_block_(NULL), |
+ first_non_full_block_(NULL), |
post_gc_processing_count_(0), |
object_group_connections_(kObjectGroupConnectionsCapacity) {} |
@@ -454,7 +687,7 @@ GlobalHandles::GlobalHandles(Isolate* isolate) |
GlobalHandles::~GlobalHandles() { |
NodeBlock* block = first_block_; |
while (block != NULL) { |
- NodeBlock* tmp = block->next(); |
+ NodeBlock* tmp = block->next_block(); |
delete block; |
block = tmp; |
} |
@@ -463,15 +696,10 @@ GlobalHandles::~GlobalHandles() { |
Handle<Object> GlobalHandles::Create(Object* value) { |
- if (first_free_ == NULL) { |
- first_block_ = new NodeBlock(this, first_block_); |
- first_block_->PutNodesOnFreeList(&first_free_); |
- } |
- ASSERT(first_free_ != NULL); |
+ if (first_non_full_block_ == NULL) new NodeBlock(this); |
+ ASSERT(first_non_full_block_ != NULL); |
// Take the first node in the free list. |
- Node* result = first_free_; |
- first_free_ = result->next_free(); |
- result->Acquire(value); |
+ Node* result = first_non_full_block_->Acquire(value); |
if (isolate_->heap()->InNewSpace(value) && |
!result->is_in_new_space_list()) { |
new_space_nodes_.Add(result); |
@@ -698,6 +926,8 @@ bool GlobalHandles::PostGarbageCollectionProcessing( |
} |
} |
new_space_nodes_.Rewind(last); |
+ bool shouldPruneBlocks = collector != SCAVENGER; |
+ SortBlocks(shouldPruneBlocks); |
return next_gc_likely_to_collect_more; |
} |
@@ -765,48 +995,30 @@ int GlobalHandles::NumberOfGlobalObjectWeakHandles() { |
void GlobalHandles::RecordStats(HeapStats* stats) { |
- *stats->global_handle_count = 0; |
- *stats->weak_global_handle_count = 0; |
- *stats->pending_global_handle_count = 0; |
- *stats->near_death_global_handle_count = 0; |
- *stats->free_global_handle_count = 0; |
- for (NodeIterator it(this); !it.done(); it.Advance()) { |
- *stats->global_handle_count += 1; |
- if (it.node()->state() == Node::WEAK) { |
- *stats->weak_global_handle_count += 1; |
- } else if (it.node()->state() == Node::PENDING) { |
- *stats->pending_global_handle_count += 1; |
- } else if (it.node()->state() == Node::NEAR_DEATH) { |
- *stats->near_death_global_handle_count += 1; |
- } else if (it.node()->state() == Node::FREE) { |
- *stats->free_global_handle_count += 1; |
- } |
- } |
+ NodeIterator::CountArray counts; |
+ int total = NodeIterator::CollectStats(this, counts); |
+ *stats->global_handle_count = total; |
+ *stats->weak_global_handle_count = counts[Node::WEAK]; |
+ *stats->pending_global_handle_count = counts[Node::PENDING]; |
+ *stats->near_death_global_handle_count = counts[Node::NEAR_DEATH]; |
+ *stats->free_global_handle_count = counts[Node::FREE]; |
} |
+ |
#ifdef DEBUG |
void GlobalHandles::PrintStats() { |
- int total = 0; |
- int weak = 0; |
- int pending = 0; |
- int near_death = 0; |
- int destroyed = 0; |
- |
- for (NodeIterator it(this); !it.done(); it.Advance()) { |
- total++; |
- if (it.node()->state() == Node::WEAK) weak++; |
- if (it.node()->state() == Node::PENDING) pending++; |
- if (it.node()->state() == Node::NEAR_DEATH) near_death++; |
- if (it.node()->state() == Node::FREE) destroyed++; |
- } |
- |
+ NodeIterator::CountArray counts; |
+ int total = NodeIterator::CollectStats(this, counts); |
+ size_t total_consumed = sizeof(NodeBlock) * number_of_blocks_; |
PrintF("Global Handle Statistics:\n"); |
- PrintF(" allocated memory = %" V8_PTR_PREFIX "dB\n", sizeof(Node) * total); |
- PrintF(" # weak = %d\n", weak); |
- PrintF(" # pending = %d\n", pending); |
- PrintF(" # near_death = %d\n", near_death); |
- PrintF(" # free = %d\n", destroyed); |
+ PrintF(" allocated blocks = %d\n", number_of_blocks_); |
+ PrintF(" allocated memory = %" V8_PTR_PREFIX "dB\n", total_consumed); |
+ PrintF(" # normal = %d\n", counts[Node::NORMAL]); |
+ PrintF(" # weak = %d\n", counts[Node::WEAK]); |
+ PrintF(" # pending = %d\n", counts[Node::PENDING]); |
+ PrintF(" # near_death = %d\n", counts[Node::NEAR_DEATH]); |
+ PrintF(" # free = %d\n", counts[Node::FREE]); |
PrintF(" # total = %d\n", total); |
} |