# HG changeset patch # User apangin # Date 1279144338 14400 # Node ID 1e7ec26380bd57206f9fe8dfda12f7656b420af1 # Parent 0e7d2a08b6053ed595aebc39d0becc3a50208b7a# Parent 9ee05c8ab82f5ddbb46b2af9fe1024bf85f27127 Merge diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/gc_implementation/includeDB_gc_parallelScavenge --- a/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge Wed Jul 14 17:52:18 2010 -0400 @@ -270,7 +270,7 @@ psParallelCompact.cpp pcTasks.hpp psParallelCompact.cpp psMarkSweep.hpp psParallelCompact.cpp psMarkSweepDecorator.hpp -psParallelCompact.cpp psCompactionManager.hpp +psParallelCompact.cpp psCompactionManager.inline.hpp psParallelCompact.cpp psPromotionManager.inline.hpp psParallelCompact.cpp psOldGen.hpp psParallelCompact.cpp psParallelCompact.hpp diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.cpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.cpp Wed Jul 14 17:52:18 2010 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ ParCompactionManager::_objarray_queues = NULL; ObjectStartArray* ParCompactionManager::_start_array = NULL; ParMarkBitMap* ParCompactionManager::_mark_bitmap = NULL; -RegionTaskQueueSet* ParCompactionManager::_region_array = NULL; +RegionTaskQueueSet* ParCompactionManager::_region_array = NULL; ParCompactionManager::ParCompactionManager() : _action(CopyAndUpdate) { @@ -43,25 +43,9 @@ _old_gen = heap->old_gen(); _start_array = old_gen()->start_array(); - marking_stack()->initialize(); - - // We want the overflow stack to be permanent - _overflow_stack = new (ResourceObj::C_HEAP) GrowableArray(10, true); - - _objarray_queue.initialize(); - _objarray_overflow_stack = - new (ResourceObj::C_HEAP) ObjArrayOverflowStack(10, true); - -#ifdef USE_RegionTaskQueueWithOverflow + _objarray_stack.initialize(); region_stack()->initialize(); -#else - region_stack()->initialize(); - - // We want the overflow stack to be permanent - _region_overflow_stack = - new (ResourceObj::C_HEAP) GrowableArray(10, true); -#endif // Note that _revisit_klass_stack is allocated out of the // C heap (as opposed to out of ResourceArena). @@ -71,12 +55,9 @@ // From some experiments (#klass/k)^2 for k = 10 seems a better fit, but this will // have to do for now until we are able to investigate a more optimal setting. _revisit_mdo_stack = new (ResourceObj::C_HEAP) GrowableArray(size*2, true); - } ParCompactionManager::~ParCompactionManager() { - delete _overflow_stack; - delete _objarray_overflow_stack; delete _revisit_klass_stack; delete _revisit_mdo_stack; // _manager_array and _stack_array are statics @@ -108,12 +89,8 @@ _manager_array[i] = new ParCompactionManager(); guarantee(_manager_array[i] != NULL, "Could not create ParCompactionManager"); stack_array()->register_queue(i, _manager_array[i]->marking_stack()); - _objarray_queues->register_queue(i, &_manager_array[i]->_objarray_queue); -#ifdef USE_RegionTaskQueueWithOverflow - region_array()->register_queue(i, _manager_array[i]->region_stack()->task_queue()); -#else + _objarray_queues->register_queue(i, &_manager_array[i]->_objarray_stack); region_array()->register_queue(i, _manager_array[i]->region_stack()); -#endif } // The VMThread gets its own ParCompactionManager, which is not available @@ -149,57 +126,6 @@ return action() == ParCompactionManager::ResetObjects; } -// For now save on a stack -void ParCompactionManager::save_for_scanning(oop m) { - stack_push(m); -} - -void ParCompactionManager::stack_push(oop obj) { - - if(!marking_stack()->push(obj)) { - overflow_stack()->push(obj); - } -} - -oop ParCompactionManager::retrieve_for_scanning() { - - // Should not be used in the parallel case - ShouldNotReachHere(); - return NULL; -} - -// Save region on a stack -void ParCompactionManager::save_for_processing(size_t region_index) { -#ifdef ASSERT - const ParallelCompactData& sd = PSParallelCompact::summary_data(); - ParallelCompactData::RegionData* const region_ptr = sd.region(region_index); - assert(region_ptr->claimed(), "must be claimed"); - assert(region_ptr->_pushed++ == 0, "should only be pushed once"); -#endif - region_stack_push(region_index); -} - -void ParCompactionManager::region_stack_push(size_t region_index) { - -#ifdef USE_RegionTaskQueueWithOverflow - region_stack()->save(region_index); -#else - if(!region_stack()->push(region_index)) { - region_overflow_stack()->push(region_index); - } -#endif -} - -bool ParCompactionManager::retrieve_for_processing(size_t& region_index) { -#ifdef USE_RegionTaskQueueWithOverflow - return region_stack()->retrieve(region_index); -#else - // Should not be used in the parallel case - ShouldNotReachHere(); - return false; -#endif -} - ParCompactionManager* ParCompactionManager::gc_thread_compaction_manager(int index) { assert(index >= 0 && index < (int)ParallelGCThreads, "index out of range"); @@ -218,8 +144,8 @@ do { // Drain the overflow stack first, to allow stealing from the marking stack. oop obj; - while (!overflow_stack()->is_empty()) { - overflow_stack()->pop()->follow_contents(this); + while (marking_stack()->pop_overflow(obj)) { + obj->follow_contents(this); } while (marking_stack()->pop_local(obj)) { obj->follow_contents(this); @@ -227,11 +153,10 @@ // Process ObjArrays one at a time to avoid marking stack bloat. ObjArrayTask task; - if (!_objarray_overflow_stack->is_empty()) { - task = _objarray_overflow_stack->pop(); + if (_objarray_stack.pop_overflow(task)) { objArrayKlass* const k = (objArrayKlass*)task.obj()->blueprint(); k->oop_follow_contents(this, task.obj(), task.index()); - } else if (_objarray_queue.pop_local(task)) { + } else if (_objarray_stack.pop_local(task)) { objArrayKlass* const k = (objArrayKlass*)task.obj()->blueprint(); k->oop_follow_contents(this, task.obj(), task.index()); } @@ -240,68 +165,18 @@ assert(marking_stacks_empty(), "Sanity"); } -void ParCompactionManager::drain_region_overflow_stack() { - size_t region_index = (size_t) -1; - while(region_stack()->retrieve_from_overflow(region_index)) { - PSParallelCompact::fill_and_update_region(this, region_index); - } -} - void ParCompactionManager::drain_region_stacks() { -#ifdef ASSERT - ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); - assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); - MutableSpace* to_space = heap->young_gen()->to_space(); - MutableSpace* old_space = heap->old_gen()->object_space(); - MutableSpace* perm_space = heap->perm_gen()->object_space(); -#endif /* ASSERT */ - -#if 1 // def DO_PARALLEL - the serial code hasn't been updated do { - -#ifdef USE_RegionTaskQueueWithOverflow - // Drain overflow stack first, so other threads can steal from - // claimed stack while we work. - size_t region_index = (size_t) -1; - while(region_stack()->retrieve_from_overflow(region_index)) { + // Drain overflow stack first so other threads can steal. + size_t region_index; + while (region_stack()->pop_overflow(region_index)) { PSParallelCompact::fill_and_update_region(this, region_index); } - while (region_stack()->retrieve_from_stealable_queue(region_index)) { + while (region_stack()->pop_local(region_index)) { PSParallelCompact::fill_and_update_region(this, region_index); } } while (!region_stack()->is_empty()); -#else - // Drain overflow stack first, so other threads can steal from - // claimed stack while we work. - while(!region_overflow_stack()->is_empty()) { - size_t region_index = region_overflow_stack()->pop(); - PSParallelCompact::fill_and_update_region(this, region_index); - } - - size_t region_index = -1; - // obj is a reference!!! - while (region_stack()->pop_local(region_index)) { - // It would be nice to assert about the type of objects we might - // pop, but they can come from anywhere, unfortunately. - PSParallelCompact::fill_and_update_region(this, region_index); - } - } while((region_stack()->size() != 0) || - (region_overflow_stack()->length() != 0)); -#endif - -#ifdef USE_RegionTaskQueueWithOverflow - assert(region_stack()->is_empty(), "Sanity"); -#else - assert(region_stack()->size() == 0, "Sanity"); - assert(region_overflow_stack()->length() == 0, "Sanity"); -#endif -#else - oop obj; - while (obj = retrieve_for_scanning()) { - obj->follow_contents(this); - } -#endif } #ifdef ASSERT diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp Wed Jul 14 17:52:18 2010 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,10 +59,10 @@ private: // 32-bit: 4K * 8 = 32KiB; 64-bit: 8K * 16 = 128KiB - #define OBJARRAY_QUEUE_SIZE (1 << NOT_LP64(12) LP64_ONLY(13)) - typedef GenericTaskQueue ObjArrayTaskQueue; - typedef GenericTaskQueueSet ObjArrayTaskQueueSet; - #undef OBJARRAY_QUEUE_SIZE + #define QUEUE_SIZE (1 << NOT_LP64(12) LP64_ONLY(13)) + typedef OverflowTaskQueue ObjArrayTaskQueue; + typedef GenericTaskQueueSet ObjArrayTaskQueueSet; + #undef QUEUE_SIZE static ParCompactionManager** _manager_array; static OopTaskQueueSet* _stack_array; @@ -72,23 +72,13 @@ static PSOldGen* _old_gen; private: - OopTaskQueue _marking_stack; - GrowableArray* _overflow_stack; - - typedef GrowableArray ObjArrayOverflowStack; - ObjArrayTaskQueue _objarray_queue; - ObjArrayOverflowStack* _objarray_overflow_stack; + OverflowTaskQueue _marking_stack; + ObjArrayTaskQueue _objarray_stack; // Is there a way to reuse the _marking_stack for the // saving empty regions? For now just create a different // type of TaskQueue. - -#ifdef USE_RegionTaskQueueWithOverflow - RegionTaskQueueWithOverflow _region_stack; -#else RegionTaskQueue _region_stack; - GrowableArray* _region_overflow_stack; -#endif #if 1 // does this happen enough to need a per thread stack? GrowableArray* _revisit_klass_stack; @@ -107,16 +97,8 @@ protected: // Array of tasks. Needed by the ParallelTaskTerminator. static RegionTaskQueueSet* region_array() { return _region_array; } - OopTaskQueue* marking_stack() { return &_marking_stack; } - GrowableArray* overflow_stack() { return _overflow_stack; } -#ifdef USE_RegionTaskQueueWithOverflow - RegionTaskQueueWithOverflow* region_stack() { return &_region_stack; } -#else - RegionTaskQueue* region_stack() { return &_region_stack; } - GrowableArray* region_overflow_stack() { - return _region_overflow_stack; - } -#endif + OverflowTaskQueue* marking_stack() { return &_marking_stack; } + RegionTaskQueue* region_stack() { return &_region_stack; } // Pushes onto the marking stack. If the marking stack is full, // pushes onto the overflow stack. @@ -124,11 +106,7 @@ // Do not implement an equivalent stack_pop. Deal with the // marking stack and overflow stack directly. - // Pushes onto the region stack. If the region stack is full, - // pushes onto the region overflow stack. - void region_stack_push(size_t region_index); - -public: + public: Action action() { return _action; } void set_action(Action v) { _action = v; } @@ -157,22 +135,15 @@ GrowableArray* revisit_mdo_stack() { return _revisit_mdo_stack; } #endif - // Save oop for later processing. Must not fail. - void save_for_scanning(oop m); - // Get a oop for scanning. If returns null, no oop were found. - oop retrieve_for_scanning(); - - inline void push_objarray(oop obj, size_t index); - - // Save region for later processing. Must not fail. - void save_for_processing(size_t region_index); - // Get a region for processing. If returns null, no region were found. - bool retrieve_for_processing(size_t& region_index); + // Save for later processing. Must not fail. + inline void push(oop obj) { _marking_stack.push(obj); } + inline void push_objarray(oop objarray, size_t index); + inline void push_region(size_t index); // Access function for compaction managers static ParCompactionManager* gc_thread_compaction_manager(int index); - static bool steal(int queue_num, int* seed, Task& t) { + static bool steal(int queue_num, int* seed, oop& t) { return stack_array()->steal(queue_num, seed, t); } @@ -180,8 +151,8 @@ return _objarray_queues->steal(queue_num, seed, t); } - static bool steal(int queue_num, int* seed, RegionTask& t) { - return region_array()->steal(queue_num, seed, t); + static bool steal(int queue_num, int* seed, size_t& region) { + return region_array()->steal(queue_num, seed, region); } // Process tasks remaining on any marking stack @@ -191,9 +162,6 @@ // Process tasks remaining on any stack void drain_region_stacks(); - // Process tasks remaining on any stack - void drain_region_overflow_stack(); - // Debugging support #ifdef ASSERT bool stacks_have_been_allocated(); @@ -208,6 +176,5 @@ } bool ParCompactionManager::marking_stacks_empty() const { - return _marking_stack.size() == 0 && _overflow_stack->is_empty() && - _objarray_queue.size() == 0 && _objarray_overflow_stack->is_empty(); + return _marking_stack.is_empty() && _objarray_stack.is_empty(); } diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.inline.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.inline.hpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.inline.hpp Wed Jul 14 17:52:18 2010 -0400 @@ -26,7 +26,16 @@ { ObjArrayTask task(obj, index); assert(task.is_valid(), "bad ObjArrayTask"); - if (!_objarray_queue.push(task)) { - _objarray_overflow_stack->push(task); - } + _objarray_stack.push(task); } + +void ParCompactionManager::push_region(size_t index) +{ +#ifdef ASSERT + const ParallelCompactData& sd = PSParallelCompact::summary_data(); + ParallelCompactData::RegionData* const region_ptr = sd.region(index); + assert(region_ptr->claimed(), "must be claimed"); + assert(region_ptr->_pushed++ == 0, "should only be pushed once"); +#endif + region_stack()->push(index); +} diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Wed Jul 14 17:52:18 2010 -0400 @@ -2474,7 +2474,7 @@ for (size_t cur = end_region - 1; cur >= beg_region; --cur) { if (sd.region(cur)->claim_unsafe()) { ParCompactionManager* cm = ParCompactionManager::manager_array(which); - cm->save_for_processing(cur); + cm->push_region(cur); if (TraceParallelOldGCCompactionPhase && Verbose) { const size_t count_mod_8 = fillable_regions & 7; @@ -3138,7 +3138,7 @@ assert(cur->data_size() > 0, "region must have live data"); cur->decrement_destination_count(); if (cur < enqueue_end && cur->available() && cur->claim()) { - cm->save_for_processing(sd.region(cur)); + cm->push_region(sd.region(cur)); } } } diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp Wed Jul 14 17:52:18 2010 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1297,11 +1297,8 @@ T heap_oop = oopDesc::load_heap_oop(p); if (!oopDesc::is_null(heap_oop)) { oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); - if (mark_bitmap()->is_unmarked(obj)) { - if (mark_obj(obj)) { - // This thread marked the object and owns the subsequent processing of it. - cm->save_for_scanning(obj); - } + if (mark_bitmap()->is_unmarked(obj) && mark_obj(obj)) { + cm->push(obj); } } } diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp Wed Jul 14 17:52:18 2010 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -94,45 +94,13 @@ print_stats(); #endif // PS_PM_STATS - for(uint i=0; ioverflow_stack_depth()->length() <= 0), - "promotion manager overflow stack must be empty"); - guarantee((UseDepthFirstScavengeOrder || - manager->overflow_stack_breadth()->length() <= 0), - "promotion manager overflow stack must be empty"); - - guarantee((!UseDepthFirstScavengeOrder || - manager->claimed_stack_depth()->size() <= 0), - "promotion manager claimed stack must be empty"); - guarantee((UseDepthFirstScavengeOrder || - manager->claimed_stack_breadth()->size() <= 0), - "promotion manager claimed stack must be empty"); + if (UseDepthFirstScavengeOrder) { + assert(manager->claimed_stack_depth()->is_empty(), "should be empty"); } else { - guarantee((!UseDepthFirstScavengeOrder || - manager->overflow_stack_depth()->length() <= 0), - "VM Thread promotion manager overflow stack " - "must be empty"); - guarantee((UseDepthFirstScavengeOrder || - manager->overflow_stack_breadth()->length() <= 0), - "VM Thread promotion manager overflow stack " - "must be empty"); - - guarantee((!UseDepthFirstScavengeOrder || - manager->claimed_stack_depth()->size() <= 0), - "VM Thread promotion manager claimed stack " - "must be empty"); - guarantee((UseDepthFirstScavengeOrder || - manager->claimed_stack_breadth()->size() <= 0), - "VM Thread promotion manager claimed stack " - "must be empty"); + assert(manager->claimed_stack_breadth()->is_empty(), "should be empty"); } - manager->flush_labs(); } } @@ -181,15 +149,9 @@ if (depth_first()) { claimed_stack_depth()->initialize(); queue_size = claimed_stack_depth()->max_elems(); - // We want the overflow stack to be permanent - _overflow_stack_depth = new (ResourceObj::C_HEAP) GrowableArray(10, true); - _overflow_stack_breadth = NULL; } else { claimed_stack_breadth()->initialize(); queue_size = claimed_stack_breadth()->max_elems(); - // We want the overflow stack to be permanent - _overflow_stack_breadth = new (ResourceObj::C_HEAP) GrowableArray(10, true); - _overflow_stack_depth = NULL; } _totally_drain = (ParallelGCThreads == 1) || (GCDrainStackTargetSize == 0); @@ -209,8 +171,7 @@ } void PSPromotionManager::reset() { - assert(claimed_stack_empty(), "reset of non-empty claimed stack"); - assert(overflow_stack_empty(), "reset of non-empty overflow stack"); + assert(stacks_empty(), "reset of non-empty stack"); // We need to get an assert in here to make sure the labs are always flushed. @@ -243,7 +204,7 @@ void PSPromotionManager::drain_stacks_depth(bool totally_drain) { assert(depth_first(), "invariant"); - assert(overflow_stack_depth() != NULL, "invariant"); + assert(claimed_stack_depth()->overflow_stack() != NULL, "invariant"); totally_drain = totally_drain || _totally_drain; #ifdef ASSERT @@ -254,41 +215,35 @@ MutableSpace* perm_space = heap->perm_gen()->object_space(); #endif /* ASSERT */ + OopStarTaskQueue* const tq = claimed_stack_depth(); do { StarTask p; // Drain overflow stack first, so other threads can steal from // claimed stack while we work. - while(!overflow_stack_depth()->is_empty()) { - // linux compiler wants different overloaded operator= in taskqueue to - // assign to p that the other compilers don't like. - StarTask ptr = overflow_stack_depth()->pop(); - process_popped_location_depth(ptr); + while (tq->pop_overflow(p)) { + process_popped_location_depth(p); } if (totally_drain) { - while (claimed_stack_depth()->pop_local(p)) { + while (tq->pop_local(p)) { process_popped_location_depth(p); } } else { - while (claimed_stack_depth()->size() > _target_stack_size && - claimed_stack_depth()->pop_local(p)) { + while (tq->size() > _target_stack_size && tq->pop_local(p)) { process_popped_location_depth(p); } } - } while( (totally_drain && claimed_stack_depth()->size() > 0) || - (overflow_stack_depth()->length() > 0) ); + } while (totally_drain && !tq->taskqueue_empty() || !tq->overflow_empty()); - assert(!totally_drain || claimed_stack_empty(), "Sanity"); - assert(totally_drain || - claimed_stack_depth()->size() <= _target_stack_size, - "Sanity"); - assert(overflow_stack_empty(), "Sanity"); + assert(!totally_drain || tq->taskqueue_empty(), "Sanity"); + assert(totally_drain || tq->size() <= _target_stack_size, "Sanity"); + assert(tq->overflow_empty(), "Sanity"); } void PSPromotionManager::drain_stacks_breadth(bool totally_drain) { assert(!depth_first(), "invariant"); - assert(overflow_stack_breadth() != NULL, "invariant"); + assert(claimed_stack_breadth()->overflow_stack() != NULL, "invariant"); totally_drain = totally_drain || _totally_drain; #ifdef ASSERT @@ -299,51 +254,39 @@ MutableSpace* perm_space = heap->perm_gen()->object_space(); #endif /* ASSERT */ + OverflowTaskQueue* const tq = claimed_stack_breadth(); do { oop obj; // Drain overflow stack first, so other threads can steal from // claimed stack while we work. - while(!overflow_stack_breadth()->is_empty()) { - obj = overflow_stack_breadth()->pop(); + while (tq->pop_overflow(obj)) { obj->copy_contents(this); } if (totally_drain) { - // obj is a reference!!! - while (claimed_stack_breadth()->pop_local(obj)) { - // It would be nice to assert about the type of objects we might - // pop, but they can come from anywhere, unfortunately. + while (tq->pop_local(obj)) { obj->copy_contents(this); } } else { - // obj is a reference!!! - while (claimed_stack_breadth()->size() > _target_stack_size && - claimed_stack_breadth()->pop_local(obj)) { - // It would be nice to assert about the type of objects we might - // pop, but they can come from anywhere, unfortunately. + while (tq->size() > _target_stack_size && tq->pop_local(obj)) { obj->copy_contents(this); } } // If we could not find any other work, flush the prefetch queue - if (claimed_stack_breadth()->size() == 0 && - (overflow_stack_breadth()->length() == 0)) { + if (tq->is_empty()) { flush_prefetch_queue(); } - } while((totally_drain && claimed_stack_breadth()->size() > 0) || - (overflow_stack_breadth()->length() > 0)); + } while (totally_drain && !tq->taskqueue_empty() || !tq->overflow_empty()); - assert(!totally_drain || claimed_stack_empty(), "Sanity"); - assert(totally_drain || - claimed_stack_breadth()->size() <= _target_stack_size, - "Sanity"); - assert(overflow_stack_empty(), "Sanity"); + assert(!totally_drain || tq->taskqueue_empty(), "Sanity"); + assert(totally_drain || tq->size() <= _target_stack_size, "Sanity"); + assert(tq->overflow_empty(), "Sanity"); } void PSPromotionManager::flush_labs() { - assert(claimed_stack_empty(), "Attempt to flush lab with live stack"); - assert(overflow_stack_empty(), "Attempt to flush lab with live overflow stack"); + assert(stacks_empty(), "Attempt to flush lab with live stack"); // If either promotion lab fills up, we can flush the // lab but not refill it, so check first. diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp Wed Jul 14 17:52:18 2010 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -78,9 +78,7 @@ PrefetchQueue _prefetch_queue; OopStarTaskQueue _claimed_stack_depth; - GrowableArray* _overflow_stack_depth; - OopTaskQueue _claimed_stack_breadth; - GrowableArray* _overflow_stack_breadth; + OverflowTaskQueue _claimed_stack_breadth; bool _depth_first; bool _totally_drain; @@ -97,9 +95,6 @@ template inline void claim_or_forward_internal_depth(T* p); template inline void claim_or_forward_internal_breadth(T* p); - GrowableArray* overflow_stack_depth() { return _overflow_stack_depth; } - GrowableArray* overflow_stack_breadth() { return _overflow_stack_breadth; } - // On the task queues we push reference locations as well as // partially-scanned arrays (in the latter case, we push an oop to // the from-space image of the array and the length on the @@ -151,18 +146,19 @@ #if PS_PM_STATS ++_total_pushes; + int stack_length = claimed_stack_depth()->overflow_stack()->length(); #endif // PS_PM_STATS - if (!claimed_stack_depth()->push(p)) { - overflow_stack_depth()->push(p); + claimed_stack_depth()->push(p); + #if PS_PM_STATS + if (claimed_stack_depth()->overflow_stack()->length() != stack_length) { ++_overflow_pushes; - uint stack_length = (uint) overflow_stack_depth()->length(); - if (stack_length > _max_overflow_length) { - _max_overflow_length = stack_length; + if ((uint)stack_length + 1 > _max_overflow_length) { + _max_overflow_length = (uint)stack_length + 1; } + } #endif // PS_PM_STATS - } } void push_breadth(oop o) { @@ -170,18 +166,19 @@ #if PS_PM_STATS ++_total_pushes; + int stack_length = claimed_stack_breadth()->overflow_stack()->length(); #endif // PS_PM_STATS - if(!claimed_stack_breadth()->push(o)) { - overflow_stack_breadth()->push(o); + claimed_stack_breadth()->push(o); + #if PS_PM_STATS + if (claimed_stack_breadth()->overflow_stack()->length() != stack_length) { ++_overflow_pushes; - uint stack_length = (uint) overflow_stack_breadth()->length(); - if (stack_length > _max_overflow_length) { - _max_overflow_length = stack_length; + if ((uint)stack_length + 1 > _max_overflow_length) { + _max_overflow_length = (uint)stack_length + 1; } + } #endif // PS_PM_STATS - } } protected: @@ -199,12 +196,10 @@ static PSPromotionManager* vm_thread_promotion_manager(); static bool steal_depth(int queue_num, int* seed, StarTask& t) { - assert(stack_array_depth() != NULL, "invariant"); return stack_array_depth()->steal(queue_num, seed, t); } - static bool steal_breadth(int queue_num, int* seed, Task& t) { - assert(stack_array_breadth() != NULL, "invariant"); + static bool steal_breadth(int queue_num, int* seed, oop& t) { return stack_array_breadth()->steal(queue_num, seed, t); } @@ -214,7 +209,7 @@ OopStarTaskQueue* claimed_stack_depth() { return &_claimed_stack_depth; } - OopTaskQueue* claimed_stack_breadth() { + OverflowTaskQueue* claimed_stack_breadth() { return &_claimed_stack_breadth; } @@ -246,25 +241,13 @@ void drain_stacks_depth(bool totally_drain); void drain_stacks_breadth(bool totally_drain); - bool claimed_stack_empty() { - if (depth_first()) { - return claimed_stack_depth()->size() <= 0; - } else { - return claimed_stack_breadth()->size() <= 0; - } - } - bool overflow_stack_empty() { - if (depth_first()) { - return overflow_stack_depth()->length() <= 0; - } else { - return overflow_stack_breadth()->length() <= 0; - } + bool depth_first() const { + return _depth_first; } bool stacks_empty() { - return claimed_stack_empty() && overflow_stack_empty(); - } - bool depth_first() { - return _depth_first; + return depth_first() ? + claimed_stack_depth()->is_empty() : + claimed_stack_breadth()->is_empty(); } inline void process_popped_location_depth(StarTask p); diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Wed Jul 14 17:52:18 2010 -0400 @@ -414,7 +414,6 @@ } // Finally, flush the promotion_manager's labs, and deallocate its stacks. - assert(promotion_manager->claimed_stack_empty(), "Sanity"); PSPromotionManager::post_scavenge(); promotion_failure_occurred = promotion_failed(); diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/opto/callnode.cpp --- a/src/share/vm/opto/callnode.cpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/opto/callnode.cpp Wed Jul 14 17:52:18 2010 -0400 @@ -1524,7 +1524,7 @@ ConnectionGraph *cgr = phase->C->congraph(); PointsToNode::EscapeState es = PointsToNode::GlobalEscape; if (cgr != NULL) - es = cgr->escape_state(obj_node(), phase); + es = cgr->escape_state(obj_node()); if (es != PointsToNode::UnknownEscape && es != PointsToNode::GlobalEscape) { // Mark it eliminated to update any counters this->set_eliminated(); @@ -1627,7 +1627,7 @@ ConnectionGraph *cgr = phase->C->congraph(); PointsToNode::EscapeState es = PointsToNode::GlobalEscape; if (cgr != NULL) - es = cgr->escape_state(obj_node(), phase); + es = cgr->escape_state(obj_node()); if (es != PointsToNode::UnknownEscape && es != PointsToNode::GlobalEscape) { // Mark it eliminated to update any counters this->set_eliminated(); diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/opto/compile.cpp --- a/src/share/vm/opto/compile.cpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/opto/compile.cpp Wed Jul 14 17:52:18 2010 -0400 @@ -637,34 +637,6 @@ if (failing()) return; NOT_PRODUCT( verify_graph_edges(); ) - // Perform escape analysis - if (_do_escape_analysis && ConnectionGraph::has_candidates(this)) { - TracePhase t2("escapeAnalysis", &_t_escapeAnalysis, true); - // Add ConP#NULL and ConN#NULL nodes before ConnectionGraph construction. - PhaseGVN* igvn = initial_gvn(); - Node* oop_null = igvn->zerocon(T_OBJECT); - Node* noop_null = igvn->zerocon(T_NARROWOOP); - - _congraph = new(comp_arena()) ConnectionGraph(this); - bool has_non_escaping_obj = _congraph->compute_escape(); - -#ifndef PRODUCT - if (PrintEscapeAnalysis) { - _congraph->dump(); - } -#endif - // Cleanup. - if (oop_null->outcnt() == 0) - igvn->hash_delete(oop_null); - if (noop_null->outcnt() == 0) - igvn->hash_delete(noop_null); - - if (!has_non_escaping_obj) { - _congraph = NULL; - } - - if (failing()) return; - } // Now optimize Optimize(); if (failing()) return; @@ -1601,6 +1573,20 @@ if (failing()) return; + // Perform escape analysis + if (_do_escape_analysis && ConnectionGraph::has_candidates(this)) { + TracePhase t2("escapeAnalysis", &_t_escapeAnalysis, true); + ConnectionGraph::do_analysis(this, &igvn); + + if (failing()) return; + + igvn.optimize(); + print_method("Iter GVN 3", 2); + + if (failing()) return; + + } + // Loop transforms on the ideal graph. Range Check Elimination, // peeling, unrolling, etc. diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/opto/compile.hpp --- a/src/share/vm/opto/compile.hpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/opto/compile.hpp Wed Jul 14 17:52:18 2010 -0400 @@ -362,6 +362,7 @@ Node* macro_node(int idx) { return _macro_nodes->at(idx); } Node* predicate_opaque1_node(int idx) { return _predicate_opaqs->at(idx);} ConnectionGraph* congraph() { return _congraph;} + void set_congraph(ConnectionGraph* congraph) { _congraph = congraph;} void add_macro_node(Node * n) { //assert(n->is_macro(), "must be a macro node"); assert(!_macro_nodes->contains(n), " duplicate entry in expand list"); diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/opto/escape.cpp --- a/src/share/vm/opto/escape.cpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/opto/escape.cpp Wed Jul 14 17:52:18 2010 -0400 @@ -81,18 +81,18 @@ } #endif -ConnectionGraph::ConnectionGraph(Compile * C) : +ConnectionGraph::ConnectionGraph(Compile * C, PhaseIterGVN *igvn) : _nodes(C->comp_arena(), C->unique(), C->unique(), PointsToNode()), _processed(C->comp_arena()), _collecting(true), _compile(C), + _igvn(igvn), _node_map(C->comp_arena()) { _phantom_object = C->top()->_idx, add_node(C->top(), PointsToNode::JavaObject, PointsToNode::GlobalEscape,true); // Add ConP(#NULL) and ConN(#NULL) nodes. - PhaseGVN* igvn = C->initial_gvn(); Node* oop_null = igvn->zerocon(T_OBJECT); _oop_null = oop_null->_idx; assert(_oop_null < C->unique(), "should be created already"); @@ -182,7 +182,7 @@ _processed.set(n->_idx); } -PointsToNode::EscapeState ConnectionGraph::escape_state(Node *n, PhaseTransform *phase) { +PointsToNode::EscapeState ConnectionGraph::escape_state(Node *n) { uint idx = n->_idx; PointsToNode::EscapeState es; @@ -207,22 +207,26 @@ if (n->uncast()->_idx >= nodes_size()) return PointsToNode::UnknownEscape; + PointsToNode::EscapeState orig_es = es; + // compute max escape state of anything this node could point to VectorSet ptset(Thread::current()->resource_area()); - PointsTo(ptset, n, phase); + PointsTo(ptset, n); for(VectorSetI i(&ptset); i.test() && es != PointsToNode::GlobalEscape; ++i) { uint pt = i.elem; PointsToNode::EscapeState pes = ptnode_adr(pt)->escape_state(); if (pes > es) es = pes; } - // cache the computed escape state - assert(es != PointsToNode::UnknownEscape, "should have computed an escape state"); - ptnode_adr(idx)->set_escape_state(es); + if (orig_es != es) { + // cache the computed escape state + assert(es != PointsToNode::UnknownEscape, "should have computed an escape state"); + ptnode_adr(idx)->set_escape_state(es); + } // orig_es could be PointsToNode::UnknownEscape return es; } -void ConnectionGraph::PointsTo(VectorSet &ptset, Node * n, PhaseTransform *phase) { +void ConnectionGraph::PointsTo(VectorSet &ptset, Node * n) { VectorSet visited(Thread::current()->resource_area()); GrowableArray worklist; @@ -990,7 +994,7 @@ GrowableArray memnode_worklist; GrowableArray orig_phis; - PhaseGVN *igvn = _compile->initial_gvn(); + PhaseGVN *igvn = _igvn; uint new_index_start = (uint) _compile->num_alias_types(); Arena* arena = Thread::current()->resource_area(); VectorSet visited(arena); @@ -1012,7 +1016,7 @@ CallNode *alloc = n->as_Call(); // copy escape information to call node PointsToNode* ptn = ptnode_adr(alloc->_idx); - PointsToNode::EscapeState es = escape_state(alloc, igvn); + PointsToNode::EscapeState es = escape_state(alloc); // We have an allocation or call which returns a Java object, // see if it is unescaped. if (es != PointsToNode::NoEscape || !ptn->_scalar_replaceable) @@ -1123,7 +1127,7 @@ } } else if (n->is_AddP()) { ptset.Clear(); - PointsTo(ptset, get_addp_base(n), igvn); + PointsTo(ptset, get_addp_base(n)); assert(ptset.Size() == 1, "AddP address is unique"); uint elem = ptset.getelem(); // Allocation node's index if (elem == _phantom_object) { @@ -1143,7 +1147,7 @@ continue; // already processed } ptset.Clear(); - PointsTo(ptset, n, igvn); + PointsTo(ptset, n); if (ptset.Size() == 1) { uint elem = ptset.getelem(); // Allocation node's index if (elem == _phantom_object) { @@ -1478,6 +1482,26 @@ return false; } +void ConnectionGraph::do_analysis(Compile *C, PhaseIterGVN *igvn) { + // Add ConP#NULL and ConN#NULL nodes before ConnectionGraph construction + // to create space for them in ConnectionGraph::_nodes[]. + Node* oop_null = igvn->zerocon(T_OBJECT); + Node* noop_null = igvn->zerocon(T_NARROWOOP); + + ConnectionGraph* congraph = new(C->comp_arena()) ConnectionGraph(C, igvn); + // Perform escape analysis + if (congraph->compute_escape()) { + // There are non escaping objects. + C->set_congraph(congraph); + } + + // Cleanup. + if (oop_null->outcnt() == 0) + igvn->hash_delete(oop_null); + if (noop_null->outcnt() == 0) + igvn->hash_delete(noop_null); +} + bool ConnectionGraph::compute_escape() { Compile* C = _compile; @@ -1492,7 +1516,7 @@ } GrowableArray cg_worklist; - PhaseGVN* igvn = C->initial_gvn(); + PhaseGVN* igvn = _igvn; bool has_allocations = false; // Push all useful nodes onto CG list and set their type. @@ -1661,6 +1685,12 @@ _collecting = false; assert(C->unique() == nodes_size(), "there should be no new ideal nodes during ConnectionGraph build"); +#ifndef PRODUCT + if (PrintEscapeAnalysis) { + dump(); // Dump ConnectionGraph + } +#endif + bool has_scalar_replaceable_candidates = alloc_worklist.length() > 0; if ( has_scalar_replaceable_candidates && C->AliasLevel() >= 3 && EliminateAllocations ) { @@ -1671,10 +1701,6 @@ if (C->failing()) return false; - // Clean up after split unique types. - ResourceMark rm; - PhaseRemoveUseless pru(C->initial_gvn(), C->for_igvn()); - C->print_method("After Escape Analysis", 2); #ifdef ASSERT @@ -1711,7 +1737,7 @@ int offset = ptn->offset(); Node* base = get_addp_base(n); ptset.Clear(); - PointsTo(ptset, base, phase); + PointsTo(ptset, base); int ptset_size = ptset.Size(); // Check if a oop field's initializing value is recorded and add @@ -1889,7 +1915,7 @@ arg = get_addp_base(arg); } ptset.Clear(); - PointsTo(ptset, arg, phase); + PointsTo(ptset, arg); for( VectorSetI j(&ptset); j.test(); ++j ) { uint pt = j.elem; set_escape_state(pt, PointsToNode::ArgEscape); @@ -1934,7 +1960,7 @@ } ptset.Clear(); - PointsTo(ptset, arg, phase); + PointsTo(ptset, arg); for( VectorSetI j(&ptset); j.test(); ++j ) { uint pt = j.elem; if (global_escapes) { @@ -1970,7 +1996,7 @@ Node *arg = call->in(i)->uncast(); set_escape_state(arg->_idx, PointsToNode::GlobalEscape); ptset.Clear(); - PointsTo(ptset, arg, phase); + PointsTo(ptset, arg); for( VectorSetI j(&ptset); j.test(); ++j ) { uint pt = j.elem; set_escape_state(pt, PointsToNode::GlobalEscape); @@ -2433,7 +2459,7 @@ Node *base = get_addp_base(n); // Create a field edge to this node from everything base could point to. VectorSet ptset(Thread::current()->resource_area()); - PointsTo(ptset, base, phase); + PointsTo(ptset, base); for( VectorSetI i(&ptset); i.test(); ++i ) { uint pt = i.elem; add_field_edge(pt, n_idx, address_offset(n, phase)); @@ -2501,7 +2527,7 @@ // For everything "adr_base" could point to, create a deferred edge from // this node to each field with the same offset. VectorSet ptset(Thread::current()->resource_area()); - PointsTo(ptset, adr_base, phase); + PointsTo(ptset, adr_base); int offset = address_offset(adr, phase); for( VectorSetI i(&ptset); i.test(); ++i ) { uint pt = i.elem; @@ -2594,7 +2620,7 @@ // For everything "adr_base" could point to, create a deferred edge // to "val" from each field with the same offset. VectorSet ptset(Thread::current()->resource_area()); - PointsTo(ptset, adr_base, phase); + PointsTo(ptset, adr_base); for( VectorSetI i(&ptset); i.test(); ++i ) { uint pt = i.elem; add_edge_from_fields(pt, val->_idx, address_offset(adr, phase)); @@ -2638,7 +2664,6 @@ #ifndef PRODUCT void ConnectionGraph::dump() { - PhaseGVN *igvn = _compile->initial_gvn(); bool first = true; uint size = nodes_size(); @@ -2648,7 +2673,7 @@ if (ptn_type != PointsToNode::JavaObject || ptn->_node == NULL) continue; - PointsToNode::EscapeState es = escape_state(ptn->_node, igvn); + PointsToNode::EscapeState es = escape_state(ptn->_node); if (ptn->_node->is_Allocate() && (es == PointsToNode::NoEscape || Verbose)) { if (first) { tty->cr(); diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/opto/escape.hpp --- a/src/share/vm/opto/escape.hpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/opto/escape.hpp Wed Jul 14 17:52:18 2010 -0400 @@ -227,6 +227,7 @@ uint _noop_null; // ConN(#NULL) Compile * _compile; // Compile object for current compilation + PhaseIterGVN * _igvn; // Value numbering // Address of an element in _nodes. Used when the element is to be modified PointsToNode *ptnode_adr(uint idx) const { @@ -257,7 +258,7 @@ // walk the connection graph starting at the node corresponding to "n" and // add the index of everything it could point to, to "ptset". This may cause // Phi's encountered to get (re)processed (which requires "phase".) - void PointsTo(VectorSet &ptset, Node * n, PhaseTransform *phase); + void PointsTo(VectorSet &ptset, Node * n); // Edge manipulation. The "from_i" and "to_i" arguments are the // node indices of the source and destination of the edge @@ -310,7 +311,7 @@ // Node: This assumes that escape analysis is run before // PhaseIterGVN creation void record_for_optimizer(Node *n) { - _compile->record_for_igvn(n); + _igvn->_worklist.push(n); } // Set the escape state of a node @@ -320,16 +321,20 @@ void verify_escape_state(int nidx, VectorSet& ptset, PhaseTransform* phase); public: - ConnectionGraph(Compile *C); + ConnectionGraph(Compile *C, PhaseIterGVN *igvn); // Check for non-escaping candidates static bool has_candidates(Compile *C); + // Perform escape analysis + static void do_analysis(Compile *C, PhaseIterGVN *igvn); + // Compute the escape information bool compute_escape(); // escape state of a node - PointsToNode::EscapeState escape_state(Node *n, PhaseTransform *phase); + PointsToNode::EscapeState escape_state(Node *n); + // other information we have collected bool is_scalar_replaceable(Node *n) { if (_collecting || (n->_idx >= nodes_size())) diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/prims/jvmtiCodeBlobEvents.cpp --- a/src/share/vm/prims/jvmtiCodeBlobEvents.cpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/prims/jvmtiCodeBlobEvents.cpp Wed Jul 14 17:52:18 2010 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -118,34 +118,13 @@ for (int i=0; i<_global_code_blobs->length(); i++) { JvmtiCodeBlobDesc* scb = _global_code_blobs->at(i); if (addr == scb->code_begin()) { + ShouldNotReachHere(); return; } } - // we must name the CodeBlob - some CodeBlobs already have names :- - // - stubs used by compiled code to call a (static) C++ runtime routine - // - non-relocatable machine code such as the interpreter, stubroutines, etc. - // - various singleton blobs - // - // others are unnamed so we create a name :- - // - OSR adapter (interpreter frame that has been on-stack replaced) - // - I2C and C2I adapters - const char* name = NULL; - if (cb->is_runtime_stub()) { - name = ((RuntimeStub*)cb)->name(); - } - if (cb->is_buffer_blob()) { - name = ((BufferBlob*)cb)->name(); - } - if (cb->is_deoptimization_stub() || cb->is_safepoint_stub()) { - name = ((SingletonBlob*)cb)->name(); - } - if (cb->is_uncommon_trap_stub() || cb->is_exception_stub()) { - name = ((SingletonBlob*)cb)->name(); - } - // record the CodeBlob details as a JvmtiCodeBlobDesc - JvmtiCodeBlobDesc* scb = new JvmtiCodeBlobDesc(name, cb->instructions_begin(), + JvmtiCodeBlobDesc* scb = new JvmtiCodeBlobDesc(cb->name(), cb->instructions_begin(), cb->instructions_end()); _global_code_blobs->append(scb); } @@ -197,7 +176,10 @@ jvmtiError JvmtiCodeBlobEvents::generate_dynamic_code_events(JvmtiEnv* env) { CodeBlobCollector collector; - // first collect all the code blobs + // First collect all the code blobs. This has to be done in a + // single pass over the code cache with CodeCache_lock held because + // there isn't any safe way to iterate over regular CodeBlobs since + // they can be freed at any point. { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); collector.collect(); @@ -213,166 +195,28 @@ } -// Support class to describe a nmethod in the CodeCache - -class nmethodDesc: public CHeapObj { - private: - jmethodID _jmethod_id; - address _code_begin; - address _code_end; - jvmtiAddrLocationMap* _map; - jint _map_length; - public: - nmethodDesc(jmethodID jmethod_id, address code_begin, address code_end, - jvmtiAddrLocationMap* map, jint map_length) { - _jmethod_id = jmethod_id; - _code_begin = code_begin; - _code_end = code_end; - _map = map; - _map_length = map_length; - } - jmethodID jmethod_id() const { return _jmethod_id; } - address code_begin() const { return _code_begin; } - address code_end() const { return _code_end; } - jvmtiAddrLocationMap* map() const { return _map; } - jint map_length() const { return _map_length; } -}; - - -// Support class to collect a list of the nmethod CodeBlobs in -// the CodeCache. -// -// Usage :- -// -// nmethodCollector collector; -// -// collector.collect(); -// JvmtiCodeBlobDesc* blob = collector.first(); -// while (blob != NULL) { -// : -// blob = collector.next(); -// } -// -class nmethodCollector : StackObj { - private: - GrowableArray* _nmethods; // collect nmethods - int _pos; // iteration support - - // used during a collection - static GrowableArray* _global_nmethods; - static void do_nmethod(nmethod* nm); - public: - nmethodCollector() { - _nmethods = NULL; - _pos = -1; - } - ~nmethodCollector() { - if (_nmethods != NULL) { - for (int i=0; i<_nmethods->length(); i++) { - nmethodDesc* blob = _nmethods->at(i); - if (blob->map()!= NULL) { - FREE_C_HEAP_ARRAY(jvmtiAddrLocationMap, blob->map()); - } - } - delete _nmethods; - } - } - - // collect list of nmethods in the cache - void collect(); - - // iteration support - return first code blob - nmethodDesc* first() { - assert(_nmethods != NULL, "not collected"); - if (_nmethods->length() == 0) { - return NULL; - } - _pos = 0; - return _nmethods->at(0); - } - - // iteration support - return next code blob - nmethodDesc* next() { - assert(_pos >= 0, "iteration not started"); - if (_pos+1 >= _nmethods->length()) { - return NULL; - } - return _nmethods->at(++_pos); - } -}; - -// used during collection -GrowableArray* nmethodCollector::_global_nmethods; - - -// called for each nmethod in the CodeCache -// -// This function simply adds a descriptor for each nmethod to the global list. - -void nmethodCollector::do_nmethod(nmethod* nm) { - // ignore zombies - if (!nm->is_alive()) { - return; - } - - assert(nm->method() != NULL, "checking"); - - // create the location map for the nmethod. - jvmtiAddrLocationMap* map; - jint map_length; - JvmtiCodeBlobEvents::build_jvmti_addr_location_map(nm, &map, &map_length); - - // record the nmethod details - nmethodDesc* snm = new nmethodDesc(nm->get_and_cache_jmethod_id(), - nm->code_begin(), - nm->code_end(), - map, - map_length); - _global_nmethods->append(snm); -} - -// collects a list of nmethod in the CodeCache. -// -// The created list is growable array of nmethodDesc - each one describes -// a nmethod and includs its JVMTI address location map. - -void nmethodCollector::collect() { - assert_locked_or_safepoint(CodeCache_lock); - assert(_global_nmethods == NULL, "checking"); - - // create the list - _global_nmethods = new (ResourceObj::C_HEAP) GrowableArray(100,true); - - // any a descriptor for each nmethod to the list. - CodeCache::nmethods_do(do_nmethod); - - // make the list the instance list - _nmethods = _global_nmethods; - _global_nmethods = NULL; -} - // Generate a COMPILED_METHOD_LOAD event for each nnmethod - jvmtiError JvmtiCodeBlobEvents::generate_compiled_method_load_events(JvmtiEnv* env) { HandleMark hm; - nmethodCollector collector; - - // first collect all nmethods - { - MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - collector.collect(); - } - // iterate over the list and post an event for each nmethod - nmethodDesc* nm_desc = collector.first(); - while (nm_desc != NULL) { - jmethodID mid = nm_desc->jmethod_id(); - assert(mid != NULL, "checking"); - JvmtiExport::post_compiled_method_load(env, mid, - (jint)(nm_desc->code_end() - nm_desc->code_begin()), - nm_desc->code_begin(), nm_desc->map_length(), - nm_desc->map()); - nm_desc = collector.next(); + // Walk the CodeCache notifying for live nmethods. The code cache + // may be changing while this is happening which is ok since newly + // created nmethod will notify normally and nmethods which are freed + // can be safely skipped. + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + nmethod* current = CodeCache::first_nmethod(); + while (current != NULL) { + // Lock the nmethod so it can't be freed + nmethodLocker nml(current); + + // Only notify for live nmethods + if (current->is_alive()) { + // Don't hold the lock over the notify or jmethodID creation + MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + current->get_and_cache_jmethod_id(); + JvmtiExport::post_compiled_method_load(current); + } + current = CodeCache::next_nmethod(current); } return JVMTI_ERROR_NONE; } diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/utilities/taskqueue.cpp --- a/src/share/vm/utilities/taskqueue.cpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/utilities/taskqueue.cpp Wed Jul 14 17:52:18 2010 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -182,73 +182,3 @@ _index < objArrayOop(_obj)->length(); } #endif // ASSERT - -bool RegionTaskQueueWithOverflow::is_empty() { - return (_region_queue.size() == 0) && - (_overflow_stack->length() == 0); -} - -bool RegionTaskQueueWithOverflow::stealable_is_empty() { - return _region_queue.size() == 0; -} - -bool RegionTaskQueueWithOverflow::overflow_is_empty() { - return _overflow_stack->length() == 0; -} - -void RegionTaskQueueWithOverflow::initialize() { - _region_queue.initialize(); - assert(_overflow_stack == 0, "Creating memory leak"); - _overflow_stack = - new (ResourceObj::C_HEAP) GrowableArray(10, true); -} - -void RegionTaskQueueWithOverflow::save(RegionTask t) { - if (TraceRegionTasksQueuing && Verbose) { - gclog_or_tty->print_cr("CTQ: save " PTR_FORMAT, t); - } - if(!_region_queue.push(t)) { - _overflow_stack->push(t); - } -} - -// Note that using this method will retrieve all regions -// that have been saved but that it will always check -// the overflow stack. It may be more efficient to -// check the stealable queue and the overflow stack -// separately. -bool RegionTaskQueueWithOverflow::retrieve(RegionTask& region_task) { - bool result = retrieve_from_overflow(region_task); - if (!result) { - result = retrieve_from_stealable_queue(region_task); - } - if (TraceRegionTasksQueuing && Verbose && result) { - gclog_or_tty->print_cr(" CTQ: retrieve " PTR_FORMAT, result); - } - return result; -} - -bool RegionTaskQueueWithOverflow::retrieve_from_stealable_queue( - RegionTask& region_task) { - bool result = _region_queue.pop_local(region_task); - if (TraceRegionTasksQueuing && Verbose) { - gclog_or_tty->print_cr("CTQ: retrieve_stealable " PTR_FORMAT, region_task); - } - return result; -} - -bool -RegionTaskQueueWithOverflow::retrieve_from_overflow(RegionTask& region_task) { - bool result; - if (!_overflow_stack->is_empty()) { - region_task = _overflow_stack->pop(); - result = true; - } else { - region_task = (RegionTask) NULL; - result = false; - } - if (TraceRegionTasksQueuing && Verbose) { - gclog_or_tty->print_cr("CTQ: retrieve_stealable " PTR_FORMAT, region_task); - } - return result; -} diff -r 0e7d2a08b605 -r 1e7ec26380bd src/share/vm/utilities/taskqueue.hpp --- a/src/share/vm/utilities/taskqueue.hpp Wed Jul 07 15:35:58 2010 -0700 +++ b/src/share/vm/utilities/taskqueue.hpp Wed Jul 14 17:52:18 2010 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -109,8 +109,9 @@ public: TaskQueueSuper() : _bottom(0), _age() {} - // Return true if the TaskQueue contains any tasks. - bool peek() { return _bottom != _age.top(); } + // Return true if the TaskQueue contains/does not contain any tasks. + bool peek() const { return _bottom != _age.top(); } + bool is_empty() const { return size() == 0; } // Return an estimate of the number of elements in the queue. // The "careful" version admits the possibility of pop_local/pop_global @@ -165,18 +166,16 @@ void initialize(); - // Push the task "t" on the queue. Returns "false" iff the queue is - // full. + // Push the task "t" on the queue. Returns "false" iff the queue is full. inline bool push(E t); - // If succeeds in claiming a task (from the 'local' end, that is, the - // most recently pushed task), returns "true" and sets "t" to that task. - // Otherwise, the queue is empty and returns false. + // Attempts to claim a task from the "local" end of the queue (the most + // recently pushed). If successful, returns true and sets t to the task; + // otherwise, returns false (the queue is empty). inline bool pop_local(E& t); - // If succeeds in claiming a task (from the 'global' end, that is, the - // least recently pushed task), returns "true" and sets "t" to that task. - // Otherwise, the queue is empty and returns false. + // Like pop_local(), but uses the "global" end of the queue (the least + // recently pushed). bool pop_global(E& t); // Delete any resource associated with the queue. @@ -198,7 +197,6 @@ template void GenericTaskQueue::initialize() { _elems = NEW_C_HEAP_ARRAY(E, N); - guarantee(_elems != NULL, "Allocation failed."); } template @@ -289,7 +287,87 @@ FREE_C_HEAP_ARRAY(E, _elems); } -// Inherits the typedef of "Task" from above. +// OverflowTaskQueue is a TaskQueue that also includes an overflow stack for +// elements that do not fit in the TaskQueue. +// +// Three methods from super classes are overridden: +// +// initialize() - initialize the super classes and create the overflow stack +// push() - push onto the task queue or, if that fails, onto the overflow stack +// is_empty() - return true if both the TaskQueue and overflow stack are empty +// +// Note that size() is not overridden--it returns the number of elements in the +// TaskQueue, and does not include the size of the overflow stack. This +// simplifies replacement of GenericTaskQueues with OverflowTaskQueues. +template +class OverflowTaskQueue: public GenericTaskQueue +{ +public: + typedef GrowableArray overflow_t; + typedef GenericTaskQueue taskqueue_t; + + OverflowTaskQueue(); + ~OverflowTaskQueue(); + void initialize(); + + inline overflow_t* overflow_stack() const { return _overflow_stack; } + + // Push task t onto the queue or onto the overflow stack. Return true. + inline bool push(E t); + + // Attempt to pop from the overflow stack; return true if anything was popped. + inline bool pop_overflow(E& t); + + inline bool taskqueue_empty() const { return taskqueue_t::is_empty(); } + inline bool overflow_empty() const { return overflow_stack()->is_empty(); } + inline bool is_empty() const { + return taskqueue_empty() && overflow_empty(); + } + +private: + overflow_t* _overflow_stack; +}; + +template +OverflowTaskQueue::OverflowTaskQueue() +{ + _overflow_stack = NULL; +} + +template +OverflowTaskQueue::~OverflowTaskQueue() +{ + if (_overflow_stack != NULL) { + delete _overflow_stack; + _overflow_stack = NULL; + } +} + +template +void OverflowTaskQueue::initialize() +{ + taskqueue_t::initialize(); + assert(_overflow_stack == NULL, "memory leak"); + _overflow_stack = new (ResourceObj::C_HEAP) GrowableArray(10, true); +} + +template +bool OverflowTaskQueue::push(E t) +{ + if (!taskqueue_t::push(t)) { + overflow_stack()->push(t); + } + return true; +} + +template +bool OverflowTaskQueue::pop_overflow(E& t) +{ + if (overflow_empty()) return false; + t = overflow_stack()->pop(); + return true; +} + class TaskQueueSetSuper: public CHeapObj { protected: static int randomParkAndMiller(int* seed0); @@ -323,11 +401,11 @@ T* queue(uint n); - // The thread with queue number "queue_num" (and whose random number seed - // is at "seed") is trying to steal a task from some other queue. (It - // may try several queues, according to some configuration parameter.) - // If some steal succeeds, returns "true" and sets "t" the stolen task, - // otherwise returns false. + // The thread with queue number "queue_num" (and whose random number seed is + // at "seed") is trying to steal a task from some other queue. (It may try + // several queues, according to some configuration parameter.) If some steal + // succeeds, returns "true" and sets "t" to the stolen task, otherwise returns + // false. bool steal(uint queue_num, int* seed, E& t); bool peek(); @@ -507,7 +585,7 @@ uint localBot = _bottom; // This value cannot be N-1. That can only occur as a result of // the assignment to bottom in this method. If it does, this method - // resets the size( to 0 before the next call (which is sequential, + // resets the size to 0 before the next call (which is sequential, // since this is pop_local.) uint dirty_n_elems = dirty_size(localBot, _age.top()); assert(dirty_n_elems != N - 1, "Shouldn't be possible..."); @@ -533,8 +611,7 @@ } } -typedef oop Task; -typedef GenericTaskQueue OopTaskQueue; +typedef GenericTaskQueue OopTaskQueue; typedef GenericTaskQueueSet OopTaskQueueSet; #ifdef _MSC_VER @@ -615,35 +692,8 @@ #pragma warning(pop) #endif -typedef GenericTaskQueue OopStarTaskQueue; +typedef OverflowTaskQueue OopStarTaskQueue; typedef GenericTaskQueueSet OopStarTaskQueueSet; -typedef size_t RegionTask; // index for region -typedef GenericTaskQueue RegionTaskQueue; -typedef GenericTaskQueueSet RegionTaskQueueSet; - -class RegionTaskQueueWithOverflow: public CHeapObj { - protected: - RegionTaskQueue _region_queue; - GrowableArray* _overflow_stack; - - public: - RegionTaskQueueWithOverflow() : _overflow_stack(NULL) {} - // Initialize both stealable queue and overflow - void initialize(); - // Save first to stealable queue and then to overflow - void save(RegionTask t); - // Retrieve first from overflow and then from stealable queue - bool retrieve(RegionTask& region_index); - // Retrieve from stealable queue - bool retrieve_from_stealable_queue(RegionTask& region_index); - // Retrieve from overflow - bool retrieve_from_overflow(RegionTask& region_index); - bool is_empty(); - bool stealable_is_empty(); - bool overflow_is_empty(); - uint stealable_size() { return _region_queue.size(); } - RegionTaskQueue* task_queue() { return &_region_queue; } -}; - -#define USE_RegionTaskQueueWithOverflow +typedef OverflowTaskQueue RegionTaskQueue; +typedef GenericTaskQueueSet RegionTaskQueueSet;