提交 4db297ee 编写于 作者: L lana

Merge

......@@ -51,9 +51,9 @@ public class G1CollectedHeap extends SharedHeap {
static private CIntegerField summaryBytesUsedField;
// G1MonitoringSupport* _g1mm;
static private AddressField g1mmField;
// MasterOldRegionSet _old_set;
// HeapRegionSet _old_set;
static private long oldSetFieldOffset;
// MasterHumongousRegionSet _humongous_set;
// HeapRegionSet _humongous_set;
static private long humongousSetFieldOffset;
static {
......
......@@ -40,12 +40,8 @@ import sun.jvm.hotspot.types.TypeDataBase;
// Mirror class for HeapRegionSetBase. Represents a group of regions.
public class HeapRegionSetBase extends VMObject {
// uint _length;
static private CIntegerField lengthField;
// uint _region_num;
static private CIntegerField regionNumField;
// size_t _total_used_bytes;
static private CIntegerField totalUsedBytesField;
static private long countField;
static {
VM.registerVMInitializedObserver(new Observer() {
......@@ -58,21 +54,13 @@ public class HeapRegionSetBase extends VMObject {
static private synchronized void initialize(TypeDataBase db) {
Type type = db.lookupType("HeapRegionSetBase");
lengthField = type.getCIntegerField("_length");
regionNumField = type.getCIntegerField("_region_num");
totalUsedBytesField = type.getCIntegerField("_total_used_bytes");
}
public long length() {
return lengthField.getValue(addr);
countField = type.getField("_count").getOffset();
}
public long regionNum() {
return regionNumField.getValue(addr);
}
public long totalUsedBytes() {
return totalUsedBytesField.getValue(addr);
public HeapRegionSetCount count() {
Address countFieldAddr = addr.addOffsetTo(countField);
return (HeapRegionSetCount) VMObjectFactory.newObject(HeapRegionSetCount.class, countFieldAddr);
}
public HeapRegionSetBase(Address addr) {
......
/*
* Copyright (c) 2014, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package sun.jvm.hotspot.gc_implementation.g1;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;
import sun.jvm.hotspot.debugger.Address;
import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.runtime.VMObject;
import sun.jvm.hotspot.runtime.VMObjectFactory;
import sun.jvm.hotspot.types.AddressField;
import sun.jvm.hotspot.types.CIntegerField;
import sun.jvm.hotspot.types.Type;
import sun.jvm.hotspot.types.TypeDataBase;
// Mirror class for HeapRegionSetCount. Represents a group of regions.
public class HeapRegionSetCount extends VMObject {
static private CIntegerField lengthField;
static private CIntegerField capacityField;
static {
VM.registerVMInitializedObserver(new Observer() {
public void update(Observable o, Object data) {
initialize(VM.getVM().getTypeDataBase());
}
});
}
static private synchronized void initialize(TypeDataBase db) {
Type type = db.lookupType("HeapRegionSetCount");
lengthField = type.getCIntegerField("_length");
capacityField = type.getCIntegerField("_capacity");
}
public long length() {
return lengthField.getValue(addr);
}
public long capacity() {
return capacityField.getValue(addr);
}
public HeapRegionSetCount(Address addr) {
super(addr);
}
}
......@@ -114,7 +114,8 @@ public class HeapSummary extends Tool {
long survivorRegionNum = g1mm.survivorRegionNum();
HeapRegionSetBase oldSet = g1h.oldSet();
HeapRegionSetBase humongousSet = g1h.humongousSet();
long oldRegionNum = oldSet.regionNum() + humongousSet.regionNum();
long oldRegionNum = oldSet.count().length()
+ humongousSet.count().capacity() / HeapRegion.grainBytes();
printG1Space("G1 Heap:", g1h.n_regions(),
g1h.used(), g1h.capacity());
System.out.println("G1 Young Generation:");
......
#
# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2014, 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
......@@ -87,9 +87,10 @@ ifeq ($(INCLUDE_ALL_GCS), false)
g1BlockOffsetTable.cpp g1CardCounts.cpp g1CollectedHeap.cpp g1CollectorPolicy.cpp \
g1ErgoVerbose.cpp g1GCPhaseTimes.cpp g1HRPrinter.cpp g1HotCardCache.cpp g1Log.cpp \
g1MMUTracker.cpp g1MarkSweep.cpp g1MemoryPool.cpp g1MonitoringSupport.cpp g1OopClosures.cpp \
g1RemSet.cpp g1RemSetSummary.cpp g1SATBCardTableModRefBS.cpp g1_globals.cpp heapRegion.cpp \
g1RemSet.cpp g1RemSetSummary.cpp g1SATBCardTableModRefBS.cpp g1StringDedup.cpp g1StringDedupStat.cpp \
g1StringDedupTable.cpp g1StringDedupThread.cpp g1StringDedupQueue.cpp g1_globals.cpp heapRegion.cpp \
g1BiasedArray.cpp heapRegionRemSet.cpp heapRegionSeq.cpp heapRegionSet.cpp heapRegionSets.cpp \
ptrQueue.cpp satbQueue.cpp sparsePRT.cpp survRateGroup.cpp vm_operations_g1.cpp \
ptrQueue.cpp satbQueue.cpp sparsePRT.cpp survRateGroup.cpp vm_operations_g1.cpp g1CodeCacheRemSet.cpp \
adjoiningGenerations.cpp adjoiningVirtualSpaces.cpp asPSOldGen.cpp asPSYoungGen.cpp \
cardTableExtension.cpp gcTaskManager.cpp gcTaskThread.cpp objectStartArray.cpp \
parallelScavengeHeap.cpp parMarkBitMap.cpp pcTasks.cpp psAdaptiveSizePolicy.cpp \
......
......@@ -33,7 +33,7 @@ jprt.need.sibling.build=false
# This tells jprt what default release we want to build
jprt.hotspot.default.release=jdk8
jprt.hotspot.default.release=jdk9
jprt.tools.default.release=${jprt.submit.option.release?${jprt.submit.option.release}:${jprt.hotspot.default.release}}
......@@ -47,72 +47,50 @@ jprt.sync.push=false
# sparc etc.
# Define the Solaris platforms we want for the various releases
jprt.my.solaris.sparcv9.jdk8=solaris_sparcv9_5.10
jprt.my.solaris.sparcv9.jdk7=solaris_sparcv9_5.10
jprt.my.solaris.sparcv9.jdk7u8=${jprt.my.solaris.sparcv9.jdk7}
jprt.my.solaris.sparcv9.jdk9=solaris_sparcv9_5.10
jprt.my.solaris.sparcv9=${jprt.my.solaris.sparcv9.${jprt.tools.default.release}}
jprt.my.solaris.x64.jdk8=solaris_x64_5.10
jprt.my.solaris.x64.jdk7=solaris_x64_5.10
jprt.my.solaris.x64.jdk7u8=${jprt.my.solaris.x64.jdk7}
jprt.my.solaris.x64.jdk9=solaris_x64_5.10
jprt.my.solaris.x64=${jprt.my.solaris.x64.${jprt.tools.default.release}}
jprt.my.linux.i586.jdk8=linux_i586_2.6
jprt.my.linux.i586.jdk7=linux_i586_2.6
jprt.my.linux.i586.jdk7u8=${jprt.my.linux.i586.jdk7}
jprt.my.linux.i586.jdk9=linux_i586_2.6
jprt.my.linux.i586=${jprt.my.linux.i586.${jprt.tools.default.release}}
jprt.my.linux.x64.jdk8=linux_x64_2.6
jprt.my.linux.x64.jdk7=linux_x64_2.6
jprt.my.linux.x64.jdk7u8=${jprt.my.linux.x64.jdk7}
jprt.my.linux.x64.jdk9=linux_x64_2.6
jprt.my.linux.x64=${jprt.my.linux.x64.${jprt.tools.default.release}}
jprt.my.linux.ppc.jdk8=linux_ppc_2.6
jprt.my.linux.ppc.jdk7=linux_ppc_2.6
jprt.my.linux.ppc.jdk7u8=${jprt.my.linux.ppc.jdk7}
jprt.my.linux.ppc.jdk9=linux_ppc_2.6
jprt.my.linux.ppc=${jprt.my.linux.ppc.${jprt.tools.default.release}}
jprt.my.linux.ppcv2.jdk8=linux_ppcv2_2.6
jprt.my.linux.ppcv2.jdk7=linux_ppcv2_2.6
jprt.my.linux.ppcv2.jdk7u8=${jprt.my.linux.ppcv2.jdk7}
jprt.my.linux.ppcv2.jdk9=linux_ppcv2_2.6
jprt.my.linux.ppcv2=${jprt.my.linux.ppcv2.${jprt.tools.default.release}}
jprt.my.linux.ppcsflt.jdk8=linux_ppcsflt_2.6
jprt.my.linux.ppcsflt.jdk7=linux_ppcsflt_2.6
jprt.my.linux.ppcsflt.jdk7u8=${jprt.my.linux.ppcsflt.jdk7}
jprt.my.linux.ppcsflt.jdk9=linux_ppcsflt_2.6
jprt.my.linux.ppcsflt=${jprt.my.linux.ppcsflt.${jprt.tools.default.release}}
jprt.my.linux.armvfpsflt.jdk8=linux_armvfpsflt_2.6
jprt.my.linux.armvfpsflt.jdk9=linux_armvfpsflt_2.6
jprt.my.linux.armvfpsflt=${jprt.my.linux.armvfpsflt.${jprt.tools.default.release}}
jprt.my.linux.armvfphflt.jdk8=linux_armvfphflt_2.6
jprt.my.linux.armvfphflt.jdk9=linux_armvfphflt_2.6
jprt.my.linux.armvfphflt=${jprt.my.linux.armvfphflt.${jprt.tools.default.release}}
# The ARM GP vfp-sflt build is not currently supported
#jprt.my.linux.armvs.jdk8=linux_armvs_2.6
#jprt.my.linux.armvs.jdk9=linux_armvs_2.6
#jprt.my.linux.armvs=${jprt.my.linux.armvs.${jprt.tools.default.release}}
jprt.my.linux.armvh.jdk8=linux_armvh_2.6
jprt.my.linux.armvh.jdk9=linux_armvh_2.6
jprt.my.linux.armvh=${jprt.my.linux.armvh.${jprt.tools.default.release}}
jprt.my.linux.armsflt.jdk8=linux_armsflt_2.6
jprt.my.linux.armsflt.jdk7=linux_armsflt_2.6
jprt.my.linux.armsflt.jdk7u8=${jprt.my.linux.armsflt.jdk7}
jprt.my.linux.armsflt.jdk9=linux_armsflt_2.6
jprt.my.linux.armsflt=${jprt.my.linux.armsflt.${jprt.tools.default.release}}
jprt.my.macosx.x64.jdk8=macosx_x64_10.7
jprt.my.macosx.x64.jdk7=macosx_x64_10.7
jprt.my.macosx.x64.jdk7u8=${jprt.my.macosx.x64.jdk7}
jprt.my.macosx.x64.jdk9=macosx_x64_10.7
jprt.my.macosx.x64=${jprt.my.macosx.x64.${jprt.tools.default.release}}
jprt.my.windows.i586.jdk8=windows_i586_6.1
jprt.my.windows.i586.jdk7=windows_i586_6.1
jprt.my.windows.i586.jdk7u8=${jprt.my.windows.i586.jdk7}
jprt.my.windows.i586.jdk9=windows_i586_6.1
jprt.my.windows.i586=${jprt.my.windows.i586.${jprt.tools.default.release}}
jprt.my.windows.x64.jdk8=windows_x64_6.1
jprt.my.windows.x64.jdk7=windows_x64_6.1
jprt.my.windows.x64.jdk7u8=${jprt.my.windows.x64.jdk7}
jprt.my.windows.x64.jdk9=windows_x64_6.1
jprt.my.windows.x64=${jprt.my.windows.x64.${jprt.tools.default.release}}
# Standard list of jprt build targets for this source tree
......@@ -143,9 +121,7 @@ jprt.build.targets.embedded= \
jprt.build.targets.all=${jprt.build.targets.standard}, \
${jprt.build.targets.embedded}, ${jprt.build.targets.open}
jprt.build.targets.jdk8=${jprt.build.targets.all}
jprt.build.targets.jdk7=${jprt.build.targets.all}
jprt.build.targets.jdk7u8=${jprt.build.targets.all}
jprt.build.targets.jdk9=${jprt.build.targets.all}
jprt.build.targets=${jprt.build.targets.${jprt.tools.default.release}}
# Subset lists of test targets for this source tree
......@@ -349,9 +325,7 @@ jprt.test.targets.embedded= \
${jprt.my.windows.i586.test.targets}, \
${jprt.my.windows.x64.test.targets}
jprt.test.targets.jdk8=${jprt.test.targets.standard}
jprt.test.targets.jdk7=${jprt.test.targets.standard}
jprt.test.targets.jdk7u8=${jprt.test.targets.jdk7}
jprt.test.targets.jdk9=${jprt.test.targets.standard}
jprt.test.targets=${jprt.test.targets.${jprt.tools.default.release}}
# The default test/Makefile targets that should be run
......@@ -399,9 +373,7 @@ jprt.make.rule.test.targets.standard = \
jprt.make.rule.test.targets.embedded = \
${jprt.make.rule.test.targets.standard.client}
jprt.make.rule.test.targets.jdk8=${jprt.make.rule.test.targets.standard}
jprt.make.rule.test.targets.jdk7=${jprt.make.rule.test.targets.standard}
jprt.make.rule.test.targets.jdk7u8=${jprt.make.rule.test.targets.jdk7}
jprt.make.rule.test.targets.jdk9=${jprt.make.rule.test.targets.standard}
jprt.make.rule.test.targets=${jprt.make.rule.test.targets.${jprt.tools.default.release}}
# 7155453: Work-around to prevent popups on OSX from blocking test completion
......
......@@ -3593,6 +3593,11 @@ void os::Aix::check_signal_handler(int sig) {
tty->print_cr(" found:%s", get_signal_handler_name(thisHandler, buf, O_BUFLEN));
// No need to check this sig any longer
sigaddset(&check_signal_done, sig);
// Running under non-interactive shell, SHUTDOWN2_SIGNAL will be reassigned SIG_IGN
if (sig == SHUTDOWN2_SIGNAL && !isatty(fileno(stdin))) {
tty->print_cr("Running in non-interactive shell, %s handler is replaced by shell",
exception_name(sig, buf, O_BUFLEN));
}
} else if (os::Aix::get_our_sigflags(sig) != 0 && (int)act.sa_flags != os::Aix::get_our_sigflags(sig)) {
tty->print("Warning: %s handler flags ", exception_name(sig, buf, O_BUFLEN));
tty->print("expected:" PTR32_FORMAT, os::Aix::get_our_sigflags(sig));
......
......@@ -3374,6 +3374,11 @@ void os::Bsd::check_signal_handler(int sig) {
tty->print_cr(" found:%s", get_signal_handler_name(thisHandler, buf, O_BUFLEN));
// No need to check this sig any longer
sigaddset(&check_signal_done, sig);
// Running under non-interactive shell, SHUTDOWN2_SIGNAL will be reassigned SIG_IGN
if (sig == SHUTDOWN2_SIGNAL && !isatty(fileno(stdin))) {
tty->print_cr("Running in non-interactive shell, %s handler is replaced by shell",
exception_name(sig, buf, O_BUFLEN));
}
} else if(os::Bsd::get_our_sigflags(sig) != 0 && (int)act.sa_flags != os::Bsd::get_our_sigflags(sig)) {
tty->print("Warning: %s handler flags ", exception_name(sig, buf, O_BUFLEN));
tty->print("expected:" PTR32_FORMAT, os::Bsd::get_our_sigflags(sig));
......
......@@ -4560,6 +4560,11 @@ void os::Linux::check_signal_handler(int sig) {
tty->print_cr(" found:%s", get_signal_handler_name(thisHandler, buf, O_BUFLEN));
// No need to check this sig any longer
sigaddset(&check_signal_done, sig);
// Running under non-interactive shell, SHUTDOWN2_SIGNAL will be reassigned SIG_IGN
if (sig == SHUTDOWN2_SIGNAL && !isatty(fileno(stdin))) {
tty->print_cr("Running in non-interactive shell, %s handler is replaced by shell",
exception_name(sig, buf, O_BUFLEN));
}
} else if(os::Linux::get_our_sigflags(sig) != 0 && (int)act.sa_flags != os::Linux::get_our_sigflags(sig)) {
tty->print("Warning: %s handler flags ", exception_name(sig, buf, O_BUFLEN));
tty->print("expected:" PTR32_FORMAT, os::Linux::get_our_sigflags(sig));
......
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2014, 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
......@@ -41,7 +41,6 @@ void OSThread::pd_initialize() {
_thread_id = 0;
sigemptyset(&_caller_sigmask);
_saved_interrupt_thread_state = _thread_new;
_vm_created_thread = false;
}
......
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2014, 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
......@@ -82,20 +82,6 @@
void set_ucontext(ucontext_t* ptr) { _ucontext = ptr; }
static void SR_handler(Thread* thread, ucontext_t* uc);
// ***************************************************************
// java.lang.Thread.interrupt state.
// ***************************************************************
private:
JavaThreadState _saved_interrupt_thread_state; // the thread state before a system call -- restored afterward
public:
JavaThreadState saved_interrupt_thread_state() { return _saved_interrupt_thread_state; }
void set_saved_interrupt_thread_state(JavaThreadState state) { _saved_interrupt_thread_state = state; }
static void handle_spinlock_contention(int tries); // Used for thread local eden locking
// ***************************************************************
......
......@@ -311,33 +311,6 @@ struct tm* os::localtime_pd(const time_t* clock, struct tm* res) {
return localtime_r(clock, res);
}
// interruptible infrastructure
// setup_interruptible saves the thread state before going into an
// interruptible system call.
// The saved state is used to restore the thread to
// its former state whether or not an interrupt is received.
// Used by classloader os::read
// os::restartable_read calls skip this layer and stay in _thread_in_native
void os::Solaris::setup_interruptible(JavaThread* thread) {
JavaThreadState thread_state = thread->thread_state();
assert(thread_state != _thread_blocked, "Coming from the wrong thread");
assert(thread_state != _thread_in_native, "Native threads skip setup_interruptible");
OSThread* osthread = thread->osthread();
osthread->set_saved_interrupt_thread_state(thread_state);
thread->frame_anchor()->make_walkable(thread);
ThreadStateTransition::transition(thread, thread_state, _thread_blocked);
}
JavaThread* os::Solaris::setup_interruptible() {
JavaThread* thread = (JavaThread*)ThreadLocalStorage::thread();
setup_interruptible(thread);
return thread;
}
void os::Solaris::try_enable_extended_io() {
typedef int (*enable_extended_FILE_stdio_t)(int, int);
......@@ -353,41 +326,6 @@ void os::Solaris::try_enable_extended_io() {
}
}
#ifdef ASSERT
JavaThread* os::Solaris::setup_interruptible_native() {
JavaThread* thread = (JavaThread*)ThreadLocalStorage::thread();
JavaThreadState thread_state = thread->thread_state();
assert(thread_state == _thread_in_native, "Assumed thread_in_native");
return thread;
}
void os::Solaris::cleanup_interruptible_native(JavaThread* thread) {
JavaThreadState thread_state = thread->thread_state();
assert(thread_state == _thread_in_native, "Assumed thread_in_native");
}
#endif
// cleanup_interruptible reverses the effects of setup_interruptible
// setup_interruptible_already_blocked() does not need any cleanup.
void os::Solaris::cleanup_interruptible(JavaThread* thread) {
OSThread* osthread = thread->osthread();
ThreadStateTransition::transition(thread, _thread_blocked, osthread->saved_interrupt_thread_state());
}
// I/O interruption related counters called in _INTERRUPTIBLE
void os::Solaris::bump_interrupted_before_count() {
RuntimeService::record_interrupted_before_count();
}
void os::Solaris::bump_interrupted_during_count() {
RuntimeService::record_interrupted_during_count();
}
static int _processors_online = 0;
jint os::Solaris::_os_thread_limit = 0;
......@@ -3366,11 +3304,20 @@ bool os::can_execute_large_page_memory() {
// Read calls from inside the vm need to perform state transitions
size_t os::read(int fd, void *buf, unsigned int nBytes) {
INTERRUPTIBLE_RETURN_INT_VM(::read(fd, buf, nBytes), os::Solaris::clear_interrupted);
size_t res;
JavaThread* thread = (JavaThread*)Thread::current();
assert(thread->thread_state() == _thread_in_vm, "Assumed _thread_in_vm");
ThreadBlockInVM tbiv(thread);
RESTARTABLE(::read(fd, buf, (size_t) nBytes), res);
return res;
}
size_t os::restartable_read(int fd, void *buf, unsigned int nBytes) {
INTERRUPTIBLE_RETURN_INT(::read(fd, buf, nBytes), os::Solaris::clear_interrupted);
size_t res;
assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native,
"Assumed _thread_in_native");
RESTARTABLE(::read(fd, buf, (size_t) nBytes), res);
return res;
}
void os::naked_short_sleep(jlong ms) {
......@@ -4471,6 +4418,11 @@ void os::Solaris::check_signal_handler(int sig) {
tty->print_cr(" found:%s", get_signal_handler_name(thisHandler, buf, O_BUFLEN));
// No need to check this sig any longer
sigaddset(&check_signal_done, sig);
// Running under non-interactive shell, SHUTDOWN2_SIGNAL will be reassigned SIG_IGN
if (sig == SHUTDOWN2_SIGNAL && !isatty(fileno(stdin))) {
tty->print_cr("Running in non-interactive shell, %s handler is replaced by shell",
exception_name(sig, buf, O_BUFLEN));
}
} else if(os::Solaris::get_our_sigflags(sig) != 0 && act.sa_flags != os::Solaris::get_our_sigflags(sig)) {
tty->print("Warning: %s handler flags ", exception_name(sig, buf, O_BUFLEN));
tty->print("expected:" PTR32_FORMAT, os::Solaris::get_our_sigflags(sig));
......@@ -5305,6 +5257,8 @@ int os::fsync(int fd) {
}
int os::available(int fd, jlong *bytes) {
assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native,
"Assumed _thread_in_native");
jlong cur, end;
int mode;
struct stat64 buf64;
......@@ -5312,14 +5266,9 @@ int os::available(int fd, jlong *bytes) {
if (::fstat64(fd, &buf64) >= 0) {
mode = buf64.st_mode;
if (S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) {
/*
* XXX: is the following call interruptible? If so, this might
* need to go through the INTERRUPT_IO() wrapper as for other
* blocking, interruptible calls in this file.
*/
int n,ioctl_return;
INTERRUPTIBLE(::ioctl(fd, FIONREAD, &n),ioctl_return,os::Solaris::clear_interrupted);
RESTARTABLE(::ioctl(fd, FIONREAD, &n), ioctl_return);
if (ioctl_return>= 0) {
*bytes = n;
return 1;
......@@ -6250,7 +6199,11 @@ bool os::is_headless_jre() {
}
size_t os::write(int fd, const void *buf, unsigned int nBytes) {
INTERRUPTIBLE_RETURN_INT(::write(fd, buf, nBytes), os::Solaris::clear_interrupted);
size_t res;
assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native,
"Assumed _thread_in_native");
RESTARTABLE((size_t) ::write(fd, buf, (size_t) nBytes), res);
return res;
}
int os::close(int fd) {
......@@ -6262,11 +6215,15 @@ int os::socket_close(int fd) {
}
int os::recv(int fd, char* buf, size_t nBytes, uint flags) {
INTERRUPTIBLE_RETURN_INT((int)::recv(fd, buf, nBytes, flags), os::Solaris::clear_interrupted);
assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native,
"Assumed _thread_in_native");
RESTARTABLE_RETURN_INT((int)::recv(fd, buf, nBytes, flags));
}
int os::send(int fd, char* buf, size_t nBytes, uint flags) {
INTERRUPTIBLE_RETURN_INT((int)::send(fd, buf, nBytes, flags), os::Solaris::clear_interrupted);
assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native,
"Assumed _thread_in_native");
RESTARTABLE_RETURN_INT((int)::send(fd, buf, nBytes, flags));
}
int os::raw_send(int fd, char* buf, size_t nBytes, uint flags) {
......@@ -6287,11 +6244,14 @@ int os::timeout(int fd, long timeout) {
pfd.fd = fd;
pfd.events = POLLIN;
assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native,
"Assumed _thread_in_native");
gettimeofday(&t, &aNull);
prevtime = ((julong)t.tv_sec * 1000) + t.tv_usec / 1000;
for(;;) {
INTERRUPTIBLE_NORESTART(::poll(&pfd, 1, timeout), res, os::Solaris::clear_interrupted);
res = ::poll(&pfd, 1, timeout);
if(res == OS_ERR && errno == EINTR) {
if(timeout != -1) {
gettimeofday(&t, &aNull);
......@@ -6307,17 +6267,30 @@ int os::timeout(int fd, long timeout) {
int os::connect(int fd, struct sockaddr *him, socklen_t len) {
int _result;
INTERRUPTIBLE_NORESTART(::connect(fd, him, len), _result,\
os::Solaris::clear_interrupted);
// Depending on when thread interruption is reset, _result could be
// one of two values when errno == EINTR
if (((_result == OS_INTRPT) || (_result == OS_ERR))
&& (errno == EINTR)) {
_result = ::connect(fd, him, len);
// On Solaris, when a connect() call is interrupted, the connection
// can be established asynchronously (see 6343810). Subsequent calls
// to connect() must check the errno value which has the semantic
// described below (copied from the connect() man page). Handling
// of asynchronously established connections is required for both
// blocking and non-blocking sockets.
// EINTR The connection attempt was interrupted
// before any data arrived by the delivery of
// a signal. The connection, however, will be
// established asynchronously.
//
// EINPROGRESS The socket is non-blocking, and the connec-
// tion cannot be completed immediately.
//
// EALREADY The socket is non-blocking, and a previous
// connection attempt has not yet been com-
// pleted.
//
// EISCONN The socket is already connected.
if (_result == OS_ERR && errno == EINTR) {
/* restarting a connect() changes its errno semantics */
INTERRUPTIBLE(::connect(fd, him, len), _result,\
os::Solaris::clear_interrupted);
RESTARTABLE(::connect(fd, him, len), _result);
/* undo these changes */
if (_result == OS_ERR) {
if (errno == EALREADY) {
......@@ -6335,20 +6308,23 @@ int os::accept(int fd, struct sockaddr* him, socklen_t* len) {
if (fd < 0) {
return OS_ERR;
}
INTERRUPTIBLE_RETURN_INT((int)::accept(fd, him, len),\
os::Solaris::clear_interrupted);
assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native,
"Assumed _thread_in_native");
RESTARTABLE_RETURN_INT((int)::accept(fd, him, len));
}
int os::recvfrom(int fd, char* buf, size_t nBytes, uint flags,
sockaddr* from, socklen_t* fromlen) {
INTERRUPTIBLE_RETURN_INT((int)::recvfrom(fd, buf, nBytes, flags, from, fromlen),\
os::Solaris::clear_interrupted);
assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native,
"Assumed _thread_in_native");
RESTARTABLE_RETURN_INT((int)::recvfrom(fd, buf, nBytes, flags, from, fromlen));
}
int os::sendto(int fd, char* buf, size_t len, uint flags,
struct sockaddr* to, socklen_t tolen) {
INTERRUPTIBLE_RETURN_INT((int)::sendto(fd, buf, len, flags, to, tolen),\
os::Solaris::clear_interrupted);
assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native,
"Assumed _thread_in_native");
RESTARTABLE_RETURN_INT((int)::sendto(fd, buf, len, flags, to, tolen));
}
int os::socket_available(int fd, jint *pbytes) {
......@@ -6363,8 +6339,9 @@ int os::socket_available(int fd, jint *pbytes) {
}
int os::bind(int fd, struct sockaddr* him, socklen_t len) {
INTERRUPTIBLE_RETURN_INT_NORESTART(::bind(fd, him, len),\
os::Solaris::clear_interrupted);
assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native,
"Assumed _thread_in_native");
return ::bind(fd, him, len);
}
// Get the default path to the core file
......
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2014, 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
......@@ -311,24 +311,6 @@ class Solaris {
outdata, validity) : -1;
}
enum {
clear_interrupted = true
};
static void setup_interruptible(JavaThread* thread);
static void setup_interruptible_already_blocked(JavaThread* thread);
static JavaThread* setup_interruptible();
static void cleanup_interruptible(JavaThread* thread);
// perf counter incrementers used by _INTERRUPTIBLE
static void bump_interrupted_before_count();
static void bump_interrupted_during_count();
#ifdef ASSERT
static JavaThread* setup_interruptible_native();
static void cleanup_interruptible_native(JavaThread* thread);
#endif
static sigset_t* unblocked_signals();
static sigset_t* vm_signals();
static sigset_t* allowdebug_blocked_signals();
......
......@@ -111,104 +111,7 @@ inline int os::closedir(DIR *dirp) {
//////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// macros for interruptible io and system calls and system call restarting
#define _INTERRUPTIBLE(_setup, _cmd, _result, _thread, _clear, _before, _after, _int_enable) \
do { \
_setup; \
_before; \
OSThread* _osthread = _thread->osthread(); \
if (_int_enable && _thread->has_last_Java_frame()) { \
/* this is java interruptible io stuff */ \
if (os::is_interrupted(_thread, _clear)) { \
os::Solaris::bump_interrupted_before_count(); \
_result = OS_INTRPT; \
} else { \
/* _cmd always expands to an assignment to _result */ \
if ((_cmd) < 0 && errno == EINTR \
&& os::is_interrupted(_thread, _clear)) { \
os::Solaris::bump_interrupted_during_count(); \
_result = OS_INTRPT; \
} \
} \
} else { \
/* this is normal blocking io stuff */ \
_cmd; \
} \
_after; \
} while(false)
// Interruptible io support + restarting of interrupted system calls
#ifndef ASSERT
#define INTERRUPTIBLE(_cmd, _result, _clear) do { \
_INTERRUPTIBLE( JavaThread* _thread = (JavaThread*)ThreadLocalStorage::thread(),_result = _cmd, _result, _thread, _clear, , , UseVMInterruptibleIO); \
} while((_result == OS_ERR) && (errno == EINTR))
#else
// This adds an assertion that it is only called from thread_in_native
// The call overhead is skipped for performance in product mode
#define INTERRUPTIBLE(_cmd, _result, _clear) do { \
_INTERRUPTIBLE(JavaThread* _thread = os::Solaris::setup_interruptible_native(), _result = _cmd, _result, _thread, _clear, , os::Solaris::cleanup_interruptible_native(_thread), UseVMInterruptibleIO ); \
} while((_result == OS_ERR) && (errno == EINTR))
#endif
// Used for calls from _thread_in_vm, not from _thread_in_native
#define INTERRUPTIBLE_VM(_cmd, _result, _clear) do { \
_INTERRUPTIBLE(JavaThread* _thread = os::Solaris::setup_interruptible(), _result = _cmd, _result, _thread, _clear, , os::Solaris::cleanup_interruptible(_thread), UseVMInterruptibleIO ); \
} while((_result == OS_ERR) && (errno == EINTR))
/* Use NORESTART when the system call cannot return EINTR, when something other
than a system call is being invoked, or when the caller must do EINTR
handling. */
#ifndef ASSERT
#define INTERRUPTIBLE_NORESTART(_cmd, _result, _clear) \
_INTERRUPTIBLE( JavaThread* _thread = (JavaThread*)ThreadLocalStorage::thread(),_result = _cmd, _result, _thread, _clear, , , UseVMInterruptibleIO)
#else
// This adds an assertion that it is only called from thread_in_native
// The call overhead is skipped for performance in product mode
#define INTERRUPTIBLE_NORESTART(_cmd, _result, _clear) \
_INTERRUPTIBLE(JavaThread* _thread = os::Solaris::setup_interruptible_native(), _result = _cmd, _result, _thread, _clear, , os::Solaris::cleanup_interruptible_native(_thread), UseVMInterruptibleIO )
#endif
// Don't attend to UseVMInterruptibleIO. Always allow interruption.
// Also assumes that it is called from the _thread_blocked state.
// Used by os_sleep().
#define INTERRUPTIBLE_NORESTART_VM_ALWAYS(_cmd, _result, _thread, _clear) \
_INTERRUPTIBLE(os::Solaris::setup_interruptible_already_blocked(_thread), _result = _cmd, _result, _thread, _clear, , , true )
#define INTERRUPTIBLE_RETURN_INT(_cmd, _clear) do { \
int _result; \
do { \
INTERRUPTIBLE(_cmd, _result, _clear); \
} while((_result == OS_ERR) && (errno == EINTR)); \
return _result; \
} while(false)
#define INTERRUPTIBLE_RETURN_INT_VM(_cmd, _clear) do { \
int _result; \
do { \
INTERRUPTIBLE_VM(_cmd, _result, _clear); \
} while((_result == OS_ERR) && (errno == EINTR)); \
return _result; \
} while(false)
#define INTERRUPTIBLE_RETURN_INT_NORESTART(_cmd, _clear) do { \
int _result; \
INTERRUPTIBLE_NORESTART(_cmd, _result, _clear); \
return _result; \
} while(false)
/* Use the RESTARTABLE macros when interruptible io is not needed */
// macros for restartable system calls
#define RESTARTABLE(_cmd, _result) do { \
do { \
......
......@@ -42,7 +42,6 @@ define_pd_global(intx, VMThreadStackSize, 512);
#endif // AMD64
define_pd_global(intx, CompilerThreadStackSize, 0);
define_pd_global(uintx, SurvivorRatio, 8);
define_pd_global(uintx, JVMInvokeMethodSlack, 8192);
......
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2014, 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
......@@ -61,10 +61,6 @@ class java_lang_String : AllStatic {
static Handle basic_create(int length, TRAPS);
static void set_value( oop string, typeArrayOop buffer) {
assert(initialized, "Must be initialized");
string->obj_field_put(value_offset, (oop)buffer);
}
static void set_offset(oop string, int offset) {
assert(initialized, "Must be initialized");
if (offset_offset > 0) {
......@@ -122,12 +118,26 @@ class java_lang_String : AllStatic {
return hash_offset;
}
static void set_value(oop string, typeArrayOop buffer) {
assert(initialized && (value_offset > 0), "Must be initialized");
string->obj_field_put(value_offset, (oop)buffer);
}
static void set_hash(oop string, unsigned int hash) {
assert(initialized && (hash_offset > 0), "Must be initialized");
string->int_field_put(hash_offset, hash);
}
// Accessors
static typeArrayOop value(oop java_string) {
assert(initialized && (value_offset > 0), "Must be initialized");
assert(is_instance(java_string), "must be java_string");
return (typeArrayOop) java_string->obj_field(value_offset);
}
static unsigned int hash(oop java_string) {
assert(initialized && (hash_offset > 0), "Must be initialized");
assert(is_instance(java_string), "must be java_string");
return java_string->int_field(hash_offset);
}
static int offset(oop java_string) {
assert(initialized, "Must be initialized");
assert(is_instance(java_string), "must be java_string");
......
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2014, 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
......@@ -35,6 +35,9 @@
#include "oops/oop.inline2.hpp"
#include "runtime/mutexLocker.hpp"
#include "utilities/hashtable.inline.hpp"
#if INCLUDE_ALL_GCS
#include "gc_implementation/g1/g1StringDedup.hpp"
#endif
// --------------------------------------------------------------------------
......@@ -728,6 +731,15 @@ oop StringTable::intern(Handle string_or_null, jchar* name,
string = java_lang_String::create_from_unicode(name, len, CHECK_NULL);
}
#if INCLUDE_ALL_GCS
if (G1StringDedup::is_enabled()) {
// Deduplicate the string before it is interned. Note that we should never
// deduplicate a string after it has been interned. Doing so will counteract
// compiler optimizations done on e.g. interned string literals.
G1StringDedup::deduplicate(string());
}
#endif
// Grab the StringTable_lock before getting the_table() because it could
// change at safepoint.
MutexLocker ml(StringTable_lock, THREAD);
......
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2014, 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
......@@ -497,6 +497,7 @@
template(int_StringBuffer_signature, "(I)Ljava/lang/StringBuffer;") \
template(char_StringBuffer_signature, "(C)Ljava/lang/StringBuffer;") \
template(int_String_signature, "(I)Ljava/lang/String;") \
template(codesource_permissioncollection_signature, "(Ljava/security/CodeSource;Ljava/security/PermissionCollection;)V") \
/* signature symbols needed by intrinsics */ \
VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, template, VM_ALIAS_IGNORE) \
\
......
......@@ -2496,7 +2496,8 @@ void CMSCollector::save_heap_summary() {
}
void CMSCollector::report_heap_summary(GCWhen::Type when) {
_gc_tracer_cm->report_gc_heap_summary(when, _last_heap_summary, _last_metaspace_summary);
_gc_tracer_cm->report_gc_heap_summary(when, _last_heap_summary);
_gc_tracer_cm->report_metaspace_summary(when, _last_metaspace_summary);
}
void CMSCollector::collect_in_foreground(bool clear_all_soft_refs, GCCause::Cause cause) {
......
......@@ -1809,8 +1809,8 @@ class G1NoteEndOfConcMarkClosure : public HeapRegionClosure {
uint _regions_claimed;
size_t _freed_bytes;
FreeRegionList* _local_cleanup_list;
OldRegionSet* _old_proxy_set;
HumongousRegionSet* _humongous_proxy_set;
HeapRegionSetCount _old_regions_removed;
HeapRegionSetCount _humongous_regions_removed;
HRRSCleanupTask* _hrrs_cleanup_task;
double _claimed_region_time;
double _max_region_time;
......@@ -1819,19 +1819,19 @@ public:
G1NoteEndOfConcMarkClosure(G1CollectedHeap* g1,
int worker_num,
FreeRegionList* local_cleanup_list,
OldRegionSet* old_proxy_set,
HumongousRegionSet* humongous_proxy_set,
HRRSCleanupTask* hrrs_cleanup_task) :
_g1(g1), _worker_num(worker_num),
_max_live_bytes(0), _regions_claimed(0),
_freed_bytes(0),
_claimed_region_time(0.0), _max_region_time(0.0),
_local_cleanup_list(local_cleanup_list),
_old_proxy_set(old_proxy_set),
_humongous_proxy_set(humongous_proxy_set),
_old_regions_removed(),
_humongous_regions_removed(),
_hrrs_cleanup_task(hrrs_cleanup_task) { }
size_t freed_bytes() { return _freed_bytes; }
const HeapRegionSetCount& old_regions_removed() { return _old_regions_removed; }
const HeapRegionSetCount& humongous_regions_removed() { return _humongous_regions_removed; }
bool doHeapRegion(HeapRegion *hr) {
if (hr->continuesHumongous()) {
......@@ -1844,13 +1844,22 @@ public:
_regions_claimed++;
hr->note_end_of_marking();
_max_live_bytes += hr->max_live_bytes();
_g1->free_region_if_empty(hr,
&_freed_bytes,
_local_cleanup_list,
_old_proxy_set,
_humongous_proxy_set,
_hrrs_cleanup_task,
true /* par */);
if (hr->used() > 0 && hr->max_live_bytes() == 0 && !hr->is_young()) {
_freed_bytes += hr->used();
hr->set_containing_set(NULL);
if (hr->isHumongous()) {
assert(hr->startsHumongous(), "we should only see starts humongous");
_humongous_regions_removed.increment(1u, hr->capacity());
_g1->free_humongous_region(hr, _local_cleanup_list, true);
} else {
_old_regions_removed.increment(1u, hr->capacity());
_g1->free_region(hr, _local_cleanup_list, true);
}
} else {
hr->rem_set()->do_cleanup_work(_hrrs_cleanup_task);
}
double region_time = (os::elapsedTime() - start);
_claimed_region_time += region_time;
if (region_time > _max_region_time) {
......@@ -1883,12 +1892,8 @@ public:
void work(uint worker_id) {
double start = os::elapsedTime();
FreeRegionList local_cleanup_list("Local Cleanup List");
OldRegionSet old_proxy_set("Local Cleanup Old Proxy Set");
HumongousRegionSet humongous_proxy_set("Local Cleanup Humongous Proxy Set");
HRRSCleanupTask hrrs_cleanup_task;
G1NoteEndOfConcMarkClosure g1_note_end(_g1h, worker_id, &local_cleanup_list,
&old_proxy_set,
&humongous_proxy_set,
&hrrs_cleanup_task);
if (G1CollectedHeap::use_parallel_gc_threads()) {
_g1h->heap_region_par_iterate_chunked(&g1_note_end, worker_id,
......@@ -1900,13 +1905,10 @@ public:
assert(g1_note_end.complete(), "Shouldn't have yielded!");
// Now update the lists
_g1h->update_sets_after_freeing_regions(g1_note_end.freed_bytes(),
NULL /* free_list */,
&old_proxy_set,
&humongous_proxy_set,
true /* par */);
_g1h->remove_from_old_sets(g1_note_end.old_regions_removed(), g1_note_end.humongous_regions_removed());
{
MutexLockerEx x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag);
_g1h->decrement_summary_bytes(g1_note_end.freed_bytes());
_max_live_bytes += g1_note_end.max_live_bytes();
_freed_bytes += g1_note_end.freed_bytes();
......@@ -1920,14 +1922,14 @@ public:
G1HRPrinter* hr_printer = _g1h->hr_printer();
if (hr_printer->is_active()) {
HeapRegionLinkedListIterator iter(&local_cleanup_list);
FreeRegionListIterator iter(&local_cleanup_list);
while (iter.more_available()) {
HeapRegion* hr = iter.get_next();
hr_printer->cleanup(hr);
}
}
_cleanup_list->add_as_tail(&local_cleanup_list);
_cleanup_list->add_ordered(&local_cleanup_list);
assert(local_cleanup_list.is_empty(), "post-condition");
HeapRegionRemSet::finish_cleanup_task(&hrrs_cleanup_task);
......@@ -1971,7 +1973,6 @@ void ConcurrentMark::cleanup() {
return;
}
HRSPhaseSetter x(HRSPhaseCleanup);
g1h->verify_region_sets_optional();
if (VerifyDuringGC) {
......@@ -2144,7 +2145,7 @@ void ConcurrentMark::completeCleanup() {
G1CollectedHeap* g1h = G1CollectedHeap::heap();
_cleanup_list.verify_optional();
_cleanup_list.verify_list();
FreeRegionList tmp_free_list("Tmp Free List");
if (G1ConcRegionFreeingVerbose) {
......@@ -2157,9 +2158,9 @@ void ConcurrentMark::completeCleanup() {
// so it's not necessary to take any locks
while (!_cleanup_list.is_empty()) {
HeapRegion* hr = _cleanup_list.remove_head();
assert(hr != NULL, "the list was not empty");
assert(hr != NULL, "Got NULL from a non-empty list");
hr->par_clear();
tmp_free_list.add_as_tail(hr);
tmp_free_list.add_ordered(hr);
// Instead of adding one region at a time to the secondary_free_list,
// we accumulate them in the local list and move them a few at a
......@@ -2179,7 +2180,7 @@ void ConcurrentMark::completeCleanup() {
{
MutexLockerEx x(SecondaryFreeList_lock, Mutex::_no_safepoint_check_flag);
g1h->secondary_free_list_add_as_tail(&tmp_free_list);
g1h->secondary_free_list_add(&tmp_free_list);
SecondaryFreeList_lock->notify_all();
}
......@@ -2528,6 +2529,11 @@ void ConcurrentMark::weakRefsWork(bool clear_all_soft_refs) {
assert(!rp->discovery_enabled(), "Post condition");
}
if (has_overflown()) {
// We can not trust g1_is_alive if the marking stack overflowed
return;
}
g1h->unlink_string_and_symbol_table(&g1_is_alive,
/* process_strings */ false, // currently strings are always roots
/* process_symbols */ true);
......
......@@ -25,7 +25,7 @@
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_CONCURRENTMARK_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_CONCURRENTMARK_HPP
#include "gc_implementation/g1/heapRegionSets.hpp"
#include "gc_implementation/g1/heapRegionSet.hpp"
#include "utilities/taskqueue.hpp"
class G1CollectedHeap;
......
......@@ -24,6 +24,14 @@
#include "precompiled.hpp"
#include "gc_implementation/g1/g1BiasedArray.hpp"
#include "memory/padded.inline.hpp"
// Allocate a new array, generic version.
address G1BiasedMappedArrayBase::create_new_base_array(size_t length, size_t elem_size) {
assert(length > 0, "just checking");
assert(elem_size > 0, "just checking");
return PaddedPrimitiveArray<u_char, mtGC>::create_unfreeable(length * elem_size);
}
#ifndef PRODUCT
void G1BiasedMappedArrayBase::verify_index(idx_t index) const {
......
......@@ -25,8 +25,8 @@
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1BIASEDARRAY_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_G1BIASEDARRAY_HPP
#include "memory/allocation.hpp"
#include "utilities/debug.hpp"
#include "memory/allocation.inline.hpp"
// Implements the common base functionality for arrays that contain provisions
// for accessing its elements using a biased index.
......@@ -48,11 +48,7 @@ protected:
_bias(0), _shift_by(0) { }
// Allocate a new array, generic version.
static address create_new_base_array(size_t length, size_t elem_size) {
assert(length > 0, "just checking");
assert(elem_size > 0, "just checking");
return NEW_C_HEAP_ARRAY(u_char, length * elem_size, mtGC);
}
static address create_new_base_array(size_t length, size_t elem_size);
// Initialize the members of this class. The biased start address of this array
// is the bias (in elements) multiplied by the element size.
......
/*
* Copyright (c) 2014, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "code/nmethod.hpp"
#include "gc_implementation/g1/g1CodeCacheRemSet.hpp"
#include "memory/iterator.hpp"
G1CodeRootChunk::G1CodeRootChunk() : _top(NULL), _next(NULL), _prev(NULL) {
_top = bottom();
}
void G1CodeRootChunk::reset() {
_next = _prev = NULL;
_top = bottom();
}
void G1CodeRootChunk::nmethods_do(CodeBlobClosure* cl) {
nmethod** cur = bottom();
while (cur != _top) {
cl->do_code_blob(*cur);
cur++;
}
}
FreeList<G1CodeRootChunk> G1CodeRootSet::_free_list;
size_t G1CodeRootSet::_num_chunks_handed_out = 0;
G1CodeRootChunk* G1CodeRootSet::new_chunk() {
G1CodeRootChunk* result = _free_list.get_chunk_at_head();
if (result == NULL) {
result = new G1CodeRootChunk();
}
G1CodeRootSet::_num_chunks_handed_out++;
result->reset();
return result;
}
void G1CodeRootSet::free_chunk(G1CodeRootChunk* chunk) {
_free_list.return_chunk_at_head(chunk);
G1CodeRootSet::_num_chunks_handed_out--;
}
void G1CodeRootSet::free_all_chunks(FreeList<G1CodeRootChunk>* list) {
G1CodeRootSet::_num_chunks_handed_out -= list->count();
_free_list.prepend(list);
}
void G1CodeRootSet::purge_chunks(size_t keep_ratio) {
size_t keep = G1CodeRootSet::_num_chunks_handed_out * keep_ratio / 100;
if (keep >= (size_t)_free_list.count()) {
return;
}
FreeList<G1CodeRootChunk> temp;
temp.initialize();
temp.set_size(G1CodeRootChunk::word_size());
_free_list.getFirstNChunksFromList((size_t)_free_list.count() - keep, &temp);
G1CodeRootChunk* cur = temp.get_chunk_at_head();
while (cur != NULL) {
delete cur;
cur = temp.get_chunk_at_head();
}
}
size_t G1CodeRootSet::static_mem_size() {
return sizeof(_free_list) + sizeof(_num_chunks_handed_out);
}
size_t G1CodeRootSet::fl_mem_size() {
return _free_list.count() * _free_list.size();
}
void G1CodeRootSet::initialize() {
_free_list.initialize();
_free_list.set_size(G1CodeRootChunk::word_size());
}
G1CodeRootSet::G1CodeRootSet() : _list(), _length(0) {
_list.initialize();
_list.set_size(G1CodeRootChunk::word_size());
}
G1CodeRootSet::~G1CodeRootSet() {
clear();
}
void G1CodeRootSet::add(nmethod* method) {
if (!contains(method)) {
// Try to add the nmethod. If there is not enough space, get a new chunk.
if (_list.head() == NULL || _list.head()->is_full()) {
G1CodeRootChunk* cur = new_chunk();
_list.return_chunk_at_head(cur);
}
bool result = _list.head()->add(method);
guarantee(result, err_msg("Not able to add nmethod "PTR_FORMAT" to newly allocated chunk.", method));
_length++;
}
}
void G1CodeRootSet::remove(nmethod* method) {
G1CodeRootChunk* found = find(method);
if (found != NULL) {
bool result = found->remove(method);
guarantee(result, err_msg("could not find nmethod "PTR_FORMAT" during removal although we previously found it", method));
// eventually free completely emptied chunk
if (found->is_empty()) {
_list.remove_chunk(found);
free(found);
}
_length--;
}
assert(!contains(method), err_msg(PTR_FORMAT" still contains nmethod "PTR_FORMAT, this, method));
}
nmethod* G1CodeRootSet::pop() {
do {
G1CodeRootChunk* cur = _list.head();
if (cur == NULL) {
assert(_length == 0, "when there are no chunks, there should be no elements");
return NULL;
}
nmethod* result = cur->pop();
if (result != NULL) {
_length--;
return result;
} else {
free(_list.get_chunk_at_head());
}
} while (true);
}
G1CodeRootChunk* G1CodeRootSet::find(nmethod* method) {
G1CodeRootChunk* cur = _list.head();
while (cur != NULL) {
if (cur->contains(method)) {
return cur;
}
cur = (G1CodeRootChunk*)cur->next();
}
return NULL;
}
void G1CodeRootSet::free(G1CodeRootChunk* chunk) {
free_chunk(chunk);
}
bool G1CodeRootSet::contains(nmethod* method) {
return find(method) != NULL;
}
void G1CodeRootSet::clear() {
free_all_chunks(&_list);
_length = 0;
}
void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const {
G1CodeRootChunk* cur = _list.head();
while (cur != NULL) {
cur->nmethods_do(blk);
cur = (G1CodeRootChunk*)cur->next();
}
}
size_t G1CodeRootSet::mem_size() {
return sizeof(this) + _list.count() * _list.size();
}
#ifndef PRODUCT
void G1CodeRootSet::test() {
initialize();
assert(_free_list.count() == 0, "Free List must be empty");
assert(_num_chunks_handed_out == 0, "No elements must have been handed out yet");
// The number of chunks that we allocate for purge testing.
size_t const num_chunks = 10;
{
G1CodeRootSet set1;
assert(set1.is_empty(), "Code root set must be initially empty but is not.");
set1.add((nmethod*)1);
assert(_num_chunks_handed_out == 1,
err_msg("Must have allocated and handed out one chunk, but handed out "
SIZE_FORMAT" chunks", _num_chunks_handed_out));
assert(set1.length() == 1, err_msg("Added exactly one element, but set contains "
SIZE_FORMAT" elements", set1.length()));
// G1CodeRootChunk::word_size() is larger than G1CodeRootChunk::num_entries which
// we cannot access.
for (uint i = 0; i < G1CodeRootChunk::word_size() + 1; i++) {
set1.add((nmethod*)1);
}
assert(_num_chunks_handed_out == 1,
err_msg("Duplicate detection must have prevented allocation of further "
"chunks but contains "SIZE_FORMAT, _num_chunks_handed_out));
assert(set1.length() == 1,
err_msg("Duplicate detection should not have increased the set size but "
"is "SIZE_FORMAT, set1.length()));
size_t num_total_after_add = G1CodeRootChunk::word_size() + 1;
for (size_t i = 0; i < num_total_after_add - 1; i++) {
set1.add((nmethod*)(2 + i));
}
assert(_num_chunks_handed_out > 1,
"After adding more code roots, more than one chunks should have been handed out");
assert(set1.length() == num_total_after_add,
err_msg("After adding in total "SIZE_FORMAT" distinct code roots, they "
"need to be in the set, but there are only "SIZE_FORMAT,
num_total_after_add, set1.length()));
size_t num_popped = 0;
while (set1.pop() != NULL) {
num_popped++;
}
assert(num_popped == num_total_after_add,
err_msg("Managed to pop "SIZE_FORMAT" code roots, but only "SIZE_FORMAT" "
"were added", num_popped, num_total_after_add));
assert(_num_chunks_handed_out == 0,
err_msg("After popping all elements, all chunks must have been returned "
"but are still "SIZE_FORMAT, _num_chunks_handed_out));
purge_chunks(0);
assert(_free_list.count() == 0,
err_msg("After purging everything, the free list must be empty but still "
"contains "SIZE_FORMAT" chunks", _free_list.count()));
// Add some more handed out chunks.
size_t i = 0;
while (_num_chunks_handed_out < num_chunks) {
set1.add((nmethod*)i);
i++;
}
{
// Generate chunks on the free list.
G1CodeRootSet set2;
size_t i = 0;
while (_num_chunks_handed_out < num_chunks * 2) {
set2.add((nmethod*)i);
i++;
}
// Exit of the scope of the set2 object will call the destructor that generates
// num_chunks elements on the free list.
}
assert(_num_chunks_handed_out == num_chunks,
err_msg("Deletion of the second set must have resulted in giving back "
"those, but there is still "SIZE_FORMAT" handed out, expecting "
SIZE_FORMAT, _num_chunks_handed_out, num_chunks));
assert((size_t)_free_list.count() == num_chunks,
err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list "
"but there are only "SIZE_FORMAT, num_chunks, _free_list.count()));
size_t const test_percentage = 50;
purge_chunks(test_percentage);
assert(_num_chunks_handed_out == num_chunks,
err_msg("Purging must not hand out chunks but there are "SIZE_FORMAT,
_num_chunks_handed_out));
assert((size_t)_free_list.count() == (ssize_t)(num_chunks * test_percentage / 100),
err_msg("Must have purged "SIZE_FORMAT" percent of "SIZE_FORMAT" chunks"
"but there are "SSIZE_FORMAT, test_percentage, num_chunks,
_free_list.count()));
// Purge the remainder of the chunks on the free list.
purge_chunks(0);
assert(_free_list.count() == 0, "Free List must be empty");
assert(_num_chunks_handed_out == num_chunks,
err_msg("Expected to be "SIZE_FORMAT" chunks handed out from the first set "
"but there are "SIZE_FORMAT, num_chunks, _num_chunks_handed_out));
// Exit of the scope of the set1 object will call the destructor that generates
// num_chunks additional elements on the free list.
}
assert(_num_chunks_handed_out == 0,
err_msg("Deletion of the only set must have resulted in no chunks handed "
"out, but there is still "SIZE_FORMAT" handed out", _num_chunks_handed_out));
assert((size_t)_free_list.count() == num_chunks,
err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list "
"but there are only "SSIZE_FORMAT, num_chunks, _free_list.count()));
// Restore initial state.
purge_chunks(0);
assert(_free_list.count() == 0, "Free List must be empty");
assert(_num_chunks_handed_out == 0, "No elements must have been handed out yet");
}
void TestCodeCacheRemSet_test() {
G1CodeRootSet::test();
}
#endif
/*
* Copyright (c) 2014, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1CODECACHEREMSET_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_G1CODECACHEREMSET_HPP
#include "memory/allocation.hpp"
#include "memory/freeList.hpp"
#include "runtime/globals.hpp"
class CodeBlobClosure;
class G1CodeRootChunk : public CHeapObj<mtGC> {
private:
static const int NUM_ENTRIES = 32;
public:
G1CodeRootChunk* _next;
G1CodeRootChunk* _prev;
nmethod** _top;
nmethod* _data[NUM_ENTRIES];
nmethod** bottom() const {
return (nmethod**) &(_data[0]);
}
nmethod** end() const {
return (nmethod**) &(_data[NUM_ENTRIES]);
}
public:
G1CodeRootChunk();
~G1CodeRootChunk() {}
static size_t word_size() { return (size_t)(align_size_up_(sizeof(G1CodeRootChunk), HeapWordSize) / HeapWordSize); }
// FreeList "interface" methods
G1CodeRootChunk* next() const { return _next; }
G1CodeRootChunk* prev() const { return _prev; }
void set_next(G1CodeRootChunk* v) { _next = v; assert(v != this, "Boom");}
void set_prev(G1CodeRootChunk* v) { _prev = v; assert(v != this, "Boom");}
void clear_next() { set_next(NULL); }
void clear_prev() { set_prev(NULL); }
size_t size() const { return word_size(); }
void link_next(G1CodeRootChunk* ptr) { set_next(ptr); }
void link_prev(G1CodeRootChunk* ptr) { set_prev(ptr); }
void link_after(G1CodeRootChunk* ptr) {
link_next(ptr);
if (ptr != NULL) ptr->link_prev((G1CodeRootChunk*)this);
}
bool is_free() { return true; }
// New G1CodeRootChunk routines
void reset();
bool is_empty() const {
return _top == bottom();
}
bool is_full() const {
return _top == (nmethod**)end();
}
bool contains(nmethod* method) {
nmethod** cur = bottom();
while (cur != _top) {
if (*cur == method) return true;
cur++;
}
return false;
}
bool add(nmethod* method) {
if (is_full()) return false;
*_top = method;
_top++;
return true;
}
bool remove(nmethod* method) {
nmethod** cur = bottom();
while (cur != _top) {
if (*cur == method) {
memmove(cur, cur + 1, (_top - (cur + 1)) * sizeof(nmethod**));
_top--;
return true;
}
cur++;
}
return false;
}
void nmethods_do(CodeBlobClosure* blk);
nmethod* pop() {
if (is_empty()) {
return NULL;
}
_top--;
return *_top;
}
};
// Implements storage for a set of code roots.
// All methods that modify the set are not thread-safe except if otherwise noted.
class G1CodeRootSet VALUE_OBJ_CLASS_SPEC {
private:
// Global free chunk list management
static FreeList<G1CodeRootChunk> _free_list;
// Total number of chunks handed out
static size_t _num_chunks_handed_out;
static G1CodeRootChunk* new_chunk();
static void free_chunk(G1CodeRootChunk* chunk);
// Free all elements of the given list.
static void free_all_chunks(FreeList<G1CodeRootChunk>* list);
// Return the chunk that contains the given nmethod, NULL otherwise.
// Scans the list of chunks backwards, as this method is used to add new
// entries, which are typically added in bulk for a single nmethod.
G1CodeRootChunk* find(nmethod* method);
void free(G1CodeRootChunk* chunk);
size_t _length;
FreeList<G1CodeRootChunk> _list;
public:
G1CodeRootSet();
~G1CodeRootSet();
static void initialize();
static void purge_chunks(size_t keep_ratio);
static size_t static_mem_size();
static size_t fl_mem_size();
// Search for the code blob from the recently allocated ones to find duplicates more quickly, as this
// method is likely to be repeatedly called with the same nmethod.
void add(nmethod* method);
void remove(nmethod* method);
nmethod* pop();
bool contains(nmethod* method);
void clear();
void nmethods_do(CodeBlobClosure* blk) const;
bool is_empty() { return length() == 0; }
// Length in elements
size_t length() const { return _length; }
// Memory size in bytes taken by this set.
size_t mem_size();
static void test() PRODUCT_RETURN;
};
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1CODECACHEREMSET_HPP
/*
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2014, 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
......@@ -34,7 +34,7 @@
#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp"
#include "gc_implementation/g1/g1YCTypes.hpp"
#include "gc_implementation/g1/heapRegionSeq.hpp"
#include "gc_implementation/g1/heapRegionSets.hpp"
#include "gc_implementation/g1/heapRegionSet.hpp"
#include "gc_implementation/shared/hSpaceCounters.hpp"
#include "gc_implementation/shared/parGCAllocBuffer.hpp"
#include "memory/barrierSet.hpp"
......@@ -243,18 +243,18 @@ private:
MemRegion _g1_committed;
// The master free list. It will satisfy all new region allocations.
MasterFreeRegionList _free_list;
FreeRegionList _free_list;
// The secondary free list which contains regions that have been
// freed up during the cleanup process. This will be appended to the
// master free list when appropriate.
SecondaryFreeRegionList _secondary_free_list;
FreeRegionList _secondary_free_list;
// It keeps track of the old regions.
MasterOldRegionSet _old_set;
HeapRegionSet _old_set;
// It keeps track of the humongous regions.
MasterHumongousRegionSet _humongous_set;
HeapRegionSet _humongous_set;
// The number of regions we could create by expansion.
uint _expansion_regions;
......@@ -497,13 +497,14 @@ protected:
// check whether there's anything available on the
// secondary_free_list and/or wait for more regions to appear on
// that list, if _free_regions_coming is set.
HeapRegion* new_region_try_secondary_free_list();
HeapRegion* new_region_try_secondary_free_list(bool is_old);
// Try to allocate a single non-humongous HeapRegion sufficient for
// an allocation of the given word_size. If do_expand is true,
// attempt to expand the heap if necessary to satisfy the allocation
// request.
HeapRegion* new_region(size_t word_size, bool do_expand);
// request. If the region is to be used as an old region or for a
// humongous object, set is_old to true. If not, to false.
HeapRegion* new_region(size_t word_size, bool is_old, bool do_expand);
// Attempt to satisfy a humongous allocation request of the given
// size by finding a contiguous set of free regions of num_regions
......@@ -757,6 +758,29 @@ public:
G1HRPrinter* hr_printer() { return &_hr_printer; }
// Frees a non-humongous region by initializing its contents and
// adding it to the free list that's passed as a parameter (this is
// usually a local list which will be appended to the master free
// list later). The used bytes of freed regions are accumulated in
// pre_used. If par is true, the region's RSet will not be freed
// up. The assumption is that this will be done later.
// The locked parameter indicates if the caller has already taken
// care of proper synchronization. This may allow some optimizations.
void free_region(HeapRegion* hr,
FreeRegionList* free_list,
bool par,
bool locked = false);
// Frees a humongous region by collapsing it into individual regions
// and calling free_region() for each of them. The freed regions
// will be added to the free list that's passed as a parameter (this
// is usually a local list which will be appended to the master free
// list later). The used bytes of freed regions are accumulated in
// pre_used. If par is true, the region's RSet will not be freed
// up. The assumption is that this will be done later.
void free_humongous_region(HeapRegion* hr,
FreeRegionList* free_list,
bool par);
protected:
// Shrink the garbage-first heap by at most the given size (in bytes!).
......@@ -835,30 +859,6 @@ protected:
G1KlassScanClosure* scan_klasses,
int worker_i);
// Frees a non-humongous region by initializing its contents and
// adding it to the free list that's passed as a parameter (this is
// usually a local list which will be appended to the master free
// list later). The used bytes of freed regions are accumulated in
// pre_used. If par is true, the region's RSet will not be freed
// up. The assumption is that this will be done later.
void free_region(HeapRegion* hr,
size_t* pre_used,
FreeRegionList* free_list,
bool par);
// Frees a humongous region by collapsing it into individual regions
// and calling free_region() for each of them. The freed regions
// will be added to the free list that's passed as a parameter (this
// is usually a local list which will be appended to the master free
// list later). The used bytes of freed regions are accumulated in
// pre_used. If par is true, the region's RSet will not be freed
// up. The assumption is that this will be done later.
void free_humongous_region(HeapRegion* hr,
size_t* pre_used,
FreeRegionList* free_list,
HumongousRegionSet* humongous_proxy_set,
bool par);
// Notifies all the necessary spaces that the committed space has
// been updated (either expanded or shrunk). It should be called
// after _g1_storage is updated.
......@@ -1228,21 +1228,17 @@ public:
bool is_on_master_free_list(HeapRegion* hr) {
return hr->containing_set() == &_free_list;
}
bool is_in_humongous_set(HeapRegion* hr) {
return hr->containing_set() == &_humongous_set;
}
#endif // ASSERT
// Wrapper for the region list operations that can be called from
// methods outside this class.
void secondary_free_list_add_as_tail(FreeRegionList* list) {
_secondary_free_list.add_as_tail(list);
void secondary_free_list_add(FreeRegionList* list) {
_secondary_free_list.add_ordered(list);
}
void append_secondary_free_list() {
_free_list.add_as_head(&_secondary_free_list);
_free_list.add_ordered(&_secondary_free_list);
}
void append_secondary_free_list_if_not_empty_with_lock() {
......@@ -1284,27 +1280,9 @@ public:
// True iff an evacuation has failed in the most-recent collection.
bool evacuation_failed() { return _evacuation_failed; }
// It will free a region if it has allocated objects in it that are
// all dead. It calls either free_region() or
// free_humongous_region() depending on the type of the region that
// is passed to it.
void free_region_if_empty(HeapRegion* hr,
size_t* pre_used,
FreeRegionList* free_list,
OldRegionSet* old_proxy_set,
HumongousRegionSet* humongous_proxy_set,
HRRSCleanupTask* hrrs_cleanup_task,
bool par);
// It appends the free list to the master free list and updates the
// master humongous list according to the contents of the proxy
// list. It also adjusts the total used bytes according to pre_used
// (if par is true, it will do so by taking the ParGCRareEvent_lock).
void update_sets_after_freeing_regions(size_t pre_used,
FreeRegionList* free_list,
OldRegionSet* old_proxy_set,
HumongousRegionSet* humongous_proxy_set,
bool par);
void remove_from_old_sets(const HeapRegionSetCount& old_regions_removed, const HeapRegionSetCount& humongous_regions_removed);
void prepend_to_freelist(FreeRegionList* list);
void decrement_summary_bytes(size_t bytes);
// Returns "TRUE" iff "p" points into the committed areas of the heap.
virtual bool is_in(const void* p) const;
......@@ -1659,6 +1637,9 @@ public:
// that were not successfully evacuated are not migrated.
void migrate_strong_code_roots();
// Free up superfluous code root memory.
void purge_code_root_memory();
// During an initial mark pause, mark all the code roots that
// point into regions *not* in the collection set.
void mark_strong_code_roots(uint worker_id);
......@@ -1671,6 +1652,8 @@ public:
// in symbol table, possibly in parallel.
void unlink_string_and_symbol_table(BoolObjectClosure* is_alive, bool unlink_strings = true, bool unlink_symbols = true);
// Redirty logged cards in the refinement queue.
void redirty_logged_cards();
// Verification
// The following is just to alert the verification code
......@@ -1797,8 +1780,6 @@ protected:
size_t _undo_waste;
OopsInHeapRegionClosure* _evac_failure_cl;
G1ParScanHeapEvacClosure* _evac_cl;
G1ParScanPartialArrayClosure* _partial_scan_cl;
int _hash_seed;
uint _queue_num;
......@@ -1926,14 +1907,6 @@ public:
return _evac_failure_cl;
}
void set_evac_closure(G1ParScanHeapEvacClosure* evac_cl) {
_evac_cl = evac_cl;
}
void set_partial_scan_closure(G1ParScanPartialArrayClosure* partial_scan_cl) {
_partial_scan_cl = partial_scan_cl;
}
int* hash_seed() { return &_hash_seed; }
uint queue_num() { return _queue_num; }
......@@ -1981,19 +1954,121 @@ public:
false /* retain */);
}
}
private:
#define G1_PARTIAL_ARRAY_MASK 0x2
inline bool has_partial_array_mask(oop* ref) const {
return ((uintptr_t)ref & G1_PARTIAL_ARRAY_MASK) == G1_PARTIAL_ARRAY_MASK;
}
// We never encode partial array oops as narrowOop*, so return false immediately.
// This allows the compiler to create optimized code when popping references from
// the work queue.
inline bool has_partial_array_mask(narrowOop* ref) const {
assert(((uintptr_t)ref & G1_PARTIAL_ARRAY_MASK) != G1_PARTIAL_ARRAY_MASK, "Partial array oop reference encoded as narrowOop*");
return false;
}
// Only implement set_partial_array_mask() for regular oops, not for narrowOops.
// We always encode partial arrays as regular oop, to allow the
// specialization for has_partial_array_mask() for narrowOops above.
// This means that unintentional use of this method with narrowOops are caught
// by the compiler.
inline oop* set_partial_array_mask(oop obj) const {
assert(((uintptr_t)(void *)obj & G1_PARTIAL_ARRAY_MASK) == 0, "Information loss!");
return (oop*) ((uintptr_t)(void *)obj | G1_PARTIAL_ARRAY_MASK);
}
inline oop clear_partial_array_mask(oop* ref) const {
return cast_to_oop((intptr_t)ref & ~G1_PARTIAL_ARRAY_MASK);
}
void do_oop_partial_array(oop* p) {
assert(has_partial_array_mask(p), "invariant");
oop from_obj = clear_partial_array_mask(p);
assert(Universe::heap()->is_in_reserved(from_obj), "must be in heap.");
assert(from_obj->is_objArray(), "must be obj array");
objArrayOop from_obj_array = objArrayOop(from_obj);
// The from-space object contains the real length.
int length = from_obj_array->length();
assert(from_obj->is_forwarded(), "must be forwarded");
oop to_obj = from_obj->forwardee();
assert(from_obj != to_obj, "should not be chunking self-forwarded objects");
objArrayOop to_obj_array = objArrayOop(to_obj);
// We keep track of the next start index in the length field of the
// to-space object.
int next_index = to_obj_array->length();
assert(0 <= next_index && next_index < length,
err_msg("invariant, next index: %d, length: %d", next_index, length));
int start = next_index;
int end = length;
int remainder = end - start;
// We'll try not to push a range that's smaller than ParGCArrayScanChunk.
if (remainder > 2 * ParGCArrayScanChunk) {
end = start + ParGCArrayScanChunk;
to_obj_array->set_length(end);
// Push the remainder before we process the range in case another
// worker has run out of things to do and can steal it.
oop* from_obj_p = set_partial_array_mask(from_obj);
push_on_queue(from_obj_p);
} else {
assert(length == end, "sanity");
// We'll process the final range for this object. Restore the length
// so that the heap remains parsable in case of evacuation failure.
to_obj_array->set_length(end);
}
_scanner.set_region(_g1h->heap_region_containing_raw(to_obj));
// Process indexes [start,end). It will also process the header
// along with the first chunk (i.e., the chunk with start == 0).
// Note that at this point the length field of to_obj_array is not
// correct given that we are using it to keep track of the next
// start index. oop_iterate_range() (thankfully!) ignores the length
// field and only relies on the start / end parameters. It does
// however return the size of the object which will be incorrect. So
// we have to ignore it even if we wanted to use it.
to_obj_array->oop_iterate_range(&_scanner, start, end);
}
// This method is applied to the fields of the objects that have just been copied.
template <class T> void do_oop_evac(T* p, HeapRegion* from) {
assert(!oopDesc::is_null(oopDesc::load_decode_heap_oop(p)),
"Reference should not be NULL here as such are never pushed to the task queue.");
oop obj = oopDesc::load_decode_heap_oop_not_null(p);
// Although we never intentionally push references outside of the collection
// set, due to (benign) races in the claim mechanism during RSet scanning more
// than one thread might claim the same card. So the same card may be
// processed multiple times. So redo this check.
if (_g1h->in_cset_fast_test(obj)) {
oop forwardee;
if (obj->is_forwarded()) {
forwardee = obj->forwardee();
} else {
forwardee = copy_to_survivor_space(obj);
}
assert(forwardee != NULL, "forwardee should not be NULL");
oopDesc::encode_store_heap_oop(p, forwardee);
}
assert(obj != NULL, "Must be");
update_rs(from, p, queue_num());
}
public:
oop copy_to_survivor_space(oop const obj);
template <class T> void deal_with_reference(T* ref_to_scan) {
if (has_partial_array_mask(ref_to_scan)) {
_partial_scan_cl->do_oop_nv(ref_to_scan);
} else {
if (!has_partial_array_mask(ref_to_scan)) {
// Note: we can use "raw" versions of "region_containing" because
// "obj_to_scan" is definitely in the heap, and is not in a
// humongous region.
HeapRegion* r = _g1h->heap_region_containing_raw(ref_to_scan);
_evac_cl->set_region(r);
_evac_cl->do_oop_nv(ref_to_scan);
do_oop_evac(ref_to_scan, r);
} else {
do_oop_partial_array((oop*)ref_to_scan);
}
}
......
......@@ -30,6 +30,7 @@
#include "gc_implementation/g1/g1AllocRegion.inline.hpp"
#include "gc_implementation/g1/g1CollectorPolicy.hpp"
#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp"
#include "gc_implementation/g1/heapRegionSet.inline.hpp"
#include "gc_implementation/g1/heapRegionSeq.inline.hpp"
#include "utilities/taskqueue.hpp"
......
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2014 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
......@@ -27,6 +27,7 @@
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
#include "gc_implementation/g1/g1GCPhaseTimes.hpp"
#include "gc_implementation/g1/g1Log.hpp"
#include "gc_implementation/g1/g1StringDedup.hpp"
// Helper class for avoiding interleaved logging
class LineBuffer: public StackObj {
......@@ -168,7 +169,9 @@ G1GCPhaseTimes::G1GCPhaseTimes(uint max_gc_threads) :
_last_termination_attempts(_max_gc_threads, SIZE_FORMAT),
_last_gc_worker_end_times_ms(_max_gc_threads, "%.1lf", false),
_last_gc_worker_times_ms(_max_gc_threads, "%.1lf"),
_last_gc_worker_other_times_ms(_max_gc_threads, "%.1lf")
_last_gc_worker_other_times_ms(_max_gc_threads, "%.1lf"),
_cur_string_dedup_queue_fixup_worker_times_ms(_max_gc_threads, "%.1lf"),
_cur_string_dedup_table_fixup_worker_times_ms(_max_gc_threads, "%.1lf")
{
assert(max_gc_threads > 0, "Must have some GC threads");
}
......@@ -229,6 +232,16 @@ void G1GCPhaseTimes::note_gc_end() {
_last_gc_worker_other_times_ms.verify();
}
void G1GCPhaseTimes::note_string_dedup_fixup_start() {
_cur_string_dedup_queue_fixup_worker_times_ms.reset();
_cur_string_dedup_table_fixup_worker_times_ms.reset();
}
void G1GCPhaseTimes::note_string_dedup_fixup_end() {
_cur_string_dedup_queue_fixup_worker_times_ms.verify();
_cur_string_dedup_table_fixup_worker_times_ms.verify();
}
void G1GCPhaseTimes::print_stats(int level, const char* str, double value) {
LineBuffer(level).append_and_print_cr("[%s: %.1lf ms]", str, value);
}
......@@ -250,6 +263,14 @@ double G1GCPhaseTimes::accounted_time_ms() {
// Strong code root migration time
misc_time_ms += _cur_strong_code_root_migration_time_ms;
// Strong code root purge time
misc_time_ms += _cur_strong_code_root_purge_time_ms;
if (G1StringDedup::is_enabled()) {
// String dedup fixup time
misc_time_ms += _cur_string_dedup_fixup_time_ms;
}
// Subtract the time taken to clean the card table from the
// current value of "other time"
misc_time_ms += _cur_clear_ct_time_ms;
......@@ -299,20 +320,43 @@ void G1GCPhaseTimes::print(double pause_time_sec) {
}
print_stats(1, "Code Root Fixup", _cur_collection_code_root_fixup_time_ms);
print_stats(1, "Code Root Migration", _cur_strong_code_root_migration_time_ms);
print_stats(1, "Code Root Purge", _cur_strong_code_root_purge_time_ms);
if (G1StringDedup::is_enabled()) {
print_stats(1, "String Dedup Fixup", _cur_string_dedup_fixup_time_ms, _active_gc_threads);
_cur_string_dedup_queue_fixup_worker_times_ms.print(2, "Queue Fixup (ms)");
_cur_string_dedup_table_fixup_worker_times_ms.print(2, "Table Fixup (ms)");
}
print_stats(1, "Clear CT", _cur_clear_ct_time_ms);
double misc_time_ms = pause_time_sec * MILLIUNITS - accounted_time_ms();
print_stats(1, "Other", misc_time_ms);
if (_cur_verify_before_time_ms > 0.0) {
print_stats(2, "Verify Before", _cur_verify_before_time_ms);
}
if (G1CollectedHeap::heap()->evacuation_failed()) {
double evac_fail_handling = _cur_evac_fail_recalc_used + _cur_evac_fail_remove_self_forwards +
_cur_evac_fail_restore_remsets;
print_stats(2, "Evacuation Failure", evac_fail_handling);
if (G1Log::finest()) {
print_stats(3, "Recalculate Used", _cur_evac_fail_recalc_used);
print_stats(3, "Remove Self Forwards", _cur_evac_fail_remove_self_forwards);
print_stats(3, "Restore RemSet", _cur_evac_fail_restore_remsets);
}
}
print_stats(2, "Choose CSet",
(_recorded_young_cset_choice_time_ms +
_recorded_non_young_cset_choice_time_ms));
print_stats(2, "Ref Proc", _cur_ref_proc_time_ms);
print_stats(2, "Ref Enq", _cur_ref_enq_time_ms);
if (G1DeferredRSUpdate) {
print_stats(2, "Redirty Cards", _recorded_redirty_logged_cards_time_ms);
}
print_stats(2, "Free CSet",
(_recorded_young_free_cset_time_ms +
_recorded_non_young_free_cset_time_ms));
if (G1Log::finest()) {
print_stats(3, "Young Free CSet", _recorded_young_free_cset_time_ms);
print_stats(3, "Non-Young Free CSet", _recorded_non_young_free_cset_time_ms);
}
if (_cur_verify_after_time_ms > 0.0) {
print_stats(2, "Verify After", _cur_verify_after_time_ms);
}
......
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2014 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
......@@ -131,6 +131,15 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
double _cur_collection_par_time_ms;
double _cur_collection_code_root_fixup_time_ms;
double _cur_strong_code_root_migration_time_ms;
double _cur_strong_code_root_purge_time_ms;
double _cur_evac_fail_recalc_used;
double _cur_evac_fail_restore_remsets;
double _cur_evac_fail_remove_self_forwards;
double _cur_string_dedup_fixup_time_ms;
WorkerDataArray<double> _cur_string_dedup_queue_fixup_worker_times_ms;
WorkerDataArray<double> _cur_string_dedup_table_fixup_worker_times_ms;
double _cur_clear_ct_time_ms;
double _cur_ref_proc_time_ms;
......@@ -142,6 +151,8 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
double _recorded_young_cset_choice_time_ms;
double _recorded_non_young_cset_choice_time_ms;
double _recorded_redirty_logged_cards_time_ms;
double _recorded_young_free_cset_time_ms;
double _recorded_non_young_free_cset_time_ms;
......@@ -223,6 +234,37 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
_cur_strong_code_root_migration_time_ms = ms;
}
void record_strong_code_root_purge_time(double ms) {
_cur_strong_code_root_purge_time_ms = ms;
}
void record_evac_fail_recalc_used_time(double ms) {
_cur_evac_fail_recalc_used = ms;
}
void record_evac_fail_restore_remsets(double ms) {
_cur_evac_fail_restore_remsets = ms;
}
void record_evac_fail_remove_self_forwards(double ms) {
_cur_evac_fail_remove_self_forwards = ms;
}
void note_string_dedup_fixup_start();
void note_string_dedup_fixup_end();
void record_string_dedup_fixup_time(double ms) {
_cur_string_dedup_fixup_time_ms = ms;
}
void record_string_dedup_queue_fixup_worker_time(uint worker_id, double ms) {
_cur_string_dedup_queue_fixup_worker_times_ms.set(worker_id, ms);
}
void record_string_dedup_table_fixup_worker_time(uint worker_id, double ms) {
_cur_string_dedup_table_fixup_worker_times_ms.set(worker_id, ms);
}
void record_ref_proc_time(double ms) {
_cur_ref_proc_time_ms = ms;
}
......@@ -251,6 +293,10 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
_recorded_non_young_cset_choice_time_ms = time_ms;
}
void record_redirty_logged_cards_time_ms(double time_ms) {
_recorded_redirty_logged_cards_time_ms = time_ms;
}
void record_cur_collection_start_sec(double time_ms) {
_cur_collection_start_sec = time_ms;
}
......
/*
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2014, 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
......@@ -31,6 +31,7 @@
#include "code/icBuffer.hpp"
#include "gc_implementation/g1/g1Log.hpp"
#include "gc_implementation/g1/g1MarkSweep.hpp"
#include "gc_implementation/g1/g1StringDedup.hpp"
#include "gc_implementation/shared/gcHeapSummary.hpp"
#include "gc_implementation/shared/gcTimer.hpp"
#include "gc_implementation/shared/gcTrace.hpp"
......@@ -194,17 +195,19 @@ class G1PrepareCompactClosure: public HeapRegionClosure {
G1CollectedHeap* _g1h;
ModRefBarrierSet* _mrbs;
CompactPoint _cp;
HumongousRegionSet _humongous_proxy_set;
HeapRegionSetCount _humongous_regions_removed;
void free_humongous_region(HeapRegion* hr) {
HeapWord* end = hr->end();
size_t dummy_pre_used;
FreeRegionList dummy_free_list("Dummy Free List for G1MarkSweep");
assert(hr->startsHumongous(),
"Only the start of a humongous region should be freed.");
_g1h->free_humongous_region(hr, &dummy_pre_used, &dummy_free_list,
&_humongous_proxy_set, false /* par */);
hr->set_containing_set(NULL);
_humongous_regions_removed.increment(1u, hr->capacity());
_g1h->free_humongous_region(hr, &dummy_free_list, false /* par */);
hr->prepare_for_compaction(&_cp);
// Also clear the part of the card table that will be unused after
// compaction.
......@@ -217,16 +220,13 @@ public:
: _g1h(G1CollectedHeap::heap()),
_mrbs(_g1h->g1_barrier_set()),
_cp(NULL, cs, cs->initialize_threshold()),
_humongous_proxy_set("G1MarkSweep Humongous Proxy Set") { }
_humongous_regions_removed() { }
void update_sets() {
// We'll recalculate total used bytes and recreate the free list
// at the end of the GC, so no point in updating those values here.
_g1h->update_sets_after_freeing_regions(0, /* pre_used */
NULL, /* free_list */
NULL, /* old_proxy_set */
&_humongous_proxy_set,
false /* par */);
HeapRegionSetCount empty_set;
_g1h->remove_from_old_sets(empty_set, _humongous_regions_removed);
}
bool doHeapRegion(HeapRegion* hr) {
......@@ -317,6 +317,10 @@ void G1MarkSweep::mark_sweep_phase3() {
// have been cleared if they pointed to non-surviving objects.)
sh->process_weak_roots(&GenMarkSweep::adjust_pointer_closure);
if (G1StringDedup::is_enabled()) {
G1StringDedup::oops_do(&GenMarkSweep::adjust_pointer_closure);
}
GenMarkSweep::adjust_marks();
G1AdjustPointersClosure blk;
......
......@@ -80,53 +80,6 @@ public:
virtual void do_oop(narrowOop* p) { do_oop_nv(p); }
};
#define G1_PARTIAL_ARRAY_MASK 0x2
inline bool has_partial_array_mask(oop* ref) {
return ((uintptr_t)ref & G1_PARTIAL_ARRAY_MASK) == G1_PARTIAL_ARRAY_MASK;
}
// We never encode partial array oops as narrowOop*, so return false immediately.
// This allows the compiler to create optimized code when popping references from
// the work queue.
inline bool has_partial_array_mask(narrowOop* ref) {
assert(((uintptr_t)ref & G1_PARTIAL_ARRAY_MASK) != G1_PARTIAL_ARRAY_MASK, "Partial array oop reference encoded as narrowOop*");
return false;
}
// Only implement set_partial_array_mask() for regular oops, not for narrowOops.
// We always encode partial arrays as regular oop, to allow the
// specialization for has_partial_array_mask() for narrowOops above.
// This means that unintentional use of this method with narrowOops are caught
// by the compiler.
inline oop* set_partial_array_mask(oop obj) {
assert(((uintptr_t)(void *)obj & G1_PARTIAL_ARRAY_MASK) == 0, "Information loss!");
return (oop*) ((uintptr_t)(void *)obj | G1_PARTIAL_ARRAY_MASK);
}
template <class T> inline oop clear_partial_array_mask(T* ref) {
return cast_to_oop((intptr_t)ref & ~G1_PARTIAL_ARRAY_MASK);
}
class G1ParScanPartialArrayClosure : public G1ParClosureSuper {
G1ParScanClosure _scanner;
public:
G1ParScanPartialArrayClosure(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state, ReferenceProcessor* rp) :
G1ParClosureSuper(g1, par_scan_state), _scanner(g1, par_scan_state, rp)
{
assert(_ref_processor == NULL, "sanity");
}
G1ParScanClosure* scanner() {
return &_scanner;
}
template <class T> void do_oop_nv(T* p);
virtual void do_oop(oop* p) { do_oop_nv(p); }
virtual void do_oop(narrowOop* p) { do_oop_nv(p); }
};
// Add back base class for metadata
class G1ParCopyHelper : public G1ParClosureSuper {
protected:
......@@ -173,15 +126,8 @@ typedef G1ParCopyClosure<G1BarrierKlass, false> G1ParScanMetadataClosure;
typedef G1ParCopyClosure<G1BarrierNone, true> G1ParScanAndMarkExtRootClosure;
typedef G1ParCopyClosure<G1BarrierKlass, true> G1ParScanAndMarkMetadataClosure;
// The following closure type is defined in g1_specialized_oop_closures.hpp:
//
// typedef G1ParCopyClosure<G1BarrierEvac, false> G1ParScanHeapEvacClosure;
// We use a separate closure to handle references during evacuation
// failure processing.
// We could have used another instance of G1ParScanHeapEvacClosure
// (since that closure no longer assumes that the references it
// handles point into the collection set).
typedef G1ParCopyClosure<G1BarrierEvac, false> G1ParScanHeapEvacFailureClosure;
......
......@@ -462,8 +462,9 @@ void G1RemSet::cleanup_after_oops_into_collection_set_do() {
int into_cset_n_buffers = into_cset_dcqs.completed_buffers_num();
if (_g1->evacuation_failed()) {
// Restore remembered sets for the regions pointing into the collection set.
double restore_remembered_set_start = os::elapsedTime();
// Restore remembered sets for the regions pointing into the collection set.
if (G1DeferredRSUpdate) {
// If deferred RS updates are enabled then we just need to transfer
// the completed buffers from (a) the DirtyCardQueueSet used to hold
......@@ -482,6 +483,8 @@ void G1RemSet::cleanup_after_oops_into_collection_set_do() {
}
assert(n_completed_buffers == into_cset_n_buffers, "missed some buffers");
}
_g1->g1_policy()->phase_times()->record_evac_fail_restore_remsets((os::elapsedTime() - restore_remembered_set_start) * 1000.0);
}
// Free any completed buffers in the DirtyCardQueueSet used to hold cards
......
/*
* Copyright (c) 2014, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "classfile/javaClasses.hpp"
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
#include "gc_implementation/g1/g1GCPhaseTimes.hpp"
#include "gc_implementation/g1/g1StringDedup.hpp"
#include "gc_implementation/g1/g1StringDedupQueue.hpp"
#include "gc_implementation/g1/g1StringDedupStat.hpp"
#include "gc_implementation/g1/g1StringDedupTable.hpp"
#include "gc_implementation/g1/g1StringDedupThread.hpp"
bool G1StringDedup::_enabled = false;
void G1StringDedup::initialize() {
assert(UseG1GC, "String deduplication only available with G1");
if (UseStringDeduplication) {
_enabled = true;
G1StringDedupQueue::create();
G1StringDedupTable::create();
G1StringDedupThread::create();
}
}
bool G1StringDedup::is_candidate_from_mark(oop obj) {
if (java_lang_String::is_instance(obj)) {
bool from_young = G1CollectedHeap::heap()->heap_region_containing_raw(obj)->is_young();
if (from_young && obj->age() < StringDeduplicationAgeThreshold) {
// Candidate found. String is being evacuated from young to old but has not
// reached the deduplication age threshold, i.e. has not previously been a
// candidate during its life in the young generation.
return true;
}
}
// Not a candidate
return false;
}
void G1StringDedup::enqueue_from_mark(oop java_string) {
assert(is_enabled(), "String deduplication not enabled");
if (is_candidate_from_mark(java_string)) {
G1StringDedupQueue::push(0 /* worker_id */, java_string);
}
}
bool G1StringDedup::is_candidate_from_evacuation(bool from_young, bool to_young, oop obj) {
if (from_young && java_lang_String::is_instance(obj)) {
if (to_young && obj->age() == StringDeduplicationAgeThreshold) {
// Candidate found. String is being evacuated from young to young and just
// reached the deduplication age threshold.
return true;
}
if (!to_young && obj->age() < StringDeduplicationAgeThreshold) {
// Candidate found. String is being evacuated from young to old but has not
// reached the deduplication age threshold, i.e. has not previously been a
// candidate during its life in the young generation.
return true;
}
}
// Not a candidate
return false;
}
void G1StringDedup::enqueue_from_evacuation(bool from_young, bool to_young, uint worker_id, oop java_string) {
assert(is_enabled(), "String deduplication not enabled");
if (is_candidate_from_evacuation(from_young, to_young, java_string)) {
G1StringDedupQueue::push(worker_id, java_string);
}
}
void G1StringDedup::deduplicate(oop java_string) {
assert(is_enabled(), "String deduplication not enabled");
G1StringDedupStat dummy; // Statistics from this path is never used
G1StringDedupTable::deduplicate(java_string, dummy);
}
void G1StringDedup::oops_do(OopClosure* keep_alive) {
assert(is_enabled(), "String deduplication not enabled");
unlink_or_oops_do(NULL, keep_alive);
}
void G1StringDedup::unlink(BoolObjectClosure* is_alive) {
assert(is_enabled(), "String deduplication not enabled");
// Don't allow a potential resize or rehash during unlink, as the unlink
// operation itself might remove enough entries to invalidate such a decision.
unlink_or_oops_do(is_alive, NULL, false /* allow_resize_and_rehash */);
}
//
// Task for parallel unlink_or_oops_do() operation on the deduplication queue
// and table.
//
class G1StringDedupUnlinkOrOopsDoTask : public AbstractGangTask {
private:
G1StringDedupUnlinkOrOopsDoClosure _cl;
public:
G1StringDedupUnlinkOrOopsDoTask(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
bool allow_resize_and_rehash) :
AbstractGangTask("G1StringDedupUnlinkOrOopsDoTask"),
_cl(is_alive, keep_alive, allow_resize_and_rehash) {
}
virtual void work(uint worker_id) {
double queue_fixup_start = os::elapsedTime();
G1StringDedupQueue::unlink_or_oops_do(&_cl);
double table_fixup_start = os::elapsedTime();
G1StringDedupTable::unlink_or_oops_do(&_cl, worker_id);
double queue_fixup_time_ms = (table_fixup_start - queue_fixup_start) * 1000.0;
double table_fixup_time_ms = (os::elapsedTime() - table_fixup_start) * 1000.0;
G1CollectorPolicy* g1p = G1CollectedHeap::heap()->g1_policy();
g1p->phase_times()->record_string_dedup_queue_fixup_worker_time(worker_id, queue_fixup_time_ms);
g1p->phase_times()->record_string_dedup_table_fixup_worker_time(worker_id, table_fixup_time_ms);
}
};
void G1StringDedup::unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, bool allow_resize_and_rehash) {
assert(is_enabled(), "String deduplication not enabled");
G1CollectorPolicy* g1p = G1CollectedHeap::heap()->g1_policy();
g1p->phase_times()->note_string_dedup_fixup_start();
double fixup_start = os::elapsedTime();
G1StringDedupUnlinkOrOopsDoTask task(is_alive, keep_alive, allow_resize_and_rehash);
if (G1CollectedHeap::use_parallel_gc_threads()) {
G1CollectedHeap* g1h = G1CollectedHeap::heap();
g1h->set_par_threads();
g1h->workers()->run_task(&task);
g1h->set_par_threads(0);
} else {
task.work(0);
}
double fixup_time_ms = (os::elapsedTime() - fixup_start) * 1000.0;
g1p->phase_times()->record_string_dedup_fixup_time(fixup_time_ms);
g1p->phase_times()->note_string_dedup_fixup_end();
}
void G1StringDedup::threads_do(ThreadClosure* tc) {
assert(is_enabled(), "String deduplication not enabled");
tc->do_thread(G1StringDedupThread::thread());
}
void G1StringDedup::print_worker_threads_on(outputStream* st) {
assert(is_enabled(), "String deduplication not enabled");
G1StringDedupThread::thread()->print_on(st);
st->cr();
}
void G1StringDedup::verify() {
assert(is_enabled(), "String deduplication not enabled");
G1StringDedupQueue::verify();
G1StringDedupTable::verify();
}
G1StringDedupUnlinkOrOopsDoClosure::G1StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
bool allow_resize_and_rehash) :
_is_alive(is_alive),
_keep_alive(keep_alive),
_resized_table(NULL),
_rehashed_table(NULL),
_next_queue(0),
_next_bucket(0) {
if (allow_resize_and_rehash) {
// If both resize and rehash is needed, only do resize. Rehash of
// the table will eventually happen if the situation persists.
_resized_table = G1StringDedupTable::prepare_resize();
if (!is_resizing()) {
_rehashed_table = G1StringDedupTable::prepare_rehash();
}
}
}
G1StringDedupUnlinkOrOopsDoClosure::~G1StringDedupUnlinkOrOopsDoClosure() {
assert(!is_resizing() || !is_rehashing(), "Can not both resize and rehash");
if (is_resizing()) {
G1StringDedupTable::finish_resize(_resized_table);
} else if (is_rehashing()) {
G1StringDedupTable::finish_rehash(_rehashed_table);
}
}
/*
* Copyright (c) 2014, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUP_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUP_HPP
//
// String Deduplication
//
// String deduplication aims to reduce the heap live-set by deduplicating identical
// instances of String so that they share the same backing character array.
//
// The deduplication process is divided in two main parts, 1) finding the objects to
// deduplicate, and 2) deduplicating those objects. The first part is done as part of
// a normal GC cycle when objects are marked or evacuated. At this time a check is
// applied on each object to check if it is a candidate for deduplication. If so, the
// object is placed on the deduplication queue for later processing. The second part,
// processing the objects on the deduplication queue, is a concurrent phase which
// starts right after the stop-the-wold marking/evacuation phase. This phase is
// executed by the deduplication thread, which pulls deduplication candidates of the
// deduplication queue and tries to deduplicate them.
//
// A deduplication hashtable is used to keep track of all unique character arrays
// used by String objects. When deduplicating, a lookup is made in this table to see
// if there is already an identical character array somewhere on the heap. If so, the
// String object is adjusted to point to that character array, releasing the reference
// to the original array allowing it to eventually be garbage collected. If the lookup
// fails the character array is instead inserted into the hashtable so that this array
// can be shared at some point in the future.
//
// Candidate selection
//
// An object is considered a deduplication candidate if all of the following
// statements are true:
//
// - The object is an instance of java.lang.String
//
// - The object is being evacuated from a young heap region
//
// - The object is being evacuated to a young/survivor heap region and the
// object's age is equal to the deduplication age threshold
//
// or
//
// The object is being evacuated to an old heap region and the object's age is
// less than the deduplication age threshold
//
// Once an string object has been promoted to an old region, or its age is higher
// than the deduplication age threshold, is will never become a candidate again.
// This approach avoids making the same object a candidate more than once.
//
// Interned strings are a bit special. They are explicitly deduplicated just before
// being inserted into the StringTable (to avoid counteracting C2 optimizations done
// on string literals), then they also become deduplication candidates if they reach
// the deduplication age threshold or are evacuated to an old heap region. The second
// attempt to deduplicate such strings will be in vain, but we have no fast way of
// filtering them out. This has not shown to be a problem, as the number of interned
// strings is usually dwarfed by the number of normal (non-interned) strings.
//
// For additional information on string deduplication, please see JEP 192,
// http://openjdk.java.net/jeps/192
//
#include "memory/allocation.hpp"
#include "oops/oop.hpp"
class OopClosure;
class BoolObjectClosure;
class ThreadClosure;
class outputStream;
class G1StringDedupTable;
//
// Main interface for interacting with string deduplication.
//
class G1StringDedup : public AllStatic {
private:
// Single state for checking if both G1 and string deduplication is enabled.
static bool _enabled;
// Candidate selection policies, returns true if the given object is
// candidate for string deduplication.
static bool is_candidate_from_mark(oop obj);
static bool is_candidate_from_evacuation(bool from_young, bool to_young, oop obj);
public:
// Returns true if both G1 and string deduplication is enabled.
static bool is_enabled() {
return _enabled;
}
static void initialize();
// Immediately deduplicates the given String object, bypassing the
// the deduplication queue.
static void deduplicate(oop java_string);
// Enqueues a deduplication candidate for later processing by the deduplication
// thread. Before enqueuing, these functions apply the appropriate candidate
// selection policy to filters out non-candidates.
static void enqueue_from_mark(oop java_string);
static void enqueue_from_evacuation(bool from_young, bool to_young,
unsigned int queue, oop java_string);
static void oops_do(OopClosure* keep_alive);
static void unlink(BoolObjectClosure* is_alive);
static void unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive,
bool allow_resize_and_rehash = true);
static void threads_do(ThreadClosure* tc);
static void print_worker_threads_on(outputStream* st);
static void verify();
};
//
// This closure encapsulates the state and the closures needed when scanning
// the deduplication queue and table during the unlink_or_oops_do() operation.
// A single instance of this closure is created and then shared by all worker
// threads participating in the scan. The _next_queue and _next_bucket fields
// provide a simple mechanism for GC workers to claim exclusive access to a
// queue or a table partition.
//
class G1StringDedupUnlinkOrOopsDoClosure : public StackObj {
private:
BoolObjectClosure* _is_alive;
OopClosure* _keep_alive;
G1StringDedupTable* _resized_table;
G1StringDedupTable* _rehashed_table;
size_t _next_queue;
size_t _next_bucket;
public:
G1StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
bool allow_resize_and_rehash);
~G1StringDedupUnlinkOrOopsDoClosure();
bool is_resizing() {
return _resized_table != NULL;
}
G1StringDedupTable* resized_table() {
return _resized_table;
}
bool is_rehashing() {
return _rehashed_table != NULL;
}
// Atomically claims the next available queue for exclusive access by
// the current thread. Returns the queue number of the claimed queue.
size_t claim_queue() {
return (size_t)Atomic::add_ptr(1, &_next_queue) - 1;
}
// Atomically claims the next available table partition for exclusive
// access by the current thread. Returns the table bucket number where
// the claimed partition starts.
size_t claim_table_partition(size_t partition_size) {
return (size_t)Atomic::add_ptr(partition_size, &_next_bucket) - partition_size;
}
// Applies and returns the result from the is_alive closure, or
// returns true if no such closure was provided.
bool is_alive(oop o) {
if (_is_alive != NULL) {
return _is_alive->do_object_b(o);
}
return true;
}
// Applies the keep_alive closure, or does nothing if no such
// closure was provided.
void keep_alive(oop* p) {
if (_keep_alive != NULL) {
_keep_alive->do_oop(p);
}
}
};
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUP_HPP
/*
* Copyright (c) 2014, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "classfile/javaClasses.hpp"
#include "gc_implementation/g1/g1StringDedupQueue.hpp"
#include "memory/gcLocker.hpp"
#include "runtime/mutexLocker.hpp"
#include "utilities/stack.inline.hpp"
G1StringDedupQueue* G1StringDedupQueue::_queue = NULL;
const size_t G1StringDedupQueue::_max_size = 1000000; // Max number of elements per queue
const size_t G1StringDedupQueue::_max_cache_size = 0; // Max cache size per queue
G1StringDedupQueue::G1StringDedupQueue() :
_cursor(0),
_empty(true),
_dropped(0) {
_nqueues = MAX2(ParallelGCThreads, (size_t)1);
_queues = NEW_C_HEAP_ARRAY(G1StringDedupWorkerQueue, _nqueues, mtGC);
for (size_t i = 0; i < _nqueues; i++) {
new (_queues + i) G1StringDedupWorkerQueue(G1StringDedupWorkerQueue::default_segment_size(), _max_cache_size, _max_size);
}
}
G1StringDedupQueue::~G1StringDedupQueue() {
ShouldNotReachHere();
}
void G1StringDedupQueue::create() {
assert(_queue == NULL, "One string deduplication queue allowed");
_queue = new G1StringDedupQueue();
}
void G1StringDedupQueue::wait() {
MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
while (_queue->_empty) {
ml.wait(Mutex::_no_safepoint_check_flag);
}
}
void G1StringDedupQueue::push(uint worker_id, oop java_string) {
assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint");
assert(worker_id < _queue->_nqueues, "Invalid queue");
// Push and notify waiter
G1StringDedupWorkerQueue& worker_queue = _queue->_queues[worker_id];
if (!worker_queue.is_full()) {
worker_queue.push(java_string);
if (_queue->_empty) {
MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
if (_queue->_empty) {
// Mark non-empty and notify waiter
_queue->_empty = false;
ml.notify();
}
}
} else {
// Queue is full, drop the string and update the statistics
Atomic::inc_ptr(&_queue->_dropped);
}
}
oop G1StringDedupQueue::pop() {
assert(!SafepointSynchronize::is_at_safepoint(), "Must not be at safepoint");
No_Safepoint_Verifier nsv;
// Try all queues before giving up
for (size_t tries = 0; tries < _queue->_nqueues; tries++) {
// The cursor indicates where we left of last time
G1StringDedupWorkerQueue* queue = &_queue->_queues[_queue->_cursor];
while (!queue->is_empty()) {
oop obj = queue->pop();
// The oop we pop can be NULL if it was marked
// dead. Just ignore those and pop the next oop.
if (obj != NULL) {
return obj;
}
}
// Try next queue
_queue->_cursor = (_queue->_cursor + 1) % _queue->_nqueues;
}
// Mark empty
_queue->_empty = true;
return NULL;
}
void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl) {
// A worker thread first claims a queue, which ensures exclusive
// access to that queue, then continues to process it.
for (;;) {
// Grab next queue to scan
size_t queue = cl->claim_queue();
if (queue >= _queue->_nqueues) {
// End of queues
break;
}
// Scan the queue
unlink_or_oops_do(cl, queue);
}
}
void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) {
assert(queue < _queue->_nqueues, "Invalid queue");
StackIterator<oop, mtGC> iter(_queue->_queues[queue]);
while (!iter.is_empty()) {
oop* p = iter.next_addr();
if (*p != NULL) {
if (cl->is_alive(*p)) {
cl->keep_alive(p);
} else {
// Clear dead reference
*p = NULL;
}
}
}
}
void G1StringDedupQueue::print_statistics(outputStream* st) {
st->print_cr(
" [Queue]\n"
" [Dropped: "UINTX_FORMAT"]", _queue->_dropped);
}
void G1StringDedupQueue::verify() {
for (size_t i = 0; i < _queue->_nqueues; i++) {
StackIterator<oop, mtGC> iter(_queue->_queues[i]);
while (!iter.is_empty()) {
oop obj = iter.next();
if (obj != NULL) {
guarantee(Universe::heap()->is_in_reserved(obj), "Object must be on the heap");
guarantee(!obj->is_forwarded(), "Object must not be forwarded");
guarantee(java_lang_String::is_instance(obj), "Object must be a String");
}
}
}
}
/*
* Copyright (c) 2014, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPQUEUE_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPQUEUE_HPP
#include "memory/allocation.hpp"
#include "oops/oop.hpp"
#include "utilities/stack.hpp"
class G1StringDedupUnlinkOrOopsDoClosure;
//
// The deduplication queue acts as the communication channel between the stop-the-world
// mark/evacuation phase and the concurrent deduplication phase. Deduplication candidates
// found during mark/evacuation are placed on this queue for later processing in the
// deduplication thread. A queue entry is an oop pointing to a String object (as opposed
// to entries in the deduplication hashtable which points to character arrays).
//
// While users of the queue treat it as a single queue, it is implemented as a set of
// queues, one queue per GC worker thread, to allow lock-free and cache-friendly enqueue
// operations by the GC workers.
//
// The oops in the queue are treated as weak pointers, meaning the objects they point to
// can become unreachable and pruned (cleared) before being popped by the deduplication
// thread.
//
// Pushing to the queue is thread safe (this relies on each thread using a unique worker
// id), but only allowed during a safepoint. Popping from the queue is NOT thread safe
// and can only be done by the deduplication thread outside a safepoint.
//
// The StringDedupQueue_lock is only used for blocking and waking up the deduplication
// thread in case the queue is empty or becomes non-empty, respectively. This lock does
// not otherwise protect the queue content.
//
class G1StringDedupQueue : public CHeapObj<mtGC> {
private:
typedef Stack<oop, mtGC> G1StringDedupWorkerQueue;
static G1StringDedupQueue* _queue;
static const size_t _max_size;
static const size_t _max_cache_size;
G1StringDedupWorkerQueue* _queues;
size_t _nqueues;
size_t _cursor;
volatile bool _empty;
// Statistics counter, only used for logging.
uintx _dropped;
G1StringDedupQueue();
~G1StringDedupQueue();
static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, size_t queue);
public:
static void create();
// Blocks and waits for the queue to become non-empty.
static void wait();
// Pushes a deduplication candidate onto a specific GC worker queue.
static void push(uint worker_id, oop java_string);
// Pops a deduplication candidate from any queue, returns NULL if
// all queues are empty.
static oop pop();
static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl);
static void print_statistics(outputStream* st);
static void verify();
};
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPQUEUE_HPP
/*
* Copyright (c) 2014, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "gc_implementation/g1/g1StringDedupStat.hpp"
G1StringDedupStat::G1StringDedupStat() :
_inspected(0),
_skipped(0),
_hashed(0),
_known(0),
_new(0),
_new_bytes(0),
_deduped(0),
_deduped_bytes(0),
_deduped_young(0),
_deduped_young_bytes(0),
_deduped_old(0),
_deduped_old_bytes(0),
_idle(0),
_exec(0),
_block(0),
_start(0.0),
_idle_elapsed(0.0),
_exec_elapsed(0.0),
_block_elapsed(0.0) {
}
void G1StringDedupStat::add(const G1StringDedupStat& stat) {
_inspected += stat._inspected;
_skipped += stat._skipped;
_hashed += stat._hashed;
_known += stat._known;
_new += stat._new;
_new_bytes += stat._new_bytes;
_deduped += stat._deduped;
_deduped_bytes += stat._deduped_bytes;
_deduped_young += stat._deduped_young;
_deduped_young_bytes += stat._deduped_young_bytes;
_deduped_old += stat._deduped_old;
_deduped_old_bytes += stat._deduped_old_bytes;
_idle += stat._idle;
_exec += stat._exec;
_block += stat._block;
_idle_elapsed += stat._idle_elapsed;
_exec_elapsed += stat._exec_elapsed;
_block_elapsed += stat._block_elapsed;
}
void G1StringDedupStat::print_summary(outputStream* st, const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat) {
double total_deduped_bytes_percent = 0.0;
if (total_stat._new_bytes > 0) {
// Avoid division by zero
total_deduped_bytes_percent = (double)total_stat._deduped_bytes / (double)total_stat._new_bytes * 100.0;
}
st->date_stamp(PrintGCDateStamps);
st->stamp(PrintGCTimeStamps);
st->print_cr(
"[GC concurrent-string-deduplication, "
G1_STRDEDUP_BYTES_FORMAT_NS"->"G1_STRDEDUP_BYTES_FORMAT_NS"("G1_STRDEDUP_BYTES_FORMAT_NS"), avg "
G1_STRDEDUP_PERCENT_FORMAT_NS", "G1_STRDEDUP_TIME_FORMAT"]",
G1_STRDEDUP_BYTES_PARAM(last_stat._new_bytes),
G1_STRDEDUP_BYTES_PARAM(last_stat._new_bytes - last_stat._deduped_bytes),
G1_STRDEDUP_BYTES_PARAM(last_stat._deduped_bytes),
total_deduped_bytes_percent,
last_stat._exec_elapsed);
}
void G1StringDedupStat::print_statistics(outputStream* st, const G1StringDedupStat& stat, bool total) {
double young_percent = 0.0;
double old_percent = 0.0;
double skipped_percent = 0.0;
double hashed_percent = 0.0;
double known_percent = 0.0;
double new_percent = 0.0;
double deduped_percent = 0.0;
double deduped_bytes_percent = 0.0;
double deduped_young_percent = 0.0;
double deduped_young_bytes_percent = 0.0;
double deduped_old_percent = 0.0;
double deduped_old_bytes_percent = 0.0;
if (stat._inspected > 0) {
// Avoid division by zero
skipped_percent = (double)stat._skipped / (double)stat._inspected * 100.0;
hashed_percent = (double)stat._hashed / (double)stat._inspected * 100.0;
known_percent = (double)stat._known / (double)stat._inspected * 100.0;
new_percent = (double)stat._new / (double)stat._inspected * 100.0;
}
if (stat._new > 0) {
// Avoid division by zero
deduped_percent = (double)stat._deduped / (double)stat._new * 100.0;
}
if (stat._deduped > 0) {
// Avoid division by zero
deduped_young_percent = (double)stat._deduped_young / (double)stat._deduped * 100.0;
deduped_old_percent = (double)stat._deduped_old / (double)stat._deduped * 100.0;
}
if (stat._new_bytes > 0) {
// Avoid division by zero
deduped_bytes_percent = (double)stat._deduped_bytes / (double)stat._new_bytes * 100.0;
}
if (stat._deduped_bytes > 0) {
// Avoid division by zero
deduped_young_bytes_percent = (double)stat._deduped_young_bytes / (double)stat._deduped_bytes * 100.0;
deduped_old_bytes_percent = (double)stat._deduped_old_bytes / (double)stat._deduped_bytes * 100.0;
}
if (total) {
st->print_cr(
" [Total Exec: "UINTX_FORMAT"/"G1_STRDEDUP_TIME_FORMAT", Idle: "UINTX_FORMAT"/"G1_STRDEDUP_TIME_FORMAT", Blocked: "UINTX_FORMAT"/"G1_STRDEDUP_TIME_FORMAT"]",
stat._exec, stat._exec_elapsed, stat._idle, stat._idle_elapsed, stat._block, stat._block_elapsed);
} else {
st->print_cr(
" [Last Exec: "G1_STRDEDUP_TIME_FORMAT", Idle: "G1_STRDEDUP_TIME_FORMAT", Blocked: "UINTX_FORMAT"/"G1_STRDEDUP_TIME_FORMAT"]",
stat._exec_elapsed, stat._idle_elapsed, stat._block, stat._block_elapsed);
}
st->print_cr(
" [Inspected: "G1_STRDEDUP_OBJECTS_FORMAT"]\n"
" [Skipped: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n"
" [Hashed: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n"
" [Known: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n"
" [New: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT") "G1_STRDEDUP_BYTES_FORMAT"]\n"
" [Deduplicated: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT") "G1_STRDEDUP_BYTES_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n"
" [Young: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT") "G1_STRDEDUP_BYTES_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n"
" [Old: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT") "G1_STRDEDUP_BYTES_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]",
stat._inspected,
stat._skipped, skipped_percent,
stat._hashed, hashed_percent,
stat._known, known_percent,
stat._new, new_percent, G1_STRDEDUP_BYTES_PARAM(stat._new_bytes),
stat._deduped, deduped_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_bytes), deduped_bytes_percent,
stat._deduped_young, deduped_young_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_young_bytes), deduped_young_bytes_percent,
stat._deduped_old, deduped_old_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_old_bytes), deduped_old_bytes_percent);
}
/*
* Copyright (c) 2014, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPSTAT_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPSTAT_HPP
#include "memory/allocation.hpp"
#include "runtime/os.hpp"
// Macros for GC log output formating
#define G1_STRDEDUP_OBJECTS_FORMAT UINTX_FORMAT_W(12)
#define G1_STRDEDUP_TIME_FORMAT "%1.7lf secs"
#define G1_STRDEDUP_PERCENT_FORMAT "%5.1lf%%"
#define G1_STRDEDUP_PERCENT_FORMAT_NS "%.1lf%%"
#define G1_STRDEDUP_BYTES_FORMAT "%8.1lf%s"
#define G1_STRDEDUP_BYTES_FORMAT_NS "%.1lf%s"
#define G1_STRDEDUP_BYTES_PARAM(bytes) byte_size_in_proper_unit((double)(bytes)), proper_unit_for_byte_size((bytes))
//
// Statistics gathered by the deduplication thread.
//
class G1StringDedupStat : public StackObj {
private:
// Counters
uintx _inspected;
uintx _skipped;
uintx _hashed;
uintx _known;
uintx _new;
uintx _new_bytes;
uintx _deduped;
uintx _deduped_bytes;
uintx _deduped_young;
uintx _deduped_young_bytes;
uintx _deduped_old;
uintx _deduped_old_bytes;
uintx _idle;
uintx _exec;
uintx _block;
// Time spent by the deduplication thread in different phases
double _start;
double _idle_elapsed;
double _exec_elapsed;
double _block_elapsed;
public:
G1StringDedupStat();
void inc_inspected() {
_inspected++;
}
void inc_skipped() {
_skipped++;
}
void inc_hashed() {
_hashed++;
}
void inc_known() {
_known++;
}
void inc_new(uintx bytes) {
_new++;
_new_bytes += bytes;
}
void inc_deduped_young(uintx bytes) {
_deduped++;
_deduped_bytes += bytes;
_deduped_young++;
_deduped_young_bytes += bytes;
}
void inc_deduped_old(uintx bytes) {
_deduped++;
_deduped_bytes += bytes;
_deduped_old++;
_deduped_old_bytes += bytes;
}
void mark_idle() {
_start = os::elapsedTime();
_idle++;
}
void mark_exec() {
double now = os::elapsedTime();
_idle_elapsed = now - _start;
_start = now;
_exec++;
}
void mark_block() {
double now = os::elapsedTime();
_exec_elapsed += now - _start;
_start = now;
_block++;
}
void mark_unblock() {
double now = os::elapsedTime();
_block_elapsed += now - _start;
_start = now;
}
void mark_done() {
double now = os::elapsedTime();
_exec_elapsed += now - _start;
}
void add(const G1StringDedupStat& stat);
static void print_summary(outputStream* st, const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat);
static void print_statistics(outputStream* st, const G1StringDedupStat& stat, bool total);
};
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPSTAT_HPP
/*
* Copyright (c) 2014, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTABLE_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTABLE_HPP
#include "gc_implementation/g1/g1StringDedupStat.hpp"
#include "runtime/mutexLocker.hpp"
class G1StringDedupEntryCache;
//
// Table entry in the deduplication hashtable. Points weakly to the
// character array. Can be chained in a linked list in case of hash
// collisions or when placed in a freelist in the entry cache.
//
class G1StringDedupEntry : public CHeapObj<mtGC> {
private:
G1StringDedupEntry* _next;
unsigned int _hash;
typeArrayOop _obj;
public:
G1StringDedupEntry() :
_next(NULL),
_hash(0),
_obj(NULL) {
}
G1StringDedupEntry* next() {
return _next;
}
G1StringDedupEntry** next_addr() {
return &_next;
}
void set_next(G1StringDedupEntry* next) {
_next = next;
}
unsigned int hash() {
return _hash;
}
void set_hash(unsigned int hash) {
_hash = hash;
}
typeArrayOop obj() {
return _obj;
}
typeArrayOop* obj_addr() {
return &_obj;
}
void set_obj(typeArrayOop obj) {
_obj = obj;
}
};
//
// The deduplication hashtable keeps track of all unique character arrays used
// by String objects. Each table entry weakly points to an character array, allowing
// otherwise unreachable character arrays to be declared dead and pruned from the
// table.
//
// The table is dynamically resized to accommodate the current number of table entries.
// The table has hash buckets with chains for hash collision. If the average chain
// length goes above or below given thresholds the table grows or shrinks accordingly.
//
// The table is also dynamically rehashed (using a new hash seed) if it becomes severely
// unbalanced, i.e., a hash chain is significantly longer than average.
//
// All access to the table is protected by the StringDedupTable_lock, except under
// safepoints in which case GC workers are allowed to access a table partitions they
// have claimed without first acquiring the lock. Note however, that this applies only
// the table partition (i.e. a range of elements in _buckets), not other parts of the
// table such as the _entries field, statistics counters, etc.
//
class G1StringDedupTable : public CHeapObj<mtGC> {
private:
// The currently active hashtable instance. Only modified when
// the table is resizes or rehashed.
static G1StringDedupTable* _table;
// Cache for reuse and fast alloc/free of table entries.
static G1StringDedupEntryCache* _entry_cache;
G1StringDedupEntry** _buckets;
size_t _size;
uintx _entries;
uintx _shrink_threshold;
uintx _grow_threshold;
bool _rehash_needed;
// The hash seed also dictates which hash function to use. A
// zero hash seed means we will use the Java compatible hash
// function (which doesn't use a seed), and a non-zero hash
// seed means we use the murmur3 hash function.
jint _hash_seed;
// Constants governing table resize/rehash/cache.
static const size_t _min_size;
static const size_t _max_size;
static const double _grow_load_factor;
static const double _shrink_load_factor;
static const uintx _rehash_multiple;
static const uintx _rehash_threshold;
static const double _max_cache_factor;
// Table statistics, only used for logging.
static uintx _entries_added;
static uintx _entries_removed;
static uintx _resize_count;
static uintx _rehash_count;
G1StringDedupTable(size_t size, jint hash_seed = 0);
~G1StringDedupTable();
// Returns the hash bucket at the given index.
G1StringDedupEntry** bucket(size_t index) {
return _buckets + index;
}
// Returns the hash bucket index for the given hash code.
size_t hash_to_index(unsigned int hash) {
return (size_t)hash & (_size - 1);
}
// Adds a new table entry to the given hash bucket.
void add(typeArrayOop value, unsigned int hash, G1StringDedupEntry** list);
// Removes the given table entry from the table.
void remove(G1StringDedupEntry** pentry, uint worker_id);
// Transfers a table entry from the current table to the destination table.
void transfer(G1StringDedupEntry** pentry, G1StringDedupTable* dest);
// Returns an existing character array in the given hash bucket, or NULL
// if no matching character array exists.
typeArrayOop lookup(typeArrayOop value, unsigned int hash,
G1StringDedupEntry** list, uintx &count);
// Returns an existing character array in the table, or inserts a new
// table entry if no matching character array exists.
typeArrayOop lookup_or_add_inner(typeArrayOop value, unsigned int hash);
// Thread safe lookup or add of table entry
static typeArrayOop lookup_or_add(typeArrayOop value, unsigned int hash) {
// Protect the table from concurrent access. Also note that this lock
// acts as a fence for _table, which could have been replaced by a new
// instance if the table was resized or rehashed.
MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag);
return _table->lookup_or_add_inner(value, hash);
}
// Returns true if the hashtable is currently using a Java compatible
// hash function.
static bool use_java_hash() {
return _table->_hash_seed == 0;
}
static bool equals(typeArrayOop value1, typeArrayOop value2);
// Computes the hash code for the given character array, using the
// currently active hash function and hash seed.
static unsigned int hash_code(typeArrayOop value);
static uintx unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl,
size_t partition_begin,
size_t partition_end,
uint worker_id);
public:
static void create();
// Deduplicates the given String object, or adds its backing
// character array to the deduplication hashtable.
static void deduplicate(oop java_string, G1StringDedupStat& stat);
// If a table resize is needed, returns a newly allocated empty
// hashtable of the proper size.
static G1StringDedupTable* prepare_resize();
// Installs a newly resized table as the currently active table
// and deletes the previously active table.
static void finish_resize(G1StringDedupTable* resized_table);
// If a table rehash is needed, returns a newly allocated empty
// hashtable and updates the hash seed.
static G1StringDedupTable* prepare_rehash();
// Transfers rehashed entries from the currently active table into
// the new table. Installs the new table as the currently active table
// and deletes the previously active table.
static void finish_rehash(G1StringDedupTable* rehashed_table);
// If the table entry cache has grown too large, trim it down according to policy
static void trim_entry_cache();
static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id);
static void print_statistics(outputStream* st);
static void verify();
};
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTABLE_HPP
/*
* Copyright (c) 2014, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "gc_implementation/g1/g1Log.hpp"
#include "gc_implementation/g1/g1StringDedup.hpp"
#include "gc_implementation/g1/g1StringDedupTable.hpp"
#include "gc_implementation/g1/g1StringDedupThread.hpp"
#include "gc_implementation/g1/g1StringDedupQueue.hpp"
G1StringDedupThread* G1StringDedupThread::_thread = NULL;
G1StringDedupThread::G1StringDedupThread() :
ConcurrentGCThread() {
set_name("String Deduplication Thread");
create_and_start();
}
G1StringDedupThread::~G1StringDedupThread() {
ShouldNotReachHere();
}
void G1StringDedupThread::create() {
assert(G1StringDedup::is_enabled(), "String deduplication not enabled");
assert(_thread == NULL, "One string deduplication thread allowed");
_thread = new G1StringDedupThread();
}
G1StringDedupThread* G1StringDedupThread::thread() {
assert(G1StringDedup::is_enabled(), "String deduplication not enabled");
assert(_thread != NULL, "String deduplication thread not created");
return _thread;
}
void G1StringDedupThread::print_on(outputStream* st) const {
st->print("\"%s\" ", name());
Thread::print_on(st);
st->cr();
}
void G1StringDedupThread::run() {
G1StringDedupStat total_stat;
initialize_in_thread();
wait_for_universe_init();
// Main loop
for (;;) {
G1StringDedupStat stat;
stat.mark_idle();
// Wait for the queue to become non-empty
G1StringDedupQueue::wait();
// Include this thread in safepoints
stsJoin();
stat.mark_exec();
// Process the queue
for (;;) {
oop java_string = G1StringDedupQueue::pop();
if (java_string == NULL) {
break;
}
G1StringDedupTable::deduplicate(java_string, stat);
// Safepoint this thread if needed
if (stsShouldYield()) {
stat.mark_block();
stsYield(NULL);
stat.mark_unblock();
}
}
G1StringDedupTable::trim_entry_cache();
stat.mark_done();
// Print statistics
total_stat.add(stat);
print(gclog_or_tty, stat, total_stat);
// Exclude this thread from safepoints
stsLeave();
}
ShouldNotReachHere();
}
void G1StringDedupThread::print(outputStream* st, const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat) {
if (G1Log::fine() || PrintStringDeduplicationStatistics) {
G1StringDedupStat::print_summary(st, last_stat, total_stat);
if (PrintStringDeduplicationStatistics) {
G1StringDedupStat::print_statistics(st, last_stat, false);
G1StringDedupStat::print_statistics(st, total_stat, true);
G1StringDedupTable::print_statistics(st);
G1StringDedupQueue::print_statistics(st);
}
}
}
/*
* Copyright (c) 2014, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTHREAD_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTHREAD_HPP
#include "gc_implementation/g1/g1StringDedupStat.hpp"
#include "gc_implementation/shared/concurrentGCThread.hpp"
//
// The deduplication thread is where the actual deduplication occurs. It waits for
// deduplication candidates to appear on the deduplication queue, removes them from
// the queue and tries to deduplicate them. It uses the deduplication hashtable to
// find identical, already existing, character arrays on the heap. The thread runs
// concurrently with the Java application but participates in safepoints to allow
// the GC to adjust and unlink oops from the deduplication queue and table.
//
class G1StringDedupThread: public ConcurrentGCThread {
private:
static G1StringDedupThread* _thread;
G1StringDedupThread();
~G1StringDedupThread();
void print(outputStream* st, const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat);
public:
static void create();
static G1StringDedupThread* thread();
virtual void run();
virtual void print_on(outputStream* st) const;
};
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTHREAD_HPP
/*
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2014, 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
......@@ -285,6 +285,10 @@
product(uintx, G1MixedGCCountTarget, 8, \
"The target number of mixed GCs after a marking cycle.") \
\
experimental(uintx, G1CodeRootsChunkCacheKeepPercent, 10, \
"The amount of code root chunks that should be kept at most " \
"as percentage of already allocated.") \
\
experimental(uintx, G1OldCSetRegionThresholdPercent, 10, \
"An upper bound for the number of old CSet regions expressed " \
"as a percentage of the heap size.") \
......
......@@ -43,8 +43,6 @@ class G1ParCopyClosure;
class G1ParScanClosure;
class G1ParPushHeapRSClosure;
typedef G1ParCopyClosure<G1BarrierEvac, false> G1ParScanHeapEvacClosure;
class FilterIntoCSClosure;
class FilterOutOfRegionClosure;
class G1CMOopClosure;
......@@ -61,7 +59,6 @@ class G1UpdateRSOrPushRefOopClosure;
#endif
#define FURTHER_SPECIALIZED_OOP_OOP_ITERATE_CLOSURES(f) \
f(G1ParScanHeapEvacClosure,_nv) \
f(G1ParScanClosure,_nv) \
f(G1ParPushHeapRSClosure,_nv) \
f(FilterIntoCSClosure,_nv) \
......
......@@ -271,6 +271,7 @@ class HeapRegion: public G1OffsetTableContigSpace {
// Fields used by the HeapRegionSetBase class and subclasses.
HeapRegion* _next;
HeapRegion* _prev;
#ifdef ASSERT
HeapRegionSetBase* _containing_set;
#endif // ASSERT
......@@ -531,11 +532,13 @@ class HeapRegion: public G1OffsetTableContigSpace {
// Methods used by the HeapRegionSetBase class and subclasses.
// Getter and setter for the next field used to link regions into
// Getter and setter for the next and prev fields used to link regions into
// linked lists.
HeapRegion* next() { return _next; }
HeapRegion* prev() { return _prev; }
void set_next(HeapRegion* next) { _next = next; }
void set_prev(HeapRegion* prev) { _prev = prev; }
// Every region added to a set is tagged with a reference to that
// set. This is used for doing consistency checking to make sure that
......@@ -596,7 +599,7 @@ class HeapRegion: public G1OffsetTableContigSpace {
void save_marks();
// Reset HR stuff to default values.
void hr_clear(bool par, bool clear_space);
void hr_clear(bool par, bool clear_space, bool locked = false);
void par_clear();
// Get the start of the unmarked area in this region.
......
......@@ -25,7 +25,7 @@
#include "precompiled.hpp"
#include "gc_implementation/g1/heapRegion.hpp"
#include "gc_implementation/g1/heapRegionSeq.inline.hpp"
#include "gc_implementation/g1/heapRegionSets.hpp"
#include "gc_implementation/g1/heapRegionSet.hpp"
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
#include "memory/allocation.hpp"
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册