--- a/make/autoconf/hotspot.m4 Tue Nov 12 10:25:55 2019 -0500
+++ b/make/autoconf/hotspot.m4 Wed Nov 13 17:21:31 2019 -0500
@@ -25,11 +25,11 @@
# All valid JVM features, regardless of platform
VALID_JVM_FEATURES="compiler1 compiler2 zero minimal dtrace jvmti jvmci \
- graal vm-structs jni-check services management cmsgc epsilongc g1gc parallelgc serialgc shenandoahgc zgc nmt cds \
+ graal vm-structs jni-check services management epsilongc g1gc parallelgc serialgc shenandoahgc zgc nmt cds \
static-build link-time-opt aot jfr"
# Deprecated JVM features (these are ignored, but with a warning)
-DEPRECATED_JVM_FEATURES="trace"
+DEPRECATED_JVM_FEATURES="trace cmsgc"
# All valid JVM variants
VALID_JVM_VARIANTS="server client minimal core zero custom"
@@ -326,10 +326,6 @@
AC_MSG_ERROR([Specified JVM feature 'jvmci' requires feature 'compiler2' or 'compiler1'])
fi
- if HOTSPOT_CHECK_JVM_FEATURE(cmsgc) && ! HOTSPOT_CHECK_JVM_FEATURE(serialgc); then
- AC_MSG_ERROR([Specified JVM feature 'cmsgc' requires feature 'serialgc'])
- fi
-
# Enable JFR by default, except for Zero, linux-sparcv9 and on minimal.
if ! HOTSPOT_CHECK_JVM_VARIANT(zero); then
if test "x$OPENJDK_TARGET_OS" != xaix; then
@@ -491,7 +487,7 @@
fi
# All variants but minimal (and custom) get these features
- NON_MINIMAL_FEATURES="$NON_MINIMAL_FEATURES cmsgc g1gc parallelgc serialgc epsilongc shenandoahgc jni-check jvmti management nmt services vm-structs zgc"
+ NON_MINIMAL_FEATURES="$NON_MINIMAL_FEATURES g1gc parallelgc serialgc epsilongc shenandoahgc jni-check jvmti management nmt services vm-structs zgc"
# Disable CDS on AIX.
if test "x$OPENJDK_TARGET_OS" = "xaix"; then
--- a/make/autoconf/toolchain.m4 Tue Nov 12 10:25:55 2019 -0500
+++ b/make/autoconf/toolchain.m4 Wed Nov 13 17:21:31 2019 -0500
@@ -481,7 +481,7 @@
COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT | \
$SED -e 's/ *Copyright .*//'`
COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \
- $SED -e 's/^.* \(@<:@1-9@:>@\.@<:@0-9.@:>@*\)@<:@^0-9.@:>@.*$/\1/'`
+ $SED -e 's/^.* \(@<:@1-9@:>@<:@0-9@:>@*\.@<:@0-9.@:>@*\)@<:@^0-9.@:>@.*$/\1/'`
elif test "x$TOOLCHAIN_TYPE" = xclang; then
# clang --version output typically looks like
# Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
--- a/make/hotspot/lib/JvmDtraceObjects.gmk Tue Nov 12 10:25:55 2019 -0500
+++ b/make/hotspot/lib/JvmDtraceObjects.gmk Wed Nov 13 17:21:31 2019 -0500
@@ -79,12 +79,6 @@
vmThread.o \
)
- ifeq ($(call check-jvm-feature, cmsgc), true)
- DTRACE_INSTRUMENTED_OBJS += $(addprefix $(JVM_OUTPUTDIR)/objs/, \
- cmsVMOperations.o \
- )
- endif
-
ifeq ($(call check-jvm-feature, parallelgc), true)
DTRACE_INSTRUMENTED_OBJS += $(addprefix $(JVM_OUTPUTDIR)/objs/, \
psVMOperations.o \
--- a/make/hotspot/lib/JvmFeatures.gmk Tue Nov 12 10:25:55 2019 -0500
+++ b/make/hotspot/lib/JvmFeatures.gmk Wed Nov 13 17:21:31 2019 -0500
@@ -138,11 +138,6 @@
aotLoader.cpp compiledIC_aot.cpp
endif
-ifneq ($(call check-jvm-feature, cmsgc), true)
- JVM_CFLAGS_FEATURES += -DINCLUDE_CMSGC=0
- JVM_EXCLUDE_PATTERNS += gc/cms
-endif
-
ifneq ($(call check-jvm-feature, g1gc), true)
JVM_CFLAGS_FEATURES += -DINCLUDE_G1GC=0
JVM_EXCLUDE_PATTERNS += gc/g1
--- a/make/hotspot/src/native/dtrace/generateJvmOffsets.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/make/hotspot/src/native/dtrace/generateJvmOffsets.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -63,7 +63,6 @@
*/
#pragma weak tty
-#pragma weak CMSExpAvgFactor
#if defined(i386) || defined(__i386) || defined(__amd64)
#pragma weak noreg
--- a/src/hotspot/cpu/aarch64/aarch64.ad Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/cpu/aarch64/aarch64.ad Wed Nov 13 17:21:31 2019 -0500
@@ -1192,9 +1192,6 @@
// predicate controlling translation of CompareAndSwapX
bool needs_acquiring_load_exclusive(const Node *load);
- // predicate controlling translation of StoreCM
- bool unnecessary_storestore(const Node *storecm);
-
// predicate controlling addressing modes
bool size_fits_all_mem_uses(AddPNode* addp, int shift);
%}
@@ -1583,29 +1580,6 @@
return true;
}
-// predicate controlling translation of StoreCM
-//
-// returns true if a StoreStore must precede the card write otherwise
-// false
-
-bool unnecessary_storestore(const Node *storecm)
-{
- assert(storecm->Opcode() == Op_StoreCM, "expecting a StoreCM");
-
- // we need to generate a dmb ishst between an object put and the
- // associated card mark when we are using CMS without conditional
- // card marking
-
- if (UseConcMarkSweepGC && !UseCondCardMark) {
- return false;
- }
-
- // a storestore is unnecesary in all other cases
-
- return true;
-}
-
-
#define __ _masm.
// advance declarations for helper functions to convert register
@@ -7220,7 +7194,6 @@
instruct storeimmCM0(immI0 zero, memory mem)
%{
match(Set mem (StoreCM mem zero));
- predicate(unnecessary_storestore(n));
ins_cost(INSN_COST);
format %{ "storestore (elided)\n\t"
--- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -64,9 +64,6 @@
define_pd_global(bool, PreserveFramePointer, false);
-// GC Ergo Flags
-define_pd_global(uintx, CMSYoungGenPerWorker, 64*M); // default max size of CMS young gen, per GC worker thread
-
define_pd_global(uintx, TypeProfileLevel, 111);
define_pd_global(bool, CompactStrings, true);
--- a/src/hotspot/cpu/arm/globals_arm.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/cpu/arm/globals_arm.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -63,9 +63,6 @@
define_pd_global(bool, PreserveFramePointer, false);
-// GC Ergo Flags
-define_pd_global(size_t, CMSYoungGenPerWorker, 16*M); // default max size of CMS young gen, per GC worker thread
-
define_pd_global(uintx, TypeProfileLevel, 0);
// No performance work done here yet.
--- a/src/hotspot/cpu/ppc/globals_ppc.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/cpu/ppc/globals_ppc.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -67,9 +67,6 @@
define_pd_global(bool, PreserveFramePointer, false);
-// GC Ergo Flags
-define_pd_global(size_t, CMSYoungGenPerWorker, 16*M); // Default max size of CMS young gen, per GC worker thread.
-
define_pd_global(uintx, TypeProfileLevel, 111);
define_pd_global(bool, CompactStrings, true);
--- a/src/hotspot/cpu/ppc/ppc.ad Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/cpu/ppc/ppc.ad Wed Nov 13 17:21:31 2019 -0500
@@ -6928,25 +6928,6 @@
ins_pipe(pipe_class_memory);
%}
-// Card-mark for CMS garbage collection.
-// This cardmark does an optimization so that it must not always
-// do a releasing store. For this, it needs the constant address of
-// CMSCollectorCardTableBarrierSetBSExt::_requires_release.
-// This constant address is split off here by expand so we can use
-// adlc / matcher functionality to load it from the constant section.
-instruct storeCM_CMS_ExEx(memory mem, immI_0 zero) %{
- match(Set mem (StoreCM mem zero));
- predicate(UseConcMarkSweepGC);
-
- expand %{
- immL baseImm %{ 0 /* TODO: PPC port (jlong)CMSCollectorCardTableBarrierSetBSExt::requires_release_address() */ %}
- iRegLdst releaseFieldAddress;
- flagsReg crx;
- loadConL_Ex(releaseFieldAddress, baseImm);
- storeCM_CMS(mem, releaseFieldAddress, crx);
- %}
-%}
-
instruct storeCM_G1(memory mem, immI_0 zero) %{
match(Set mem (StoreCM mem zero));
predicate(UseG1GC);
--- a/src/hotspot/cpu/s390/globals_s390.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/cpu/s390/globals_s390.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -69,9 +69,6 @@
define_pd_global(bool, PreserveFramePointer, false);
-// GC Ergo Flags
-define_pd_global(size_t, CMSYoungGenPerWorker, 16*M); // Default max size of CMS young gen, per GC worker thread.
-
define_pd_global(uintx, TypeProfileLevel, 111);
define_pd_global(bool, CompactStrings, true);
--- a/src/hotspot/cpu/sparc/globals_sparc.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/cpu/sparc/globals_sparc.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -74,9 +74,6 @@
define_pd_global(bool, PreserveFramePointer, false);
-// GC Ergo Flags
-define_pd_global(size_t, CMSYoungGenPerWorker, 16*M); // default max size of CMS young gen, per GC worker thread
-
define_pd_global(uintx, TypeProfileLevel, 111);
define_pd_global(bool, CompactStrings, true);
--- a/src/hotspot/cpu/x86/assembler_x86.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/cpu/x86/assembler_x86.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -9163,6 +9163,26 @@
emit_int8((unsigned char)(0xD0 | encode));
}
+void Assembler::btsq(Address dst, int imm8) {
+ assert(isByte(imm8), "not a byte");
+ InstructionMark im(this);
+ prefixq(dst);
+ emit_int8((unsigned char)0x0F);
+ emit_int8((unsigned char)0xBA);
+ emit_operand(rbp /* 5 */, dst, 1);
+ emit_int8(imm8);
+}
+
+void Assembler::btrq(Address dst, int imm8) {
+ assert(isByte(imm8), "not a byte");
+ InstructionMark im(this);
+ prefixq(dst);
+ emit_int8((unsigned char)0x0F);
+ emit_int8((unsigned char)0xBA);
+ emit_operand(rsi /* 6 */, dst, 1);
+ emit_int8(imm8);
+}
+
void Assembler::orq(Address dst, int32_t imm32) {
InstructionMark im(this);
prefixq(dst);
--- a/src/hotspot/cpu/x86/assembler_x86.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/cpu/x86/assembler_x86.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -1592,6 +1592,9 @@
#ifdef _LP64
void notq(Register dst);
+
+ void btsq(Address dst, int imm8);
+ void btrq(Address dst, int imm8);
#endif
void orl(Address dst, int32_t imm32);
--- a/src/hotspot/cpu/x86/globals_x86.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/cpu/x86/globals_x86.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -81,9 +81,6 @@
define_pd_global(bool, RewriteBytecodes, true);
define_pd_global(bool, RewriteFrequentPairs, true);
-// GC Ergo Flags
-define_pd_global(size_t, CMSYoungGenPerWorker, 64*M); // default max size of CMS young gen, per GC worker thread
-
define_pd_global(uintx, TypeProfileLevel, 111);
define_pd_global(bool, CompactStrings, true);
--- a/src/hotspot/cpu/x86/x86_64.ad Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/cpu/x86/x86_64.ad Wed Nov 13 17:21:31 2019 -0500
@@ -3116,6 +3116,26 @@
interface(CONST_INTER);
%}
+operand immL_Pow2()
+%{
+ predicate(is_power_of_2_long(n->get_long()));
+ match(ConL);
+
+ op_cost(15);
+ format %{ %}
+ interface(CONST_INTER);
+%}
+
+operand immL_NotPow2()
+%{
+ predicate(is_power_of_2_long(~n->get_long()));
+ match(ConL);
+
+ op_cost(15);
+ format %{ %}
+ interface(CONST_INTER);
+%}
+
// Long Immediate zero
operand immL0()
%{
@@ -9841,6 +9861,23 @@
ins_pipe(ialu_mem_imm);
%}
+instruct btrL_mem_imm(memory dst, immL_NotPow2 con, rFlagsReg cr)
+%{
+ // con should be a pure 64-bit immediate given that not(con) is a power of 2
+ // because AND/OR works well enough for 8/32-bit values.
+ predicate(log2_long(~n->in(3)->in(2)->get_long()) > 30);
+
+ match(Set dst (StoreL dst (AndL (LoadL dst) con)));
+ effect(KILL cr);
+
+ ins_cost(125);
+ format %{ "btrq $dst, log2(not($con))\t# long" %}
+ ins_encode %{
+ __ btrq($dst$$Address, log2_long(~$con$$constant));
+ %}
+ ins_pipe(ialu_mem_imm);
+%}
+
// BMI1 instructions
instruct andnL_rReg_rReg_mem(rRegL dst, rRegL src1, memory src2, immL_M1 minus_1, rFlagsReg cr) %{
match(Set dst (AndL (XorL src1 minus_1) (LoadL src2)));
@@ -10034,6 +10071,23 @@
ins_pipe(ialu_mem_imm);
%}
+instruct btsL_mem_imm(memory dst, immL_Pow2 con, rFlagsReg cr)
+%{
+ // con should be a pure 64-bit power of 2 immediate
+ // because AND/OR works well enough for 8/32-bit values.
+ predicate(log2_long(n->in(3)->in(2)->get_long()) > 31);
+
+ match(Set dst (StoreL dst (OrL (LoadL dst) con)));
+ effect(KILL cr);
+
+ ins_cost(125);
+ format %{ "btsq $dst, log2($con)\t# long" %}
+ ins_encode %{
+ __ btsq($dst$$Address, log2_long($con$$constant));
+ %}
+ ins_pipe(ialu_mem_imm);
+%}
+
// Xor Instructions
// Xor Register with Register
instruct xorL_rReg(rRegL dst, rRegL src, rFlagsReg cr)
--- a/src/hotspot/cpu/zero/globals_zero.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/cpu/zero/globals_zero.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -66,9 +66,6 @@
define_pd_global(bool, RewriteBytecodes, true);
define_pd_global(bool, RewriteFrequentPairs, true);
-// GC Ergo Flags
-define_pd_global(size_t, CMSYoungGenPerWorker, 16*M); // default max size of CMS young gen, per GC worker thread
-
define_pd_global(uintx, TypeProfileLevel, 0);
define_pd_global(bool, PreserveFramePointer, false);
--- a/src/hotspot/os/bsd/os_bsd.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/os/bsd/os_bsd.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -2005,6 +2005,10 @@
return 0;
}
+int os::numa_get_group_id_for_address(const void* address) {
+ return 0;
+}
+
bool os::get_page_info(char *start, page_info* info) {
return false;
}
--- a/src/hotspot/os/linux/os_linux.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/os/linux/os_linux.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -3007,6 +3007,19 @@
return 0;
}
+int os::numa_get_group_id_for_address(const void* address) {
+ void** pages = const_cast<void**>(&address);
+ int id = -1;
+
+ if (os::Linux::numa_move_pages(0, 1, pages, NULL, &id, 0) == -1) {
+ return -1;
+ }
+ if (id < 0) {
+ return -1;
+ }
+ return id;
+}
+
int os::Linux::get_existing_num_nodes() {
int node;
int highest_node_number = Linux::numa_max_node();
@@ -3135,6 +3148,8 @@
libnuma_v2_dlsym(handle, "numa_get_membind")));
set_numa_get_interleave_mask(CAST_TO_FN_PTR(numa_get_interleave_mask_func_t,
libnuma_v2_dlsym(handle, "numa_get_interleave_mask")));
+ set_numa_move_pages(CAST_TO_FN_PTR(numa_move_pages_func_t,
+ libnuma_dlsym(handle, "numa_move_pages")));
if (numa_available() != -1) {
set_numa_all_nodes((unsigned long*)libnuma_dlsym(handle, "numa_all_nodes"));
@@ -3269,6 +3284,7 @@
os::Linux::numa_distance_func_t os::Linux::_numa_distance;
os::Linux::numa_get_membind_func_t os::Linux::_numa_get_membind;
os::Linux::numa_get_interleave_mask_func_t os::Linux::_numa_get_interleave_mask;
+os::Linux::numa_move_pages_func_t os::Linux::_numa_move_pages;
os::Linux::NumaAllocationPolicy os::Linux::_current_numa_policy;
unsigned long* os::Linux::_numa_all_nodes;
struct bitmask* os::Linux::_numa_all_nodes_ptr;
--- a/src/hotspot/os/linux/os_linux.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/os/linux/os_linux.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -216,6 +216,7 @@
typedef void (*numa_interleave_memory_v2_func_t)(void *start, size_t size, struct bitmask* mask);
typedef struct bitmask* (*numa_get_membind_func_t)(void);
typedef struct bitmask* (*numa_get_interleave_mask_func_t)(void);
+ typedef long (*numa_move_pages_func_t)(int pid, unsigned long count, void **pages, const int *nodes, int *status, int flags);
typedef void (*numa_set_bind_policy_func_t)(int policy);
typedef int (*numa_bitmask_isbitset_func_t)(struct bitmask *bmp, unsigned int n);
@@ -234,6 +235,7 @@
static numa_distance_func_t _numa_distance;
static numa_get_membind_func_t _numa_get_membind;
static numa_get_interleave_mask_func_t _numa_get_interleave_mask;
+ static numa_move_pages_func_t _numa_move_pages;
static unsigned long* _numa_all_nodes;
static struct bitmask* _numa_all_nodes_ptr;
static struct bitmask* _numa_nodes_ptr;
@@ -253,6 +255,7 @@
static void set_numa_distance(numa_distance_func_t func) { _numa_distance = func; }
static void set_numa_get_membind(numa_get_membind_func_t func) { _numa_get_membind = func; }
static void set_numa_get_interleave_mask(numa_get_interleave_mask_func_t func) { _numa_get_interleave_mask = func; }
+ static void set_numa_move_pages(numa_move_pages_func_t func) { _numa_move_pages = func; }
static void set_numa_all_nodes(unsigned long* ptr) { _numa_all_nodes = ptr; }
static void set_numa_all_nodes_ptr(struct bitmask **ptr) { _numa_all_nodes_ptr = (ptr == NULL ? NULL : *ptr); }
static void set_numa_nodes_ptr(struct bitmask **ptr) { _numa_nodes_ptr = (ptr == NULL ? NULL : *ptr); }
@@ -318,6 +321,9 @@
static int numa_distance(int node1, int node2) {
return _numa_distance != NULL ? _numa_distance(node1, node2) : -1;
}
+ static long numa_move_pages(int pid, unsigned long count, void **pages, const int *nodes, int *status, int flags) {
+ return _numa_move_pages != NULL ? _numa_move_pages(pid, count, pages, nodes, status, flags) : -1;
+ }
static int get_node_by_cpu(int cpu_id);
static int get_existing_num_nodes();
// Check if numa node is configured (non-zero memory node).
--- a/src/hotspot/os/solaris/os_solaris.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/os/solaris/os_solaris.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -2072,7 +2072,7 @@
char *res = Solaris::mmap_chunk(addr, size, MAP_PRIVATE|MAP_FIXED, prot);
if (res != NULL) {
if (UseNUMAInterleaving) {
- numa_make_global(addr, bytes);
+ numa_make_global(addr, bytes);
}
return 0;
}
@@ -2267,6 +2267,10 @@
return ids[os::random() % r];
}
+int os::numa_get_group_id_for_address(const void* address) {
+ return 0;
+}
+
// Request information about the page.
bool os::get_page_info(char *start, page_info* info) {
const uint_t info_types[] = { MEMINFO_VLGRP, MEMINFO_VPAGESIZE };
--- a/src/hotspot/os/windows/os_windows.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/os/windows/os_windows.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -3447,6 +3447,10 @@
}
}
+int os::numa_get_group_id_for_address(const void* address) {
+ return 0;
+}
+
bool os::get_page_info(char *start, page_info* info) {
return false;
}
@@ -5688,7 +5692,7 @@
// up the offset from FS of the thread pointer.
void os::win32::initialize_thread_ptr_offset() {
os::os_exception_wrapper((java_call_t)call_wrapper_dummy,
- NULL, NULL, NULL, NULL);
+ NULL, methodHandle(), NULL, NULL);
}
bool os::supports_map_sync() {
--- a/src/hotspot/share/aot/aotCompiledMethod.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/aot/aotCompiledMethod.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -160,8 +160,6 @@
}
bool AOTCompiledMethod::make_not_entrant_helper(int new_state) {
- // Make sure the method is not flushed in case of a safepoint in code below.
- methodHandle the_method(method());
NoSafepointVerifier nsv;
{
@@ -208,10 +206,7 @@
bool AOTCompiledMethod::make_entrant() {
assert(!method()->is_old(), "reviving evolved method!");
- // Make sure the method is not flushed in case of a safepoint in code below.
- methodHandle the_method(method());
NoSafepointVerifier nsv;
-
{
// Enter critical section. Does not block for safepoint.
MutexLocker pl(CompiledMethod_lock, Mutex::_no_safepoint_check_flag);
--- a/src/hotspot/share/c1/c1_Runtime1.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/c1/c1_Runtime1.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1422,7 +1422,7 @@
assert (nm != NULL, "no more nmethod?");
nm->make_not_entrant();
- methodHandle m(nm->method());
+ methodHandle m(thread, nm->method());
MethodData* mdo = m->method_data();
if (mdo == NULL && !HAS_PENDING_EXCEPTION) {
@@ -1443,7 +1443,7 @@
if (TracePredicateFailedTraps) {
stringStream ss1, ss2;
vframeStream vfst(thread);
- methodHandle inlinee = methodHandle(vfst.method());
+ Method* inlinee = vfst.method();
inlinee->print_short_name(&ss1);
m->print_short_name(&ss2);
tty->print_cr("Predicate failed trap in method %s at bci %d inlined in %s at pc " INTPTR_FORMAT, ss1.as_string(), vfst.bci(), ss2.as_string(), p2i(caller_frame.pc()));
--- a/src/hotspot/share/ci/ciEnv.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/ci/ciEnv.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -760,7 +760,7 @@
InstanceKlass* accessor_klass = accessor->get_instanceKlass();
Klass* holder_klass = holder->get_Klass();
- methodHandle dest_method;
+ Method* dest_method;
LinkInfo link_info(holder_klass, name, sig, accessor_klass, LinkInfo::needs_access_check, tag);
switch (bc) {
case Bytecodes::_invokestatic:
@@ -782,7 +782,7 @@
default: ShouldNotReachHere();
}
- return dest_method();
+ return dest_method;
}
--- a/src/hotspot/share/ci/ciExceptionHandler.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/ci/ciExceptionHandler.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, 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,7 @@
if (_catch_klass == NULL) {
bool will_link;
assert(_loading_klass->get_instanceKlass()->is_linked(), "must be linked before accessing constant pool");
- constantPoolHandle cpool(_loading_klass->get_instanceKlass()->constants());
+ constantPoolHandle cpool(THREAD, _loading_klass->get_instanceKlass()->constants());
ciKlass* k = CURRENT_ENV->get_klass_by_index(cpool,
_catch_klass_index,
will_link,
--- a/src/hotspot/share/ci/ciField.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/ci/ciField.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -391,7 +391,7 @@
LinkInfo link_info(_holder->get_instanceKlass(),
_name->get_symbol(), _signature->get_symbol(),
- accessing_method->get_Method());
+ methodHandle(THREAD, accessing_method->get_Method()));
fieldDescriptor result;
LinkResolver::resolve_field(result, link_info, bc, false, KILL_COMPILE_ON_FATAL_(false));
--- a/src/hotspot/share/ci/ciInstanceKlass.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/ci/ciInstanceKlass.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -32,7 +32,7 @@
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
#include "oops/oop.inline.hpp"
-#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/jniHandles.inline.hpp"
--- a/src/hotspot/share/ci/ciMethod.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/ci/ciMethod.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -72,25 +72,25 @@
assert(h_m() != NULL, "no null method");
if (LogTouchedMethods) {
- h_m()->log_touched(Thread::current());
+ h_m->log_touched(Thread::current());
}
// These fields are always filled in in loaded methods.
- _flags = ciFlags(h_m()->access_flags());
+ _flags = ciFlags(h_m->access_flags());
// Easy to compute, so fill them in now.
- _max_stack = h_m()->max_stack();
- _max_locals = h_m()->max_locals();
- _code_size = h_m()->code_size();
- _intrinsic_id = h_m()->intrinsic_id();
- _handler_count = h_m()->exception_table_length();
- _size_of_parameters = h_m()->size_of_parameters();
- _uses_monitors = h_m()->access_flags().has_monitor_bytecodes();
- _balanced_monitors = !_uses_monitors || h_m()->access_flags().is_monitor_matching();
- _is_c1_compilable = !h_m()->is_not_c1_compilable();
- _is_c2_compilable = !h_m()->is_not_c2_compilable();
+ _max_stack = h_m->max_stack();
+ _max_locals = h_m->max_locals();
+ _code_size = h_m->code_size();
+ _intrinsic_id = h_m->intrinsic_id();
+ _handler_count = h_m->exception_table_length();
+ _size_of_parameters = h_m->size_of_parameters();
+ _uses_monitors = h_m->access_flags().has_monitor_bytecodes();
+ _balanced_monitors = !_uses_monitors || h_m->access_flags().is_monitor_matching();
+ _is_c1_compilable = !h_m->is_not_c1_compilable();
+ _is_c2_compilable = !h_m->is_not_c2_compilable();
_can_be_parsed = true;
- _has_reserved_stack_access = h_m()->has_reserved_stack_access();
- _is_overpass = h_m()->is_overpass();
+ _has_reserved_stack_access = h_m->has_reserved_stack_access();
+ _is_overpass = h_m->is_overpass();
// Lazy fields, filled in on demand. Require allocation.
_code = NULL;
_exception_handlers = NULL;
@@ -114,8 +114,8 @@
DEBUG_ONLY(CompilerThread::current()->check_possible_safepoint());
}
- if (h_m()->method_holder()->is_linked()) {
- _can_be_statically_bound = h_m()->can_be_statically_bound();
+ if (h_m->method_holder()->is_linked()) {
+ _can_be_statically_bound = h_m->can_be_statically_bound();
} else {
// Have to use a conservative value in this case.
_can_be_statically_bound = false;
@@ -123,25 +123,25 @@
// Adjust the definition of this condition to be more useful:
// %%% take these conditions into account in vtable generation
- if (!_can_be_statically_bound && h_m()->is_private())
+ if (!_can_be_statically_bound && h_m->is_private())
_can_be_statically_bound = true;
- if (_can_be_statically_bound && h_m()->is_abstract())
+ if (_can_be_statically_bound && h_m->is_abstract())
_can_be_statically_bound = false;
// generating _signature may allow GC and therefore move m.
// These fields are always filled in.
- _name = env->get_symbol(h_m()->name());
- ciSymbol* sig_symbol = env->get_symbol(h_m()->signature());
- constantPoolHandle cpool = h_m()->constants();
+ _name = env->get_symbol(h_m->name());
+ ciSymbol* sig_symbol = env->get_symbol(h_m->signature());
+ constantPoolHandle cpool(Thread::current(), h_m->constants());
_signature = new (env->arena()) ciSignature(_holder, cpool, sig_symbol);
_method_data = NULL;
- _nmethod_age = h_m()->nmethod_age();
+ _nmethod_age = h_m->nmethod_age();
// Take a snapshot of these values, so they will be commensurate with the MDO.
if (ProfileInterpreter || TieredCompilation) {
- int invcnt = h_m()->interpreter_invocation_count();
+ int invcnt = h_m->interpreter_invocation_count();
// if the value overflowed report it as max int
_interpreter_invocation_count = invcnt < 0 ? max_jint : invcnt ;
- _interpreter_throwout_count = h_m()->interpreter_throwout_count();
+ _interpreter_throwout_count = h_m->interpreter_throwout_count();
} else {
_interpreter_invocation_count = 0;
_interpreter_throwout_count = 0;
@@ -431,7 +431,7 @@
ResourceBitMap ciMethod::live_local_oops_at_bci(int bci) {
VM_ENTRY_MARK;
InterpreterOopMap mask;
- OopMapCache::compute_one_oop_map(get_Method(), bci, &mask);
+ OopMapCache::compute_one_oop_map(methodHandle(THREAD, get_Method()), bci, &mask);
int mask_size = max_locals();
ResourceBitMap result(mask_size);
int i;
@@ -749,8 +749,8 @@
{
MutexLocker locker(Compile_lock);
Klass* context = actual_recv->get_Klass();
- target = Dependencies::find_unique_concrete_method(context,
- root_m->get_Method());
+ target = methodHandle(THREAD, Dependencies::find_unique_concrete_method(context,
+ root_m->get_Method()));
// %%% Should upgrade this ciMethod API to look for 1 or 2 concrete methods.
}
@@ -810,7 +810,7 @@
LinkInfo link_info(resolved, h_name, h_signature, caller_klass,
check_access ? LinkInfo::needs_access_check : LinkInfo::skip_access_check);
- methodHandle m;
+ Method* m = NULL;
// Only do exact lookup if receiver klass has been linked. Otherwise,
// the vtable has not been setup, and the LinkResolver will fail.
if (recv->is_array_klass()
@@ -823,14 +823,14 @@
}
}
- if (m.is_null()) {
+ if (m == NULL) {
// Return NULL only if there was a problem with lookup (uninitialized class, etc.)
return NULL;
}
ciMethod* result = this;
- if (m() != get_Method()) {
- result = CURRENT_THREAD_ENV->get_method(m());
+ if (m != get_Method()) {
+ result = CURRENT_THREAD_ENV->get_method(m);
}
// Don't return abstract methods because they aren't
@@ -1035,7 +1035,8 @@
bool result = true;
if (_method_data == NULL || _method_data->is_empty()) {
GUARDED_VM_ENTRY({
- result = ensure_method_data(get_Method());
+ methodHandle mh(Thread::current(), get_Method());
+ result = ensure_method_data(mh);
});
}
return result;
@@ -1268,7 +1269,7 @@
HandleMark hm(THREAD);
constantPoolHandle pool (THREAD, get_Method()->constants());
Bytecodes::Code code = (is_static ? Bytecodes::_invokestatic : Bytecodes::_invokevirtual);
- methodHandle spec_method = LinkResolver::resolve_method_statically(code, pool, refinfo_index, THREAD);
+ Method* spec_method = LinkResolver::resolve_method_statically(code, pool, refinfo_index, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
return false;
--- a/src/hotspot/share/ci/ciReplay.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/ci/ciReplay.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -597,7 +597,7 @@
nm->make_not_entrant();
}
replay_state = this;
- CompileBroker::compile_method(method, entry_bci, comp_level,
+ CompileBroker::compile_method(methodHandle(THREAD, method), entry_bci, comp_level,
methodHandle(), 0, CompileTask::Reason_Replay, THREAD);
replay_state = NULL;
reset();
@@ -634,7 +634,7 @@
MutexLocker ml(MethodData_lock, THREAD);
if (method->method_data() == NULL) {
ClassLoaderData* loader_data = method->method_holder()->class_loader_data();
- MethodData* method_data = MethodData::allocate(loader_data, method, CHECK);
+ MethodData* method_data = MethodData::allocate(loader_data, methodHandle(THREAD, method), CHECK);
method->set_method_data(method_data);
}
}
--- a/src/hotspot/share/ci/ciStreams.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/ci/ciStreams.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -186,7 +186,7 @@
// or checkcast, get the referenced klass.
ciKlass* ciBytecodeStream::get_klass(bool& will_link) {
VM_ENTRY_MARK;
- constantPoolHandle cpool(_method->get_Method()->constants());
+ constantPoolHandle cpool(THREAD, _method->get_Method()->constants());
return CURRENT_ENV->get_klass_by_index(cpool, get_klass_index(), will_link, _holder);
}
@@ -217,7 +217,7 @@
int index = get_constant_raw_index();
if (has_cache_index()) {
VM_ENTRY_MARK;
- constantPoolHandle cpool(_method->get_Method()->constants());
+ constantPoolHandle cpool(THREAD, _method->get_Method()->constants());
return cpool->object_to_cp_index(index);
}
return index;
@@ -236,7 +236,7 @@
pool_index = -1;
}
VM_ENTRY_MARK;
- constantPoolHandle cpool(_method->get_Method()->constants());
+ constantPoolHandle cpool(THREAD, _method->get_Method()->constants());
return CURRENT_ENV->get_constant_by_index(cpool, pool_index, cache_index, _holder);
}
@@ -289,7 +289,7 @@
// for checking linkability when retrieving the associated field.
ciInstanceKlass* ciBytecodeStream::get_declared_field_holder() {
VM_ENTRY_MARK;
- constantPoolHandle cpool(_method->get_Method()->constants());
+ constantPoolHandle cpool(THREAD, _method->get_Method()->constants());
int holder_index = get_field_holder_index();
bool ignore;
return CURRENT_ENV->get_klass_by_index(cpool, holder_index, ignore, _holder)
@@ -431,7 +431,7 @@
// constant pool cache at the current bci.
bool ciBytecodeStream::has_appendix() {
VM_ENTRY_MARK;
- constantPoolHandle cpool(_method->get_Method()->constants());
+ constantPoolHandle cpool(THREAD, _method->get_Method()->constants());
return ConstantPool::has_appendix_at_if_loaded(cpool, get_method_index());
}
@@ -442,7 +442,7 @@
// the current bci.
ciObject* ciBytecodeStream::get_appendix() {
VM_ENTRY_MARK;
- constantPoolHandle cpool(_method->get_Method()->constants());
+ constantPoolHandle cpool(THREAD, _method->get_Method()->constants());
oop appendix_oop = ConstantPool::appendix_at_if_loaded(cpool, get_method_index());
return CURRENT_ENV->get_object(appendix_oop);
}
@@ -454,7 +454,7 @@
// pool cache at the current bci has a local signature.
bool ciBytecodeStream::has_local_signature() {
GUARDED_VM_ENTRY(
- constantPoolHandle cpool(_method->get_Method()->constants());
+ constantPoolHandle cpool(Thread::current(), _method->get_Method()->constants());
return ConstantPool::has_local_signature_at_if_loaded(cpool, get_method_index());
)
}
@@ -472,7 +472,7 @@
// for checking linkability when retrieving the associated method.
ciKlass* ciBytecodeStream::get_declared_method_holder() {
VM_ENTRY_MARK;
- constantPoolHandle cpool(_method->get_Method()->constants());
+ constantPoolHandle cpool(THREAD, _method->get_Method()->constants());
bool ignore;
// report as MethodHandle for invokedynamic, which is syntactically classless
if (cur_bc() == Bytecodes::_invokedynamic)
--- a/src/hotspot/share/classfile/bytecodeAssembler.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/classfile/bytecodeAssembler.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -54,7 +54,8 @@
_orig->length() + _entries.length(), CHECK_NULL);
cp->set_pool_holder(_orig->pool_holder());
- _orig->copy_cp_to(1, _orig->length() - 1, cp, 1, CHECK_NULL);
+ constantPoolHandle cp_h(THREAD, cp);
+ _orig->copy_cp_to(1, _orig->length() - 1, cp_h, 1, CHECK_NULL);
// Preserve dynamic constant information from the original pool
if (_orig->has_dynamic_constant()) {
--- a/src/hotspot/share/classfile/classFileParser.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/classfile/classFileParser.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -47,7 +47,7 @@
#include "memory/universe.hpp"
#include "oops/annotations.hpp"
#include "oops/constantPool.inline.hpp"
-#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/instanceMirrorKlass.hpp"
#include "oops/klass.inline.hpp"
@@ -332,7 +332,7 @@
hashValues[names_count++] = hash;
if (names_count == SymbolTable::symbol_alloc_batch_size) {
SymbolTable::new_symbols(_loader_data,
- cp,
+ constantPoolHandle(THREAD, cp),
names_count,
names,
lengths,
@@ -369,7 +369,7 @@
// Allocate the remaining symbols
if (names_count > 0) {
SymbolTable::new_symbols(_loader_data,
- cp,
+ constantPoolHandle(THREAD, cp),
names_count,
names,
lengths,
@@ -2870,7 +2870,7 @@
}
if (parsed_annotations.has_any_annotations())
- parsed_annotations.apply_to(m);
+ parsed_annotations.apply_to(methodHandle(THREAD, m));
// Copy annotations
copy_method_annotations(m->constMethod(),
@@ -3753,7 +3753,7 @@
#ifndef PRODUCT
static void print_field_layout(const Symbol* name,
Array<u2>* fields,
- const constantPoolHandle& cp,
+ ConstantPool* cp,
int instance_size,
int instance_fields_start,
int instance_fields_end,
--- a/src/hotspot/share/classfile/javaClasses.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/classfile/javaClasses.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -42,7 +42,7 @@
#include "memory/oopFactory.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
-#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/instanceMirrorKlass.hpp"
#include "oops/klass.hpp"
@@ -2270,7 +2270,7 @@
st->print_cr("%s", buf);
}
-void java_lang_Throwable::print_stack_element(outputStream *st, const methodHandle& method, int bci) {
+void java_lang_Throwable::print_stack_element(outputStream *st, Method* method, int bci) {
Handle mirror (Thread::current(), method->method_holder()->java_mirror());
int method_id = method->orig_method_idnum();
int version = method->constants()->version();
@@ -2376,7 +2376,6 @@
// trace as utilizing vframe.
#ifdef ASSERT
vframeStream st(thread);
- methodHandle st_method(THREAD, st.method());
#endif
int total_count = 0;
RegisterMap map(thread, false);
@@ -2426,14 +2425,9 @@
}
}
#ifdef ASSERT
- assert(st_method() == method && st.bci() == bci,
+ assert(st.method() == method && st.bci() == bci,
"Wrong stack trace");
st.next();
- // vframeStream::method isn't GC-safe so store off a copy
- // of the Method* in case we GC.
- if (!st.at_end()) {
- st_method = st.method();
- }
#endif
// the format of the stacktrace will be:
@@ -2696,7 +2690,7 @@
}
java_lang_StackTraceElement::set_fileName(element(), source_file);
- int line_number = Backtrace::get_line_number(method, bci);
+ int line_number = Backtrace::get_line_number(method(), bci);
java_lang_StackTraceElement::set_lineNumber(element(), line_number);
}
}
@@ -2771,7 +2765,8 @@
short version = stackFrame->short_field(_version_offset);
int bci = stackFrame->int_field(_bci_offset);
Symbol* name = method->name();
- java_lang_StackTraceElement::fill_in(stack_trace_element, holder, method, version, bci, name, CHECK);
+ java_lang_StackTraceElement::fill_in(stack_trace_element, holder, methodHandle(THREAD, method),
+ version, bci, name, CHECK);
}
#define STACKFRAMEINFO_FIELDS_DO(macro) \
--- a/src/hotspot/share/classfile/javaClasses.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/classfile/javaClasses.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -561,7 +561,7 @@
static oop message(oop throwable);
static void set_message(oop throwable, oop value);
static Symbol* detail_message(oop throwable);
- static void print_stack_element(outputStream *st, const methodHandle& method, int bci);
+ static void print_stack_element(outputStream *st, Method* method, int bci);
static void print_stack_usage(Handle stream);
static void compute_offsets();
@@ -1402,7 +1402,7 @@
static int version_at(unsigned int merged);
static int mid_at(unsigned int merged);
static int cpref_at(unsigned int merged);
- static int get_line_number(const methodHandle& method, int bci);
+ static int get_line_number(Method* method, int bci);
static Symbol* get_source_file_name(InstanceKlass* holder, int version);
// Debugging
--- a/src/hotspot/share/classfile/javaClasses.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/classfile/javaClasses.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -264,7 +264,7 @@
return extract_low_short_from_int(merged);
}
-inline int Backtrace::get_line_number(const methodHandle& method, int bci) {
+inline int Backtrace::get_line_number(Method* method, int bci) {
int line_number = 0;
if (method->is_native()) {
// Negative value different from -1 below, enabling Java code in
--- a/src/hotspot/share/classfile/systemDictionary.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/classfile/systemDictionary.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -2338,9 +2338,9 @@
}
-methodHandle SystemDictionary::find_method_handle_intrinsic(vmIntrinsics::ID iid,
- Symbol* signature,
- TRAPS) {
+Method* SystemDictionary::find_method_handle_intrinsic(vmIntrinsics::ID iid,
+ Symbol* signature,
+ TRAPS) {
methodHandle empty;
assert(MethodHandles::is_signature_polymorphic(iid) &&
MethodHandles::is_signature_polymorphic_intrinsic(iid) &&
@@ -2354,14 +2354,14 @@
if (spe == NULL || spe->method() == NULL) {
spe = NULL;
// Must create lots of stuff here, but outside of the SystemDictionary lock.
- m = Method::make_method_handle_intrinsic(iid, signature, CHECK_(empty));
+ m = Method::make_method_handle_intrinsic(iid, signature, CHECK_NULL);
if (!Arguments::is_interpreter_only()) {
// Generate a compiled form of the MH intrinsic.
AdapterHandlerLibrary::create_native_wrapper(m);
// Check if have the compiled code.
if (!m->has_compiled_code()) {
- THROW_MSG_(vmSymbols::java_lang_VirtualMachineError(),
- "Out of space in CodeCache for method handle intrinsic", empty);
+ THROW_MSG_NULL(vmSymbols::java_lang_VirtualMachineError(),
+ "Out of space in CodeCache for method handle intrinsic");
}
}
// Now grab the lock. We might have to throw away the new method,
@@ -2384,12 +2384,11 @@
}
// Helper for unpacking the return value from linkMethod and linkCallSite.
-static methodHandle unpack_method_and_appendix(Handle mname,
- Klass* accessing_klass,
- objArrayHandle appendix_box,
- Handle* appendix_result,
- TRAPS) {
- methodHandle empty;
+static Method* unpack_method_and_appendix(Handle mname,
+ Klass* accessing_klass,
+ objArrayHandle appendix_box,
+ Handle* appendix_result,
+ TRAPS) {
if (mname.not_null()) {
Method* m = java_lang_invoke_MemberName::vmtarget(mname());
if (m != NULL) {
@@ -2407,35 +2406,34 @@
// the target is stored in the cpCache and if a reference to this
// MemberName is dropped we need a way to make sure the
// class_loader containing this method is kept alive.
+ methodHandle mh(THREAD, m); // record_dependency can safepoint.
ClassLoaderData* this_key = accessing_klass->class_loader_data();
this_key->record_dependency(m->method_holder());
- return methodHandle(THREAD, m);
+ return mh();
}
}
- THROW_MSG_(vmSymbols::java_lang_LinkageError(), "bad value from MethodHandleNatives", empty);
- return empty;
+ THROW_MSG_NULL(vmSymbols::java_lang_LinkageError(), "bad value from MethodHandleNatives");
}
-methodHandle SystemDictionary::find_method_handle_invoker(Klass* klass,
- Symbol* name,
- Symbol* signature,
- Klass* accessing_klass,
- Handle *appendix_result,
- TRAPS) {
- methodHandle empty;
+Method* SystemDictionary::find_method_handle_invoker(Klass* klass,
+ Symbol* name,
+ Symbol* signature,
+ Klass* accessing_klass,
+ Handle *appendix_result,
+ TRAPS) {
assert(THREAD->can_call_java() ,"");
Handle method_type =
- SystemDictionary::find_method_handle_type(signature, accessing_klass, CHECK_(empty));
+ SystemDictionary::find_method_handle_type(signature, accessing_klass, CHECK_NULL);
int ref_kind = JVM_REF_invokeVirtual;
- oop name_oop = StringTable::intern(name, CHECK_(empty));
+ oop name_oop = StringTable::intern(name, CHECK_NULL);
Handle name_str (THREAD, name_oop);
- objArrayHandle appendix_box = oopFactory::new_objArray_handle(SystemDictionary::Object_klass(), 1, CHECK_(empty));
+ objArrayHandle appendix_box = oopFactory::new_objArray_handle(SystemDictionary::Object_klass(), 1, CHECK_NULL);
assert(appendix_box->obj_at(0) == NULL, "");
// This should not happen. JDK code should take care of that.
if (accessing_klass == NULL || method_type.is_null()) {
- THROW_MSG_(vmSymbols::java_lang_InternalError(), "bad invokehandle", empty);
+ THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "bad invokehandle");
}
// call java.lang.invoke.MethodHandleNatives::linkMethod(... String, MethodType) -> MemberName
@@ -2451,7 +2449,7 @@
SystemDictionary::MethodHandleNatives_klass(),
vmSymbols::linkMethod_name(),
vmSymbols::linkMethod_signature(),
- &args, CHECK_(empty));
+ &args, CHECK_NULL);
Handle mname(THREAD, (oop) result.get_jobject());
return unpack_method_and_appendix(mname, accessing_klass, appendix_box, appendix_result, THREAD);
}
@@ -2755,11 +2753,12 @@
Handle value(THREAD, (oop) result.get_jobject());
if (is_indy) {
Handle appendix;
- methodHandle method = unpack_method_and_appendix(value,
- bootstrap_specifier.caller(),
- appendix_box,
- &appendix, CHECK);
- bootstrap_specifier.set_resolved_method(method, appendix);
+ Method* method = unpack_method_and_appendix(value,
+ bootstrap_specifier.caller(),
+ appendix_box,
+ &appendix, CHECK);
+ methodHandle mh(THREAD, method);
+ bootstrap_specifier.set_resolved_method(mh, appendix);
} else {
bootstrap_specifier.set_resolved_value(value);
}
--- a/src/hotspot/share/classfile/systemDictionary.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/classfile/systemDictionary.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -464,17 +464,17 @@
// JSR 292
// find a java.lang.invoke.MethodHandle.invoke* method for a given signature
// (asks Java to compute it if necessary, except in a compiler thread)
- static methodHandle find_method_handle_invoker(Klass* klass,
- Symbol* name,
- Symbol* signature,
- Klass* accessing_klass,
- Handle *appendix_result,
- TRAPS);
+ static Method* find_method_handle_invoker(Klass* klass,
+ Symbol* name,
+ Symbol* signature,
+ Klass* accessing_klass,
+ Handle *appendix_result,
+ TRAPS);
// for a given signature, find the internal MethodHandle method (linkTo* or invokeBasic)
// (does not ask Java, since this is a low-level intrinsic defined by the JVM)
- static methodHandle find_method_handle_intrinsic(vmIntrinsics::ID iid,
- Symbol* signature,
- TRAPS);
+ static Method* find_method_handle_intrinsic(vmIntrinsics::ID iid,
+ Symbol* signature,
+ TRAPS);
// compute java_mirror (java.lang.Class instance) for a type ("I", "[[B", "LFoo;", etc.)
// Either the accessing_klass or the CL/PD can be non-null, but not both.
--- a/src/hotspot/share/classfile/vmSymbols.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/classfile/vmSymbols.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1069,19 +1069,18 @@
const char* declared_name = name_at(declared_id);
const char* actual_name = name_at(actual_id);
- methodHandle mh = m;
m = NULL;
ttyLocker ttyl;
if (xtty != NULL) {
xtty->begin_elem("intrinsic_misdeclared actual='%s' declared='%s'",
actual_name, declared_name);
- xtty->method(mh);
+ xtty->method(m);
xtty->end_elem("%s", "");
}
if (PrintMiscellaneous && (WizardMode || Verbose)) {
tty->print_cr("*** misidentified method; %s(%d) should be %s(%d):",
declared_name, declared_id, actual_name, actual_id);
- mh()->print_short_name(tty);
+ m->print_short_name(tty);
tty->cr();
}
}
--- a/src/hotspot/share/code/compiledIC.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/code/compiledIC.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -287,7 +287,7 @@
if (TraceICs) {
ResourceMark rm;
- assert(!call_info->selected_method().is_null(), "Unexpected null selected method");
+ assert(call_info->selected_method() != NULL, "Unexpected null selected method");
tty->print_cr ("IC@" INTPTR_FORMAT ": to megamorphic %s entry: " INTPTR_FORMAT,
p2i(instruction_address()), call_info->selected_method()->print_value_string(), p2i(entry));
}
--- a/src/hotspot/share/code/compiledMethod.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/code/compiledMethod.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -355,7 +355,7 @@
if (method() != NULL && !method()->is_native()) {
address pc = fr.pc();
SimpleScopeDesc ssd(this, pc);
- Bytecode_invoke call(ssd.method(), ssd.bci());
+ Bytecode_invoke call(methodHandle(Thread::current(), ssd.method()), ssd.bci());
bool has_receiver = call.has_receiver();
bool has_appendix = call.has_appendix();
Symbol* signature = call.signature();
--- a/src/hotspot/share/code/nmethod.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/code/nmethod.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -964,15 +964,16 @@
#if defined(SUPPORT_DATA_STRUCTS)
if (AbstractDisassembler::show_structs()) {
- if (printmethod || PrintDebugInfo || CompilerOracle::has_option_string(_method, "PrintDebugInfo")) {
+ methodHandle mh(Thread::current(), _method);
+ if (printmethod || PrintDebugInfo || CompilerOracle::has_option_string(mh, "PrintDebugInfo")) {
print_scopes();
tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
}
- if (printmethod || PrintRelocations || CompilerOracle::has_option_string(_method, "PrintRelocations")) {
+ if (printmethod || PrintRelocations || CompilerOracle::has_option_string(mh, "PrintRelocations")) {
print_relocations();
tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
}
- if (printmethod || PrintDependencies || CompilerOracle::has_option_string(_method, "PrintDependencies")) {
+ if (printmethod || PrintDependencies || CompilerOracle::has_option_string(mh, "PrintDependencies")) {
print_dependencies();
tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
}
@@ -1302,9 +1303,8 @@
return false;
}
- // Make sure neither the nmethod nor the method is flushed in case of a safepoint in code below.
+ // Make sure the nmethod is not flushed.
nmethodLocker nml(this);
- methodHandle the_method(method());
// This can be called while the system is already at a safepoint which is ok
NoSafepointVerifier nsv;
@@ -3079,13 +3079,13 @@
}
if (block_begin == entry_point()) {
- methodHandle m = method();
- if (m.not_null()) {
+ Method* m = method();
+ if (m != NULL) {
stream->print(" # ");
m->print_value_on(stream);
stream->cr();
}
- if (m.not_null() && !is_osr_method()) {
+ if (m != NULL && !is_osr_method()) {
ResourceMark rm;
int sizeargs = m->size_of_parameters();
BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, sizeargs);
@@ -3237,6 +3237,8 @@
}
assert(!oop_map_required, "missed oopmap");
+ Thread* thread = Thread::current();
+
// Print any debug info present at this pc.
ScopeDesc* sd = scope_desc_in(begin, end);
if (sd != NULL) {
@@ -3267,7 +3269,7 @@
case Bytecodes::_invokestatic:
case Bytecodes::_invokeinterface:
{
- Bytecode_invoke invoke(sd->method(), sd->bci());
+ Bytecode_invoke invoke(methodHandle(thread, sd->method()), sd->bci());
st->print(" ");
if (invoke.name() != NULL)
invoke.name()->print_symbol_on(st);
@@ -3280,7 +3282,7 @@
case Bytecodes::_getstatic:
case Bytecodes::_putstatic:
{
- Bytecode_field field(sd->method(), sd->bci());
+ Bytecode_field field(methodHandle(thread, sd->method()), sd->bci());
st->print(" ");
if (field.name() != NULL)
field.name()->print_symbol_on(st);
@@ -3356,7 +3358,7 @@
if (cm != NULL && cm->is_far_code()) {
// Temporary fix, see JDK-8143106
CompiledDirectStaticCall* csc = CompiledDirectStaticCall::at(instruction_address());
- csc->set_to_far(methodHandle(cm->method()), dest);
+ csc->set_to_far(methodHandle(Thread::current(), cm->method()), dest);
return;
}
}
--- a/src/hotspot/share/compiler/compileBroker.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/compiler/compileBroker.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -299,7 +299,7 @@
/**
* Check if a CompilerThread can be removed and update count if requested.
*/
-static bool can_remove(CompilerThread *ct, bool do_it) {
+bool CompileBroker::can_remove(CompilerThread *ct, bool do_it) {
assert(UseDynamicNumberOfCompilerThreads, "or shouldn't be here");
if (!ReduceNumberOfCompilerThreads) return false;
@@ -313,13 +313,32 @@
// Keep thread alive for at least some time.
if (ct->idle_time_millis() < (c1 ? 500 : 100)) return false;
+#if INCLUDE_JVMCI
+ if (compiler->is_jvmci()) {
+ // Handles for JVMCI thread objects may get released concurrently.
+ if (do_it) {
+ assert(CompileThread_lock->owner() == ct, "must be holding lock");
+ } else {
+ // Skip check if it's the last thread and let caller check again.
+ return true;
+ }
+ }
+#endif
+
// We only allow the last compiler thread of each type to get removed.
- jobject last_compiler = c1 ? CompileBroker::compiler1_object(compiler_count - 1)
- : CompileBroker::compiler2_object(compiler_count - 1);
+ jobject last_compiler = c1 ? compiler1_object(compiler_count - 1)
+ : compiler2_object(compiler_count - 1);
if (ct->threadObj() == JNIHandles::resolve_non_null(last_compiler)) {
if (do_it) {
assert_locked_or_safepoint(CompileThread_lock); // Update must be consistent.
compiler->set_num_compiler_threads(compiler_count - 1);
+#if INCLUDE_JVMCI
+ if (compiler->is_jvmci()) {
+ // Old j.l.Thread object can die when no longer referenced elsewhere.
+ JNIHandles::destroy_global(compiler2_object(compiler_count - 1));
+ _compiler2_objects[compiler_count - 1] = NULL;
+ }
+#endif
}
return true;
}
@@ -426,7 +445,7 @@
if (UseDynamicNumberOfCompilerThreads && _first == NULL) {
// Still nothing to compile. Give caller a chance to stop this thread.
- if (can_remove(CompilerThread::current(), false)) return NULL;
+ if (CompileBroker::can_remove(CompilerThread::current(), false)) return NULL;
}
}
@@ -446,8 +465,9 @@
if (task != NULL) {
// Save method pointers across unlock safepoint. The task is removed from
// the compilation queue, which is walked during RedefineClasses.
- save_method = methodHandle(task->method());
- save_hot_method = methodHandle(task->hot_method());
+ Thread* thread = Thread::current();
+ save_method = methodHandle(thread, task->method());
+ save_hot_method = methodHandle(thread, task->hot_method());
remove(task);
}
@@ -842,10 +862,15 @@
char name_buffer[256];
for (int i = 0; i < _c2_count; i++) {
+ jobject thread_handle = NULL;
+ // Create all j.l.Thread objects for C1 and C2 threads here, but only one
+ // for JVMCI compiler which can create further ones on demand.
+ JVMCI_ONLY(if (!UseJVMCICompiler || !UseDynamicNumberOfCompilerThreads || i == 0) {)
// Create a name for our thread.
sprintf(name_buffer, "%s CompilerThread%d", _compilers[1]->name(), i);
Handle thread_oop = create_thread_oop(name_buffer, CHECK);
- jobject thread_handle = JNIHandles::make_global(thread_oop);
+ thread_handle = JNIHandles::make_global(thread_oop);
+ JVMCI_ONLY(})
_compiler2_objects[i] = thread_handle;
_compiler2_logs[i] = NULL;
@@ -912,6 +937,39 @@
(int)(available_cc_np / (128*K)));
for (int i = old_c2_count; i < new_c2_count; i++) {
+#if INCLUDE_JVMCI
+ if (UseJVMCICompiler) {
+ // Native compiler threads as used in C1/C2 can reuse the j.l.Thread
+ // objects as their existence is completely hidden from the rest of
+ // the VM (and those compiler threads can't call Java code to do the
+ // creation anyway). For JVMCI we have to create new j.l.Thread objects
+ // as they are visible and we can see unexpected thread lifecycle
+ // transitions if we bind them to new JavaThreads.
+ if (!THREAD->can_call_java()) break;
+ char name_buffer[256];
+ sprintf(name_buffer, "%s CompilerThread%d", _compilers[1]->name(), i);
+ Handle thread_oop;
+ {
+ // We have to give up the lock temporarily for the Java calls.
+ MutexUnlocker mu(CompileThread_lock);
+ thread_oop = create_thread_oop(name_buffer, THREAD);
+ }
+ if (HAS_PENDING_EXCEPTION) {
+ if (TraceCompilerThreads) {
+ ResourceMark rm;
+ tty->print_cr("JVMCI compiler thread creation failed:");
+ PENDING_EXCEPTION->print();
+ }
+ CLEAR_PENDING_EXCEPTION;
+ break;
+ }
+ // Check if another thread has beaten us during the Java calls.
+ if (_compilers[1]->num_compiler_threads() != i) break;
+ jobject thread_handle = JNIHandles::make_global(thread_oop);
+ assert(compiler2_object(i) == NULL, "Old one must be released!");
+ _compiler2_objects[i] = thread_handle;
+ }
+#endif
JavaThread *ct = make_thread(compiler2_object(i), _c2_compile_queue, _compilers[1], CHECK);
if (ct == NULL) break;
_compilers[1]->set_num_compiler_threads(i + 1);
--- a/src/hotspot/share/compiler/compileBroker.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/compiler/compileBroker.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -395,6 +395,8 @@
return _compiler2_objects[idx];
}
+ static bool can_remove(CompilerThread *ct, bool do_it);
+
static CompileLog* get_log(CompilerThread* ct);
static int get_total_compile_count() { return _total_compile_count; }
--- a/src/hotspot/share/compiler/compileTask.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/compiler/compileTask.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -340,7 +340,7 @@
if (_osr_bci != CompileBroker::standard_entry_bci) {
log->print(" compile_kind='osr'"); // same as nmethod::compile_kind
} // else compile_kind='c2c'
- if (!method.is_null()) log->method(method);
+ if (!method.is_null()) log->method(method());
if (_osr_bci != CompileBroker::standard_entry_bci) {
log->print(" osr_bci='%d'", _osr_bci);
}
@@ -356,21 +356,16 @@
// ------------------------------------------------------------------
// CompileTask::log_task_queued
void CompileTask::log_task_queued() {
- Thread* thread = Thread::current();
ttyLocker ttyl;
- ResourceMark rm(thread);
+ ResourceMark rm;
xtty->begin_elem("task_queued");
log_task(xtty);
assert(_compile_reason > CompileTask::Reason_None && _compile_reason < CompileTask::Reason_Count, "Valid values");
xtty->print(" comment='%s'", reason_name(_compile_reason));
- if (_hot_method != NULL) {
- methodHandle hot(thread, _hot_method);
- methodHandle method(thread, _method);
- if (hot() != method()) {
- xtty->method(hot);
- }
+ if (_hot_method != NULL && _hot_method != _method) {
+ xtty->method(_hot_method);
}
if (_hot_count != 0) {
xtty->print(" hot_count='%d'", _hot_count);
--- a/src/hotspot/share/compiler/tieredThresholdPolicy.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/compiler/tieredThresholdPolicy.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -43,7 +43,7 @@
#include "c1/c1_Compiler.hpp"
#include "opto/c2compiler.hpp"
-bool TieredThresholdPolicy::call_predicate_helper(Method* method, CompLevel cur_level, int i, int b, double scale) {
+bool TieredThresholdPolicy::call_predicate_helper(const methodHandle& method, CompLevel cur_level, int i, int b, double scale) {
double threshold_scaling;
if (CompilerOracle::has_option_value(method, "CompileThresholdScaling", threshold_scaling)) {
scale *= threshold_scaling;
@@ -74,7 +74,7 @@
}
}
-bool TieredThresholdPolicy::loop_predicate_helper(Method* method, CompLevel cur_level, int i, int b, double scale) {
+bool TieredThresholdPolicy::loop_predicate_helper(const methodHandle& method, CompLevel cur_level, int i, int b, double scale) {
double threshold_scaling;
if (CompilerOracle::has_option_value(method, "CompileThresholdScaling", threshold_scaling)) {
scale *= threshold_scaling;
@@ -110,7 +110,7 @@
return false;
}
-bool TieredThresholdPolicy::force_comp_at_level_simple(Method* method) {
+bool TieredThresholdPolicy::force_comp_at_level_simple(const methodHandle& method) {
if (CompilationModeFlag::quick_internal()) {
#if INCLUDE_JVMCI
if (UseJVMCICompiler) {
@@ -132,10 +132,10 @@
return CompLevel_none;
}
-void TieredThresholdPolicy::print_counters(const char* prefix, const methodHandle& mh) {
- int invocation_count = mh->invocation_count();
- int backedge_count = mh->backedge_count();
- MethodData* mdh = mh->method_data();
+void TieredThresholdPolicy::print_counters(const char* prefix, Method* m) {
+ int invocation_count = m->invocation_count();
+ int backedge_count = m->backedge_count();
+ MethodData* mdh = m->method_data();
int mdo_invocations = 0, mdo_backedges = 0;
int mdo_invocations_start = 0, mdo_backedges_start = 0;
if (mdh != NULL) {
@@ -149,13 +149,13 @@
mdo_invocations, mdo_invocations_start,
mdo_backedges, mdo_backedges_start);
tty->print(" %smax levels=%d,%d", prefix,
- mh->highest_comp_level(), mh->highest_osr_comp_level());
+ m->highest_comp_level(), m->highest_osr_comp_level());
}
// Print an event.
-void TieredThresholdPolicy::print_event(EventType type, const methodHandle& mh, const methodHandle& imh,
+void TieredThresholdPolicy::print_event(EventType type, Method* m, Method* im,
int bci, CompLevel level) {
- bool inlinee_event = mh() != imh();
+ bool inlinee_event = m != im;
ttyLocker tty_lock;
tty->print("%lf: [", os::elapsedTime());
@@ -189,10 +189,10 @@
tty->print(" level=%d ", level);
ResourceMark rm;
- char *method_name = mh->name_and_sig_as_C_string();
+ char *method_name = m->name_and_sig_as_C_string();
tty->print("[%s", method_name);
if (inlinee_event) {
- char *inlinee_name = imh->name_and_sig_as_C_string();
+ char *inlinee_name = im->name_and_sig_as_C_string();
tty->print(" [%s]] ", inlinee_name);
}
else tty->print("] ");
@@ -200,39 +200,39 @@
CompileBroker::queue_size(CompLevel_full_optimization));
tty->print(" rate=");
- if (mh->prev_time() == 0) tty->print("n/a");
- else tty->print("%f", mh->rate());
+ if (m->prev_time() == 0) tty->print("n/a");
+ else tty->print("%f", m->rate());
tty->print(" k=%.2lf,%.2lf", threshold_scale(CompLevel_full_profile, Tier3LoadFeedback),
threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback));
if (type != COMPILE) {
- print_counters("", mh);
+ print_counters("", m);
if (inlinee_event) {
- print_counters("inlinee ", imh);
+ print_counters("inlinee ", im);
}
tty->print(" compilable=");
bool need_comma = false;
- if (!mh->is_not_compilable(CompLevel_full_profile)) {
+ if (!m->is_not_compilable(CompLevel_full_profile)) {
tty->print("c1");
need_comma = true;
}
- if (!mh->is_not_osr_compilable(CompLevel_full_profile)) {
+ if (!m->is_not_osr_compilable(CompLevel_full_profile)) {
if (need_comma) tty->print(",");
tty->print("c1-osr");
need_comma = true;
}
- if (!mh->is_not_compilable(CompLevel_full_optimization)) {
+ if (!m->is_not_compilable(CompLevel_full_optimization)) {
if (need_comma) tty->print(",");
tty->print("c2");
need_comma = true;
}
- if (!mh->is_not_osr_compilable(CompLevel_full_optimization)) {
+ if (!m->is_not_osr_compilable(CompLevel_full_optimization)) {
if (need_comma) tty->print(",");
tty->print("c2-osr");
}
tty->print(" status=");
- if (mh->queued_for_compilation()) {
+ if (m->queued_for_compilation()) {
tty->print("in-queue");
} else tty->print("idle");
}
@@ -376,12 +376,14 @@
max_method = max_task->method();
}
+ methodHandle max_method_h(Thread::current(), max_method);
+
if (max_task != NULL && max_task->comp_level() == CompLevel_full_profile &&
TieredStopAtLevel > CompLevel_full_profile &&
- max_method != NULL && is_method_profiled(max_method)) {
+ max_method != NULL && is_method_profiled(max_method_h)) {
max_task->set_comp_level(CompLevel_limited_profile);
- if (CompileBroker::compilation_is_complete(max_method, max_task->osr_bci(), CompLevel_limited_profile)) {
+ if (CompileBroker::compilation_is_complete(max_method_h, max_task->osr_bci(), CompLevel_limited_profile)) {
if (PrintTieredEvents) {
print_event(REMOVE_FROM_QUEUE, max_method, max_method, max_task->osr_bci(), (CompLevel)max_task->comp_level());
}
@@ -401,8 +403,7 @@
void TieredThresholdPolicy::reprofile(ScopeDesc* trap_scope, bool is_osr) {
for (ScopeDesc* sd = trap_scope;; sd = sd->sender()) {
if (PrintTieredEvents) {
- methodHandle mh(sd->method());
- print_event(REPROFILE, mh, mh, InvocationEntryBci, CompLevel_none);
+ print_event(REPROFILE, sd->method(), sd->method(), InvocationEntryBci, CompLevel_none);
}
MethodData* mdo = sd->method()->method_data();
if (mdo != NULL) {
@@ -430,7 +431,7 @@
}
if (PrintTieredEvents) {
- print_event(bci == InvocationEntryBci ? CALL : LOOP, method, inlinee, bci, comp_level);
+ print_event(bci == InvocationEntryBci ? CALL : LOOP, method(), inlinee(), bci, comp_level);
}
if (bci == InvocationEntryBci) {
@@ -481,7 +482,7 @@
if (level == CompLevel_aot) {
if (mh->has_aot_code()) {
if (PrintTieredEvents) {
- print_event(COMPILE, mh, mh, bci, level);
+ print_event(COMPILE, mh(), mh(), bci, level);
}
MutexLocker ml(Compile_lock);
NoSafepointVerifier nsv;
@@ -525,7 +526,7 @@
}
if (!CompileBroker::compilation_is_in_queue(mh)) {
if (PrintTieredEvents) {
- print_event(COMPILE, mh, mh, bci, level);
+ print_event(COMPILE, mh(), mh(), bci, level);
}
int hot_count = (bci == InvocationEntryBci) ? mh->invocation_count() : mh->backedge_count();
update_rate(os::javaTimeMillis(), mh());
@@ -610,7 +611,7 @@
}
// Is method profiled enough?
-bool TieredThresholdPolicy::is_method_profiled(Method* method) {
+bool TieredThresholdPolicy::is_method_profiled(const methodHandle& method) {
MethodData* mdo = method->method_data();
if (mdo != NULL) {
int i = mdo->invocation_count_delta();
@@ -647,7 +648,7 @@
// Tier?LoadFeedback is basically a coefficient that determines of
// how many methods per compiler thread can be in the queue before
// the threshold values double.
-bool TieredThresholdPolicy::loop_predicate(int i, int b, CompLevel cur_level, Method* method) {
+bool TieredThresholdPolicy::loop_predicate(int i, int b, CompLevel cur_level, const methodHandle& method) {
double k = 1;
switch(cur_level) {
case CompLevel_aot: {
@@ -672,10 +673,10 @@
default:
return true;
}
- return loop_predicate_helper(method, cur_level, i, b, k);
+ return loop_predicate_helper(method, cur_level, i, b, k);
}
-bool TieredThresholdPolicy::call_predicate(int i, int b, CompLevel cur_level, Method* method) {
+bool TieredThresholdPolicy::call_predicate(int i, int b, CompLevel cur_level, const methodHandle& method) {
double k = 1;
switch(cur_level) {
case CompLevel_aot: {
@@ -705,14 +706,15 @@
// Determine is a method is mature.
bool TieredThresholdPolicy::is_mature(Method* method) {
- if (is_trivial(method) || force_comp_at_level_simple(method)) return true;
+ methodHandle mh(Thread::current(), method);
+ if (is_trivial(method) || force_comp_at_level_simple(mh)) return true;
MethodData* mdo = method->method_data();
if (mdo != NULL) {
int i = mdo->invocation_count();
int b = mdo->backedge_count();
double k = ProfileMaturityPercentage / 100.0;
CompLevel main_profile_level = CompilationModeFlag::disable_intermediate() ? CompLevel_none : CompLevel_full_profile;
- return call_predicate_helper(method, main_profile_level, i, b, k) || loop_predicate_helper(method, main_profile_level, i, b, k);
+ return call_predicate_helper(mh, main_profile_level, i, b, k) || loop_predicate_helper(mh, main_profile_level, i, b, k);
}
return false;
}
@@ -720,7 +722,7 @@
// If a method is old enough and is still in the interpreter we would want to
// start profiling without waiting for the compiled method to arrive.
// We also take the load on compilers into the account.
-bool TieredThresholdPolicy::should_create_mdo(Method* method, CompLevel cur_level) {
+bool TieredThresholdPolicy::should_create_mdo(const methodHandle& method, CompLevel cur_level) {
if (cur_level != CompLevel_none || force_comp_at_level_simple(method)) {
return false;
}
@@ -799,7 +801,7 @@
*/
// Common transition function. Given a predicate determines if a method should transition to another level.
-CompLevel TieredThresholdPolicy::common(Predicate p, Method* method, CompLevel cur_level, bool disable_feedback) {
+CompLevel TieredThresholdPolicy::common(Predicate p, const methodHandle& method, CompLevel cur_level, bool disable_feedback) {
CompLevel next_level = cur_level;
int i = method->invocation_count();
int b = method->backedge_count();
@@ -807,7 +809,7 @@
if (force_comp_at_level_simple(method)) {
next_level = CompLevel_simple;
} else {
- if (!CompilationModeFlag::disable_intermediate() && is_trivial(method)) {
+ if (!CompilationModeFlag::disable_intermediate() && is_trivial(method())) {
next_level = CompLevel_simple;
} else {
switch(cur_level) {
@@ -926,7 +928,7 @@
}
// Determine if a method should be compiled with a normal entry point at a different level.
-CompLevel TieredThresholdPolicy::call_event(Method* method, CompLevel cur_level, JavaThread* thread) {
+CompLevel TieredThresholdPolicy::call_event(const methodHandle& method, CompLevel cur_level, JavaThread* thread) {
CompLevel osr_level = MIN2((CompLevel) method->highest_osr_comp_level(),
common(&TieredThresholdPolicy::loop_predicate, method, cur_level, true));
CompLevel next_level = common(&TieredThresholdPolicy::call_predicate, method, cur_level);
@@ -947,7 +949,7 @@
}
// Determine if we should do an OSR compilation of a given method.
-CompLevel TieredThresholdPolicy::loop_event(Method* method, CompLevel cur_level, JavaThread* thread) {
+CompLevel TieredThresholdPolicy::loop_event(const methodHandle& method, CompLevel cur_level, JavaThread* thread) {
CompLevel next_level = common(&TieredThresholdPolicy::loop_predicate, method, cur_level, true);
if (cur_level == CompLevel_none) {
// If there is a live OSR method that means that we deopted to the interpreter
@@ -983,10 +985,10 @@
// Handle the invocation event.
void TieredThresholdPolicy::method_invocation_event(const methodHandle& mh, const methodHandle& imh,
CompLevel level, CompiledMethod* nm, JavaThread* thread) {
- if (should_create_mdo(mh(), level)) {
+ if (should_create_mdo(mh, level)) {
create_mdo(mh, thread);
}
- CompLevel next_level = call_event(mh(), level, thread);
+ CompLevel next_level = call_event(mh, level, thread);
if (next_level != level) {
if (maybe_switch_to_aot(mh, level, next_level, thread)) {
// No JITting necessary
@@ -1002,16 +1004,16 @@
// with a regular entry from here.
void TieredThresholdPolicy::method_back_branch_event(const methodHandle& mh, const methodHandle& imh,
int bci, CompLevel level, CompiledMethod* nm, JavaThread* thread) {
- if (should_create_mdo(mh(), level)) {
+ if (should_create_mdo(mh, level)) {
create_mdo(mh, thread);
}
// Check if MDO should be created for the inlined method
- if (should_create_mdo(imh(), level)) {
+ if (should_create_mdo(imh, level)) {
create_mdo(imh, thread);
}
if (is_compilation_enabled()) {
- CompLevel next_osr_level = loop_event(imh(), level, thread);
+ CompLevel next_osr_level = loop_event(imh, level, thread);
CompLevel max_osr_level = (CompLevel)imh->highest_osr_comp_level();
// At the very least compile the OSR version
if (!CompileBroker::compilation_is_in_queue(imh) && (next_osr_level != level)) {
@@ -1032,7 +1034,7 @@
// Current loop event level is not AOT
guarantee(nm != NULL, "Should have nmethod here");
cur_level = comp_level(mh());
- next_level = call_event(mh(), cur_level, thread);
+ next_level = call_event(mh, cur_level, thread);
if (max_osr_level == CompLevel_full_optimization) {
// The inlinee OSRed to full opt, we need to modify the enclosing method to avoid deopts
@@ -1068,7 +1070,7 @@
}
} else {
cur_level = comp_level(mh());
- next_level = call_event(mh(), cur_level, thread);
+ next_level = call_event(mh, cur_level, thread);
if (next_level != cur_level) {
if (!maybe_switch_to_aot(mh, cur_level, next_level, thread) && !CompileBroker::compilation_is_in_queue(mh)) {
compile(mh, InvocationEntryBci, next_level, thread);
--- a/src/hotspot/share/compiler/tieredThresholdPolicy.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/compiler/tieredThresholdPolicy.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -173,19 +173,19 @@
// Call and loop predicates determine whether a transition to a higher compilation
// level should be performed (pointers to predicate functions are passed to common_TF().
// Predicates also take compiler load into account.
- typedef bool (TieredThresholdPolicy::*Predicate)(int i, int b, CompLevel cur_level, Method* method);
- bool call_predicate(int i, int b, CompLevel cur_level, Method* method);
- bool loop_predicate(int i, int b, CompLevel cur_level, Method* method);
+ typedef bool (TieredThresholdPolicy::*Predicate)(int i, int b, CompLevel cur_level, const methodHandle& method);
+ bool call_predicate(int i, int b, CompLevel cur_level, const methodHandle& method);
+ bool loop_predicate(int i, int b, CompLevel cur_level, const methodHandle& method);
// Common transition function. Given a predicate determines if a method should transition to another level.
- CompLevel common(Predicate p, Method* method, CompLevel cur_level, bool disable_feedback = false);
+ CompLevel common(Predicate p, const methodHandle& method, CompLevel cur_level, bool disable_feedback = false);
// Transition functions.
// call_event determines if a method should be compiled at a different
// level with a regular invocation entry.
- CompLevel call_event(Method* method, CompLevel cur_level, JavaThread* thread);
+ CompLevel call_event(const methodHandle& method, CompLevel cur_level, JavaThread* thread);
// loop_event checks if a method should be OSR compiled at a different
// level.
- CompLevel loop_event(Method* method, CompLevel cur_level, JavaThread* thread);
- void print_counters(const char* prefix, const methodHandle& mh);
+ CompLevel loop_event(const methodHandle& method, CompLevel cur_level, JavaThread* thread);
+ void print_counters(const char* prefix, Method* m);
// Has a method been long around?
// We don't remove old methods from the compile queue even if they have
// very low activity (see select_task()).
@@ -205,11 +205,11 @@
// If a method is old enough and is still in the interpreter we would want to
// start profiling without waiting for the compiled method to arrive. This function
// determines whether we should do that.
- inline bool should_create_mdo(Method* method, CompLevel cur_level);
+ inline bool should_create_mdo(const methodHandle& method, CompLevel cur_level);
// Create MDO if necessary.
void create_mdo(const methodHandle& mh, JavaThread* thread);
// Is method profiled enough?
- bool is_method_profiled(Method* method);
+ bool is_method_profiled(const methodHandle& method);
double _increase_threshold_at_ratio;
@@ -221,19 +221,19 @@
void set_c2_count(int x) { _c2_count = x; }
enum EventType { CALL, LOOP, COMPILE, REMOVE_FROM_QUEUE, UPDATE_IN_QUEUE, REPROFILE, MAKE_NOT_ENTRANT };
- void print_event(EventType type, const methodHandle& mh, const methodHandle& imh, int bci, CompLevel level);
+ void print_event(EventType type, Method* m, Method* im, int bci, CompLevel level);
// Check if the method can be compiled, change level if necessary
void compile(const methodHandle& mh, int bci, CompLevel level, JavaThread* thread);
// Simple methods are as good being compiled with C1 as C2.
// This function tells if it's such a function.
inline static bool is_trivial(Method* method);
// Force method to be compiled at CompLevel_simple?
- inline bool force_comp_at_level_simple(Method* method);
+ inline bool force_comp_at_level_simple(const methodHandle& method);
// Predicate helpers are used by .*_predicate() methods as well as others.
// They check the given counter values, multiplied by the scale against the thresholds.
- inline bool call_predicate_helper(Method* method, CompLevel cur_level, int i, int b, double scale);
- inline bool loop_predicate_helper(Method* method, CompLevel cur_level, int i, int b, double scale);
+ inline bool call_predicate_helper(const methodHandle& method, CompLevel cur_level, int i, int b, double scale);
+ inline bool loop_predicate_helper(const methodHandle& method, CompLevel cur_level, int i, int b, double scale);
// Get a compilation level for a given method.
static CompLevel comp_level(Method* method);
--- a/src/hotspot/share/gc/cms/adaptiveFreeList.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,151 +0,0 @@
-/*
- * Copyright (c) 2012, 2018, 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/cms/adaptiveFreeList.hpp"
-#include "gc/cms/freeChunk.hpp"
-#include "gc/shared/collectedHeap.hpp"
-#include "memory/freeList.inline.hpp"
-#include "runtime/globals.hpp"
-#include "runtime/mutex.hpp"
-#include "runtime/orderAccess.hpp"
-#include "runtime/vmThread.hpp"
-
-template <>
-void AdaptiveFreeList<FreeChunk>::print_on(outputStream* st, const char* c) const {
- if (c != NULL) {
- st->print("%16s", c);
- } else {
- st->print(SIZE_FORMAT_W(16), size());
- }
- st->print("\t"
- SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t"
- SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\n",
- bfr_surp(), surplus(), desired(), prev_sweep(), before_sweep(),
- count(), coal_births(), coal_deaths(), split_births(), split_deaths());
-}
-
-template <class Chunk>
-AdaptiveFreeList<Chunk>::AdaptiveFreeList() : FreeList<Chunk>(), _hint(0) {
- init_statistics();
-}
-
-template <class Chunk>
-void AdaptiveFreeList<Chunk>::initialize() {
- FreeList<Chunk>::initialize();
- set_hint(0);
- init_statistics(true /* split_birth */);
-}
-
-template <class Chunk>
-void AdaptiveFreeList<Chunk>::reset(size_t hint) {
- FreeList<Chunk>::reset();
- set_hint(hint);
-}
-
-template <class Chunk>
-void AdaptiveFreeList<Chunk>::init_statistics(bool split_birth) {
- _allocation_stats.initialize(split_birth);
-}
-
-template <class Chunk>
-size_t AdaptiveFreeList<Chunk>::get_better_size() {
-
- // A candidate chunk has been found. If it is already under
- // populated and there is a hinT, REturn the hint(). Else
- // return the size of this chunk.
- if (surplus() <= 0) {
- if (hint() != 0) {
- return hint();
- } else {
- return size();
- }
- } else {
- // This list has a surplus so use it.
- return size();
- }
-}
-
-
-template <class Chunk>
-void AdaptiveFreeList<Chunk>::return_chunk_at_head(Chunk* chunk) {
- assert_proper_lock_protection();
- return_chunk_at_head(chunk, true);
-}
-
-template <class Chunk>
-void AdaptiveFreeList<Chunk>::return_chunk_at_head(Chunk* chunk, bool record_return) {
- FreeList<Chunk>::return_chunk_at_head(chunk, record_return);
-#ifdef ASSERT
- if (record_return) {
- increment_returned_bytes_by(size()*HeapWordSize);
- }
-#endif
-}
-
-template <class Chunk>
-void AdaptiveFreeList<Chunk>::return_chunk_at_tail(Chunk* chunk) {
- AdaptiveFreeList<Chunk>::return_chunk_at_tail(chunk, true);
-}
-
-template <class Chunk>
-void AdaptiveFreeList<Chunk>::return_chunk_at_tail(Chunk* chunk, bool record_return) {
- FreeList<Chunk>::return_chunk_at_tail(chunk, record_return);
-#ifdef ASSERT
- if (record_return) {
- increment_returned_bytes_by(size()*HeapWordSize);
- }
-#endif
-}
-
-#ifndef PRODUCT
-template <class Chunk>
-void AdaptiveFreeList<Chunk>::verify_stats() const {
- // The +1 of the LH comparand is to allow some "looseness" in
- // checking: we usually call this interface when adding a block
- // and we'll subsequently update the stats; we cannot update the
- // stats beforehand because in the case of the large-block BT
- // dictionary for example, this might be the first block and
- // in that case there would be no place that we could record
- // the stats (which are kept in the block itself).
- assert((_allocation_stats.prev_sweep() + _allocation_stats.split_births()
- + _allocation_stats.coal_births() + 1) // Total Production Stock + 1
- >= (_allocation_stats.split_deaths() + _allocation_stats.coal_deaths()
- + (ssize_t)count()), // Total Current Stock + depletion
- "FreeList " PTR_FORMAT " of size " SIZE_FORMAT
- " violates Conservation Principle: "
- "prev_sweep(" SIZE_FORMAT ")"
- " + split_births(" SIZE_FORMAT ")"
- " + coal_births(" SIZE_FORMAT ") + 1 >= "
- " split_deaths(" SIZE_FORMAT ")"
- " coal_deaths(" SIZE_FORMAT ")"
- " + count(" SSIZE_FORMAT ")",
- p2i(this), size(), _allocation_stats.prev_sweep(), _allocation_stats.split_births(),
- _allocation_stats.coal_births(), _allocation_stats.split_deaths(),
- _allocation_stats.coal_deaths(), count());
-}
-#endif
-
-// Needs to be after the definitions have been seen.
-template class AdaptiveFreeList<FreeChunk>;
--- a/src/hotspot/share/gc/cms/adaptiveFreeList.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,229 +0,0 @@
-/*
- * Copyright (c) 2001, 2019, 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_GC_CMS_ADAPTIVEFREELIST_HPP
-#define SHARE_GC_CMS_ADAPTIVEFREELIST_HPP
-
-#include "gc/cms/allocationStats.hpp"
-#include "memory/freeList.hpp"
-
-class CompactibleFreeListSpace;
-
-// A class for maintaining a free list of Chunk's. The FreeList
-// maintains a the structure of the list (head, tail, etc.) plus
-// statistics for allocations from the list. The links between items
-// are not part of FreeList. The statistics are
-// used to make decisions about coalescing Chunk's when they
-// are swept during collection.
-//
-// See the corresponding .cpp file for a description of the specifics
-// for that implementation.
-
-class Mutex;
-
-template <class Chunk>
-class AdaptiveFreeList : public FreeList<Chunk> {
- friend class CompactibleFreeListSpace;
- friend class VMStructs;
- // friend class PrintTreeCensusClosure<Chunk, FreeList_t>;
-
- size_t _hint; // next larger size list with a positive surplus
-
- AllocationStats _allocation_stats; // allocation-related statistics
-
- public:
-
- AdaptiveFreeList();
-
- using FreeList<Chunk>::assert_proper_lock_protection;
-#ifdef ASSERT
- using FreeList<Chunk>::protecting_lock;
-#endif
- using FreeList<Chunk>::count;
- using FreeList<Chunk>::size;
- using FreeList<Chunk>::verify_chunk_in_free_list;
- using FreeList<Chunk>::getFirstNChunksFromList;
- using FreeList<Chunk>::print_on;
- void return_chunk_at_head(Chunk* fc, bool record_return);
- void return_chunk_at_head(Chunk* fc);
- void return_chunk_at_tail(Chunk* fc, bool record_return);
- void return_chunk_at_tail(Chunk* fc);
- using FreeList<Chunk>::return_chunk_at_tail;
- using FreeList<Chunk>::remove_chunk;
- using FreeList<Chunk>::prepend;
- using FreeList<Chunk>::print_labels_on;
- using FreeList<Chunk>::get_chunk_at_head;
-
- // Initialize.
- void initialize();
-
- // Reset the head, tail, hint, and count of a free list.
- void reset(size_t hint);
-
- void print_on(outputStream* st, const char* c = NULL) const;
-
- size_t hint() const {
- return _hint;
- }
- void set_hint(size_t v) {
- assert_proper_lock_protection();
- assert(v == 0 || size() < v, "Bad hint");
- _hint = v;
- }
-
- size_t get_better_size();
-
- // Accessors for statistics
- void init_statistics(bool split_birth = false);
-
- AllocationStats* allocation_stats() {
- assert_proper_lock_protection();
- return &_allocation_stats;
- }
-
- ssize_t desired() const {
- return _allocation_stats.desired();
- }
- void set_desired(ssize_t v) {
- assert_proper_lock_protection();
- _allocation_stats.set_desired(v);
- }
- void compute_desired(float inter_sweep_current,
- float inter_sweep_estimate,
- float intra_sweep_estimate) {
- assert_proper_lock_protection();
- _allocation_stats.compute_desired(count(),
- inter_sweep_current,
- inter_sweep_estimate,
- intra_sweep_estimate);
- }
- ssize_t coal_desired() const {
- return _allocation_stats.coal_desired();
- }
- void set_coal_desired(ssize_t v) {
- assert_proper_lock_protection();
- _allocation_stats.set_coal_desired(v);
- }
-
- ssize_t surplus() const {
- return _allocation_stats.surplus();
- }
- void set_surplus(ssize_t v) {
- assert_proper_lock_protection();
- _allocation_stats.set_surplus(v);
- }
- void increment_surplus() {
- assert_proper_lock_protection();
- _allocation_stats.increment_surplus();
- }
- void decrement_surplus() {
- assert_proper_lock_protection();
- _allocation_stats.decrement_surplus();
- }
-
- ssize_t bfr_surp() const {
- return _allocation_stats.bfr_surp();
- }
- void set_bfr_surp(ssize_t v) {
- assert_proper_lock_protection();
- _allocation_stats.set_bfr_surp(v);
- }
- ssize_t prev_sweep() const {
- return _allocation_stats.prev_sweep();
- }
- void set_prev_sweep(ssize_t v) {
- assert_proper_lock_protection();
- _allocation_stats.set_prev_sweep(v);
- }
- ssize_t before_sweep() const {
- return _allocation_stats.before_sweep();
- }
- void set_before_sweep(ssize_t v) {
- assert_proper_lock_protection();
- _allocation_stats.set_before_sweep(v);
- }
-
- ssize_t coal_births() const {
- return _allocation_stats.coal_births();
- }
- void set_coal_births(ssize_t v) {
- assert_proper_lock_protection();
- _allocation_stats.set_coal_births(v);
- }
- void increment_coal_births() {
- assert_proper_lock_protection();
- _allocation_stats.increment_coal_births();
- }
-
- ssize_t coal_deaths() const {
- return _allocation_stats.coal_deaths();
- }
- void set_coal_deaths(ssize_t v) {
- assert_proper_lock_protection();
- _allocation_stats.set_coal_deaths(v);
- }
- void increment_coal_deaths() {
- assert_proper_lock_protection();
- _allocation_stats.increment_coal_deaths();
- }
-
- ssize_t split_births() const {
- return _allocation_stats.split_births();
- }
- void set_split_births(ssize_t v) {
- assert_proper_lock_protection();
- _allocation_stats.set_split_births(v);
- }
- void increment_split_births() {
- assert_proper_lock_protection();
- _allocation_stats.increment_split_births();
- }
-
- ssize_t split_deaths() const {
- return _allocation_stats.split_deaths();
- }
- void set_split_deaths(ssize_t v) {
- assert_proper_lock_protection();
- _allocation_stats.set_split_deaths(v);
- }
- void increment_split_deaths() {
- assert_proper_lock_protection();
- _allocation_stats.increment_split_deaths();
- }
-
-#ifndef PRODUCT
- // For debugging. The "_returned_bytes" in all the lists are summed
- // and compared with the total number of bytes swept during a
- // collection.
- size_t returned_bytes() const { return _allocation_stats.returned_bytes(); }
- void set_returned_bytes(size_t v) { _allocation_stats.set_returned_bytes(v); }
- void increment_returned_bytes_by(size_t v) {
- _allocation_stats.set_returned_bytes(_allocation_stats.returned_bytes() + v);
- }
- // Stats verification
- void verify_stats() const;
-#endif // NOT PRODUCT
-};
-
-#endif // SHARE_GC_CMS_ADAPTIVEFREELIST_HPP
--- a/src/hotspot/share/gc/cms/allocationStats.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2005, 2015, 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/cms/allocationStats.hpp"
-#include "utilities/macros.hpp"
-#include "utilities/ostream.hpp"
-
-// Technically this should be derived from machine speed, and
-// ideally it would be dynamically adjusted.
-float AllocationStats::_threshold = ((float)CMS_SweepTimerThresholdMillis)/1000;
-
-void AllocationStats::initialize(bool split_birth) {
- AdaptivePaddedAverage* dummy =
- new (&_demand_rate_estimate) AdaptivePaddedAverage(CMS_FLSWeight,
- CMS_FLSPadding);
- _desired = 0;
- _coal_desired = 0;
- _surplus = 0;
- _bfr_surp = 0;
- _prev_sweep = 0;
- _before_sweep = 0;
- _coal_births = 0;
- _coal_deaths = 0;
- _split_births = (split_birth ? 1 : 0);
- _split_deaths = 0;
- _returned_bytes = 0;
-}
--- a/src/hotspot/share/gc/cms/allocationStats.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-/*
- * Copyright (c) 2001, 2019, 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_GC_CMS_ALLOCATIONSTATS_HPP
-#define SHARE_GC_CMS_ALLOCATIONSTATS_HPP
-
-#include "gc/shared/gcUtil.hpp"
-#include "logging/log.hpp"
-#include "runtime/globals.hpp"
-#include "utilities/globalDefinitions.hpp"
-#include "utilities/macros.hpp"
-
-class AllocationStats {
- // A duration threshold (in ms) used to filter
- // possibly unreliable samples.
- static float _threshold;
-
- // We measure the demand between the end of the previous sweep and
- // beginning of this sweep:
- // Count(end_last_sweep) - Count(start_this_sweep)
- // + split_births(between) - split_deaths(between)
- // The above number divided by the time since the end of the
- // previous sweep gives us a time rate of demand for blocks
- // of this size. We compute a padded average of this rate as
- // our current estimate for the time rate of demand for blocks
- // of this size. Similarly, we keep a padded average for the time
- // between sweeps. Our current estimate for demand for blocks of
- // this size is then simply computed as the product of these two
- // estimates.
- AdaptivePaddedAverage _demand_rate_estimate;
-
- ssize_t _desired; // Demand estimate computed as described above
- ssize_t _coal_desired; // desired +/- small-percent for tuning coalescing
-
- ssize_t _surplus; // count - (desired +/- small-percent),
- // used to tune splitting in best fit
- ssize_t _bfr_surp; // surplus at start of current sweep
- ssize_t _prev_sweep; // count from end of previous sweep
- ssize_t _before_sweep; // count from before current sweep
- ssize_t _coal_births; // additional chunks from coalescing
- ssize_t _coal_deaths; // loss from coalescing
- ssize_t _split_births; // additional chunks from splitting
- ssize_t _split_deaths; // loss from splitting
- size_t _returned_bytes; // number of bytes returned to list.
- public:
- void initialize(bool split_birth = false);
-
- AllocationStats() {
- initialize();
- }
-
- // The rate estimate is in blocks per second.
- void compute_desired(size_t count,
- float inter_sweep_current,
- float inter_sweep_estimate,
- float intra_sweep_estimate) {
- // If the latest inter-sweep time is below our granularity
- // of measurement, we may call in here with
- // inter_sweep_current == 0. However, even for suitably small
- // but non-zero inter-sweep durations, we may not trust the accuracy
- // of accumulated data, since it has not been "integrated"
- // (read "low-pass-filtered") long enough, and would be
- // vulnerable to noisy glitches. In such cases, we
- // ignore the current sample and use currently available
- // historical estimates.
- assert(prev_sweep() + split_births() + coal_births() // "Total Production Stock"
- >= split_deaths() + coal_deaths() + (ssize_t)count, // "Current stock + depletion"
- "Conservation Principle");
- if (inter_sweep_current > _threshold) {
- ssize_t demand = prev_sweep() - (ssize_t)count + split_births() + coal_births()
- - split_deaths() - coal_deaths();
- assert(demand >= 0,
- "Demand (" SSIZE_FORMAT ") should be non-negative for "
- PTR_FORMAT " (size=" SIZE_FORMAT ")",
- demand, p2i(this), count);
- // Defensive: adjust for imprecision in event counting
- if (demand < 0) {
- demand = 0;
- }
- float old_rate = _demand_rate_estimate.padded_average();
- float rate = ((float)demand)/inter_sweep_current;
- _demand_rate_estimate.sample(rate);
- float new_rate = _demand_rate_estimate.padded_average();
- ssize_t old_desired = _desired;
- float delta_ise = (CMSExtrapolateSweep ? intra_sweep_estimate : 0.0);
- _desired = (ssize_t)(new_rate * (inter_sweep_estimate + delta_ise));
- log_trace(gc, freelist)("demand: " SSIZE_FORMAT ", old_rate: %f, current_rate: %f, "
- "new_rate: %f, old_desired: " SSIZE_FORMAT ", new_desired: " SSIZE_FORMAT,
- demand, old_rate, rate, new_rate, old_desired, _desired);
- }
- }
-
- ssize_t desired() const { return _desired; }
- void set_desired(ssize_t v) { _desired = v; }
-
- ssize_t coal_desired() const { return _coal_desired; }
- void set_coal_desired(ssize_t v) { _coal_desired = v; }
-
- ssize_t surplus() const { return _surplus; }
- void set_surplus(ssize_t v) { _surplus = v; }
- void increment_surplus() { _surplus++; }
- void decrement_surplus() { _surplus--; }
-
- ssize_t bfr_surp() const { return _bfr_surp; }
- void set_bfr_surp(ssize_t v) { _bfr_surp = v; }
- ssize_t prev_sweep() const { return _prev_sweep; }
- void set_prev_sweep(ssize_t v) { _prev_sweep = v; }
- ssize_t before_sweep() const { return _before_sweep; }
- void set_before_sweep(ssize_t v) { _before_sweep = v; }
-
- ssize_t coal_births() const { return _coal_births; }
- void set_coal_births(ssize_t v) { _coal_births = v; }
- void increment_coal_births() { _coal_births++; }
-
- ssize_t coal_deaths() const { return _coal_deaths; }
- void set_coal_deaths(ssize_t v) { _coal_deaths = v; }
- void increment_coal_deaths() { _coal_deaths++; }
-
- ssize_t split_births() const { return _split_births; }
- void set_split_births(ssize_t v) { _split_births = v; }
- void increment_split_births() { _split_births++; }
-
- ssize_t split_deaths() const { return _split_deaths; }
- void set_split_deaths(ssize_t v) { _split_deaths = v; }
- void increment_split_deaths() { _split_deaths++; }
-
- NOT_PRODUCT(
- size_t returned_bytes() const { return _returned_bytes; }
- void set_returned_bytes(size_t v) { _returned_bytes = v; }
- )
-};
-
-#endif // SHARE_GC_CMS_ALLOCATIONSTATS_HPP
--- a/src/hotspot/share/gc/cms/cmsArguments.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,225 +0,0 @@
-/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates.
- * 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/cms/cmsArguments.hpp"
-#include "gc/cms/cmsHeap.hpp"
-#include "gc/cms/compactibleFreeListSpace.hpp"
-#include "gc/shared/cardTableRS.hpp"
-#include "gc/shared/gcArguments.hpp"
-#include "gc/shared/genCollectedHeap.hpp"
-#include "gc/shared/workerPolicy.hpp"
-#include "runtime/arguments.hpp"
-#include "runtime/globals.hpp"
-#include "runtime/globals_extension.hpp"
-#include "utilities/defaultStream.hpp"
-
-void CMSArguments::set_parnew_gc_flags() {
- assert(!UseSerialGC && !UseParallelOldGC && !UseParallelGC && !UseG1GC,
- "control point invariant");
- assert(UseConcMarkSweepGC, "CMS is expected to be on here");
-
- if (FLAG_IS_DEFAULT(ParallelGCThreads)) {
- FLAG_SET_DEFAULT(ParallelGCThreads, WorkerPolicy::parallel_worker_threads());
- assert(ParallelGCThreads > 0, "We should always have at least one thread by default");
- } else if (ParallelGCThreads == 0) {
- jio_fprintf(defaultStream::error_stream(),
- "The ParNew GC can not be combined with -XX:ParallelGCThreads=0\n");
- vm_exit(1);
- }
-
- // By default YoungPLABSize and OldPLABSize are set to 4096 and 1024 respectively,
- // these settings are default for Parallel Scavenger. For ParNew+Tenured configuration
- // we set them to 1024 and 1024.
- // See CR 6362902.
- if (FLAG_IS_DEFAULT(YoungPLABSize)) {
- FLAG_SET_DEFAULT(YoungPLABSize, (intx)1024);
- }
- if (FLAG_IS_DEFAULT(OldPLABSize)) {
- FLAG_SET_DEFAULT(OldPLABSize, (intx)1024);
- }
-
- // When using compressed oops, we use local overflow stacks,
- // rather than using a global overflow list chained through
- // the klass word of the object's pre-image.
- if (UseCompressedOops && !ParGCUseLocalOverflow) {
- if (!FLAG_IS_DEFAULT(ParGCUseLocalOverflow)) {
- warning("Forcing +ParGCUseLocalOverflow: needed if using compressed references");
- }
- FLAG_SET_DEFAULT(ParGCUseLocalOverflow, true);
- }
- assert(ParGCUseLocalOverflow || !UseCompressedOops, "Error");
-}
-
-// Adjust some sizes to suit CMS and/or ParNew needs; these work well on
-// sparc/solaris for certain applications, but would gain from
-// further optimization and tuning efforts, and would almost
-// certainly gain from analysis of platform and environment.
-void CMSArguments::initialize() {
- GCArguments::initialize();
-
- assert(!UseSerialGC && !UseParallelOldGC && !UseParallelGC, "Error");
- assert(UseConcMarkSweepGC, "CMS is expected to be on here");
-
- // CMS space iteration, which FLSVerifyAllHeapreferences entails,
- // insists that we hold the requisite locks so that the iteration is
- // MT-safe. For the verification at start-up and shut-down, we don't
- // yet have a good way of acquiring and releasing these locks,
- // which are not visible at the CollectedHeap level. We want to
- // be able to acquire these locks and then do the iteration rather
- // than just disable the lock verification. This will be fixed under
- // bug 4788986.
- if (UseConcMarkSweepGC && FLSVerifyAllHeapReferences) {
- if (VerifyDuringStartup) {
- warning("Heap verification at start-up disabled "
- "(due to current incompatibility with FLSVerifyAllHeapReferences)");
- VerifyDuringStartup = false; // Disable verification at start-up
- }
-
- if (VerifyBeforeExit) {
- warning("Heap verification at shutdown disabled "
- "(due to current incompatibility with FLSVerifyAllHeapReferences)");
- VerifyBeforeExit = false; // Disable verification at shutdown
- }
- }
-
- if (!ClassUnloading) {
- FLAG_SET_CMDLINE(CMSClassUnloadingEnabled, false);
- }
-
- // Set CMS global values
- CompactibleFreeListSpace::set_cms_values();
-
- // Turn off AdaptiveSizePolicy by default for cms until it is complete.
- disable_adaptive_size_policy("UseConcMarkSweepGC");
-
- set_parnew_gc_flags();
-
- size_t max_heap = align_down(MaxHeapSize,
- CardTableRS::ct_max_alignment_constraint());
-
- // Now make adjustments for CMS
- intx tenuring_default = (intx)6;
- size_t young_gen_per_worker = CMSYoungGenPerWorker;
-
- // Preferred young gen size for "short" pauses:
- // upper bound depends on # of threads and NewRatio.
- const size_t preferred_max_new_size_unaligned =
- MIN2(max_heap/(NewRatio+1), ScaleForWordSize(young_gen_per_worker * ParallelGCThreads));
- size_t preferred_max_new_size =
- align_up(preferred_max_new_size_unaligned, os::vm_page_size());
-
- // Unless explicitly requested otherwise, size young gen
- // for "short" pauses ~ CMSYoungGenPerWorker*ParallelGCThreads
-
- // If either MaxNewSize or NewRatio is set on the command line,
- // assume the user is trying to set the size of the young gen.
- if (FLAG_IS_DEFAULT(MaxNewSize) && FLAG_IS_DEFAULT(NewRatio)) {
-
- // Set MaxNewSize to our calculated preferred_max_new_size unless
- // NewSize was set on the command line and it is larger than
- // preferred_max_new_size.
- if (!FLAG_IS_DEFAULT(NewSize)) { // NewSize explicitly set at command-line
- FLAG_SET_ERGO(MaxNewSize, MAX2(NewSize, preferred_max_new_size));
- } else {
- FLAG_SET_ERGO(MaxNewSize, preferred_max_new_size);
- }
- log_trace(gc, heap)("CMS ergo set MaxNewSize: " SIZE_FORMAT, MaxNewSize);
-
- // Code along this path potentially sets NewSize and OldSize
- log_trace(gc, heap)("CMS set min_heap_size: " SIZE_FORMAT " initial_heap_size: " SIZE_FORMAT " max_heap: " SIZE_FORMAT,
- MinHeapSize, InitialHeapSize, max_heap);
- size_t min_new = preferred_max_new_size;
- if (FLAG_IS_CMDLINE(NewSize)) {
- min_new = NewSize;
- }
- if (max_heap > min_new && MinHeapSize > min_new) {
- // Unless explicitly requested otherwise, make young gen
- // at least min_new, and at most preferred_max_new_size.
- if (FLAG_IS_DEFAULT(NewSize)) {
- FLAG_SET_ERGO(NewSize, MAX2(NewSize, min_new));
- FLAG_SET_ERGO(NewSize, MIN2(preferred_max_new_size, NewSize));
- log_trace(gc, heap)("CMS ergo set NewSize: " SIZE_FORMAT, NewSize);
- }
- // Unless explicitly requested otherwise, size old gen
- // so it's NewRatio x of NewSize.
- if (FLAG_IS_DEFAULT(OldSize)) {
- if (max_heap > NewSize) {
- FLAG_SET_ERGO(OldSize, MIN2(NewRatio*NewSize, max_heap - NewSize));
- log_trace(gc, heap)("CMS ergo set OldSize: " SIZE_FORMAT, OldSize);
- }
- }
- }
- }
- // Unless explicitly requested otherwise, definitely
- // promote all objects surviving "tenuring_default" scavenges.
- if (FLAG_IS_DEFAULT(MaxTenuringThreshold) &&
- FLAG_IS_DEFAULT(SurvivorRatio)) {
- FLAG_SET_ERGO(MaxTenuringThreshold, tenuring_default);
- }
- // If we decided above (or user explicitly requested)
- // `promote all' (via MaxTenuringThreshold := 0),
- // prefer minuscule survivor spaces so as not to waste
- // space for (non-existent) survivors
- if (FLAG_IS_DEFAULT(SurvivorRatio) && MaxTenuringThreshold == 0) {
- FLAG_SET_ERGO(SurvivorRatio, MAX2((uintx)1024, SurvivorRatio));
- }
-
- // OldPLABSize is interpreted in CMS as not the size of the PLAB in words,
- // but rather the number of free blocks of a given size that are used when
- // replenishing the local per-worker free list caches.
- if (FLAG_IS_DEFAULT(OldPLABSize)) {
- if (!FLAG_IS_DEFAULT(ResizeOldPLAB) && !ResizeOldPLAB) {
- // OldPLAB sizing manually turned off: Use a larger default setting,
- // unless it was manually specified. This is because a too-low value
- // will slow down scavenges.
- FLAG_SET_ERGO(OldPLABSize, CompactibleFreeListSpaceLAB::_default_static_old_plab_size); // default value before 6631166
- } else {
- FLAG_SET_DEFAULT(OldPLABSize, CompactibleFreeListSpaceLAB::_default_dynamic_old_plab_size); // old CMSParPromoteBlocksToClaim default
- }
- }
-
- // If either of the static initialization defaults have changed, note this
- // modification.
- if (!FLAG_IS_DEFAULT(OldPLABSize) || !FLAG_IS_DEFAULT(OldPLABWeight)) {
- CompactibleFreeListSpaceLAB::modify_initialization(OldPLABSize, OldPLABWeight);
- }
-
- log_trace(gc)("MarkStackSize: %uk MarkStackSizeMax: %uk", (unsigned int) (MarkStackSize / K), (uint) (MarkStackSizeMax / K));
-}
-
-void CMSArguments::disable_adaptive_size_policy(const char* collector_name) {
- if (UseAdaptiveSizePolicy) {
- if (FLAG_IS_CMDLINE(UseAdaptiveSizePolicy)) {
- warning("Disabling UseAdaptiveSizePolicy; it is incompatible with %s.",
- collector_name);
- }
- FLAG_SET_DEFAULT(UseAdaptiveSizePolicy, false);
- }
-}
-
-CollectedHeap* CMSArguments::create_heap() {
- return new CMSHeap();
-}
--- a/src/hotspot/share/gc/cms/cmsArguments.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates.
- * 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_GC_CMS_CMSARGUMENTS_HPP
-#define SHARE_GC_CMS_CMSARGUMENTS_HPP
-
-#include "gc/shared/gcArguments.hpp"
-#include "gc/shared/genArguments.hpp"
-
-class CollectedHeap;
-
-class CMSArguments : public GenArguments {
-private:
- void disable_adaptive_size_policy(const char* collector_name);
- void set_parnew_gc_flags();
-
- virtual void initialize();
- virtual CollectedHeap* create_heap();
-};
-
-#endif // SHARE_GC_CMS_CMSARGUMENTS_HPP
--- a/src/hotspot/share/gc/cms/cmsCardTable.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,470 +0,0 @@
-/*
- * Copyright (c) 2007, 2018, 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/cms/cmsCardTable.hpp"
-#include "gc/cms/cmsHeap.hpp"
-#include "gc/shared/cardTableBarrierSet.hpp"
-#include "gc/shared/cardTableRS.hpp"
-#include "gc/shared/collectedHeap.hpp"
-#include "gc/shared/space.inline.hpp"
-#include "memory/allocation.inline.hpp"
-#include "memory/virtualspace.hpp"
-#include "oops/oop.inline.hpp"
-#include "runtime/java.hpp"
-#include "runtime/mutexLocker.hpp"
-#include "runtime/orderAccess.hpp"
-#include "runtime/vmThread.hpp"
-
-CMSCardTable::CMSCardTable(MemRegion whole_heap) :
- CardTableRS(whole_heap, CMSPrecleaningEnabled /* scanned_concurrently */) {
-}
-
-// Returns the number of chunks necessary to cover "mr".
-size_t CMSCardTable::chunks_to_cover(MemRegion mr) {
- return (size_t)(addr_to_chunk_index(mr.last()) -
- addr_to_chunk_index(mr.start()) + 1);
-}
-
-// Returns the index of the chunk in a stride which
-// covers the given address.
-uintptr_t CMSCardTable::addr_to_chunk_index(const void* addr) {
- uintptr_t card = (uintptr_t) byte_for(addr);
- return card / ParGCCardsPerStrideChunk;
-}
-
-void CMSCardTable::
-non_clean_card_iterate_parallel_work(Space* sp, MemRegion mr,
- OopsInGenClosure* cl,
- CardTableRS* ct,
- uint n_threads) {
- assert(n_threads > 0, "expected n_threads > 0");
- assert(n_threads <= ParallelGCThreads,
- "n_threads: %u > ParallelGCThreads: %u", n_threads, ParallelGCThreads);
-
- // Make sure the LNC array is valid for the space.
- CardValue** lowest_non_clean;
- uintptr_t lowest_non_clean_base_chunk_index;
- size_t lowest_non_clean_chunk_size;
- get_LNC_array_for_space(sp, lowest_non_clean,
- lowest_non_clean_base_chunk_index,
- lowest_non_clean_chunk_size);
-
- uint n_strides = n_threads * ParGCStridesPerThread;
- SequentialSubTasksDone* pst = sp->par_seq_tasks();
- // Sets the condition for completion of the subtask (how many threads
- // need to finish in order to be done).
- pst->set_n_threads(n_threads);
- pst->set_n_tasks(n_strides);
-
- uint stride = 0;
- while (pst->try_claim_task(/* reference */ stride)) {
- process_stride(sp, mr, stride, n_strides,
- cl, ct,
- lowest_non_clean,
- lowest_non_clean_base_chunk_index,
- lowest_non_clean_chunk_size);
- }
- if (pst->all_tasks_completed()) {
- // Clear lowest_non_clean array for next time.
- intptr_t first_chunk_index = addr_to_chunk_index(mr.start());
- uintptr_t last_chunk_index = addr_to_chunk_index(mr.last());
- for (uintptr_t ch = first_chunk_index; ch <= last_chunk_index; ch++) {
- intptr_t ind = ch - lowest_non_clean_base_chunk_index;
- assert(0 <= ind && ind < (intptr_t)lowest_non_clean_chunk_size,
- "Bounds error");
- lowest_non_clean[ind] = NULL;
- }
- }
-}
-
-void
-CMSCardTable::
-process_stride(Space* sp,
- MemRegion used,
- jint stride, int n_strides,
- OopsInGenClosure* cl,
- CardTableRS* ct,
- CardValue** lowest_non_clean,
- uintptr_t lowest_non_clean_base_chunk_index,
- size_t lowest_non_clean_chunk_size) {
- // We go from higher to lower addresses here; it wouldn't help that much
- // because of the strided parallelism pattern used here.
-
- // Find the first card address of the first chunk in the stride that is
- // at least "bottom" of the used region.
- CardValue* start_card = byte_for(used.start());
- CardValue* end_card = byte_after(used.last());
- uintptr_t start_chunk = addr_to_chunk_index(used.start());
- uintptr_t start_chunk_stride_num = start_chunk % n_strides;
- CardValue* chunk_card_start;
-
- if ((uintptr_t)stride >= start_chunk_stride_num) {
- chunk_card_start = (start_card +
- (stride - start_chunk_stride_num) * ParGCCardsPerStrideChunk);
- } else {
- // Go ahead to the next chunk group boundary, then to the requested stride.
- chunk_card_start = (start_card +
- (n_strides - start_chunk_stride_num + stride) * ParGCCardsPerStrideChunk);
- }
-
- while (chunk_card_start < end_card) {
- // Even though we go from lower to higher addresses below, the
- // strided parallelism can interleave the actual processing of the
- // dirty pages in various ways. For a specific chunk within this
- // stride, we take care to avoid double scanning or missing a card
- // by suitably initializing the "min_done" field in process_chunk_boundaries()
- // below, together with the dirty region extension accomplished in
- // DirtyCardToOopClosure::do_MemRegion().
- CardValue* chunk_card_end = chunk_card_start + ParGCCardsPerStrideChunk;
- // Invariant: chunk_mr should be fully contained within the "used" region.
- MemRegion chunk_mr = MemRegion(addr_for(chunk_card_start),
- chunk_card_end >= end_card ?
- used.end() : addr_for(chunk_card_end));
- assert(chunk_mr.word_size() > 0, "[chunk_card_start > used_end)");
- assert(used.contains(chunk_mr), "chunk_mr should be subset of used");
-
- // This function is used by the parallel card table iteration.
- const bool parallel = true;
-
- DirtyCardToOopClosure* dcto_cl = sp->new_dcto_cl(cl, precision(),
- cl->gen_boundary(),
- parallel);
- ClearNoncleanCardWrapper clear_cl(dcto_cl, ct, parallel);
-
-
- // Process the chunk.
- process_chunk_boundaries(sp,
- dcto_cl,
- chunk_mr,
- used,
- lowest_non_clean,
- lowest_non_clean_base_chunk_index,
- lowest_non_clean_chunk_size);
-
- // We want the LNC array updates above in process_chunk_boundaries
- // to be visible before any of the card table value changes as a
- // result of the dirty card iteration below.
- OrderAccess::storestore();
-
- // We want to clear the cards: clear_cl here does the work of finding
- // contiguous dirty ranges of cards to process and clear.
- clear_cl.do_MemRegion(chunk_mr);
-
- // Find the next chunk of the stride.
- chunk_card_start += ParGCCardsPerStrideChunk * n_strides;
- }
-}
-
-void
-CMSCardTable::
-process_chunk_boundaries(Space* sp,
- DirtyCardToOopClosure* dcto_cl,
- MemRegion chunk_mr,
- MemRegion used,
- CardValue** lowest_non_clean,
- uintptr_t lowest_non_clean_base_chunk_index,
- size_t lowest_non_clean_chunk_size)
-{
- // We must worry about non-array objects that cross chunk boundaries,
- // because such objects are both precisely and imprecisely marked:
- // .. if the head of such an object is dirty, the entire object
- // needs to be scanned, under the interpretation that this
- // was an imprecise mark
- // .. if the head of such an object is not dirty, we can assume
- // precise marking and it's efficient to scan just the dirty
- // cards.
- // In either case, each scanned reference must be scanned precisely
- // once so as to avoid cloning of a young referent. For efficiency,
- // our closures depend on this property and do not protect against
- // double scans.
-
- uintptr_t start_chunk_index = addr_to_chunk_index(chunk_mr.start());
- assert(start_chunk_index >= lowest_non_clean_base_chunk_index, "Bounds error.");
- uintptr_t cur_chunk_index = start_chunk_index - lowest_non_clean_base_chunk_index;
-
- // First, set "our" lowest_non_clean entry, which would be
- // used by the thread scanning an adjoining left chunk with
- // a non-array object straddling the mutual boundary.
- // Find the object that spans our boundary, if one exists.
- // first_block is the block possibly straddling our left boundary.
- HeapWord* first_block = sp->block_start(chunk_mr.start());
- assert((chunk_mr.start() != used.start()) || (first_block == chunk_mr.start()),
- "First chunk should always have a co-initial block");
- // Does the block straddle the chunk's left boundary, and is it
- // a non-array object?
- if (first_block < chunk_mr.start() // first block straddles left bdry
- && sp->block_is_obj(first_block) // first block is an object
- && !(oop(first_block)->is_objArray() // first block is not an array (arrays are precisely dirtied)
- || oop(first_block)->is_typeArray())) {
- // Find our least non-clean card, so that a left neighbor
- // does not scan an object straddling the mutual boundary
- // too far to the right, and attempt to scan a portion of
- // that object twice.
- CardValue* first_dirty_card = NULL;
- CardValue* last_card_of_first_obj =
- byte_for(first_block + sp->block_size(first_block) - 1);
- CardValue* first_card_of_cur_chunk = byte_for(chunk_mr.start());
- CardValue* last_card_of_cur_chunk = byte_for(chunk_mr.last());
- CardValue* last_card_to_check = MIN2(last_card_of_cur_chunk, last_card_of_first_obj);
- // Note that this does not need to go beyond our last card
- // if our first object completely straddles this chunk.
- for (CardValue* cur = first_card_of_cur_chunk;
- cur <= last_card_to_check; cur++) {
- CardValue val = *cur;
- if (card_will_be_scanned(val)) {
- first_dirty_card = cur;
- break;
- } else {
- assert(!card_may_have_been_dirty(val), "Error");
- }
- }
- if (first_dirty_card != NULL) {
- assert(cur_chunk_index < lowest_non_clean_chunk_size, "Bounds error.");
- assert(lowest_non_clean[cur_chunk_index] == NULL,
- "Write exactly once : value should be stable hereafter for this round");
- lowest_non_clean[cur_chunk_index] = first_dirty_card;
- }
- } else {
- // In this case we can help our neighbor by just asking them
- // to stop at our first card (even though it may not be dirty).
- assert(lowest_non_clean[cur_chunk_index] == NULL, "Write once : value should be stable hereafter");
- CardValue* first_card_of_cur_chunk = byte_for(chunk_mr.start());
- lowest_non_clean[cur_chunk_index] = first_card_of_cur_chunk;
- }
-
- // Next, set our own max_to_do, which will strictly/exclusively bound
- // the highest address that we will scan past the right end of our chunk.
- HeapWord* max_to_do = NULL;
- if (chunk_mr.end() < used.end()) {
- // This is not the last chunk in the used region.
- // What is our last block? We check the first block of
- // the next (right) chunk rather than strictly check our last block
- // because it's potentially more efficient to do so.
- HeapWord* const last_block = sp->block_start(chunk_mr.end());
- assert(last_block <= chunk_mr.end(), "In case this property changes.");
- if ((last_block == chunk_mr.end()) // our last block does not straddle boundary
- || !sp->block_is_obj(last_block) // last_block isn't an object
- || oop(last_block)->is_objArray() // last_block is an array (precisely marked)
- || oop(last_block)->is_typeArray()) {
- max_to_do = chunk_mr.end();
- } else {
- assert(last_block < chunk_mr.end(), "Tautology");
- // It is a non-array object that straddles the right boundary of this chunk.
- // last_obj_card is the card corresponding to the start of the last object
- // in the chunk. Note that the last object may not start in
- // the chunk.
- CardValue* const last_obj_card = byte_for(last_block);
- const CardValue val = *last_obj_card;
- if (!card_will_be_scanned(val)) {
- assert(!card_may_have_been_dirty(val), "Error");
- // The card containing the head is not dirty. Any marks on
- // subsequent cards still in this chunk must have been made
- // precisely; we can cap processing at the end of our chunk.
- max_to_do = chunk_mr.end();
- } else {
- // The last object must be considered dirty, and extends onto the
- // following chunk. Look for a dirty card in that chunk that will
- // bound our processing.
- CardValue* limit_card = NULL;
- const size_t last_block_size = sp->block_size(last_block);
- CardValue* const last_card_of_last_obj =
- byte_for(last_block + last_block_size - 1);
- CardValue* const first_card_of_next_chunk = byte_for(chunk_mr.end());
- // This search potentially goes a long distance looking
- // for the next card that will be scanned, terminating
- // at the end of the last_block, if no earlier dirty card
- // is found.
- assert(byte_for(chunk_mr.end()) - byte_for(chunk_mr.start()) == ParGCCardsPerStrideChunk,
- "last card of next chunk may be wrong");
- for (CardValue* cur = first_card_of_next_chunk;
- cur <= last_card_of_last_obj; cur++) {
- const CardValue val = *cur;
- if (card_will_be_scanned(val)) {
- limit_card = cur; break;
- } else {
- assert(!card_may_have_been_dirty(val), "Error: card can't be skipped");
- }
- }
- if (limit_card != NULL) {
- max_to_do = addr_for(limit_card);
- assert(limit_card != NULL && max_to_do != NULL, "Error");
- } else {
- // The following is a pessimistic value, because it's possible
- // that a dirty card on a subsequent chunk has been cleared by
- // the time we get to look at it; we'll correct for that further below,
- // using the LNC array which records the least non-clean card
- // before cards were cleared in a particular chunk.
- limit_card = last_card_of_last_obj;
- max_to_do = last_block + last_block_size;
- assert(limit_card != NULL && max_to_do != NULL, "Error");
- }
- assert(0 < cur_chunk_index+1 && cur_chunk_index+1 < lowest_non_clean_chunk_size,
- "Bounds error.");
- // It is possible that a dirty card for the last object may have been
- // cleared before we had a chance to examine it. In that case, the value
- // will have been logged in the LNC for that chunk.
- // We need to examine as many chunks to the right as this object
- // covers. However, we need to bound this checking to the largest
- // entry in the LNC array: this is because the heap may expand
- // after the LNC array has been created but before we reach this point,
- // and the last block in our chunk may have been expanded to include
- // the expansion delta (and possibly subsequently allocated from, so
- // it wouldn't be sufficient to check whether that last block was
- // or was not an object at this point).
- uintptr_t last_chunk_index_to_check = addr_to_chunk_index(last_block + last_block_size - 1)
- - lowest_non_clean_base_chunk_index;
- const uintptr_t last_chunk_index = addr_to_chunk_index(used.last())
- - lowest_non_clean_base_chunk_index;
- if (last_chunk_index_to_check > last_chunk_index) {
- assert(last_block + last_block_size > used.end(),
- "Inconsistency detected: last_block [" PTR_FORMAT "," PTR_FORMAT "]"
- " does not exceed used.end() = " PTR_FORMAT ","
- " yet last_chunk_index_to_check " INTPTR_FORMAT
- " exceeds last_chunk_index " INTPTR_FORMAT,
- p2i(last_block), p2i(last_block + last_block_size),
- p2i(used.end()),
- last_chunk_index_to_check, last_chunk_index);
- assert(sp->used_region().end() > used.end(),
- "Expansion did not happen: "
- "[" PTR_FORMAT "," PTR_FORMAT ") -> [" PTR_FORMAT "," PTR_FORMAT ")",
- p2i(sp->used_region().start()), p2i(sp->used_region().end()),
- p2i(used.start()), p2i(used.end()));
- last_chunk_index_to_check = last_chunk_index;
- }
- for (uintptr_t lnc_index = cur_chunk_index + 1;
- lnc_index <= last_chunk_index_to_check;
- lnc_index++) {
- CardValue* lnc_card = lowest_non_clean[lnc_index];
- if (lnc_card != NULL) {
- // we can stop at the first non-NULL entry we find
- if (lnc_card <= limit_card) {
- limit_card = lnc_card;
- max_to_do = addr_for(limit_card);
- assert(limit_card != NULL && max_to_do != NULL, "Error");
- }
- // In any case, we break now
- break;
- } // else continue to look for a non-NULL entry if any
- }
- assert(limit_card != NULL && max_to_do != NULL, "Error");
- }
- assert(max_to_do != NULL, "OOPS 1 !");
- }
- assert(max_to_do != NULL, "OOPS 2!");
- } else {
- max_to_do = used.end();
- }
- assert(max_to_do != NULL, "OOPS 3!");
- // Now we can set the closure we're using so it doesn't to beyond
- // max_to_do.
- dcto_cl->set_min_done(max_to_do);
-#ifndef PRODUCT
- dcto_cl->set_last_bottom(max_to_do);
-#endif
-}
-
-void
-CMSCardTable::
-get_LNC_array_for_space(Space* sp,
- CardValue**& lowest_non_clean,
- uintptr_t& lowest_non_clean_base_chunk_index,
- size_t& lowest_non_clean_chunk_size) {
-
- int i = find_covering_region_containing(sp->bottom());
- MemRegion covered = _covered[i];
- size_t n_chunks = chunks_to_cover(covered);
-
- // Only the first thread to obtain the lock will resize the
- // LNC array for the covered region. Any later expansion can't affect
- // the used_at_save_marks region.
- // (I observed a bug in which the first thread to execute this would
- // resize, and then it would cause "expand_and_allocate" that would
- // increase the number of chunks in the covered region. Then a second
- // thread would come and execute this, see that the size didn't match,
- // and free and allocate again. So the first thread would be using a
- // freed "_lowest_non_clean" array.)
-
- // Do a dirty read here. If we pass the conditional then take the rare
- // event lock and do the read again in case some other thread had already
- // succeeded and done the resize.
- int cur_collection = CMSHeap::heap()->total_collections();
- // Updated _last_LNC_resizing_collection[i] must not be visible before
- // _lowest_non_clean and friends are visible. Therefore use acquire/release
- // to guarantee this on non TSO architecures.
- if (OrderAccess::load_acquire(&_last_LNC_resizing_collection[i]) != cur_collection) {
- MutexLocker x(ParGCRareEvent_lock);
- // This load_acquire is here for clarity only. The MutexLocker already fences.
- if (OrderAccess::load_acquire(&_last_LNC_resizing_collection[i]) != cur_collection) {
- if (_lowest_non_clean[i] == NULL ||
- n_chunks != _lowest_non_clean_chunk_size[i]) {
-
- // Should we delete the old?
- if (_lowest_non_clean[i] != NULL) {
- assert(n_chunks != _lowest_non_clean_chunk_size[i],
- "logical consequence");
- FREE_C_HEAP_ARRAY(CardPtr, _lowest_non_clean[i]);
- _lowest_non_clean[i] = NULL;
- }
- // Now allocate a new one if necessary.
- if (_lowest_non_clean[i] == NULL) {
- _lowest_non_clean[i] = NEW_C_HEAP_ARRAY(CardPtr, n_chunks, mtGC);
- _lowest_non_clean_chunk_size[i] = n_chunks;
- _lowest_non_clean_base_chunk_index[i] = addr_to_chunk_index(covered.start());
- for (int j = 0; j < (int)n_chunks; j++)
- _lowest_non_clean[i][j] = NULL;
- }
- }
- // Make sure this gets visible only after _lowest_non_clean* was initialized
- OrderAccess::release_store(&_last_LNC_resizing_collection[i], cur_collection);
- }
- }
- // In any case, now do the initialization.
- lowest_non_clean = _lowest_non_clean[i];
- lowest_non_clean_base_chunk_index = _lowest_non_clean_base_chunk_index[i];
- lowest_non_clean_chunk_size = _lowest_non_clean_chunk_size[i];
-}
-
-#ifdef ASSERT
-void CMSCardTable::verify_used_region_at_save_marks(Space* sp) const {
- MemRegion ur = sp->used_region();
- MemRegion urasm = sp->used_region_at_save_marks();
-
- if (!ur.contains(urasm)) {
- log_warning(gc)("CMS+ParNew: Did you forget to call save_marks()? "
- "[" PTR_FORMAT ", " PTR_FORMAT ") is not contained in "
- "[" PTR_FORMAT ", " PTR_FORMAT ")",
- p2i(urasm.start()), p2i(urasm.end()), p2i(ur.start()), p2i(ur.end()));
- MemRegion ur2 = sp->used_region();
- MemRegion urasm2 = sp->used_region_at_save_marks();
- if (!ur.equals(ur2)) {
- log_warning(gc)("CMS+ParNew: Flickering used_region()!!");
- }
- if (!urasm.equals(urasm2)) {
- log_warning(gc)("CMS+ParNew: Flickering used_region_at_save_marks()!!");
- }
- ShouldNotReachHere();
- }
-}
-#endif // ASSERT
--- a/src/hotspot/share/gc/cms/cmsCardTable.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2001, 2018, 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_GC_CMS_CMSCARDTABLE_HPP
-#define SHARE_GC_CMS_CMSCARDTABLE_HPP
-
-#include "gc/shared/cardTableRS.hpp"
-#include "utilities/globalDefinitions.hpp"
-
-class DirtyCardToOopClosure;
-class MemRegion;
-class OopsInGenClosure;
-class Space;
-
-class CMSCardTable : public CardTableRS {
-private:
- // Returns the number of chunks necessary to cover "mr".
- size_t chunks_to_cover(MemRegion mr);
-
- // Returns the index of the chunk in a stride which
- // covers the given address.
- uintptr_t addr_to_chunk_index(const void* addr);
-
- // Initializes "lowest_non_clean" to point to the array for the region
- // covering "sp", and "lowest_non_clean_base_chunk_index" to the chunk
- // index of the corresponding to the first element of that array.
- // Ensures that these arrays are of sufficient size, allocating if necessary.
- // May be called by several threads concurrently.
- void get_LNC_array_for_space(Space* sp,
- CardValue**& lowest_non_clean,
- uintptr_t& lowest_non_clean_base_chunk_index,
- size_t& lowest_non_clean_chunk_size);
-
- // Apply cl, which must either itself apply dcto_cl or be dcto_cl,
- // to the cards in the stride (of n_strides) within the given space.
- void process_stride(Space* sp,
- MemRegion used,
- jint stride, int n_strides,
- OopsInGenClosure* cl,
- CardTableRS* ct,
- CardValue** lowest_non_clean,
- uintptr_t lowest_non_clean_base_chunk_index,
- size_t lowest_non_clean_chunk_size);
-
- // Makes sure that chunk boundaries are handled appropriately, by
- // adjusting the min_done of dcto_cl, and by using a special card-table
- // value to indicate how min_done should be set.
- void process_chunk_boundaries(Space* sp,
- DirtyCardToOopClosure* dcto_cl,
- MemRegion chunk_mr,
- MemRegion used,
- CardValue** lowest_non_clean,
- uintptr_t lowest_non_clean_base_chunk_index,
- size_t lowest_non_clean_chunk_size);
-
- virtual void verify_used_region_at_save_marks(Space* sp) const NOT_DEBUG_RETURN;
-
-protected:
- // Work method used to implement non_clean_card_iterate_possibly_parallel()
- // above in the parallel case.
- virtual void non_clean_card_iterate_parallel_work(Space* sp, MemRegion mr,
- OopsInGenClosure* cl, CardTableRS* ct,
- uint n_threads);
-
-public:
- CMSCardTable(MemRegion whole_heap);
-};
-
-#endif // SHARE_GC_CMS_CMSCARDTABLE_HPP
--- a/src/hotspot/share/gc/cms/cmsGCStats.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-/*
- * Copyright (c) 2003, 2015, 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/cms/cmsGCStats.hpp"
-#include "gc/shared/gcUtil.inline.hpp"
-#include "runtime/globals.hpp"
-
-CMSGCStats::CMSGCStats() {
- _avg_promoted = new AdaptivePaddedNoZeroDevAverage(
- CMSExpAvgFactor,
- PromotedPadding);
-}
--- a/src/hotspot/share/gc/cms/cmsGCStats.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2003, 2018, 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_GC_CMS_CMSGCSTATS_HPP
-#define SHARE_GC_CMS_CMSGCSTATS_HPP
-
-#include "gc/shared/gcStats.hpp"
-
-class CMSGCStats : public GCStats {
- public:
- CMSGCStats();
-
- virtual Name kind() {
- return CMSGCStatsKind;
- }
-};
-
-#endif // SHARE_GC_CMS_CMSGCSTATS_HPP
--- a/src/hotspot/share/gc/cms/cmsHeap.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,263 +0,0 @@
-/*
- * Copyright (c) 2017, 2019, 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/cms/cmsCardTable.hpp"
-#include "gc/cms/cmsVMOperations.hpp"
-#include "gc/cms/compactibleFreeListSpace.hpp"
-#include "gc/cms/concurrentMarkSweepGeneration.hpp"
-#include "gc/cms/concurrentMarkSweepThread.hpp"
-#include "gc/cms/cmsHeap.hpp"
-#include "gc/cms/parNewGeneration.hpp"
-#include "gc/shared/genCollectedHeap.hpp"
-#include "gc/shared/genMemoryPools.hpp"
-#include "gc/shared/genOopClosures.inline.hpp"
-#include "gc/shared/strongRootsScope.hpp"
-#include "gc/shared/workgroup.hpp"
-#include "memory/universe.hpp"
-#include "oops/oop.inline.hpp"
-#include "runtime/vmThread.hpp"
-#include "services/memoryManager.hpp"
-#include "utilities/stack.inline.hpp"
-
-class CompactibleFreeListSpacePool : public CollectedMemoryPool {
-private:
- CompactibleFreeListSpace* _space;
-public:
- CompactibleFreeListSpacePool(CompactibleFreeListSpace* space,
- const char* name,
- size_t max_size,
- bool support_usage_threshold) :
- CollectedMemoryPool(name, space->capacity(), max_size, support_usage_threshold),
- _space(space) {
- }
-
- MemoryUsage get_memory_usage() {
- size_t max_heap_size = (available_for_allocation() ? max_size() : 0);
- size_t used = used_in_bytes();
- size_t committed = _space->capacity();
-
- return MemoryUsage(initial_size(), used, committed, max_heap_size);
- }
-
- size_t used_in_bytes() {
- return _space->used_stable();
- }
-};
-
-CMSHeap::CMSHeap() :
- GenCollectedHeap(Generation::ParNew,
- Generation::ConcurrentMarkSweep,
- "ParNew:CMS"),
- _workers(NULL),
- _eden_pool(NULL),
- _survivor_pool(NULL),
- _old_pool(NULL) {
-}
-
-jint CMSHeap::initialize() {
- jint status = GenCollectedHeap::initialize();
- if (status != JNI_OK) return status;
-
- _workers = new WorkGang("GC Thread", ParallelGCThreads,
- /* are_GC_task_threads */true,
- /* are_ConcurrentGC_threads */false);
- if (_workers == NULL) {
- return JNI_ENOMEM;
- }
- _workers->initialize_workers();
-
- // If we are running CMS, create the collector responsible
- // for collecting the CMS generations.
- if (!create_cms_collector()) {
- return JNI_ENOMEM;
- }
-
- return JNI_OK;
-}
-
-CardTableRS* CMSHeap::create_rem_set(const MemRegion& reserved_region) {
- return new CMSCardTable(reserved_region);
-}
-
-void CMSHeap::initialize_serviceability() {
- _young_manager = new GCMemoryManager("ParNew", "end of minor GC");
- _old_manager = new GCMemoryManager("ConcurrentMarkSweep", "end of major GC");
-
- ParNewGeneration* young = young_gen();
- _eden_pool = new ContiguousSpacePool(young->eden(),
- "Par Eden Space",
- young->max_eden_size(),
- false);
-
- _survivor_pool = new SurvivorContiguousSpacePool(young,
- "Par Survivor Space",
- young->max_survivor_size(),
- false);
-
- ConcurrentMarkSweepGeneration* old = (ConcurrentMarkSweepGeneration*) old_gen();
- _old_pool = new CompactibleFreeListSpacePool(old->cmsSpace(),
- "CMS Old Gen",
- old->reserved().byte_size(),
- true);
-
- _young_manager->add_pool(_eden_pool);
- _young_manager->add_pool(_survivor_pool);
- young->set_gc_manager(_young_manager);
-
- _old_manager->add_pool(_eden_pool);
- _old_manager->add_pool(_survivor_pool);
- _old_manager->add_pool(_old_pool);
- old ->set_gc_manager(_old_manager);
-
-}
-
-CMSHeap* CMSHeap::heap() {
- CollectedHeap* heap = Universe::heap();
- assert(heap != NULL, "Uninitialized access to CMSHeap::heap()");
- assert(heap->kind() == CollectedHeap::CMS, "Invalid name");
- return static_cast<CMSHeap*>(heap);
-}
-
-void CMSHeap::gc_threads_do(ThreadClosure* tc) const {
- assert(workers() != NULL, "should have workers here");
- workers()->threads_do(tc);
- ConcurrentMarkSweepThread::threads_do(tc);
-}
-
-void CMSHeap::print_gc_threads_on(outputStream* st) const {
- assert(workers() != NULL, "should have workers here");
- workers()->print_worker_threads_on(st);
- ConcurrentMarkSweepThread::print_all_on(st);
-}
-
-void CMSHeap::print_on_error(outputStream* st) const {
- GenCollectedHeap::print_on_error(st);
- st->cr();
- CMSCollector::print_on_error(st);
-}
-
-bool CMSHeap::create_cms_collector() {
- assert(old_gen()->kind() == Generation::ConcurrentMarkSweep,
- "Unexpected generation kinds");
- CMSCollector* collector =
- new CMSCollector((ConcurrentMarkSweepGeneration*) old_gen(), rem_set());
-
- if (collector == NULL || !collector->completed_initialization()) {
- if (collector) {
- delete collector; // Be nice in embedded situation
- }
- vm_shutdown_during_initialization("Could not create CMS collector");
- return false;
- }
- return true; // success
-}
-
-void CMSHeap::collect(GCCause::Cause cause) {
- if (should_do_concurrent_full_gc(cause)) {
- // Mostly concurrent full collection.
- collect_mostly_concurrent(cause);
- } else {
- GenCollectedHeap::collect(cause);
- }
-}
-
-bool CMSHeap::should_do_concurrent_full_gc(GCCause::Cause cause) {
- switch (cause) {
- case GCCause::_gc_locker: return GCLockerInvokesConcurrent;
- case GCCause::_java_lang_system_gc:
- case GCCause::_dcmd_gc_run: return ExplicitGCInvokesConcurrent;
- default: return false;
- }
-}
-
-void CMSHeap::collect_mostly_concurrent(GCCause::Cause cause) {
- assert(!Heap_lock->owned_by_self(), "Should not own Heap_lock");
-
- MutexLocker ml(Heap_lock);
- // Read the GC counts while holding the Heap_lock
- unsigned int full_gc_count_before = total_full_collections();
- unsigned int gc_count_before = total_collections();
- {
- MutexUnlocker mu(Heap_lock);
- VM_GenCollectFullConcurrent op(gc_count_before, full_gc_count_before, cause);
- VMThread::execute(&op);
- }
-}
-
-void CMSHeap::stop() {
- ConcurrentMarkSweepThread::cmst()->stop();
-}
-
-void CMSHeap::safepoint_synchronize_begin() {
- ConcurrentMarkSweepThread::synchronize(false);
-}
-
-void CMSHeap::safepoint_synchronize_end() {
- ConcurrentMarkSweepThread::desynchronize(false);
-}
-
-void CMSHeap::cms_process_roots(StrongRootsScope* scope,
- bool young_gen_as_roots,
- ScanningOption so,
- bool only_strong_roots,
- OopsInGenClosure* root_closure,
- CLDClosure* cld_closure) {
- MarkingCodeBlobClosure mark_code_closure(root_closure, !CodeBlobToOopClosure::FixRelocations);
- CLDClosure* weak_cld_closure = only_strong_roots ? NULL : cld_closure;
-
- process_roots(scope, so, root_closure, cld_closure, weak_cld_closure, &mark_code_closure);
-
- if (young_gen_as_roots &&
- _process_strong_tasks->try_claim_task(GCH_PS_younger_gens)) {
- root_closure->set_generation(young_gen());
- young_gen()->oop_iterate(root_closure);
- root_closure->reset_generation();
- }
-
- _process_strong_tasks->all_tasks_completed(scope->n_threads());
-}
-
-void CMSHeap::gc_prologue(bool full) {
- GenCollectedHeap::gc_prologue(full);
-};
-
-void CMSHeap::gc_epilogue(bool full) {
- GenCollectedHeap::gc_epilogue(full);
-};
-
-GrowableArray<GCMemoryManager*> CMSHeap::memory_managers() {
- GrowableArray<GCMemoryManager*> memory_managers(2);
- memory_managers.append(_young_manager);
- memory_managers.append(_old_manager);
- return memory_managers;
-}
-
-GrowableArray<MemoryPool*> CMSHeap::memory_pools() {
- GrowableArray<MemoryPool*> memory_pools(3);
- memory_pools.append(_eden_pool);
- memory_pools.append(_survivor_pool);
- memory_pools.append(_old_pool);
- return memory_pools;
-}
--- a/src/hotspot/share/gc/cms/cmsHeap.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-/*
- * Copyright (c) 2017, 2019, 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_GC_CMS_CMSHEAP_HPP
-#define SHARE_GC_CMS_CMSHEAP_HPP
-
-#include "gc/cms/concurrentMarkSweepGeneration.hpp"
-#include "gc/cms/parNewGeneration.hpp"
-#include "gc/shared/collectedHeap.hpp"
-#include "gc/shared/gcCause.hpp"
-#include "gc/shared/genCollectedHeap.hpp"
-#include "gc/shared/oopStorageParState.hpp"
-#include "utilities/growableArray.hpp"
-
-class CLDClosure;
-class GCMemoryManager;
-class MemoryPool;
-class OopsInGenClosure;
-class outputStream;
-class StrongRootsScope;
-class ThreadClosure;
-class WorkGang;
-
-class CMSHeap : public GenCollectedHeap {
-public:
- CMSHeap();
-
- // Returns JNI_OK on success
- virtual jint initialize();
- virtual CardTableRS* create_rem_set(const MemRegion& reserved_region);
-
- // Convenience function to be used in situations where the heap type can be
- // asserted to be this type.
- static CMSHeap* heap();
-
- virtual Name kind() const {
- return CollectedHeap::CMS;
- }
-
- virtual const char* name() const {
- return "Concurrent Mark Sweep";
- }
-
- WorkGang* workers() const { return _workers; }
-
- virtual void print_gc_threads_on(outputStream* st) const;
- virtual void gc_threads_do(ThreadClosure* tc) const;
- virtual void print_on_error(outputStream* st) const;
-
- // Perform a full collection of the heap; intended for use in implementing
- // "System.gc". This implies as full a collection as the CollectedHeap
- // supports. Caller does not hold the Heap_lock on entry.
- void collect(GCCause::Cause cause);
-
- void stop();
- void safepoint_synchronize_begin();
- void safepoint_synchronize_end();
-
- virtual GrowableArray<GCMemoryManager*> memory_managers();
- virtual GrowableArray<MemoryPool*> memory_pools();
-
- // If "young_gen_as_roots" is false, younger generations are
- // not scanned as roots; in this case, the caller must be arranging to
- // scan the younger generations itself. (For example, a generation might
- // explicitly mark reachable objects in younger generations, to avoid
- // excess storage retention.)
- void cms_process_roots(StrongRootsScope* scope,
- bool young_gen_as_roots,
- ScanningOption so,
- bool only_strong_roots,
- OopsInGenClosure* root_closure,
- CLDClosure* cld_closure);
-
- GCMemoryManager* old_manager() const { return _old_manager; }
-
- ParNewGeneration* young_gen() const {
- assert(_young_gen->kind() == Generation::ParNew, "Wrong generation type");
- return static_cast<ParNewGeneration*>(_young_gen);
- }
-
- ConcurrentMarkSweepGeneration* old_gen() const {
- assert(_old_gen->kind() == Generation::ConcurrentMarkSweep, "Wrong generation kind");
- return static_cast<ConcurrentMarkSweepGeneration*>(_old_gen);
- }
-
- // Apply "cur->do_oop" or "older->do_oop" to all the oops in objects
- // allocated since the last call to save_marks in the young generation.
- // The "cur" closure is applied to references in the younger generation
- // at "level", and the "older" closure to older generations.
- template <typename OopClosureType1, typename OopClosureType2>
- void oop_since_save_marks_iterate(OopClosureType1* cur,
- OopClosureType2* older);
-
-private:
- WorkGang* _workers;
- MemoryPool* _eden_pool;
- MemoryPool* _survivor_pool;
- MemoryPool* _old_pool;
-
- virtual void gc_prologue(bool full);
- virtual void gc_epilogue(bool full);
-
- virtual void initialize_serviceability();
-
- // Accessor for memory state verification support
- NOT_PRODUCT(
- virtual size_t skip_header_HeapWords() { return CMSCollector::skip_header_HeapWords(); }
- )
-
- // Returns success or failure.
- bool create_cms_collector();
-
- // In support of ExplicitGCInvokesConcurrent functionality
- bool should_do_concurrent_full_gc(GCCause::Cause cause);
-
- void collect_mostly_concurrent(GCCause::Cause cause);
-};
-
-#endif // SHARE_GC_CMS_CMSHEAP_HPP
--- a/src/hotspot/share/gc/cms/cmsHeap.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2001, 2018, 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_GC_CMS_CMSHEAP_INLINE_HPP
-#define SHARE_GC_CMS_CMSHEAP_INLINE_HPP
-
-#include "gc/cms/cmsHeap.hpp"
-#include "gc/cms/concurrentMarkSweepGeneration.inline.hpp"
-#include "gc/serial/defNewGeneration.inline.hpp"
-
-template <typename OopClosureType1, typename OopClosureType2>
-void CMSHeap::oop_since_save_marks_iterate(OopClosureType1* cur,
- OopClosureType2* older) {
- young_gen()->oop_since_save_marks_iterate(cur);
- old_gen()->oop_since_save_marks_iterate(older);
-}
-
-#endif // SHARE_GC_CMS_CMSHEAP_INLINE_HPP
--- a/src/hotspot/share/gc/cms/cmsLockVerifier.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2002, 2015, 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/cms/cmsLockVerifier.hpp"
-#include "gc/cms/concurrentMarkSweepThread.hpp"
-#include "memory/universe.hpp"
-#include "runtime/vmThread.hpp"
-
-///////////// Locking verification specific to CMS //////////////
-// Much like "assert_lock_strong()", except that it relaxes the
-// assertion somewhat for the parallel GC case, where VM thread
-// or the CMS thread might hold the lock on behalf of the parallel
-// threads. The second argument is in support of an extra locking
-// check for CFL spaces' free list locks.
-#ifndef PRODUCT
-void CMSLockVerifier::assert_locked(const Mutex* lock,
- const Mutex* p_lock1,
- const Mutex* p_lock2) {
- if (!Universe::is_fully_initialized()) {
- return;
- }
-
- Thread* myThread = Thread::current();
-
- if (lock == NULL) { // a "lock-free" structure, e.g. MUT, protected by CMS token
- assert(p_lock1 == NULL && p_lock2 == NULL, "Unexpected caller error");
- if (myThread->is_ConcurrentGC_thread()) {
- // This test might have to change in the future, if there can be
- // multiple peer CMS threads. But for now, if we're testing the CMS
- assert(myThread == ConcurrentMarkSweepThread::cmst(),
- "In CMS, CMS thread is the only Conc GC thread.");
- assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "CMS thread should have CMS token");
- } else if (myThread->is_VM_thread()) {
- assert(ConcurrentMarkSweepThread::vm_thread_has_cms_token(),
- "VM thread should have CMS token");
- } else {
- // Token should be held on our behalf by one of the other
- // of CMS or VM thread; not enough easily testable
- // state info to test which here.
- assert(myThread->is_GC_task_thread(), "Unexpected thread type");
- }
- return;
- }
-
- if (myThread->is_VM_thread()
- || myThread->is_ConcurrentGC_thread()
- || myThread->is_Java_thread()) {
- // Make sure that we are holding the associated lock.
- assert_lock_strong(lock);
- // The checking of p_lock is a spl case for CFLS' free list
- // locks: we make sure that none of the parallel GC work gang
- // threads are holding "sub-locks" of freeListLock(). We check only
- // the parDictionaryAllocLock because the others are too numerous.
- // This spl case code is somewhat ugly and any improvements
- // are welcome.
- assert(p_lock1 == NULL || !p_lock1->is_locked() || p_lock1->owned_by_self(),
- "Possible race between this and parallel GC threads");
- assert(p_lock2 == NULL || !p_lock2->is_locked() || p_lock2->owned_by_self(),
- "Possible race between this and parallel GC threads");
- } else if (myThread->is_GC_task_thread()) {
- // Make sure that the VM or CMS thread holds lock on our behalf
- // XXX If there were a concept of a gang_master for a (set of)
- // gang_workers, we could have used the identity of that thread
- // for checking ownership here; for now we just disjunct.
- assert(lock->owner() == VMThread::vm_thread() ||
- lock->owner() == ConcurrentMarkSweepThread::cmst(),
- "Should be locked by VM thread or CMS thread on my behalf");
- if (p_lock1 != NULL) {
- assert_lock_strong(p_lock1);
- }
- if (p_lock2 != NULL) {
- assert_lock_strong(p_lock2);
- }
- } else {
- // Make sure we didn't miss some other thread type calling into here;
- // perhaps as a result of future VM evolution.
- ShouldNotReachHere();
- }
-}
-#endif
--- a/src/hotspot/share/gc/cms/cmsLockVerifier.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2002, 2019, 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_GC_CMS_CMSLOCKVERIFIER_HPP
-#define SHARE_GC_CMS_CMSLOCKVERIFIER_HPP
-
-#include "runtime/mutex.hpp"
-
-///////////// Locking verification specific to CMS //////////////
-// Much like "assert_lock_strong()", except
-// that it relaxes the assertion somewhat for the parallel GC case, where
-// main GC thread or the CMS thread might hold the lock on behalf of
-// the parallel threads.
-class CMSLockVerifier: AllStatic {
- public:
- static void assert_locked(const Mutex* lock, const Mutex* p_lock1, const Mutex* p_lock2)
- PRODUCT_RETURN;
- static void assert_locked(const Mutex* lock, const Mutex* p_lock) {
- assert_locked(lock, p_lock, NULL);
- }
- static void assert_locked(const Mutex* lock) {
- assert_locked(lock, NULL);
- }
-};
-
-#endif // SHARE_GC_CMS_CMSLOCKVERIFIER_HPP
--- a/src/hotspot/share/gc/cms/cmsOopClosures.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,333 +0,0 @@
-/*
- * Copyright (c) 2007, 2019, 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_GC_CMS_CMSOOPCLOSURES_HPP
-#define SHARE_GC_CMS_CMSOOPCLOSURES_HPP
-
-#include "gc/shared/genOopClosures.hpp"
-#include "gc/shared/taskqueue.hpp"
-#include "memory/iterator.hpp"
-
-/////////////////////////////////////////////////////////////////
-// Closures used by ConcurrentMarkSweepGeneration's collector
-/////////////////////////////////////////////////////////////////
-class ConcurrentMarkSweepGeneration;
-class CMSBitMap;
-class CMSMarkStack;
-class CMSCollector;
-class MarkFromRootsClosure;
-class ParMarkFromRootsClosure;
-
-class Mutex;
-
-// Decode the oop and call do_oop on it.
-#define DO_OOP_WORK_DEFN \
- void do_oop(oop obj); \
- template <class T> inline void do_oop_work(T* p);
-
-// TODO: This duplication of the MetadataVisitingOopIterateClosure class is only needed
-// because some CMS OopClosures derive from OopsInGenClosure. It would be
-// good to get rid of them completely.
-class MetadataVisitingOopsInGenClosure: public OopsInGenClosure {
- public:
- virtual bool do_metadata() { return true; }
- virtual void do_klass(Klass* k);
- virtual void do_cld(ClassLoaderData* cld);
-};
-
-class MarkRefsIntoClosure: public MetadataVisitingOopsInGenClosure {
- private:
- const MemRegion _span;
- CMSBitMap* _bitMap;
- protected:
- DO_OOP_WORK_DEFN
- public:
- MarkRefsIntoClosure(MemRegion span, CMSBitMap* bitMap);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-class ParMarkRefsIntoClosure: public MetadataVisitingOopsInGenClosure {
- private:
- const MemRegion _span;
- CMSBitMap* _bitMap;
- protected:
- DO_OOP_WORK_DEFN
- public:
- ParMarkRefsIntoClosure(MemRegion span, CMSBitMap* bitMap);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-// A variant of the above used in certain kinds of CMS
-// marking verification.
-class MarkRefsIntoVerifyClosure: public MetadataVisitingOopsInGenClosure {
- private:
- const MemRegion _span;
- CMSBitMap* _verification_bm;
- CMSBitMap* _cms_bm;
- protected:
- DO_OOP_WORK_DEFN
- public:
- MarkRefsIntoVerifyClosure(MemRegion span, CMSBitMap* verification_bm,
- CMSBitMap* cms_bm);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-// The non-parallel version (the parallel version appears further below).
-class PushAndMarkClosure: public MetadataVisitingOopIterateClosure {
- private:
- CMSCollector* _collector;
- MemRegion _span;
- CMSBitMap* _bit_map;
- CMSBitMap* _mod_union_table;
- CMSMarkStack* _mark_stack;
- bool _concurrent_precleaning;
- protected:
- DO_OOP_WORK_DEFN
- public:
- PushAndMarkClosure(CMSCollector* collector,
- MemRegion span,
- ReferenceDiscoverer* rd,
- CMSBitMap* bit_map,
- CMSBitMap* mod_union_table,
- CMSMarkStack* mark_stack,
- bool concurrent_precleaning);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-// In the parallel case, the bit map and the
-// reference processor are currently all shared. Access to
-// these shared mutable structures must use appropriate
-// synchronization (for instance, via CAS). The marking stack
-// used in the non-parallel case above is here replaced with
-// an OopTaskQueue structure to allow efficient work stealing.
-class ParPushAndMarkClosure: public MetadataVisitingOopIterateClosure {
- private:
- CMSCollector* _collector;
- MemRegion _span;
- CMSBitMap* _bit_map;
- OopTaskQueue* _work_queue;
- protected:
- DO_OOP_WORK_DEFN
- public:
- ParPushAndMarkClosure(CMSCollector* collector,
- MemRegion span,
- ReferenceDiscoverer* rd,
- CMSBitMap* bit_map,
- OopTaskQueue* work_queue);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-// The non-parallel version (the parallel version appears further below).
-class MarkRefsIntoAndScanClosure: public MetadataVisitingOopsInGenClosure {
- private:
- MemRegion _span;
- CMSBitMap* _bit_map;
- CMSMarkStack* _mark_stack;
- PushAndMarkClosure _pushAndMarkClosure;
- CMSCollector* _collector;
- Mutex* _freelistLock;
- bool _yield;
- // Whether closure is being used for concurrent precleaning
- bool _concurrent_precleaning;
- protected:
- DO_OOP_WORK_DEFN
- public:
- MarkRefsIntoAndScanClosure(MemRegion span,
- ReferenceDiscoverer* rd,
- CMSBitMap* bit_map,
- CMSBitMap* mod_union_table,
- CMSMarkStack* mark_stack,
- CMSCollector* collector,
- bool should_yield,
- bool concurrent_precleaning);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-
- void set_freelistLock(Mutex* m) {
- _freelistLock = m;
- }
-
- private:
- inline void do_yield_check();
- void do_yield_work();
- bool take_from_overflow_list();
-};
-
-// In this, the parallel avatar of MarkRefsIntoAndScanClosure, the revisit
-// stack and the bitMap are shared, so access needs to be suitably
-// synchronized. An OopTaskQueue structure, supporting efficient
-// work stealing, replaces a CMSMarkStack for storing grey objects.
-class ParMarkRefsIntoAndScanClosure: public MetadataVisitingOopsInGenClosure {
- private:
- MemRegion _span;
- CMSBitMap* _bit_map;
- OopTaskQueue* _work_queue;
- const uint _low_water_mark;
- ParPushAndMarkClosure _parPushAndMarkClosure;
- protected:
- DO_OOP_WORK_DEFN
- public:
- ParMarkRefsIntoAndScanClosure(CMSCollector* collector,
- MemRegion span,
- ReferenceDiscoverer* rd,
- CMSBitMap* bit_map,
- OopTaskQueue* work_queue);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-
- void trim_queue(uint size);
-};
-
-// This closure is used during the concurrent marking phase
-// following the first checkpoint. Its use is buried in
-// the closure MarkFromRootsClosure.
-class PushOrMarkClosure: public MetadataVisitingOopIterateClosure {
- private:
- CMSCollector* _collector;
- MemRegion _span;
- CMSBitMap* _bitMap;
- CMSMarkStack* _markStack;
- HeapWord* const _finger;
- MarkFromRootsClosure* const
- _parent;
- protected:
- DO_OOP_WORK_DEFN
- public:
- PushOrMarkClosure(CMSCollector* cms_collector,
- MemRegion span,
- CMSBitMap* bitMap,
- CMSMarkStack* markStack,
- HeapWord* finger,
- MarkFromRootsClosure* parent);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-
- // Deal with a stack overflow condition
- void handle_stack_overflow(HeapWord* lost);
- private:
- inline void do_yield_check();
-};
-
-// A parallel (MT) version of the above.
-// This closure is used during the concurrent marking phase
-// following the first checkpoint. Its use is buried in
-// the closure ParMarkFromRootsClosure.
-class ParPushOrMarkClosure: public MetadataVisitingOopIterateClosure {
- private:
- CMSCollector* _collector;
- MemRegion _whole_span;
- MemRegion _span; // local chunk
- CMSBitMap* _bit_map;
- OopTaskQueue* _work_queue;
- CMSMarkStack* _overflow_stack;
- HeapWord* const _finger;
- HeapWord* volatile* const _global_finger_addr;
- ParMarkFromRootsClosure* const _parent;
- protected:
- DO_OOP_WORK_DEFN
- public:
- ParPushOrMarkClosure(CMSCollector* cms_collector,
- MemRegion span,
- CMSBitMap* bit_map,
- OopTaskQueue* work_queue,
- CMSMarkStack* mark_stack,
- HeapWord* finger,
- HeapWord* volatile* global_finger_addr,
- ParMarkFromRootsClosure* parent);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-
- // Deal with a stack overflow condition
- void handle_stack_overflow(HeapWord* lost);
- private:
- inline void do_yield_check();
-};
-
-// For objects in CMS generation, this closure marks
-// given objects (transitively) as being reachable/live.
-// This is currently used during the (weak) reference object
-// processing phase of the CMS final checkpoint step, as
-// well as during the concurrent precleaning of the discovered
-// reference lists.
-class CMSKeepAliveClosure: public MetadataVisitingOopIterateClosure {
- private:
- CMSCollector* _collector;
- const MemRegion _span;
- CMSMarkStack* _mark_stack;
- CMSBitMap* _bit_map;
- bool _concurrent_precleaning;
- protected:
- DO_OOP_WORK_DEFN
- public:
- CMSKeepAliveClosure(CMSCollector* collector, MemRegion span,
- CMSBitMap* bit_map, CMSMarkStack* mark_stack,
- bool cpc);
- bool concurrent_precleaning() const { return _concurrent_precleaning; }
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-class CMSInnerParMarkAndPushClosure: public MetadataVisitingOopIterateClosure {
- private:
- CMSCollector* _collector;
- MemRegion _span;
- OopTaskQueue* _work_queue;
- CMSBitMap* _bit_map;
- protected:
- DO_OOP_WORK_DEFN
- public:
- CMSInnerParMarkAndPushClosure(CMSCollector* collector,
- MemRegion span, CMSBitMap* bit_map,
- OopTaskQueue* work_queue);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-// A parallel (MT) version of the above, used when
-// reference processing is parallel; the only difference
-// is in the do_oop method.
-class CMSParKeepAliveClosure: public MetadataVisitingOopIterateClosure {
- private:
- MemRegion _span;
- OopTaskQueue* _work_queue;
- CMSBitMap* _bit_map;
- CMSInnerParMarkAndPushClosure
- _mark_and_push;
- const uint _low_water_mark;
- void trim_queue(uint max);
- protected:
- DO_OOP_WORK_DEFN
- public:
- CMSParKeepAliveClosure(CMSCollector* collector, MemRegion span,
- CMSBitMap* bit_map, OopTaskQueue* work_queue);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-#endif // SHARE_GC_CMS_CMSOOPCLOSURES_HPP
--- a/src/hotspot/share/gc/cms/cmsOopClosures.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * Copyright (c) 2007, 2019, 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_GC_CMS_CMSOOPCLOSURES_INLINE_HPP
-#define SHARE_GC_CMS_CMSOOPCLOSURES_INLINE_HPP
-
-#include "gc/cms/cmsOopClosures.hpp"
-#include "gc/cms/concurrentMarkSweepGeneration.hpp"
-#include "gc/shared/taskqueue.inline.hpp"
-#include "oops/access.inline.hpp"
-#include "oops/compressedOops.inline.hpp"
-#include "oops/oop.inline.hpp"
-
-// MetadataVisitingOopIterateClosure and MetadataVisitingOopsInGenClosure are duplicated,
-// until we get rid of OopsInGenClosure.
-
-inline void MetadataVisitingOopsInGenClosure::do_klass(Klass* k) {
- ClassLoaderData* cld = k->class_loader_data();
- MetadataVisitingOopsInGenClosure::do_cld(cld);
-}
-
-inline void MetadataVisitingOopsInGenClosure::do_cld(ClassLoaderData* cld) {
- cld->oops_do(this, ClassLoaderData::_claim_strong);
-}
-
-// Decode the oop and call do_oop on it.
-#define DO_OOP_WORK_IMPL(cls) \
- template <class T> void cls::do_oop_work(T* p) { \
- T heap_oop = RawAccess<>::oop_load(p); \
- if (!CompressedOops::is_null(heap_oop)) { \
- oop obj = CompressedOops::decode_not_null(heap_oop); \
- do_oop(obj); \
- } \
- } \
- inline void cls::do_oop(oop* p) { do_oop_work(p); } \
- inline void cls::do_oop(narrowOop* p) { do_oop_work(p); }
-
-DO_OOP_WORK_IMPL(MarkRefsIntoClosure)
-DO_OOP_WORK_IMPL(ParMarkRefsIntoClosure)
-DO_OOP_WORK_IMPL(MarkRefsIntoVerifyClosure)
-DO_OOP_WORK_IMPL(PushAndMarkClosure)
-DO_OOP_WORK_IMPL(ParPushAndMarkClosure)
-DO_OOP_WORK_IMPL(MarkRefsIntoAndScanClosure)
-DO_OOP_WORK_IMPL(ParMarkRefsIntoAndScanClosure)
-
-// Trim our work_queue so its length is below max at return
-inline void ParMarkRefsIntoAndScanClosure::trim_queue(uint max) {
- while (_work_queue->size() > max) {
- oop newOop;
- if (_work_queue->pop_local(newOop)) {
- assert(oopDesc::is_oop(newOop), "Expected an oop");
- assert(_bit_map->isMarked((HeapWord*)newOop),
- "only grey objects on this stack");
- // iterate over the oops in this oop, marking and pushing
- // the ones in CMS heap (i.e. in _span).
- newOop->oop_iterate(&_parPushAndMarkClosure);
- }
- }
-}
-
-DO_OOP_WORK_IMPL(PushOrMarkClosure)
-DO_OOP_WORK_IMPL(ParPushOrMarkClosure)
-DO_OOP_WORK_IMPL(CMSKeepAliveClosure)
-DO_OOP_WORK_IMPL(CMSInnerParMarkAndPushClosure)
-DO_OOP_WORK_IMPL(CMSParKeepAliveClosure)
-
-#endif // SHARE_GC_CMS_CMSOOPCLOSURES_INLINE_HPP
--- a/src/hotspot/share/gc/cms/cmsVMOperations.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,255 +0,0 @@
-/*
- * Copyright (c) 2005, 2019, 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/cms/cmsHeap.hpp"
-#include "gc/cms/cmsVMOperations.hpp"
-#include "gc/cms/concurrentMarkSweepGeneration.inline.hpp"
-#include "gc/cms/concurrentMarkSweepThread.hpp"
-#include "gc/shared/gcLocker.hpp"
-#include "gc/shared/gcTimer.hpp"
-#include "gc/shared/gcTraceTime.inline.hpp"
-#include "gc/shared/isGCActiveMark.hpp"
-#include "memory/universe.hpp"
-#include "runtime/handles.inline.hpp"
-#include "runtime/interfaceSupport.inline.hpp"
-#include "runtime/os.hpp"
-#include "utilities/dtrace.hpp"
-
-//////////////////////////////////////////////////////////
-// Methods in abstract class VM_CMS_Operation
-//////////////////////////////////////////////////////////
-void VM_CMS_Operation::verify_before_gc() {
- if (VerifyBeforeGC &&
- CMSHeap::heap()->total_collections() >= VerifyGCStartAt) {
- GCTraceTime(Info, gc, phases, verify) tm("Verify Before", _collector->_gc_timer_cm);
- HandleMark hm;
- FreelistLocker x(_collector);
- MutexLocker y(_collector->bitMapLock(), Mutex::_no_safepoint_check_flag);
- CMSHeap::heap()->prepare_for_verify();
- Universe::verify();
- }
-}
-
-void VM_CMS_Operation::verify_after_gc() {
- if (VerifyAfterGC &&
- CMSHeap::heap()->total_collections() >= VerifyGCStartAt) {
- GCTraceTime(Info, gc, phases, verify) tm("Verify After", _collector->_gc_timer_cm);
- HandleMark hm;
- FreelistLocker x(_collector);
- MutexLocker y(_collector->bitMapLock(), Mutex::_no_safepoint_check_flag);
- Universe::verify();
- }
-}
-
-bool VM_CMS_Operation::lost_race() const {
- if (CMSCollector::abstract_state() == CMSCollector::Idling) {
- // We lost a race to a foreground collection
- // -- there's nothing to do
- return true;
- }
- assert(CMSCollector::abstract_state() == legal_state(),
- "Inconsistent collector state?");
- return false;
-}
-
-bool VM_CMS_Operation::doit_prologue() {
- assert(Thread::current()->is_ConcurrentGC_thread(), "just checking");
- assert(!CMSCollector::foregroundGCShouldWait(), "Possible deadlock");
- assert(!ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "Possible deadlock");
-
- Heap_lock->lock();
- if (lost_race()) {
- assert(_prologue_succeeded == false, "Initialized in c'tor");
- Heap_lock->unlock();
- } else {
- _prologue_succeeded = true;
- }
- return _prologue_succeeded;
-}
-
-void VM_CMS_Operation::doit_epilogue() {
- assert(Thread::current()->is_ConcurrentGC_thread(), "just checking");
- assert(!CMSCollector::foregroundGCShouldWait(), "Possible deadlock");
- assert(!ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "Possible deadlock");
-
- if (Universe::has_reference_pending_list()) {
- Heap_lock->notify_all();
- }
- Heap_lock->unlock();
-}
-
-//////////////////////////////////////////////////////////
-// Methods in class VM_CMS_Initial_Mark
-//////////////////////////////////////////////////////////
-void VM_CMS_Initial_Mark::doit() {
- if (lost_race()) {
- // Nothing to do.
- return;
- }
- HS_PRIVATE_CMS_INITMARK_BEGIN();
- GCIdMark gc_id_mark(_gc_id);
-
- _collector->_gc_timer_cm->register_gc_pause_start("Initial Mark");
-
- CMSHeap* heap = CMSHeap::heap();
- GCCauseSetter gccs(heap, GCCause::_cms_initial_mark);
-
- VM_CMS_Operation::verify_before_gc();
-
- IsGCActiveMark x; // stop-world GC active
- _collector->do_CMS_operation(CMSCollector::CMS_op_checkpointRootsInitial, heap->gc_cause());
-
- VM_CMS_Operation::verify_after_gc();
-
- _collector->_gc_timer_cm->register_gc_pause_end();
-
- HS_PRIVATE_CMS_INITMARK_END();
-}
-
-//////////////////////////////////////////////////////////
-// Methods in class VM_CMS_Final_Remark_Operation
-//////////////////////////////////////////////////////////
-void VM_CMS_Final_Remark::doit() {
- if (lost_race()) {
- // Nothing to do.
- return;
- }
- HS_PRIVATE_CMS_REMARK_BEGIN();
- GCIdMark gc_id_mark(_gc_id);
-
- _collector->_gc_timer_cm->register_gc_pause_start("Final Mark");
-
- CMSHeap* heap = CMSHeap::heap();
- GCCauseSetter gccs(heap, GCCause::_cms_final_remark);
-
- VM_CMS_Operation::verify_before_gc();
-
- IsGCActiveMark x; // stop-world GC active
- _collector->do_CMS_operation(CMSCollector::CMS_op_checkpointRootsFinal, heap->gc_cause());
-
- VM_CMS_Operation::verify_after_gc();
-
- _collector->save_heap_summary();
- _collector->_gc_timer_cm->register_gc_pause_end();
-
- HS_PRIVATE_CMS_REMARK_END();
-}
-
-// VM operation to invoke a concurrent collection of a
-// GenCollectedHeap heap.
-void VM_GenCollectFullConcurrent::doit() {
- assert(Thread::current()->is_VM_thread(), "Should be VM thread");
- assert(GCLockerInvokesConcurrent || ExplicitGCInvokesConcurrent, "Unexpected");
-
- CMSHeap* heap = CMSHeap::heap();
- if (_gc_count_before == heap->total_collections()) {
- // The "full" of do_full_collection call below "forces"
- // a collection; the second arg, 0, below ensures that
- // only the young gen is collected. XXX In the future,
- // we'll probably need to have something in this interface
- // to say do this only if we are sure we will not bail
- // out to a full collection in this attempt, but that's
- // for the future.
- assert(SafepointSynchronize::is_at_safepoint(),
- "We can only be executing this arm of if at a safepoint");
- GCCauseSetter gccs(heap, _gc_cause);
- heap->do_full_collection(heap->must_clear_all_soft_refs(), GenCollectedHeap::YoungGen);
- } // Else no need for a foreground young gc
- assert((_gc_count_before < heap->total_collections()) ||
- (GCLocker::is_active() /* gc may have been skipped */
- && (_gc_count_before == heap->total_collections())),
- "total_collections() should be monotonically increasing");
-
- MutexLocker x(FullGCCount_lock, Mutex::_no_safepoint_check_flag);
- assert(_full_gc_count_before <= heap->total_full_collections(), "Error");
- if (heap->total_full_collections() == _full_gc_count_before) {
- // Nudge the CMS thread to start a concurrent collection.
- CMSCollector::request_full_gc(_full_gc_count_before, _gc_cause);
- } else {
- assert(_full_gc_count_before < heap->total_full_collections(), "Error");
- FullGCCount_lock->notify_all(); // Inform the Java thread its work is done
- }
-}
-
-bool VM_GenCollectFullConcurrent::evaluate_at_safepoint() const {
- Thread* thr = Thread::current();
- assert(thr != NULL, "Unexpected tid");
- if (!thr->is_Java_thread()) {
- assert(thr->is_VM_thread(), "Expected to be evaluated by VM thread");
- CMSHeap* heap = CMSHeap::heap();
- if (_gc_count_before != heap->total_collections()) {
- // No need to do a young gc, we'll just nudge the CMS thread
- // in the doit() method above, to be executed soon.
- assert(_gc_count_before < heap->total_collections(),
- "total_collections() should be monotonically increasing");
- return false; // no need for foreground young gc
- }
- }
- return true; // may still need foreground young gc
-}
-
-
-void VM_GenCollectFullConcurrent::doit_epilogue() {
- Thread* thr = Thread::current();
- assert(thr->is_Java_thread(), "just checking");
- JavaThread* jt = (JavaThread*)thr;
-
- if (Universe::has_reference_pending_list()) {
- Heap_lock->notify_all();
- }
- Heap_lock->unlock();
-
- // It is fine to test whether completed collections has
- // exceeded our request count without locking because
- // the completion count is monotonically increasing;
- // this will break for very long-running apps when the
- // count overflows and wraps around. XXX fix me !!!
- // e.g. at the rate of 1 full gc per ms, this could
- // overflow in about 1000 years.
- CMSHeap* heap = CMSHeap::heap();
- if (_gc_cause != GCCause::_gc_locker &&
- heap->total_full_collections_completed() <= _full_gc_count_before) {
- // maybe we should change the condition to test _gc_cause ==
- // GCCause::_java_lang_system_gc or GCCause::_dcmd_gc_run,
- // instead of _gc_cause != GCCause::_gc_locker
- assert(GCCause::is_user_requested_gc(_gc_cause),
- "the only way to get here if this was a System.gc()-induced GC");
- assert(ExplicitGCInvokesConcurrent, "Error");
- // Now, wait for witnessing concurrent gc cycle to complete,
- // but do so in native mode, because we want to lock the
- // FullGCEvent_lock, which may be needed by the VM thread
- // or by the CMS thread, so we do not want to be suspended
- // while holding that lock.
- ThreadToNativeFromVM native(jt);
- MutexLocker ml(FullGCCount_lock, Mutex::_no_safepoint_check_flag);
- // Either a concurrent or a stop-world full gc is sufficient
- // witness to our request.
- while (heap->total_full_collections_completed() <= _full_gc_count_before) {
- FullGCCount_lock->wait_without_safepoint_check();
- }
- }
-}
--- a/src/hotspot/share/gc/cms/cmsVMOperations.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-/*
- * Copyright (c) 2005, 2019, 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_GC_CMS_CMSVMOPERATIONS_HPP
-#define SHARE_GC_CMS_CMSVMOPERATIONS_HPP
-
-#include "gc/cms/concurrentMarkSweepGeneration.hpp"
-#include "gc/shared/gcCause.hpp"
-#include "gc/shared/gcId.hpp"
-#include "gc/shared/gcVMOperations.hpp"
-#include "runtime/vmOperations.hpp"
-
-// The VM_CMS_Operation is slightly different from
-// a VM_GC_Operation -- and would not have subclassed easily
-// to VM_GC_Operation without several changes to VM_GC_Operation.
-// To minimize the changes, we have replicated some of the VM_GC_Operation
-// functionality here. We will consolidate that back by doing subclassing
-// as appropriate in Dolphin.
-//
-// VM_Operation
-// VM_CMS_Operation
-// - implements the common portion of work done in support
-// of CMS' stop-world phases (initial mark and remark).
-//
-// VM_CMS_Initial_Mark
-// VM_CMS_Final_Mark
-//
-
-// Forward decl.
-class CMSCollector;
-
-class VM_CMS_Operation: public VM_Operation {
- protected:
- CMSCollector* _collector; // associated collector
- bool _prologue_succeeded; // whether doit_prologue succeeded
- uint _gc_id;
-
- bool lost_race() const;
-
- public:
- VM_CMS_Operation(CMSCollector* collector):
- _collector(collector),
- _prologue_succeeded(false),
- _gc_id(GCId::current()) {}
- ~VM_CMS_Operation() {}
-
- // The legal collector state for executing this CMS op.
- virtual const CMSCollector::CollectorState legal_state() const = 0;
-
- // Whether the pending list lock needs to be held
- virtual const bool needs_pending_list_lock() const = 0;
-
- // Execute operations in the context of the caller,
- // prior to execution of the vm operation itself.
- virtual bool doit_prologue();
- // Execute operations in the context of the caller,
- // following completion of the vm operation.
- virtual void doit_epilogue();
-
- virtual bool evaluate_at_safepoint() const { return true; }
- virtual bool is_cheap_allocated() const { return false; }
- virtual bool allow_nested_vm_operations() const { return false; }
- bool prologue_succeeded() const { return _prologue_succeeded; }
-
- void verify_before_gc();
- void verify_after_gc();
-};
-
-
-// VM_CMS_Operation for the initial marking phase of CMS.
-class VM_CMS_Initial_Mark: public VM_CMS_Operation {
- public:
- VM_CMS_Initial_Mark(CMSCollector* _collector) :
- VM_CMS_Operation(_collector) {}
-
- virtual VMOp_Type type() const { return VMOp_CMS_Initial_Mark; }
- virtual void doit();
-
- virtual const CMSCollector::CollectorState legal_state() const {
- return CMSCollector::InitialMarking;
- }
-
- virtual const bool needs_pending_list_lock() const {
- return false;
- }
-};
-
-// VM_CMS_Operation for the final remark phase of CMS.
-class VM_CMS_Final_Remark: public VM_CMS_Operation {
- public:
- VM_CMS_Final_Remark(CMSCollector* _collector) :
- VM_CMS_Operation(_collector) {}
- virtual VMOp_Type type() const { return VMOp_CMS_Final_Remark; }
- virtual void doit();
-
- virtual const CMSCollector::CollectorState legal_state() const {
- return CMSCollector::FinalMarking;
- }
-
- virtual const bool needs_pending_list_lock() const {
- return true;
- }
-};
-
-
-// VM operation to invoke a concurrent collection of the heap as a
-// GenCollectedHeap heap.
-class VM_GenCollectFullConcurrent: public VM_GC_Operation {
- public:
- VM_GenCollectFullConcurrent(uint gc_count_before,
- uint full_gc_count_before,
- GCCause::Cause gc_cause)
- : VM_GC_Operation(gc_count_before, gc_cause, full_gc_count_before, true /* full */)
- {
- assert(FullGCCount_lock != NULL, "Error");
- }
- ~VM_GenCollectFullConcurrent() {}
- virtual VMOp_Type type() const { return VMOp_GenCollectFullConcurrent; }
- virtual void doit();
- virtual void doit_epilogue();
- virtual bool is_cheap_allocated() const { return false; }
- virtual bool evaluate_at_safepoint() const;
-};
-
-#endif // SHARE_GC_CMS_CMSVMOPERATIONS_HPP
--- a/src/hotspot/share/gc/cms/cms_globals.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,429 +0,0 @@
-/*
- * Copyright (c) 1997, 2018, 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_GC_CMS_CMS_GLOBALS_HPP
-#define SHARE_GC_CMS_CMS_GLOBALS_HPP
-
-#define GC_CMS_FLAGS(develop, \
- develop_pd, \
- product, \
- product_pd, \
- diagnostic, \
- diagnostic_pd, \
- experimental, \
- notproduct, \
- manageable, \
- product_rw, \
- lp64_product, \
- range, \
- constraint, \
- writeable) \
- product(bool, UseCMSBestFit, true, \
- "Use CMS best fit allocation strategy") \
- \
- product(size_t, CMSOldPLABMax, 1024, \
- "Maximum size of CMS gen promotion LAB caches per worker " \
- "per block size") \
- range(1, max_uintx) \
- constraint(CMSOldPLABMaxConstraintFunc,AfterMemoryInit) \
- \
- product(size_t, CMSOldPLABMin, 16, \
- "Minimum size of CMS gen promotion LAB caches per worker " \
- "per block size") \
- range(1, max_uintx) \
- constraint(CMSOldPLABMinConstraintFunc,AfterMemoryInit) \
- \
- product(uintx, CMSOldPLABNumRefills, 4, \
- "Nominal number of refills of CMS gen promotion LAB cache " \
- "per worker per block size") \
- range(1, max_uintx) \
- \
- product(bool, CMSOldPLABResizeQuicker, false, \
- "React on-the-fly during a scavenge to a sudden " \
- "change in block demand rate") \
- \
- product(uintx, CMSOldPLABToleranceFactor, 4, \
- "The tolerance of the phase-change detector for on-the-fly " \
- "PLAB resizing during a scavenge") \
- range(1, max_uintx) \
- \
- product(uintx, CMSOldPLABReactivityFactor, 2, \
- "The gain in the feedback loop for on-the-fly PLAB resizing " \
- "during a scavenge") \
- range(1, max_uintx) \
- \
- product_pd(size_t, CMSYoungGenPerWorker, \
- "The maximum size of young gen chosen by default per GC worker " \
- "thread available") \
- range(1, max_uintx) \
- \
- product(uintx, CMSIncrementalSafetyFactor, 10, \
- "Percentage (0-100) used to add conservatism when computing the " \
- "duty cycle") \
- range(0, 100) \
- \
- product(uintx, CMSExpAvgFactor, 50, \
- "Percentage (0-100) used to weight the current sample when " \
- "computing exponential averages for CMS statistics") \
- range(0, 100) \
- \
- product(uintx, CMS_FLSWeight, 75, \
- "Percentage (0-100) used to weight the current sample when " \
- "computing exponentially decaying averages for CMS FLS " \
- "statistics") \
- range(0, 100) \
- \
- product(uintx, CMS_FLSPadding, 1, \
- "The multiple of deviation from mean to use for buffering " \
- "against volatility in free list demand") \
- range(0, max_juint) \
- \
- product(uintx, FLSCoalescePolicy, 2, \
- "CMS: aggressiveness level for coalescing, increasing " \
- "from 0 to 4") \
- range(0, 4) \
- \
- product(bool, FLSAlwaysCoalesceLarge, false, \
- "CMS: larger free blocks are always available for coalescing") \
- \
- product(double, FLSLargestBlockCoalesceProximity, 0.99, \
- "CMS: the smaller the percentage the greater the coalescing " \
- "force") \
- range(0.0, 1.0) \
- \
- product(double, CMSSmallCoalSurplusPercent, 1.05, \
- "CMS: the factor by which to inflate estimated demand of small " \
- "block sizes to prevent coalescing with an adjoining block") \
- range(0.0, DBL_MAX) \
- \
- product(double, CMSLargeCoalSurplusPercent, 0.95, \
- "CMS: the factor by which to inflate estimated demand of large " \
- "block sizes to prevent coalescing with an adjoining block") \
- range(0.0, DBL_MAX) \
- \
- product(double, CMSSmallSplitSurplusPercent, 1.10, \
- "CMS: the factor by which to inflate estimated demand of small " \
- "block sizes to prevent splitting to supply demand for smaller " \
- "blocks") \
- range(0.0, DBL_MAX) \
- \
- product(double, CMSLargeSplitSurplusPercent, 1.00, \
- "CMS: the factor by which to inflate estimated demand of large " \
- "block sizes to prevent splitting to supply demand for smaller " \
- "blocks") \
- range(0.0, DBL_MAX) \
- \
- product(bool, CMSExtrapolateSweep, false, \
- "CMS: cushion for block demand during sweep") \
- \
- product(uintx, CMS_SweepWeight, 75, \
- "Percentage (0-100) used to weight the current sample when " \
- "computing exponentially decaying average for inter-sweep " \
- "duration") \
- range(0, 100) \
- \
- product(uintx, CMS_SweepPadding, 1, \
- "The multiple of deviation from mean to use for buffering " \
- "against volatility in inter-sweep duration") \
- range(0, max_juint) \
- \
- product(uintx, CMS_SweepTimerThresholdMillis, 10, \
- "Skip block flux-rate sampling for an epoch unless inter-sweep " \
- "duration exceeds this threshold in milliseconds") \
- range(0, max_uintx) \
- \
- product(bool, CMSClassUnloadingEnabled, true, \
- "Whether class unloading enabled when using CMS GC") \
- \
- product(uintx, CMSClassUnloadingMaxInterval, 0, \
- "When CMS class unloading is enabled, the maximum CMS cycle " \
- "count for which classes may not be unloaded") \
- range(0, max_uintx) \
- \
- product(uintx, CMSIndexedFreeListReplenish, 4, \
- "Replenish an indexed free list with this number of chunks") \
- range(1, max_uintx) \
- \
- product(bool, CMSReplenishIntermediate, true, \
- "Replenish all intermediate free-list caches") \
- \
- product(bool, CMSSplitIndexedFreeListBlocks, true, \
- "When satisfying batched demand, split blocks from the " \
- "IndexedFreeList whose size is a multiple of requested size") \
- \
- product(bool, CMSLoopWarn, false, \
- "Warn in case of excessive CMS looping") \
- \
- notproduct(bool, CMSMarkStackOverflowALot, false, \
- "Simulate frequent marking stack / work queue overflow") \
- \
- notproduct(uintx, CMSMarkStackOverflowInterval, 1000, \
- "An \"interval\" counter that determines how frequently " \
- "to simulate overflow; a smaller number increases frequency") \
- \
- product(uintx, CMSMaxAbortablePrecleanLoops, 0, \
- "Maximum number of abortable preclean iterations, if > 0") \
- range(0, max_uintx) \
- \
- product(intx, CMSMaxAbortablePrecleanTime, 5000, \
- "Maximum time in abortable preclean (in milliseconds)") \
- range(0, max_intx) \
- \
- product(uintx, CMSAbortablePrecleanMinWorkPerIteration, 100, \
- "Nominal minimum work per abortable preclean iteration") \
- range(0, max_uintx) \
- \
- manageable(intx, CMSAbortablePrecleanWaitMillis, 100, \
- "Time that we sleep between iterations when not given " \
- "enough work per iteration") \
- range(0, max_intx) \
- \
- /* 4096 = CardTable::card_size_in_words * BitsPerWord */ \
- product(size_t, CMSRescanMultiple, 32, \
- "Size (in cards) of CMS parallel rescan task") \
- range(1, SIZE_MAX / 4096) \
- constraint(CMSRescanMultipleConstraintFunc,AfterMemoryInit) \
- \
- /* 4096 = CardTable::card_size_in_words * BitsPerWord */ \
- product(size_t, CMSConcMarkMultiple, 32, \
- "Size (in cards) of CMS concurrent MT marking task") \
- range(1, SIZE_MAX / 4096) \
- constraint(CMSConcMarkMultipleConstraintFunc,AfterMemoryInit) \
- \
- product(bool, CMSAbortSemantics, false, \
- "Whether abort-on-overflow semantics is implemented") \
- \
- product(bool, CMSParallelInitialMarkEnabled, true, \
- "Use the parallel initial mark.") \
- \
- product(bool, CMSParallelRemarkEnabled, true, \
- "Whether parallel remark enabled (only if ParNewGC)") \
- \
- product(bool, CMSParallelSurvivorRemarkEnabled, true, \
- "Whether parallel remark of survivor space " \
- "enabled (effective only if CMSParallelRemarkEnabled)") \
- \
- product(bool, CMSPLABRecordAlways, true, \
- "Always record survivor space PLAB boundaries (effective only " \
- "if CMSParallelSurvivorRemarkEnabled)") \
- \
- product(bool, CMSEdenChunksRecordAlways, true, \
- "Always record eden chunks used for the parallel initial mark " \
- "or remark of eden") \
- \
- product(bool, CMSConcurrentMTEnabled, true, \
- "Whether multi-threaded concurrent work enabled " \
- "(effective only if ParNewGC)") \
- \
- product(bool, CMSPrecleaningEnabled, true, \
- "Whether concurrent precleaning enabled") \
- \
- product(uintx, CMSPrecleanIter, 3, \
- "Maximum number of precleaning iteration passes") \
- range(0, 9) \
- \
- product(uintx, CMSPrecleanDenominator, 3, \
- "CMSPrecleanNumerator:CMSPrecleanDenominator yields convergence " \
- "ratio") \
- range(1, max_uintx) \
- constraint(CMSPrecleanDenominatorConstraintFunc,AfterErgo) \
- \
- product(uintx, CMSPrecleanNumerator, 2, \
- "CMSPrecleanNumerator:CMSPrecleanDenominator yields convergence " \
- "ratio") \
- range(0, max_uintx-1) \
- constraint(CMSPrecleanNumeratorConstraintFunc,AfterErgo) \
- \
- product(bool, CMSPrecleanRefLists1, true, \
- "Preclean ref lists during (initial) preclean phase") \
- \
- product(bool, CMSPrecleanRefLists2, false, \
- "Preclean ref lists during abortable preclean phase") \
- \
- product(bool, CMSPrecleanSurvivors1, false, \
- "Preclean survivors during (initial) preclean phase") \
- \
- product(bool, CMSPrecleanSurvivors2, true, \
- "Preclean survivors during abortable preclean phase") \
- \
- product(uintx, CMSPrecleanThreshold, 1000, \
- "Do not iterate again if number of dirty cards is less than this")\
- range(100, max_uintx) \
- \
- product(bool, CMSCleanOnEnter, true, \
- "Clean-on-enter optimization for reducing number of dirty cards") \
- \
- product(uintx, CMSRemarkVerifyVariant, 1, \
- "Choose variant (1,2) of verification following remark") \
- range(1, 2) \
- \
- product(size_t, CMSScheduleRemarkEdenSizeThreshold, 2*M, \
- "If Eden size is below this, do not try to schedule remark") \
- range(0, max_uintx) \
- \
- product(uintx, CMSScheduleRemarkEdenPenetration, 50, \
- "The Eden occupancy percentage (0-100) at which " \
- "to try and schedule remark pause") \
- range(0, 100) \
- \
- product(uintx, CMSScheduleRemarkSamplingRatio, 5, \
- "Start sampling eden top at least before young gen " \
- "occupancy reaches 1/<ratio> of the size at which " \
- "we plan to schedule remark") \
- range(1, max_uintx) \
- \
- product(uintx, CMSSamplingGrain, 16*K, \
- "The minimum distance between eden samples for CMS (see above)") \
- range(ObjectAlignmentInBytes, max_uintx) \
- constraint(CMSSamplingGrainConstraintFunc,AfterMemoryInit) \
- \
- product(bool, CMSScavengeBeforeRemark, false, \
- "Attempt scavenge before the CMS remark step") \
- \
- product(uintx, CMSWorkQueueDrainThreshold, 10, \
- "Don't drain below this size per parallel worker/thief") \
- range(1, max_juint) \
- constraint(CMSWorkQueueDrainThresholdConstraintFunc,AfterErgo) \
- \
- manageable(intx, CMSWaitDuration, 2000, \
- "Time in milliseconds that CMS thread waits for young GC") \
- range(min_jint, max_jint) \
- \
- develop(uintx, CMSCheckInterval, 1000, \
- "Interval in milliseconds that CMS thread checks if it " \
- "should start a collection cycle") \
- \
- product(bool, CMSYield, true, \
- "Yield between steps of CMS") \
- \
- product(size_t, CMSBitMapYieldQuantum, 10*M, \
- "Bitmap operations should process at most this many bits " \
- "between yields") \
- range(1, max_uintx) \
- constraint(CMSBitMapYieldQuantumConstraintFunc,AfterMemoryInit) \
- \
- product(bool, CMSPrintChunksInDump, false, \
- "If logging for the \"gc\" and \"promotion\" tags is enabled on" \
- "trace level include more detailed information about the" \
- "free chunks") \
- \
- product(bool, CMSPrintObjectsInDump, false, \
- "If logging for the \"gc\" and \"promotion\" tags is enabled on" \
- "trace level include more detailed information about the" \
- "allocated objects") \
- \
- diagnostic(bool, FLSVerifyAllHeapReferences, false, \
- "Verify that all references across the FLS boundary " \
- "are to valid objects") \
- \
- diagnostic(bool, FLSVerifyLists, false, \
- "Do lots of (expensive) FreeListSpace verification") \
- \
- diagnostic(bool, FLSVerifyIndexTable, false, \
- "Do lots of (expensive) FLS index table verification") \
- \
- product(uintx, CMSTriggerRatio, 80, \
- "Percentage of MinHeapFreeRatio in CMS generation that is " \
- "allocated before a CMS collection cycle commences") \
- range(0, 100) \
- \
- product(uintx, CMSBootstrapOccupancy, 50, \
- "Percentage CMS generation occupancy at which to " \
- "initiate CMS collection for bootstrapping collection stats") \
- range(0, 100) \
- \
- product(intx, CMSInitiatingOccupancyFraction, -1, \
- "Percentage CMS generation occupancy to start a CMS collection " \
- "cycle. A negative value means that CMSTriggerRatio is used") \
- range(min_intx, 100) \
- \
- manageable(intx, CMSTriggerInterval, -1, \
- "Commence a CMS collection cycle (at least) every so many " \
- "milliseconds (0 permanently, -1 disabled)") \
- range(-1, max_intx) \
- \
- product(bool, UseCMSInitiatingOccupancyOnly, false, \
- "Only use occupancy as a criterion for starting a CMS collection")\
- \
- product(uintx, CMSIsTooFullPercentage, 98, \
- "An absolute ceiling above which CMS will always consider the " \
- "unloading of classes when class unloading is enabled") \
- range(0, 100) \
- \
- develop(bool, CMSTestInFreeList, false, \
- "Check if the coalesced range is already in the " \
- "free lists as claimed") \
- \
- notproduct(bool, CMSVerifyReturnedBytes, false, \
- "Check that all the garbage collected was returned to the " \
- "free lists") \
- \
- diagnostic(bool, BindCMSThreadToCPU, false, \
- "Bind CMS Thread to CPU if possible") \
- \
- diagnostic(uintx, CPUForCMSThread, 0, \
- "When BindCMSThreadToCPU is true, the CPU to bind CMS thread to") \
- range(0, max_juint) \
- \
- product(uintx, CMSCoordinatorYieldSleepCount, 10, \
- "Number of times the coordinator GC thread will sleep while " \
- "yielding before giving up and resuming GC") \
- range(0, max_juint) \
- \
- product(uintx, CMSYieldSleepCount, 0, \
- "Number of times a GC thread (minus the coordinator) " \
- "will sleep while yielding before giving up and resuming GC") \
- range(0, max_juint) \
- \
- product(bool, ParGCUseLocalOverflow, false, \
- "Instead of a global overflow list, use local overflow stacks") \
- \
- product(bool, ParGCTrimOverflow, true, \
- "Eagerly trim the local overflow lists " \
- "(when ParGCUseLocalOverflow)") \
- \
- notproduct(bool, ParGCWorkQueueOverflowALot, false, \
- "Simulate work queue overflow in ParNew") \
- \
- notproduct(uintx, ParGCWorkQueueOverflowInterval, 1000, \
- "An `interval' counter that determines how frequently " \
- "we simulate overflow; a smaller number increases frequency") \
- \
- product(uintx, ParGCDesiredObjsFromOverflowList, 20, \
- "The desired number of objects to claim from the overflow list") \
- range(0, max_uintx) \
- \
- diagnostic(uintx, ParGCStridesPerThread, 2, \
- "The number of strides per worker thread that we divide up the " \
- "card table scanning work into") \
- range(1, max_uintx) \
- constraint(ParGCStridesPerThreadConstraintFunc,AfterErgo) \
- \
- diagnostic(intx, ParGCCardsPerStrideChunk, 256, \
- "The number of cards in each chunk of the parallel chunks used " \
- "during card table scanning") \
- range(1, max_intx) \
- constraint(ParGCCardsPerStrideChunkConstraintFunc,AfterMemoryInit)
-
-#endif // SHARE_GC_CMS_CMS_GLOBALS_HPP
--- a/src/hotspot/share/gc/cms/compactibleFreeListSpace.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,3141 +0,0 @@
-/*
- * Copyright (c) 2001, 2019, 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/cms/cmsHeap.hpp"
-#include "gc/cms/cmsLockVerifier.hpp"
-#include "gc/cms/compactibleFreeListSpace.hpp"
-#include "gc/cms/concurrentMarkSweepGeneration.inline.hpp"
-#include "gc/cms/concurrentMarkSweepThread.hpp"
-#include "gc/shared/blockOffsetTable.inline.hpp"
-#include "gc/shared/collectedHeap.inline.hpp"
-#include "gc/shared/genOopClosures.inline.hpp"
-#include "gc/shared/space.inline.hpp"
-#include "gc/shared/spaceDecorator.inline.hpp"
-#include "logging/log.hpp"
-#include "logging/logStream.hpp"
-#include "memory/allocation.inline.hpp"
-#include "memory/binaryTreeDictionary.inline.hpp"
-#include "memory/iterator.inline.hpp"
-#include "memory/resourceArea.hpp"
-#include "memory/universe.hpp"
-#include "oops/access.inline.hpp"
-#include "oops/compressedOops.inline.hpp"
-#include "oops/oop.inline.hpp"
-#include "runtime/globals.hpp"
-#include "runtime/handles.inline.hpp"
-#include "runtime/init.hpp"
-#include "runtime/java.hpp"
-#include "runtime/orderAccess.hpp"
-#include "runtime/vmThread.hpp"
-#include "utilities/align.hpp"
-#include "utilities/copy.hpp"
-
-// Specialize for AdaptiveFreeList which tries to avoid
-// splitting a chunk of a size that is under populated in favor of
-// an over populated size. The general get_better_list() just returns
-// the current list.
-template <>
-TreeList<FreeChunk, AdaptiveFreeList<FreeChunk> >*
-TreeList<FreeChunk, AdaptiveFreeList<FreeChunk> >::get_better_list(
- BinaryTreeDictionary<FreeChunk, ::AdaptiveFreeList<FreeChunk> >* dictionary) {
- // A candidate chunk has been found. If it is already under
- // populated, get a chunk associated with the hint for this
- // chunk.
-
- TreeList<FreeChunk, ::AdaptiveFreeList<FreeChunk> >* curTL = this;
- if (curTL->surplus() <= 0) {
- /* Use the hint to find a size with a surplus, and reset the hint. */
- TreeList<FreeChunk, ::AdaptiveFreeList<FreeChunk> >* hintTL = this;
- while (hintTL->hint() != 0) {
- assert(hintTL->hint() > hintTL->size(),
- "hint points in the wrong direction");
- hintTL = dictionary->find_list(hintTL->hint());
- assert(curTL != hintTL, "Infinite loop");
- if (hintTL == NULL ||
- hintTL == curTL /* Should not happen but protect against it */ ) {
- // No useful hint. Set the hint to NULL and go on.
- curTL->set_hint(0);
- break;
- }
- assert(hintTL->size() > curTL->size(), "hint is inconsistent");
- if (hintTL->surplus() > 0) {
- // The hint led to a list that has a surplus. Use it.
- // Set the hint for the candidate to an overpopulated
- // size.
- curTL->set_hint(hintTL->size());
- // Change the candidate.
- curTL = hintTL;
- break;
- }
- }
- }
- return curTL;
-}
-
-void AFLBinaryTreeDictionary::dict_census_update(size_t size, bool split, bool birth) {
- TreeList<FreeChunk, AdaptiveFreeList<FreeChunk> >* nd = find_list(size);
- if (nd) {
- if (split) {
- if (birth) {
- nd->increment_split_births();
- nd->increment_surplus();
- } else {
- nd->increment_split_deaths();
- nd->decrement_surplus();
- }
- } else {
- if (birth) {
- nd->increment_coal_births();
- nd->increment_surplus();
- } else {
- nd->increment_coal_deaths();
- nd->decrement_surplus();
- }
- }
- }
- // A list for this size may not be found (nd == 0) if
- // This is a death where the appropriate list is now
- // empty and has been removed from the list.
- // This is a birth associated with a LinAB. The chunk
- // for the LinAB is not in the dictionary.
-}
-
-bool AFLBinaryTreeDictionary::coal_dict_over_populated(size_t size) {
- if (FLSAlwaysCoalesceLarge) return true;
-
- TreeList<FreeChunk, AdaptiveFreeList<FreeChunk> >* list_of_size = find_list(size);
- // None of requested size implies overpopulated.
- return list_of_size == NULL || list_of_size->coal_desired() <= 0 ||
- list_of_size->count() > list_of_size->coal_desired();
-}
-
-// For each list in the tree, calculate the desired, desired
-// coalesce, count before sweep, and surplus before sweep.
-class BeginSweepClosure : public AscendTreeCensusClosure<FreeChunk, AdaptiveFreeList<FreeChunk> > {
- double _percentage;
- float _inter_sweep_current;
- float _inter_sweep_estimate;
- float _intra_sweep_estimate;
-
- public:
- BeginSweepClosure(double p, float inter_sweep_current,
- float inter_sweep_estimate,
- float intra_sweep_estimate) :
- _percentage(p),
- _inter_sweep_current(inter_sweep_current),
- _inter_sweep_estimate(inter_sweep_estimate),
- _intra_sweep_estimate(intra_sweep_estimate) { }
-
- void do_list(AdaptiveFreeList<FreeChunk>* fl) {
- double coalSurplusPercent = _percentage;
- fl->compute_desired(_inter_sweep_current, _inter_sweep_estimate, _intra_sweep_estimate);
- fl->set_coal_desired((ssize_t)((double)fl->desired() * coalSurplusPercent));
- fl->set_before_sweep(fl->count());
- fl->set_bfr_surp(fl->surplus());
- }
-};
-
-void AFLBinaryTreeDictionary::begin_sweep_dict_census(double coalSurplusPercent,
- float inter_sweep_current, float inter_sweep_estimate, float intra_sweep_estimate) {
- BeginSweepClosure bsc(coalSurplusPercent, inter_sweep_current,
- inter_sweep_estimate,
- intra_sweep_estimate);
- bsc.do_tree(root());
-}
-
-// Calculate surpluses for the lists in the tree.
-class setTreeSurplusClosure : public AscendTreeCensusClosure<FreeChunk, AdaptiveFreeList<FreeChunk> > {
- double percentage;
- public:
- setTreeSurplusClosure(double v) { percentage = v; }
-
- void do_list(AdaptiveFreeList<FreeChunk>* fl) {
- double splitSurplusPercent = percentage;
- fl->set_surplus(fl->count() -
- (ssize_t)((double)fl->desired() * splitSurplusPercent));
- }
-};
-
-void AFLBinaryTreeDictionary::set_tree_surplus(double splitSurplusPercent) {
- setTreeSurplusClosure sts(splitSurplusPercent);
- sts.do_tree(root());
-}
-
-// Set hints for the lists in the tree.
-class setTreeHintsClosure : public DescendTreeCensusClosure<FreeChunk, AdaptiveFreeList<FreeChunk> > {
- size_t hint;
- public:
- setTreeHintsClosure(size_t v) { hint = v; }
-
- void do_list(AdaptiveFreeList<FreeChunk>* fl) {
- fl->set_hint(hint);
- assert(fl->hint() == 0 || fl->hint() > fl->size(),
- "Current hint is inconsistent");
- if (fl->surplus() > 0) {
- hint = fl->size();
- }
- }
-};
-
-void AFLBinaryTreeDictionary::set_tree_hints(void) {
- setTreeHintsClosure sth(0);
- sth.do_tree(root());
-}
-
-// Save count before previous sweep and splits and coalesces.
-class clearTreeCensusClosure : public AscendTreeCensusClosure<FreeChunk, AdaptiveFreeList<FreeChunk> > {
- void do_list(AdaptiveFreeList<FreeChunk>* fl) {
- fl->set_prev_sweep(fl->count());
- fl->set_coal_births(0);
- fl->set_coal_deaths(0);
- fl->set_split_births(0);
- fl->set_split_deaths(0);
- }
-};
-
-void AFLBinaryTreeDictionary::clear_tree_census(void) {
- clearTreeCensusClosure ctc;
- ctc.do_tree(root());
-}
-
-// Do reporting and post sweep clean up.
-void AFLBinaryTreeDictionary::end_sweep_dict_census(double splitSurplusPercent) {
- // Does walking the tree 3 times hurt?
- set_tree_surplus(splitSurplusPercent);
- set_tree_hints();
- LogTarget(Trace, gc, freelist, stats) log;
- if (log.is_enabled()) {
- LogStream out(log);
- report_statistics(&out);
- }
- clear_tree_census();
-}
-
-// Print census information - counts, births, deaths, etc.
-// for each list in the tree. Also print some summary
-// information.
-class PrintTreeCensusClosure : public AscendTreeCensusClosure<FreeChunk, AdaptiveFreeList<FreeChunk> > {
- int _print_line;
- size_t _total_free;
- AdaptiveFreeList<FreeChunk> _total;
-
- public:
- PrintTreeCensusClosure() {
- _print_line = 0;
- _total_free = 0;
- }
- AdaptiveFreeList<FreeChunk>* total() { return &_total; }
- size_t total_free() { return _total_free; }
-
- void do_list(AdaptiveFreeList<FreeChunk>* fl) {
- LogStreamHandle(Debug, gc, freelist, census) out;
-
- if (++_print_line >= 40) {
- AdaptiveFreeList<FreeChunk>::print_labels_on(&out, "size");
- _print_line = 0;
- }
- fl->print_on(&out);
- _total_free += fl->count() * fl->size() ;
- total()->set_count( total()->count() + fl->count() );
- total()->set_bfr_surp( total()->bfr_surp() + fl->bfr_surp() );
- total()->set_surplus( total()->split_deaths() + fl->surplus() );
- total()->set_desired( total()->desired() + fl->desired() );
- total()->set_prev_sweep( total()->prev_sweep() + fl->prev_sweep() );
- total()->set_before_sweep(total()->before_sweep() + fl->before_sweep());
- total()->set_coal_births( total()->coal_births() + fl->coal_births() );
- total()->set_coal_deaths( total()->coal_deaths() + fl->coal_deaths() );
- total()->set_split_births(total()->split_births() + fl->split_births());
- total()->set_split_deaths(total()->split_deaths() + fl->split_deaths());
- }
-};
-
-void AFLBinaryTreeDictionary::print_dict_census(outputStream* st) const {
-
- st->print_cr("BinaryTree");
- AdaptiveFreeList<FreeChunk>::print_labels_on(st, "size");
- PrintTreeCensusClosure ptc;
- ptc.do_tree(root());
-
- AdaptiveFreeList<FreeChunk>* total = ptc.total();
- AdaptiveFreeList<FreeChunk>::print_labels_on(st, " ");
- total->print_on(st, "TOTAL\t");
- st->print_cr("total_free(words): " SIZE_FORMAT_W(16) " growth: %8.5f deficit: %8.5f",
- ptc.total_free(),
- (double)(total->split_births() + total->coal_births()
- - total->split_deaths() - total->coal_deaths())
- /(total->prev_sweep() != 0 ? (double)total->prev_sweep() : 1.0),
- (double)(total->desired() - total->count())
- /(total->desired() != 0 ? (double)total->desired() : 1.0));
-}
-
-/////////////////////////////////////////////////////////////////////////
-//// CompactibleFreeListSpace
-/////////////////////////////////////////////////////////////////////////
-
-// highest ranked free list lock rank
-int CompactibleFreeListSpace::_lockRank = Mutex::leaf + 3;
-
-// Defaults are 0 so things will break badly if incorrectly initialized.
-size_t CompactibleFreeListSpace::IndexSetStart = 0;
-size_t CompactibleFreeListSpace::IndexSetStride = 0;
-size_t CompactibleFreeListSpace::_min_chunk_size_in_bytes = 0;
-
-size_t MinChunkSize = 0;
-
-void CompactibleFreeListSpace::set_cms_values() {
- // Set CMS global values
- assert(MinChunkSize == 0, "already set");
-
- // MinChunkSize should be a multiple of MinObjAlignment and be large enough
- // for chunks to contain a FreeChunk.
- _min_chunk_size_in_bytes = align_up(sizeof(FreeChunk), MinObjAlignmentInBytes);
- MinChunkSize = _min_chunk_size_in_bytes / BytesPerWord;
-
- assert(IndexSetStart == 0 && IndexSetStride == 0, "already set");
- IndexSetStart = MinChunkSize;
- IndexSetStride = MinObjAlignment;
-}
-
-// Constructor
-CompactibleFreeListSpace::CompactibleFreeListSpace(BlockOffsetSharedArray* bs, MemRegion mr) :
- _rescan_task_size(CardTable::card_size_in_words * BitsPerWord *
- CMSRescanMultiple),
- _marking_task_size(CardTable::card_size_in_words * BitsPerWord *
- CMSConcMarkMultiple),
- _bt(bs, mr),
- _collector(NULL),
- // free list locks are in the range of values taken by _lockRank
- // This range currently is [_leaf+2, _leaf+3]
- // Note: this requires that CFLspace c'tors
- // are called serially in the order in which the locks are
- // are acquired in the program text. This is true today.
- _freelistLock(_lockRank--, "CompactibleFreeListSpace_lock", true,
- Monitor::_safepoint_check_never),
- _preconsumptionDirtyCardClosure(NULL),
- _parDictionaryAllocLock(Mutex::leaf - 1, // == rank(ExpandHeap_lock) - 1
- "CompactibleFreeListSpace_dict_par_lock", true,
- Monitor::_safepoint_check_never)
-{
- assert(sizeof(FreeChunk) / BytesPerWord <= MinChunkSize,
- "FreeChunk is larger than expected");
- _bt.set_space(this);
- initialize(mr, SpaceDecorator::Clear, SpaceDecorator::Mangle);
-
- _dictionary = new AFLBinaryTreeDictionary(mr);
-
- assert(_dictionary != NULL, "CMS dictionary initialization");
- // The indexed free lists are initially all empty and are lazily
- // filled in on demand. Initialize the array elements to NULL.
- initializeIndexedFreeListArray();
-
- _smallLinearAllocBlock.set(0, 0, 1024*SmallForLinearAlloc,
- SmallForLinearAlloc);
-
- // CMSIndexedFreeListReplenish should be at least 1
- CMSIndexedFreeListReplenish = MAX2((uintx)1, CMSIndexedFreeListReplenish);
- _promoInfo.setSpace(this);
- if (UseCMSBestFit) {
- _fitStrategy = FreeBlockBestFitFirst;
- } else {
- _fitStrategy = FreeBlockStrategyNone;
- }
- check_free_list_consistency();
-
- // Initialize locks for parallel case.
- for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) {
- _indexedFreeListParLocks[i] = new Mutex(Mutex::leaf - 1, // == ExpandHeap_lock - 1
- "a freelist par lock", true, Mutex::_safepoint_check_never);
- DEBUG_ONLY(
- _indexedFreeList[i].set_protecting_lock(_indexedFreeListParLocks[i]);
- )
- }
- _dictionary->set_par_lock(&_parDictionaryAllocLock);
-
- _used_stable = 0;
-}
-
-// Like CompactibleSpace forward() but always calls cross_threshold() to
-// update the block offset table. Removed initialize_threshold call because
-// CFLS does not use a block offset array for contiguous spaces.
-HeapWord* CompactibleFreeListSpace::forward(oop q, size_t size,
- CompactPoint* cp, HeapWord* compact_top) {
- // q is alive
- // First check if we should switch compaction space
- assert(this == cp->space, "'this' should be current compaction space.");
- size_t compaction_max_size = pointer_delta(end(), compact_top);
- assert(adjustObjectSize(size) == cp->space->adjust_object_size_v(size),
- "virtual adjustObjectSize_v() method is not correct");
- size_t adjusted_size = adjustObjectSize(size);
- assert(compaction_max_size >= MinChunkSize || compaction_max_size == 0,
- "no small fragments allowed");
- assert(minimum_free_block_size() == MinChunkSize,
- "for de-virtualized reference below");
- // Can't leave a nonzero size, residual fragment smaller than MinChunkSize
- if (adjusted_size + MinChunkSize > compaction_max_size &&
- adjusted_size != compaction_max_size) {
- do {
- // switch to next compaction space
- cp->space->set_compaction_top(compact_top);
- cp->space = cp->space->next_compaction_space();
- if (cp->space == NULL) {
- cp->gen = CMSHeap::heap()->young_gen();
- assert(cp->gen != NULL, "compaction must succeed");
- cp->space = cp->gen->first_compaction_space();
- assert(cp->space != NULL, "generation must have a first compaction space");
- }
- compact_top = cp->space->bottom();
- cp->space->set_compaction_top(compact_top);
- // The correct adjusted_size may not be the same as that for this method
- // (i.e., cp->space may no longer be "this" so adjust the size again.
- // Use the virtual method which is not used above to save the virtual
- // dispatch.
- adjusted_size = cp->space->adjust_object_size_v(size);
- compaction_max_size = pointer_delta(cp->space->end(), compact_top);
- assert(cp->space->minimum_free_block_size() == 0, "just checking");
- } while (adjusted_size > compaction_max_size);
- }
-
- // store the forwarding pointer into the mark word
- if ((HeapWord*)q != compact_top) {
- q->forward_to(oop(compact_top));
- assert(q->is_gc_marked(), "encoding the pointer should preserve the mark");
- } else {
- // if the object isn't moving we can just set the mark to the default
- // mark and handle it specially later on.
- q->init_mark_raw();
- assert(q->forwardee() == NULL, "should be forwarded to NULL");
- }
-
- compact_top += adjusted_size;
-
- // we need to update the offset table so that the beginnings of objects can be
- // found during scavenge. Note that we are updating the offset table based on
- // where the object will be once the compaction phase finishes.
-
- // Always call cross_threshold(). A contiguous space can only call it when
- // the compaction_top exceeds the current threshold but not for an
- // non-contiguous space.
- cp->threshold =
- cp->space->cross_threshold(compact_top - adjusted_size, compact_top);
- return compact_top;
-}
-
-// A modified copy of OffsetTableContigSpace::cross_threshold() with _offsets -> _bt
-// and use of single_block instead of alloc_block. The name here is not really
-// appropriate - maybe a more general name could be invented for both the
-// contiguous and noncontiguous spaces.
-
-HeapWord* CompactibleFreeListSpace::cross_threshold(HeapWord* start, HeapWord* the_end) {
- _bt.single_block(start, the_end);
- return end();
-}
-
-// Initialize them to NULL.
-void CompactibleFreeListSpace::initializeIndexedFreeListArray() {
- for (size_t i = 0; i < IndexSetSize; i++) {
- // Note that on platforms where objects are double word aligned,
- // the odd array elements are not used. It is convenient, however,
- // to map directly from the object size to the array element.
- _indexedFreeList[i].reset(IndexSetSize);
- _indexedFreeList[i].set_size(i);
- assert(_indexedFreeList[i].count() == 0, "reset check failed");
- assert(_indexedFreeList[i].head() == NULL, "reset check failed");
- assert(_indexedFreeList[i].tail() == NULL, "reset check failed");
- assert(_indexedFreeList[i].hint() == IndexSetSize, "reset check failed");
- }
-}
-
-size_t CompactibleFreeListSpace::obj_size(const HeapWord* addr) const {
- return adjustObjectSize(oop(addr)->size());
-}
-
-void CompactibleFreeListSpace::resetIndexedFreeListArray() {
- for (size_t i = 1; i < IndexSetSize; i++) {
- assert(_indexedFreeList[i].size() == (size_t) i,
- "Indexed free list sizes are incorrect");
- _indexedFreeList[i].reset(IndexSetSize);
- assert(_indexedFreeList[i].count() == 0, "reset check failed");
- assert(_indexedFreeList[i].head() == NULL, "reset check failed");
- assert(_indexedFreeList[i].tail() == NULL, "reset check failed");
- assert(_indexedFreeList[i].hint() == IndexSetSize, "reset check failed");
- }
-}
-
-void CompactibleFreeListSpace::reset(MemRegion mr) {
- resetIndexedFreeListArray();
- dictionary()->reset();
- if (BlockOffsetArrayUseUnallocatedBlock) {
- assert(end() == mr.end(), "We are compacting to the bottom of CMS gen");
- // Everything's allocated until proven otherwise.
- _bt.set_unallocated_block(end());
- }
- if (!mr.is_empty()) {
- assert(mr.word_size() >= MinChunkSize, "Chunk size is too small");
- _bt.single_block(mr.start(), mr.word_size());
- FreeChunk* fc = (FreeChunk*) mr.start();
- fc->set_size(mr.word_size());
- if (mr.word_size() >= IndexSetSize ) {
- returnChunkToDictionary(fc);
- } else {
- _bt.verify_not_unallocated((HeapWord*)fc, fc->size());
- _indexedFreeList[mr.word_size()].return_chunk_at_head(fc);
- }
- coalBirth(mr.word_size());
- }
- _promoInfo.reset();
- _smallLinearAllocBlock._ptr = NULL;
- _smallLinearAllocBlock._word_size = 0;
-}
-
-void CompactibleFreeListSpace::reset_after_compaction() {
- // Reset the space to the new reality - one free chunk.
- MemRegion mr(compaction_top(), end());
- reset(mr);
- // Now refill the linear allocation block(s) if possible.
- refillLinearAllocBlocksIfNeeded();
-}
-
-// Walks the entire dictionary, returning a coterminal
-// chunk, if it exists. Use with caution since it involves
-// a potentially complete walk of a potentially large tree.
-FreeChunk* CompactibleFreeListSpace::find_chunk_at_end() {
-
- assert_lock_strong(&_freelistLock);
-
- return dictionary()->find_chunk_ends_at(end());
-}
-
-
-#ifndef PRODUCT
-void CompactibleFreeListSpace::initializeIndexedFreeListArrayReturnedBytes() {
- for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) {
- _indexedFreeList[i].allocation_stats()->set_returned_bytes(0);
- }
-}
-
-size_t CompactibleFreeListSpace::sumIndexedFreeListArrayReturnedBytes() {
- size_t sum = 0;
- for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) {
- sum += _indexedFreeList[i].allocation_stats()->returned_bytes();
- }
- return sum;
-}
-
-size_t CompactibleFreeListSpace::totalCountInIndexedFreeLists() const {
- size_t count = 0;
- for (size_t i = IndexSetStart; i < IndexSetSize; i++) {
- debug_only(
- ssize_t total_list_count = 0;
- for (FreeChunk* fc = _indexedFreeList[i].head(); fc != NULL;
- fc = fc->next()) {
- total_list_count++;
- }
- assert(total_list_count == _indexedFreeList[i].count(),
- "Count in list is incorrect");
- )
- count += _indexedFreeList[i].count();
- }
- return count;
-}
-
-size_t CompactibleFreeListSpace::totalCount() {
- size_t num = totalCountInIndexedFreeLists();
- num += dictionary()->total_count();
- if (_smallLinearAllocBlock._word_size != 0) {
- num++;
- }
- return num;
-}
-#endif
-
-bool CompactibleFreeListSpace::is_free_block(const HeapWord* p) const {
- FreeChunk* fc = (FreeChunk*) p;
- return fc->is_free();
-}
-
-size_t CompactibleFreeListSpace::used() const {
- return capacity() - free();
-}
-
-size_t CompactibleFreeListSpace::used_stable() const {
- return _used_stable;
-}
-
-void CompactibleFreeListSpace::recalculate_used_stable() {
- _used_stable = used();
-}
-
-size_t CompactibleFreeListSpace::free() const {
- // "MT-safe, but not MT-precise"(TM), if you will: i.e.
- // if you do this while the structures are in flux you
- // may get an approximate answer only; for instance
- // because there is concurrent allocation either
- // directly by mutators or for promotion during a GC.
- // It's "MT-safe", however, in the sense that you are guaranteed
- // not to crash and burn, for instance, because of walking
- // pointers that could disappear as you were walking them.
- // The approximation is because the various components
- // that are read below are not read atomically (and
- // further the computation of totalSizeInIndexedFreeLists()
- // is itself a non-atomic computation. The normal use of
- // this is during a resize operation at the end of GC
- // and at that time you are guaranteed to get the
- // correct actual value. However, for instance, this is
- // also read completely asynchronously by the "perf-sampler"
- // that supports jvmstat, and you are apt to see the values
- // flicker in such cases.
- assert(_dictionary != NULL, "No _dictionary?");
- return (_dictionary->total_chunk_size(DEBUG_ONLY(freelistLock())) +
- totalSizeInIndexedFreeLists() +
- _smallLinearAllocBlock._word_size) * HeapWordSize;
-}
-
-size_t CompactibleFreeListSpace::max_alloc_in_words() const {
- assert(_dictionary != NULL, "No _dictionary?");
- assert_locked();
- size_t res = _dictionary->max_chunk_size();
- res = MAX2(res, MIN2(_smallLinearAllocBlock._word_size,
- (size_t) SmallForLinearAlloc - 1));
- // XXX the following could potentially be pretty slow;
- // should one, pessimistically for the rare cases when res
- // calculated above is less than IndexSetSize,
- // just return res calculated above? My reasoning was that
- // those cases will be so rare that the extra time spent doesn't
- // really matter....
- // Note: do not change the loop test i >= res + IndexSetStride
- // to i > res below, because i is unsigned and res may be zero.
- for (size_t i = IndexSetSize - 1; i >= res + IndexSetStride;
- i -= IndexSetStride) {
- if (_indexedFreeList[i].head() != NULL) {
- assert(_indexedFreeList[i].count() != 0, "Inconsistent FreeList");
- return i;
- }
- }
- return res;
-}
-
-void LinearAllocBlock::print_on(outputStream* st) const {
- st->print_cr(" LinearAllocBlock: ptr = " PTR_FORMAT ", word_size = " SIZE_FORMAT
- ", refillsize = " SIZE_FORMAT ", allocation_size_limit = " SIZE_FORMAT,
- p2i(_ptr), _word_size, _refillSize, _allocation_size_limit);
-}
-
-void CompactibleFreeListSpace::print_on(outputStream* st) const {
- st->print_cr("COMPACTIBLE FREELIST SPACE");
- st->print_cr(" Space:");
- Space::print_on(st);
-
- st->print_cr("promoInfo:");
- _promoInfo.print_on(st);
-
- st->print_cr("_smallLinearAllocBlock");
- _smallLinearAllocBlock.print_on(st);
-
- // dump_memory_block(_smallLinearAllocBlock->_ptr, 128);
-
- st->print_cr(" _fitStrategy = %s", BOOL_TO_STR(_fitStrategy));
-}
-
-void CompactibleFreeListSpace::print_indexed_free_lists(outputStream* st)
-const {
- reportIndexedFreeListStatistics(st);
- st->print_cr("Layout of Indexed Freelists");
- st->print_cr("---------------------------");
- AdaptiveFreeList<FreeChunk>::print_labels_on(st, "size");
- for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) {
- _indexedFreeList[i].print_on(st);
- for (FreeChunk* fc = _indexedFreeList[i].head(); fc != NULL; fc = fc->next()) {
- st->print_cr("\t[" PTR_FORMAT "," PTR_FORMAT ") %s",
- p2i(fc), p2i((HeapWord*)fc + i),
- fc->cantCoalesce() ? "\t CC" : "");
- }
- }
-}
-
-void CompactibleFreeListSpace::print_promo_info_blocks(outputStream* st)
-const {
- _promoInfo.print_on(st);
-}
-
-void CompactibleFreeListSpace::print_dictionary_free_lists(outputStream* st)
-const {
- _dictionary->report_statistics(st);
- st->print_cr("Layout of Freelists in Tree");
- st->print_cr("---------------------------");
- _dictionary->print_free_lists(st);
-}
-
-class BlkPrintingClosure: public BlkClosure {
- const CMSCollector* _collector;
- const CompactibleFreeListSpace* _sp;
- const CMSBitMap* _live_bit_map;
- const bool _post_remark;
- outputStream* _st;
-public:
- BlkPrintingClosure(const CMSCollector* collector,
- const CompactibleFreeListSpace* sp,
- const CMSBitMap* live_bit_map,
- outputStream* st):
- _collector(collector),
- _sp(sp),
- _live_bit_map(live_bit_map),
- _post_remark(collector->abstract_state() > CMSCollector::FinalMarking),
- _st(st) { }
- size_t do_blk(HeapWord* addr);
-};
-
-size_t BlkPrintingClosure::do_blk(HeapWord* addr) {
- size_t sz = _sp->block_size_no_stall(addr, _collector);
- assert(sz != 0, "Should always be able to compute a size");
- if (_sp->block_is_obj(addr)) {
- const bool dead = _post_remark && !_live_bit_map->isMarked(addr);
- _st->print_cr(PTR_FORMAT ": %s object of size " SIZE_FORMAT "%s",
- p2i(addr),
- dead ? "dead" : "live",
- sz,
- (!dead && CMSPrintObjectsInDump) ? ":" : ".");
- if (CMSPrintObjectsInDump && !dead) {
- oop(addr)->print_on(_st);
- _st->print_cr("--------------------------------------");
- }
- } else { // free block
- _st->print_cr(PTR_FORMAT ": free block of size " SIZE_FORMAT "%s",
- p2i(addr), sz, CMSPrintChunksInDump ? ":" : ".");
- if (CMSPrintChunksInDump) {
- ((FreeChunk*)addr)->print_on(_st);
- _st->print_cr("--------------------------------------");
- }
- }
- return sz;
-}
-
-void CompactibleFreeListSpace::dump_at_safepoint_with_locks(CMSCollector* c, outputStream* st) {
- st->print_cr("=========================");
- st->print_cr("Block layout in CMS Heap:");
- st->print_cr("=========================");
- BlkPrintingClosure bpcl(c, this, c->markBitMap(), st);
- blk_iterate(&bpcl);
-
- st->print_cr("=======================================");
- st->print_cr("Order & Layout of Promotion Info Blocks");
- st->print_cr("=======================================");
- print_promo_info_blocks(st);
-
- st->print_cr("===========================");
- st->print_cr("Order of Indexed Free Lists");
- st->print_cr("=========================");
- print_indexed_free_lists(st);
-
- st->print_cr("=================================");
- st->print_cr("Order of Free Lists in Dictionary");
- st->print_cr("=================================");
- print_dictionary_free_lists(st);
-}
-
-
-void CompactibleFreeListSpace::reportFreeListStatistics(const char* title) const {
- assert_lock_strong(&_freelistLock);
- Log(gc, freelist, stats) log;
- if (!log.is_debug()) {
- return;
- }
- log.debug("%s", title);
-
- LogStream out(log.debug());
- _dictionary->report_statistics(&out);
-
- if (log.is_trace()) {
- LogStream trace_out(log.trace());
- reportIndexedFreeListStatistics(&trace_out);
- size_t total_size = totalSizeInIndexedFreeLists() +
- _dictionary->total_chunk_size(DEBUG_ONLY(freelistLock()));
- log.trace(" free=" SIZE_FORMAT " frag=%1.4f", total_size, flsFrag());
- }
-}
-
-void CompactibleFreeListSpace::reportIndexedFreeListStatistics(outputStream* st) const {
- assert_lock_strong(&_freelistLock);
- st->print_cr("Statistics for IndexedFreeLists:");
- st->print_cr("--------------------------------");
- size_t total_size = totalSizeInIndexedFreeLists();
- size_t free_blocks = numFreeBlocksInIndexedFreeLists();
- st->print_cr("Total Free Space: " SIZE_FORMAT, total_size);
- st->print_cr("Max Chunk Size: " SIZE_FORMAT, maxChunkSizeInIndexedFreeLists());
- st->print_cr("Number of Blocks: " SIZE_FORMAT, free_blocks);
- if (free_blocks != 0) {
- st->print_cr("Av. Block Size: " SIZE_FORMAT, total_size/free_blocks);
- }
-}
-
-size_t CompactibleFreeListSpace::numFreeBlocksInIndexedFreeLists() const {
- size_t res = 0;
- for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) {
- debug_only(
- ssize_t recount = 0;
- for (FreeChunk* fc = _indexedFreeList[i].head(); fc != NULL;
- fc = fc->next()) {
- recount += 1;
- }
- assert(recount == _indexedFreeList[i].count(),
- "Incorrect count in list");
- )
- res += _indexedFreeList[i].count();
- }
- return res;
-}
-
-size_t CompactibleFreeListSpace::maxChunkSizeInIndexedFreeLists() const {
- for (size_t i = IndexSetSize - 1; i != 0; i -= IndexSetStride) {
- if (_indexedFreeList[i].head() != NULL) {
- assert(_indexedFreeList[i].count() != 0, "Inconsistent FreeList");
- return (size_t)i;
- }
- }
- return 0;
-}
-
-void CompactibleFreeListSpace::set_end(HeapWord* value) {
- HeapWord* prevEnd = end();
- assert(prevEnd != value, "unnecessary set_end call");
- assert(prevEnd == NULL || !BlockOffsetArrayUseUnallocatedBlock || value >= unallocated_block(),
- "New end is below unallocated block");
- _end = value;
- if (prevEnd != NULL) {
- // Resize the underlying block offset table.
- _bt.resize(pointer_delta(value, bottom()));
- if (value <= prevEnd) {
- assert(!BlockOffsetArrayUseUnallocatedBlock || value >= unallocated_block(),
- "New end is below unallocated block");
- } else {
- // Now, take this new chunk and add it to the free blocks.
- // Note that the BOT has not yet been updated for this block.
- size_t newFcSize = pointer_delta(value, prevEnd);
- // Add the block to the free lists, if possible coalescing it
- // with the last free block, and update the BOT and census data.
- addChunkToFreeListsAtEndRecordingStats(prevEnd, newFcSize);
- }
- }
-}
-
-class FreeListSpaceDCTOC : public FilteringDCTOC {
- CompactibleFreeListSpace* _cfls;
- CMSCollector* _collector;
- bool _parallel;
-protected:
- // Override.
-#define walk_mem_region_with_cl_DECL(ClosureType) \
- virtual void walk_mem_region_with_cl(MemRegion mr, \
- HeapWord* bottom, HeapWord* top, \
- ClosureType* cl); \
- void walk_mem_region_with_cl_par(MemRegion mr, \
- HeapWord* bottom, HeapWord* top, \
- ClosureType* cl); \
- void walk_mem_region_with_cl_nopar(MemRegion mr, \
- HeapWord* bottom, HeapWord* top, \
- ClosureType* cl)
- walk_mem_region_with_cl_DECL(OopIterateClosure);
- walk_mem_region_with_cl_DECL(FilteringClosure);
-
-public:
- FreeListSpaceDCTOC(CompactibleFreeListSpace* sp,
- CMSCollector* collector,
- OopIterateClosure* cl,
- CardTable::PrecisionStyle precision,
- HeapWord* boundary,
- bool parallel) :
- FilteringDCTOC(sp, cl, precision, boundary),
- _cfls(sp), _collector(collector), _parallel(parallel) {}
-};
-
-// We de-virtualize the block-related calls below, since we know that our
-// space is a CompactibleFreeListSpace.
-
-#define FreeListSpaceDCTOC__walk_mem_region_with_cl_DEFN(ClosureType) \
-void FreeListSpaceDCTOC::walk_mem_region_with_cl(MemRegion mr, \
- HeapWord* bottom, \
- HeapWord* top, \
- ClosureType* cl) { \
- if (_parallel) { \
- walk_mem_region_with_cl_par(mr, bottom, top, cl); \
- } else { \
- walk_mem_region_with_cl_nopar(mr, bottom, top, cl); \
- } \
-} \
-void FreeListSpaceDCTOC::walk_mem_region_with_cl_par(MemRegion mr, \
- HeapWord* bottom, \
- HeapWord* top, \
- ClosureType* cl) { \
- /* Skip parts that are before "mr", in case "block_start" sent us \
- back too far. */ \
- HeapWord* mr_start = mr.start(); \
- size_t bot_size = _cfls->CompactibleFreeListSpace::block_size(bottom); \
- HeapWord* next = bottom + bot_size; \
- while (next < mr_start) { \
- bottom = next; \
- bot_size = _cfls->CompactibleFreeListSpace::block_size(bottom); \
- next = bottom + bot_size; \
- } \
- \
- while (bottom < top) { \
- if (_cfls->CompactibleFreeListSpace::block_is_obj(bottom) && \
- !_cfls->CompactibleFreeListSpace::obj_allocated_since_save_marks( \
- oop(bottom)) && \
- !_collector->CMSCollector::is_dead_obj(oop(bottom))) { \
- size_t word_sz = oop(bottom)->oop_iterate_size(cl, mr); \
- bottom += _cfls->adjustObjectSize(word_sz); \
- } else { \
- bottom += _cfls->CompactibleFreeListSpace::block_size(bottom); \
- } \
- } \
-} \
-void FreeListSpaceDCTOC::walk_mem_region_with_cl_nopar(MemRegion mr, \
- HeapWord* bottom, \
- HeapWord* top, \
- ClosureType* cl) { \
- /* Skip parts that are before "mr", in case "block_start" sent us \
- back too far. */ \
- HeapWord* mr_start = mr.start(); \
- size_t bot_size = _cfls->CompactibleFreeListSpace::block_size_nopar(bottom); \
- HeapWord* next = bottom + bot_size; \
- while (next < mr_start) { \
- bottom = next; \
- bot_size = _cfls->CompactibleFreeListSpace::block_size_nopar(bottom); \
- next = bottom + bot_size; \
- } \
- \
- while (bottom < top) { \
- if (_cfls->CompactibleFreeListSpace::block_is_obj_nopar(bottom) && \
- !_cfls->CompactibleFreeListSpace::obj_allocated_since_save_marks( \
- oop(bottom)) && \
- !_collector->CMSCollector::is_dead_obj(oop(bottom))) { \
- size_t word_sz = oop(bottom)->oop_iterate_size(cl, mr); \
- bottom += _cfls->adjustObjectSize(word_sz); \
- } else { \
- bottom += _cfls->CompactibleFreeListSpace::block_size_nopar(bottom); \
- } \
- } \
-}
-
-// (There are only two of these, rather than N, because the split is due
-// only to the introduction of the FilteringClosure, a local part of the
-// impl of this abstraction.)
-FreeListSpaceDCTOC__walk_mem_region_with_cl_DEFN(OopIterateClosure)
-FreeListSpaceDCTOC__walk_mem_region_with_cl_DEFN(FilteringClosure)
-
-DirtyCardToOopClosure*
-CompactibleFreeListSpace::new_dcto_cl(OopIterateClosure* cl,
- CardTable::PrecisionStyle precision,
- HeapWord* boundary,
- bool parallel) {
- return new FreeListSpaceDCTOC(this, _collector, cl, precision, boundary, parallel);
-}
-
-
-// Note on locking for the space iteration functions:
-// since the collector's iteration activities are concurrent with
-// allocation activities by mutators, absent a suitable mutual exclusion
-// mechanism the iterators may go awry. For instance a block being iterated
-// may suddenly be allocated or divided up and part of it allocated and
-// so on.
-
-// Apply the given closure to each block in the space.
-void CompactibleFreeListSpace::blk_iterate_careful(BlkClosureCareful* cl) {
- assert_lock_strong(freelistLock());
- HeapWord *cur, *limit;
- for (cur = bottom(), limit = end(); cur < limit;
- cur += cl->do_blk_careful(cur));
-}
-
-// Apply the given closure to each block in the space.
-void CompactibleFreeListSpace::blk_iterate(BlkClosure* cl) {
- assert_lock_strong(freelistLock());
- HeapWord *cur, *limit;
- for (cur = bottom(), limit = end(); cur < limit;
- cur += cl->do_blk(cur));
-}
-
-// Apply the given closure to each oop in the space.
-void CompactibleFreeListSpace::oop_iterate(OopIterateClosure* cl) {
- assert_lock_strong(freelistLock());
- HeapWord *cur, *limit;
- size_t curSize;
- for (cur = bottom(), limit = end(); cur < limit;
- cur += curSize) {
- curSize = block_size(cur);
- if (block_is_obj(cur)) {
- oop(cur)->oop_iterate(cl);
- }
- }
-}
-
-// NOTE: In the following methods, in order to safely be able to
-// apply the closure to an object, we need to be sure that the
-// object has been initialized. We are guaranteed that an object
-// is initialized if we are holding the Heap_lock with the
-// world stopped.
-void CompactibleFreeListSpace::verify_objects_initialized() const {
- if (is_init_completed()) {
- assert_locked_or_safepoint(Heap_lock);
- if (Universe::is_fully_initialized()) {
- guarantee(SafepointSynchronize::is_at_safepoint(),
- "Required for objects to be initialized");
- }
- } // else make a concession at vm start-up
-}
-
-// Apply the given closure to each object in the space
-void CompactibleFreeListSpace::object_iterate(ObjectClosure* blk) {
- assert_lock_strong(freelistLock());
- NOT_PRODUCT(verify_objects_initialized());
- HeapWord *cur, *limit;
- size_t curSize;
- for (cur = bottom(), limit = end(); cur < limit;
- cur += curSize) {
- curSize = block_size(cur);
- if (block_is_obj(cur)) {
- blk->do_object(oop(cur));
- }
- }
-}
-
-// Apply the given closure to each live object in the space
-// The usage of CompactibleFreeListSpace
-// by the ConcurrentMarkSweepGeneration for concurrent GC's allows
-// objects in the space with references to objects that are no longer
-// valid. For example, an object may reference another object
-// that has already been sweep up (collected). This method uses
-// obj_is_alive() to determine whether it is safe to apply the closure to
-// an object. See obj_is_alive() for details on how liveness of an
-// object is decided.
-
-void CompactibleFreeListSpace::safe_object_iterate(ObjectClosure* blk) {
- assert_lock_strong(freelistLock());
- NOT_PRODUCT(verify_objects_initialized());
- HeapWord *cur, *limit;
- size_t curSize;
- for (cur = bottom(), limit = end(); cur < limit;
- cur += curSize) {
- curSize = block_size(cur);
- if (block_is_obj(cur) && obj_is_alive(cur)) {
- blk->do_object(oop(cur));
- }
- }
-}
-
-void CompactibleFreeListSpace::object_iterate_mem(MemRegion mr,
- UpwardsObjectClosure* cl) {
- assert_locked(freelistLock());
- NOT_PRODUCT(verify_objects_initialized());
- assert(!mr.is_empty(), "Should be non-empty");
- // We use MemRegion(bottom(), end()) rather than used_region() below
- // because the two are not necessarily equal for some kinds of
- // spaces, in particular, certain kinds of free list spaces.
- // We could use the more complicated but more precise:
- // MemRegion(used_region().start(), align_up(used_region().end(), CardSize))
- // but the slight imprecision seems acceptable in the assertion check.
- assert(MemRegion(bottom(), end()).contains(mr),
- "Should be within used space");
- HeapWord* prev = cl->previous(); // max address from last time
- if (prev >= mr.end()) { // nothing to do
- return;
- }
- // This assert will not work when we go from cms space to perm
- // space, and use same closure. Easy fix deferred for later. XXX YSR
- // assert(prev == NULL || contains(prev), "Should be within space");
-
- bool last_was_obj_array = false;
- HeapWord *blk_start_addr, *region_start_addr;
- if (prev > mr.start()) {
- region_start_addr = prev;
- blk_start_addr = prev;
- // The previous invocation may have pushed "prev" beyond the
- // last allocated block yet there may be still be blocks
- // in this region due to a particular coalescing policy.
- // Relax the assertion so that the case where the unallocated
- // block is maintained and "prev" is beyond the unallocated
- // block does not cause the assertion to fire.
- assert((BlockOffsetArrayUseUnallocatedBlock &&
- (!is_in(prev))) ||
- (blk_start_addr == block_start(region_start_addr)), "invariant");
- } else {
- region_start_addr = mr.start();
- blk_start_addr = block_start(region_start_addr);
- }
- HeapWord* region_end_addr = mr.end();
- MemRegion derived_mr(region_start_addr, region_end_addr);
- while (blk_start_addr < region_end_addr) {
- const size_t size = block_size(blk_start_addr);
- if (block_is_obj(blk_start_addr)) {
- last_was_obj_array = cl->do_object_bm(oop(blk_start_addr), derived_mr);
- } else {
- last_was_obj_array = false;
- }
- blk_start_addr += size;
- }
- if (!last_was_obj_array) {
- assert((bottom() <= blk_start_addr) && (blk_start_addr <= end()),
- "Should be within (closed) used space");
- assert(blk_start_addr > prev, "Invariant");
- cl->set_previous(blk_start_addr); // min address for next time
- }
-}
-
-// Callers of this iterator beware: The closure application should
-// be robust in the face of uninitialized objects and should (always)
-// return a correct size so that the next addr + size below gives us a
-// valid block boundary. [See for instance,
-// ScanMarkedObjectsAgainCarefullyClosure::do_object_careful()
-// in ConcurrentMarkSweepGeneration.cpp.]
-HeapWord*
-CompactibleFreeListSpace::object_iterate_careful_m(MemRegion mr,
- ObjectClosureCareful* cl) {
- assert_lock_strong(freelistLock());
- // Can't use used_region() below because it may not necessarily
- // be the same as [bottom(),end()); although we could
- // use [used_region().start(),align_up(used_region().end(),CardSize)),
- // that appears too cumbersome, so we just do the simpler check
- // in the assertion below.
- assert(!mr.is_empty() && MemRegion(bottom(),end()).contains(mr),
- "mr should be non-empty and within used space");
- HeapWord *addr, *end;
- size_t size;
- for (addr = block_start_careful(mr.start()), end = mr.end();
- addr < end; addr += size) {
- FreeChunk* fc = (FreeChunk*)addr;
- if (fc->is_free()) {
- // Since we hold the free list lock, which protects direct
- // allocation in this generation by mutators, a free object
- // will remain free throughout this iteration code.
- size = fc->size();
- } else {
- // Note that the object need not necessarily be initialized,
- // because (for instance) the free list lock does NOT protect
- // object initialization. The closure application below must
- // therefore be correct in the face of uninitialized objects.
- size = cl->do_object_careful_m(oop(addr), mr);
- if (size == 0) {
- // An unparsable object found. Signal early termination.
- return addr;
- }
- }
- }
- return NULL;
-}
-
-
-HeapWord* CompactibleFreeListSpace::block_start_const(const void* p) const {
- NOT_PRODUCT(verify_objects_initialized());
- return _bt.block_start(p);
-}
-
-HeapWord* CompactibleFreeListSpace::block_start_careful(const void* p) const {
- return _bt.block_start_careful(p);
-}
-
-size_t CompactibleFreeListSpace::block_size(const HeapWord* p) const {
- NOT_PRODUCT(verify_objects_initialized());
- // This must be volatile, or else there is a danger that the compiler
- // will compile the code below into a sometimes-infinite loop, by keeping
- // the value read the first time in a register.
- while (true) {
- // We must do this until we get a consistent view of the object.
- if (FreeChunk::indicatesFreeChunk(p)) {
- volatile FreeChunk* fc = (volatile FreeChunk*)p;
- size_t res = fc->size();
-
- // Bugfix for systems with weak memory model (PPC64/IA64). The
- // block's free bit was set and we have read the size of the
- // block. Acquire and check the free bit again. If the block is
- // still free, the read size is correct.
- OrderAccess::acquire();
-
- // If the object is still a free chunk, return the size, else it
- // has been allocated so try again.
- if (FreeChunk::indicatesFreeChunk(p)) {
- assert(res != 0, "Block size should not be 0");
- return res;
- }
- } else {
- // Ensure klass read before size.
- Klass* k = oop(p)->klass_or_null_acquire();
- if (k != NULL) {
- assert(k->is_klass(), "Should really be klass oop.");
- oop o = (oop)p;
- assert(oopDesc::is_oop(o, true /* ignore mark word */), "Should be an oop.");
-
- size_t res = o->size_given_klass(k);
- res = adjustObjectSize(res);
- assert(res != 0, "Block size should not be 0");
- return res;
- }
- }
- }
-}
-
-// TODO: Now that is_parsable is gone, we should combine these two functions.
-// A variant of the above that uses the Printezis bits for
-// unparsable but allocated objects. This avoids any possible
-// stalls waiting for mutators to initialize objects, and is
-// thus potentially faster than the variant above. However,
-// this variant may return a zero size for a block that is
-// under mutation and for which a consistent size cannot be
-// inferred without stalling; see CMSCollector::block_size_if_printezis_bits().
-size_t CompactibleFreeListSpace::block_size_no_stall(HeapWord* p,
- const CMSCollector* c)
-const {
- assert(MemRegion(bottom(), end()).contains(p), "p not in space");
- // This must be volatile, or else there is a danger that the compiler
- // will compile the code below into a sometimes-infinite loop, by keeping
- // the value read the first time in a register.
- DEBUG_ONLY(uint loops = 0;)
- while (true) {
- // We must do this until we get a consistent view of the object.
- if (FreeChunk::indicatesFreeChunk(p)) {
- volatile FreeChunk* fc = (volatile FreeChunk*)p;
- size_t res = fc->size();
-
- // Bugfix for systems with weak memory model (PPC64/IA64). The
- // free bit of the block was set and we have read the size of
- // the block. Acquire and check the free bit again. If the
- // block is still free, the read size is correct.
- OrderAccess::acquire();
-
- if (FreeChunk::indicatesFreeChunk(p)) {
- assert(res != 0, "Block size should not be 0");
- assert(loops == 0, "Should be 0");
- return res;
- }
- } else {
- // Ensure klass read before size.
- Klass* k = oop(p)->klass_or_null_acquire();
- if (k != NULL) {
- assert(k->is_klass(), "Should really be klass oop.");
- oop o = (oop)p;
- assert(oopDesc::is_oop(o), "Should be an oop");
-
- size_t res = o->size_given_klass(k);
- res = adjustObjectSize(res);
- assert(res != 0, "Block size should not be 0");
- return res;
- } else {
- // May return 0 if P-bits not present.
- return c->block_size_if_printezis_bits(p);
- }
- }
- assert(loops == 0, "Can loop at most once");
- DEBUG_ONLY(loops++;)
- }
-}
-
-size_t CompactibleFreeListSpace::block_size_nopar(const HeapWord* p) const {
- NOT_PRODUCT(verify_objects_initialized());
- assert(MemRegion(bottom(), end()).contains(p), "p not in space");
- FreeChunk* fc = (FreeChunk*)p;
- if (fc->is_free()) {
- return fc->size();
- } else {
- // Ignore mark word because this may be a recently promoted
- // object whose mark word is used to chain together grey
- // objects (the last one would have a null value).
- assert(oopDesc::is_oop(oop(p), true), "Should be an oop");
- return adjustObjectSize(oop(p)->size());
- }
-}
-
-// This implementation assumes that the property of "being an object" is
-// stable. But being a free chunk may not be (because of parallel
-// promotion.)
-bool CompactibleFreeListSpace::block_is_obj(const HeapWord* p) const {
- FreeChunk* fc = (FreeChunk*)p;
- assert(is_in_reserved(p), "Should be in space");
- if (FreeChunk::indicatesFreeChunk(p)) return false;
- Klass* k = oop(p)->klass_or_null_acquire();
- if (k != NULL) {
- // Ignore mark word because it may have been used to
- // chain together promoted objects (the last one
- // would have a null value).
- assert(oopDesc::is_oop(oop(p), true), "Should be an oop");
- return true;
- } else {
- return false; // Was not an object at the start of collection.
- }
-}
-
-// Check if the object is alive. This fact is checked either by consulting
-// the main marking bitmap in the sweeping phase or, if it's a permanent
-// generation and we're not in the sweeping phase, by checking the
-// perm_gen_verify_bit_map where we store the "deadness" information if
-// we did not sweep the perm gen in the most recent previous GC cycle.
-bool CompactibleFreeListSpace::obj_is_alive(const HeapWord* p) const {
- assert(SafepointSynchronize::is_at_safepoint() || !is_init_completed(),
- "Else races are possible");
- assert(block_is_obj(p), "The address should point to an object");
-
- // If we're sweeping, we use object liveness information from the main bit map
- // for both perm gen and old gen.
- // We don't need to lock the bitmap (live_map or dead_map below), because
- // EITHER we are in the middle of the sweeping phase, and the
- // main marking bit map (live_map below) is locked,
- // OR we're in other phases and perm_gen_verify_bit_map (dead_map below)
- // is stable, because it's mutated only in the sweeping phase.
- // NOTE: This method is also used by jmap where, if class unloading is
- // off, the results can return "false" for legitimate perm objects,
- // when we are not in the midst of a sweeping phase, which can result
- // in jmap not reporting certain perm gen objects. This will be moot
- // if/when the perm gen goes away in the future.
- if (_collector->abstract_state() == CMSCollector::Sweeping) {
- CMSBitMap* live_map = _collector->markBitMap();
- return live_map->par_isMarked((HeapWord*) p);
- }
- return true;
-}
-
-bool CompactibleFreeListSpace::block_is_obj_nopar(const HeapWord* p) const {
- FreeChunk* fc = (FreeChunk*)p;
- assert(is_in_reserved(p), "Should be in space");
- assert(_bt.block_start(p) == p, "Should be a block boundary");
- if (!fc->is_free()) {
- // Ignore mark word because it may have been used to
- // chain together promoted objects (the last one
- // would have a null value).
- assert(oopDesc::is_oop(oop(p), true), "Should be an oop");
- return true;
- }
- return false;
-}
-
-// "MT-safe but not guaranteed MT-precise" (TM); you may get an
-// approximate answer if you don't hold the freelistlock when you call this.
-size_t CompactibleFreeListSpace::totalSizeInIndexedFreeLists() const {
- size_t size = 0;
- for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) {
- debug_only(
- // We may be calling here without the lock in which case we
- // won't do this modest sanity check.
- if (freelistLock()->owned_by_self()) {
- size_t total_list_size = 0;
- for (FreeChunk* fc = _indexedFreeList[i].head(); fc != NULL;
- fc = fc->next()) {
- total_list_size += i;
- }
- assert(total_list_size == i * _indexedFreeList[i].count(),
- "Count in list is incorrect");
- }
- )
- size += i * _indexedFreeList[i].count();
- }
- return size;
-}
-
-HeapWord* CompactibleFreeListSpace::par_allocate(size_t size) {
- MutexLocker x(freelistLock(), Mutex::_no_safepoint_check_flag);
- return allocate(size);
-}
-
-HeapWord*
-CompactibleFreeListSpace::getChunkFromSmallLinearAllocBlockRemainder(size_t size) {
- return getChunkFromLinearAllocBlockRemainder(&_smallLinearAllocBlock, size);
-}
-
-HeapWord* CompactibleFreeListSpace::allocate(size_t size) {
- assert_lock_strong(freelistLock());
- HeapWord* res = NULL;
- assert(size == adjustObjectSize(size),
- "use adjustObjectSize() before calling into allocate()");
-
- res = allocate_adaptive_freelists(size);
-
- if (res != NULL) {
- // check that res does lie in this space!
- assert(is_in_reserved(res), "Not in this space!");
- assert(is_aligned((void*)res), "alignment check");
-
- FreeChunk* fc = (FreeChunk*)res;
- fc->markNotFree();
- assert(!fc->is_free(), "shouldn't be marked free");
- assert(oop(fc)->klass_or_null() == NULL, "should look uninitialized");
- // Verify that the block offset table shows this to
- // be a single block, but not one which is unallocated.
- _bt.verify_single_block(res, size);
- _bt.verify_not_unallocated(res, size);
- // mangle a just allocated object with a distinct pattern.
- debug_only(fc->mangleAllocated(size));
- }
-
- // During GC we do not need to recalculate the stable used value for
- // every allocation in old gen. It is done once at the end of GC instead
- // for performance reasons.
- if (!CMSHeap::heap()->is_gc_active()) {
- recalculate_used_stable();
- }
-
- return res;
-}
-
-HeapWord* CompactibleFreeListSpace::allocate_adaptive_freelists(size_t size) {
- assert_lock_strong(freelistLock());
- HeapWord* res = NULL;
- assert(size == adjustObjectSize(size),
- "use adjustObjectSize() before calling into allocate()");
-
- // Strategy
- // if small
- // exact size from small object indexed list if small
- // small or large linear allocation block (linAB) as appropriate
- // take from lists of greater sized chunks
- // else
- // dictionary
- // small or large linear allocation block if it has the space
- // Try allocating exact size from indexTable first
- if (size < IndexSetSize) {
- res = (HeapWord*) getChunkFromIndexedFreeList(size);
- if(res != NULL) {
- assert(res != (HeapWord*)_indexedFreeList[size].head(),
- "Not removed from free list");
- // no block offset table adjustment is necessary on blocks in
- // the indexed lists.
-
- // Try allocating from the small LinAB
- } else if (size < _smallLinearAllocBlock._allocation_size_limit &&
- (res = getChunkFromSmallLinearAllocBlock(size)) != NULL) {
- // if successful, the above also adjusts block offset table
- // Note that this call will refill the LinAB to
- // satisfy the request. This is different that
- // evm.
- // Don't record chunk off a LinAB? smallSplitBirth(size);
- } else {
- // Raid the exact free lists larger than size, even if they are not
- // overpopulated.
- res = (HeapWord*) getChunkFromGreater(size);
- }
- } else {
- // Big objects get allocated directly from the dictionary.
- res = (HeapWord*) getChunkFromDictionaryExact(size);
- if (res == NULL) {
- // Try hard not to fail since an allocation failure will likely
- // trigger a synchronous GC. Try to get the space from the
- // allocation blocks.
- res = getChunkFromSmallLinearAllocBlockRemainder(size);
- }
- }
-
- return res;
-}
-
-// A worst-case estimate of the space required (in HeapWords) to expand the heap
-// when promoting obj.
-size_t CompactibleFreeListSpace::expansionSpaceRequired(size_t obj_size) const {
- // Depending on the object size, expansion may require refilling either a
- // bigLAB or a smallLAB plus refilling a PromotionInfo object. MinChunkSize
- // is added because the dictionary may over-allocate to avoid fragmentation.
- size_t space = obj_size;
- space += _promoInfo.refillSize() + 2 * MinChunkSize;
- return space;
-}
-
-FreeChunk* CompactibleFreeListSpace::getChunkFromGreater(size_t numWords) {
- FreeChunk* ret;
-
- assert(numWords >= MinChunkSize, "Size is less than minimum");
- assert(linearAllocationWouldFail() || bestFitFirst(),
- "Should not be here");
-
- size_t i;
- size_t currSize = numWords + MinChunkSize;
- assert(is_object_aligned(currSize), "currSize should be aligned");
- for (i = currSize; i < IndexSetSize; i += IndexSetStride) {
- AdaptiveFreeList<FreeChunk>* fl = &_indexedFreeList[i];
- if (fl->head()) {
- ret = getFromListGreater(fl, numWords);
- assert(ret == NULL || ret->is_free(), "Should be returning a free chunk");
- return ret;
- }
- }
-
- currSize = MAX2((size_t)SmallForDictionary,
- (size_t)(numWords + MinChunkSize));
-
- /* Try to get a chunk that satisfies request, while avoiding
- fragmentation that can't be handled. */
- {
- ret = dictionary()->get_chunk(currSize);
- if (ret != NULL) {
- assert(ret->size() - numWords >= MinChunkSize,
- "Chunk is too small");
- _bt.allocated((HeapWord*)ret, ret->size());
- /* Carve returned chunk. */
- (void) splitChunkAndReturnRemainder(ret, numWords);
- /* Label this as no longer a free chunk. */
- assert(ret->is_free(), "This chunk should be free");
- ret->link_prev(NULL);
- }
- assert(ret == NULL || ret->is_free(), "Should be returning a free chunk");
- return ret;
- }
- ShouldNotReachHere();
-}
-
-bool CompactibleFreeListSpace::verifyChunkInIndexedFreeLists(FreeChunk* fc) const {
- assert(fc->size() < IndexSetSize, "Size of chunk is too large");
- return _indexedFreeList[fc->size()].verify_chunk_in_free_list(fc);
-}
-
-bool CompactibleFreeListSpace::verify_chunk_is_linear_alloc_block(FreeChunk* fc) const {
- assert((_smallLinearAllocBlock._ptr != (HeapWord*)fc) ||
- (_smallLinearAllocBlock._word_size == fc->size()),
- "Linear allocation block shows incorrect size");
- return ((_smallLinearAllocBlock._ptr == (HeapWord*)fc) &&
- (_smallLinearAllocBlock._word_size == fc->size()));
-}
-
-// Check if the purported free chunk is present either as a linear
-// allocation block, the size-indexed table of (smaller) free blocks,
-// or the larger free blocks kept in the binary tree dictionary.
-bool CompactibleFreeListSpace::verify_chunk_in_free_list(FreeChunk* fc) const {
- if (verify_chunk_is_linear_alloc_block(fc)) {
- return true;
- } else if (fc->size() < IndexSetSize) {
- return verifyChunkInIndexedFreeLists(fc);
- } else {
- return dictionary()->verify_chunk_in_free_list(fc);
- }
-}
-
-#ifndef PRODUCT
-void CompactibleFreeListSpace::assert_locked() const {
- CMSLockVerifier::assert_locked(freelistLock(), parDictionaryAllocLock());
-}
-
-void CompactibleFreeListSpace::assert_locked(const Mutex* lock) const {
- CMSLockVerifier::assert_locked(lock);
-}
-#endif
-
-FreeChunk* CompactibleFreeListSpace::allocateScratch(size_t size) {
- // In the parallel case, the main thread holds the free list lock
- // on behalf the parallel threads.
- FreeChunk* fc;
- {
- // If GC is parallel, this might be called by several threads.
- // This should be rare enough that the locking overhead won't affect
- // the sequential code.
- MutexLocker x(parDictionaryAllocLock(),
- Mutex::_no_safepoint_check_flag);
- fc = getChunkFromDictionary(size);
- }
- if (fc != NULL) {
- fc->dontCoalesce();
- assert(fc->is_free(), "Should be free, but not coalescable");
- // Verify that the block offset table shows this to
- // be a single block, but not one which is unallocated.
- _bt.verify_single_block((HeapWord*)fc, fc->size());
- _bt.verify_not_unallocated((HeapWord*)fc, fc->size());
- }
- return fc;
-}
-
-oop CompactibleFreeListSpace::promote(oop obj, size_t obj_size) {
- assert(obj_size == (size_t)obj->size(), "bad obj_size passed in");
- assert_locked();
-
- // if we are tracking promotions, then first ensure space for
- // promotion (including spooling space for saving header if necessary).
- // then allocate and copy, then track promoted info if needed.
- // When tracking (see PromotionInfo::track()), the mark word may
- // be displaced and in this case restoration of the mark word
- // occurs in the (oop_since_save_marks_)iterate phase.
- if (_promoInfo.tracking() && !_promoInfo.ensure_spooling_space()) {
- return NULL;
- }
- // Call the allocate(size_t, bool) form directly to avoid the
- // additional call through the allocate(size_t) form. Having
- // the compile inline the call is problematic because allocate(size_t)
- // is a virtual method.
- HeapWord* res = allocate(adjustObjectSize(obj_size));
- if (res != NULL) {
- Copy::aligned_disjoint_words((HeapWord*)obj, res, obj_size);
- // if we should be tracking promotions, do so.
- if (_promoInfo.tracking()) {
- _promoInfo.track((PromotedObject*)res);
- }
- }
- return oop(res);
-}
-
-HeapWord*
-CompactibleFreeListSpace::getChunkFromSmallLinearAllocBlock(size_t size) {
- assert_locked();
- assert(size >= MinChunkSize, "minimum chunk size");
- assert(size < _smallLinearAllocBlock._allocation_size_limit,
- "maximum from smallLinearAllocBlock");
- return getChunkFromLinearAllocBlock(&_smallLinearAllocBlock, size);
-}
-
-HeapWord*
-CompactibleFreeListSpace::getChunkFromLinearAllocBlock(LinearAllocBlock *blk,
- size_t size) {
- assert_locked();
- assert(size >= MinChunkSize, "too small");
- HeapWord* res = NULL;
- // Try to do linear allocation from blk, making sure that
- if (blk->_word_size == 0) {
- // We have probably been unable to fill this either in the prologue or
- // when it was exhausted at the last linear allocation. Bail out until
- // next time.
- assert(blk->_ptr == NULL, "consistency check");
- return NULL;
- }
- assert(blk->_word_size != 0 && blk->_ptr != NULL, "consistency check");
- res = getChunkFromLinearAllocBlockRemainder(blk, size);
- if (res != NULL) return res;
-
- // about to exhaust this linear allocation block
- if (blk->_word_size == size) { // exactly satisfied
- res = blk->_ptr;
- _bt.allocated(res, blk->_word_size);
- } else if (size + MinChunkSize <= blk->_refillSize) {
- size_t sz = blk->_word_size;
- // Update _unallocated_block if the size is such that chunk would be
- // returned to the indexed free list. All other chunks in the indexed
- // free lists are allocated from the dictionary so that _unallocated_block
- // has already been adjusted for them. Do it here so that the cost
- // for all chunks added back to the indexed free lists.
- if (sz < SmallForDictionary) {
- _bt.allocated(blk->_ptr, sz);
- }
- // Return the chunk that isn't big enough, and then refill below.
- addChunkToFreeLists(blk->_ptr, sz);
- split_birth(sz);
- // Don't keep statistics on adding back chunk from a LinAB.
- } else {
- // A refilled block would not satisfy the request.
- return NULL;
- }
-
- blk->_ptr = NULL; blk->_word_size = 0;
- refillLinearAllocBlock(blk);
- assert(blk->_ptr == NULL || blk->_word_size >= size + MinChunkSize,
- "block was replenished");
- if (res != NULL) {
- split_birth(size);
- repairLinearAllocBlock(blk);
- } else if (blk->_ptr != NULL) {
- res = blk->_ptr;
- size_t blk_size = blk->_word_size;
- blk->_word_size -= size;
- blk->_ptr += size;
- split_birth(size);
- repairLinearAllocBlock(blk);
- // Update BOT last so that other (parallel) GC threads see a consistent
- // view of the BOT and free blocks.
- // Above must occur before BOT is updated below.
- OrderAccess::storestore();
- _bt.split_block(res, blk_size, size); // adjust block offset table
- }
- return res;
-}
-
-HeapWord* CompactibleFreeListSpace::getChunkFromLinearAllocBlockRemainder(
- LinearAllocBlock* blk,
- size_t size) {
- assert_locked();
- assert(size >= MinChunkSize, "too small");
-
- HeapWord* res = NULL;
- // This is the common case. Keep it simple.
- if (blk->_word_size >= size + MinChunkSize) {
- assert(blk->_ptr != NULL, "consistency check");
- res = blk->_ptr;
- // Note that the BOT is up-to-date for the linAB before allocation. It
- // indicates the start of the linAB. The split_block() updates the
- // BOT for the linAB after the allocation (indicates the start of the
- // next chunk to be allocated).
- size_t blk_size = blk->_word_size;
- blk->_word_size -= size;
- blk->_ptr += size;
- split_birth(size);
- repairLinearAllocBlock(blk);
- // Update BOT last so that other (parallel) GC threads see a consistent
- // view of the BOT and free blocks.
- // Above must occur before BOT is updated below.
- OrderAccess::storestore();
- _bt.split_block(res, blk_size, size); // adjust block offset table
- _bt.allocated(res, size);
- }
- return res;
-}
-
-FreeChunk*
-CompactibleFreeListSpace::getChunkFromIndexedFreeList(size_t size) {
- assert_locked();
- assert(size < SmallForDictionary, "just checking");
- FreeChunk* res;
- res = _indexedFreeList[size].get_chunk_at_head();
- if (res == NULL) {
- res = getChunkFromIndexedFreeListHelper(size);
- }
- _bt.verify_not_unallocated((HeapWord*) res, size);
- assert(res == NULL || res->size() == size, "Incorrect block size");
- return res;
-}
-
-FreeChunk*
-CompactibleFreeListSpace::getChunkFromIndexedFreeListHelper(size_t size,
- bool replenish) {
- assert_locked();
- FreeChunk* fc = NULL;
- if (size < SmallForDictionary) {
- assert(_indexedFreeList[size].head() == NULL ||
- _indexedFreeList[size].surplus() <= 0,
- "List for this size should be empty or under populated");
- // Try best fit in exact lists before replenishing the list
- if (!bestFitFirst() || (fc = bestFitSmall(size)) == NULL) {
- // Replenish list.
- //
- // Things tried that failed.
- // Tried allocating out of the two LinAB's first before
- // replenishing lists.
- // Tried small linAB of size 256 (size in indexed list)
- // and replenishing indexed lists from the small linAB.
- //
- FreeChunk* newFc = NULL;
- const size_t replenish_size = CMSIndexedFreeListReplenish * size;
- if (replenish_size < SmallForDictionary) {
- // Do not replenish from an underpopulated size.
- if (_indexedFreeList[replenish_size].surplus() > 0 &&
- _indexedFreeList[replenish_size].head() != NULL) {
- newFc = _indexedFreeList[replenish_size].get_chunk_at_head();
- } else if (bestFitFirst()) {
- newFc = bestFitSmall(replenish_size);
- }
- }
- if (newFc == NULL && replenish_size > size) {
- assert(CMSIndexedFreeListReplenish > 1, "ctl pt invariant");
- newFc = getChunkFromIndexedFreeListHelper(replenish_size, false);
- }
- // Note: The stats update re split-death of block obtained above
- // will be recorded below precisely when we know we are going to
- // be actually splitting it into more than one pieces below.
- if (newFc != NULL) {
- if (replenish || CMSReplenishIntermediate) {
- // Replenish this list and return one block to caller.
- size_t i;
- FreeChunk *curFc, *nextFc;
- size_t num_blk = newFc->size() / size;
- assert(num_blk >= 1, "Smaller than requested?");
- assert(newFc->size() % size == 0, "Should be integral multiple of request");
- if (num_blk > 1) {
- // we are sure we will be splitting the block just obtained
- // into multiple pieces; record the split-death of the original
- splitDeath(replenish_size);
- }
- // carve up and link blocks 0, ..., num_blk - 2
- // The last chunk is not added to the lists but is returned as the
- // free chunk.
- for (curFc = newFc, nextFc = (FreeChunk*)((HeapWord*)curFc + size),
- i = 0;
- i < (num_blk - 1);
- curFc = nextFc, nextFc = (FreeChunk*)((HeapWord*)nextFc + size),
- i++) {
- curFc->set_size(size);
- // Don't record this as a return in order to try and
- // determine the "returns" from a GC.
- _bt.verify_not_unallocated((HeapWord*) fc, size);
- _indexedFreeList[size].return_chunk_at_tail(curFc, false);
- _bt.mark_block((HeapWord*)curFc, size);
- split_birth(size);
- // Don't record the initial population of the indexed list
- // as a split birth.
- }
-
- // check that the arithmetic was OK above
- assert((HeapWord*)nextFc == (HeapWord*)newFc + num_blk*size,
- "inconsistency in carving newFc");
- curFc->set_size(size);
- _bt.mark_block((HeapWord*)curFc, size);
- split_birth(size);
- fc = curFc;
- } else {
- // Return entire block to caller
- fc = newFc;
- }
- }
- }
- } else {
- // Get a free chunk from the free chunk dictionary to be returned to
- // replenish the indexed free list.
- fc = getChunkFromDictionaryExact(size);
- }
- // assert(fc == NULL || fc->is_free(), "Should be returning a free chunk");
- return fc;
-}
-
-FreeChunk*
-CompactibleFreeListSpace::getChunkFromDictionary(size_t size) {
- assert_locked();
- FreeChunk* fc = _dictionary->get_chunk(size);
- if (fc == NULL) {
- return NULL;
- }
- _bt.allocated((HeapWord*)fc, fc->size());
- if (fc->size() >= size + MinChunkSize) {
- fc = splitChunkAndReturnRemainder(fc, size);
- }
- assert(fc->size() >= size, "chunk too small");
- assert(fc->size() < size + MinChunkSize, "chunk too big");
- _bt.verify_single_block((HeapWord*)fc, fc->size());
- return fc;
-}
-
-FreeChunk*
-CompactibleFreeListSpace::getChunkFromDictionaryExact(size_t size) {
- assert_locked();
- FreeChunk* fc = _dictionary->get_chunk(size);
- if (fc == NULL) {
- return fc;
- }
- _bt.allocated((HeapWord*)fc, fc->size());
- if (fc->size() == size) {
- _bt.verify_single_block((HeapWord*)fc, size);
- return fc;
- }
- assert(fc->size() > size, "get_chunk() guarantee");
- if (fc->size() < size + MinChunkSize) {
- // Return the chunk to the dictionary and go get a bigger one.
- returnChunkToDictionary(fc);
- fc = _dictionary->get_chunk(size + MinChunkSize);
- if (fc == NULL) {
- return NULL;
- }
- _bt.allocated((HeapWord*)fc, fc->size());
- }
- assert(fc->size() >= size + MinChunkSize, "tautology");
- fc = splitChunkAndReturnRemainder(fc, size);
- assert(fc->size() == size, "chunk is wrong size");
- _bt.verify_single_block((HeapWord*)fc, size);
- return fc;
-}
-
-void
-CompactibleFreeListSpace::returnChunkToDictionary(FreeChunk* chunk) {
- assert_locked();
-
- size_t size = chunk->size();
- _bt.verify_single_block((HeapWord*)chunk, size);
- // adjust _unallocated_block downward, as necessary
- _bt.freed((HeapWord*)chunk, size);
- _dictionary->return_chunk(chunk);
-#ifndef PRODUCT
- if (CMSCollector::abstract_state() != CMSCollector::Sweeping) {
- TreeChunk<FreeChunk, AdaptiveFreeList<FreeChunk> >* tc = TreeChunk<FreeChunk, AdaptiveFreeList<FreeChunk> >::as_TreeChunk(chunk);
- TreeList<FreeChunk, AdaptiveFreeList<FreeChunk> >* tl = tc->list();
- tl->verify_stats();
- }
-#endif // PRODUCT
-}
-
-void
-CompactibleFreeListSpace::returnChunkToFreeList(FreeChunk* fc) {
- assert_locked();
- size_t size = fc->size();
- _bt.verify_single_block((HeapWord*) fc, size);
- _bt.verify_not_unallocated((HeapWord*) fc, size);
- _indexedFreeList[size].return_chunk_at_tail(fc);
-#ifndef PRODUCT
- if (CMSCollector::abstract_state() != CMSCollector::Sweeping) {
- _indexedFreeList[size].verify_stats();
- }
-#endif // PRODUCT
-}
-
-// Add chunk to end of last block -- if it's the largest
-// block -- and update BOT and census data. We would
-// of course have preferred to coalesce it with the
-// last block, but it's currently less expensive to find the
-// largest block than it is to find the last.
-void
-CompactibleFreeListSpace::addChunkToFreeListsAtEndRecordingStats(
- HeapWord* chunk, size_t size) {
- // check that the chunk does lie in this space!
- assert(chunk != NULL && is_in_reserved(chunk), "Not in this space!");
- // One of the parallel gc task threads may be here
- // whilst others are allocating.
- Mutex* lock = &_parDictionaryAllocLock;
- FreeChunk* ec;
- {
- MutexLocker x(lock, Mutex::_no_safepoint_check_flag);
- ec = dictionary()->find_largest_dict(); // get largest block
- if (ec != NULL && ec->end() == (uintptr_t*) chunk) {
- // It's a coterminal block - we can coalesce.
- size_t old_size = ec->size();
- coalDeath(old_size);
- removeChunkFromDictionary(ec);
- size += old_size;
- } else {
- ec = (FreeChunk*)chunk;
- }
- }
- ec->set_size(size);
- debug_only(ec->mangleFreed(size));
- if (size < SmallForDictionary) {
- lock = _indexedFreeListParLocks[size];
- }
- MutexLocker x(lock, Mutex::_no_safepoint_check_flag);
- addChunkAndRepairOffsetTable((HeapWord*)ec, size, true);
- // record the birth under the lock since the recording involves
- // manipulation of the list on which the chunk lives and
- // if the chunk is allocated and is the last on the list,
- // the list can go away.
- coalBirth(size);
-}
-
-void
-CompactibleFreeListSpace::addChunkToFreeLists(HeapWord* chunk,
- size_t size) {
- // check that the chunk does lie in this space!
- assert(chunk != NULL && is_in_reserved(chunk), "Not in this space!");
- assert_locked();
- _bt.verify_single_block(chunk, size);
-
- FreeChunk* fc = (FreeChunk*) chunk;
- fc->set_size(size);
- debug_only(fc->mangleFreed(size));
- if (size < SmallForDictionary) {
- returnChunkToFreeList(fc);
- } else {
- returnChunkToDictionary(fc);
- }
-}
-
-void
-CompactibleFreeListSpace::addChunkAndRepairOffsetTable(HeapWord* chunk,
- size_t size, bool coalesced) {
- assert_locked();
- assert(chunk != NULL, "null chunk");
- if (coalesced) {
- // repair BOT
- _bt.single_block(chunk, size);
- }
- addChunkToFreeLists(chunk, size);
-}
-
-// We _must_ find the purported chunk on our free lists;
-// we assert if we don't.
-void
-CompactibleFreeListSpace::removeFreeChunkFromFreeLists(FreeChunk* fc) {
- size_t size = fc->size();
- assert_locked();
- debug_only(verifyFreeLists());
- if (size < SmallForDictionary) {
- removeChunkFromIndexedFreeList(fc);
- } else {
- removeChunkFromDictionary(fc);
- }
- _bt.verify_single_block((HeapWord*)fc, size);
- debug_only(verifyFreeLists());
-}
-
-void
-CompactibleFreeListSpace::removeChunkFromDictionary(FreeChunk* fc) {
- size_t size = fc->size();
- assert_locked();
- assert(fc != NULL, "null chunk");
- _bt.verify_single_block((HeapWord*)fc, size);
- _dictionary->remove_chunk(fc);
- // adjust _unallocated_block upward, as necessary
- _bt.allocated((HeapWord*)fc, size);
-}
-
-void
-CompactibleFreeListSpace::removeChunkFromIndexedFreeList(FreeChunk* fc) {
- assert_locked();
- size_t size = fc->size();
- _bt.verify_single_block((HeapWord*)fc, size);
- NOT_PRODUCT(
- if (FLSVerifyIndexTable) {
- verifyIndexedFreeList(size);
- }
- )
- _indexedFreeList[size].remove_chunk(fc);
- NOT_PRODUCT(
- if (FLSVerifyIndexTable) {
- verifyIndexedFreeList(size);
- }
- )
-}
-
-FreeChunk* CompactibleFreeListSpace::bestFitSmall(size_t numWords) {
- /* A hint is the next larger size that has a surplus.
- Start search at a size large enough to guarantee that
- the excess is >= MIN_CHUNK. */
- size_t start = align_object_size(numWords + MinChunkSize);
- if (start < IndexSetSize) {
- AdaptiveFreeList<FreeChunk>* it = _indexedFreeList;
- size_t hint = _indexedFreeList[start].hint();
- while (hint < IndexSetSize) {
- assert(is_object_aligned(hint), "hint should be aligned");
- AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[hint];
- if (fl->surplus() > 0 && fl->head() != NULL) {
- // Found a list with surplus, reset original hint
- // and split out a free chunk which is returned.
- _indexedFreeList[start].set_hint(hint);
- FreeChunk* res = getFromListGreater(fl, numWords);
- assert(res == NULL || res->is_free(),
- "Should be returning a free chunk");
- return res;
- }
- hint = fl->hint(); /* keep looking */
- }
- /* None found. */
- it[start].set_hint(IndexSetSize);
- }
- return NULL;
-}
-
-/* Requires fl->size >= numWords + MinChunkSize */
-FreeChunk* CompactibleFreeListSpace::getFromListGreater(AdaptiveFreeList<FreeChunk>* fl,
- size_t numWords) {
- FreeChunk *curr = fl->head();
- size_t oldNumWords = curr->size();
- assert(numWords >= MinChunkSize, "Word size is too small");
- assert(curr != NULL, "List is empty");
- assert(oldNumWords >= numWords + MinChunkSize,
- "Size of chunks in the list is too small");
-
- fl->remove_chunk(curr);
- // recorded indirectly by splitChunkAndReturnRemainder -
- // smallSplit(oldNumWords, numWords);
- FreeChunk* new_chunk = splitChunkAndReturnRemainder(curr, numWords);
- // Does anything have to be done for the remainder in terms of
- // fixing the card table?
- assert(new_chunk == NULL || new_chunk->is_free(),
- "Should be returning a free chunk");
- return new_chunk;
-}
-
-FreeChunk*
-CompactibleFreeListSpace::splitChunkAndReturnRemainder(FreeChunk* chunk,
- size_t new_size) {
- assert_locked();
- size_t size = chunk->size();
- assert(size > new_size, "Split from a smaller block?");
- assert(is_aligned(chunk), "alignment problem");
- assert(size == adjustObjectSize(size), "alignment problem");
- size_t rem_sz = size - new_size;
- assert(rem_sz == adjustObjectSize(rem_sz), "alignment problem");
- assert(rem_sz >= MinChunkSize, "Free chunk smaller than minimum");
- FreeChunk* ffc = (FreeChunk*)((HeapWord*)chunk + new_size);
- assert(is_aligned(ffc), "alignment problem");
- ffc->set_size(rem_sz);
- ffc->link_next(NULL);
- ffc->link_prev(NULL); // Mark as a free block for other (parallel) GC threads.
- // Above must occur before BOT is updated below.
- // adjust block offset table
- OrderAccess::storestore();
- assert(chunk->is_free() && ffc->is_free(), "Error");
- _bt.split_block((HeapWord*)chunk, chunk->size(), new_size);
- if (rem_sz < SmallForDictionary) {
- // The freeList lock is held, but multiple GC task threads might be executing in parallel.
- bool is_par = Thread::current()->is_GC_task_thread();
- if (is_par) _indexedFreeListParLocks[rem_sz]->lock_without_safepoint_check();
- returnChunkToFreeList(ffc);
- split(size, rem_sz);
- if (is_par) _indexedFreeListParLocks[rem_sz]->unlock();
- } else {
- returnChunkToDictionary(ffc);
- split(size, rem_sz);
- }
- chunk->set_size(new_size);
- return chunk;
-}
-
-void
-CompactibleFreeListSpace::sweep_completed() {
- // Now that space is probably plentiful, refill linear
- // allocation blocks as needed.
- refillLinearAllocBlocksIfNeeded();
-}
-
-void
-CompactibleFreeListSpace::gc_prologue() {
- assert_locked();
- reportFreeListStatistics("Before GC:");
- refillLinearAllocBlocksIfNeeded();
-}
-
-void
-CompactibleFreeListSpace::gc_epilogue() {
- assert_locked();
- assert(_promoInfo.noPromotions(), "_promoInfo inconsistency");
- _promoInfo.stopTrackingPromotions();
- repairLinearAllocationBlocks();
- reportFreeListStatistics("After GC:");
-}
-
-// Iteration support, mostly delegated from a CMS generation
-
-void CompactibleFreeListSpace::save_marks() {
- assert(Thread::current()->is_VM_thread(),
- "Global variable should only be set when single-threaded");
- // Mark the "end" of the used space at the time of this call;
- // note, however, that promoted objects from this point
- // on are tracked in the _promoInfo below.
- set_saved_mark_word(unallocated_block());
-#ifdef ASSERT
- // Check the sanity of save_marks() etc.
- MemRegion ur = used_region();
- MemRegion urasm = used_region_at_save_marks();
- assert(ur.contains(urasm),
- " Error at save_marks(): [" PTR_FORMAT "," PTR_FORMAT ")"
- " should contain [" PTR_FORMAT "," PTR_FORMAT ")",
- p2i(ur.start()), p2i(ur.end()), p2i(urasm.start()), p2i(urasm.end()));
-#endif
- // inform allocator that promotions should be tracked.
- assert(_promoInfo.noPromotions(), "_promoInfo inconsistency");
- _promoInfo.startTrackingPromotions();
-}
-
-bool CompactibleFreeListSpace::no_allocs_since_save_marks() {
- assert(_promoInfo.tracking(), "No preceding save_marks?");
- return _promoInfo.noPromotions();
-}
-
-bool CompactibleFreeListSpace::linearAllocationWouldFail() const {
- return _smallLinearAllocBlock._word_size == 0;
-}
-
-void CompactibleFreeListSpace::repairLinearAllocationBlocks() {
- // Fix up linear allocation blocks to look like free blocks
- repairLinearAllocBlock(&_smallLinearAllocBlock);
-}
-
-void CompactibleFreeListSpace::repairLinearAllocBlock(LinearAllocBlock* blk) {
- assert_locked();
- if (blk->_ptr != NULL) {
- assert(blk->_word_size != 0 && blk->_word_size >= MinChunkSize,
- "Minimum block size requirement");
- FreeChunk* fc = (FreeChunk*)(blk->_ptr);
- fc->set_size(blk->_word_size);
- fc->link_prev(NULL); // mark as free
- fc->dontCoalesce();
- assert(fc->is_free(), "just marked it free");
- assert(fc->cantCoalesce(), "just marked it uncoalescable");
- }
-}
-
-void CompactibleFreeListSpace::refillLinearAllocBlocksIfNeeded() {
- assert_locked();
- if (_smallLinearAllocBlock._ptr == NULL) {
- assert(_smallLinearAllocBlock._word_size == 0,
- "Size of linAB should be zero if the ptr is NULL");
- // Reset the linAB refill and allocation size limit.
- _smallLinearAllocBlock.set(0, 0, 1024*SmallForLinearAlloc, SmallForLinearAlloc);
- }
- refillLinearAllocBlockIfNeeded(&_smallLinearAllocBlock);
-}
-
-void
-CompactibleFreeListSpace::refillLinearAllocBlockIfNeeded(LinearAllocBlock* blk) {
- assert_locked();
- assert((blk->_ptr == NULL && blk->_word_size == 0) ||
- (blk->_ptr != NULL && blk->_word_size >= MinChunkSize),
- "blk invariant");
- if (blk->_ptr == NULL) {
- refillLinearAllocBlock(blk);
- }
-}
-
-void
-CompactibleFreeListSpace::refillLinearAllocBlock(LinearAllocBlock* blk) {
- assert_locked();
- assert(blk->_word_size == 0 && blk->_ptr == NULL,
- "linear allocation block should be empty");
- FreeChunk* fc;
- if (blk->_refillSize < SmallForDictionary &&
- (fc = getChunkFromIndexedFreeList(blk->_refillSize)) != NULL) {
- // A linAB's strategy might be to use small sizes to reduce
- // fragmentation but still get the benefits of allocation from a
- // linAB.
- } else {
- fc = getChunkFromDictionary(blk->_refillSize);
- }
- if (fc != NULL) {
- blk->_ptr = (HeapWord*)fc;
- blk->_word_size = fc->size();
- fc->dontCoalesce(); // to prevent sweeper from sweeping us up
- }
-}
-
-// Support for compaction
-void CompactibleFreeListSpace::prepare_for_compaction(CompactPoint* cp) {
- scan_and_forward(this, cp);
- // Prepare_for_compaction() uses the space between live objects
- // so that later phase can skip dead space quickly. So verification
- // of the free lists doesn't work after.
-}
-
-void CompactibleFreeListSpace::adjust_pointers() {
- // In other versions of adjust_pointers(), a bail out
- // based on the amount of live data in the generation
- // (i.e., if 0, bail out) may be used.
- // Cannot test used() == 0 here because the free lists have already
- // been mangled by the compaction.
-
- scan_and_adjust_pointers(this);
- // See note about verification in prepare_for_compaction().
-}
-
-void CompactibleFreeListSpace::compact() {
- scan_and_compact(this);
-}
-
-// Fragmentation metric = 1 - [sum of (fbs**2) / (sum of fbs)**2]
-// where fbs is free block sizes
-double CompactibleFreeListSpace::flsFrag() const {
- size_t itabFree = totalSizeInIndexedFreeLists();
- double frag = 0.0;
- size_t i;
-
- for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) {
- double sz = i;
- frag += _indexedFreeList[i].count() * (sz * sz);
- }
-
- double totFree = itabFree +
- _dictionary->total_chunk_size(DEBUG_ONLY(freelistLock()));
- if (totFree > 0) {
- frag = ((frag + _dictionary->sum_of_squared_block_sizes()) /
- (totFree * totFree));
- frag = (double)1.0 - frag;
- } else {
- assert(frag == 0.0, "Follows from totFree == 0");
- }
- return frag;
-}
-
-void CompactibleFreeListSpace::beginSweepFLCensus(
- float inter_sweep_current,
- float inter_sweep_estimate,
- float intra_sweep_estimate) {
- assert_locked();
- size_t i;
- for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) {
- AdaptiveFreeList<FreeChunk>* fl = &_indexedFreeList[i];
- log_trace(gc, freelist)("size[" SIZE_FORMAT "] : ", i);
- fl->compute_desired(inter_sweep_current, inter_sweep_estimate, intra_sweep_estimate);
- fl->set_coal_desired((ssize_t)((double)fl->desired() * CMSSmallCoalSurplusPercent));
- fl->set_before_sweep(fl->count());
- fl->set_bfr_surp(fl->surplus());
- }
- _dictionary->begin_sweep_dict_census(CMSLargeCoalSurplusPercent,
- inter_sweep_current,
- inter_sweep_estimate,
- intra_sweep_estimate);
-}
-
-void CompactibleFreeListSpace::setFLSurplus() {
- assert_locked();
- size_t i;
- for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) {
- AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[i];
- fl->set_surplus(fl->count() -
- (ssize_t)((double)fl->desired() * CMSSmallSplitSurplusPercent));
- }
-}
-
-void CompactibleFreeListSpace::setFLHints() {
- assert_locked();
- size_t i;
- size_t h = IndexSetSize;
- for (i = IndexSetSize - 1; i != 0; i -= IndexSetStride) {
- AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[i];
- fl->set_hint(h);
- if (fl->surplus() > 0) {
- h = i;
- }
- }
-}
-
-void CompactibleFreeListSpace::clearFLCensus() {
- assert_locked();
- size_t i;
- for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) {
- AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[i];
- fl->set_prev_sweep(fl->count());
- fl->set_coal_births(0);
- fl->set_coal_deaths(0);
- fl->set_split_births(0);
- fl->set_split_deaths(0);
- }
-}
-
-void CompactibleFreeListSpace::endSweepFLCensus(size_t sweep_count) {
- log_debug(gc, freelist)("CMS: Large block " PTR_FORMAT, p2i(dictionary()->find_largest_dict()));
- setFLSurplus();
- setFLHints();
- printFLCensus(sweep_count);
- clearFLCensus();
- assert_locked();
- _dictionary->end_sweep_dict_census(CMSLargeSplitSurplusPercent);
-}
-
-bool CompactibleFreeListSpace::coalOverPopulated(size_t size) {
- if (size < SmallForDictionary) {
- AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[size];
- return (fl->coal_desired() < 0) ||
- ((int)fl->count() > fl->coal_desired());
- } else {
- return dictionary()->coal_dict_over_populated(size);
- }
-}
-
-void CompactibleFreeListSpace::smallCoalBirth(size_t size) {
- assert(size < SmallForDictionary, "Size too large for indexed list");
- AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[size];
- fl->increment_coal_births();
- fl->increment_surplus();
-}
-
-void CompactibleFreeListSpace::smallCoalDeath(size_t size) {
- assert(size < SmallForDictionary, "Size too large for indexed list");
- AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[size];
- fl->increment_coal_deaths();
- fl->decrement_surplus();
-}
-
-void CompactibleFreeListSpace::coalBirth(size_t size) {
- if (size < SmallForDictionary) {
- smallCoalBirth(size);
- } else {
- dictionary()->dict_census_update(size,
- false /* split */,
- true /* birth */);
- }
-}
-
-void CompactibleFreeListSpace::coalDeath(size_t size) {
- if(size < SmallForDictionary) {
- smallCoalDeath(size);
- } else {
- dictionary()->dict_census_update(size,
- false /* split */,
- false /* birth */);
- }
-}
-
-void CompactibleFreeListSpace::smallSplitBirth(size_t size) {
- assert(size < SmallForDictionary, "Size too large for indexed list");
- AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[size];
- fl->increment_split_births();
- fl->increment_surplus();
-}
-
-void CompactibleFreeListSpace::smallSplitDeath(size_t size) {
- assert(size < SmallForDictionary, "Size too large for indexed list");
- AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[size];
- fl->increment_split_deaths();
- fl->decrement_surplus();
-}
-
-void CompactibleFreeListSpace::split_birth(size_t size) {
- if (size < SmallForDictionary) {
- smallSplitBirth(size);
- } else {
- dictionary()->dict_census_update(size,
- true /* split */,
- true /* birth */);
- }
-}
-
-void CompactibleFreeListSpace::splitDeath(size_t size) {
- if (size < SmallForDictionary) {
- smallSplitDeath(size);
- } else {
- dictionary()->dict_census_update(size,
- true /* split */,
- false /* birth */);
- }
-}
-
-void CompactibleFreeListSpace::split(size_t from, size_t to1) {
- size_t to2 = from - to1;
- splitDeath(from);
- split_birth(to1);
- split_birth(to2);
-}
-
-void CompactibleFreeListSpace::print() const {
- print_on(tty);
-}
-
-void CompactibleFreeListSpace::prepare_for_verify() {
- assert_locked();
- repairLinearAllocationBlocks();
- // Verify that the SpoolBlocks look like free blocks of
- // appropriate sizes... To be done ...
-}
-
-class VerifyAllBlksClosure: public BlkClosure {
- private:
- const CompactibleFreeListSpace* _sp;
- const MemRegion _span;
- HeapWord* _last_addr;
- size_t _last_size;
- bool _last_was_obj;
- bool _last_was_live;
-
- public:
- VerifyAllBlksClosure(const CompactibleFreeListSpace* sp,
- MemRegion span) : _sp(sp), _span(span),
- _last_addr(NULL), _last_size(0),
- _last_was_obj(false), _last_was_live(false) { }
-
- virtual size_t do_blk(HeapWord* addr) {
- size_t res;
- bool was_obj = false;
- bool was_live = false;
- if (_sp->block_is_obj(addr)) {
- was_obj = true;
- oop p = oop(addr);
- guarantee(oopDesc::is_oop(p), "Should be an oop");
- res = _sp->adjustObjectSize(p->size());
- if (_sp->obj_is_alive(addr)) {
- was_live = true;
- oopDesc::verify(p);
- }
- } else {
- FreeChunk* fc = (FreeChunk*)addr;
- res = fc->size();
- if (FLSVerifyLists && !fc->cantCoalesce()) {
- guarantee(_sp->verify_chunk_in_free_list(fc),
- "Chunk should be on a free list");
- }
- }
- if (res == 0) {
- Log(gc, verify) log;
- log.error("Livelock: no rank reduction!");
- log.error(" Current: addr = " PTR_FORMAT ", size = " SIZE_FORMAT ", obj = %s, live = %s \n"
- " Previous: addr = " PTR_FORMAT ", size = " SIZE_FORMAT ", obj = %s, live = %s \n",
- p2i(addr), res, was_obj ?"true":"false", was_live ?"true":"false",
- p2i(_last_addr), _last_size, _last_was_obj?"true":"false", _last_was_live?"true":"false");
- LogStream ls(log.error());
- _sp->print_on(&ls);
- guarantee(false, "Verification failed.");
- }
- _last_addr = addr;
- _last_size = res;
- _last_was_obj = was_obj;
- _last_was_live = was_live;
- return res;
- }
-};
-
-class VerifyAllOopsClosure: public BasicOopIterateClosure {
- private:
- const CMSCollector* _collector;
- const CompactibleFreeListSpace* _sp;
- const MemRegion _span;
- const bool _past_remark;
- const CMSBitMap* _bit_map;
-
- protected:
- void do_oop(void* p, oop obj) {
- if (_span.contains(obj)) { // the interior oop points into CMS heap
- if (!_span.contains(p)) { // reference from outside CMS heap
- // Should be a valid object; the first disjunct below allows
- // us to sidestep an assertion in block_is_obj() that insists
- // that p be in _sp. Note that several generations (and spaces)
- // are spanned by _span (CMS heap) above.
- guarantee(!_sp->is_in_reserved(obj) ||
- _sp->block_is_obj((HeapWord*)obj),
- "Should be an object");
- guarantee(oopDesc::is_oop(obj), "Should be an oop");
- oopDesc::verify(obj);
- if (_past_remark) {
- // Remark has been completed, the object should be marked
- _bit_map->isMarked((HeapWord*)obj);
- }
- } else { // reference within CMS heap
- if (_past_remark) {
- // Remark has been completed -- so the referent should have
- // been marked, if referring object is.
- if (_bit_map->isMarked(_collector->block_start(p))) {
- guarantee(_bit_map->isMarked((HeapWord*)obj), "Marking error?");
- }
- }
- }
- } else if (_sp->is_in_reserved(p)) {
- // the reference is from FLS, and points out of FLS
- guarantee(oopDesc::is_oop(obj), "Should be an oop");
- oopDesc::verify(obj);
- }
- }
-
- template <class T> void do_oop_work(T* p) {
- T heap_oop = RawAccess<>::oop_load(p);
- if (!CompressedOops::is_null(heap_oop)) {
- oop obj = CompressedOops::decode_not_null(heap_oop);
- do_oop(p, obj);
- }
- }
-
- public:
- VerifyAllOopsClosure(const CMSCollector* collector,
- const CompactibleFreeListSpace* sp, MemRegion span,
- bool past_remark, CMSBitMap* bit_map) :
- _collector(collector), _sp(sp), _span(span),
- _past_remark(past_remark), _bit_map(bit_map) { }
-
- virtual void do_oop(oop* p) { VerifyAllOopsClosure::do_oop_work(p); }
- virtual void do_oop(narrowOop* p) { VerifyAllOopsClosure::do_oop_work(p); }
-};
-
-void CompactibleFreeListSpace::verify() const {
- assert_lock_strong(&_freelistLock);
- verify_objects_initialized();
- MemRegion span = _collector->_span;
- bool past_remark = (_collector->abstract_state() ==
- CMSCollector::Sweeping);
-
- ResourceMark rm;
- HandleMark hm;
-
- // Check integrity of CFL data structures
- _promoInfo.verify();
- _dictionary->verify();
- if (FLSVerifyIndexTable) {
- verifyIndexedFreeLists();
- }
- // Check integrity of all objects and free blocks in space
- {
- VerifyAllBlksClosure cl(this, span);
- ((CompactibleFreeListSpace*)this)->blk_iterate(&cl); // cast off const
- }
- // Check that all references in the heap to FLS
- // are to valid objects in FLS or that references in
- // FLS are to valid objects elsewhere in the heap
- if (FLSVerifyAllHeapReferences)
- {
- VerifyAllOopsClosure cl(_collector, this, span, past_remark,
- _collector->markBitMap());
-
- // Iterate over all oops in the heap.
- CMSHeap::heap()->oop_iterate(&cl);
- }
-
- if (VerifyObjectStartArray) {
- // Verify the block offset table
- _bt.verify();
- }
-}
-
-#ifndef PRODUCT
-void CompactibleFreeListSpace::verifyFreeLists() const {
- if (FLSVerifyLists) {
- _dictionary->verify();
- verifyIndexedFreeLists();
- } else {
- if (FLSVerifyDictionary) {
- _dictionary->verify();
- }
- if (FLSVerifyIndexTable) {
- verifyIndexedFreeLists();
- }
- }
-}
-#endif
-
-void CompactibleFreeListSpace::verifyIndexedFreeLists() const {
- size_t i = 0;
- for (; i < IndexSetStart; i++) {
- guarantee(_indexedFreeList[i].head() == NULL, "should be NULL");
- }
- for (; i < IndexSetSize; i++) {
- verifyIndexedFreeList(i);
- }
-}
-
-void CompactibleFreeListSpace::verifyIndexedFreeList(size_t size) const {
- FreeChunk* fc = _indexedFreeList[size].head();
- FreeChunk* tail = _indexedFreeList[size].tail();
- size_t num = _indexedFreeList[size].count();
- size_t n = 0;
- guarantee(((size >= IndexSetStart) && (size % IndexSetStride == 0)) || fc == NULL,
- "Slot should have been empty");
- for (; fc != NULL; fc = fc->next(), n++) {
- guarantee(fc->size() == size, "Size inconsistency");
- guarantee(fc->is_free(), "!free?");
- guarantee(fc->next() == NULL || fc->next()->prev() == fc, "Broken list");
- guarantee((fc->next() == NULL) == (fc == tail), "Incorrect tail");
- }
- guarantee(n == num, "Incorrect count");
-}
-
-#ifndef PRODUCT
-void CompactibleFreeListSpace::check_free_list_consistency() const {
- assert((TreeChunk<FreeChunk, AdaptiveFreeList<FreeChunk> >::min_size() <= IndexSetSize),
- "Some sizes can't be allocated without recourse to"
- " linear allocation buffers");
- assert((TreeChunk<FreeChunk, AdaptiveFreeList<FreeChunk> >::min_size()*HeapWordSize == sizeof(TreeChunk<FreeChunk, AdaptiveFreeList<FreeChunk> >)),
- "else MIN_TREE_CHUNK_SIZE is wrong");
- assert(IndexSetStart != 0, "IndexSetStart not initialized");
- assert(IndexSetStride != 0, "IndexSetStride not initialized");
-}
-#endif
-
-void CompactibleFreeListSpace::printFLCensus(size_t sweep_count) const {
- assert_lock_strong(&_freelistLock);
- LogTarget(Debug, gc, freelist, census) log;
- if (!log.is_enabled()) {
- return;
- }
- AdaptiveFreeList<FreeChunk> total;
- log.print("end sweep# " SIZE_FORMAT, sweep_count);
- ResourceMark rm;
- LogStream ls(log);
- outputStream* out = &ls;
- AdaptiveFreeList<FreeChunk>::print_labels_on(out, "size");
- size_t total_free = 0;
- for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) {
- const AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[i];
- total_free += fl->count() * fl->size();
- if (i % (40*IndexSetStride) == 0) {
- AdaptiveFreeList<FreeChunk>::print_labels_on(out, "size");
- }
- fl->print_on(out);
- total.set_bfr_surp( total.bfr_surp() + fl->bfr_surp() );
- total.set_surplus( total.surplus() + fl->surplus() );
- total.set_desired( total.desired() + fl->desired() );
- total.set_prev_sweep( total.prev_sweep() + fl->prev_sweep() );
- total.set_before_sweep(total.before_sweep() + fl->before_sweep());
- total.set_count( total.count() + fl->count() );
- total.set_coal_births( total.coal_births() + fl->coal_births() );
- total.set_coal_deaths( total.coal_deaths() + fl->coal_deaths() );
- total.set_split_births(total.split_births() + fl->split_births());
- total.set_split_deaths(total.split_deaths() + fl->split_deaths());
- }
- total.print_on(out, "TOTAL");
- log.print("Total free in indexed lists " SIZE_FORMAT " words", total_free);
- log.print("growth: %8.5f deficit: %8.5f",
- (double)(total.split_births()+total.coal_births()-total.split_deaths()-total.coal_deaths())/
- (total.prev_sweep() != 0 ? (double)total.prev_sweep() : 1.0),
- (double)(total.desired() - total.count())/(total.desired() != 0 ? (double)total.desired() : 1.0));
- _dictionary->print_dict_census(out);
-}
-
-///////////////////////////////////////////////////////////////////////////
-// CompactibleFreeListSpaceLAB
-///////////////////////////////////////////////////////////////////////////
-
-#define VECTOR_257(x) \
- /* 1 2 3 4 5 6 7 8 9 1x 11 12 13 14 15 16 17 18 19 2x 21 22 23 24 25 26 27 28 29 3x 31 32 */ \
- { x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \
- x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \
- x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \
- x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \
- x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \
- x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \
- x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \
- x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \
- x }
-
-// Initialize with default setting for CMS, _not_
-// generic OldPLABSize, whose static default is different; if overridden at the
-// command-line, this will get reinitialized via a call to
-// modify_initialization() below.
-AdaptiveWeightedAverage CompactibleFreeListSpaceLAB::_blocks_to_claim[] =
- VECTOR_257(AdaptiveWeightedAverage(OldPLABWeight, (float)CompactibleFreeListSpaceLAB::_default_dynamic_old_plab_size));
-size_t CompactibleFreeListSpaceLAB::_global_num_blocks[] = VECTOR_257(0);
-uint CompactibleFreeListSpaceLAB::_global_num_workers[] = VECTOR_257(0);
-
-CompactibleFreeListSpaceLAB::CompactibleFreeListSpaceLAB(CompactibleFreeListSpace* cfls) :
- _cfls(cfls)
-{
- assert(CompactibleFreeListSpace::IndexSetSize == 257, "Modify VECTOR_257() macro above");
- for (size_t i = CompactibleFreeListSpace::IndexSetStart;
- i < CompactibleFreeListSpace::IndexSetSize;
- i += CompactibleFreeListSpace::IndexSetStride) {
- _indexedFreeList[i].set_size(i);
- _num_blocks[i] = 0;
- }
-}
-
-static bool _CFLS_LAB_modified = false;
-
-void CompactibleFreeListSpaceLAB::modify_initialization(size_t n, unsigned wt) {
- assert(!_CFLS_LAB_modified, "Call only once");
- _CFLS_LAB_modified = true;
- for (size_t i = CompactibleFreeListSpace::IndexSetStart;
- i < CompactibleFreeListSpace::IndexSetSize;
- i += CompactibleFreeListSpace::IndexSetStride) {
- _blocks_to_claim[i].modify(n, wt, true /* force */);
- }
-}
-
-HeapWord* CompactibleFreeListSpaceLAB::alloc(size_t word_sz) {
- FreeChunk* res;
- assert(word_sz == _cfls->adjustObjectSize(word_sz), "Error");
- if (word_sz >= CompactibleFreeListSpace::IndexSetSize) {
- // This locking manages sync with other large object allocations.
- MutexLocker x(_cfls->parDictionaryAllocLock(),
- Mutex::_no_safepoint_check_flag);
- res = _cfls->getChunkFromDictionaryExact(word_sz);
- if (res == NULL) return NULL;
- } else {
- AdaptiveFreeList<FreeChunk>* fl = &_indexedFreeList[word_sz];
- if (fl->count() == 0) {
- // Attempt to refill this local free list.
- get_from_global_pool(word_sz, fl);
- // If it didn't work, give up.
- if (fl->count() == 0) return NULL;
- }
- res = fl->get_chunk_at_head();
- assert(res != NULL, "Why was count non-zero?");
- }
- res->markNotFree();
- assert(!res->is_free(), "shouldn't be marked free");
- assert(oop(res)->klass_or_null() == NULL, "should look uninitialized");
- // mangle a just allocated object with a distinct pattern.
- debug_only(res->mangleAllocated(word_sz));
- return (HeapWord*)res;
-}
-
-// Get a chunk of blocks of the right size and update related
-// book-keeping stats
-void CompactibleFreeListSpaceLAB::get_from_global_pool(size_t word_sz, AdaptiveFreeList<FreeChunk>* fl) {
- // Get the #blocks we want to claim
- size_t n_blks = (size_t)_blocks_to_claim[word_sz].average();
- assert(n_blks > 0, "Error");
- assert(ResizeOldPLAB || n_blks == OldPLABSize, "Error");
- // In some cases, when the application has a phase change,
- // there may be a sudden and sharp shift in the object survival
- // profile, and updating the counts at the end of a scavenge
- // may not be quick enough, giving rise to large scavenge pauses
- // during these phase changes. It is beneficial to detect such
- // changes on-the-fly during a scavenge and avoid such a phase-change
- // pothole. The following code is a heuristic attempt to do that.
- // It is protected by a product flag until we have gained
- // enough experience with this heuristic and fine-tuned its behavior.
- // WARNING: This might increase fragmentation if we overreact to
- // small spikes, so some kind of historical smoothing based on
- // previous experience with the greater reactivity might be useful.
- // Lacking sufficient experience, CMSOldPLABResizeQuicker is disabled by
- // default.
- if (ResizeOldPLAB && CMSOldPLABResizeQuicker) {
- //
- // On a 32-bit VM, the denominator can become zero because of integer overflow,
- // which is why there is a cast to double.
- //
- size_t multiple = (size_t) (_num_blocks[word_sz]/(((double)CMSOldPLABToleranceFactor)*CMSOldPLABNumRefills*n_blks));
- n_blks += CMSOldPLABReactivityFactor*multiple*n_blks;
- n_blks = MIN2(n_blks, CMSOldPLABMax);
- }
- assert(n_blks > 0, "Error");
- _cfls->par_get_chunk_of_blocks(word_sz, n_blks, fl);
- // Update stats table entry for this block size
- _num_blocks[word_sz] += fl->count();
-}
-
-void CompactibleFreeListSpaceLAB::compute_desired_plab_size() {
- for (size_t i = CompactibleFreeListSpace::IndexSetStart;
- i < CompactibleFreeListSpace::IndexSetSize;
- i += CompactibleFreeListSpace::IndexSetStride) {
- assert((_global_num_workers[i] == 0) == (_global_num_blocks[i] == 0),
- "Counter inconsistency");
- if (_global_num_workers[i] > 0) {
- // Need to smooth wrt historical average
- if (ResizeOldPLAB) {
- _blocks_to_claim[i].sample(
- MAX2(CMSOldPLABMin,
- MIN2(CMSOldPLABMax,
- _global_num_blocks[i]/_global_num_workers[i]/CMSOldPLABNumRefills)));
- }
- // Reset counters for next round
- _global_num_workers[i] = 0;
- _global_num_blocks[i] = 0;
- log_trace(gc, plab)("[" SIZE_FORMAT "]: " SIZE_FORMAT, i, (size_t)_blocks_to_claim[i].average());
- }
- }
-}
-
-// If this is changed in the future to allow parallel
-// access, one would need to take the FL locks and,
-// depending on how it is used, stagger access from
-// parallel threads to reduce contention.
-void CompactibleFreeListSpaceLAB::retire(int tid) {
- // We run this single threaded with the world stopped;
- // so no need for locks and such.
- NOT_PRODUCT(Thread* t = Thread::current();)
- assert(Thread::current()->is_VM_thread(), "Error");
- for (size_t i = CompactibleFreeListSpace::IndexSetStart;
- i < CompactibleFreeListSpace::IndexSetSize;
- i += CompactibleFreeListSpace::IndexSetStride) {
- assert(_num_blocks[i] >= (size_t)_indexedFreeList[i].count(),
- "Can't retire more than what we obtained");
- if (_num_blocks[i] > 0) {
- size_t num_retire = _indexedFreeList[i].count();
- assert(_num_blocks[i] > num_retire, "Should have used at least one");
- {
- // MutexLocker x(_cfls->_indexedFreeListParLocks[i],
- // Mutex::_no_safepoint_check_flag);
-
- // Update globals stats for num_blocks used
- _global_num_blocks[i] += (_num_blocks[i] - num_retire);
- _global_num_workers[i]++;
- assert(_global_num_workers[i] <= ParallelGCThreads, "Too big");
- if (num_retire > 0) {
- _cfls->_indexedFreeList[i].prepend(&_indexedFreeList[i]);
- // Reset this list.
- _indexedFreeList[i] = AdaptiveFreeList<FreeChunk>();
- _indexedFreeList[i].set_size(i);
- }
- }
- log_trace(gc, plab)("%d[" SIZE_FORMAT "]: " SIZE_FORMAT "/" SIZE_FORMAT "/" SIZE_FORMAT,
- tid, i, num_retire, _num_blocks[i], (size_t)_blocks_to_claim[i].average());
- // Reset stats for next round
- _num_blocks[i] = 0;
- }
- }
-}
-
-// Used by par_get_chunk_of_blocks() for the chunks from the
-// indexed_free_lists. Looks for a chunk with size that is a multiple
-// of "word_sz" and if found, splits it into "word_sz" chunks and add
-// to the free list "fl". "n" is the maximum number of chunks to
-// be added to "fl".
-bool CompactibleFreeListSpace:: par_get_chunk_of_blocks_IFL(size_t word_sz, size_t n, AdaptiveFreeList<FreeChunk>* fl) {
-
- // We'll try all multiples of word_sz in the indexed set, starting with
- // word_sz itself and, if CMSSplitIndexedFreeListBlocks, try larger multiples,
- // then try getting a big chunk and splitting it.
- {
- bool found;
- int k;
- size_t cur_sz;
- for (k = 1, cur_sz = k * word_sz, found = false;
- (cur_sz < CompactibleFreeListSpace::IndexSetSize) &&
- (CMSSplitIndexedFreeListBlocks || k <= 1);
- k++, cur_sz = k * word_sz) {
- AdaptiveFreeList<FreeChunk> fl_for_cur_sz; // Empty.
- fl_for_cur_sz.set_size(cur_sz);
- {
- MutexLocker x(_indexedFreeListParLocks[cur_sz],
- Mutex::_no_safepoint_check_flag);
- AdaptiveFreeList<FreeChunk>* gfl = &_indexedFreeList[cur_sz];
- if (gfl->count() != 0) {
- // nn is the number of chunks of size cur_sz that
- // we'd need to split k-ways each, in order to create
- // "n" chunks of size word_sz each.
- const size_t nn = MAX2(n/k, (size_t)1);
- gfl->getFirstNChunksFromList(nn, &fl_for_cur_sz);
- found = true;
- if (k > 1) {
- // Update split death stats for the cur_sz-size blocks list:
- // we increment the split death count by the number of blocks
- // we just took from the cur_sz-size blocks list and which
- // we will be splitting below.
- ssize_t deaths = gfl->split_deaths() +
- fl_for_cur_sz.count();
- gfl->set_split_deaths(deaths);
- }
- }
- }
- // Now transfer fl_for_cur_sz to fl. Common case, we hope, is k = 1.
- if (found) {
- if (k == 1) {
- fl->prepend(&fl_for_cur_sz);
- } else {
- // Divide each block on fl_for_cur_sz up k ways.
- FreeChunk* fc;
- while ((fc = fl_for_cur_sz.get_chunk_at_head()) != NULL) {
- // Must do this in reverse order, so that anybody attempting to
- // access the main chunk sees it as a single free block until we
- // change it.
- size_t fc_size = fc->size();
- assert(fc->is_free(), "Error");
- for (int i = k-1; i >= 0; i--) {
- FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz);
- assert((i != 0) ||
- ((fc == ffc) && ffc->is_free() &&
- (ffc->size() == k*word_sz) && (fc_size == word_sz)),
- "Counting error");
- ffc->set_size(word_sz);
- ffc->link_prev(NULL); // Mark as a free block for other (parallel) GC threads.
- ffc->link_next(NULL);
- // Above must occur before BOT is updated below.
- OrderAccess::storestore();
- // splitting from the right, fc_size == i * word_sz
- _bt.mark_block((HeapWord*)ffc, word_sz, true /* reducing */);
- fc_size -= word_sz;
- assert(fc_size == i*word_sz, "Error");
- _bt.verify_not_unallocated((HeapWord*)ffc, word_sz);
- _bt.verify_single_block((HeapWord*)fc, fc_size);
- _bt.verify_single_block((HeapWord*)ffc, word_sz);
- // Push this on "fl".
- fl->return_chunk_at_head(ffc);
- }
- // TRAP
- assert(fl->tail()->next() == NULL, "List invariant.");
- }
- }
- // Update birth stats for this block size.
- size_t num = fl->count();
- MutexLocker x(_indexedFreeListParLocks[word_sz],
- Mutex::_no_safepoint_check_flag);
- ssize_t births = _indexedFreeList[word_sz].split_births() + num;
- _indexedFreeList[word_sz].set_split_births(births);
- return true;
- }
- }
- return found;
- }
-}
-
-FreeChunk* CompactibleFreeListSpace::get_n_way_chunk_to_split(size_t word_sz, size_t n) {
-
- FreeChunk* fc = NULL;
- FreeChunk* rem_fc = NULL;
- size_t rem;
- {
- MutexLocker x(parDictionaryAllocLock(),
- Mutex::_no_safepoint_check_flag);
- while (n > 0) {
- fc = dictionary()->get_chunk(MAX2(n * word_sz, _dictionary->min_size()));
- if (fc != NULL) {
- break;
- } else {
- n--;
- }
- }
- if (fc == NULL) return NULL;
- // Otherwise, split up that block.
- assert((ssize_t)n >= 1, "Control point invariant");
- assert(fc->is_free(), "Error: should be a free block");
- _bt.verify_single_block((HeapWord*)fc, fc->size());
- const size_t nn = fc->size() / word_sz;
- n = MIN2(nn, n);
- assert((ssize_t)n >= 1, "Control point invariant");
- rem = fc->size() - n * word_sz;
- // If there is a remainder, and it's too small, allocate one fewer.
- if (rem > 0 && rem < MinChunkSize) {
- n--; rem += word_sz;
- }
- // Note that at this point we may have n == 0.
- assert((ssize_t)n >= 0, "Control point invariant");
-
- // If n is 0, the chunk fc that was found is not large
- // enough to leave a viable remainder. We are unable to
- // allocate even one block. Return fc to the
- // dictionary and return, leaving "fl" empty.
- if (n == 0) {
- returnChunkToDictionary(fc);
- return NULL;
- }
-
- _bt.allocated((HeapWord*)fc, fc->size(), true /* reducing */); // update _unallocated_blk
- dictionary()->dict_census_update(fc->size(),
- true /*split*/,
- false /*birth*/);
-
- // First return the remainder, if any.
- // Note that we hold the lock until we decide if we're going to give
- // back the remainder to the dictionary, since a concurrent allocation
- // may otherwise see the heap as empty. (We're willing to take that
- // hit if the block is a small block.)
- if (rem > 0) {
- size_t prefix_size = n * word_sz;
- rem_fc = (FreeChunk*)((HeapWord*)fc + prefix_size);
- rem_fc->set_size(rem);
- rem_fc->link_prev(NULL); // Mark as a free block for other (parallel) GC threads.
- rem_fc->link_next(NULL);
- // Above must occur before BOT is updated below.
- assert((ssize_t)n > 0 && prefix_size > 0 && rem_fc > fc, "Error");
- OrderAccess::storestore();
- _bt.split_block((HeapWord*)fc, fc->size(), prefix_size);
- assert(fc->is_free(), "Error");
- fc->set_size(prefix_size);
- if (rem >= IndexSetSize) {
- returnChunkToDictionary(rem_fc);
- dictionary()->dict_census_update(rem, true /*split*/, true /*birth*/);
- rem_fc = NULL;
- }
- // Otherwise, return it to the small list below.
- }
- }
- if (rem_fc != NULL) {
- MutexLocker x(_indexedFreeListParLocks[rem],
- Mutex::_no_safepoint_check_flag);
- _bt.verify_not_unallocated((HeapWord*)rem_fc, rem_fc->size());
- _indexedFreeList[rem].return_chunk_at_head(rem_fc);
- smallSplitBirth(rem);
- }
- assert(n * word_sz == fc->size(),
- "Chunk size " SIZE_FORMAT " is not exactly splittable by "
- SIZE_FORMAT " sized chunks of size " SIZE_FORMAT,
- fc->size(), n, word_sz);
- return fc;
-}
-
-void CompactibleFreeListSpace:: par_get_chunk_of_blocks_dictionary(size_t word_sz, size_t targetted_number_of_chunks, AdaptiveFreeList<FreeChunk>* fl) {
-
- FreeChunk* fc = get_n_way_chunk_to_split(word_sz, targetted_number_of_chunks);
-
- if (fc == NULL) {
- return;
- }
-
- size_t n = fc->size() / word_sz;
-
- assert((ssize_t)n > 0, "Consistency");
- // Now do the splitting up.
- // Must do this in reverse order, so that anybody attempting to
- // access the main chunk sees it as a single free block until we
- // change it.
- size_t fc_size = n * word_sz;
- // All but first chunk in this loop
- for (ssize_t i = n-1; i > 0; i--) {
- FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz);
- ffc->set_size(word_sz);
- ffc->link_prev(NULL); // Mark as a free block for other (parallel) GC threads.
- ffc->link_next(NULL);
- // Above must occur before BOT is updated below.
- OrderAccess::storestore();
- // splitting from the right, fc_size == (n - i + 1) * wordsize
- _bt.mark_block((HeapWord*)ffc, word_sz, true /* reducing */);
- fc_size -= word_sz;
- _bt.verify_not_unallocated((HeapWord*)ffc, ffc->size());
- _bt.verify_single_block((HeapWord*)ffc, ffc->size());
- _bt.verify_single_block((HeapWord*)fc, fc_size);
- // Push this on "fl".
- fl->return_chunk_at_head(ffc);
- }
- // First chunk
- assert(fc->is_free() && fc->size() == n*word_sz, "Error: should still be a free block");
- // The blocks above should show their new sizes before the first block below
- fc->set_size(word_sz);
- fc->link_prev(NULL); // idempotent wrt free-ness, see assert above
- fc->link_next(NULL);
- _bt.verify_not_unallocated((HeapWord*)fc, fc->size());
- _bt.verify_single_block((HeapWord*)fc, fc->size());
- fl->return_chunk_at_head(fc);
-
- assert((ssize_t)n > 0 && (ssize_t)n == fl->count(), "Incorrect number of blocks");
- {
- // Update the stats for this block size.
- MutexLocker x(_indexedFreeListParLocks[word_sz],
- Mutex::_no_safepoint_check_flag);
- const ssize_t births = _indexedFreeList[word_sz].split_births() + n;
- _indexedFreeList[word_sz].set_split_births(births);
- // ssize_t new_surplus = _indexedFreeList[word_sz].surplus() + n;
- // _indexedFreeList[word_sz].set_surplus(new_surplus);
- }
-
- // TRAP
- assert(fl->tail()->next() == NULL, "List invariant.");
-}
-
-void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n, AdaptiveFreeList<FreeChunk>* fl) {
- assert(fl->count() == 0, "Precondition.");
- assert(word_sz < CompactibleFreeListSpace::IndexSetSize,
- "Precondition");
-
- if (par_get_chunk_of_blocks_IFL(word_sz, n, fl)) {
- // Got it
- return;
- }
-
- // Otherwise, we'll split a block from the dictionary.
- par_get_chunk_of_blocks_dictionary(word_sz, n, fl);
-}
-
-const size_t CompactibleFreeListSpace::max_flag_size_for_task_size() const {
- const size_t ergo_max = _old_gen->reserved().word_size() / (CardTable::card_size_in_words * BitsPerWord);
- return ergo_max;
-}
-
-// Set up the space's par_seq_tasks structure for work claiming
-// for parallel rescan. See CMSParRemarkTask where this is currently used.
-// XXX Need to suitably abstract and generalize this and the next
-// method into one.
-void
-CompactibleFreeListSpace::
-initialize_sequential_subtasks_for_rescan(int n_threads) {
- // The "size" of each task is fixed according to rescan_task_size.
- assert(n_threads > 0, "Unexpected n_threads argument");
- const size_t task_size = rescan_task_size();
- size_t n_tasks = (used_region().word_size() + task_size - 1)/task_size;
- assert((n_tasks == 0) == used_region().is_empty(), "n_tasks incorrect");
- assert(n_tasks == 0 ||
- ((used_region().start() + (n_tasks - 1)*task_size < used_region().end()) &&
- (used_region().start() + n_tasks*task_size >= used_region().end())),
- "n_tasks calculation incorrect");
- SequentialSubTasksDone* pst = conc_par_seq_tasks();
- assert(!pst->valid(), "Clobbering existing data?");
- // Sets the condition for completion of the subtask (how many threads
- // need to finish in order to be done).
- pst->set_n_threads(n_threads);
- pst->set_n_tasks((int)n_tasks);
-}
-
-// Set up the space's par_seq_tasks structure for work claiming
-// for parallel concurrent marking. See CMSConcMarkTask where this is currently used.
-void
-CompactibleFreeListSpace::
-initialize_sequential_subtasks_for_marking(int n_threads,
- HeapWord* low) {
- // The "size" of each task is fixed according to rescan_task_size.
- assert(n_threads > 0, "Unexpected n_threads argument");
- const size_t task_size = marking_task_size();
- assert(task_size > CardTable::card_size_in_words &&
- (task_size % CardTable::card_size_in_words == 0),
- "Otherwise arithmetic below would be incorrect");
- MemRegion span = _old_gen->reserved();
- if (low != NULL) {
- if (span.contains(low)) {
- // Align low down to a card boundary so that
- // we can use block_offset_careful() on span boundaries.
- HeapWord* aligned_low = align_down(low, CardTable::card_size);
- // Clip span prefix at aligned_low
- span = span.intersection(MemRegion(aligned_low, span.end()));
- } else if (low > span.end()) {
- span = MemRegion(low, low); // Null region
- } // else use entire span
- }
- assert(span.is_empty() ||
- ((uintptr_t)span.start() % CardTable::card_size == 0),
- "span should start at a card boundary");
- size_t n_tasks = (span.word_size() + task_size - 1)/task_size;
- assert((n_tasks == 0) == span.is_empty(), "Inconsistency");
- assert(n_tasks == 0 ||
- ((span.start() + (n_tasks - 1)*task_size < span.end()) &&
- (span.start() + n_tasks*task_size >= span.end())),
- "n_tasks calculation incorrect");
- SequentialSubTasksDone* pst = conc_par_seq_tasks();
- assert(!pst->valid(), "Clobbering existing data?");
- // Sets the condition for completion of the subtask (how many threads
- // need to finish in order to be done).
- pst->set_n_threads(n_threads);
- pst->set_n_tasks((int)n_tasks);
-}
--- a/src/hotspot/share/gc/cms/compactibleFreeListSpace.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,758 +0,0 @@
-/*
- * Copyright (c) 2001, 2019, 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_GC_CMS_COMPACTIBLEFREELISTSPACE_HPP
-#define SHARE_GC_CMS_COMPACTIBLEFREELISTSPACE_HPP
-
-#include "gc/cms/adaptiveFreeList.hpp"
-#include "gc/cms/promotionInfo.hpp"
-#include "gc/shared/blockOffsetTable.hpp"
-#include "gc/shared/cardTable.hpp"
-#include "gc/shared/space.hpp"
-#include "logging/log.hpp"
-#include "memory/binaryTreeDictionary.hpp"
-#include "memory/freeList.hpp"
-
-// Classes in support of keeping track of promotions into a non-Contiguous
-// space, in this case a CompactibleFreeListSpace.
-
-// Forward declarations
-class CMSCollector;
-class CompactibleFreeListSpace;
-class ConcurrentMarkSweepGeneration;
-class BlkClosure;
-class BlkClosureCareful;
-class FreeChunk;
-class UpwardsObjectClosure;
-class ObjectClosureCareful;
-class Klass;
-
-class AFLBinaryTreeDictionary : public BinaryTreeDictionary<FreeChunk, AdaptiveFreeList<FreeChunk> > {
- public:
- AFLBinaryTreeDictionary(MemRegion mr)
- : BinaryTreeDictionary<FreeChunk, AdaptiveFreeList<FreeChunk> >(mr) {}
-
- // Find the list with size "size" in the binary tree and update
- // the statistics in the list according to "split" (chunk was
- // split or coalesce) and "birth" (chunk was added or removed).
- void dict_census_update(size_t size, bool split, bool birth);
- // Return true if the dictionary is overpopulated (more chunks of
- // this size than desired) for size "size".
- bool coal_dict_over_populated(size_t size);
- // Methods called at the beginning of a sweep to prepare the
- // statistics for the sweep.
- void begin_sweep_dict_census(double coalSurplusPercent,
- float inter_sweep_current,
- float inter_sweep_estimate,
- float intra_sweep_estimate);
- // Methods called after the end of a sweep to modify the
- // statistics for the sweep.
- void end_sweep_dict_census(double splitSurplusPercent);
- // Accessors for statistics
- void set_tree_surplus(double splitSurplusPercent);
- void set_tree_hints(void);
- // Reset statistics for all the lists in the tree.
- void clear_tree_census(void);
- // Print the statistics for all the lists in the tree. Also may
- // print out summaries.
- void print_dict_census(outputStream* st) const;
-};
-
-class LinearAllocBlock {
- public:
- LinearAllocBlock() : _ptr(0), _word_size(0), _refillSize(0),
- _allocation_size_limit(0) {}
- void set(HeapWord* ptr, size_t word_size, size_t refill_size,
- size_t allocation_size_limit) {
- _ptr = ptr;
- _word_size = word_size;
- _refillSize = refill_size;
- _allocation_size_limit = allocation_size_limit;
- }
- HeapWord* _ptr;
- size_t _word_size;
- size_t _refillSize;
- size_t _allocation_size_limit; // Largest size that will be allocated
-
- void print_on(outputStream* st) const;
-};
-
-// Concrete subclass of CompactibleSpace that implements
-// a free list space, such as used in the concurrent mark sweep
-// generation.
-
-class CompactibleFreeListSpace: public CompactibleSpace {
- friend class VMStructs;
- friend class ConcurrentMarkSweepGeneration;
- friend class CMSCollector;
- // Local alloc buffer for promotion into this space.
- friend class CompactibleFreeListSpaceLAB;
- // Allow scan_and_* functions to call (private) overrides of the auxiliary functions on this class
- template <typename SpaceType>
- friend void CompactibleSpace::scan_and_adjust_pointers(SpaceType* space);
- template <typename SpaceType>
- friend void CompactibleSpace::scan_and_compact(SpaceType* space);
- template <typename SpaceType>
- friend void CompactibleSpace::verify_up_to_first_dead(SpaceType* space);
- template <typename SpaceType>
- friend void CompactibleSpace::scan_and_forward(SpaceType* space, CompactPoint* cp);
-
- // "Size" of chunks of work (executed during parallel remark phases
- // of CMS collection); this probably belongs in CMSCollector, although
- // it's cached here because it's used in
- // initialize_sequential_subtasks_for_rescan() which modifies
- // par_seq_tasks which also lives in Space. XXX
- const size_t _rescan_task_size;
- const size_t _marking_task_size;
-
- // Yet another sequential tasks done structure. This supports
- // CMS GC, where we have threads dynamically
- // claiming sub-tasks from a larger parallel task.
- SequentialSubTasksDone _conc_par_seq_tasks;
-
- BlockOffsetArrayNonContigSpace _bt;
-
- CMSCollector* _collector;
- ConcurrentMarkSweepGeneration* _old_gen;
-
- // Data structures for free blocks (used during allocation/sweeping)
-
- // Allocation is done linearly from two different blocks depending on
- // whether the request is small or large, in an effort to reduce
- // fragmentation. We assume that any locking for allocation is done
- // by the containing generation. Thus, none of the methods in this
- // space are re-entrant.
- enum SomeConstants {
- SmallForLinearAlloc = 16, // size < this then use _sLAB
- SmallForDictionary = 257, // size < this then use _indexedFreeList
- IndexSetSize = SmallForDictionary // keep this odd-sized
- };
- static size_t IndexSetStart;
- static size_t IndexSetStride;
- static size_t _min_chunk_size_in_bytes;
-
- private:
- enum FitStrategyOptions {
- FreeBlockStrategyNone = 0,
- FreeBlockBestFitFirst
- };
-
- PromotionInfo _promoInfo;
-
- // Helps to impose a global total order on freelistLock ranks;
- // assumes that CFLSpace's are allocated in global total order
- static int _lockRank;
-
- // A lock protecting the free lists and free blocks;
- // mutable because of ubiquity of locking even for otherwise const methods
- mutable Mutex _freelistLock;
-
- // Locking verifier convenience function
- void assert_locked() const PRODUCT_RETURN;
- void assert_locked(const Mutex* lock) const PRODUCT_RETURN;
-
- // Linear allocation blocks
- LinearAllocBlock _smallLinearAllocBlock;
-
- AFLBinaryTreeDictionary* _dictionary; // Pointer to dictionary for large size blocks
-
- // Indexed array for small size blocks
- AdaptiveFreeList<FreeChunk> _indexedFreeList[IndexSetSize];
-
- // Allocation strategy
- bool _fitStrategy; // Use best fit strategy
-
- // This is an address close to the largest free chunk in the heap.
- // It is currently assumed to be at the end of the heap. Free
- // chunks with addresses greater than nearLargestChunk are coalesced
- // in an effort to maintain a large chunk at the end of the heap.
- HeapWord* _nearLargestChunk;
-
- // Used to keep track of limit of sweep for the space
- HeapWord* _sweep_limit;
-
- // Stable value of used().
- size_t _used_stable;
-
- // Used to make the young collector update the mod union table
- MemRegionClosure* _preconsumptionDirtyCardClosure;
-
- // Support for compacting cms
- HeapWord* cross_threshold(HeapWord* start, HeapWord* end);
- HeapWord* forward(oop q, size_t size, CompactPoint* cp, HeapWord* compact_top);
-
- // Initialization helpers.
- void initializeIndexedFreeListArray();
-
- // Extra stuff to manage promotion parallelism.
-
- // A lock protecting the dictionary during par promotion allocation.
- mutable Mutex _parDictionaryAllocLock;
- Mutex* parDictionaryAllocLock() const { return &_parDictionaryAllocLock; }
-
- // Locks protecting the exact lists during par promotion allocation.
- Mutex* _indexedFreeListParLocks[IndexSetSize];
-
- // Attempt to obtain up to "n" blocks of the size "word_sz" (which is
- // required to be smaller than "IndexSetSize".) If successful,
- // adds them to "fl", which is required to be an empty free list.
- // If the count of "fl" is negative, it's absolute value indicates a
- // number of free chunks that had been previously "borrowed" from global
- // list of size "word_sz", and must now be decremented.
- void par_get_chunk_of_blocks(size_t word_sz, size_t n, AdaptiveFreeList<FreeChunk>* fl);
-
- // Used by par_get_chunk_of_blocks() for the chunks from the
- // indexed_free_lists.
- bool par_get_chunk_of_blocks_IFL(size_t word_sz, size_t n, AdaptiveFreeList<FreeChunk>* fl);
-
- // Used by par_get_chunk_of_blocks_dictionary() to get a chunk
- // evenly splittable into "n" "word_sz" chunks. Returns that
- // evenly splittable chunk. May split a larger chunk to get the
- // evenly splittable chunk.
- FreeChunk* get_n_way_chunk_to_split(size_t word_sz, size_t n);
-
- // Used by par_get_chunk_of_blocks() for the chunks from the
- // dictionary.
- void par_get_chunk_of_blocks_dictionary(size_t word_sz, size_t n, AdaptiveFreeList<FreeChunk>* fl);
-
- // Allocation helper functions
- // Allocate using a strategy that takes from the indexed free lists
- // first. This allocation strategy assumes a companion sweeping
- // strategy that attempts to keep the needed number of chunks in each
- // indexed free lists.
- HeapWord* allocate_adaptive_freelists(size_t size);
-
- // Gets a chunk from the linear allocation block (LinAB). If there
- // is not enough space in the LinAB, refills it.
- HeapWord* getChunkFromLinearAllocBlock(LinearAllocBlock* blk, size_t size);
- HeapWord* getChunkFromSmallLinearAllocBlock(size_t size);
- // Get a chunk from the space remaining in the linear allocation block. Do
- // not attempt to refill if the space is not available, return NULL. Do the
- // repairs on the linear allocation block as appropriate.
- HeapWord* getChunkFromLinearAllocBlockRemainder(LinearAllocBlock* blk, size_t size);
- inline HeapWord* getChunkFromSmallLinearAllocBlockRemainder(size_t size);
-
- // Helper function for getChunkFromIndexedFreeList.
- // Replenish the indexed free list for this "size". Do not take from an
- // underpopulated size.
- FreeChunk* getChunkFromIndexedFreeListHelper(size_t size, bool replenish = true);
-
- // Get a chunk from the indexed free list. If the indexed free list
- // does not have a free chunk, try to replenish the indexed free list
- // then get the free chunk from the replenished indexed free list.
- inline FreeChunk* getChunkFromIndexedFreeList(size_t size);
-
- // The returned chunk may be larger than requested (or null).
- FreeChunk* getChunkFromDictionary(size_t size);
- // The returned chunk is the exact size requested (or null).
- FreeChunk* getChunkFromDictionaryExact(size_t size);
-
- // Find a chunk in the indexed free list that is the best
- // fit for size "numWords".
- FreeChunk* bestFitSmall(size_t numWords);
- // For free list "fl" of chunks of size > numWords,
- // remove a chunk, split off a chunk of size numWords
- // and return it. The split off remainder is returned to
- // the free lists. The old name for getFromListGreater
- // was lookInListGreater.
- FreeChunk* getFromListGreater(AdaptiveFreeList<FreeChunk>* fl, size_t numWords);
- // Get a chunk in the indexed free list or dictionary,
- // by considering a larger chunk and splitting it.
- FreeChunk* getChunkFromGreater(size_t numWords);
- // Verify that the given chunk is in the indexed free lists.
- bool verifyChunkInIndexedFreeLists(FreeChunk* fc) const;
- // Remove the specified chunk from the indexed free lists.
- void removeChunkFromIndexedFreeList(FreeChunk* fc);
- // Remove the specified chunk from the dictionary.
- void removeChunkFromDictionary(FreeChunk* fc);
- // Split a free chunk into a smaller free chunk of size "new_size".
- // Return the smaller free chunk and return the remainder to the
- // free lists.
- FreeChunk* splitChunkAndReturnRemainder(FreeChunk* chunk, size_t new_size);
- // Add a chunk to the free lists.
- void addChunkToFreeLists(HeapWord* chunk, size_t size);
- // Add a chunk to the free lists, preferring to suffix it
- // to the last free chunk at end of space if possible, and
- // updating the block census stats as well as block offset table.
- // Take any locks as appropriate if we are multithreaded.
- void addChunkToFreeListsAtEndRecordingStats(HeapWord* chunk, size_t size);
- // Add a free chunk to the indexed free lists.
- void returnChunkToFreeList(FreeChunk* chunk);
- // Add a free chunk to the dictionary.
- void returnChunkToDictionary(FreeChunk* chunk);
-
- // Functions for maintaining the linear allocation buffers (LinAB).
- // Repairing a linear allocation block refers to operations
- // performed on the remainder of a LinAB after an allocation
- // has been made from it.
- void repairLinearAllocationBlocks();
- void repairLinearAllocBlock(LinearAllocBlock* blk);
- void refillLinearAllocBlock(LinearAllocBlock* blk);
- void refillLinearAllocBlockIfNeeded(LinearAllocBlock* blk);
- void refillLinearAllocBlocksIfNeeded();
-
- void verify_objects_initialized() const;
-
- // Statistics reporting helper functions
- void reportFreeListStatistics(const char* title) const;
- void reportIndexedFreeListStatistics(outputStream* st) const;
- size_t maxChunkSizeInIndexedFreeLists() const;
- size_t numFreeBlocksInIndexedFreeLists() const;
- // Accessor
- HeapWord* unallocated_block() const {
- if (BlockOffsetArrayUseUnallocatedBlock) {
- HeapWord* ub = _bt.unallocated_block();
- assert(ub >= bottom() &&
- ub <= end(), "space invariant");
- return ub;
- } else {
- return end();
- }
- }
- void freed(HeapWord* start, size_t size) {
- _bt.freed(start, size);
- }
-
- // Auxiliary functions for scan_and_{forward,adjust_pointers,compact} support.
- // See comments for CompactibleSpace for more information.
- inline HeapWord* scan_limit() const {
- return end();
- }
-
- inline bool scanned_block_is_obj(const HeapWord* addr) const {
- return CompactibleFreeListSpace::block_is_obj(addr); // Avoid virtual call
- }
-
- inline size_t scanned_block_size(const HeapWord* addr) const {
- return CompactibleFreeListSpace::block_size(addr); // Avoid virtual call
- }
-
- inline size_t adjust_obj_size(size_t size) const {
- return adjustObjectSize(size);
- }
-
- inline size_t obj_size(const HeapWord* addr) const;
-
- protected:
- // Reset the indexed free list to its initial empty condition.
- void resetIndexedFreeListArray();
- // Reset to an initial state with a single free block described
- // by the MemRegion parameter.
- void reset(MemRegion mr);
- // Return the total number of words in the indexed free lists.
- size_t totalSizeInIndexedFreeLists() const;
-
- public:
- // Constructor
- CompactibleFreeListSpace(BlockOffsetSharedArray* bs, MemRegion mr);
- // Accessors
- bool bestFitFirst() { return _fitStrategy == FreeBlockBestFitFirst; }
- AFLBinaryTreeDictionary* dictionary() const { return _dictionary; }
- HeapWord* nearLargestChunk() const { return _nearLargestChunk; }
- void set_nearLargestChunk(HeapWord* v) { _nearLargestChunk = v; }
-
- // Set CMS global values.
- static void set_cms_values();
-
- // Return the free chunk at the end of the space. If no such
- // chunk exists, return NULL.
- FreeChunk* find_chunk_at_end();
-
- void set_collector(CMSCollector* collector) { _collector = collector; }
-
- // Support for parallelization of rescan and marking.
- const size_t rescan_task_size() const { return _rescan_task_size; }
- const size_t marking_task_size() const { return _marking_task_size; }
- // Return ergonomic max size for CMSRescanMultiple and CMSConcMarkMultiple.
- const size_t max_flag_size_for_task_size() const;
- SequentialSubTasksDone* conc_par_seq_tasks() {return &_conc_par_seq_tasks; }
- void initialize_sequential_subtasks_for_rescan(int n_threads);
- void initialize_sequential_subtasks_for_marking(int n_threads,
- HeapWord* low = NULL);
-
- virtual MemRegionClosure* preconsumptionDirtyCardClosure() const {
- return _preconsumptionDirtyCardClosure;
- }
-
- void setPreconsumptionDirtyCardClosure(MemRegionClosure* cl) {
- _preconsumptionDirtyCardClosure = cl;
- }
-
- // Space enquiries
- size_t used() const;
- size_t free() const;
- size_t max_alloc_in_words() const;
- // XXX: should have a less conservative used_region() than that of
- // Space; we could consider keeping track of highest allocated
- // address and correcting that at each sweep, as the sweeper
- // goes through the entire allocated part of the generation. We
- // could also use that information to keep the sweeper from
- // sweeping more than is necessary. The allocator and sweeper will
- // of course need to synchronize on this, since the sweeper will
- // try to bump down the address and the allocator will try to bump it up.
- // For now, however, we'll just use the default used_region()
- // which overestimates the region by returning the entire
- // committed region (this is safe, but inefficient).
-
- // Returns monotonically increasing stable used space bytes for CMS.
- // This is required for jstat and other memory monitoring tools
- // that might otherwise see inconsistent used space values during a garbage
- // collection, promotion or allocation into compactibleFreeListSpace.
- // The value returned by this function might be smaller than the
- // actual value.
- size_t used_stable() const;
- // Recalculate and cache the current stable used() value. Only to be called
- // in places where we can be sure that the result is stable.
- void recalculate_used_stable();
-
- // Returns a subregion of the space containing all the objects in
- // the space.
- MemRegion used_region() const {
- return MemRegion(bottom(),
- BlockOffsetArrayUseUnallocatedBlock ?
- unallocated_block() : end());
- }
-
- virtual bool is_free_block(const HeapWord* p) const;
-
- // Resizing support
- void set_end(HeapWord* value); // override
-
- // Never mangle CompactibleFreeListSpace
- void mangle_unused_area() {}
- void mangle_unused_area_complete() {}
-
- // Mutual exclusion support
- Mutex* freelistLock() const { return &_freelistLock; }
-
- // Iteration support
- void oop_iterate(OopIterateClosure* cl);
-
- void object_iterate(ObjectClosure* blk);
- // Apply the closure to each object in the space whose references
- // point to objects in the heap. The usage of CompactibleFreeListSpace
- // by the ConcurrentMarkSweepGeneration for concurrent GC's allows
- // objects in the space with references to objects that are no longer
- // valid. For example, an object may reference another object
- // that has already been sweep up (collected). This method uses
- // obj_is_alive() to determine whether it is safe to iterate of
- // an object.
- void safe_object_iterate(ObjectClosure* blk);
-
- // Iterate over all objects that intersect with mr, calling "cl->do_object"
- // on each. There is an exception to this: if this closure has already
- // been invoked on an object, it may skip such objects in some cases. This is
- // Most likely to happen in an "upwards" (ascending address) iteration of
- // MemRegions.
- void object_iterate_mem(MemRegion mr, UpwardsObjectClosure* cl);
-
- // Requires that "mr" be entirely within the space.
- // Apply "cl->do_object" to all objects that intersect with "mr".
- // If the iteration encounters an unparseable portion of the region,
- // terminate the iteration and return the address of the start of the
- // subregion that isn't done. Return of "NULL" indicates that the
- // iteration completed.
- HeapWord* object_iterate_careful_m(MemRegion mr,
- ObjectClosureCareful* cl);
-
- // Override: provides a DCTO_CL specific to this kind of space.
- DirtyCardToOopClosure* new_dcto_cl(OopIterateClosure* cl,
- CardTable::PrecisionStyle precision,
- HeapWord* boundary,
- bool parallel);
-
- void blk_iterate(BlkClosure* cl);
- void blk_iterate_careful(BlkClosureCareful* cl);
- HeapWord* block_start_const(const void* p) const;
- HeapWord* block_start_careful(const void* p) const;
- size_t block_size(const HeapWord* p) const;
- size_t block_size_no_stall(HeapWord* p, const CMSCollector* c) const;
- bool block_is_obj(const HeapWord* p) const;
- bool obj_is_alive(const HeapWord* p) const;
- size_t block_size_nopar(const HeapWord* p) const;
- bool block_is_obj_nopar(const HeapWord* p) const;
-
- // Iteration support for promotion
- void save_marks();
- bool no_allocs_since_save_marks();
-
- // Iteration support for sweeping
- void save_sweep_limit() {
- _sweep_limit = BlockOffsetArrayUseUnallocatedBlock ?
- unallocated_block() : end();
- log_develop_trace(gc, sweep)(">>>>> Saving sweep limit " PTR_FORMAT
- " for space [" PTR_FORMAT "," PTR_FORMAT ") <<<<<<",
- p2i(_sweep_limit), p2i(bottom()), p2i(end()));
- }
- NOT_PRODUCT(
- void clear_sweep_limit() { _sweep_limit = NULL; }
- )
- HeapWord* sweep_limit() { return _sweep_limit; }
-
- // Apply "blk->do_oop" to the addresses of all reference fields in objects
- // promoted into this generation since the most recent save_marks() call.
- // Fields in objects allocated by applications of the closure
- // *are* included in the iteration. Thus, when the iteration completes
- // there should be no further such objects remaining.
- template <typename OopClosureType>
- void oop_since_save_marks_iterate(OopClosureType* blk);
-
- // Allocation support
- HeapWord* allocate(size_t size);
- HeapWord* par_allocate(size_t size);
-
- oop promote(oop obj, size_t obj_size);
- void gc_prologue();
- void gc_epilogue();
-
- // This call is used by a containing CMS generation / collector
- // to inform the CFLS space that a sweep has been completed
- // and that the space can do any related house-keeping functions.
- void sweep_completed();
-
- // For an object in this space, the mark-word's two
- // LSB's having the value [11] indicates that it has been
- // promoted since the most recent call to save_marks() on
- // this generation and has not subsequently been iterated
- // over (using oop_since_save_marks_iterate() above).
- // This property holds only for single-threaded collections,
- // and is typically used for Cheney scans; for MT scavenges,
- // the property holds for all objects promoted during that
- // scavenge for the duration of the scavenge and is used
- // by card-scanning to avoid scanning objects (being) promoted
- // during that scavenge.
- bool obj_allocated_since_save_marks(const oop obj) const {
- assert(is_in_reserved(obj), "Wrong space?");
- return ((PromotedObject*)obj)->hasPromotedMark();
- }
-
- // A worst-case estimate of the space required (in HeapWords) to expand the
- // heap when promoting an obj of size obj_size.
- size_t expansionSpaceRequired(size_t obj_size) const;
-
- FreeChunk* allocateScratch(size_t size);
-
- // Returns true if either the small or large linear allocation buffer is empty.
- bool linearAllocationWouldFail() const;
-
- // Adjust the chunk for the minimum size. This version is called in
- // most cases in CompactibleFreeListSpace methods.
- inline static size_t adjustObjectSize(size_t size) {
- return align_object_size(MAX2(size, (size_t)MinChunkSize));
- }
- // This is a virtual version of adjustObjectSize() that is called
- // only occasionally when the compaction space changes and the type
- // of the new compaction space is is only known to be CompactibleSpace.
- size_t adjust_object_size_v(size_t size) const {
- return adjustObjectSize(size);
- }
- // Minimum size of a free block.
- virtual size_t minimum_free_block_size() const { return MinChunkSize; }
- void removeFreeChunkFromFreeLists(FreeChunk* chunk);
- void addChunkAndRepairOffsetTable(HeapWord* chunk, size_t size,
- bool coalesced);
-
- // Support for compaction.
- void prepare_for_compaction(CompactPoint* cp);
- void adjust_pointers();
- void compact();
- // Reset the space to reflect the fact that a compaction of the
- // space has been done.
- virtual void reset_after_compaction();
-
- // Debugging support.
- void print() const;
- void print_on(outputStream* st) const;
- void prepare_for_verify();
- void verify() const;
- void verifyFreeLists() const PRODUCT_RETURN;
- void verifyIndexedFreeLists() const;
- void verifyIndexedFreeList(size_t size) const;
- // Verify that the given chunk is in the free lists:
- // i.e. either the binary tree dictionary, the indexed free lists
- // or the linear allocation block.
- bool verify_chunk_in_free_list(FreeChunk* fc) const;
- // Verify that the given chunk is the linear allocation block.
- bool verify_chunk_is_linear_alloc_block(FreeChunk* fc) const;
- // Do some basic checks on the the free lists.
- void check_free_list_consistency() const PRODUCT_RETURN;
-
- // Printing support
- void dump_at_safepoint_with_locks(CMSCollector* c, outputStream* st);
- void print_indexed_free_lists(outputStream* st) const;
- void print_dictionary_free_lists(outputStream* st) const;
- void print_promo_info_blocks(outputStream* st) const;
-
- NOT_PRODUCT (
- void initializeIndexedFreeListArrayReturnedBytes();
- size_t sumIndexedFreeListArrayReturnedBytes();
- // Return the total number of chunks in the indexed free lists.
- size_t totalCountInIndexedFreeLists() const;
- // Return the total number of chunks in the space.
- size_t totalCount();
- )
-
- // The census consists of counts of the quantities such as
- // the current count of the free chunks, number of chunks
- // created as a result of the split of a larger chunk or
- // coalescing of smaller chucks, etc. The counts in the
- // census is used to make decisions on splitting and
- // coalescing of chunks during the sweep of garbage.
-
- // Print the statistics for the free lists.
- void printFLCensus(size_t sweep_count) const;
-
- // Statistics functions
- // Initialize census for lists before the sweep.
- void beginSweepFLCensus(float inter_sweep_current,
- float inter_sweep_estimate,
- float intra_sweep_estimate);
- // Set the surplus for each of the free lists.
- void setFLSurplus();
- // Set the hint for each of the free lists.
- void setFLHints();
- // Clear the census for each of the free lists.
- void clearFLCensus();
- // Perform functions for the census after the end of the sweep.
- void endSweepFLCensus(size_t sweep_count);
- // Return true if the count of free chunks is greater
- // than the desired number of free chunks.
- bool coalOverPopulated(size_t size);
-
-// Record (for each size):
-//
-// split-births = #chunks added due to splits in (prev-sweep-end,
-// this-sweep-start)
-// split-deaths = #chunks removed for splits in (prev-sweep-end,
-// this-sweep-start)
-// num-curr = #chunks at start of this sweep
-// num-prev = #chunks at end of previous sweep
-//
-// The above are quantities that are measured. Now define:
-//
-// num-desired := num-prev + split-births - split-deaths - num-curr
-//
-// Roughly, num-prev + split-births is the supply,
-// split-deaths is demand due to other sizes
-// and num-curr is what we have left.
-//
-// Thus, num-desired is roughly speaking the "legitimate demand"
-// for blocks of this size and what we are striving to reach at the
-// end of the current sweep.
-//
-// For a given list, let num-len be its current population.
-// Define, for a free list of a given size:
-//
-// coal-overpopulated := num-len >= num-desired * coal-surplus
-// (coal-surplus is set to 1.05, i.e. we allow a little slop when
-// coalescing -- we do not coalesce unless we think that the current
-// supply has exceeded the estimated demand by more than 5%).
-//
-// For the set of sizes in the binary tree, which is neither dense nor
-// closed, it may be the case that for a particular size we have never
-// had, or do not now have, or did not have at the previous sweep,
-// chunks of that size. We need to extend the definition of
-// coal-overpopulated to such sizes as well:
-//
-// For a chunk in/not in the binary tree, extend coal-overpopulated
-// defined above to include all sizes as follows:
-//
-// . a size that is non-existent is coal-overpopulated
-// . a size that has a num-desired <= 0 as defined above is
-// coal-overpopulated.
-//
-// Also define, for a chunk heap-offset C and mountain heap-offset M:
-//
-// close-to-mountain := C >= 0.99 * M
-//
-// Now, the coalescing strategy is:
-//
-// Coalesce left-hand chunk with right-hand chunk if and
-// only if:
-//
-// EITHER
-// . left-hand chunk is of a size that is coal-overpopulated
-// OR
-// . right-hand chunk is close-to-mountain
- void smallCoalBirth(size_t size);
- void smallCoalDeath(size_t size);
- void coalBirth(size_t size);
- void coalDeath(size_t size);
- void smallSplitBirth(size_t size);
- void smallSplitDeath(size_t size);
- void split_birth(size_t size);
- void splitDeath(size_t size);
- void split(size_t from, size_t to1);
-
- double flsFrag() const;
-};
-
-// A parallel-GC-thread-local allocation buffer for allocation into a
-// CompactibleFreeListSpace.
-class CompactibleFreeListSpaceLAB : public CHeapObj<mtGC> {
- // The space that this buffer allocates into.
- CompactibleFreeListSpace* _cfls;
-
- // Our local free lists.
- AdaptiveFreeList<FreeChunk> _indexedFreeList[CompactibleFreeListSpace::IndexSetSize];
-
- // Initialized from a command-line arg.
-
- // Allocation statistics in support of dynamic adjustment of
- // #blocks to claim per get_from_global_pool() call below.
- static AdaptiveWeightedAverage
- _blocks_to_claim [CompactibleFreeListSpace::IndexSetSize];
- static size_t _global_num_blocks [CompactibleFreeListSpace::IndexSetSize];
- static uint _global_num_workers[CompactibleFreeListSpace::IndexSetSize];
- size_t _num_blocks [CompactibleFreeListSpace::IndexSetSize];
-
- // Internal work method
- void get_from_global_pool(size_t word_sz, AdaptiveFreeList<FreeChunk>* fl);
-
-public:
- static const int _default_dynamic_old_plab_size = 16;
- static const int _default_static_old_plab_size = 50;
-
- CompactibleFreeListSpaceLAB(CompactibleFreeListSpace* cfls);
-
- // Allocate and return a block of the given size, or else return NULL.
- HeapWord* alloc(size_t word_sz);
-
- // Return any unused portions of the buffer to the global pool.
- void retire(int tid);
-
- // Dynamic OldPLABSize sizing
- static void compute_desired_plab_size();
- // When the settings are modified from default static initialization
- static void modify_initialization(size_t n, unsigned wt);
-};
-
-size_t PromotionInfo::refillSize() const {
- const size_t CMSSpoolBlockSize = 256;
- const size_t sz = heap_word_size(sizeof(SpoolBlock) + sizeof(markWord)
- * CMSSpoolBlockSize);
- return CompactibleFreeListSpace::adjustObjectSize(sz);
-}
-
-#endif // SHARE_GC_CMS_COMPACTIBLEFREELISTSPACE_HPP
--- a/src/hotspot/share/gc/cms/compactibleFreeListSpace.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2001, 2018, 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_GC_CMS_COMPACTIBLEFREELISTSPACE_INLINE_HPP
-#define SHARE_GC_CMS_COMPACTIBLEFREELISTSPACE_INLINE_HPP
-
-#include "gc/cms/compactibleFreeListSpace.hpp"
-#include "gc/cms/promotionInfo.inline.hpp"
-
-template <typename OopClosureType>
-void CompactibleFreeListSpace::oop_since_save_marks_iterate(OopClosureType* blk) {
- _promoInfo.promoted_oops_iterate(blk);
-
- // This also restores any displaced headers and removes the elements from
- // the iteration set as they are processed, so that we have a clean slate
- // at the end of the iteration. Note, thus, that if new objects are
- // promoted as a result of the iteration they are iterated over as well.
- assert(_promoInfo.noPromotions(), "_promoInfo inconsistency");
-}
-
-#endif // SHARE_GC_CMS_COMPACTIBLEFREELISTSPACE_INLINE_HPP
--- a/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,8145 +0,0 @@
-/*
- * Copyright (c) 2001, 2019, 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/classLoaderDataGraph.hpp"
-#include "classfile/systemDictionary.hpp"
-#include "code/codeCache.hpp"
-#include "gc/cms/cmsGCStats.hpp"
-#include "gc/cms/cmsHeap.hpp"
-#include "gc/cms/cmsOopClosures.inline.hpp"
-#include "gc/cms/cmsVMOperations.hpp"
-#include "gc/cms/compactibleFreeListSpace.hpp"
-#include "gc/cms/concurrentMarkSweepGeneration.inline.hpp"
-#include "gc/cms/concurrentMarkSweepThread.hpp"
-#include "gc/cms/parNewGeneration.hpp"
-#include "gc/cms/promotionInfo.inline.hpp"
-#include "gc/serial/genMarkSweep.hpp"
-#include "gc/serial/tenuredGeneration.hpp"
-#include "gc/shared/adaptiveSizePolicy.hpp"
-#include "gc/shared/cardGeneration.inline.hpp"
-#include "gc/shared/cardTableRS.hpp"
-#include "gc/shared/collectedHeap.inline.hpp"
-#include "gc/shared/collectorCounters.hpp"
-#include "gc/shared/gcLocker.hpp"
-#include "gc/shared/gcPolicyCounters.hpp"
-#include "gc/shared/gcTimer.hpp"
-#include "gc/shared/gcTrace.hpp"
-#include "gc/shared/gcTraceTime.inline.hpp"
-#include "gc/shared/genCollectedHeap.hpp"
-#include "gc/shared/genOopClosures.inline.hpp"
-#include "gc/shared/isGCActiveMark.hpp"
-#include "gc/shared/owstTaskTerminator.hpp"
-#include "gc/shared/referencePolicy.hpp"
-#include "gc/shared/referenceProcessorPhaseTimes.hpp"
-#include "gc/shared/space.inline.hpp"
-#include "gc/shared/strongRootsScope.hpp"
-#include "gc/shared/taskqueue.inline.hpp"
-#include "gc/shared/weakProcessor.hpp"
-#include "gc/shared/workerPolicy.hpp"
-#include "logging/log.hpp"
-#include "logging/logStream.hpp"
-#include "memory/allocation.hpp"
-#include "memory/binaryTreeDictionary.inline.hpp"
-#include "memory/iterator.inline.hpp"
-#include "memory/padded.hpp"
-#include "memory/resourceArea.hpp"
-#include "memory/universe.hpp"
-#include "oops/access.inline.hpp"
-#include "oops/oop.inline.hpp"
-#include "prims/jvmtiExport.hpp"
-#include "runtime/atomic.hpp"
-#include "runtime/flags/flagSetting.hpp"
-#include "runtime/globals_extension.hpp"
-#include "runtime/handles.inline.hpp"
-#include "runtime/java.hpp"
-#include "runtime/orderAccess.hpp"
-#include "runtime/timer.hpp"
-#include "runtime/vmThread.hpp"
-#include "services/memoryService.hpp"
-#include "services/runtimeService.hpp"
-#include "utilities/align.hpp"
-#include "utilities/stack.inline.hpp"
-#if INCLUDE_JVMCI
-#include "jvmci/jvmci.hpp"
-#endif
-
-// statics
-CMSCollector* ConcurrentMarkSweepGeneration::_collector = NULL;
-bool CMSCollector::_full_gc_requested = false;
-GCCause::Cause CMSCollector::_full_gc_cause = GCCause::_no_gc;
-
-//////////////////////////////////////////////////////////////////
-// In support of CMS/VM thread synchronization
-//////////////////////////////////////////////////////////////////
-// We split use of the CGC_lock into 2 "levels".
-// The low-level locking is of the usual CGC_lock monitor. We introduce
-// a higher level "token" (hereafter "CMS token") built on top of the
-// low level monitor (hereafter "CGC lock").
-// The token-passing protocol gives priority to the VM thread. The
-// CMS-lock doesn't provide any fairness guarantees, but clients
-// should ensure that it is only held for very short, bounded
-// durations.
-//
-// When either of the CMS thread or the VM thread is involved in
-// collection operations during which it does not want the other
-// thread to interfere, it obtains the CMS token.
-//
-// If either thread tries to get the token while the other has
-// it, that thread waits. However, if the VM thread and CMS thread
-// both want the token, then the VM thread gets priority while the
-// CMS thread waits. This ensures, for instance, that the "concurrent"
-// phases of the CMS thread's work do not block out the VM thread
-// for long periods of time as the CMS thread continues to hog
-// the token. (See bug 4616232).
-//
-// The baton-passing functions are, however, controlled by the
-// flags _foregroundGCShouldWait and _foregroundGCIsActive,
-// and here the low-level CMS lock, not the high level token,
-// ensures mutual exclusion.
-//
-// Two important conditions that we have to satisfy:
-// 1. if a thread does a low-level wait on the CMS lock, then it
-// relinquishes the CMS token if it were holding that token
-// when it acquired the low-level CMS lock.
-// 2. any low-level notifications on the low-level lock
-// should only be sent when a thread has relinquished the token.
-//
-// In the absence of either property, we'd have potential deadlock.
-//
-// We protect each of the CMS (concurrent and sequential) phases
-// with the CMS _token_, not the CMS _lock_.
-//
-// The only code protected by CMS lock is the token acquisition code
-// itself, see ConcurrentMarkSweepThread::[de]synchronize(), and the
-// baton-passing code.
-//
-// Unfortunately, i couldn't come up with a good abstraction to factor and
-// hide the naked CGC_lock manipulation in the baton-passing code
-// further below. That's something we should try to do. Also, the proof
-// of correctness of this 2-level locking scheme is far from obvious,
-// and potentially quite slippery. We have an uneasy suspicion, for instance,
-// that there may be a theoretical possibility of delay/starvation in the
-// low-level lock/wait/notify scheme used for the baton-passing because of
-// potential interference with the priority scheme embodied in the
-// CMS-token-passing protocol. See related comments at a CGC_lock->wait()
-// invocation further below and marked with "XXX 20011219YSR".
-// Indeed, as we note elsewhere, this may become yet more slippery
-// in the presence of multiple CMS and/or multiple VM threads. XXX
-
-class CMSTokenSync: public StackObj {
- private:
- bool _is_cms_thread;
- public:
- CMSTokenSync(bool is_cms_thread):
- _is_cms_thread(is_cms_thread) {
- assert(is_cms_thread == Thread::current()->is_ConcurrentGC_thread(),
- "Incorrect argument to constructor");
- ConcurrentMarkSweepThread::synchronize(_is_cms_thread);
- }
-
- ~CMSTokenSync() {
- assert(_is_cms_thread ?
- ConcurrentMarkSweepThread::cms_thread_has_cms_token() :
- ConcurrentMarkSweepThread::vm_thread_has_cms_token(),
- "Incorrect state");
- ConcurrentMarkSweepThread::desynchronize(_is_cms_thread);
- }
-};
-
-// Convenience class that does a CMSTokenSync, and then acquires
-// upto three locks.
-class CMSTokenSyncWithLocks: public CMSTokenSync {
- private:
- // Note: locks are acquired in textual declaration order
- // and released in the opposite order
- MutexLocker _locker1, _locker2, _locker3;
- public:
- CMSTokenSyncWithLocks(bool is_cms_thread, Mutex* mutex1,
- Mutex* mutex2 = NULL, Mutex* mutex3 = NULL):
- CMSTokenSync(is_cms_thread),
- _locker1(mutex1, Mutex::_no_safepoint_check_flag),
- _locker2(mutex2, Mutex::_no_safepoint_check_flag),
- _locker3(mutex3, Mutex::_no_safepoint_check_flag)
- { }
-};
-
-
-//////////////////////////////////////////////////////////////////
-// Concurrent Mark-Sweep Generation /////////////////////////////
-//////////////////////////////////////////////////////////////////
-
-NOT_PRODUCT(CompactibleFreeListSpace* debug_cms_space;)
-
-// This struct contains per-thread things necessary to support parallel
-// young-gen collection.
-class CMSParGCThreadState: public CHeapObj<mtGC> {
- public:
- CompactibleFreeListSpaceLAB lab;
- PromotionInfo promo;
-
- // Constructor.
- CMSParGCThreadState(CompactibleFreeListSpace* cfls) : lab(cfls) {
- promo.setSpace(cfls);
- }
-};
-
-ConcurrentMarkSweepGeneration::ConcurrentMarkSweepGeneration(
- ReservedSpace rs,
- size_t initial_byte_size,
- size_t min_byte_size,
- size_t max_byte_size,
- CardTableRS* ct) :
- CardGeneration(rs, initial_byte_size, ct),
- _dilatation_factor(((double)MinChunkSize)/((double)(CollectedHeap::min_fill_size()))),
- _did_compact(false)
-{
- HeapWord* bottom = (HeapWord*) _virtual_space.low();
- HeapWord* end = (HeapWord*) _virtual_space.high();
-
- _direct_allocated_words = 0;
- NOT_PRODUCT(
- _numObjectsPromoted = 0;
- _numWordsPromoted = 0;
- _numObjectsAllocated = 0;
- _numWordsAllocated = 0;
- )
-
- _cmsSpace = new CompactibleFreeListSpace(_bts, MemRegion(bottom, end));
- NOT_PRODUCT(debug_cms_space = _cmsSpace;)
- _cmsSpace->_old_gen = this;
-
- _gc_stats = new CMSGCStats();
-
- // Verify the assumption that FreeChunk::_prev and OopDesc::_klass
- // offsets match. The ability to tell free chunks from objects
- // depends on this property.
- debug_only(
- FreeChunk* junk = NULL;
- assert(UseCompressedClassPointers ||
- junk->prev_addr() == (void*)(oop(junk)->klass_addr()),
- "Offset of FreeChunk::_prev within FreeChunk must match"
- " that of OopDesc::_klass within OopDesc");
- )
-
- _par_gc_thread_states = NEW_C_HEAP_ARRAY(CMSParGCThreadState*, ParallelGCThreads, mtGC);
- for (uint i = 0; i < ParallelGCThreads; i++) {
- _par_gc_thread_states[i] = new CMSParGCThreadState(cmsSpace());
- }
-
- _incremental_collection_failed = false;
- // The "dilatation_factor" is the expansion that can occur on
- // account of the fact that the minimum object size in the CMS
- // generation may be larger than that in, say, a contiguous young
- // generation.
- // Ideally, in the calculation below, we'd compute the dilatation
- // factor as: MinChunkSize/(promoting_gen's min object size)
- // Since we do not have such a general query interface for the
- // promoting generation, we'll instead just use the minimum
- // object size (which today is a header's worth of space);
- // note that all arithmetic is in units of HeapWords.
- assert(MinChunkSize >= CollectedHeap::min_fill_size(), "just checking");
- assert(_dilatation_factor >= 1.0, "from previous assert");
-
- initialize_performance_counters(min_byte_size, max_byte_size);
-}
-
-
-// The field "_initiating_occupancy" represents the occupancy percentage
-// at which we trigger a new collection cycle. Unless explicitly specified
-// via CMSInitiatingOccupancyFraction (argument "io" below), it
-// is calculated by:
-//
-// Let "f" be MinHeapFreeRatio in
-//
-// _initiating_occupancy = 100-f +
-// f * (CMSTriggerRatio/100)
-// where CMSTriggerRatio is the argument "tr" below.
-//
-// That is, if we assume the heap is at its desired maximum occupancy at the
-// end of a collection, we let CMSTriggerRatio of the (purported) free
-// space be allocated before initiating a new collection cycle.
-//
-void ConcurrentMarkSweepGeneration::init_initiating_occupancy(intx io, uintx tr) {
- assert(io <= 100 && tr <= 100, "Check the arguments");
- if (io >= 0) {
- _initiating_occupancy = (double)io / 100.0;
- } else {
- _initiating_occupancy = ((100 - MinHeapFreeRatio) +
- (double)(tr * MinHeapFreeRatio) / 100.0)
- / 100.0;
- }
-}
-
-void ConcurrentMarkSweepGeneration::ref_processor_init() {
- assert(collector() != NULL, "no collector");
- collector()->ref_processor_init();
-}
-
-void CMSCollector::ref_processor_init() {
- if (_ref_processor == NULL) {
- // Allocate and initialize a reference processor
- _ref_processor =
- new ReferenceProcessor(&_span_based_discoverer,
- (ParallelGCThreads > 1) && ParallelRefProcEnabled, // mt processing
- ParallelGCThreads, // mt processing degree
- _cmsGen->refs_discovery_is_mt(), // mt discovery
- MAX2(ConcGCThreads, ParallelGCThreads), // mt discovery degree
- _cmsGen->refs_discovery_is_atomic(), // discovery is not atomic
- &_is_alive_closure, // closure for liveness info
- false); // disable adjusting number of processing threads
- // Initialize the _ref_processor field of CMSGen
- _cmsGen->set_ref_processor(_ref_processor);
-
- }
-}
-
-AdaptiveSizePolicy* CMSCollector::size_policy() {
- return CMSHeap::heap()->size_policy();
-}
-
-void ConcurrentMarkSweepGeneration::initialize_performance_counters(size_t min_old_size,
- size_t max_old_size) {
-
- const char* gen_name = "old";
- // Generation Counters - generation 1, 1 subspace
- _gen_counters = new GenerationCounters(gen_name, 1, 1,
- min_old_size, max_old_size, &_virtual_space);
-
- _space_counters = new GSpaceCounters(gen_name, 0,
- _virtual_space.reserved_size(),
- this, _gen_counters);
-}
-
-CMSStats::CMSStats(ConcurrentMarkSweepGeneration* cms_gen, unsigned int alpha):
- _cms_gen(cms_gen)
-{
- assert(alpha <= 100, "bad value");
- _saved_alpha = alpha;
-
- // Initialize the alphas to the bootstrap value of 100.
- _gc0_alpha = _cms_alpha = 100;
-
- _cms_begin_time.update();
- _cms_end_time.update();
-
- _gc0_duration = 0.0;
- _gc0_period = 0.0;
- _gc0_promoted = 0;
-
- _cms_duration = 0.0;
- _cms_period = 0.0;
- _cms_allocated = 0;
-
- _cms_used_at_gc0_begin = 0;
- _cms_used_at_gc0_end = 0;
- _allow_duty_cycle_reduction = false;
- _valid_bits = 0;
-}
-
-double CMSStats::cms_free_adjustment_factor(size_t free) const {
- // TBD: CR 6909490
- return 1.0;
-}
-
-void CMSStats::adjust_cms_free_adjustment_factor(bool fail, size_t free) {
-}
-
-// If promotion failure handling is on use
-// the padded average size of the promotion for each
-// young generation collection.
-double CMSStats::time_until_cms_gen_full() const {
- size_t cms_free = _cms_gen->cmsSpace()->free();
- CMSHeap* heap = CMSHeap::heap();
- size_t expected_promotion = MIN2(heap->young_gen()->capacity(),
- (size_t) _cms_gen->gc_stats()->avg_promoted()->padded_average());
- if (cms_free > expected_promotion) {
- // Start a cms collection if there isn't enough space to promote
- // for the next young collection. Use the padded average as
- // a safety factor.
- cms_free -= expected_promotion;
-
- // Adjust by the safety factor.
- double cms_free_dbl = (double)cms_free;
- double cms_adjustment = (100.0 - CMSIncrementalSafetyFactor) / 100.0;
- // Apply a further correction factor which tries to adjust
- // for recent occurance of concurrent mode failures.
- cms_adjustment = cms_adjustment * cms_free_adjustment_factor(cms_free);
- cms_free_dbl = cms_free_dbl * cms_adjustment;
-
- log_trace(gc)("CMSStats::time_until_cms_gen_full: cms_free " SIZE_FORMAT " expected_promotion " SIZE_FORMAT,
- cms_free, expected_promotion);
- log_trace(gc)(" cms_free_dbl %f cms_consumption_rate %f", cms_free_dbl, cms_consumption_rate() + 1.0);
- // Add 1 in case the consumption rate goes to zero.
- return cms_free_dbl / (cms_consumption_rate() + 1.0);
- }
- return 0.0;
-}
-
-// Compare the duration of the cms collection to the
-// time remaining before the cms generation is empty.
-// Note that the time from the start of the cms collection
-// to the start of the cms sweep (less than the total
-// duration of the cms collection) can be used. This
-// has been tried and some applications experienced
-// promotion failures early in execution. This was
-// possibly because the averages were not accurate
-// enough at the beginning.
-double CMSStats::time_until_cms_start() const {
- // We add "gc0_period" to the "work" calculation
- // below because this query is done (mostly) at the
- // end of a scavenge, so we need to conservatively
- // account for that much possible delay
- // in the query so as to avoid concurrent mode failures
- // due to starting the collection just a wee bit too
- // late.
- double work = cms_duration() + gc0_period();
- double deadline = time_until_cms_gen_full();
- // If a concurrent mode failure occurred recently, we want to be
- // more conservative and halve our expected time_until_cms_gen_full()
- if (work > deadline) {
- log_develop_trace(gc)("CMSCollector: collect because of anticipated promotion before full %3.7f + %3.7f > %3.7f ",
- cms_duration(), gc0_period(), time_until_cms_gen_full());
- return 0.0;
- }
- return work - deadline;
-}
-
-#ifndef PRODUCT
-void CMSStats::print_on(outputStream *st) const {
- st->print(" gc0_alpha=%d,cms_alpha=%d", _gc0_alpha, _cms_alpha);
- st->print(",gc0_dur=%g,gc0_per=%g,gc0_promo=" SIZE_FORMAT,
- gc0_duration(), gc0_period(), gc0_promoted());
- st->print(",cms_dur=%g,cms_per=%g,cms_alloc=" SIZE_FORMAT,
- cms_duration(), cms_period(), cms_allocated());
- st->print(",cms_since_beg=%g,cms_since_end=%g",
- cms_time_since_begin(), cms_time_since_end());
- st->print(",cms_used_beg=" SIZE_FORMAT ",cms_used_end=" SIZE_FORMAT,
- _cms_used_at_gc0_begin, _cms_used_at_gc0_end);
-
- if (valid()) {
- st->print(",promo_rate=%g,cms_alloc_rate=%g",
- promotion_rate(), cms_allocation_rate());
- st->print(",cms_consumption_rate=%g,time_until_full=%g",
- cms_consumption_rate(), time_until_cms_gen_full());
- }
- st->cr();
-}
-#endif // #ifndef PRODUCT
-
-CMSCollector::CollectorState CMSCollector::_collectorState =
- CMSCollector::Idling;
-bool CMSCollector::_foregroundGCIsActive = false;
-bool CMSCollector::_foregroundGCShouldWait = false;
-
-CMSCollector::CMSCollector(ConcurrentMarkSweepGeneration* cmsGen,
- CardTableRS* ct):
- _overflow_list(NULL),
- _conc_workers(NULL), // may be set later
- _completed_initialization(false),
- _collection_count_start(0),
- _should_unload_classes(CMSClassUnloadingEnabled),
- _concurrent_cycles_since_last_unload(0),
- _roots_scanning_options(GenCollectedHeap::SO_None),
- _verification_mark_bm(0, Mutex::leaf + 1, "CMS_verification_mark_bm_lock"),
- _verifying(false),
- _inter_sweep_estimate(CMS_SweepWeight, CMS_SweepPadding),
- _intra_sweep_estimate(CMS_SweepWeight, CMS_SweepPadding),
- _gc_tracer_cm(new (ResourceObj::C_HEAP, mtGC) CMSTracer()),
- _gc_timer_cm(new (ResourceObj::C_HEAP, mtGC) ConcurrentGCTimer()),
- _cms_start_registered(false),
- _cmsGen(cmsGen),
- // Adjust span to cover old (cms) gen
- _span(cmsGen->reserved()),
- _ct(ct),
- _markBitMap(0, Mutex::leaf + 1, "CMS_markBitMap_lock"),
- _modUnionTable((CardTable::card_shift - LogHeapWordSize),
- -1 /* lock-free */, "No_lock" /* dummy */),
- _restart_addr(NULL),
- _ser_pmc_preclean_ovflw(0),
- _ser_pmc_remark_ovflw(0),
- _par_pmc_remark_ovflw(0),
- _ser_kac_preclean_ovflw(0),
- _ser_kac_ovflw(0),
- _par_kac_ovflw(0),
-#ifndef PRODUCT
- _num_par_pushes(0),
-#endif
- _span_based_discoverer(_span),
- _ref_processor(NULL), // will be set later
- // Construct the is_alive_closure with _span & markBitMap
- _is_alive_closure(_span, &_markBitMap),
- _modUnionClosurePar(&_modUnionTable),
- _between_prologue_and_epilogue(false),
- _abort_preclean(false),
- _start_sampling(false),
- _stats(cmsGen),
- _eden_chunk_lock(new Mutex(Mutex::leaf + 1, "CMS_eden_chunk_lock", true,
- //verify that this lock should be acquired with safepoint check.
- Monitor::_safepoint_check_never)),
- _eden_chunk_array(NULL), // may be set in ctor body
- _eden_chunk_index(0), // -- ditto --
- _eden_chunk_capacity(0), // -- ditto --
- _survivor_chunk_array(NULL), // -- ditto --
- _survivor_chunk_index(0), // -- ditto --
- _survivor_chunk_capacity(0), // -- ditto --
- _survivor_plab_array(NULL) // -- ditto --
-{
- // Now expand the span and allocate the collection support structures
- // (MUT, marking bit map etc.) to cover both generations subject to
- // collection.
-
- // For use by dirty card to oop closures.
- _cmsGen->cmsSpace()->set_collector(this);
-
- // Allocate MUT and marking bit map
- {
- MutexLocker x(_markBitMap.lock(), Mutex::_no_safepoint_check_flag);
- if (!_markBitMap.allocate(_span)) {
- log_warning(gc)("Failed to allocate CMS Bit Map");
- return;
- }
- assert(_markBitMap.covers(_span), "_markBitMap inconsistency?");
- }
- {
- _modUnionTable.allocate(_span);
- assert(_modUnionTable.covers(_span), "_modUnionTable inconsistency?");
- }
-
- if (!_markStack.allocate(MarkStackSize)) {
- log_warning(gc)("Failed to allocate CMS Marking Stack");
- return;
- }
-
- // Support for multi-threaded concurrent phases
- if (CMSConcurrentMTEnabled) {
- if (FLAG_IS_DEFAULT(ConcGCThreads)) {
- // just for now
- FLAG_SET_DEFAULT(ConcGCThreads, (ParallelGCThreads + 3) / 4);
- }
- if (ConcGCThreads > 1) {
- _conc_workers = new YieldingFlexibleWorkGang("CMS Thread",
- ConcGCThreads, true);
- if (_conc_workers == NULL) {
- log_warning(gc)("GC/CMS: _conc_workers allocation failure: forcing -CMSConcurrentMTEnabled");
- CMSConcurrentMTEnabled = false;
- } else {
- _conc_workers->initialize_workers();
- }
- } else {
- CMSConcurrentMTEnabled = false;
- }
- }
- if (!CMSConcurrentMTEnabled) {
- ConcGCThreads = 0;
- } else {
- // Turn off CMSCleanOnEnter optimization temporarily for
- // the MT case where it's not fixed yet; see 6178663.
- CMSCleanOnEnter = false;
- }
- assert((_conc_workers != NULL) == (ConcGCThreads > 1),
- "Inconsistency");
- log_debug(gc)("ConcGCThreads: %u", ConcGCThreads);
- log_debug(gc)("ParallelGCThreads: %u", ParallelGCThreads);
-
- // Parallel task queues; these are shared for the
- // concurrent and stop-world phases of CMS, but
- // are not shared with parallel scavenge (ParNew).
- {
- uint i;
- uint num_queues = MAX2(ParallelGCThreads, ConcGCThreads);
-
- if ((CMSParallelRemarkEnabled || CMSConcurrentMTEnabled
- || ParallelRefProcEnabled)
- && num_queues > 0) {
- _task_queues = new OopTaskQueueSet(num_queues);
- if (_task_queues == NULL) {
- log_warning(gc)("task_queues allocation failure.");
- return;
- }
- typedef Padded<OopTaskQueue> PaddedOopTaskQueue;
- for (i = 0; i < num_queues; i++) {
- PaddedOopTaskQueue *q = new PaddedOopTaskQueue();
- if (q == NULL) {
- log_warning(gc)("work_queue allocation failure.");
- return;
- }
- _task_queues->register_queue(i, q);
- }
- for (i = 0; i < num_queues; i++) {
- _task_queues->queue(i)->initialize();
- }
- }
- }
-
- _cmsGen ->init_initiating_occupancy(CMSInitiatingOccupancyFraction, CMSTriggerRatio);
-
- // Clip CMSBootstrapOccupancy between 0 and 100.
- _bootstrap_occupancy = CMSBootstrapOccupancy / 100.0;
-
- // Now tell CMS generations the identity of their collector
- ConcurrentMarkSweepGeneration::set_collector(this);
-
- // Create & start a CMS thread for this CMS collector
- _cmsThread = ConcurrentMarkSweepThread::start(this);
- assert(cmsThread() != NULL, "CMS Thread should have been created");
- assert(cmsThread()->collector() == this,
- "CMS Thread should refer to this gen");
- assert(CGC_lock != NULL, "Where's the CGC_lock?");
-
- // Support for parallelizing young gen rescan
- CMSHeap* heap = CMSHeap::heap();
- _young_gen = heap->young_gen();
- if (heap->supports_inline_contig_alloc()) {
- _top_addr = heap->top_addr();
- _end_addr = heap->end_addr();
- assert(_young_gen != NULL, "no _young_gen");
- _eden_chunk_index = 0;
- _eden_chunk_capacity = (_young_gen->max_capacity() + CMSSamplingGrain) / CMSSamplingGrain;
- _eden_chunk_array = NEW_C_HEAP_ARRAY(HeapWord*, _eden_chunk_capacity, mtGC);
- }
-
- // Support for parallelizing survivor space rescan
- if ((CMSParallelRemarkEnabled && CMSParallelSurvivorRemarkEnabled) || CMSParallelInitialMarkEnabled) {
- const size_t max_plab_samples =
- _young_gen->max_survivor_size() / (PLAB::min_size() * HeapWordSize);
-
- _survivor_plab_array = NEW_C_HEAP_ARRAY(ChunkArray, ParallelGCThreads, mtGC);
- _survivor_chunk_array = NEW_C_HEAP_ARRAY(HeapWord*, max_plab_samples, mtGC);
- _cursor = NEW_C_HEAP_ARRAY(size_t, ParallelGCThreads, mtGC);
- _survivor_chunk_capacity = max_plab_samples;
- for (uint i = 0; i < ParallelGCThreads; i++) {
- HeapWord** vec = NEW_C_HEAP_ARRAY(HeapWord*, max_plab_samples, mtGC);
- ChunkArray* cur = ::new (&_survivor_plab_array[i]) ChunkArray(vec, max_plab_samples);
- assert(cur->end() == 0, "Should be 0");
- assert(cur->array() == vec, "Should be vec");
- assert(cur->capacity() == max_plab_samples, "Error");
- }
- }
-
- NOT_PRODUCT(_overflow_counter = CMSMarkStackOverflowInterval;)
- _gc_counters = new CollectorCounters("CMS full collection pauses", 1);
- _cgc_counters = new CollectorCounters("CMS concurrent cycle pauses", 2);
- _completed_initialization = true;
- _inter_sweep_timer.start(); // start of time
-}
-
-const char* ConcurrentMarkSweepGeneration::name() const {
- return "concurrent mark-sweep generation";
-}
-void ConcurrentMarkSweepGeneration::update_counters() {
- if (UsePerfData) {
- _space_counters->update_all();
- _gen_counters->update_all();
- }
-}
-
-// this is an optimized version of update_counters(). it takes the
-// used value as a parameter rather than computing it.
-//
-void ConcurrentMarkSweepGeneration::update_counters(size_t used) {
- if (UsePerfData) {
- _space_counters->update_used(used);
- _space_counters->update_capacity();
- _gen_counters->update_all();
- }
-}
-
-void ConcurrentMarkSweepGeneration::print() const {
- Generation::print();
- cmsSpace()->print();
-}
-
-#ifndef PRODUCT
-void ConcurrentMarkSweepGeneration::print_statistics() {
- cmsSpace()->printFLCensus(0);
-}
-#endif
-
-size_t
-ConcurrentMarkSweepGeneration::contiguous_available() const {
- // dld proposes an improvement in precision here. If the committed
- // part of the space ends in a free block we should add that to
- // uncommitted size in the calculation below. Will make this
- // change later, staying with the approximation below for the
- // time being. -- ysr.
- return MAX2(_virtual_space.uncommitted_size(), unsafe_max_alloc_nogc());
-}
-
-size_t
-ConcurrentMarkSweepGeneration::unsafe_max_alloc_nogc() const {
- return _cmsSpace->max_alloc_in_words() * HeapWordSize;
-}
-
-size_t ConcurrentMarkSweepGeneration::used_stable() const {
- return cmsSpace()->used_stable();
-}
-
-size_t ConcurrentMarkSweepGeneration::max_available() const {
- return free() + _virtual_space.uncommitted_size();
-}
-
-bool ConcurrentMarkSweepGeneration::promotion_attempt_is_safe(size_t max_promotion_in_bytes) const {
- size_t available = max_available();
- size_t av_promo = (size_t)gc_stats()->avg_promoted()->padded_average();
- bool res = (available >= av_promo) || (available >= max_promotion_in_bytes);
- log_trace(gc, promotion)("CMS: promo attempt is%s safe: available(" SIZE_FORMAT ") %s av_promo(" SIZE_FORMAT "), max_promo(" SIZE_FORMAT ")",
- res? "":" not", available, res? ">=":"<", av_promo, max_promotion_in_bytes);
- return res;
-}
-
-// At a promotion failure dump information on block layout in heap
-// (cms old generation).
-void ConcurrentMarkSweepGeneration::promotion_failure_occurred() {
- Log(gc, promotion) log;
- if (log.is_trace()) {
- LogStream ls(log.trace());
- cmsSpace()->dump_at_safepoint_with_locks(collector(), &ls);
- }
-}
-
-void ConcurrentMarkSweepGeneration::reset_after_compaction() {
- // Clear the promotion information. These pointers can be adjusted
- // along with all the other pointers into the heap but
- // compaction is expected to be a rare event with
- // a heap using cms so don't do it without seeing the need.
- for (uint i = 0; i < ParallelGCThreads; i++) {
- _par_gc_thread_states[i]->promo.reset();
- }
-}
-
-void ConcurrentMarkSweepGeneration::compute_new_size() {
- assert_locked_or_safepoint(Heap_lock);
-
- // If incremental collection failed, we just want to expand
- // to the limit.
- if (incremental_collection_failed()) {
- clear_incremental_collection_failed();
- grow_to_reserved();
- return;
- }
-
- // The heap has been compacted but not reset yet.
- // Any metric such as free() or used() will be incorrect.
-
- CardGeneration::compute_new_size();
-
- // Reset again after a possible resizing
- if (did_compact()) {
- cmsSpace()->reset_after_compaction();
- }
-}
-
-void ConcurrentMarkSweepGeneration::compute_new_size_free_list() {
- assert_locked_or_safepoint(Heap_lock);
-
- // If incremental collection failed, we just want to expand
- // to the limit.
- if (incremental_collection_failed()) {
- clear_incremental_collection_failed();
- grow_to_reserved();
- return;
- }
-
- double free_percentage = ((double) free()) / capacity();
- double desired_free_percentage = (double) MinHeapFreeRatio / 100;
- double maximum_free_percentage = (double) MaxHeapFreeRatio / 100;
-
- // compute expansion delta needed for reaching desired free percentage
- if (free_percentage < desired_free_percentage) {
- size_t desired_capacity = (size_t)(used() / ((double) 1 - desired_free_percentage));
- assert(desired_capacity >= capacity(), "invalid expansion size");
- size_t expand_bytes = MAX2(desired_capacity - capacity(), MinHeapDeltaBytes);
- Log(gc) log;
- if (log.is_trace()) {
- size_t desired_capacity = (size_t)(used() / ((double) 1 - desired_free_percentage));
- log.trace("From compute_new_size: ");
- log.trace(" Free fraction %f", free_percentage);
- log.trace(" Desired free fraction %f", desired_free_percentage);
- log.trace(" Maximum free fraction %f", maximum_free_percentage);
- log.trace(" Capacity " SIZE_FORMAT, capacity() / 1000);
- log.trace(" Desired capacity " SIZE_FORMAT, desired_capacity / 1000);
- CMSHeap* heap = CMSHeap::heap();
- size_t young_size = heap->young_gen()->capacity();
- log.trace(" Young gen size " SIZE_FORMAT, young_size / 1000);
- log.trace(" unsafe_max_alloc_nogc " SIZE_FORMAT, unsafe_max_alloc_nogc() / 1000);
- log.trace(" contiguous available " SIZE_FORMAT, contiguous_available() / 1000);
- log.trace(" Expand by " SIZE_FORMAT " (bytes)", expand_bytes);
- }
- // safe if expansion fails
- expand_for_gc_cause(expand_bytes, 0, CMSExpansionCause::_satisfy_free_ratio);
- log.trace(" Expanded free fraction %f", ((double) free()) / capacity());
- } else {
- size_t desired_capacity = (size_t)(used() / ((double) 1 - desired_free_percentage));
- assert(desired_capacity <= capacity(), "invalid expansion size");
- size_t shrink_bytes = capacity() - desired_capacity;
- // Don't shrink unless the delta is greater than the minimum shrink we want
- if (shrink_bytes >= MinHeapDeltaBytes) {
- shrink_free_list_by(shrink_bytes);
- }
- }
-}
-
-Mutex* ConcurrentMarkSweepGeneration::freelistLock() const {
- return cmsSpace()->freelistLock();
-}
-
-HeapWord* ConcurrentMarkSweepGeneration::allocate(size_t size, bool tlab) {
- CMSSynchronousYieldRequest yr;
- MutexLocker x(freelistLock(), Mutex::_no_safepoint_check_flag);
- return have_lock_and_allocate(size, tlab);
-}
-
-HeapWord* ConcurrentMarkSweepGeneration::have_lock_and_allocate(size_t size,
- bool tlab /* ignored */) {
- assert_lock_strong(freelistLock());
- size_t adjustedSize = CompactibleFreeListSpace::adjustObjectSize(size);
- HeapWord* res = cmsSpace()->allocate(adjustedSize);
- // Allocate the object live (grey) if the background collector has
- // started marking. This is necessary because the marker may
- // have passed this address and consequently this object will
- // not otherwise be greyed and would be incorrectly swept up.
- // Note that if this object contains references, the writing
- // of those references will dirty the card containing this object
- // allowing the object to be blackened (and its references scanned)
- // either during a preclean phase or at the final checkpoint.
- if (res != NULL) {
- // We may block here with an uninitialized object with
- // its mark-bit or P-bits not yet set. Such objects need
- // to be safely navigable by block_start().
- assert(oop(res)->klass_or_null() == NULL, "Object should be uninitialized here.");
- assert(!((FreeChunk*)res)->is_free(), "Error, block will look free but show wrong size");
- collector()->direct_allocated(res, adjustedSize);
- _direct_allocated_words += adjustedSize;
- // allocation counters
- NOT_PRODUCT(
- _numObjectsAllocated++;
- _numWordsAllocated += (int)adjustedSize;
- )
- }
- return res;
-}
-
-// In the case of direct allocation by mutators in a generation that
-// is being concurrently collected, the object must be allocated
-// live (grey) if the background collector has started marking.
-// This is necessary because the marker may
-// have passed this address and consequently this object will
-// not otherwise be greyed and would be incorrectly swept up.
-// Note that if this object contains references, the writing
-// of those references will dirty the card containing this object
-// allowing the object to be blackened (and its references scanned)
-// either during a preclean phase or at the final checkpoint.
-void CMSCollector::direct_allocated(HeapWord* start, size_t size) {
- assert(_markBitMap.covers(start, size), "Out of bounds");
- if (_collectorState >= Marking) {
- MutexLocker y(_markBitMap.lock(),
- Mutex::_no_safepoint_check_flag);
- // [see comments preceding SweepClosure::do_blk() below for details]
- //
- // Can the P-bits be deleted now? JJJ
- //
- // 1. need to mark the object as live so it isn't collected
- // 2. need to mark the 2nd bit to indicate the object may be uninitialized
- // 3. need to mark the end of the object so marking, precleaning or sweeping
- // can skip over uninitialized or unparsable objects. An allocated
- // object is considered uninitialized for our purposes as long as
- // its klass word is NULL. All old gen objects are parsable
- // as soon as they are initialized.)
- _markBitMap.mark(start); // object is live
- _markBitMap.mark(start + 1); // object is potentially uninitialized?
- _markBitMap.mark(start + size - 1);
- // mark end of object
- }
- // check that oop looks uninitialized
- assert(oop(start)->klass_or_null() == NULL, "_klass should be NULL");
-}
-
-void CMSCollector::promoted(bool par, HeapWord* start,
- bool is_obj_array, size_t obj_size) {
- assert(_markBitMap.covers(start), "Out of bounds");
- // See comment in direct_allocated() about when objects should
- // be allocated live.
- if (_collectorState >= Marking) {
- // we already hold the marking bit map lock, taken in
- // the prologue
- if (par) {
- _markBitMap.par_mark(start);
- } else {
- _markBitMap.mark(start);
- }
- // We don't need to mark the object as uninitialized (as
- // in direct_allocated above) because this is being done with the
- // world stopped and the object will be initialized by the
- // time the marking, precleaning or sweeping get to look at it.
- // But see the code for copying objects into the CMS generation,
- // where we need to ensure that concurrent readers of the
- // block offset table are able to safely navigate a block that
- // is in flux from being free to being allocated (and in
- // transition while being copied into) and subsequently
- // becoming a bona-fide object when the copy/promotion is complete.
- assert(SafepointSynchronize::is_at_safepoint(),
- "expect promotion only at safepoints");
-
- if (_collectorState < Sweeping) {
- // Mark the appropriate cards in the modUnionTable, so that
- // this object gets scanned before the sweep. If this is
- // not done, CMS generation references in the object might
- // not get marked.
- // For the case of arrays, which are otherwise precisely
- // marked, we need to dirty the entire array, not just its head.
- if (is_obj_array) {
- // The [par_]mark_range() method expects mr.end() below to
- // be aligned to the granularity of a bit's representation
- // in the heap. In the case of the MUT below, that's a
- // card size.
- MemRegion mr(start,
- align_up(start + obj_size,
- CardTable::card_size /* bytes */));
- if (par) {
- _modUnionTable.par_mark_range(mr);
- } else {
- _modUnionTable.mark_range(mr);
- }
- } else { // not an obj array; we can just mark the head
- if (par) {
- _modUnionTable.par_mark(start);
- } else {
- _modUnionTable.mark(start);
- }
- }
- }
- }
-}
-
-oop ConcurrentMarkSweepGeneration::promote(oop obj, size_t obj_size) {
- assert(obj_size == (size_t)obj->size(), "bad obj_size passed in");
- // allocate, copy and if necessary update promoinfo --
- // delegate to underlying space.
- assert_lock_strong(freelistLock());
-
-#ifndef PRODUCT
- if (CMSHeap::heap()->promotion_should_fail()) {
- return NULL;
- }
-#endif // #ifndef PRODUCT
-
- oop res = _cmsSpace->promote(obj, obj_size);
- if (res == NULL) {
- // expand and retry
- size_t s = _cmsSpace->expansionSpaceRequired(obj_size); // HeapWords
- expand_for_gc_cause(s*HeapWordSize, MinHeapDeltaBytes, CMSExpansionCause::_satisfy_promotion);
- // Since this is the old generation, we don't try to promote
- // into a more senior generation.
- res = _cmsSpace->promote(obj, obj_size);
- }
- if (res != NULL) {
- // See comment in allocate() about when objects should
- // be allocated live.
- assert(oopDesc::is_oop(obj), "Will dereference klass pointer below");
- collector()->promoted(false, // Not parallel
- (HeapWord*)res, obj->is_objArray(), obj_size);
- // promotion counters
- NOT_PRODUCT(
- _numObjectsPromoted++;
- _numWordsPromoted +=
- (int)(CompactibleFreeListSpace::adjustObjectSize(obj->size()));
- )
- }
- return res;
-}
-
-
-// IMPORTANT: Notes on object size recognition in CMS.
-// ---------------------------------------------------
-// A block of storage in the CMS generation is always in
-// one of three states. A free block (FREE), an allocated
-// object (OBJECT) whose size() method reports the correct size,
-// and an intermediate state (TRANSIENT) in which its size cannot
-// be accurately determined.
-// STATE IDENTIFICATION: (32 bit and 64 bit w/o COOPS)
-// -----------------------------------------------------
-// FREE: klass_word & 1 == 1; mark_word holds block size
-//
-// OBJECT: klass_word installed; klass_word != 0 && klass_word & 1 == 0;
-// obj->size() computes correct size
-//
-// TRANSIENT: klass_word == 0; size is indeterminate until we become an OBJECT
-//
-// STATE IDENTIFICATION: (64 bit+COOPS)
-// ------------------------------------
-// FREE: mark_word & CMS_FREE_BIT == 1; mark_word & ~CMS_FREE_BIT gives block_size
-//
-// OBJECT: klass_word installed; klass_word != 0;
-// obj->size() computes correct size
-//
-// TRANSIENT: klass_word == 0; size is indeterminate until we become an OBJECT
-//
-//
-// STATE TRANSITION DIAGRAM
-//
-// mut / parnew mut / parnew
-// FREE --------------------> TRANSIENT ---------------------> OBJECT --|
-// ^ |
-// |------------------------ DEAD <------------------------------------|
-// sweep mut
-//
-// While a block is in TRANSIENT state its size cannot be determined
-// so readers will either need to come back later or stall until
-// the size can be determined. Note that for the case of direct
-// allocation, P-bits, when available, may be used to determine the
-// size of an object that may not yet have been initialized.
-
-// Things to support parallel young-gen collection.
-oop
-ConcurrentMarkSweepGeneration::par_promote(int thread_num,
- oop old, markWord m,
- size_t word_sz) {
-#ifndef PRODUCT
- if (CMSHeap::heap()->promotion_should_fail()) {
- return NULL;
- }
-#endif // #ifndef PRODUCT
-
- CMSParGCThreadState* ps = _par_gc_thread_states[thread_num];
- PromotionInfo* promoInfo = &ps->promo;
- // if we are tracking promotions, then first ensure space for
- // promotion (including spooling space for saving header if necessary).
- // then allocate and copy, then track promoted info if needed.
- // When tracking (see PromotionInfo::track()), the mark word may
- // be displaced and in this case restoration of the mark word
- // occurs in the (oop_since_save_marks_)iterate phase.
- if (promoInfo->tracking() && !promoInfo->ensure_spooling_space()) {
- // Out of space for allocating spooling buffers;
- // try expanding and allocating spooling buffers.
- if (!expand_and_ensure_spooling_space(promoInfo)) {
- return NULL;
- }
- }
- assert(!promoInfo->tracking() || promoInfo->has_spooling_space(), "Control point invariant");
- const size_t alloc_sz = CompactibleFreeListSpace::adjustObjectSize(word_sz);
- HeapWord* obj_ptr = ps->lab.alloc(alloc_sz);
- if (obj_ptr == NULL) {
- obj_ptr = expand_and_par_lab_allocate(ps, alloc_sz);
- if (obj_ptr == NULL) {
- return NULL;
- }
- }
- oop obj = oop(obj_ptr);
- OrderAccess::storestore();
- assert(obj->klass_or_null() == NULL, "Object should be uninitialized here.");
- assert(!((FreeChunk*)obj_ptr)->is_free(), "Error, block will look free but show wrong size");
- // IMPORTANT: See note on object initialization for CMS above.
- // Otherwise, copy the object. Here we must be careful to insert the
- // klass pointer last, since this marks the block as an allocated object.
- // Except with compressed oops it's the mark word.
- HeapWord* old_ptr = (HeapWord*)old;
- // Restore the mark word copied above.
- obj->set_mark_raw(m);
- assert(obj->klass_or_null() == NULL, "Object should be uninitialized here.");
- assert(!((FreeChunk*)obj_ptr)->is_free(), "Error, block will look free but show wrong size");
- OrderAccess::storestore();
-
- if (UseCompressedClassPointers) {
- // Copy gap missed by (aligned) header size calculation below
- obj->set_klass_gap(old->klass_gap());
- }
- if (word_sz > (size_t)oopDesc::header_size()) {
- Copy::aligned_disjoint_words(old_ptr + oopDesc::header_size(),
- obj_ptr + oopDesc::header_size(),
- word_sz - oopDesc::header_size());
- }
-
- // Now we can track the promoted object, if necessary. We take care
- // to delay the transition from uninitialized to full object
- // (i.e., insertion of klass pointer) until after, so that it
- // atomically becomes a promoted object.
- if (promoInfo->tracking()) {
- promoInfo->track((PromotedObject*)obj, old->klass());
- }
- assert(obj->klass_or_null() == NULL, "Object should be uninitialized here.");
- assert(!((FreeChunk*)obj_ptr)->is_free(), "Error, block will look free but show wrong size");
- assert(oopDesc::is_oop(old), "Will use and dereference old klass ptr below");
-
- // Finally, install the klass pointer (this should be volatile).
- OrderAccess::storestore();
- obj->set_klass(old->klass());
- // We should now be able to calculate the right size for this object
- assert(oopDesc::is_oop(obj) && obj->size() == (int)word_sz, "Error, incorrect size computed for promoted object");
-
- collector()->promoted(true, // parallel
- obj_ptr, old->is_objArray(), word_sz);
-
- NOT_PRODUCT(
- Atomic::inc(&_numObjectsPromoted);
- Atomic::add(alloc_sz, &_numWordsPromoted);
- )
-
- return obj;
-}
-
-void
-ConcurrentMarkSweepGeneration::
-par_promote_alloc_done(int thread_num) {
- CMSParGCThreadState* ps = _par_gc_thread_states[thread_num];
- ps->lab.retire(thread_num);
-}
-
-void
-ConcurrentMarkSweepGeneration::
-par_oop_since_save_marks_iterate_done(int thread_num) {
- CMSParGCThreadState* ps = _par_gc_thread_states[thread_num];
- ParScanWithoutBarrierClosure* dummy_cl = NULL;
- ps->promo.promoted_oops_iterate(dummy_cl);
-
- // Because card-scanning has been completed, subsequent phases
- // (e.g., reference processing) will not need to recognize which
- // objects have been promoted during this GC. So, we can now disable
- // promotion tracking.
- ps->promo.stopTrackingPromotions();
-}
-
-bool ConcurrentMarkSweepGeneration::should_collect(bool full,
- size_t size,
- bool tlab)
-{
- // We allow a STW collection only if a full
- // collection was requested.
- return full || should_allocate(size, tlab); // FIX ME !!!
- // This and promotion failure handling are connected at the
- // hip and should be fixed by untying them.
-}
-
-bool CMSCollector::shouldConcurrentCollect() {
- LogTarget(Trace, gc) log;
-
- if (_full_gc_requested) {
- log.print("CMSCollector: collect because of explicit gc request (or GCLocker)");
- return true;
- }
-
- FreelistLocker x(this);
- // ------------------------------------------------------------------
- // Print out lots of information which affects the initiation of
- // a collection.
- if (log.is_enabled() && stats().valid()) {
- log.print("CMSCollector shouldConcurrentCollect: ");
-
- LogStream out(log);
- stats().print_on(&out);
-
- log.print("time_until_cms_gen_full %3.7f", stats().time_until_cms_gen_full());
- log.print("free=" SIZE_FORMAT, _cmsGen->free());
- log.print("contiguous_available=" SIZE_FORMAT, _cmsGen->contiguous_available());
- log.print("promotion_rate=%g", stats().promotion_rate());
- log.print("cms_allocation_rate=%g", stats().cms_allocation_rate());
- log.print("occupancy=%3.7f", _cmsGen->occupancy());
- log.print("initiatingOccupancy=%3.7f", _cmsGen->initiating_occupancy());
- log.print("cms_time_since_begin=%3.7f", stats().cms_time_since_begin());
- log.print("cms_time_since_end=%3.7f", stats().cms_time_since_end());
- log.print("metadata initialized %d", MetaspaceGC::should_concurrent_collect());
- }
- // ------------------------------------------------------------------
-
- // If the estimated time to complete a cms collection (cms_duration())
- // is less than the estimated time remaining until the cms generation
- // is full, start a collection.
- if (!UseCMSInitiatingOccupancyOnly) {
- if (stats().valid()) {
- if (stats().time_until_cms_start() == 0.0) {
- return true;
- }
- } else {
- // We want to conservatively collect somewhat early in order
- // to try and "bootstrap" our CMS/promotion statistics;
- // this branch will not fire after the first successful CMS
- // collection because the stats should then be valid.
- if (_cmsGen->occupancy() >= _bootstrap_occupancy) {
- log.print(" CMSCollector: collect for bootstrapping statistics: occupancy = %f, boot occupancy = %f",
- _cmsGen->occupancy(), _bootstrap_occupancy);
- return true;
- }
- }
- }
-
- // Otherwise, we start a collection cycle if
- // old gen want a collection cycle started. Each may use
- // an appropriate criterion for making this decision.
- // XXX We need to make sure that the gen expansion
- // criterion dovetails well with this. XXX NEED TO FIX THIS
- if (_cmsGen->should_concurrent_collect()) {
- log.print("CMS old gen initiated");
- return true;
- }
-
- // We start a collection if we believe an incremental collection may fail;
- // this is not likely to be productive in practice because it's probably too
- // late anyway.
- CMSHeap* heap = CMSHeap::heap();
- if (heap->incremental_collection_will_fail(true /* consult_young */)) {
- log.print("CMSCollector: collect because incremental collection will fail ");
- return true;
- }
-
- if (MetaspaceGC::should_concurrent_collect()) {
- log.print("CMSCollector: collect for metadata allocation ");
- return true;
- }
-
- // CMSTriggerInterval starts a CMS cycle if enough time has passed.
- if (CMSTriggerInterval >= 0) {
- if (CMSTriggerInterval == 0) {
- // Trigger always
- return true;
- }
-
- // Check the CMS time since begin (we do not check the stats validity
- // as we want to be able to trigger the first CMS cycle as well)
- if (stats().cms_time_since_begin() >= (CMSTriggerInterval / ((double) MILLIUNITS))) {
- if (stats().valid()) {
- log.print("CMSCollector: collect because of trigger interval (time since last begin %3.7f secs)",
- stats().cms_time_since_begin());
- } else {
- log.print("CMSCollector: collect because of trigger interval (first collection)");
- }
- return true;
- }
- }
-
- return false;
-}
-
-void CMSCollector::set_did_compact(bool v) { _cmsGen->set_did_compact(v); }
-
-// Clear _expansion_cause fields of constituent generations
-void CMSCollector::clear_expansion_cause() {
- _cmsGen->clear_expansion_cause();
-}
-
-// We should be conservative in starting a collection cycle. To
-// start too eagerly runs the risk of collecting too often in the
-// extreme. To collect too rarely falls back on full collections,
-// which works, even if not optimum in terms of concurrent work.
-// As a work around for too eagerly collecting, use the flag
-// UseCMSInitiatingOccupancyOnly. This also has the advantage of
-// giving the user an easily understandable way of controlling the
-// collections.
-// We want to start a new collection cycle if any of the following
-// conditions hold:
-// . our current occupancy exceeds the configured initiating occupancy
-// for this generation, or
-// . we recently needed to expand this space and have not, since that
-// expansion, done a collection of this generation, or
-// . the underlying space believes that it may be a good idea to initiate
-// a concurrent collection (this may be based on criteria such as the
-// following: the space uses linear allocation and linear allocation is
-// going to fail, or there is believed to be excessive fragmentation in
-// the generation, etc... or ...
-// [.(currently done by CMSCollector::shouldConcurrentCollect() only for
-// the case of the old generation; see CR 6543076):
-// we may be approaching a point at which allocation requests may fail because
-// we will be out of sufficient free space given allocation rate estimates.]
-bool ConcurrentMarkSweepGeneration::should_concurrent_collect() const {
-
- assert_lock_strong(freelistLock());
- if (occupancy() > initiating_occupancy()) {
- log_trace(gc)(" %s: collect because of occupancy %f / %f ",
- short_name(), occupancy(), initiating_occupancy());
- return true;
- }
- if (UseCMSInitiatingOccupancyOnly) {
- return false;
- }
- if (expansion_cause() == CMSExpansionCause::_satisfy_allocation) {
- log_trace(gc)(" %s: collect because expanded for allocation ", short_name());
- return true;
- }
- return false;
-}
-
-void ConcurrentMarkSweepGeneration::collect(bool full,
- bool clear_all_soft_refs,
- size_t size,
- bool tlab)
-{
- collector()->collect(full, clear_all_soft_refs, size, tlab);
-}
-
-void CMSCollector::collect(bool full,
- bool clear_all_soft_refs,
- size_t size,
- bool tlab)
-{
- // The following "if" branch is present for defensive reasons.
- // In the current uses of this interface, it can be replaced with:
- // assert(!GCLocker.is_active(), "Can't be called otherwise");
- // But I am not placing that assert here to allow future
- // generality in invoking this interface.
- if (GCLocker::is_active()) {
- // A consistency test for GCLocker
- assert(GCLocker::needs_gc(), "Should have been set already");
- // Skip this foreground collection, instead
- // expanding the heap if necessary.
- // Need the free list locks for the call to free() in compute_new_size()
- compute_new_size();
- return;
- }
- acquire_control_and_collect(full, clear_all_soft_refs);
-}
-
-void CMSCollector::request_full_gc(unsigned int full_gc_count, GCCause::Cause cause) {
- CMSHeap* heap = CMSHeap::heap();
- unsigned int gc_count = heap->total_full_collections();
- if (gc_count == full_gc_count) {
- MutexLocker y(CGC_lock, Mutex::_no_safepoint_check_flag);
- _full_gc_requested = true;
- _full_gc_cause = cause;
- CGC_lock->notify(); // nudge CMS thread
- } else {
- assert(gc_count > full_gc_count, "Error: causal loop");
- }
-}
-
-bool CMSCollector::is_external_interruption() {
- GCCause::Cause cause = CMSHeap::heap()->gc_cause();
- return GCCause::is_user_requested_gc(cause) ||
- GCCause::is_serviceability_requested_gc(cause);
-}
-
-void CMSCollector::report_concurrent_mode_interruption() {
- if (is_external_interruption()) {
- log_debug(gc)("Concurrent mode interrupted");
- } else {
- log_debug(gc)("Concurrent mode failure");
- _gc_tracer_cm->report_concurrent_mode_failure();
- }
-}
-
-
-// The foreground and background collectors need to coordinate in order
-// to make sure that they do not mutually interfere with CMS collections.
-// When a background collection is active,
-// the foreground collector may need to take over (preempt) and
-// synchronously complete an ongoing collection. Depending on the
-// frequency of the background collections and the heap usage
-// of the application, this preemption can be seldom or frequent.
-// There are only certain
-// points in the background collection that the "collection-baton"
-// can be passed to the foreground collector.
-//
-// The foreground collector will wait for the baton before
-// starting any part of the collection. The foreground collector
-// will only wait at one location.
-//
-// The background collector will yield the baton before starting a new
-// phase of the collection (e.g., before initial marking, marking from roots,
-// precleaning, final re-mark, sweep etc.) This is normally done at the head
-// of the loop which switches the phases. The background collector does some
-// of the phases (initial mark, final re-mark) with the world stopped.
-// Because of locking involved in stopping the world,
-// the foreground collector should not block waiting for the background
-// collector when it is doing a stop-the-world phase. The background
-// collector will yield the baton at an additional point just before
-// it enters a stop-the-world phase. Once the world is stopped, the
-// background collector checks the phase of the collection. If the
-// phase has not changed, it proceeds with the collection. If the
-// phase has changed, it skips that phase of the collection. See
-// the comments on the use of the Heap_lock in collect_in_background().
-//
-// Variable used in baton passing.
-// _foregroundGCIsActive - Set to true by the foreground collector when
-// it wants the baton. The foreground clears it when it has finished
-// the collection.
-// _foregroundGCShouldWait - Set to true by the background collector
-// when it is running. The foreground collector waits while
-// _foregroundGCShouldWait is true.
-// CGC_lock - monitor used to protect access to the above variables
-// and to notify the foreground and background collectors.
-// _collectorState - current state of the CMS collection.
-//
-// The foreground collector
-// acquires the CGC_lock
-// sets _foregroundGCIsActive
-// waits on the CGC_lock for _foregroundGCShouldWait to be false
-// various locks acquired in preparation for the collection
-// are released so as not to block the background collector
-// that is in the midst of a collection
-// proceeds with the collection
-// clears _foregroundGCIsActive
-// returns
-//
-// The background collector in a loop iterating on the phases of the
-// collection
-// acquires the CGC_lock
-// sets _foregroundGCShouldWait
-// if _foregroundGCIsActive is set
-// clears _foregroundGCShouldWait, notifies _CGC_lock
-// waits on _CGC_lock for _foregroundGCIsActive to become false
-// and exits the loop.
-// otherwise
-// proceed with that phase of the collection
-// if the phase is a stop-the-world phase,
-// yield the baton once more just before enqueueing
-// the stop-world CMS operation (executed by the VM thread).
-// returns after all phases of the collection are done
-//
-
-void CMSCollector::acquire_control_and_collect(bool full,
- bool clear_all_soft_refs) {
- assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint");
- assert(!Thread::current()->is_ConcurrentGC_thread(),
- "shouldn't try to acquire control from self!");
-
- // Start the protocol for acquiring control of the
- // collection from the background collector (aka CMS thread).
- assert(ConcurrentMarkSweepThread::vm_thread_has_cms_token(),
- "VM thread should have CMS token");
- // Remember the possibly interrupted state of an ongoing
- // concurrent collection
- CollectorState first_state = _collectorState;
-
- // Signal to a possibly ongoing concurrent collection that
- // we want to do a foreground collection.
- _foregroundGCIsActive = true;
-
- // release locks and wait for a notify from the background collector
- // releasing the locks in only necessary for phases which
- // do yields to improve the granularity of the collection.
- assert_lock_strong(bitMapLock());
- // We need to lock the Free list lock for the space that we are
- // currently collecting.
- assert(haveFreelistLocks(), "Must be holding free list locks");
- bitMapLock()->unlock();
- releaseFreelistLocks();
- {
- MutexLocker x(CGC_lock, Mutex::_no_safepoint_check_flag);
- if (_foregroundGCShouldWait) {
- // We are going to be waiting for action for the CMS thread;
- // it had better not be gone (for instance at shutdown)!
- assert(ConcurrentMarkSweepThread::cmst() != NULL && !ConcurrentMarkSweepThread::cmst()->has_terminated(),
- "CMS thread must be running");
- // Wait here until the background collector gives us the go-ahead
- ConcurrentMarkSweepThread::clear_CMS_flag(
- ConcurrentMarkSweepThread::CMS_vm_has_token); // release token
- // Get a possibly blocked CMS thread going:
- // Note that we set _foregroundGCIsActive true above,
- // without protection of the CGC_lock.
- CGC_lock->notify();
- assert(!ConcurrentMarkSweepThread::vm_thread_wants_cms_token(),
- "Possible deadlock");
- while (_foregroundGCShouldWait) {
- // wait for notification
- CGC_lock->wait_without_safepoint_check();
- // Possibility of delay/starvation here, since CMS token does
- // not know to give priority to VM thread? Actually, i think
- // there wouldn't be any delay/starvation, but the proof of
- // that "fact" (?) appears non-trivial. XXX 20011219YSR
- }
- ConcurrentMarkSweepThread::set_CMS_flag(
- ConcurrentMarkSweepThread::CMS_vm_has_token);
- }
- }
- // The CMS_token is already held. Get back the other locks.
- assert(ConcurrentMarkSweepThread::vm_thread_has_cms_token(),
- "VM thread should have CMS token");
- getFreelistLocks();
- bitMapLock()->lock_without_safepoint_check();
- log_debug(gc, state)("CMS foreground collector has asked for control " INTPTR_FORMAT " with first state %d",
- p2i(Thread::current()), first_state);
- log_debug(gc, state)(" gets control with state %d", _collectorState);
-
- // Inform cms gen if this was due to partial collection failing.
- // The CMS gen may use this fact to determine its expansion policy.
- CMSHeap* heap = CMSHeap::heap();
- if (heap->incremental_collection_will_fail(false /* don't consult_young */)) {
- assert(!_cmsGen->incremental_collection_failed(),
- "Should have been noticed, reacted to and cleared");
- _cmsGen->set_incremental_collection_failed();
- }
-
- if (first_state > Idling) {
- report_concurrent_mode_interruption();
- }
-
- set_did_compact(true);
-
- // If the collection is being acquired from the background
- // collector, there may be references on the discovered
- // references lists. Abandon those references, since some
- // of them may have become unreachable after concurrent
- // discovery; the STW compacting collector will redo discovery
- // more precisely, without being subject to floating garbage.
- // Leaving otherwise unreachable references in the discovered
- // lists would require special handling.
- ref_processor()->disable_discovery();
- ref_processor()->abandon_partial_discovery();
- ref_processor()->verify_no_references_recorded();
-
- if (first_state > Idling) {
- save_heap_summary();
- }
-
- do_compaction_work(clear_all_soft_refs);
-
- // Has the GC time limit been exceeded?
- size_t max_eden_size = _young_gen->max_eden_size();
- GCCause::Cause gc_cause = heap->gc_cause();
- size_policy()->check_gc_overhead_limit(_young_gen->eden()->used(),
- _cmsGen->max_capacity(),
- max_eden_size,
- full,
- gc_cause,
- heap->soft_ref_policy());
-
- // Reset the expansion cause, now that we just completed
- // a collection cycle.
- clear_expansion_cause();
- _foregroundGCIsActive = false;
- return;
-}
-
-// Resize the tenured generation
-// after obtaining the free list locks for the
-// two generations.
-void CMSCollector::compute_new_size() {
- assert_locked_or_safepoint(Heap_lock);
- FreelistLocker z(this);
- MetaspaceGC::compute_new_size();
- _cmsGen->compute_new_size_free_list();
- // recalculate CMS used space after CMS collection
- _cmsGen->cmsSpace()->recalculate_used_stable();
-}
-
-// A work method used by the foreground collector to do
-// a mark-sweep-compact.
-void CMSCollector::do_compaction_work(bool clear_all_soft_refs) {
- CMSHeap* heap = CMSHeap::heap();
-
- STWGCTimer* gc_timer = GenMarkSweep::gc_timer();
- gc_timer->register_gc_start();
-
- SerialOldTracer* gc_tracer = GenMarkSweep::gc_tracer();
- gc_tracer->report_gc_start(heap->gc_cause(), gc_timer->gc_start());
-
- heap->pre_full_gc_dump(gc_timer);
-
- GCTraceTime(Trace, gc, phases) t("CMS:MSC");
-
- // Temporarily widen the span of the weak reference processing to
- // the entire heap.
- MemRegion new_span(CMSHeap::heap()->reserved_region());
- ReferenceProcessorSpanMutator rp_mut_span(ref_processor(), new_span);
- // Temporarily, clear the "is_alive_non_header" field of the
- // reference processor.
- ReferenceProcessorIsAliveMutator rp_mut_closure(ref_processor(), NULL);
- // Temporarily make reference _processing_ single threaded (non-MT).
- ReferenceProcessorMTProcMutator rp_mut_mt_processing(ref_processor(), false);
- // Temporarily make refs discovery atomic
- ReferenceProcessorAtomicMutator rp_mut_atomic(ref_processor(), true);
- // Temporarily make reference _discovery_ single threaded (non-MT)
- ReferenceProcessorMTDiscoveryMutator rp_mut_discovery(ref_processor(), false);
-
- ref_processor()->set_enqueuing_is_done(false);
- ref_processor()->enable_discovery();
- ref_processor()->setup_policy(clear_all_soft_refs);
- // If an asynchronous collection finishes, the _modUnionTable is
- // all clear. If we are assuming the collection from an asynchronous
- // collection, clear the _modUnionTable.
- assert(_collectorState != Idling || _modUnionTable.isAllClear(),
- "_modUnionTable should be clear if the baton was not passed");
- _modUnionTable.clear_all();
- assert(_collectorState != Idling || _ct->cld_rem_set()->mod_union_is_clear(),
- "mod union for klasses should be clear if the baton was passed");
- _ct->cld_rem_set()->clear_mod_union();
-
-
- // We must adjust the allocation statistics being maintained
- // in the free list space. We do so by reading and clearing
- // the sweep timer and updating the block flux rate estimates below.
- assert(!_intra_sweep_timer.is_active(), "_intra_sweep_timer should be inactive");
- if (_inter_sweep_timer.is_active()) {
- _inter_sweep_timer.stop();
- // Note that we do not use this sample to update the _inter_sweep_estimate.
- _cmsGen->cmsSpace()->beginSweepFLCensus((float)(_inter_sweep_timer.seconds()),
- _inter_sweep_estimate.padded_average(),
- _intra_sweep_estimate.padded_average());
- }
-
- GenMarkSweep::invoke_at_safepoint(ref_processor(), clear_all_soft_refs);
- #ifdef ASSERT
- CompactibleFreeListSpace* cms_space = _cmsGen->cmsSpace();
- size_t free_size = cms_space->free();
- assert(free_size ==
- pointer_delta(cms_space->end(), cms_space->compaction_top())
- * HeapWordSize,
- "All the free space should be compacted into one chunk at top");
- assert(cms_space->dictionary()->total_chunk_size(
- debug_only(cms_space->freelistLock())) == 0 ||
- cms_space->totalSizeInIndexedFreeLists() == 0,
- "All the free space should be in a single chunk");
- size_t num = cms_space->totalCount();
- assert((free_size == 0 && num == 0) ||
- (free_size > 0 && (num == 1 || num == 2)),
- "There should be at most 2 free chunks after compaction");
- #endif // ASSERT
- _collectorState = Resetting;
- assert(_restart_addr == NULL,
- "Should have been NULL'd before baton was passed");
- reset_stw();
- _cmsGen->reset_after_compaction();
- _concurrent_cycles_since_last_unload = 0;
-
- // Clear any data recorded in the PLAB chunk arrays.
- if (_survivor_plab_array != NULL) {
- reset_survivor_plab_arrays();
- }
-
- // Adjust the per-size allocation stats for the next epoch.
- _cmsGen->cmsSpace()->endSweepFLCensus(sweep_count() /* fake */);
- // Restart the "inter sweep timer" for the next epoch.
- _inter_sweep_timer.reset();
- _inter_sweep_timer.start();
-
- // No longer a need to do a concurrent collection for Metaspace.
- MetaspaceGC::set_should_concurrent_collect(false);
-
- heap->post_full_gc_dump(gc_timer);
-
- gc_timer->register_gc_end();
-
- gc_tracer->report_gc_end(gc_timer->gc_end(), gc_timer->time_partitions());
-
- // For a mark-sweep-compact, compute_new_size() will be called
- // in the heap's do_collection() method.
-}
-
-void CMSCollector::print_eden_and_survivor_chunk_arrays() {
- Log(gc, heap) log;
- if (!log.is_trace()) {
- return;
- }
-
- ContiguousSpace* eden_space = _young_gen->eden();
- ContiguousSpace* from_space = _young_gen->from();
- ContiguousSpace* to_space = _young_gen->to();
- // Eden
- if (_eden_chunk_array != NULL) {
- log.trace("eden " PTR_FORMAT "-" PTR_FORMAT "-" PTR_FORMAT "(" SIZE_FORMAT ")",
- p2i(eden_space->bottom()), p2i(eden_space->top()),
- p2i(eden_space->end()), eden_space->capacity());
- log.trace("_eden_chunk_index=" SIZE_FORMAT ", _eden_chunk_capacity=" SIZE_FORMAT,
- _eden_chunk_index, _eden_chunk_capacity);
- for (size_t i = 0; i < _eden_chunk_index; i++) {
- log.trace("_eden_chunk_array[" SIZE_FORMAT "]=" PTR_FORMAT, i, p2i(_eden_chunk_array[i]));
- }
- }
- // Survivor
- if (_survivor_chunk_array != NULL) {
- log.trace("survivor " PTR_FORMAT "-" PTR_FORMAT "-" PTR_FORMAT "(" SIZE_FORMAT ")",
- p2i(from_space->bottom()), p2i(from_space->top()),
- p2i(from_space->end()), from_space->capacity());
- log.trace("_survivor_chunk_index=" SIZE_FORMAT ", _survivor_chunk_capacity=" SIZE_FORMAT,
- _survivor_chunk_index, _survivor_chunk_capacity);
- for (size_t i = 0; i < _survivor_chunk_index; i++) {
- log.trace("_survivor_chunk_array[" SIZE_FORMAT "]=" PTR_FORMAT, i, p2i(_survivor_chunk_array[i]));
- }
- }
-}
-
-void CMSCollector::getFreelistLocks() const {
- // Get locks for all free lists in all generations that this
- // collector is responsible for
- _cmsGen->freelistLock()->lock_without_safepoint_check();
-}
-
-void CMSCollector::releaseFreelistLocks() const {
- // Release locks for all free lists in all generations that this
- // collector is responsible for
- _cmsGen->freelistLock()->unlock();
-}
-
-bool CMSCollector::haveFreelistLocks() const {
- // Check locks for all free lists in all generations that this
- // collector is responsible for
- assert_lock_strong(_cmsGen->freelistLock());
- PRODUCT_ONLY(ShouldNotReachHere());
- return true;
-}
-
-// A utility class that is used by the CMS collector to
-// temporarily "release" the foreground collector from its
-// usual obligation to wait for the background collector to
-// complete an ongoing phase before proceeding.
-class ReleaseForegroundGC: public StackObj {
- private:
- CMSCollector* _c;
- public:
- ReleaseForegroundGC(CMSCollector* c) : _c(c) {
- assert(_c->_foregroundGCShouldWait, "Else should not need to call");
- MutexLocker x(CGC_lock, Mutex::_no_safepoint_check_flag);
- // allow a potentially blocked foreground collector to proceed
- _c->_foregroundGCShouldWait = false;
- if (_c->_foregroundGCIsActive) {
- CGC_lock->notify();
- }
- assert(!ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "Possible deadlock");
- }
-
- ~ReleaseForegroundGC() {
- assert(!_c->_foregroundGCShouldWait, "Usage protocol violation?");
- MutexLocker x(CGC_lock, Mutex::_no_safepoint_check_flag);
- _c->_foregroundGCShouldWait = true;
- }
-};
-
-void CMSCollector::collect_in_background(GCCause::Cause cause) {
- assert(Thread::current()->is_ConcurrentGC_thread(),
- "A CMS asynchronous collection is only allowed on a CMS thread.");
-
- CMSHeap* heap = CMSHeap::heap();
- {
- MutexLocker hl(Heap_lock, Mutex::_no_safepoint_check_flag);
- FreelistLocker fll(this);
- MutexLocker x(CGC_lock, Mutex::_no_safepoint_check_flag);
- if (_foregroundGCIsActive) {
- // The foreground collector is. Skip this
- // background collection.
- assert(!_foregroundGCShouldWait, "Should be clear");
- return;
- } else {
- assert(_collectorState == Idling, "Should be idling before start.");
- _collectorState = InitialMarking;
- register_gc_start(cause);
- // Reset the expansion cause, now that we are about to begin
- // a new cycle.
- clear_expansion_cause();
-
- // Clear the MetaspaceGC flag since a concurrent collection
- // is starting but also clear it after the collection.
- MetaspaceGC::set_should_concurrent_collect(false);
- }
- // Decide if we want to enable class unloading as part of the
- // ensuing concurrent GC cycle.
- update_should_unload_classes();
- _full_gc_requested = false; // acks all outstanding full gc requests
- _full_gc_cause = GCCause::_no_gc;
- // Signal that we are about to start a collection
- heap->increment_total_full_collections(); // ... starting a collection cycle
- _collection_count_start = heap->total_full_collections();
- }
-
- size_t prev_used = _cmsGen->used();
-
- // The change of the collection state is normally done at this level;
- // the exceptions are phases that are executed while the world is
- // stopped. For those phases the change of state is done while the
- // world is stopped. For baton passing purposes this allows the
- // background collector to finish the phase and change state atomically.
- // The foreground collector cannot wait on a phase that is done
- // while the world is stopped because the foreground collector already
- // has the world stopped and would deadlock.
- while (_collectorState != Idling) {
- log_debug(gc, state)("Thread " INTPTR_FORMAT " in CMS state %d",
- p2i(Thread::current()), _collectorState);
- // The foreground collector
- // holds the Heap_lock throughout its collection.
- // holds the CMS token (but not the lock)
- // except while it is waiting for the background collector to yield.
- //
- // The foreground collector should be blocked (not for long)
- // if the background collector is about to start a phase
- // executed with world stopped. If the background
- // collector has already started such a phase, the
- // foreground collector is blocked waiting for the
- // Heap_lock. The stop-world phases (InitialMarking and FinalMarking)
- // are executed in the VM thread.
- //
- // The locking order is
- // PendingListLock (PLL) -- if applicable (FinalMarking)
- // Heap_lock (both this & PLL locked in VM_CMS_Operation::prologue())
- // CMS token (claimed in
- // stop_world_and_do() -->
- // safepoint_synchronize() -->
- // CMSThread::synchronize())
-
- {
- // Check if the FG collector wants us to yield.
- CMSTokenSync x(true); // is cms thread
- if (waitForForegroundGC()) {
- // We yielded to a foreground GC, nothing more to be
- // done this round.
- assert(_foregroundGCShouldWait == false, "We set it to false in "
- "waitForForegroundGC()");
- log_debug(gc, state)("CMS Thread " INTPTR_FORMAT " exiting collection CMS state %d",
- p2i(Thread::current()), _collectorState);
- return;
- } else {
- // The background collector can run but check to see if the
- // foreground collector has done a collection while the
- // background collector was waiting to get the CGC_lock
- // above. If yes, break so that _foregroundGCShouldWait
- // is cleared before returning.
- if (_collectorState == Idling) {
- break;
- }
- }
- }
-
- assert(_foregroundGCShouldWait, "Foreground collector, if active, "
- "should be waiting");
-
- switch (_collectorState) {
- case InitialMarking:
- {
- ReleaseForegroundGC x(this);
- stats().record_cms_begin();
- VM_CMS_Initial_Mark initial_mark_op(this);
- VMThread::execute(&initial_mark_op);
- }
- // The collector state may be any legal state at this point
- // since the background collector may have yielded to the
- // foreground collector.
- break;
- case Marking:
- // initial marking in checkpointRootsInitialWork has been completed
- if (markFromRoots()) { // we were successful
- assert(_collectorState == Precleaning, "Collector state should "
- "have changed");
- } else {
- assert(_foregroundGCIsActive, "Internal state inconsistency");
- }
- break;
- case Precleaning:
- // marking from roots in markFromRoots has been completed
- preclean();
- assert(_collectorState == AbortablePreclean ||
- _collectorState == FinalMarking,
- "Collector state should have changed");
- break;
- case AbortablePreclean:
- abortable_preclean();
- assert(_collectorState == FinalMarking, "Collector state should "
- "have changed");
- break;
- case FinalMarking:
- {
- ReleaseForegroundGC x(this);
-
- VM_CMS_Final_Remark final_remark_op(this);
- VMThread::execute(&final_remark_op);
- }
- assert(_foregroundGCShouldWait, "block post-condition");
- break;
- case Sweeping:
- // final marking in checkpointRootsFinal has been completed
- sweep();
- assert(_collectorState == Resizing, "Collector state change "
- "to Resizing must be done under the free_list_lock");
-
- case Resizing: {
- // Sweeping has been completed...
- // At this point the background collection has completed.
- // Don't move the call to compute_new_size() down
- // into code that might be executed if the background
- // collection was preempted.
- {
- ReleaseForegroundGC x(this); // unblock FG collection
- MutexLocker y(Heap_lock, Mutex::_no_safepoint_check_flag);
- CMSTokenSync z(true); // not strictly needed.
- if (_collectorState == Resizing) {
- compute_new_size();
- save_heap_summary();
- _collectorState = Resetting;
- } else {
- assert(_collectorState == Idling, "The state should only change"
- " because the foreground collector has finished the collection");
- }
- }
- break;
- }
- case Resetting:
- // CMS heap resizing has been completed
- reset_concurrent();
- assert(_collectorState == Idling, "Collector state should "
- "have changed");
-
- MetaspaceGC::set_should_concurrent_collect(false);
-
- stats().record_cms_end();
- // Don't move the concurrent_phases_end() and compute_new_size()
- // calls to here because a preempted background collection
- // has it's state set to "Resetting".
- break;
- case Idling:
- default:
- ShouldNotReachHere();
- break;
- }
- log_debug(gc, state)(" Thread " INTPTR_FORMAT " done - next CMS state %d",
- p2i(Thread::current()), _collectorState);
- assert(_foregroundGCShouldWait, "block post-condition");
- }
-
- // Should this be in gc_epilogue?
- heap->counters()->update_counters();
-
- {
- // Clear _foregroundGCShouldWait and, in the event that the
- // foreground collector is waiting, notify it, before
- // returning.
- MutexLocker x(CGC_lock, Mutex::_no_safepoint_check_flag);
- _foregroundGCShouldWait = false;
- if (_foregroundGCIsActive) {
- CGC_lock->notify();
- }
- assert(!ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "Possible deadlock");
- }
- log_debug(gc, state)("CMS Thread " INTPTR_FORMAT " exiting collection CMS state %d",
- p2i(Thread::current()), _collectorState);
- log_info(gc, heap)("Old: " SIZE_FORMAT "K->" SIZE_FORMAT "K(" SIZE_FORMAT "K)",
- prev_used / K, _cmsGen->used()/K, _cmsGen->capacity() /K);
-}
-
-void CMSCollector::register_gc_start(GCCause::Cause cause) {
- _cms_start_registered = true;
- _gc_timer_cm->register_gc_start();
- _gc_tracer_cm->report_gc_start(cause, _gc_timer_cm->gc_start());
-}
-
-void CMSCollector::register_gc_end() {
- if (_cms_start_registered) {
- report_heap_summary(GCWhen::AfterGC);
-
- _gc_timer_cm->register_gc_end();
- _gc_tracer_cm->report_gc_end(_gc_timer_cm->gc_end(), _gc_timer_cm->time_partitions());
- _cms_start_registered = false;
- }
-}
-
-void CMSCollector::save_heap_summary() {
- CMSHeap* heap = CMSHeap::heap();
- _last_heap_summary = heap->create_heap_summary();
- _last_metaspace_summary = heap->create_metaspace_summary();
-}
-
-void CMSCollector::report_heap_summary(GCWhen::Type when) {
- _gc_tracer_cm->report_gc_heap_summary(when, _last_heap_summary);
- _gc_tracer_cm->report_metaspace_summary(when, _last_metaspace_summary);
-}
-
-bool CMSCollector::waitForForegroundGC() {
- bool res = false;
- assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "CMS thread should have CMS token");
- // Block the foreground collector until the
- // background collectors decides whether to
- // yield.
- MutexLocker x(CGC_lock, Mutex::_no_safepoint_check_flag);
- _foregroundGCShouldWait = true;
- if (_foregroundGCIsActive) {
- // The background collector yields to the
- // foreground collector and returns a value
- // indicating that it has yielded. The foreground
- // collector can proceed.
- res = true;
- _foregroundGCShouldWait = false;
- ConcurrentMarkSweepThread::clear_CMS_flag(
- ConcurrentMarkSweepThread::CMS_cms_has_token);
- ConcurrentMarkSweepThread::set_CMS_flag(
- ConcurrentMarkSweepThread::CMS_cms_wants_token);
- // Get a possibly blocked foreground thread going
- CGC_lock->notify();
- log_debug(gc, state)("CMS Thread " INTPTR_FORMAT " waiting at CMS state %d",
- p2i(Thread::current()), _collectorState);
- while (_foregroundGCIsActive) {
- CGC_lock->wait_without_safepoint_check();
- }
- ConcurrentMarkSweepThread::set_CMS_flag(
- ConcurrentMarkSweepThread::CMS_cms_has_token);
- ConcurrentMarkSweepThread::clear_CMS_flag(
- ConcurrentMarkSweepThread::CMS_cms_wants_token);
- }
- log_debug(gc, state)("CMS Thread " INTPTR_FORMAT " continuing at CMS state %d",
- p2i(Thread::current()), _collectorState);
- return res;
-}
-
-// Because of the need to lock the free lists and other structures in
-// the collector, common to all the generations that the collector is
-// collecting, we need the gc_prologues of individual CMS generations
-// delegate to their collector. It may have been simpler had the
-// current infrastructure allowed one to call a prologue on a
-// collector. In the absence of that we have the generation's
-// prologue delegate to the collector, which delegates back
-// some "local" work to a worker method in the individual generations
-// that it's responsible for collecting, while itself doing any
-// work common to all generations it's responsible for. A similar
-// comment applies to the gc_epilogue()'s.
-// The role of the variable _between_prologue_and_epilogue is to
-// enforce the invocation protocol.
-void CMSCollector::gc_prologue(bool full) {
- // Call gc_prologue_work() for the CMSGen
- // we are responsible for.
-
- // The following locking discipline assumes that we are only called
- // when the world is stopped.
- assert(SafepointSynchronize::is_at_safepoint(), "world is stopped assumption");
-
- // The CMSCollector prologue must call the gc_prologues for the
- // "generations" that it's responsible
- // for.
-
- assert( Thread::current()->is_VM_thread()
- || ( CMSScavengeBeforeRemark
- && Thread::current()->is_ConcurrentGC_thread()),
- "Incorrect thread type for prologue execution");
-
- if (_between_prologue_and_epilogue) {
- // We have already been invoked; this is a gc_prologue delegation
- // from yet another CMS generation that we are responsible for, just
- // ignore it since all relevant work has already been done.
- return;
- }
-
- // set a bit saying prologue has been called; cleared in epilogue
- _between_prologue_and_epilogue = true;
- // Claim locks for common data structures, then call gc_prologue_work()
- // for each CMSGen.
-
- getFreelistLocks(); // gets free list locks on constituent spaces
- bitMapLock()->lock_without_safepoint_check();
-
- // Should call gc_prologue_work() for all cms gens we are responsible for
- bool duringMarking = _collectorState >= Marking
- && _collectorState < Sweeping;
-
- // The young collections clear the modified oops state, which tells if
- // there are any modified oops in the class. The remark phase also needs
- // that information. Tell the young collection to save the union of all
- // modified klasses.
- if (duringMarking) {
- _ct->cld_rem_set()->set_accumulate_modified_oops(true);
- }
-
- bool registerClosure = duringMarking;
-
- _cmsGen->gc_prologue_work(full, registerClosure, &_modUnionClosurePar);
-
- if (!full) {
- stats().record_gc0_begin();
- }
-}
-
-void ConcurrentMarkSweepGeneration::gc_prologue(bool full) {
-
- _capacity_at_prologue = capacity();
- _used_at_prologue = used();
- _cmsSpace->recalculate_used_stable();
-
- // We enable promotion tracking so that card-scanning can recognize
- // which objects have been promoted during this GC and skip them.
- for (uint i = 0; i < ParallelGCThreads; i++) {
- _par_gc_thread_states[i]->promo.startTrackingPromotions();
- }
-
- // Delegate to CMScollector which knows how to coordinate between
- // this and any other CMS generations that it is responsible for
- // collecting.
- collector()->gc_prologue(full);
-}
-
-// This is a "private" interface for use by this generation's CMSCollector.
-// Not to be called directly by any other entity (for instance,
-// GenCollectedHeap, which calls the "public" gc_prologue method above).
-void ConcurrentMarkSweepGeneration::gc_prologue_work(bool full,
- bool registerClosure, ModUnionClosure* modUnionClosure) {
- assert(!incremental_collection_failed(), "Shouldn't be set yet");
- assert(cmsSpace()->preconsumptionDirtyCardClosure() == NULL,
- "Should be NULL");
- if (registerClosure) {
- cmsSpace()->setPreconsumptionDirtyCardClosure(modUnionClosure);
- }
- cmsSpace()->gc_prologue();
- // Clear stat counters
- NOT_PRODUCT(
- assert(_numObjectsPromoted == 0, "check");
- assert(_numWordsPromoted == 0, "check");
- log_develop_trace(gc, alloc)("Allocated " SIZE_FORMAT " objects, " SIZE_FORMAT " bytes concurrently",
- _numObjectsAllocated, _numWordsAllocated*sizeof(HeapWord));
- _numObjectsAllocated = 0;
- _numWordsAllocated = 0;
- )
-}
-
-void CMSCollector::gc_epilogue(bool full) {
- // The following locking discipline assumes that we are only called
- // when the world is stopped.
- assert(SafepointSynchronize::is_at_safepoint(),
- "world is stopped assumption");
-
- // Currently the CMS epilogue (see CompactibleFreeListSpace) merely checks
- // if linear allocation blocks need to be appropriately marked to allow the
- // the blocks to be parsable. We also check here whether we need to nudge the
- // CMS collector thread to start a new cycle (if it's not already active).
- assert( Thread::current()->is_VM_thread()
- || ( CMSScavengeBeforeRemark
- && Thread::current()->is_ConcurrentGC_thread()),
- "Incorrect thread type for epilogue execution");
-
- if (!_between_prologue_and_epilogue) {
- // We have already been invoked; this is a gc_epilogue delegation
- // from yet another CMS generation that we are responsible for, just
- // ignore it since all relevant work has already been done.
- return;
- }
- assert(haveFreelistLocks(), "must have freelist locks");
- assert_lock_strong(bitMapLock());
-
- _ct->cld_rem_set()->set_accumulate_modified_oops(false);
-
- _cmsGen->gc_epilogue_work(full);
-
- if (_collectorState == AbortablePreclean || _collectorState == Precleaning) {
- // in case sampling was not already enabled, enable it
- _start_sampling = true;
- }
- // reset _eden_chunk_array so sampling starts afresh
- _eden_chunk_index = 0;
-
- size_t cms_used = _cmsGen->cmsSpace()->used();
- _cmsGen->cmsSpace()->recalculate_used_stable();
-
- // update performance counters - this uses a special version of
- // update_counters() that allows the utilization to be passed as a
- // parameter, avoiding multiple calls to used().
- //
- _cmsGen->update_counters(cms_used);
-
- bitMapLock()->unlock();
- releaseFreelistLocks();
-
- if (!CleanChunkPoolAsync) {
- Chunk::clean_chunk_pool();
- }
-
- set_did_compact(false);
- _between_prologue_and_epilogue = false; // ready for next cycle
-}
-
-void ConcurrentMarkSweepGeneration::gc_epilogue(bool full) {
- collector()->gc_epilogue(full);
-
- // When using ParNew, promotion tracking should have already been
- // disabled. However, the prologue (which enables promotion
- // tracking) and epilogue are called irrespective of the type of
- // GC. So they will also be called before and after Full GCs, during
- // which promotion tracking will not be explicitly disabled. So,
- // it's safer to also disable it here too (to be symmetric with
- // enabling it in the prologue).
- for (uint i = 0; i < ParallelGCThreads; i++) {
- _par_gc_thread_states[i]->promo.stopTrackingPromotions();
- }
-}
-
-void ConcurrentMarkSweepGeneration::gc_epilogue_work(bool full) {
- assert(!incremental_collection_failed(), "Should have been cleared");
- cmsSpace()->setPreconsumptionDirtyCardClosure(NULL);
- cmsSpace()->gc_epilogue();
- // Print stat counters
- NOT_PRODUCT(
- assert(_numObjectsAllocated == 0, "check");
- assert(_numWordsAllocated == 0, "check");
- log_develop_trace(gc, promotion)("Promoted " SIZE_FORMAT " objects, " SIZE_FORMAT " bytes",
- _numObjectsPromoted, _numWordsPromoted*sizeof(HeapWord));
- _numObjectsPromoted = 0;
- _numWordsPromoted = 0;
- )
-
- // Call down the chain in contiguous_available needs the freelistLock
- // so print this out before releasing the freeListLock.
- log_develop_trace(gc)(" Contiguous available " SIZE_FORMAT " bytes ", contiguous_available());
-}
-
-#ifndef PRODUCT
-bool CMSCollector::have_cms_token() {
- Thread* thr = Thread::current();
- if (thr->is_VM_thread()) {
- return ConcurrentMarkSweepThread::vm_thread_has_cms_token();
- } else if (thr->is_ConcurrentGC_thread()) {
- return ConcurrentMarkSweepThread::cms_thread_has_cms_token();
- } else if (thr->is_GC_task_thread()) {
- return ConcurrentMarkSweepThread::vm_thread_has_cms_token() &&
- ParGCRareEvent_lock->owned_by_self();
- }
- return false;
-}
-
-// Check reachability of the given heap address in CMS generation,
-// treating all other generations as roots.
-bool CMSCollector::is_cms_reachable(HeapWord* addr) {
- // We could "guarantee" below, rather than assert, but I'll
- // leave these as "asserts" so that an adventurous debugger
- // could try this in the product build provided some subset of
- // the conditions were met, provided they were interested in the
- // results and knew that the computation below wouldn't interfere
- // with other concurrent computations mutating the structures
- // being read or written.
- assert(SafepointSynchronize::is_at_safepoint(),
- "Else mutations in object graph will make answer suspect");
- assert(have_cms_token(), "Should hold cms token");
- assert(haveFreelistLocks(), "must hold free list locks");
- assert_lock_strong(bitMapLock());
-
- // Clear the marking bit map array before starting, but, just
- // for kicks, first report if the given address is already marked
- tty->print_cr("Start: Address " PTR_FORMAT " is%s marked", p2i(addr),
- _markBitMap.isMarked(addr) ? "" : " not");
-
- if (verify_after_remark()) {
- MutexLocker x(verification_mark_bm()->lock(), Mutex::_no_safepoint_check_flag);
- bool result = verification_mark_bm()->isMarked(addr);
- tty->print_cr("TransitiveMark: Address " PTR_FORMAT " %s marked", p2i(addr),
- result ? "IS" : "is NOT");
- return result;
- } else {
- tty->print_cr("Could not compute result");
- return false;
- }
-}
-#endif
-
-void
-CMSCollector::print_on_error(outputStream* st) {
- CMSCollector* collector = ConcurrentMarkSweepGeneration::_collector;
- if (collector != NULL) {
- CMSBitMap* bitmap = &collector->_markBitMap;
- st->print_cr("Marking Bits: (CMSBitMap*) " PTR_FORMAT, p2i(bitmap));
- bitmap->print_on_error(st, " Bits: ");
-
- st->cr();
-
- CMSBitMap* mut_bitmap = &collector->_modUnionTable;
- st->print_cr("Mod Union Table: (CMSBitMap*) " PTR_FORMAT, p2i(mut_bitmap));
- mut_bitmap->print_on_error(st, " Bits: ");
- }
-}
-
-////////////////////////////////////////////////////////
-// CMS Verification Support
-////////////////////////////////////////////////////////
-// Following the remark phase, the following invariant
-// should hold -- each object in the CMS heap which is
-// marked in markBitMap() should be marked in the verification_mark_bm().
-
-class VerifyMarkedClosure: public BitMapClosure {
- CMSBitMap* _marks;
- bool _failed;
-
- public:
- VerifyMarkedClosure(CMSBitMap* bm): _marks(bm), _failed(false) {}
-
- bool do_bit(size_t offset) {
- HeapWord* addr = _marks->offsetToHeapWord(offset);
- if (!_marks->isMarked(addr)) {
- Log(gc, verify) log;
- ResourceMark rm;
- LogStream ls(log.error());
- oop(addr)->print_on(&ls);
- log.error(" (" INTPTR_FORMAT " should have been marked)", p2i(addr));
- _failed = true;
- }
- return true;
- }
-
- bool failed() { return _failed; }
-};
-
-bool CMSCollector::verify_after_remark() {
- GCTraceTime(Info, gc, phases, verify) tm("Verifying CMS Marking.");
- MutexLocker ml(verification_mark_bm()->lock(), Mutex::_no_safepoint_check_flag);
- static bool init = false;
-
- assert(SafepointSynchronize::is_at_safepoint(),
- "Else mutations in object graph will make answer suspect");
- assert(have_cms_token(),
- "Else there may be mutual interference in use of "
- " verification data structures");
- assert(_collectorState > Marking && _collectorState <= Sweeping,
- "Else marking info checked here may be obsolete");
- assert(haveFreelistLocks(), "must hold free list locks");
- assert_lock_strong(bitMapLock());
-
-
- // Allocate marking bit map if not already allocated
- if (!init) { // first time
- if (!verification_mark_bm()->allocate(_span)) {
- return false;
- }
- init = true;
- }
-
- assert(verification_mark_stack()->isEmpty(), "Should be empty");
-
- // Turn off refs discovery -- so we will be tracing through refs.
- // This is as intended, because by this time
- // GC must already have cleared any refs that need to be cleared,
- // and traced those that need to be marked; moreover,
- // the marking done here is not going to interfere in any
- // way with the marking information used by GC.
- NoRefDiscovery no_discovery(ref_processor());
-
-#if COMPILER2_OR_JVMCI
- DerivedPointerTableDeactivate dpt_deact;
-#endif
-
- // Clear any marks from a previous round
- verification_mark_bm()->clear_all();
- assert(verification_mark_stack()->isEmpty(), "markStack should be empty");
- verify_work_stacks_empty();
-
- CMSHeap* heap = CMSHeap::heap();
- heap->ensure_parsability(false); // fill TLABs, but no need to retire them
- // Update the saved marks which may affect the root scans.
- heap->save_marks();
-
- if (CMSRemarkVerifyVariant == 1) {
- // In this first variant of verification, we complete
- // all marking, then check if the new marks-vector is
- // a subset of the CMS marks-vector.
- verify_after_remark_work_1();
- } else {
- guarantee(CMSRemarkVerifyVariant == 2, "Range checking for CMSRemarkVerifyVariant should guarantee 1 or 2");
- // In this second variant of verification, we flag an error
- // (i.e. an object reachable in the new marks-vector not reachable
- // in the CMS marks-vector) immediately, also indicating the
- // identify of an object (A) that references the unmarked object (B) --
- // presumably, a mutation to A failed to be picked up by preclean/remark?
- verify_after_remark_work_2();
- }
-
- return true;
-}
-
-void CMSCollector::verify_after_remark_work_1() {
- ResourceMark rm;
- HandleMark hm;
- CMSHeap* heap = CMSHeap::heap();
-
- // Get a clear set of claim bits for the roots processing to work with.
- ClassLoaderDataGraph::clear_claimed_marks();
-
- // Mark from roots one level into CMS
- MarkRefsIntoClosure notOlder(_span, verification_mark_bm());
- heap->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel.
-
- {
- StrongRootsScope srs(1);
-
- heap->cms_process_roots(&srs,
- true, // young gen as roots
- GenCollectedHeap::ScanningOption(roots_scanning_options()),
- should_unload_classes(),
- ¬Older,
- NULL);
- }
-
- // Now mark from the roots
- MarkFromRootsClosure markFromRootsClosure(this, _span,
- verification_mark_bm(), verification_mark_stack(),
- false /* don't yield */, true /* verifying */);
- assert(_restart_addr == NULL, "Expected pre-condition");
- verification_mark_bm()->iterate(&markFromRootsClosure);
- while (_restart_addr != NULL) {
- // Deal with stack overflow: by restarting at the indicated
- // address.
- HeapWord* ra = _restart_addr;
- markFromRootsClosure.reset(ra);
- _restart_addr = NULL;
- verification_mark_bm()->iterate(&markFromRootsClosure, ra, _span.end());
- }
- assert(verification_mark_stack()->isEmpty(), "Should have been drained");
- verify_work_stacks_empty();
-
- // Marking completed -- now verify that each bit marked in
- // verification_mark_bm() is also marked in markBitMap(); flag all
- // errors by printing corresponding objects.
- VerifyMarkedClosure vcl(markBitMap());
- verification_mark_bm()->iterate(&vcl);
- if (vcl.failed()) {
- Log(gc, verify) log;
- log.error("Failed marking verification after remark");
- ResourceMark rm;
- LogStream ls(log.error());
- heap->print_on(&ls);
- fatal("CMS: failed marking verification after remark");
- }
-}
-
-class VerifyCLDOopsCLDClosure : public CLDClosure {
- class VerifyCLDOopsClosure : public OopClosure {
- CMSBitMap* _bitmap;
- public:
- VerifyCLDOopsClosure(CMSBitMap* bitmap) : _bitmap(bitmap) { }
- void do_oop(oop* p) { guarantee(*p == NULL || _bitmap->isMarked((HeapWord*) *p), "Should be marked"); }
- void do_oop(narrowOop* p) { ShouldNotReachHere(); }
- } _oop_closure;
- public:
- VerifyCLDOopsCLDClosure(CMSBitMap* bitmap) : _oop_closure(bitmap) {}
- void do_cld(ClassLoaderData* cld) {
- cld->oops_do(&_oop_closure, ClassLoaderData::_claim_none, false);
- }
-};
-
-void CMSCollector::verify_after_remark_work_2() {
- ResourceMark rm;
- HandleMark hm;
- CMSHeap* heap = CMSHeap::heap();
-
- // Get a clear set of claim bits for the roots processing to work with.
- ClassLoaderDataGraph::clear_claimed_marks();
-
- // Mark from roots one level into CMS
- MarkRefsIntoVerifyClosure notOlder(_span, verification_mark_bm(),
- markBitMap());
- CLDToOopClosure cld_closure(¬Older, ClassLoaderData::_claim_strong);
-
- heap->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel.
-
- {
- StrongRootsScope srs(1);
-
- heap->cms_process_roots(&srs,
- true, // young gen as roots
- GenCollectedHeap::ScanningOption(roots_scanning_options()),
- should_unload_classes(),
- ¬Older,
- &cld_closure);
- }
-
- // Now mark from the roots
- MarkFromRootsVerifyClosure markFromRootsClosure(this, _span,
- verification_mark_bm(), markBitMap(), verification_mark_stack());
- assert(_restart_addr == NULL, "Expected pre-condition");
- verification_mark_bm()->iterate(&markFromRootsClosure);
- while (_restart_addr != NULL) {
- // Deal with stack overflow: by restarting at the indicated
- // address.
- HeapWord* ra = _restart_addr;
- markFromRootsClosure.reset(ra);
- _restart_addr = NULL;
- verification_mark_bm()->iterate(&markFromRootsClosure, ra, _span.end());
- }
- assert(verification_mark_stack()->isEmpty(), "Should have been drained");
- verify_work_stacks_empty();
-
- VerifyCLDOopsCLDClosure verify_cld_oops(verification_mark_bm());
- ClassLoaderDataGraph::cld_do(&verify_cld_oops);
-
- // Marking completed -- now verify that each bit marked in
- // verification_mark_bm() is also marked in markBitMap(); flag all
- // errors by printing corresponding objects.
- VerifyMarkedClosure vcl(markBitMap());
- verification_mark_bm()->iterate(&vcl);
- assert(!vcl.failed(), "Else verification above should not have succeeded");
-}
-
-void ConcurrentMarkSweepGeneration::save_marks() {
- // delegate to CMS space
- cmsSpace()->save_marks();
-}
-
-bool ConcurrentMarkSweepGeneration::no_allocs_since_save_marks() {
- return cmsSpace()->no_allocs_since_save_marks();
-}
-
-void
-ConcurrentMarkSweepGeneration::oop_iterate(OopIterateClosure* cl) {
- if (freelistLock()->owned_by_self()) {
- Generation::oop_iterate(cl);
- } else {
- MutexLocker x(freelistLock(), Mutex::_no_safepoint_check_flag);
- Generation::oop_iterate(cl);
- }
-}
-
-void
-ConcurrentMarkSweepGeneration::object_iterate(ObjectClosure* cl) {
- if (freelistLock()->owned_by_self()) {
- Generation::object_iterate(cl);
- } else {
- MutexLocker x(freelistLock(), Mutex::_no_safepoint_check_flag);
- Generation::object_iterate(cl);
- }
-}
-
-void
-ConcurrentMarkSweepGeneration::safe_object_iterate(ObjectClosure* cl) {
- if (freelistLock()->owned_by_self()) {
- Generation::safe_object_iterate(cl);
- } else {
- MutexLocker x(freelistLock(), Mutex::_no_safepoint_check_flag);
- Generation::safe_object_iterate(cl);
- }
-}
-
-void
-ConcurrentMarkSweepGeneration::post_compact() {
-}
-
-void
-ConcurrentMarkSweepGeneration::prepare_for_verify() {
- // Fix the linear allocation blocks to look like free blocks.
-
- // Locks are normally acquired/released in gc_prologue/gc_epilogue, but those
- // are not called when the heap is verified during universe initialization and
- // at vm shutdown.
- if (freelistLock()->owned_by_self()) {
- cmsSpace()->prepare_for_verify();
- } else {
- MutexLocker fll(freelistLock(), Mutex::_no_safepoint_check_flag);
- cmsSpace()->prepare_for_verify();
- }
-}
-
-void
-ConcurrentMarkSweepGeneration::verify() {
- // Locks are normally acquired/released in gc_prologue/gc_epilogue, but those
- // are not called when the heap is verified during universe initialization and
- // at vm shutdown.
- if (freelistLock()->owned_by_self()) {
- cmsSpace()->verify();
- } else {
- MutexLocker fll(freelistLock(), Mutex::_no_safepoint_check_flag);
- cmsSpace()->verify();
- }
-}
-
-void CMSCollector::verify() {
- _cmsGen->verify();
-}
-
-#ifndef PRODUCT
-bool CMSCollector::overflow_list_is_empty() const {
- assert(_num_par_pushes >= 0, "Inconsistency");
- if (_overflow_list == NULL) {
- assert(_num_par_pushes == 0, "Inconsistency");
- }
- return _overflow_list == NULL;
-}
-
-// The methods verify_work_stacks_empty() and verify_overflow_empty()
-// merely consolidate assertion checks that appear to occur together frequently.
-void CMSCollector::verify_work_stacks_empty() const {
- assert(_markStack.isEmpty(), "Marking stack should be empty");
- assert(overflow_list_is_empty(), "Overflow list should be empty");
-}
-
-void CMSCollector::verify_overflow_empty() const {
- assert(overflow_list_is_empty(), "Overflow list should be empty");
- assert(no_preserved_marks(), "No preserved marks");
-}
-#endif // PRODUCT
-
-// Decide if we want to enable class unloading as part of the
-// ensuing concurrent GC cycle. We will collect and
-// unload classes if it's the case that:
-// (a) class unloading is enabled at the command line, and
-// (b) old gen is getting really full
-// NOTE: Provided there is no change in the state of the heap between
-// calls to this method, it should have idempotent results. Moreover,
-// its results should be monotonically increasing (i.e. going from 0 to 1,
-// but not 1 to 0) between successive calls between which the heap was
-// not collected. For the implementation below, it must thus rely on
-// the property that concurrent_cycles_since_last_unload()
-// will not decrease unless a collection cycle happened and that
-// _cmsGen->is_too_full() are
-// themselves also monotonic in that sense. See check_monotonicity()
-// below.
-void CMSCollector::update_should_unload_classes() {
- _should_unload_classes = false;
- if (CMSClassUnloadingEnabled) {
- _should_unload_classes = (concurrent_cycles_since_last_unload() >=
- CMSClassUnloadingMaxInterval)
- || _cmsGen->is_too_full();
- }
-}
-
-bool ConcurrentMarkSweepGeneration::is_too_full() const {
- bool res = should_concurrent_collect();
- res = res && (occupancy() > (double)CMSIsTooFullPercentage/100.0);
- return res;
-}
-
-void CMSCollector::setup_cms_unloading_and_verification_state() {
- const bool should_verify = VerifyBeforeGC || VerifyAfterGC || VerifyDuringGC
- || VerifyBeforeExit;
- const int rso = GenCollectedHeap::SO_AllCodeCache;
-
- // We set the proper root for this CMS cycle here.
- if (should_unload_classes()) { // Should unload classes this cycle
- remove_root_scanning_option(rso); // Shrink the root set appropriately
- set_verifying(should_verify); // Set verification state for this cycle
- return; // Nothing else needs to be done at this time
- }
-
- // Not unloading classes this cycle
- assert(!should_unload_classes(), "Inconsistency!");
-
- // If we are not unloading classes then add SO_AllCodeCache to root
- // scanning options.
- add_root_scanning_option(rso);
-
- if ((!verifying() || unloaded_classes_last_cycle()) && should_verify) {
- set_verifying(true);
- } else if (verifying() && !should_verify) {
- // We were verifying, but some verification flags got disabled.
- set_verifying(false);
- // Exclude symbols, strings and code cache elements from root scanning to
- // reduce IM and RM pauses.
- remove_root_scanning_option(rso);
- }
-}
-
-
-#ifndef PRODUCT
-HeapWord* CMSCollector::block_start(const void* p) const {
- const HeapWord* addr = (HeapWord*)p;
- if (_span.contains(p)) {
- if (_cmsGen->cmsSpace()->is_in_reserved(addr)) {
- return _cmsGen->cmsSpace()->block_start(p);
- }
- }
- return NULL;
-}
-#endif
-
-HeapWord*
-ConcurrentMarkSweepGeneration::expand_and_allocate(size_t word_size,
- bool tlab,
- bool parallel) {
- CMSSynchronousYieldRequest yr;
- assert(!tlab, "Can't deal with TLAB allocation");
- MutexLocker x(freelistLock(), Mutex::_no_safepoint_check_flag);
- expand_for_gc_cause(word_size*HeapWordSize, MinHeapDeltaBytes, CMSExpansionCause::_satisfy_allocation);
- if (GCExpandToAllocateDelayMillis > 0) {
- os::naked_sleep(GCExpandToAllocateDelayMillis);
- }
- return have_lock_and_allocate(word_size, tlab);
-}
-
-void ConcurrentMarkSweepGeneration::expand_for_gc_cause(
- size_t bytes,
- size_t expand_bytes,
- CMSExpansionCause::Cause cause)
-{
-
- bool success = expand(bytes, expand_bytes);
-
- // remember why we expanded; this information is used
- // by shouldConcurrentCollect() when making decisions on whether to start
- // a new CMS cycle.
- if (success) {
- set_expansion_cause(cause);
- log_trace(gc)("Expanded CMS gen for %s", CMSExpansionCause::to_string(cause));
- }
-}
-
-HeapWord* ConcurrentMarkSweepGeneration::expand_and_par_lab_allocate(CMSParGCThreadState* ps, size_t word_sz) {
- HeapWord* res = NULL;
- MutexLocker x(ParGCRareEvent_lock);
- while (true) {
- // Expansion by some other thread might make alloc OK now:
- res = ps->lab.alloc(word_sz);
- if (res != NULL) return res;
- // If there's not enough expansion space available, give up.
- if (_virtual_space.uncommitted_size() < (word_sz * HeapWordSize)) {
- return NULL;
- }
- // Otherwise, we try expansion.
- expand_for_gc_cause(word_sz*HeapWordSize, MinHeapDeltaBytes, CMSExpansionCause::_allocate_par_lab);
- // Now go around the loop and try alloc again;
- // A competing par_promote might beat us to the expansion space,
- // so we may go around the loop again if promotion fails again.
- if (GCExpandToAllocateDelayMillis > 0) {
- os::naked_sleep(GCExpandToAllocateDelayMillis);
- }
- }
-}
-
-
-bool ConcurrentMarkSweepGeneration::expand_and_ensure_spooling_space(
- PromotionInfo* promo) {
- MutexLocker x(ParGCRareEvent_lock);
- size_t refill_size_bytes = promo->refillSize() * HeapWordSize;
- while (true) {
- // Expansion by some other thread might make alloc OK now:
- if (promo->ensure_spooling_space()) {
- assert(promo->has_spooling_space(),
- "Post-condition of successful ensure_spooling_space()");
- return true;
- }
- // If there's not enough expansion space available, give up.
- if (_virtual_space.uncommitted_size() < refill_size_bytes) {
- return false;
- }
- // Otherwise, we try expansion.
- expand_for_gc_cause(refill_size_bytes, MinHeapDeltaBytes, CMSExpansionCause::_allocate_par_spooling_space);
- // Now go around the loop and try alloc again;
- // A competing allocation might beat us to the expansion space,
- // so we may go around the loop again if allocation fails again.
- if (GCExpandToAllocateDelayMillis > 0) {
- os::naked_sleep(GCExpandToAllocateDelayMillis);
- }
- }
-}
-
-void ConcurrentMarkSweepGeneration::shrink(size_t bytes) {
- // Only shrink if a compaction was done so that all the free space
- // in the generation is in a contiguous block at the end.
- if (did_compact()) {
- CardGeneration::shrink(bytes);
- }
-}
-
-void ConcurrentMarkSweepGeneration::assert_correct_size_change_locking() {
- assert_locked_or_safepoint(Heap_lock);
-}
-
-void ConcurrentMarkSweepGeneration::shrink_free_list_by(size_t bytes) {
- assert_locked_or_safepoint(Heap_lock);
- assert_lock_strong(freelistLock());
- log_trace(gc)("Shrinking of CMS not yet implemented");
- return;
-}
-
-
-// Simple ctor/dtor wrapper for accounting & timer chores around concurrent
-// phases.
-class CMSPhaseAccounting: public StackObj {
- public:
- CMSPhaseAccounting(CMSCollector *collector,
- const char *title);
- ~CMSPhaseAccounting();
-
- private:
- CMSCollector *_collector;
- const char *_title;
- GCTraceConcTime(Info, gc) _trace_time;
-
- public:
- // Not MT-safe; so do not pass around these StackObj's
- // where they may be accessed by other threads.
- double wallclock_millis() {
- return TimeHelper::counter_to_millis(os::elapsed_counter() - _trace_time.start_time());
- }
-};
-
-CMSPhaseAccounting::CMSPhaseAccounting(CMSCollector *collector,
- const char *title) :
- _collector(collector), _title(title), _trace_time(title) {
-
- _collector->resetYields();
- _collector->resetTimer();
- _collector->startTimer();
- _collector->gc_timer_cm()->register_gc_concurrent_start(title);
-}
-
-CMSPhaseAccounting::~CMSPhaseAccounting() {
- _collector->gc_timer_cm()->register_gc_concurrent_end();
- _collector->stopTimer();
- log_debug(gc)("Concurrent active time: %.3fms", TimeHelper::counter_to_millis(_collector->timerTicks()));
- log_trace(gc)(" (CMS %s yielded %d times)", _title, _collector->yields());
-}
-
-// CMS work
-
-// The common parts of CMSParInitialMarkTask and CMSParRemarkTask.
-class CMSParMarkTask : public AbstractGangTask {
- protected:
- CMSCollector* _collector;
- uint _n_workers;
- CMSParMarkTask(const char* name, CMSCollector* collector, uint n_workers) :
- AbstractGangTask(name),
- _collector(collector),
- _n_workers(n_workers) {}
- // Work method in support of parallel rescan ... of young gen spaces
- void do_young_space_rescan(OopsInGenClosure* cl,
- ContiguousSpace* space,
- HeapWord** chunk_array, size_t chunk_top);
- void work_on_young_gen_roots(OopsInGenClosure* cl);
-};
-
-// Parallel initial mark task
-class CMSParInitialMarkTask: public CMSParMarkTask {
- StrongRootsScope* _strong_roots_scope;
- public:
- CMSParInitialMarkTask(CMSCollector* collector, StrongRootsScope* strong_roots_scope, uint n_workers) :
- CMSParMarkTask("Scan roots and young gen for initial mark in parallel", collector, n_workers),
- _strong_roots_scope(strong_roots_scope) {}
- void work(uint worker_id);
-};
-
-// Checkpoint the roots into this generation from outside
-// this generation. [Note this initial checkpoint need only
-// be approximate -- we'll do a catch up phase subsequently.]
-void CMSCollector::checkpointRootsInitial() {
- assert(_collectorState == InitialMarking, "Wrong collector state");
- check_correct_thread_executing();
- TraceCMSMemoryManagerStats tms(_collectorState, CMSHeap::heap()->gc_cause());
-
- save_heap_summary();
- report_heap_summary(GCWhen::BeforeGC);
-
- ReferenceProcessor* rp = ref_processor();
- assert(_restart_addr == NULL, "Control point invariant");
- {
- // acquire locks for subsequent manipulations
- MutexLocker x(bitMapLock(),
- Mutex::_no_safepoint_check_flag);
- checkpointRootsInitialWork();
- // enable ("weak") refs discovery
- rp->enable_discovery();
- _collectorState = Marking;
- }
-
- _cmsGen->cmsSpace()->recalculate_used_stable();
-}
-
-void CMSCollector::checkpointRootsInitialWork() {
- assert(SafepointSynchronize::is_at_safepoint(), "world should be stopped");
- assert(_collectorState == InitialMarking, "just checking");
-
- // Already have locks.
- assert_lock_strong(bitMapLock());
- assert(_markBitMap.isAllClear(), "was reset at end of previous cycle");
-
- // Setup the verification and class unloading state for this
- // CMS collection cycle.
- setup_cms_unloading_and_verification_state();
-
- GCTraceTime(Trace, gc, phases) ts("checkpointRootsInitialWork", _gc_timer_cm);
-
- // Reset all the PLAB chunk arrays if necessary.
- if (_survivor_plab_array != NULL && !CMSPLABRecordAlways) {
- reset_survivor_plab_arrays();
- }
-
- ResourceMark rm;
- HandleMark hm;
-
- MarkRefsIntoClosure notOlder(_span, &_markBitMap);
- CMSHeap* heap = CMSHeap::heap();
-
- verify_work_stacks_empty();
- verify_overflow_empty();
-
- heap->ensure_parsability(false); // fill TLABs, but no need to retire them
- // Update the saved marks which may affect the root scans.
- heap->save_marks();
-
- // weak reference processing has not started yet.
- ref_processor()->set_enqueuing_is_done(false);
-
- // Need to remember all newly created CLDs,
- // so that we can guarantee that the remark finds them.
- ClassLoaderDataGraph::remember_new_clds(true);
-
- // Whenever a CLD is found, it will be claimed before proceeding to mark
- // the klasses. The claimed marks need to be cleared before marking starts.
- ClassLoaderDataGraph::clear_claimed_marks();
-
- print_eden_and_survivor_chunk_arrays();
-
- {
-#if COMPILER2_OR_JVMCI
- DerivedPointerTableDeactivate dpt_deact;
-#endif
- if (CMSParallelInitialMarkEnabled) {
- // The parallel version.
- WorkGang* workers = heap->workers();
- assert(workers != NULL, "Need parallel worker threads.");
- uint n_workers = workers->active_workers();
-
- StrongRootsScope srs(n_workers);
-
- CMSParInitialMarkTask tsk(this, &srs, n_workers);
- initialize_sequential_subtasks_for_young_gen_rescan(n_workers);
- // If the total workers is greater than 1, then multiple workers
- // may be used at some time and the initialization has been set
- // such that the single threaded path cannot be used.
- if (workers->total_workers() > 1) {
- workers->run_task(&tsk);
- } else {
- tsk.work(0);
- }
- } else {
- // The serial version.
- CLDToOopClosure cld_closure(¬Older, ClassLoaderData::_claim_strong);
- heap->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel.
-
- StrongRootsScope srs(1);
-
- heap->cms_process_roots(&srs,
- true, // young gen as roots
- GenCollectedHeap::ScanningOption(roots_scanning_options()),
- should_unload_classes(),
- ¬Older,
- &cld_closure);
- }
- }
-
- // Clear mod-union table; it will be dirtied in the prologue of
- // CMS generation per each young generation collection.
-
- assert(_modUnionTable.isAllClear(),
- "Was cleared in most recent final checkpoint phase"
- " or no bits are set in the gc_prologue before the start of the next "
- "subsequent marking phase.");
-
- assert(_ct->cld_rem_set()->mod_union_is_clear(), "Must be");
-
- // Save the end of the used_region of the constituent generations
- // to be used to limit the extent of sweep in each generation.
- save_sweep_limits();
- verify_overflow_empty();
-}
-
-bool CMSCollector::markFromRoots() {
- // we might be tempted to assert that:
- // assert(!SafepointSynchronize::is_at_safepoint(),
- // "inconsistent argument?");
- // However that wouldn't be right, because it's possible that
- // a safepoint is indeed in progress as a young generation
- // stop-the-world GC happens even as we mark in this generation.
- assert(_collectorState == Marking, "inconsistent state?");
- check_correct_thread_executing();
- verify_overflow_empty();
-
- // Weak ref discovery note: We may be discovering weak
- // refs in this generation concurrent (but interleaved) with
- // weak ref discovery by the young generation collector.
-
- CMSTokenSyncWithLocks ts(true, bitMapLock());
- GCTraceCPUTime tcpu;
- CMSPhaseAccounting pa(this, "Concurrent Mark");
- bool res = markFromRootsWork();
- if (res) {
- _collectorState = Precleaning;
- } else { // We failed and a foreground collection wants to take over
- assert(_foregroundGCIsActive, "internal state inconsistency");
- assert(_restart_addr == NULL, "foreground will restart from scratch");
- log_debug(gc)("bailing out to foreground collection");
- }
- verify_overflow_empty();
- return res;
-}
-
-bool CMSCollector::markFromRootsWork() {
- // iterate over marked bits in bit map, doing a full scan and mark
- // from these roots using the following algorithm:
- // . if oop is to the right of the current scan pointer,
- // mark corresponding bit (we'll process it later)
- // . else (oop is to left of current scan pointer)
- // push oop on marking stack
- // . drain the marking stack
-
- // Note that when we do a marking step we need to hold the
- // bit map lock -- recall that direct allocation (by mutators)
- // and promotion (by the young generation collector) is also
- // marking the bit map. [the so-called allocate live policy.]
- // Because the implementation of bit map marking is not
- // robust wrt simultaneous marking of bits in the same word,
- // we need to make sure that there is no such interference
- // between concurrent such updates.
-
- // already have locks
- assert_lock_strong(bitMapLock());
-
- verify_work_stacks_empty();
- verify_overflow_empty();
- bool result = false;
- if (CMSConcurrentMTEnabled && ConcGCThreads > 0) {
- result = do_marking_mt();
- } else {
- result = do_marking_st();
- }
- return result;
-}
-
-// Forward decl
-class CMSConcMarkingTask;
-
-class CMSConcMarkingParallelTerminator: public ParallelTaskTerminator {
- CMSCollector* _collector;
- CMSConcMarkingTask* _task;
- public:
- virtual void yield();
-
- // "n_threads" is the number of threads to be terminated.
- // "queue_set" is a set of work queues of other threads.
- // "collector" is the CMS collector associated with this task terminator.
- // "yield" indicates whether we need the gang as a whole to yield.
- CMSConcMarkingParallelTerminator(int n_threads, TaskQueueSetSuper* queue_set, CMSCollector* collector) :
- ParallelTaskTerminator(n_threads, queue_set),
- _collector(collector) { }
-
- void set_task(CMSConcMarkingTask* task) {
- _task = task;
- }
-};
-
-class CMSConcMarkingOWSTTerminator: public OWSTTaskTerminator {
- CMSCollector* _collector;
- CMSConcMarkingTask* _task;
- public:
- virtual void yield();
-
- // "n_threads" is the number of threads to be terminated.
- // "queue_set" is a set of work queues of other threads.
- // "collector" is the CMS collector associated with this task terminator.
- // "yield" indicates whether we need the gang as a whole to yield.
- CMSConcMarkingOWSTTerminator(int n_threads, TaskQueueSetSuper* queue_set, CMSCollector* collector) :
- OWSTTaskTerminator(n_threads, queue_set),
- _collector(collector) { }
-
- void set_task(CMSConcMarkingTask* task) {
- _task = task;
- }
-};
-
-class CMSConcMarkingTaskTerminator {
- private:
- ParallelTaskTerminator* _term;
- public:
- CMSConcMarkingTaskTerminator(int n_threads, TaskQueueSetSuper* queue_set, CMSCollector* collector) {
- if (UseOWSTTaskTerminator) {
- _term = new CMSConcMarkingOWSTTerminator(n_threads, queue_set, collector);
- } else {
- _term = new CMSConcMarkingParallelTerminator(n_threads, queue_set, collector);
- }
- }
- ~CMSConcMarkingTaskTerminator() {
- assert(_term != NULL, "Must not be NULL");
- delete _term;
- }
-
- void set_task(CMSConcMarkingTask* task);
- ParallelTaskTerminator* terminator() const { return _term; }
-};
-
-class CMSConcMarkingTerminatorTerminator: public TerminatorTerminator {
- CMSConcMarkingTask* _task;
- public:
- bool should_exit_termination();
- void set_task(CMSConcMarkingTask* task) {
- _task = task;
- }
-};
-
-// MT Concurrent Marking Task
-class CMSConcMarkingTask: public YieldingFlexibleGangTask {
- CMSCollector* _collector;
- uint _n_workers; // requested/desired # workers
- bool _result;
- CompactibleFreeListSpace* _cms_space;
- char _pad_front[64]; // padding to ...
- HeapWord* volatile _global_finger; // ... avoid sharing cache line
- char _pad_back[64];
- HeapWord* _restart_addr;
-
- // Exposed here for yielding support
- Mutex* const _bit_map_lock;
-
- // The per thread work queues, available here for stealing
- OopTaskQueueSet* _task_queues;
-
- // Termination (and yielding) support
- CMSConcMarkingTaskTerminator _term;
- CMSConcMarkingTerminatorTerminator _term_term;
-
- public:
- CMSConcMarkingTask(CMSCollector* collector,
- CompactibleFreeListSpace* cms_space,
- YieldingFlexibleWorkGang* workers,
- OopTaskQueueSet* task_queues):
- YieldingFlexibleGangTask("Concurrent marking done multi-threaded"),
- _collector(collector),
- _n_workers(0),
- _result(true),
- _cms_space(cms_space),
- _bit_map_lock(collector->bitMapLock()),
- _task_queues(task_queues),
- _term(_n_workers, task_queues, _collector)
- {
- _requested_size = _n_workers;
- _term.set_task(this);
- _term_term.set_task(this);
- _restart_addr = _global_finger = _cms_space->bottom();
- }
-
-
- OopTaskQueueSet* task_queues() { return _task_queues; }
-
- OopTaskQueue* work_queue(int i) { return task_queues()->queue(i); }
-
- HeapWord* volatile* global_finger_addr() { return &_global_finger; }
-
- ParallelTaskTerminator* terminator() { return _term.terminator(); }
-
- virtual void set_for_termination(uint active_workers) {
- terminator()->reset_for_reuse(active_workers);
- }
-
- void work(uint worker_id);
- bool should_yield() {
- return ConcurrentMarkSweepThread::should_yield()
- && !_collector->foregroundGCIsActive();
- }
-
- virtual void coordinator_yield(); // stuff done by coordinator
- bool result() { return _result; }
-
- void reset(HeapWord* ra) {
- assert(_global_finger >= _cms_space->end(), "Postcondition of ::work(i)");
- _restart_addr = _global_finger = ra;
- _term.terminator()->reset_for_reuse();
- }
-
- static bool get_work_from_overflow_stack(CMSMarkStack* ovflw_stk,
- OopTaskQueue* work_q);
-
- private:
- void do_scan_and_mark(int i, CompactibleFreeListSpace* sp);
- void do_work_steal(int i);
- void bump_global_finger(HeapWord* f);
-};
-
-bool CMSConcMarkingTerminatorTerminator::should_exit_termination() {
- assert(_task != NULL, "Error");
- return _task->yielding();
- // Note that we do not need the disjunct || _task->should_yield() above
- // because we want terminating threads to yield only if the task
- // is already in the midst of yielding, which happens only after at least one
- // thread has yielded.
-}
-
-void CMSConcMarkingParallelTerminator::yield() {
- if (_task->should_yield()) {
- _task->yield();
- } else {
- ParallelTaskTerminator::yield();
- }
-}
-
-void CMSConcMarkingOWSTTerminator::yield() {
- if (_task->should_yield()) {
- _task->yield();
- } else {
- OWSTTaskTerminator::yield();
- }
-}
-
-void CMSConcMarkingTaskTerminator::set_task(CMSConcMarkingTask* task) {
- if (UseOWSTTaskTerminator) {
- ((CMSConcMarkingOWSTTerminator*)_term)->set_task(task);
- } else {
- ((CMSConcMarkingParallelTerminator*)_term)->set_task(task);
- }
-}
-
-////////////////////////////////////////////////////////////////
-// Concurrent Marking Algorithm Sketch
-////////////////////////////////////////////////////////////////
-// Until all tasks exhausted (both spaces):
-// -- claim next available chunk
-// -- bump global finger via CAS
-// -- find first object that starts in this chunk
-// and start scanning bitmap from that position
-// -- scan marked objects for oops
-// -- CAS-mark target, and if successful:
-// . if target oop is above global finger (volatile read)
-// nothing to do
-// . if target oop is in chunk and above local finger
-// then nothing to do
-// . else push on work-queue
-// -- Deal with possible overflow issues:
-// . local work-queue overflow causes stuff to be pushed on
-// global (common) overflow queue
-// . always first empty local work queue
-// . then get a batch of oops from global work queue if any
-// . then do work stealing
-// -- When all tasks claimed (both spaces)
-// and local work queue empty,
-// then in a loop do:
-// . check global overflow stack; steal a batch of oops and trace
-// . try to steal from other threads oif GOS is empty
-// . if neither is available, offer termination
-// -- Terminate and return result
-//
-void CMSConcMarkingTask::work(uint worker_id) {
- elapsedTimer _timer;
- ResourceMark rm;
- HandleMark hm;
-
- DEBUG_ONLY(_collector->verify_overflow_empty();)
-
- // Before we begin work, our work queue should be empty
- assert(work_queue(worker_id)->size() == 0, "Expected to be empty");
- // Scan the bitmap covering _cms_space, tracing through grey objects.
- _timer.start();
- do_scan_and_mark(worker_id, _cms_space);
- _timer.stop();
- log_trace(gc, task)("Finished cms space scanning in %dth thread: %3.3f sec", worker_id, _timer.seconds());
-
- // ... do work stealing
- _timer.reset();
- _timer.start();
- do_work_steal(worker_id);
- _timer.stop();
- log_trace(gc, task)("Finished work stealing in %dth thread: %3.3f sec", worker_id, _timer.seconds());
- assert(_collector->_markStack.isEmpty(), "Should have been emptied");
- assert(work_queue(worker_id)->size() == 0, "Should have been emptied");
- // Note that under the current task protocol, the
- // following assertion is true even of the spaces
- // expanded since the completion of the concurrent
- // marking. XXX This will likely change under a strict
- // ABORT semantics.
- // After perm removal the comparison was changed to
- // greater than or equal to from strictly greater than.
- // Before perm removal the highest address sweep would
- // have been at the end of perm gen but now is at the
- // end of the tenured gen.
- assert(_global_finger >= _cms_space->end(),
- "All tasks have been completed");
- DEBUG_ONLY(_collector->verify_overflow_empty();)
-}
-
-void CMSConcMarkingTask::bump_global_finger(HeapWord* f) {
- HeapWord* read = _global_finger;
- HeapWord* cur = read;
- while (f > read) {
- cur = read;
- read = Atomic::cmpxchg(f, &_global_finger, cur);
- if (cur == read) {
- // our cas succeeded
- assert(_global_finger >= f, "protocol consistency");
- break;
- }
- }
-}
-
-// This is really inefficient, and should be redone by
-// using (not yet available) block-read and -write interfaces to the
-// stack and the work_queue. XXX FIX ME !!!
-bool CMSConcMarkingTask::get_work_from_overflow_stack(CMSMarkStack* ovflw_stk,
- OopTaskQueue* work_q) {
- // Fast lock-free check
- if (ovflw_stk->length() == 0) {
- return false;
- }
- assert(work_q->size() == 0, "Shouldn't steal");
- MutexLocker ml(ovflw_stk->par_lock(),
- Mutex::_no_safepoint_check_flag);
- // Grab up to 1/4 the size of the work queue
- size_t num = MIN2((size_t)(work_q->max_elems() - work_q->size())/4,
- (size_t)ParGCDesiredObjsFromOverflowList);
- num = MIN2(num, ovflw_stk->length());
- for (int i = (int) num; i > 0; i--) {
- oop cur = ovflw_stk->pop();
- assert(cur != NULL, "Counted wrong?");
- work_q->push(cur);
- }
- return num > 0;
-}
-
-void CMSConcMarkingTask::do_scan_and_mark(int i, CompactibleFreeListSpace* sp) {
- SequentialSubTasksDone* pst = sp->conc_par_seq_tasks();
- int n_tasks = pst->n_tasks();
- // We allow that there may be no tasks to do here because
- // we are restarting after a stack overflow.
- assert(pst->valid() || n_tasks == 0, "Uninitialized use?");
- uint nth_task = 0;
-
- HeapWord* aligned_start = sp->bottom();
- if (sp->used_region().contains(_restart_addr)) {
- // Align down to a card boundary for the start of 0th task
- // for this space.
- aligned_start = align_down(_restart_addr, CardTable::card_size);
- }
-
- size_t chunk_size = sp->marking_task_size();
- while (pst->try_claim_task(/* reference */ nth_task)) {
- // Having claimed the nth task in this space,
- // compute the chunk that it corresponds to:
- MemRegion span = MemRegion(aligned_start + nth_task*chunk_size,
- aligned_start + (nth_task+1)*chunk_size);
- // Try and bump the global finger via a CAS;
- // note that we need to do the global finger bump
- // _before_ taking the intersection below, because
- // the task corresponding to that region will be
- // deemed done even if the used_region() expands
- // because of allocation -- as it almost certainly will
- // during start-up while the threads yield in the
- // closure below.
- HeapWord* finger = span.end();
- bump_global_finger(finger); // atomically
- // There are null tasks here corresponding to chunks
- // beyond the "top" address of the space.
- span = span.intersection(sp->used_region());
- if (!span.is_empty()) { // Non-null task
- HeapWord* prev_obj;
- assert(!span.contains(_restart_addr) || nth_task == 0,
- "Inconsistency");
- if (nth_task == 0) {
- // For the 0th task, we'll not need to compute a block_start.
- if (span.contains(_restart_addr)) {
- // In the case of a restart because of stack overflow,
- // we might additionally skip a chunk prefix.
- prev_obj = _restart_addr;
- } else {
- prev_obj = span.start();
- }
- } else {
- // We want to skip the first object because
- // the protocol is to scan any object in its entirety
- // that _starts_ in this span; a fortiori, any
- // object starting in an earlier span is scanned
- // as part of an earlier claimed task.
- // Below we use the "careful" version of block_start
- // so we do not try to navigate uninitialized objects.
- prev_obj = sp->block_start_careful(span.start());
- // Below we use a variant of block_size that uses the
- // Printezis bits to avoid waiting for allocated
- // objects to become initialized/parsable.
- while (prev_obj < span.start()) {
- size_t sz = sp->block_size_no_stall(prev_obj, _collector);
- if (sz > 0) {
- prev_obj += sz;
- } else {
- // In this case we may end up doing a bit of redundant
- // scanning, but that appears unavoidable, short of
- // locking the free list locks; see bug 6324141.
- break;
- }
- }
- }
- if (prev_obj < span.end()) {
- MemRegion my_span = MemRegion(prev_obj, span.end());
- // Do the marking work within a non-empty span --
- // the last argument to the constructor indicates whether the
- // iteration should be incremental with periodic yields.
- ParMarkFromRootsClosure cl(this, _collector, my_span,
- &_collector->_markBitMap,
- work_queue(i),
- &_collector->_markStack);
- _collector->_markBitMap.iterate(&cl, my_span.start(), my_span.end());
- } // else nothing to do for this task
- } // else nothing to do for this task
- }
- // We'd be tempted to assert here that since there are no
- // more tasks left to claim in this space, the global_finger
- // must exceed space->top() and a fortiori space->end(). However,
- // that would not quite be correct because the bumping of
- // global_finger occurs strictly after the claiming of a task,
- // so by the time we reach here the global finger may not yet
- // have been bumped up by the thread that claimed the last
- // task.
- pst->all_tasks_completed();
-}
-
-class ParConcMarkingClosure: public MetadataVisitingOopIterateClosure {
- private:
- CMSCollector* _collector;
- CMSConcMarkingTask* _task;
- MemRegion _span;
- CMSBitMap* _bit_map;
- CMSMarkStack* _overflow_stack;
- OopTaskQueue* _work_queue;
- protected:
- DO_OOP_WORK_DEFN
- public:
- ParConcMarkingClosure(CMSCollector* collector, CMSConcMarkingTask* task, OopTaskQueue* work_queue,
- CMSBitMap* bit_map, CMSMarkStack* overflow_stack):
- MetadataVisitingOopIterateClosure(collector->ref_processor()),
- _collector(collector),
- _task(task),
- _span(collector->_span),
- _bit_map(bit_map),
- _overflow_stack(overflow_stack),
- _work_queue(work_queue)
- { }
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-
- void trim_queue(size_t max);
- void handle_stack_overflow(HeapWord* lost);
- void do_yield_check() {
- if (_task->should_yield()) {
- _task->yield();
- }
- }
-};
-
-DO_OOP_WORK_IMPL(ParConcMarkingClosure)
-
-// Grey object scanning during work stealing phase --
-// the salient assumption here is that any references
-// that are in these stolen objects being scanned must
-// already have been initialized (else they would not have
-// been published), so we do not need to check for
-// uninitialized objects before pushing here.
-void ParConcMarkingClosure::do_oop(oop obj) {
- assert(oopDesc::is_oop_or_null(obj, true), "Expected an oop or NULL at " PTR_FORMAT, p2i(obj));
- HeapWord* addr = (HeapWord*)obj;
- // Check if oop points into the CMS generation
- // and is not marked
- if (_span.contains(addr) && !_bit_map->isMarked(addr)) {
- // a white object ...
- // If we manage to "claim" the object, by being the
- // first thread to mark it, then we push it on our
- // marking stack
- if (_bit_map->par_mark(addr)) { // ... now grey
- // push on work queue (grey set)
- bool simulate_overflow = false;
- NOT_PRODUCT(
- if (CMSMarkStackOverflowALot &&
- _collector->simulate_overflow()) {
- // simulate a stack overflow
- simulate_overflow = true;
- }
- )
- if (simulate_overflow ||
- !(_work_queue->push(obj) || _overflow_stack->par_push(obj))) {
- // stack overflow
- log_trace(gc)("CMS marking stack overflow (benign) at " SIZE_FORMAT, _overflow_stack->capacity());
- // We cannot assert that the overflow stack is full because
- // it may have been emptied since.
- assert(simulate_overflow ||
- _work_queue->size() == _work_queue->max_elems(),
- "Else push should have succeeded");
- handle_stack_overflow(addr);
- }
- } // Else, some other thread got there first
- do_yield_check();
- }
-}
-
-void ParConcMarkingClosure::trim_queue(size_t max) {
- while (_work_queue->size() > max) {
- oop new_oop;
- if (_work_queue->pop_local(new_oop)) {
- assert(oopDesc::is_oop(new_oop), "Should be an oop");
- assert(_bit_map->isMarked((HeapWord*)new_oop), "Grey object");
- assert(_span.contains((HeapWord*)new_oop), "Not in span");
- new_oop->oop_iterate(this); // do_oop() above
- do_yield_check();
- }
- }
-}
-
-// Upon stack overflow, we discard (part of) the stack,
-// remembering the least address amongst those discarded
-// in CMSCollector's _restart_address.
-void ParConcMarkingClosure::handle_stack_overflow(HeapWord* lost) {
- // We need to do this under a mutex to prevent other
- // workers from interfering with the work done below.
- MutexLocker ml(_overflow_stack->par_lock(),
- Mutex::_no_safepoint_check_flag);
- // Remember the least grey address discarded
- HeapWord* ra = (HeapWord*)_overflow_stack->least_value(lost);
- _collector->lower_restart_addr(ra);
- _overflow_stack->reset(); // discard stack contents
- _overflow_stack->expand(); // expand the stack if possible
-}
-
-
-void CMSConcMarkingTask::do_work_steal(int i) {
- OopTaskQueue* work_q = work_queue(i);
- oop obj_to_scan;
- CMSBitMap* bm = &(_collector->_markBitMap);
- CMSMarkStack* ovflw = &(_collector->_markStack);
- ParConcMarkingClosure cl(_collector, this, work_q, bm, ovflw);
- while (true) {
- cl.trim_queue(0);
- assert(work_q->size() == 0, "Should have been emptied above");
- if (get_work_from_overflow_stack(ovflw, work_q)) {
- // Can't assert below because the work obtained from the
- // overflow stack may already have been stolen from us.
- // assert(work_q->size() > 0, "Work from overflow stack");
- continue;
- } else if (task_queues()->steal(i, /* reference */ obj_to_scan)) {
- assert(oopDesc::is_oop(obj_to_scan), "Should be an oop");
- assert(bm->isMarked((HeapWord*)obj_to_scan), "Grey object");
- obj_to_scan->oop_iterate(&cl);
- } else if (terminator()->offer_termination(&_term_term)) {
- assert(work_q->size() == 0, "Impossible!");
- break;
- } else if (yielding() || should_yield()) {
- yield();
- }
- }
-}
-
-// This is run by the CMS (coordinator) thread.
-void CMSConcMarkingTask::coordinator_yield() {
- assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "CMS thread should hold CMS token");
- // First give up the locks, then yield, then re-lock
- // We should probably use a constructor/destructor idiom to
- // do this unlock/lock or modify the MutexUnlocker class to
- // serve our purpose. XXX
- assert_lock_strong(_bit_map_lock);
- _bit_map_lock->unlock();
- ConcurrentMarkSweepThread::desynchronize(true);
- _collector->stopTimer();
- _collector->incrementYields();
-
- // It is possible for whichever thread initiated the yield request
- // not to get a chance to wake up and take the bitmap lock between
- // this thread releasing it and reacquiring it. So, while the
- // should_yield() flag is on, let's sleep for a bit to give the
- // other thread a chance to wake up. The limit imposed on the number
- // of iterations is defensive, to avoid any unforseen circumstances
- // putting us into an infinite loop. Since it's always been this
- // (coordinator_yield()) method that was observed to cause the
- // problem, we are using a parameter (CMSCoordinatorYieldSleepCount)
- // which is by default non-zero. For the other seven methods that
- // also perform the yield operation, as are using a different
- // parameter (CMSYieldSleepCount) which is by default zero. This way we
- // can enable the sleeping for those methods too, if necessary.
- // See 6442774.
- //
- // We really need to reconsider the synchronization between the GC
- // thread and the yield-requesting threads in the future and we
- // should really use wait/notify, which is the recommended
- // way of doing this type of interaction. Additionally, we should
- // consolidate the eight methods that do the yield operation and they
- // are almost identical into one for better maintainability and
- // readability. See 6445193.
- //
- // Tony 2006.06.29
- for (unsigned i = 0; i < CMSCoordinatorYieldSleepCount &&
- ConcurrentMarkSweepThread::should_yield() &&
- !CMSCollector::foregroundGCIsActive(); ++i) {
- os::naked_short_sleep(1);
- }
-
- ConcurrentMarkSweepThread::synchronize(true);
- _bit_map_lock->lock_without_safepoint_check();
- _collector->startTimer();
-}
-
-bool CMSCollector::do_marking_mt() {
- assert(ConcGCThreads > 0 && conc_workers() != NULL, "precondition");
- uint num_workers = WorkerPolicy::calc_active_conc_workers(conc_workers()->total_workers(),
- conc_workers()->active_workers(),
- Threads::number_of_non_daemon_threads());
- num_workers = conc_workers()->update_active_workers(num_workers);
- log_info(gc,task)("Using %u workers of %u for marking", num_workers, conc_workers()->total_workers());
-
- CompactibleFreeListSpace* cms_space = _cmsGen->cmsSpace();
-
- CMSConcMarkingTask tsk(this,
- cms_space,
- conc_workers(),
- task_queues());
-
- // Since the actual number of workers we get may be different
- // from the number we requested above, do we need to do anything different
- // below? In particular, may be we need to subclass the SequantialSubTasksDone
- // class?? XXX
- cms_space ->initialize_sequential_subtasks_for_marking(num_workers);
-
- // Refs discovery is already non-atomic.
- assert(!ref_processor()->discovery_is_atomic(), "Should be non-atomic");
- assert(ref_processor()->discovery_is_mt(), "Discovery should be MT");
- conc_workers()->start_task(&tsk);
- while (tsk.yielded()) {
- tsk.coordinator_yield();
- conc_workers()->continue_task(&tsk);
- }
- // If the task was aborted, _restart_addr will be non-NULL
- assert(tsk.completed() || _restart_addr != NULL, "Inconsistency");
- while (_restart_addr != NULL) {
- // XXX For now we do not make use of ABORTED state and have not
- // yet implemented the right abort semantics (even in the original
- // single-threaded CMS case). That needs some more investigation
- // and is deferred for now; see CR# TBF. 07252005YSR. XXX
- assert(!CMSAbortSemantics || tsk.aborted(), "Inconsistency");
- // If _restart_addr is non-NULL, a marking stack overflow
- // occurred; we need to do a fresh marking iteration from the
- // indicated restart address.
- if (_foregroundGCIsActive) {
- // We may be running into repeated stack overflows, having
- // reached the limit of the stack size, while making very
- // slow forward progress. It may be best to bail out and
- // let the foreground collector do its job.
- // Clear _restart_addr, so that foreground GC
- // works from scratch. This avoids the headache of
- // a "rescan" which would otherwise be needed because
- // of the dirty mod union table & card table.
- _restart_addr = NULL;
- return false;
- }
- // Adjust the task to restart from _restart_addr
- tsk.reset(_restart_addr);
- cms_space ->initialize_sequential_subtasks_for_marking(num_workers,
- _restart_addr);
- _restart_addr = NULL;
- // Get the workers going again
- conc_workers()->start_task(&tsk);
- while (tsk.yielded()) {
- tsk.coordinator_yield();
- conc_workers()->continue_task(&tsk);
- }
- }
- assert(tsk.completed(), "Inconsistency");
- assert(tsk.result() == true, "Inconsistency");
- return true;
-}
-
-bool CMSCollector::do_marking_st() {
- ResourceMark rm;
- HandleMark hm;
-
- // Temporarily make refs discovery single threaded (non-MT)
- ReferenceProcessorMTDiscoveryMutator rp_mut_discovery(ref_processor(), false);
- MarkFromRootsClosure markFromRootsClosure(this, _span, &_markBitMap,
- &_markStack, CMSYield);
- // the last argument to iterate indicates whether the iteration
- // should be incremental with periodic yields.
- _markBitMap.iterate(&markFromRootsClosure);
- // If _restart_addr is non-NULL, a marking stack overflow
- // occurred; we need to do a fresh iteration from the
- // indicated restart address.
- while (_restart_addr != NULL) {
- if (_foregroundGCIsActive) {
- // We may be running into repeated stack overflows, having
- // reached the limit of the stack size, while making very
- // slow forward progress. It may be best to bail out and
- // let the foreground collector do its job.
- // Clear _restart_addr, so that foreground GC
- // works from scratch. This avoids the headache of
- // a "rescan" which would otherwise be needed because
- // of the dirty mod union table & card table.
- _restart_addr = NULL;
- return false; // indicating failure to complete marking
- }
- // Deal with stack overflow:
- // we restart marking from _restart_addr
- HeapWord* ra = _restart_addr;
- markFromRootsClosure.reset(ra);
- _restart_addr = NULL;
- _markBitMap.iterate(&markFromRootsClosure, ra, _span.end());
- }
- return true;
-}
-
-void CMSCollector::preclean() {
- check_correct_thread_executing();
- assert(Thread::current()->is_ConcurrentGC_thread(), "Wrong thread");
- verify_work_stacks_empty();
- verify_overflow_empty();
- _abort_preclean = false;
- if (CMSPrecleaningEnabled) {
- if (!CMSEdenChunksRecordAlways) {
- _eden_chunk_index = 0;
- }
- size_t used = get_eden_used();
- size_t capacity = get_eden_capacity();
- // Don't start sampling unless we will get sufficiently
- // many samples.
- if (used < (((capacity / CMSScheduleRemarkSamplingRatio) / 100)
- * CMSScheduleRemarkEdenPenetration)) {
- _start_sampling = true;
- } else {
- _start_sampling = false;
- }
- GCTraceCPUTime tcpu;
- CMSPhaseAccounting pa(this, "Concurrent Preclean");
- preclean_work(CMSPrecleanRefLists1, CMSPrecleanSurvivors1);
- }
- CMSTokenSync x(true); // is cms thread
- if (CMSPrecleaningEnabled) {
- sample_eden();
- _collectorState = AbortablePreclean;
- } else {
- _collectorState = FinalMarking;
- }
- verify_work_stacks_empty();
- verify_overflow_empty();
-}
-
-// Try and schedule the remark such that young gen
-// occupancy is CMSScheduleRemarkEdenPenetration %.
-void CMSCollector::abortable_preclean() {
- check_correct_thread_executing();
- assert(CMSPrecleaningEnabled, "Inconsistent control state");
- assert(_collectorState == AbortablePreclean, "Inconsistent control state");
-
- // If Eden's current occupancy is below this threshold,
- // immediately schedule the remark; else preclean
- // past the next scavenge in an effort to
- // schedule the pause as described above. By choosing
- // CMSScheduleRemarkEdenSizeThreshold >= max eden size
- // we will never do an actual abortable preclean cycle.
- if (get_eden_used() > CMSScheduleRemarkEdenSizeThreshold) {
- GCTraceCPUTime tcpu;
- CMSPhaseAccounting pa(this, "Concurrent Abortable Preclean");
- // We need more smarts in the abortable preclean
- // loop below to deal with cases where allocation
- // in young gen is very very slow, and our precleaning
- // is running a losing race against a horde of
- // mutators intent on flooding us with CMS updates
- // (dirty cards).
- // One, admittedly dumb, strategy is to give up
- // after a certain number of abortable precleaning loops
- // or after a certain maximum time. We want to make
- // this smarter in the next iteration.
- // XXX FIX ME!!! YSR
- size_t loops = 0, workdone = 0, cumworkdone = 0, waited = 0;
- while (!(should_abort_preclean() ||
- ConcurrentMarkSweepThread::cmst()->should_terminate())) {
- workdone = preclean_work(CMSPrecleanRefLists2, CMSPrecleanSurvivors2);
- cumworkdone += workdone;
- loops++;
- // Voluntarily terminate abortable preclean phase if we have
- // been at it for too long.
- if ((CMSMaxAbortablePrecleanLoops != 0) &&
- loops >= CMSMaxAbortablePrecleanLoops) {
- log_debug(gc)(" CMS: abort preclean due to loops ");
- break;
- }
- if (pa.wallclock_millis() > CMSMaxAbortablePrecleanTime) {
- log_debug(gc)(" CMS: abort preclean due to time ");
- break;
- }
- // If we are doing little work each iteration, we should
- // take a short break.
- if (workdone < CMSAbortablePrecleanMinWorkPerIteration) {
- // Sleep for some time, waiting for work to accumulate
- stopTimer();
- cmsThread()->wait_on_cms_lock(CMSAbortablePrecleanWaitMillis);
- startTimer();
- waited++;
- }
- }
- log_trace(gc)(" [" SIZE_FORMAT " iterations, " SIZE_FORMAT " waits, " SIZE_FORMAT " cards)] ",
- loops, waited, cumworkdone);
- }
- CMSTokenSync x(true); // is cms thread
- if (_collectorState != Idling) {
- assert(_collectorState == AbortablePreclean,
- "Spontaneous state transition?");
- _collectorState = FinalMarking;
- } // Else, a foreground collection completed this CMS cycle.
- return;
-}
-
-// Respond to an Eden sampling opportunity
-void CMSCollector::sample_eden() {
- // Make sure a young gc cannot sneak in between our
- // reading and recording of a sample.
- assert(Thread::current()->is_ConcurrentGC_thread(),
- "Only the cms thread may collect Eden samples");
- assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "Should collect samples while holding CMS token");
- if (!_start_sampling) {
- return;
- }
- // When CMSEdenChunksRecordAlways is true, the eden chunk array
- // is populated by the young generation.
- if (_eden_chunk_array != NULL && !CMSEdenChunksRecordAlways) {
- if (_eden_chunk_index < _eden_chunk_capacity) {
- _eden_chunk_array[_eden_chunk_index] = *_top_addr; // take sample
- assert(_eden_chunk_array[_eden_chunk_index] <= *_end_addr,
- "Unexpected state of Eden");
- // We'd like to check that what we just sampled is an oop-start address;
- // however, we cannot do that here since the object may not yet have been
- // initialized. So we'll instead do the check when we _use_ this sample
- // later.
- if (_eden_chunk_index == 0 ||
- (pointer_delta(_eden_chunk_array[_eden_chunk_index],
- _eden_chunk_array[_eden_chunk_index-1])
- >= CMSSamplingGrain)) {
- _eden_chunk_index++; // commit sample
- }
- }
- }
- if ((_collectorState == AbortablePreclean) && !_abort_preclean) {
- size_t used = get_eden_used();
- size_t capacity = get_eden_capacity();
- assert(used <= capacity, "Unexpected state of Eden");
- if (used > (capacity/100 * CMSScheduleRemarkEdenPenetration)) {
- _abort_preclean = true;
- }
- }
-}
-
-size_t CMSCollector::preclean_work(bool clean_refs, bool clean_survivor) {
- assert(_collectorState == Precleaning ||
- _collectorState == AbortablePreclean, "incorrect state");
- ResourceMark rm;
- HandleMark hm;
-
- // Precleaning is currently not MT but the reference processor
- // may be set for MT. Disable it temporarily here.
- ReferenceProcessor* rp = ref_processor();
- ReferenceProcessorMTDiscoveryMutator rp_mut_discovery(rp, false);
-
- // Do one pass of scrubbing the discovered reference lists
- // to remove any reference objects with strongly-reachable
- // referents.
- if (clean_refs) {
- CMSPrecleanRefsYieldClosure yield_cl(this);
- assert(_span_based_discoverer.span().equals(_span), "Spans should be equal");
- CMSKeepAliveClosure keep_alive(this, _span, &_markBitMap,
- &_markStack, true /* preclean */);
- CMSDrainMarkingStackClosure complete_trace(this,
- _span, &_markBitMap, &_markStack,
- &keep_alive, true /* preclean */);
-
- // We don't want this step to interfere with a young
- // collection because we don't want to take CPU
- // or memory bandwidth away from the young GC threads
- // (which may be as many as there are CPUs).
- // Note that we don't need to protect ourselves from
- // interference with mutators because they can't
- // manipulate the discovered reference lists nor affect
- // the computed reachability of the referents, the
- // only properties manipulated by the precleaning
- // of these reference lists.
- stopTimer();
- CMSTokenSyncWithLocks x(true /* is cms thread */,
- bitMapLock());
- startTimer();
- sample_eden();
-
- // The following will yield to allow foreground
- // collection to proceed promptly. XXX YSR:
- // The code in this method may need further
- // tweaking for better performance and some restructuring
- // for cleaner interfaces.
- GCTimer *gc_timer = NULL; // Currently not tracing concurrent phases
- rp->preclean_discovered_references(
- rp->is_alive_non_header(), &keep_alive, &complete_trace, &yield_cl,
- gc_timer);
- }
-
- if (clean_survivor) { // preclean the active survivor space(s)
- PushAndMarkClosure pam_cl(this, _span, ref_processor(),
- &_markBitMap, &_modUnionTable,
- &_markStack, true /* precleaning phase */);
- stopTimer();
- CMSTokenSyncWithLocks ts(true /* is cms thread */,
- bitMapLock());
- startTimer();
- unsigned int before_count =
- CMSHeap::heap()->total_collections();
- SurvivorSpacePrecleanClosure
- sss_cl(this, _span, &_markBitMap, &_markStack,
- &pam_cl, before_count, CMSYield);
- _young_gen->from()->object_iterate_careful(&sss_cl);
- _young_gen->to()->object_iterate_careful(&sss_cl);
- }
- MarkRefsIntoAndScanClosure
- mrias_cl(_span, ref_processor(), &_markBitMap, &_modUnionTable,
- &_markStack, this, CMSYield,
- true /* precleaning phase */);
- // CAUTION: The following closure has persistent state that may need to
- // be reset upon a decrease in the sequence of addresses it
- // processes.
- ScanMarkedObjectsAgainCarefullyClosure
- smoac_cl(this, _span,
- &_markBitMap, &_markStack, &mrias_cl, CMSYield);
-
- // Preclean dirty cards in ModUnionTable and CardTable using
- // appropriate convergence criterion;
- // repeat CMSPrecleanIter times unless we find that
- // we are losing.
- assert(CMSPrecleanIter < 10, "CMSPrecleanIter is too large");
- assert(CMSPrecleanNumerator < CMSPrecleanDenominator,
- "Bad convergence multiplier");
- assert(CMSPrecleanThreshold >= 100,
- "Unreasonably low CMSPrecleanThreshold");
-
- size_t numIter, cumNumCards, lastNumCards, curNumCards;
- for (numIter = 0, cumNumCards = lastNumCards = curNumCards = 0;
- numIter < CMSPrecleanIter;
- numIter++, lastNumCards = curNumCards, cumNumCards += curNumCards) {
- curNumCards = preclean_mod_union_table(_cmsGen, &smoac_cl);
- log_trace(gc)(" (modUnionTable: " SIZE_FORMAT " cards)", curNumCards);
- // Either there are very few dirty cards, so re-mark
- // pause will be small anyway, or our pre-cleaning isn't
- // that much faster than the rate at which cards are being
- // dirtied, so we might as well stop and re-mark since
- // precleaning won't improve our re-mark time by much.
- if (curNumCards <= CMSPrecleanThreshold ||
- (numIter > 0 &&
- (curNumCards * CMSPrecleanDenominator >
- lastNumCards * CMSPrecleanNumerator))) {
- numIter++;
- cumNumCards += curNumCards;
- break;
- }
- }
-
- preclean_cld(&mrias_cl, _cmsGen->freelistLock());
-
- curNumCards = preclean_card_table(_cmsGen, &smoac_cl);
- cumNumCards += curNumCards;
- log_trace(gc)(" (cardTable: " SIZE_FORMAT " cards, re-scanned " SIZE_FORMAT " cards, " SIZE_FORMAT " iterations)",
- curNumCards, cumNumCards, numIter);
- return cumNumCards; // as a measure of useful work done
-}
-
-// PRECLEANING NOTES:
-// Precleaning involves:
-// . reading the bits of the modUnionTable and clearing the set bits.
-// . For the cards corresponding to the set bits, we scan the
-// objects on those cards. This means we need the free_list_lock
-// so that we can safely iterate over the CMS space when scanning
-// for oops.
-// . When we scan the objects, we'll be both reading and setting
-// marks in the marking bit map, so we'll need the marking bit map.
-// . For protecting _collector_state transitions, we take the CGC_lock.
-// Note that any races in the reading of of card table entries by the
-// CMS thread on the one hand and the clearing of those entries by the
-// VM thread or the setting of those entries by the mutator threads on the
-// other are quite benign. However, for efficiency it makes sense to keep
-// the VM thread from racing with the CMS thread while the latter is
-// dirty card info to the modUnionTable. We therefore also use the
-// CGC_lock to protect the reading of the card table and the mod union
-// table by the CM thread.
-// . We run concurrently with mutator updates, so scanning
-// needs to be done carefully -- we should not try to scan
-// potentially uninitialized objects.
-//
-// Locking strategy: While holding the CGC_lock, we scan over and
-// reset a maximal dirty range of the mod union / card tables, then lock
-// the free_list_lock and bitmap lock to do a full marking, then
-// release these locks; and repeat the cycle. This allows for a
-// certain amount of fairness in the sharing of these locks between
-// the CMS collector on the one hand, and the VM thread and the
-// mutators on the other.
-
-// NOTE: preclean_mod_union_table() and preclean_card_table()
-// further below are largely identical; if you need to modify
-// one of these methods, please check the other method too.
-
-size_t CMSCollector::preclean_mod_union_table(
- ConcurrentMarkSweepGeneration* old_gen,
- ScanMarkedObjectsAgainCarefullyClosure* cl) {
- verify_work_stacks_empty();
- verify_overflow_empty();
-
- // strategy: starting with the first card, accumulate contiguous
- // ranges of dirty cards; clear these cards, then scan the region
- // covered by these cards.
-
- // Since all of the MUT is committed ahead, we can just use
- // that, in case the generations expand while we are precleaning.
- // It might also be fine to just use the committed part of the
- // generation, but we might potentially miss cards when the
- // generation is rapidly expanding while we are in the midst
- // of precleaning.
- HeapWord* startAddr = old_gen->reserved().start();
- HeapWord* endAddr = old_gen->reserved().end();
-
- cl->setFreelistLock(old_gen->freelistLock()); // needed for yielding
-
- size_t numDirtyCards, cumNumDirtyCards;
- HeapWord *nextAddr, *lastAddr;
- for (cumNumDirtyCards = numDirtyCards = 0,
- nextAddr = lastAddr = startAddr;
- nextAddr < endAddr;
- nextAddr = lastAddr, cumNumDirtyCards += numDirtyCards) {
-
- ResourceMark rm;
- HandleMark hm;
-
- MemRegion dirtyRegion;
- {
- stopTimer();
- // Potential yield point
- CMSTokenSync ts(true);
- startTimer();
- sample_eden();
- // Get dirty region starting at nextOffset (inclusive),
- // simultaneously clearing it.
- dirtyRegion =
- _modUnionTable.getAndClearMarkedRegion(nextAddr, endAddr);
- assert(dirtyRegion.start() >= nextAddr,
- "returned region inconsistent?");
- }
- // Remember where the next search should begin.
- // The returned region (if non-empty) is a right open interval,
- // so lastOffset is obtained from the right end of that
- // interval.
- lastAddr = dirtyRegion.end();
- // Should do something more transparent and less hacky XXX
- numDirtyCards =
- _modUnionTable.heapWordDiffToOffsetDiff(dirtyRegion.word_size());
-
- // We'll scan the cards in the dirty region (with periodic
- // yields for foreground GC as needed).
- if (!dirtyRegion.is_empty()) {
- assert(numDirtyCards > 0, "consistency check");
- HeapWord* stop_point = NULL;
- stopTimer();
- // Potential yield point
- CMSTokenSyncWithLocks ts(true, old_gen->freelistLock(),
- bitMapLock());
- startTimer();
- {
- verify_work_stacks_empty();
- verify_overflow_empty();
- sample_eden();
- stop_point =
- old_gen->cmsSpace()->object_iterate_careful_m(dirtyRegion, cl);
- }
- if (stop_point != NULL) {
- // The careful iteration stopped early either because it found an
- // uninitialized object, or because we were in the midst of an
- // "abortable preclean", which should now be aborted. Redirty
- // the bits corresponding to the partially-scanned or unscanned
- // cards. We'll either restart at the next block boundary or
- // abort the preclean.
- assert((_collectorState == AbortablePreclean && should_abort_preclean()),
- "Should only be AbortablePreclean.");
- _modUnionTable.mark_range(MemRegion(stop_point, dirtyRegion.end()));
- if (should_abort_preclean()) {
- break; // out of preclean loop
- } else {
- // Compute the next address at which preclean should pick up;
- // might need bitMapLock in order to read P-bits.
- lastAddr = next_card_start_after_block(stop_point);
- }
- }
- } else {
- assert(lastAddr == endAddr, "consistency check");
- assert(numDirtyCards == 0, "consistency check");
- break;
- }
- }
- verify_work_stacks_empty();
- verify_overflow_empty();
- return cumNumDirtyCards;
-}
-
-// NOTE: preclean_mod_union_table() above and preclean_card_table()
-// below are largely identical; if you need to modify
-// one of these methods, please check the other method too.
-
-size_t CMSCollector::preclean_card_table(ConcurrentMarkSweepGeneration* old_gen,
- ScanMarkedObjectsAgainCarefullyClosure* cl) {
- // strategy: it's similar to precleamModUnionTable above, in that
- // we accumulate contiguous ranges of dirty cards, mark these cards
- // precleaned, then scan the region covered by these cards.
- HeapWord* endAddr = (HeapWord*)(old_gen->_virtual_space.high());
- HeapWord* startAddr = (HeapWord*)(old_gen->_virtual_space.low());
-
- cl->setFreelistLock(old_gen->freelistLock()); // needed for yielding
-
- size_t numDirtyCards, cumNumDirtyCards;
- HeapWord *lastAddr, *nextAddr;
-
- for (cumNumDirtyCards = numDirtyCards = 0,
- nextAddr = lastAddr = startAddr;
- nextAddr < endAddr;
- nextAddr = lastAddr, cumNumDirtyCards += numDirtyCards) {
-
- ResourceMark rm;
- HandleMark hm;
-
- MemRegion dirtyRegion;
- {
- // See comments in "Precleaning notes" above on why we
- // do this locking. XXX Could the locking overheads be
- // too high when dirty cards are sparse? [I don't think so.]
- stopTimer();
- CMSTokenSync x(true); // is cms thread
- startTimer();
- sample_eden();
- // Get and clear dirty region from card table
- dirtyRegion = _ct->dirty_card_range_after_reset(MemRegion(nextAddr, endAddr),
- true,
- CardTable::precleaned_card_val());
-
- assert(dirtyRegion.start() >= nextAddr,
- "returned region inconsistent?");
- }
- lastAddr = dirtyRegion.end();
- numDirtyCards =
- dirtyRegion.word_size()/CardTable::card_size_in_words;
-
- if (!dirtyRegion.is_empty()) {
- stopTimer();
- CMSTokenSyncWithLocks ts(true, old_gen->freelistLock(), bitMapLock());
- startTimer();
- sample_eden();
- verify_work_stacks_empty();
- verify_overflow_empty();
- HeapWord* stop_point =
- old_gen->cmsSpace()->object_iterate_careful_m(dirtyRegion, cl);
- if (stop_point != NULL) {
- assert((_collectorState == AbortablePreclean && should_abort_preclean()),
- "Should only be AbortablePreclean.");
- _ct->invalidate(MemRegion(stop_point, dirtyRegion.end()));
- if (should_abort_preclean()) {
- break; // out of preclean loop
- } else {
- // Compute the next address at which preclean should pick up.
- lastAddr = next_card_start_after_block(stop_point);
- }
- }
- } else {
- break;
- }
- }
- verify_work_stacks_empty();
- verify_overflow_empty();
- return cumNumDirtyCards;
-}
-
-class PrecleanCLDClosure : public CLDClosure {
- MetadataVisitingOopsInGenClosure* _cm_closure;
- public:
- PrecleanCLDClosure(MetadataVisitingOopsInGenClosure* oop_closure) : _cm_closure(oop_closure) {}
- void do_cld(ClassLoaderData* cld) {
- if (cld->has_accumulated_modified_oops()) {
- cld->clear_accumulated_modified_oops();
-
- _cm_closure->do_cld(cld);
- }
- }
-};
-
-// The freelist lock is needed to prevent asserts, is it really needed?
-void CMSCollector::preclean_cld(MarkRefsIntoAndScanClosure* cl, Mutex* freelistLock) {
- // Needed to walk CLDG
- MutexLocker ml(ClassLoaderDataGraph_lock);
-
- cl->set_freelistLock(freelistLock);
-
- CMSTokenSyncWithLocks ts(true, freelistLock, bitMapLock());
-
- // SSS: Add equivalent to ScanMarkedObjectsAgainCarefullyClosure::do_yield_check and should_abort_preclean?
- // SSS: We should probably check if precleaning should be aborted, at suitable intervals?
- PrecleanCLDClosure preclean_closure(cl);
- ClassLoaderDataGraph::cld_do(&preclean_closure);
-
- verify_work_stacks_empty();
- verify_overflow_empty();
-}
-
-void CMSCollector::checkpointRootsFinal() {
- assert(_collectorState == FinalMarking, "incorrect state transition?");
- check_correct_thread_executing();
- // world is stopped at this checkpoint
- assert(SafepointSynchronize::is_at_safepoint(),
- "world should be stopped");
- TraceCMSMemoryManagerStats tms(_collectorState, CMSHeap::heap()->gc_cause());
-
- verify_work_stacks_empty();
- verify_overflow_empty();
-
- log_debug(gc)("YG occupancy: " SIZE_FORMAT " K (" SIZE_FORMAT " K)",
- _young_gen->used() / K, _young_gen->capacity() / K);
- {
- if (CMSScavengeBeforeRemark) {
- CMSHeap* heap = CMSHeap::heap();
- // Temporarily set flag to false, GCH->do_collection will
- // expect it to be false and set to true
- FlagSetting fl(heap->_is_gc_active, false);
-
- heap->do_collection(true, // full (i.e. force, see below)
- false, // !clear_all_soft_refs
- 0, // size
- false, // is_tlab
- GenCollectedHeap::YoungGen // type
- );
- }
- FreelistLocker x(this);
- MutexLocker y(bitMapLock(),
- Mutex::_no_safepoint_check_flag);
- checkpointRootsFinalWork();
- _cmsGen->cmsSpace()->recalculate_used_stable();
- }
- verify_work_stacks_empty();
- verify_overflow_empty();
-}
-
-void CMSCollector::checkpointRootsFinalWork() {
- GCTraceTime(Trace, gc, phases) tm("checkpointRootsFinalWork", _gc_timer_cm);
-
- assert(haveFreelistLocks(), "must have free list locks");
- assert_lock_strong(bitMapLock());
-
- ResourceMark rm;
- HandleMark hm;
-
- CMSHeap* heap = CMSHeap::heap();
-
- assert(haveFreelistLocks(), "must have free list locks");
- assert_lock_strong(bitMapLock());
-
- // We might assume that we need not fill TLAB's when
- // CMSScavengeBeforeRemark is set, because we may have just done
- // a scavenge which would have filled all TLAB's -- and besides
- // Eden would be empty. This however may not always be the case --
- // for instance although we asked for a scavenge, it may not have
- // happened because of a JNI critical section. We probably need
- // a policy for deciding whether we can in that case wait until
- // the critical section releases and then do the remark following
- // the scavenge, and skip it here. In the absence of that policy,
- // or of an indication of whether the scavenge did indeed occur,
- // we cannot rely on TLAB's having been filled and must do
- // so here just in case a scavenge did not happen.
- heap->ensure_parsability(false); // fill TLAB's, but no need to retire them
- // Update the saved marks which may affect the root scans.
- heap->save_marks();
-
- print_eden_and_survivor_chunk_arrays();
-
- {
-#if COMPILER2_OR_JVMCI
- DerivedPointerTableDeactivate dpt_deact;
-#endif
-
- // Note on the role of the mod union table:
- // Since the marker in "markFromRoots" marks concurrently with
- // mutators, it is possible for some reachable objects not to have been
- // scanned. For instance, an only reference to an object A was
- // placed in object B after the marker scanned B. Unless B is rescanned,
- // A would be collected. Such updates to references in marked objects
- // are detected via the mod union table which is the set of all cards
- // dirtied since the first checkpoint in this GC cycle and prior to
- // the most recent young generation GC, minus those cleaned up by the
- // concurrent precleaning.
- if (CMSParallelRemarkEnabled) {
- GCTraceTime(Debug, gc, phases) t("Rescan (parallel)", _gc_timer_cm);
- do_remark_parallel();
- } else {
- GCTraceTime(Debug, gc, phases) t("Rescan (non-parallel)", _gc_timer_cm);
- do_remark_non_parallel();
- }
- }
- verify_work_stacks_empty();
- verify_overflow_empty();
-
- {
- GCTraceTime(Trace, gc, phases) ts("refProcessingWork", _gc_timer_cm);
- refProcessingWork();
- }
- verify_work_stacks_empty();
- verify_overflow_empty();
-
- if (should_unload_classes()) {
- heap->prune_scavengable_nmethods();
- }
-
- // If we encountered any (marking stack / work queue) overflow
- // events during the current CMS cycle, take appropriate
- // remedial measures, where possible, so as to try and avoid
- // recurrence of that condition.
- assert(_markStack.isEmpty(), "No grey objects");
- size_t ser_ovflw = _ser_pmc_remark_ovflw + _ser_pmc_preclean_ovflw +
- _ser_kac_ovflw + _ser_kac_preclean_ovflw;
- if (ser_ovflw > 0) {
- log_trace(gc)("Marking stack overflow (benign) (pmc_pc=" SIZE_FORMAT ", pmc_rm=" SIZE_FORMAT ", kac=" SIZE_FORMAT ", kac_preclean=" SIZE_FORMAT ")",
- _ser_pmc_preclean_ovflw, _ser_pmc_remark_ovflw, _ser_kac_ovflw, _ser_kac_preclean_ovflw);
- _markStack.expand();
- _ser_pmc_remark_ovflw = 0;
- _ser_pmc_preclean_ovflw = 0;
- _ser_kac_preclean_ovflw = 0;
- _ser_kac_ovflw = 0;
- }
- if (_par_pmc_remark_ovflw > 0 || _par_kac_ovflw > 0) {
- log_trace(gc)("Work queue overflow (benign) (pmc_rm=" SIZE_FORMAT ", kac=" SIZE_FORMAT ")",
- _par_pmc_remark_ovflw, _par_kac_ovflw);
- _par_pmc_remark_ovflw = 0;
- _par_kac_ovflw = 0;
- }
- if (_markStack._hit_limit > 0) {
- log_trace(gc)(" (benign) Hit max stack size limit (" SIZE_FORMAT ")",
- _markStack._hit_limit);
- }
- if (_markStack._failed_double > 0) {
- log_trace(gc)(" (benign) Failed stack doubling (" SIZE_FORMAT "), current capacity " SIZE_FORMAT,
- _markStack._failed_double, _markStack.capacity());
- }
- _markStack._hit_limit = 0;
- _markStack._failed_double = 0;
-
- if ((VerifyAfterGC || VerifyDuringGC) &&
- CMSHeap::heap()->total_collections() >= VerifyGCStartAt) {
- verify_after_remark();
- }
-
- _gc_tracer_cm->report_object_count_after_gc(&_is_alive_closure);
-
- // Change under the freelistLocks.
- _collectorState = Sweeping;
- // Call isAllClear() under bitMapLock
- assert(_modUnionTable.isAllClear(),
- "Should be clear by end of the final marking");
- assert(_ct->cld_rem_set()->mod_union_is_clear(),
- "Should be clear by end of the final marking");
-}
-
-void CMSParInitialMarkTask::work(uint worker_id) {
- elapsedTimer _timer;
- ResourceMark rm;
- HandleMark hm;
-
- // ---------- scan from roots --------------
- _timer.start();
- CMSHeap* heap = CMSHeap::heap();
- ParMarkRefsIntoClosure par_mri_cl(_collector->_span, &(_collector->_markBitMap));
-
- // ---------- young gen roots --------------
- {
- work_on_young_gen_roots(&par_mri_cl);
- _timer.stop();
- log_trace(gc, task)("Finished young gen initial mark scan work in %dth thread: %3.3f sec", worker_id, _timer.seconds());
- }
-
- // ---------- remaining roots --------------
- _timer.reset();
- _timer.start();
-
- CLDToOopClosure cld_closure(&par_mri_cl, ClassLoaderData::_claim_strong);
-
- heap->cms_process_roots(_strong_roots_scope,
- false, // yg was scanned above
- GenCollectedHeap::ScanningOption(_collector->CMSCollector::roots_scanning_options()),
- _collector->should_unload_classes(),
- &par_mri_cl,
- &cld_closure);
-
- assert(_collector->should_unload_classes()
- || (_collector->CMSCollector::roots_scanning_options() & GenCollectedHeap::SO_AllCodeCache),
- "if we didn't scan the code cache, we have to be ready to drop nmethods with expired weak oops");
- _timer.stop();
- log_trace(gc, task)("Finished remaining root initial mark scan work in %dth thread: %3.3f sec", worker_id, _timer.seconds());
-}
-
-// Parallel remark task
-class CMSParRemarkTask: public CMSParMarkTask {
- CompactibleFreeListSpace* _cms_space;
-
- // The per-thread work queues, available here for stealing.
- OopTaskQueueSet* _task_queues;
- TaskTerminator _term;
- StrongRootsScope* _strong_roots_scope;
-
- public:
- // A value of 0 passed to n_workers will cause the number of
- // workers to be taken from the active workers in the work gang.
- CMSParRemarkTask(CMSCollector* collector,
- CompactibleFreeListSpace* cms_space,
- uint n_workers, WorkGang* workers,
- OopTaskQueueSet* task_queues,
- StrongRootsScope* strong_roots_scope):
- CMSParMarkTask("Rescan roots and grey objects in parallel",
- collector, n_workers),
- _cms_space(cms_space),
- _task_queues(task_queues),
- _term(n_workers, task_queues),
- _strong_roots_scope(strong_roots_scope) { }
-
- OopTaskQueueSet* task_queues() { return _task_queues; }
-
- OopTaskQueue* work_queue(int i) { return task_queues()->queue(i); }
-
- ParallelTaskTerminator* terminator() { return _term.terminator(); }
- uint n_workers() { return _n_workers; }
-
- void work(uint worker_id);
-
- private:
- // ... of dirty cards in old space
- void do_dirty_card_rescan_tasks(CompactibleFreeListSpace* sp, int i,
- ParMarkRefsIntoAndScanClosure* cl);
-
- // ... work stealing for the above
- void do_work_steal(int i, ParMarkRefsIntoAndScanClosure* cl);
-};
-
-class RemarkCLDClosure : public CLDClosure {
- CLDToOopClosure _cm_closure;
- public:
- RemarkCLDClosure(OopClosure* oop_closure) : _cm_closure(oop_closure, ClassLoaderData::_claim_strong) {}
- void do_cld(ClassLoaderData* cld) {
- // Check if we have modified any oops in the CLD during the concurrent marking.
- if (cld->has_accumulated_modified_oops()) {
- cld->clear_accumulated_modified_oops();
-
- // We could have transfered the current modified marks to the accumulated marks,
- // like we do with the Card Table to Mod Union Table. But it's not really necessary.
- } else if (cld->has_modified_oops()) {
- // Don't clear anything, this info is needed by the next young collection.
- } else {
- // No modified oops in the ClassLoaderData.
- return;
- }
-
- // The klass has modified fields, need to scan the klass.
- _cm_closure.do_cld(cld);
- }
-};
-
-void CMSParMarkTask::work_on_young_gen_roots(OopsInGenClosure* cl) {
- ParNewGeneration* young_gen = _collector->_young_gen;
- ContiguousSpace* eden_space = young_gen->eden();
- ContiguousSpace* from_space = young_gen->from();
- ContiguousSpace* to_space = young_gen->to();
-
- HeapWord** eca = _collector->_eden_chunk_array;
- size_t ect = _collector->_eden_chunk_index;
- HeapWord** sca = _collector->_survivor_chunk_array;
- size_t sct = _collector->_survivor_chunk_index;
-
- assert(ect <= _collector->_eden_chunk_capacity, "out of bounds");
- assert(sct <= _collector->_survivor_chunk_capacity, "out of bounds");
-
- do_young_space_rescan(cl, to_space, NULL, 0);
- do_young_space_rescan(cl, from_space, sca, sct);
- do_young_space_rescan(cl, eden_space, eca, ect);
-}
-
-// work_queue(i) is passed to the closure
-// ParMarkRefsIntoAndScanClosure. The "i" parameter
-// also is passed to do_dirty_card_rescan_tasks() and to
-// do_work_steal() to select the i-th task_queue.
-
-void CMSParRemarkTask::work(uint worker_id) {
- elapsedTimer _timer;
- ResourceMark rm;
- HandleMark hm;
-
- // ---------- rescan from roots --------------
- _timer.start();
- CMSHeap* heap = CMSHeap::heap();
- ParMarkRefsIntoAndScanClosure par_mrias_cl(_collector,
- _collector->_span, _collector->ref_processor(),
- &(_collector->_markBitMap),
- work_queue(worker_id));
-
- // Rescan young gen roots first since these are likely
- // coarsely partitioned and may, on that account, constitute
- // the critical path; thus, it's best to start off that
- // work first.
- // ---------- young gen roots --------------
- {
- work_on_young_gen_roots(&par_mrias_cl);
- _timer.stop();
- log_trace(gc, task)("Finished young gen rescan work in %dth thread: %3.3f sec", worker_id, _timer.seconds());
- }
-
- // ---------- remaining roots --------------
- _timer.reset();
- _timer.start();
- heap->cms_process_roots(_strong_roots_scope,
- false, // yg was scanned above
- GenCollectedHeap::ScanningOption(_collector->CMSCollector::roots_scanning_options()),
- _collector->should_unload_classes(),
- &par_mrias_cl,
- NULL); // The dirty klasses will be handled below
-
- assert(_collector->should_unload_classes()
- || (_collector->CMSCollector::roots_scanning_options() & GenCollectedHeap::SO_AllCodeCache),
- "if we didn't scan the code cache, we have to be ready to drop nmethods with expired weak oops");
- _timer.stop();
- log_trace(gc, task)("Finished remaining root rescan work in %dth thread: %3.3f sec", worker_id, _timer.seconds());
-
- // ---------- unhandled CLD scanning ----------
- if (worker_id == 0) { // Single threaded at the moment.
- _timer.reset();
- _timer.start();
-
- // Scan all new class loader data objects and new dependencies that were
- // introduced during concurrent marking.
- ResourceMark rm;
- GrowableArray<ClassLoaderData*>* array = ClassLoaderDataGraph::new_clds();
- for (int i = 0; i < array->length(); i++) {
- Devirtualizer::do_cld(&par_mrias_cl, array->at(i));
- }
-
- // We don't need to keep track of new CLDs anymore.
- ClassLoaderDataGraph::remember_new_clds(false);
-
- _timer.stop();
- log_trace(gc, task)("Finished unhandled CLD scanning work in %dth thread: %3.3f sec", worker_id, _timer.seconds());
- }
-
- // We might have added oops to ClassLoaderData::_handles during the
- // concurrent marking phase. These oops do not always point to newly allocated objects
- // that are guaranteed to be kept alive. Hence,
- // we do have to revisit the _handles block during the remark phase.
-
- // ---------- dirty CLD scanning ----------
- if (worker_id == 0) { // Single threaded at the moment.
- _timer.reset();
- _timer.start();
-
- // Scan all classes that was dirtied during the concurrent marking phase.
- RemarkCLDClosure remark_closure(&par_mrias_cl);
- ClassLoaderDataGraph::cld_do(&remark_closure);
-
- _timer.stop();
- log_trace(gc, task)("Finished dirty CLD scanning work in %dth thread: %3.3f sec", worker_id, _timer.seconds());
- }
-
-
- // ---------- rescan dirty cards ------------
- _timer.reset();
- _timer.start();
-
- // Do the rescan tasks for each of the two spaces
- // (cms_space) in turn.
- // "worker_id" is passed to select the task_queue for "worker_id"
- do_dirty_card_rescan_tasks(_cms_space, worker_id, &par_mrias_cl);
- _timer.stop();
- log_trace(gc, task)("Finished dirty card rescan work in %dth thread: %3.3f sec", worker_id, _timer.seconds());
-
- // ---------- steal work from other threads ...
- // ---------- ... and drain overflow list.
- _timer.reset();
- _timer.start();
- do_work_steal(worker_id, &par_mrias_cl);
- _timer.stop();
- log_trace(gc, task)("Finished work stealing in %dth thread: %3.3f sec", worker_id, _timer.seconds());
-}
-
-void
-CMSParMarkTask::do_young_space_rescan(
- OopsInGenClosure* cl, ContiguousSpace* space,
- HeapWord** chunk_array, size_t chunk_top) {
- // Until all tasks completed:
- // . claim an unclaimed task
- // . compute region boundaries corresponding to task claimed
- // using chunk_array
- // . par_oop_iterate(cl) over that region
-
- ResourceMark rm;
- HandleMark hm;
-
- SequentialSubTasksDone* pst = space->par_seq_tasks();
-
- uint nth_task = 0;
- uint n_tasks = pst->n_tasks();
-
- if (n_tasks > 0) {
- assert(pst->valid(), "Uninitialized use?");
- HeapWord *start, *end;
- while (pst->try_claim_task(/* reference */ nth_task)) {
- // We claimed task # nth_task; compute its boundaries.
- if (chunk_top == 0) { // no samples were taken
- assert(nth_task == 0 && n_tasks == 1, "Can have only 1 eden task");
- start = space->bottom();
- end = space->top();
- } else if (nth_task == 0) {
- start = space->bottom();
- end = chunk_array[nth_task];
- } else if (nth_task < (uint)chunk_top) {
- assert(nth_task >= 1, "Control point invariant");
- start = chunk_array[nth_task - 1];
- end = chunk_array[nth_task];
- } else {
- assert(nth_task == (uint)chunk_top, "Control point invariant");
- start = chunk_array[chunk_top - 1];
- end = space->top();
- }
- MemRegion mr(start, end);
- // Verify that mr is in space
- assert(mr.is_empty() || space->used_region().contains(mr),
- "Should be in space");
- // Verify that "start" is an object boundary
- assert(mr.is_empty() || oopDesc::is_oop(oop(mr.start())),
- "Should be an oop");
- space->par_oop_iterate(mr, cl);
- }
- pst->all_tasks_completed();
- }
-}
-
-void
-CMSParRemarkTask::do_dirty_card_rescan_tasks(
- CompactibleFreeListSpace* sp, int i,
- ParMarkRefsIntoAndScanClosure* cl) {
- // Until all tasks completed:
- // . claim an unclaimed task
- // . compute region boundaries corresponding to task claimed
- // . transfer dirty bits ct->mut for that region
- // . apply rescanclosure to dirty mut bits for that region
-
- ResourceMark rm;
- HandleMark hm;
-
- OopTaskQueue* work_q = work_queue(i);
- ModUnionClosure modUnionClosure(&(_collector->_modUnionTable));
- // CAUTION! CAUTION! CAUTION! CAUTION! CAUTION! CAUTION! CAUTION!
- // CAUTION: This closure has state that persists across calls to
- // the work method dirty_range_iterate_clear() in that it has
- // embedded in it a (subtype of) UpwardsObjectClosure. The
- // use of that state in the embedded UpwardsObjectClosure instance
- // assumes that the cards are always iterated (even if in parallel
- // by several threads) in monotonically increasing order per each
- // thread. This is true of the implementation below which picks
- // card ranges (chunks) in monotonically increasing order globally
- // and, a-fortiori, in monotonically increasing order per thread
- // (the latter order being a subsequence of the former).
- // If the work code below is ever reorganized into a more chaotic
- // work-partitioning form than the current "sequential tasks"
- // paradigm, the use of that persistent state will have to be
- // revisited and modified appropriately. See also related
- // bug 4756801 work on which should examine this code to make
- // sure that the changes there do not run counter to the
- // assumptions made here and necessary for correctness and
- // efficiency. Note also that this code might yield inefficient
- // behavior in the case of very large objects that span one or
- // more work chunks. Such objects would potentially be scanned
- // several times redundantly. Work on 4756801 should try and
- // address that performance anomaly if at all possible. XXX
- MemRegion full_span = _collector->_span;
- CMSBitMap* bm = &(_collector->_markBitMap); // shared
- MarkFromDirtyCardsClosure
- greyRescanClosure(_collector, full_span, // entire span of interest
- sp, bm, work_q, cl);
-
- SequentialSubTasksDone* pst = sp->conc_par_seq_tasks();
- assert(pst->valid(), "Uninitialized use?");
- uint nth_task = 0;
- const int alignment = CardTable::card_size * BitsPerWord;
- MemRegion span = sp->used_region();
- HeapWord* start_addr = span.start();
- HeapWord* end_addr = align_up(span.end(), alignment);
- const size_t chunk_size = sp->rescan_task_size(); // in HeapWord units
- assert(is_aligned(start_addr, alignment), "Check alignment");
- assert(is_aligned(chunk_size, alignment), "Check alignment");
-
- while (pst->try_claim_task(/* reference */ nth_task)) {
- // Having claimed the nth_task, compute corresponding mem-region,
- // which is a-fortiori aligned correctly (i.e. at a MUT boundary).
- // The alignment restriction ensures that we do not need any
- // synchronization with other gang-workers while setting or
- // clearing bits in thus chunk of the MUT.
- MemRegion this_span = MemRegion(start_addr + nth_task*chunk_size,
- start_addr + (nth_task+1)*chunk_size);
- // The last chunk's end might be way beyond end of the
- // used region. In that case pull back appropriately.
- if (this_span.end() > end_addr) {
- this_span.set_end(end_addr);
- assert(!this_span.is_empty(), "Program logic (calculation of n_tasks)");
- }
- // Iterate over the dirty cards covering this chunk, marking them
- // precleaned, and setting the corresponding bits in the mod union
- // table. Since we have been careful to partition at Card and MUT-word
- // boundaries no synchronization is needed between parallel threads.
- _collector->_ct->dirty_card_iterate(this_span,
- &modUnionClosure);
-
- // Having transferred these marks into the modUnionTable,
- // rescan the marked objects on the dirty cards in the modUnionTable.
- // Even if this is at a synchronous collection, the initial marking
- // may have been done during an asynchronous collection so there
- // may be dirty bits in the mod-union table.
- _collector->_modUnionTable.dirty_range_iterate_clear(
- this_span, &greyRescanClosure);
- _collector->_modUnionTable.verifyNoOneBitsInRange(
- this_span.start(),
- this_span.end());
- }
- pst->all_tasks_completed(); // declare that i am done
-}
-
-// . see if we can share work_queues with ParNew? XXX
-void
-CMSParRemarkTask::do_work_steal(int i, ParMarkRefsIntoAndScanClosure* cl) {
- OopTaskQueue* work_q = work_queue(i);
- NOT_PRODUCT(int num_steals = 0;)
- oop obj_to_scan;
- CMSBitMap* bm = &(_collector->_markBitMap);
-
- while (true) {
- // Completely finish any left over work from (an) earlier round(s)
- cl->trim_queue(0);
- size_t num_from_overflow_list = MIN2((size_t)(work_q->max_elems() - work_q->size())/4,
- (size_t)ParGCDesiredObjsFromOverflowList);
- // Now check if there's any work in the overflow list
- // Passing ParallelGCThreads as the third parameter, no_of_gc_threads,
- // only affects the number of attempts made to get work from the
- // overflow list and does not affect the number of workers. Just
- // pass ParallelGCThreads so this behavior is unchanged.
- if (_collector->par_take_from_overflow_list(num_from_overflow_list,
- work_q,
- ParallelGCThreads)) {
- // found something in global overflow list;
- // not yet ready to go stealing work from others.
- // We'd like to assert(work_q->size() != 0, ...)
- // because we just took work from the overflow list,
- // but of course we can't since all of that could have
- // been already stolen from us.
- // "He giveth and He taketh away."
- continue;
- }
- // Verify that we have no work before we resort to stealing
- assert(work_q->size() == 0, "Have work, shouldn't steal");
- // Try to steal from other queues that have work
- if (task_queues()->steal(i, /* reference */ obj_to_scan)) {
- NOT_PRODUCT(num_steals++;)
- assert(oopDesc::is_oop(obj_to_scan), "Oops, not an oop!");
- assert(bm->isMarked((HeapWord*)obj_to_scan), "Stole an unmarked oop?");
- // Do scanning work
- obj_to_scan->oop_iterate(cl);
- // Loop around, finish this work, and try to steal some more
- } else if (terminator()->offer_termination()) {
- break; // nirvana from the infinite cycle
- }
- }
- log_develop_trace(gc, task)("\t(%d: stole %d oops)", i, num_steals);
- assert(work_q->size() == 0 && _collector->overflow_list_is_empty(),
- "Else our work is not yet done");
-}
-
-// Record object boundaries in _eden_chunk_array by sampling the eden
-// top in the slow-path eden object allocation code path and record
-// the boundaries, if CMSEdenChunksRecordAlways is true. If
-// CMSEdenChunksRecordAlways is false, we use the other asynchronous
-// sampling in sample_eden() that activates during the part of the
-// preclean phase.
-void CMSCollector::sample_eden_chunk() {
- if (CMSEdenChunksRecordAlways && _eden_chunk_array != NULL) {
- if (_eden_chunk_lock->try_lock()) {
- // Record a sample. This is the critical section. The contents
- // of the _eden_chunk_array have to be non-decreasing in the
- // address order.
- _eden_chunk_array[_eden_chunk_index] = *_top_addr;
- assert(_eden_chunk_array[_eden_chunk_index] <= *_end_addr,
- "Unexpected state of Eden");
- if (_eden_chunk_index == 0 ||
- ((_eden_chunk_array[_eden_chunk_index] > _eden_chunk_array[_eden_chunk_index-1]) &&
- (pointer_delta(_eden_chunk_array[_eden_chunk_index],
- _eden_chunk_array[_eden_chunk_index-1]) >= CMSSamplingGrain))) {
- _eden_chunk_index++; // commit sample
- }
- _eden_chunk_lock->unlock();
- }
- }
-}
-
-// Return a thread-local PLAB recording array, as appropriate.
-void* CMSCollector::get_data_recorder(int thr_num) {
- if (_survivor_plab_array != NULL &&
- (CMSPLABRecordAlways ||
- (_collectorState > Marking && _collectorState < FinalMarking))) {
- assert(thr_num < (int)ParallelGCThreads, "thr_num is out of bounds");
- ChunkArray* ca = &_survivor_plab_array[thr_num];
- ca->reset(); // clear it so that fresh data is recorded
- return (void*) ca;
- } else {
- return NULL;
- }
-}
-
-// Reset all the thread-local PLAB recording arrays
-void CMSCollector::reset_survivor_plab_arrays() {
- for (uint i = 0; i < ParallelGCThreads; i++) {
- _survivor_plab_array[i].reset();
- }
-}
-
-// Merge the per-thread plab arrays into the global survivor chunk
-// array which will provide the partitioning of the survivor space
-// for CMS initial scan and rescan.
-void CMSCollector::merge_survivor_plab_arrays(ContiguousSpace* surv,
- int no_of_gc_threads) {
- assert(_survivor_plab_array != NULL, "Error");
- assert(_survivor_chunk_array != NULL, "Error");
- assert(_collectorState == FinalMarking ||
- (CMSParallelInitialMarkEnabled && _collectorState == InitialMarking), "Error");
- for (int j = 0; j < no_of_gc_threads; j++) {
- _cursor[j] = 0;
- }
- HeapWord* top = surv->top();
- size_t i;
- for (i = 0; i < _survivor_chunk_capacity; i++) { // all sca entries
- HeapWord* min_val = top; // Higher than any PLAB address
- uint min_tid = 0; // position of min_val this round
- for (int j = 0; j < no_of_gc_threads; j++) {
- ChunkArray* cur_sca = &_survivor_plab_array[j];
- if (_cursor[j] == cur_sca->end()) {
- continue;
- }
- assert(_cursor[j] < cur_sca->end(), "ctl pt invariant");
- HeapWord* cur_val = cur_sca->nth(_cursor[j]);
- assert(surv->used_region().contains(cur_val), "Out of bounds value");
- if (cur_val < min_val) {
- min_tid = j;
- min_val = cur_val;
- } else {
- assert(cur_val < top, "All recorded addresses should be less");
- }
- }
- // At this point min_val and min_tid are respectively
- // the least address in _survivor_plab_array[j]->nth(_cursor[j])
- // and the thread (j) that witnesses that address.
- // We record this address in the _survivor_chunk_array[i]
- // and increment _cursor[min_tid] prior to the next round i.
- if (min_val == top) {
- break;
- }
- _survivor_chunk_array[i] = min_val;
- _cursor[min_tid]++;
- }
- // We are all done; record the size of the _survivor_chunk_array
- _survivor_chunk_index = i; // exclusive: [0, i)
- log_trace(gc, survivor)(" (Survivor:" SIZE_FORMAT "chunks) ", i);
- // Verify that we used up all the recorded entries
- #ifdef ASSERT
- size_t total = 0;
- for (int j = 0; j < no_of_gc_threads; j++) {
- assert(_cursor[j] == _survivor_plab_array[j].end(), "Ctl pt invariant");
- total += _cursor[j];
- }
- assert(total == _survivor_chunk_index, "Ctl Pt Invariant");
- // Check that the merged array is in sorted order
- if (total > 0) {
- for (size_t i = 0; i < total - 1; i++) {
- log_develop_trace(gc, survivor)(" (chunk" SIZE_FORMAT ":" INTPTR_FORMAT ") ",
- i, p2i(_survivor_chunk_array[i]));
- assert(_survivor_chunk_array[i] < _survivor_chunk_array[i+1],
- "Not sorted");
- }
- }
- #endif // ASSERT
-}
-
-// Set up the space's par_seq_tasks structure for work claiming
-// for parallel initial scan and rescan of young gen.
-// See ParRescanTask where this is currently used.
-void
-CMSCollector::
-initialize_sequential_subtasks_for_young_gen_rescan(int n_threads) {
- assert(n_threads > 0, "Unexpected n_threads argument");
-
- // Eden space
- if (!_young_gen->eden()->is_empty()) {
- SequentialSubTasksDone* pst = _young_gen->eden()->par_seq_tasks();
- assert(!pst->valid(), "Clobbering existing data?");
- // Each valid entry in [0, _eden_chunk_index) represents a task.
- size_t n_tasks = _eden_chunk_index + 1;
- assert(n_tasks == 1 || _eden_chunk_array != NULL, "Error");
- // Sets the condition for completion of the subtask (how many threads
- // need to finish in order to be done).
- pst->set_n_threads(n_threads);
- pst->set_n_tasks((int)n_tasks);
- }
-
- // Merge the survivor plab arrays into _survivor_chunk_array
- if (_survivor_plab_array != NULL) {
- merge_survivor_plab_arrays(_young_gen->from(), n_threads);
- } else {
- assert(_survivor_chunk_index == 0, "Error");
- }
-
- // To space
- {
- SequentialSubTasksDone* pst = _young_gen->to()->par_seq_tasks();
- assert(!pst->valid(), "Clobbering existing data?");
- // Sets the condition for completion of the subtask (how many threads
- // need to finish in order to be done).
- pst->set_n_threads(n_threads);
- pst->set_n_tasks(1);
- assert(pst->valid(), "Error");
- }
-
- // From space
- {
- SequentialSubTasksDone* pst = _young_gen->from()->par_seq_tasks();
- assert(!pst->valid(), "Clobbering existing data?");
- size_t n_tasks = _survivor_chunk_index + 1;
- assert(n_tasks == 1 || _survivor_chunk_array != NULL, "Error");
- // Sets the condition for completion of the subtask (how many threads
- // need to finish in order to be done).
- pst->set_n_threads(n_threads);
- pst->set_n_tasks((int)n_tasks);
- assert(pst->valid(), "Error");
- }
-}
-
-// Parallel version of remark
-void CMSCollector::do_remark_parallel() {
- CMSHeap* heap = CMSHeap::heap();
- WorkGang* workers = heap->workers();
- assert(workers != NULL, "Need parallel worker threads.");
- // Choose to use the number of GC workers most recently set
- // into "active_workers".
- uint n_workers = workers->active_workers();
-
- CompactibleFreeListSpace* cms_space = _cmsGen->cmsSpace();
-
- StrongRootsScope srs(n_workers);
-
- CMSParRemarkTask tsk(this, cms_space, n_workers, workers, task_queues(), &srs);
-
- // We won't be iterating over the cards in the card table updating
- // the younger_gen cards, so we shouldn't call the following else
- // the verification code as well as subsequent younger_refs_iterate
- // code would get confused. XXX
- // heap->rem_set()->prepare_for_younger_refs_iterate(true); // parallel
-
- // The young gen rescan work will not be done as part of
- // process_roots (which currently doesn't know how to
- // parallelize such a scan), but rather will be broken up into
- // a set of parallel tasks (via the sampling that the [abortable]
- // preclean phase did of eden, plus the [two] tasks of
- // scanning the [two] survivor spaces. Further fine-grain
- // parallelization of the scanning of the survivor spaces
- // themselves, and of precleaning of the young gen itself
- // is deferred to the future.
- initialize_sequential_subtasks_for_young_gen_rescan(n_workers);
-
- // The dirty card rescan work is broken up into a "sequence"
- // of parallel tasks (per constituent space) that are dynamically
- // claimed by the parallel threads.
- cms_space->initialize_sequential_subtasks_for_rescan(n_workers);
-
- // It turns out that even when we're using 1 thread, doing the work in a
- // separate thread causes wide variance in run times. We can't help this
- // in the multi-threaded case, but we special-case n=1 here to get
- // repeatable measurements of the 1-thread overhead of the parallel code.
- if (n_workers > 1) {
- // Make refs discovery MT-safe, if it isn't already: it may not
- // necessarily be so, since it's possible that we are doing
- // ST marking.
- ReferenceProcessorMTDiscoveryMutator mt(ref_processor(), true);
- workers->run_task(&tsk);
- } else {
- ReferenceProcessorMTDiscoveryMutator mt(ref_processor(), false);
- tsk.work(0);
- }
-
- // restore, single-threaded for now, any preserved marks
- // as a result of work_q overflow
- restore_preserved_marks_if_any();
-}
-
-// Non-parallel version of remark
-void CMSCollector::do_remark_non_parallel() {
- ResourceMark rm;
- HandleMark hm;
- CMSHeap* heap = CMSHeap::heap();
- ReferenceProcessorMTDiscoveryMutator mt(ref_processor(), false);
-
- MarkRefsIntoAndScanClosure
- mrias_cl(_span, ref_processor(), &_markBitMap, NULL /* not precleaning */,
- &_markStack, this,
- false /* should_yield */, false /* not precleaning */);
- MarkFromDirtyCardsClosure
- markFromDirtyCardsClosure(this, _span,
- NULL, // space is set further below
- &_markBitMap, &_markStack, &mrias_cl);
- {
- GCTraceTime(Trace, gc, phases) t("Grey Object Rescan", _gc_timer_cm);
- // Iterate over the dirty cards, setting the corresponding bits in the
- // mod union table.
- {
- ModUnionClosure modUnionClosure(&_modUnionTable);
- _ct->dirty_card_iterate(_cmsGen->used_region(),
- &modUnionClosure);
- }
- // Having transferred these marks into the modUnionTable, we just need
- // to rescan the marked objects on the dirty cards in the modUnionTable.
- // The initial marking may have been done during an asynchronous
- // collection so there may be dirty bits in the mod-union table.
- const int alignment = CardTable::card_size * BitsPerWord;
- {
- // ... First handle dirty cards in CMS gen
- markFromDirtyCardsClosure.set_space(_cmsGen->cmsSpace());
- MemRegion ur = _cmsGen->used_region();
- HeapWord* lb = ur.start();
- HeapWord* ub = align_up(ur.end(), alignment);
- MemRegion cms_span(lb, ub);
- _modUnionTable.dirty_range_iterate_clear(cms_span,
- &markFromDirtyCardsClosure);
- verify_work_stacks_empty();
- log_trace(gc)(" (re-scanned " SIZE_FORMAT " dirty cards in cms gen) ", markFromDirtyCardsClosure.num_dirty_cards());
- }
- }
- if (VerifyDuringGC &&
- CMSHeap::heap()->total_collections() >= VerifyGCStartAt) {
- HandleMark hm; // Discard invalid handles created during verification
- Universe::verify();
- }
- {
- GCTraceTime(Trace, gc, phases) t("Root Rescan", _gc_timer_cm);
-
- verify_work_stacks_empty();
-
- heap->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel.
- StrongRootsScope srs(1);
-
- heap->cms_process_roots(&srs,
- true, // young gen as roots
- GenCollectedHeap::ScanningOption(roots_scanning_options()),
- should_unload_classes(),
- &mrias_cl,
- NULL); // The dirty klasses will be handled below
-
- assert(should_unload_classes()
- || (roots_scanning_options() & GenCollectedHeap::SO_AllCodeCache),
- "if we didn't scan the code cache, we have to be ready to drop nmethods with expired weak oops");
- }
-
- {
- GCTraceTime(Trace, gc, phases) t("Visit Unhandled CLDs", _gc_timer_cm);
-
- verify_work_stacks_empty();
-
- // Scan all class loader data objects that might have been introduced
- // during concurrent marking.
- ResourceMark rm;
- GrowableArray<ClassLoaderData*>* array = ClassLoaderDataGraph::new_clds();
- for (int i = 0; i < array->length(); i++) {
- Devirtualizer::do_cld(&mrias_cl, array->at(i));
- }
-
- // We don't need to keep track of new CLDs anymore.
- ClassLoaderDataGraph::remember_new_clds(false);
-
- verify_work_stacks_empty();
- }
-
- // We might have added oops to ClassLoaderData::_handles during the
- // concurrent marking phase. These oops do not point to newly allocated objects
- // that are guaranteed to be kept alive. Hence,
- // we do have to revisit the _handles block during the remark phase.
- {
- GCTraceTime(Trace, gc, phases) t("Dirty CLD Scan", _gc_timer_cm);
-
- verify_work_stacks_empty();
-
- RemarkCLDClosure remark_closure(&mrias_cl);
- ClassLoaderDataGraph::cld_do(&remark_closure);
-
- verify_work_stacks_empty();
- }
-
- verify_work_stacks_empty();
- // Restore evacuated mark words, if any, used for overflow list links
- restore_preserved_marks_if_any();
-
- verify_overflow_empty();
-}
-
-////////////////////////////////////////////////////////
-// Parallel Reference Processing Task Proxy Class
-////////////////////////////////////////////////////////
-class AbstractGangTaskWOopQueues : public AbstractGangTask {
- OopTaskQueueSet* _queues;
- TaskTerminator _terminator;
- public:
- AbstractGangTaskWOopQueues(const char* name, OopTaskQueueSet* queues, uint n_threads) :
- AbstractGangTask(name), _queues(queues), _terminator(n_threads, _queues) {}
- ParallelTaskTerminator* terminator() { return _terminator.terminator(); }
- OopTaskQueueSet* queues() { return _queues; }
-};
-
-class CMSRefProcTaskProxy: public AbstractGangTaskWOopQueues {
- typedef AbstractRefProcTaskExecutor::ProcessTask ProcessTask;
- CMSCollector* _collector;
- CMSBitMap* _mark_bit_map;
- const MemRegion _span;
- ProcessTask& _task;
-
-public:
- CMSRefProcTaskProxy(ProcessTask& task,
- CMSCollector* collector,
- const MemRegion& span,
- CMSBitMap* mark_bit_map,
- AbstractWorkGang* workers,
- OopTaskQueueSet* task_queues):
- AbstractGangTaskWOopQueues("Process referents by policy in parallel",
- task_queues,
- workers->active_workers()),
- _collector(collector),
- _mark_bit_map(mark_bit_map),
- _span(span),
- _task(task)
- {
- assert(_collector->_span.equals(_span) && !_span.is_empty(),
- "Inconsistency in _span");
- }
-
- OopTaskQueueSet* task_queues() { return queues(); }
-
- OopTaskQueue* work_queue(int i) { return task_queues()->queue(i); }
-
- void do_work_steal(int i,
- CMSParDrainMarkingStackClosure* drain,
- CMSParKeepAliveClosure* keep_alive);
-
- virtual void work(uint worker_id);
-};
-
-void CMSRefProcTaskProxy::work(uint worker_id) {
- ResourceMark rm;
- HandleMark hm;
- assert(_collector->_span.equals(_span), "Inconsistency in _span");
- CMSParKeepAliveClosure par_keep_alive(_collector, _span,
- _mark_bit_map,
- work_queue(worker_id));
- CMSParDrainMarkingStackClosure par_drain_stack(_collector, _span,
- _mark_bit_map,
- work_queue(worker_id));
- CMSIsAliveClosure is_alive_closure(_span, _mark_bit_map);
- _task.work(worker_id, is_alive_closure, par_keep_alive, par_drain_stack);
- if (_task.marks_oops_alive()) {
- do_work_steal(worker_id, &par_drain_stack, &par_keep_alive);
- }
- assert(work_queue(worker_id)->size() == 0, "work_queue should be empty");
- assert(_collector->_overflow_list == NULL, "non-empty _overflow_list");
-}
-
-CMSParKeepAliveClosure::CMSParKeepAliveClosure(CMSCollector* collector,
- MemRegion span, CMSBitMap* bit_map, OopTaskQueue* work_queue):
- _span(span),
- _work_queue(work_queue),
- _bit_map(bit_map),
- _mark_and_push(collector, span, bit_map, work_queue),
- _low_water_mark(MIN2((work_queue->max_elems()/4),
- ((uint)CMSWorkQueueDrainThreshold * ParallelGCThreads)))
-{ }
-
-// . see if we can share work_queues with ParNew? XXX
-void CMSRefProcTaskProxy::do_work_steal(int i,
- CMSParDrainMarkingStackClosure* drain,
- CMSParKeepAliveClosure* keep_alive) {
- OopTaskQueue* work_q = work_queue(i);
- NOT_PRODUCT(int num_steals = 0;)
- oop obj_to_scan;
-
- while (true) {
- // Completely finish any left over work from (an) earlier round(s)
- drain->trim_queue(0);
- size_t num_from_overflow_list = MIN2((size_t)(work_q->max_elems() - work_q->size())/4,
- (size_t)ParGCDesiredObjsFromOverflowList);
- // Now check if there's any work in the overflow list
- // Passing ParallelGCThreads as the third parameter, no_of_gc_threads,
- // only affects the number of attempts made to get work from the
- // overflow list and does not affect the number of workers. Just
- // pass ParallelGCThreads so this behavior is unchanged.
- if (_collector->par_take_from_overflow_list(num_from_overflow_list,
- work_q,
- ParallelGCThreads)) {
- // Found something in global overflow list;
- // not yet ready to go stealing work from others.
- // We'd like to assert(work_q->size() != 0, ...)
- // because we just took work from the overflow list,
- // but of course we can't, since all of that might have
- // been already stolen from us.
- continue;
- }
- // Verify that we have no work before we resort to stealing
- assert(work_q->size() == 0, "Have work, shouldn't steal");
- // Try to steal from other queues that have work
- if (task_queues()->steal(i, /* reference */ obj_to_scan)) {
- NOT_PRODUCT(num_steals++;)
- assert(oopDesc::is_oop(obj_to_scan), "Oops, not an oop!");
- assert(_mark_bit_map->isMarked((HeapWord*)obj_to_scan), "Stole an unmarked oop?");
- // Do scanning work
- obj_to_scan->oop_iterate(keep_alive);
- // Loop around, finish this work, and try to steal some more
- } else if (terminator()->offer_termination()) {
- break; // nirvana from the infinite cycle
- }
- }
- log_develop_trace(gc, task)("\t(%d: stole %d oops)", i, num_steals);
-}
-
-void CMSRefProcTaskExecutor::execute(ProcessTask& task, uint ergo_workers) {
- CMSHeap* heap = CMSHeap::heap();
- WorkGang* workers = heap->workers();
- assert(workers != NULL, "Need parallel worker threads.");
- assert(workers->active_workers() == ergo_workers,
- "Ergonomically chosen workers (%u) must be equal to active workers (%u)",
- ergo_workers, workers->active_workers());
- CMSRefProcTaskProxy rp_task(task, &_collector,
- _collector.ref_processor_span(),
- _collector.markBitMap(),
- workers, _collector.task_queues());
- workers->run_task(&rp_task, workers->active_workers());
-}
-
-void CMSCollector::refProcessingWork() {
- ResourceMark rm;
- HandleMark hm;
-
- ReferenceProcessor* rp = ref_processor();
- assert(_span_based_discoverer.span().equals(_span), "Spans should be equal");
- assert(!rp->enqueuing_is_done(), "Enqueuing should not be complete");
- // Process weak references.
- rp->setup_policy(false);
- verify_work_stacks_empty();
-
- ReferenceProcessorPhaseTimes pt(_gc_timer_cm, rp->max_num_queues());
- {
- GCTraceTime(Debug, gc, phases) t("Reference Processing", _gc_timer_cm);
-
- // Setup keep_alive and complete closures.
- CMSKeepAliveClosure cmsKeepAliveClosure(this, _span, &_markBitMap,
- &_markStack, false /* !preclean */);
- CMSDrainMarkingStackClosure cmsDrainMarkingStackClosure(this,
- _span, &_markBitMap, &_markStack,
- &cmsKeepAliveClosure, false /* !preclean */);
-
- ReferenceProcessorStats stats;
- if (rp->processing_is_mt()) {
- // Set the degree of MT here. If the discovery is done MT, there
- // may have been a different number of threads doing the discovery
- // and a different number of discovered lists may have Ref objects.
- // That is OK as long as the Reference lists are balanced (see
- // balance_all_queues() and balance_queues()).
- CMSHeap* heap = CMSHeap::heap();
- uint active_workers = ParallelGCThreads;
- WorkGang* workers = heap->workers();
- if (workers != NULL) {
- active_workers = workers->active_workers();
- // The expectation is that active_workers will have already
- // been set to a reasonable value. If it has not been set,
- // investigate.
- assert(active_workers > 0, "Should have been set during scavenge");
- }
- rp->set_active_mt_degree(active_workers);
- CMSRefProcTaskExecutor task_executor(*this);
- stats = rp->process_discovered_references(&_is_alive_closure,
- &cmsKeepAliveClosure,
- &cmsDrainMarkingStackClosure,
- &task_executor,
- &pt);
- } else {
- stats = rp->process_discovered_references(&_is_alive_closure,
- &cmsKeepAliveClosure,
- &cmsDrainMarkingStackClosure,
- NULL,
- &pt);
- }
- _gc_tracer_cm->report_gc_reference_stats(stats);
- pt.print_all_references();
- }
-
- // This is the point where the entire marking should have completed.
- verify_work_stacks_empty();
-
- {
- GCTraceTime(Debug, gc, phases) t("Weak Processing", _gc_timer_cm);
- WeakProcessor::weak_oops_do(&_is_alive_closure, &do_nothing_cl);
- }
-
- if (should_unload_classes()) {
- {
- GCTraceTime(Debug, gc, phases) t("Class Unloading", _gc_timer_cm);
-
- // Unload classes and purge the SystemDictionary.
- bool purged_class = SystemDictionary::do_unloading(_gc_timer_cm);
-
- // Unload nmethods.
- CodeCache::do_unloading(&_is_alive_closure, purged_class);
-
- // Prune dead klasses from subklass/sibling/implementor lists.
- Klass::clean_weak_klass_links(purged_class);
-
- // Clean JVMCI metadata handles.
- JVMCI_ONLY(JVMCI::do_unloading(purged_class));
- }
- }
-
- // Restore any preserved marks as a result of mark stack or
- // work queue overflow
- restore_preserved_marks_if_any(); // done single-threaded for now
-
- rp->set_enqueuing_is_done(true);
- rp->verify_no_references_recorded();
-}
-
-#ifndef PRODUCT
-void CMSCollector::check_correct_thread_executing() {
- Thread* t = Thread::current();
- // Only the VM thread or the CMS thread should be here.
- assert(t->is_ConcurrentGC_thread() || t->is_VM_thread(),
- "Unexpected thread type");
- // If this is the vm thread, the foreground process
- // should not be waiting. Note that _foregroundGCIsActive is
- // true while the foreground collector is waiting.
- if (_foregroundGCShouldWait) {
- // We cannot be the VM thread
- assert(t->is_ConcurrentGC_thread(),
- "Should be CMS thread");
- } else {
- // We can be the CMS thread only if we are in a stop-world
- // phase of CMS collection.
- if (t->is_ConcurrentGC_thread()) {
- assert(_collectorState == InitialMarking ||
- _collectorState == FinalMarking,
- "Should be a stop-world phase");
- // The CMS thread should be holding the CMS_token.
- assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "Potential interference with concurrently "
- "executing VM thread");
- }
- }
-}
-#endif
-
-void CMSCollector::sweep() {
- assert(_collectorState == Sweeping, "just checking");
- check_correct_thread_executing();
- verify_work_stacks_empty();
- verify_overflow_empty();
- increment_sweep_count();
- TraceCMSMemoryManagerStats tms(_collectorState, CMSHeap::heap()->gc_cause());
-
- _inter_sweep_timer.stop();
- _inter_sweep_estimate.sample(_inter_sweep_timer.seconds());
-
- assert(!_intra_sweep_timer.is_active(), "Should not be active");
- _intra_sweep_timer.reset();
- _intra_sweep_timer.start();
- {
- GCTraceCPUTime tcpu;
- CMSPhaseAccounting pa(this, "Concurrent Sweep");
- // First sweep the old gen
- {
- CMSTokenSyncWithLocks ts(true, _cmsGen->freelistLock(),
- bitMapLock());
- sweepWork(_cmsGen);
- }
-
- // Update Universe::_heap_*_at_gc figures.
- // We need all the free list locks to make the abstract state
- // transition from Sweeping to Resetting. See detailed note
- // further below.
- {
- CMSTokenSyncWithLocks ts(true, _cmsGen->freelistLock());
-
- // Update heap occupancy information which is used as
- // input to soft ref clearing policy at the next gc.
- Universe::update_heap_info_at_gc();
-
- // recalculate CMS used space after CMS collection
- _cmsGen->cmsSpace()->recalculate_used_stable();
-
- _collectorState = Resizing;
- }
- }
- verify_work_stacks_empty();
- verify_overflow_empty();
-
- if (should_unload_classes()) {
- // Delay purge to the beginning of the next safepoint. Metaspace::contains
- // requires that the virtual spaces are stable and not deleted.
- ClassLoaderDataGraph::set_should_purge(true);
- }
-
- _intra_sweep_timer.stop();
- _intra_sweep_estimate.sample(_intra_sweep_timer.seconds());
-
- _inter_sweep_timer.reset();
- _inter_sweep_timer.start();
-
- // We need to use a monotonically non-decreasing time in ms
- // or we will see time-warp warnings and os::javaTimeMillis()
- // does not guarantee monotonicity.
- jlong now = os::javaTimeNanos() / NANOSECS_PER_MILLISEC;
- update_time_of_last_gc(now);
-
- // NOTE on abstract state transitions:
- // Mutators allocate-live and/or mark the mod-union table dirty
- // based on the state of the collection. The former is done in
- // the interval [Marking, Sweeping] and the latter in the interval
- // [Marking, Sweeping). Thus the transitions into the Marking state
- // and out of the Sweeping state must be synchronously visible
- // globally to the mutators.
- // The transition into the Marking state happens with the world
- // stopped so the mutators will globally see it. Sweeping is
- // done asynchronously by the background collector so the transition
- // from the Sweeping state to the Resizing state must be done
- // under the freelistLock (as is the check for whether to
- // allocate-live and whether to dirty the mod-union table).
- assert(_collectorState == Resizing, "Change of collector state to"
- " Resizing must be done under the freelistLocks (plural)");
-
- // Now that sweeping has been completed, we clear
- // the incremental_collection_failed flag,
- // thus inviting a younger gen collection to promote into
- // this generation. If such a promotion may still fail,
- // the flag will be set again when a young collection is
- // attempted.
- CMSHeap* heap = CMSHeap::heap();
- heap->clear_incremental_collection_failed(); // Worth retrying as fresh space may have been freed up
- heap->update_full_collections_completed(_collection_count_start);
-}
-
-// FIX ME!!! Looks like this belongs in CFLSpace, with
-// CMSGen merely delegating to it.
-void ConcurrentMarkSweepGeneration::setNearLargestChunk() {
- double nearLargestPercent = FLSLargestBlockCoalesceProximity;
- HeapWord* minAddr = _cmsSpace->bottom();
- HeapWord* largestAddr =
- (HeapWord*) _cmsSpace->dictionary()->find_largest_dict();
- if (largestAddr == NULL) {
- // The dictionary appears to be empty. In this case
- // try to coalesce at the end of the heap.
- largestAddr = _cmsSpace->end();
- }
- size_t largestOffset = pointer_delta(largestAddr, minAddr);
- size_t nearLargestOffset =
- (size_t)((double)largestOffset * nearLargestPercent) - MinChunkSize;
- log_debug(gc, freelist)("CMS: Large Block: " PTR_FORMAT "; Proximity: " PTR_FORMAT " -> " PTR_FORMAT,
- p2i(largestAddr), p2i(_cmsSpace->nearLargestChunk()), p2i(minAddr + nearLargestOffset));
- _cmsSpace->set_nearLargestChunk(minAddr + nearLargestOffset);
-}
-
-bool ConcurrentMarkSweepGeneration::isNearLargestChunk(HeapWord* addr) {
- return addr >= _cmsSpace->nearLargestChunk();
-}
-
-FreeChunk* ConcurrentMarkSweepGeneration::find_chunk_at_end() {
- return _cmsSpace->find_chunk_at_end();
-}
-
-void ConcurrentMarkSweepGeneration::update_gc_stats(Generation* current_generation,
- bool full) {
- // If the young generation has been collected, gather any statistics
- // that are of interest at this point.
- bool current_is_young = CMSHeap::heap()->is_young_gen(current_generation);
- if (!full && current_is_young) {
- // Gather statistics on the young generation collection.
- collector()->stats().record_gc0_end(used());
- }
- _cmsSpace->recalculate_used_stable();
-}
-
-void CMSCollector::sweepWork(ConcurrentMarkSweepGeneration* old_gen) {
- // We iterate over the space(s) underlying this generation,
- // checking the mark bit map to see if the bits corresponding
- // to specific blocks are marked or not. Blocks that are
- // marked are live and are not swept up. All remaining blocks
- // are swept up, with coalescing on-the-fly as we sweep up
- // contiguous free and/or garbage blocks:
- // We need to ensure that the sweeper synchronizes with allocators
- // and stop-the-world collectors. In particular, the following
- // locks are used:
- // . CMS token: if this is held, a stop the world collection cannot occur
- // . freelistLock: if this is held no allocation can occur from this
- // generation by another thread
- // . bitMapLock: if this is held, no other thread can access or update
- //
-
- // Note that we need to hold the freelistLock if we use
- // block iterate below; else the iterator might go awry if
- // a mutator (or promotion) causes block contents to change
- // (for instance if the allocator divvies up a block).
- // If we hold the free list lock, for all practical purposes
- // young generation GC's can't occur (they'll usually need to
- // promote), so we might as well prevent all young generation
- // GC's while we do a sweeping step. For the same reason, we might
- // as well take the bit map lock for the entire duration
-
- // check that we hold the requisite locks
- assert(have_cms_token(), "Should hold cms token");
- assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), "Should possess CMS token to sweep");
- assert_lock_strong(old_gen->freelistLock());
- assert_lock_strong(bitMapLock());
-
- assert(!_inter_sweep_timer.is_active(), "Was switched off in an outer context");
- assert(_intra_sweep_timer.is_active(), "Was switched on in an outer context");
- old_gen->cmsSpace()->beginSweepFLCensus((float)(_inter_sweep_timer.seconds()),
- _inter_sweep_estimate.padded_average(),
- _intra_sweep_estimate.padded_average());
- old_gen->setNearLargestChunk();
-
- {
- SweepClosure sweepClosure(this, old_gen, &_markBitMap, CMSYield);
- old_gen->cmsSpace()->blk_iterate_careful(&sweepClosure);
- // We need to free-up/coalesce garbage/blocks from a
- // co-terminal free run. This is done in the SweepClosure
- // destructor; so, do not remove this scope, else the
- // end-of-sweep-census below will be off by a little bit.
- }
- old_gen->cmsSpace()->sweep_completed();
- old_gen->cmsSpace()->endSweepFLCensus(sweep_count());
- if (should_unload_classes()) { // unloaded classes this cycle,
- _concurrent_cycles_since_last_unload = 0; // ... reset count
- } else { // did not unload classes,
- _concurrent_cycles_since_last_unload++; // ... increment count
- }
-}
-
-// Reset CMS data structures (for now just the marking bit map)
-// preparatory for the next cycle.
-void CMSCollector::reset_concurrent() {
- CMSTokenSyncWithLocks ts(true, bitMapLock());
-
- // If the state is not "Resetting", the foreground thread
- // has done a collection and the resetting.
- if (_collectorState != Resetting) {
- assert(_collectorState == Idling, "The state should only change"
- " because the foreground collector has finished the collection");
- return;
- }
-
- {
- // Clear the mark bitmap (no grey objects to start with)
- // for the next cycle.
- GCTraceCPUTime tcpu;
- CMSPhaseAccounting cmspa(this, "Concurrent Reset");
-
- HeapWord* curAddr = _markBitMap.startWord();
- while (curAddr < _markBitMap.endWord()) {
- size_t remaining = pointer_delta(_markBitMap.endWord(), curAddr);
- MemRegion chunk(curAddr, MIN2(CMSBitMapYieldQuantum, remaining));
- _markBitMap.clear_large_range(chunk);
- if (ConcurrentMarkSweepThread::should_yield() &&
- !foregroundGCIsActive() &&
- CMSYield) {
- assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "CMS thread should hold CMS token");
- assert_lock_strong(bitMapLock());
- bitMapLock()->unlock();
- ConcurrentMarkSweepThread::desynchronize(true);
- stopTimer();
- incrementYields();
-
- // See the comment in coordinator_yield()
- for (unsigned i = 0; i < CMSYieldSleepCount &&
- ConcurrentMarkSweepThread::should_yield() &&
- !CMSCollector::foregroundGCIsActive(); ++i) {
- os::naked_short_sleep(1);
- }
-
- ConcurrentMarkSweepThread::synchronize(true);
- bitMapLock()->lock_without_safepoint_check();
- startTimer();
- }
- curAddr = chunk.end();
- }
- // A successful mostly concurrent collection has been done.
- // Because only the full (i.e., concurrent mode failure) collections
- // are being measured for gc overhead limits, clean the "near" flag
- // and count.
- size_policy()->reset_gc_overhead_limit_count();
- _collectorState = Idling;
- }
-
- register_gc_end();
-}
-
-// Same as above but for STW paths
-void CMSCollector::reset_stw() {
- // already have the lock
- assert(_collectorState == Resetting, "just checking");
- assert_lock_strong(bitMapLock());
- GCIdMark gc_id_mark(_cmsThread->gc_id());
- _markBitMap.clear_all();
- _collectorState = Idling;
- register_gc_end();
-}
-
-void CMSCollector::do_CMS_operation(CMS_op_type op, GCCause::Cause gc_cause) {
- GCTraceCPUTime tcpu;
- TraceCollectorStats tcs_cgc(cgc_counters());
-
- switch (op) {
- case CMS_op_checkpointRootsInitial: {
- GCTraceTime(Info, gc) t("Pause Initial Mark", NULL, GCCause::_no_gc, true);
- SvcGCMarker sgcm(SvcGCMarker::CONCURRENT);
- checkpointRootsInitial();
- break;
- }
- case CMS_op_checkpointRootsFinal: {
- GCTraceTime(Info, gc) t("Pause Remark", NULL, GCCause::_no_gc, true);
- SvcGCMarker sgcm(SvcGCMarker::CONCURRENT);
- checkpointRootsFinal();
- break;
- }
- default:
- fatal("No such CMS_op");
- }
-}
-
-#ifndef PRODUCT
-size_t const CMSCollector::skip_header_HeapWords() {
- return FreeChunk::header_size();
-}
-
-// Try and collect here conditions that should hold when
-// CMS thread is exiting. The idea is that the foreground GC
-// thread should not be blocked if it wants to terminate
-// the CMS thread and yet continue to run the VM for a while
-// after that.
-void CMSCollector::verify_ok_to_terminate() const {
- assert(Thread::current()->is_ConcurrentGC_thread(),
- "should be called by CMS thread");
- assert(!_foregroundGCShouldWait, "should be false");
- // We could check here that all the various low-level locks
- // are not held by the CMS thread, but that is overkill; see
- // also CMSThread::verify_ok_to_terminate() where the CGC_lock
- // is checked.
-}
-#endif
-
-size_t CMSCollector::block_size_using_printezis_bits(HeapWord* addr) const {
- assert(_markBitMap.isMarked(addr) && _markBitMap.isMarked(addr + 1),
- "missing Printezis mark?");
- HeapWord* nextOneAddr = _markBitMap.getNextMarkedWordAddress(addr + 2);
- size_t size = pointer_delta(nextOneAddr + 1, addr);
- assert(size == CompactibleFreeListSpace::adjustObjectSize(size),
- "alignment problem");
- assert(size >= 3, "Necessary for Printezis marks to work");
- return size;
-}
-
-// A variant of the above (block_size_using_printezis_bits()) except
-// that we return 0 if the P-bits are not yet set.
-size_t CMSCollector::block_size_if_printezis_bits(HeapWord* addr) const {
- if (_markBitMap.isMarked(addr + 1)) {
- assert(_markBitMap.isMarked(addr), "P-bit can be set only for marked objects");
- HeapWord* nextOneAddr = _markBitMap.getNextMarkedWordAddress(addr + 2);
- size_t size = pointer_delta(nextOneAddr + 1, addr);
- assert(size == CompactibleFreeListSpace::adjustObjectSize(size),
- "alignment problem");
- assert(size >= 3, "Necessary for Printezis marks to work");
- return size;
- }
- return 0;
-}
-
-HeapWord* CMSCollector::next_card_start_after_block(HeapWord* addr) const {
- size_t sz = 0;
- oop p = (oop)addr;
- if (p->klass_or_null_acquire() != NULL) {
- sz = CompactibleFreeListSpace::adjustObjectSize(p->size());
- } else {
- sz = block_size_using_printezis_bits(addr);
- }
- assert(sz > 0, "size must be nonzero");
- HeapWord* next_block = addr + sz;
- HeapWord* next_card = align_up(next_block, CardTable::card_size);
- assert(align_down((uintptr_t)addr, CardTable::card_size) <
- align_down((uintptr_t)next_card, CardTable::card_size),
- "must be different cards");
- return next_card;
-}
-
-
-// CMS Bit Map Wrapper /////////////////////////////////////////
-
-// Construct a CMS bit map infrastructure, but don't create the
-// bit vector itself. That is done by a separate call CMSBitMap::allocate()
-// further below.
-CMSBitMap::CMSBitMap(int shifter, int mutex_rank, const char* mutex_name):
- _shifter(shifter),
- _bm(),
- _lock(mutex_rank >= 0 ? new Mutex(mutex_rank, mutex_name, true,
- Monitor::_safepoint_check_never) : NULL)
-{
- _bmStartWord = 0;
- _bmWordSize = 0;
-}
-
-bool CMSBitMap::allocate(MemRegion mr) {
- _bmStartWord = mr.start();
- _bmWordSize = mr.word_size();
- ReservedSpace brs(ReservedSpace::allocation_align_size_up(
- (_bmWordSize >> (_shifter + LogBitsPerByte)) + 1));
- if (!brs.is_reserved()) {
- log_warning(gc)("CMS bit map allocation failure");
- return false;
- }
- // For now we'll just commit all of the bit map up front.
- // Later on we'll try to be more parsimonious with swap.
- if (!_virtual_space.initialize(brs, brs.size())) {
- log_warning(gc)("CMS bit map backing store failure");
- return false;
- }
- assert(_virtual_space.committed_size() == brs.size(),
- "didn't reserve backing store for all of CMS bit map?");
- assert(_virtual_space.committed_size() << (_shifter + LogBitsPerByte) >=
- _bmWordSize, "inconsistency in bit map sizing");
- _bm = BitMapView((BitMap::bm_word_t*)_virtual_space.low(), _bmWordSize >> _shifter);
-
- // bm.clear(); // can we rely on getting zero'd memory? verify below
- assert(isAllClear(),
- "Expected zero'd memory from ReservedSpace constructor");
- assert(_bm.size() == heapWordDiffToOffsetDiff(sizeInWords()),
- "consistency check");
- return true;
-}
-
-void CMSBitMap::dirty_range_iterate_clear(MemRegion mr, MemRegionClosure* cl) {
- HeapWord *next_addr, *end_addr, *last_addr;
- assert_locked();
- assert(covers(mr), "out-of-range error");
- // XXX assert that start and end are appropriately aligned
- for (next_addr = mr.start(), end_addr = mr.end();
- next_addr < end_addr; next_addr = last_addr) {
- MemRegion dirty_region = getAndClearMarkedRegion(next_addr, end_addr);
- last_addr = dirty_region.end();
- if (!dirty_region.is_empty()) {
- cl->do_MemRegion(dirty_region);
- } else {
- assert(last_addr == end_addr, "program logic");
- return;
- }
- }
-}
-
-void CMSBitMap::print_on_error(outputStream* st, const char* prefix) const {
- _bm.print_on_error(st, prefix);
-}
-
-#ifndef PRODUCT
-void CMSBitMap::assert_locked() const {
- CMSLockVerifier::assert_locked(lock());
-}
-
-bool CMSBitMap::covers(MemRegion mr) const {
- // assert(_bm.map() == _virtual_space.low(), "map inconsistency");
- assert((size_t)_bm.size() == (_bmWordSize >> _shifter),
- "size inconsistency");
- return (mr.start() >= _bmStartWord) &&
- (mr.end() <= endWord());
-}
-
-bool CMSBitMap::covers(HeapWord* start, size_t size) const {
- return (start >= _bmStartWord && (start + size) <= endWord());
-}
-
-void CMSBitMap::verifyNoOneBitsInRange(HeapWord* left, HeapWord* right) {
- // verify that there are no 1 bits in the interval [left, right)
- FalseBitMapClosure falseBitMapClosure;
- iterate(&falseBitMapClosure, left, right);
-}
-
-void CMSBitMap::region_invariant(MemRegion mr)
-{
- assert_locked();
- // mr = mr.intersection(MemRegion(_bmStartWord, _bmWordSize));
- assert(!mr.is_empty(), "unexpected empty region");
- assert(covers(mr), "mr should be covered by bit map");
- // convert address range into offset range
- size_t start_ofs = heapWordToOffset(mr.start());
- // Make sure that end() is appropriately aligned
- assert(mr.end() == align_up(mr.end(), (1 << (_shifter+LogHeapWordSize))),
- "Misaligned mr.end()");
- size_t end_ofs = heapWordToOffset(mr.end());
- assert(end_ofs > start_ofs, "Should mark at least one bit");
-}
-
-#endif
-
-bool CMSMarkStack::allocate(size_t size) {
- // allocate a stack of the requisite depth
- ReservedSpace rs(ReservedSpace::allocation_align_size_up(
- size * sizeof(oop)));
- if (!rs.is_reserved()) {
- log_warning(gc)("CMSMarkStack allocation failure");
- return false;
- }
- if (!_virtual_space.initialize(rs, rs.size())) {
- log_warning(gc)("CMSMarkStack backing store failure");
- return false;
- }
- assert(_virtual_space.committed_size() == rs.size(),
- "didn't reserve backing store for all of CMS stack?");
- _base = (oop*)(_virtual_space.low());
- _index = 0;
- _capacity = size;
- NOT_PRODUCT(_max_depth = 0);
- return true;
-}
-
-// XXX FIX ME !!! In the MT case we come in here holding a
-// leaf lock. For printing we need to take a further lock
-// which has lower rank. We need to recalibrate the two
-// lock-ranks involved in order to be able to print the
-// messages below. (Or defer the printing to the caller.
-// For now we take the expedient path of just disabling the
-// messages for the problematic case.)
-void CMSMarkStack::expand() {
- assert(_capacity <= MarkStackSizeMax, "stack bigger than permitted");
- if (_capacity == MarkStackSizeMax) {
- if (_hit_limit++ == 0 && !CMSConcurrentMTEnabled) {
- // We print a warning message only once per CMS cycle.
- log_debug(gc)(" (benign) Hit CMSMarkStack max size limit");
- }
- return;
- }
- // Double capacity if possible
- size_t new_capacity = MIN2(_capacity*2, MarkStackSizeMax);
- // Do not give up existing stack until we have managed to
- // get the double capacity that we desired.
- ReservedSpace rs(ReservedSpace::allocation_align_size_up(
- new_capacity * sizeof(oop)));
- if (rs.is_reserved()) {
- // Release the backing store associated with old stack
- _virtual_space.release();
- // Reinitialize virtual space for new stack
- if (!_virtual_space.initialize(rs, rs.size())) {
- fatal("Not enough swap for expanded marking stack");
- }
- _base = (oop*)(_virtual_space.low());
- _index = 0;
- _capacity = new_capacity;
- } else if (_failed_double++ == 0 && !CMSConcurrentMTEnabled) {
- // Failed to double capacity, continue;
- // we print a detail message only once per CMS cycle.
- log_debug(gc)(" (benign) Failed to expand marking stack from " SIZE_FORMAT "K to " SIZE_FORMAT "K",
- _capacity / K, new_capacity / K);
- }
-}
-
-
-// Closures
-// XXX: there seems to be a lot of code duplication here;
-// should refactor and consolidate common code.
-
-// This closure is used to mark refs into the CMS generation in
-// the CMS bit map. Called at the first checkpoint. This closure
-// assumes that we do not need to re-mark dirty cards; if the CMS
-// generation on which this is used is not an oldest
-// generation then this will lose younger_gen cards!
-
-MarkRefsIntoClosure::MarkRefsIntoClosure(
- MemRegion span, CMSBitMap* bitMap):
- _span(span),
- _bitMap(bitMap)
-{
- assert(ref_discoverer() == NULL, "deliberately left NULL");
- assert(_bitMap->covers(_span), "_bitMap/_span mismatch");
-}
-
-void MarkRefsIntoClosure::do_oop(oop obj) {
- // if p points into _span, then mark corresponding bit in _markBitMap
- assert(oopDesc::is_oop(obj), "expected an oop");
- HeapWord* addr = (HeapWord*)obj;
- if (_span.contains(addr)) {
- // this should be made more efficient
- _bitMap->mark(addr);
- }
-}
-
-ParMarkRefsIntoClosure::ParMarkRefsIntoClosure(
- MemRegion span, CMSBitMap* bitMap):
- _span(span),
- _bitMap(bitMap)
-{
- assert(ref_discoverer() == NULL, "deliberately left NULL");
- assert(_bitMap->covers(_span), "_bitMap/_span mismatch");
-}
-
-void ParMarkRefsIntoClosure::do_oop(oop obj) {
- // if p points into _span, then mark corresponding bit in _markBitMap
- assert(oopDesc::is_oop(obj), "expected an oop");
- HeapWord* addr = (HeapWord*)obj;
- if (_span.contains(addr)) {
- // this should be made more efficient
- _bitMap->par_mark(addr);
- }
-}
-
-// A variant of the above, used for CMS marking verification.
-MarkRefsIntoVerifyClosure::MarkRefsIntoVerifyClosure(
- MemRegion span, CMSBitMap* verification_bm, CMSBitMap* cms_bm):
- _span(span),
- _verification_bm(verification_bm),
- _cms_bm(cms_bm)
-{
- assert(ref_discoverer() == NULL, "deliberately left NULL");
- assert(_verification_bm->covers(_span), "_verification_bm/_span mismatch");
-}
-
-void MarkRefsIntoVerifyClosure::do_oop(oop obj) {
- // if p points into _span, then mark corresponding bit in _markBitMap
- assert(oopDesc::is_oop(obj), "expected an oop");
- HeapWord* addr = (HeapWord*)obj;
- if (_span.contains(addr)) {
- _verification_bm->mark(addr);
- if (!_cms_bm->isMarked(addr)) {
- Log(gc, verify) log;
- ResourceMark rm;
- LogStream ls(log.error());
- oop(addr)->print_on(&ls);
- log.error(" (" INTPTR_FORMAT " should have been marked)", p2i(addr));
- fatal("... aborting");
- }
- }
-}
-
-//////////////////////////////////////////////////
-// MarkRefsIntoAndScanClosure
-//////////////////////////////////////////////////
-
-MarkRefsIntoAndScanClosure::MarkRefsIntoAndScanClosure(MemRegion span,
- ReferenceDiscoverer* rd,
- CMSBitMap* bit_map,
- CMSBitMap* mod_union_table,
- CMSMarkStack* mark_stack,
- CMSCollector* collector,
- bool should_yield,
- bool concurrent_precleaning):
- _span(span),
- _bit_map(bit_map),
- _mark_stack(mark_stack),
- _pushAndMarkClosure(collector, span, rd, bit_map, mod_union_table,
- mark_stack, concurrent_precleaning),
- _collector(collector),
- _freelistLock(NULL),
- _yield(should_yield),
- _concurrent_precleaning(concurrent_precleaning)
-{
- // FIXME: Should initialize in base class constructor.
- assert(rd != NULL, "ref_discoverer shouldn't be NULL");
- set_ref_discoverer_internal(rd);
-}
-
-// This closure is used to mark refs into the CMS generation at the
-// second (final) checkpoint, and to scan and transitively follow
-// the unmarked oops. It is also used during the concurrent precleaning
-// phase while scanning objects on dirty cards in the CMS generation.
-// The marks are made in the marking bit map and the marking stack is
-// used for keeping the (newly) grey objects during the scan.
-// The parallel version (Par_...) appears further below.
-void MarkRefsIntoAndScanClosure::do_oop(oop obj) {
- if (obj != NULL) {
- assert(oopDesc::is_oop(obj), "expected an oop");
- HeapWord* addr = (HeapWord*)obj;
- assert(_mark_stack->isEmpty(), "pre-condition (eager drainage)");
- assert(_collector->overflow_list_is_empty(),
- "overflow list should be empty");
- if (_span.contains(addr) &&
- !_bit_map->isMarked(addr)) {
- // mark bit map (object is now grey)
- _bit_map->mark(addr);
- // push on marking stack (stack should be empty), and drain the
- // stack by applying this closure to the oops in the oops popped
- // from the stack (i.e. blacken the grey objects)
- bool res = _mark_stack->push(obj);
- assert(res, "Should have space to push on empty stack");
- do {
- oop new_oop = _mark_stack->pop();
- assert(new_oop != NULL && oopDesc::is_oop(new_oop), "Expected an oop");
- assert(_bit_map->isMarked((HeapWord*)new_oop),
- "only grey objects on this stack");
- // iterate over the oops in this oop, marking and pushing
- // the ones in CMS heap (i.e. in _span).
- new_oop->oop_iterate(&_pushAndMarkClosure);
- // check if it's time to yield
- do_yield_check();
- } while (!_mark_stack->isEmpty() ||
- (!_concurrent_precleaning && take_from_overflow_list()));
- // if marking stack is empty, and we are not doing this
- // during precleaning, then check the overflow list
- }
- assert(_mark_stack->isEmpty(), "post-condition (eager drainage)");
- assert(_collector->overflow_list_is_empty(),
- "overflow list was drained above");
-
- assert(_collector->no_preserved_marks(),
- "All preserved marks should have been restored above");
- }
-}
-
-void MarkRefsIntoAndScanClosure::do_yield_work() {
- assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "CMS thread should hold CMS token");
- assert_lock_strong(_freelistLock);
- assert_lock_strong(_bit_map->lock());
- // relinquish the free_list_lock and bitMaplock()
- _bit_map->lock()->unlock();
- _freelistLock->unlock();
- ConcurrentMarkSweepThread::desynchronize(true);
- _collector->stopTimer();
- _collector->incrementYields();
-
- // See the comment in coordinator_yield()
- for (unsigned i = 0;
- i < CMSYieldSleepCount &&
- ConcurrentMarkSweepThread::should_yield() &&
- !CMSCollector::foregroundGCIsActive();
- ++i) {
- os::naked_short_sleep(1);
- }
-
- ConcurrentMarkSweepThread::synchronize(true);
- _freelistLock->lock_without_safepoint_check();
- _bit_map->lock()->lock_without_safepoint_check();
- _collector->startTimer();
-}
-
-///////////////////////////////////////////////////////////
-// ParMarkRefsIntoAndScanClosure: a parallel version of
-// MarkRefsIntoAndScanClosure
-///////////////////////////////////////////////////////////
-ParMarkRefsIntoAndScanClosure::ParMarkRefsIntoAndScanClosure(
- CMSCollector* collector, MemRegion span, ReferenceDiscoverer* rd,
- CMSBitMap* bit_map, OopTaskQueue* work_queue):
- _span(span),
- _bit_map(bit_map),
- _work_queue(work_queue),
- _low_water_mark(MIN2((work_queue->max_elems()/4),
- ((uint)CMSWorkQueueDrainThreshold * ParallelGCThreads))),
- _parPushAndMarkClosure(collector, span, rd, bit_map, work_queue)
-{
- // FIXME: Should initialize in base class constructor.
- assert(rd != NULL, "ref_discoverer shouldn't be NULL");
- set_ref_discoverer_internal(rd);
-}
-
-// This closure is used to mark refs into the CMS generation at the
-// second (final) checkpoint, and to scan and transitively follow
-// the unmarked oops. The marks are made in the marking bit map and
-// the work_queue is used for keeping the (newly) grey objects during
-// the scan phase whence they are also available for stealing by parallel
-// threads. Since the marking bit map is shared, updates are
-// synchronized (via CAS).
-void ParMarkRefsIntoAndScanClosure::do_oop(oop obj) {
- if (obj != NULL) {
- // Ignore mark word because this could be an already marked oop
- // that may be chained at the end of the overflow list.
- assert(oopDesc::is_oop(obj, true), "expected an oop");
- HeapWord* addr = (HeapWord*)obj;
- if (_span.contains(addr) &&
- !_bit_map->isMarked(addr)) {
- // mark bit map (object will become grey):
- // It is possible for several threads to be
- // trying to "claim" this object concurrently;
- // the unique thread that succeeds in marking the
- // object first will do the subsequent push on
- // to the work queue (or overflow list).
- if (_bit_map->par_mark(addr)) {
- // push on work_queue (which may not be empty), and trim the
- // queue to an appropriate length by applying this closure to
- // the oops in the oops popped from the stack (i.e. blacken the
- // grey objects)
- bool res = _work_queue->push(obj);
- assert(res, "Low water mark should be less than capacity?");
- trim_queue(_low_water_mark);
- } // Else, another thread claimed the object
- }
- }
-}
-
-// This closure is used to rescan the marked objects on the dirty cards
-// in the mod union table and the card table proper.
-size_t ScanMarkedObjectsAgainCarefullyClosure::do_object_careful_m(
- oop p, MemRegion mr) {
-
- size_t size = 0;
- HeapWord* addr = (HeapWord*)p;
- DEBUG_ONLY(_collector->verify_work_stacks_empty();)
- assert(_span.contains(addr), "we are scanning the CMS generation");
- // check if it's time to yield
- if (do_yield_check()) {
- // We yielded for some foreground stop-world work,
- // and we have been asked to abort this ongoing preclean cycle.
- return 0;
- }
- if (_bitMap->isMarked(addr)) {
- // it's marked; is it potentially uninitialized?
- if (p->klass_or_null_acquire() != NULL) {
- // an initialized object; ignore mark word in verification below
- // since we are running concurrent with mutators
- assert(oopDesc::is_oop(p, true), "should be an oop");
- if (p->is_objArray()) {
- // objArrays are precisely marked; restrict scanning
- // to dirty cards only.
- size = CompactibleFreeListSpace::adjustObjectSize(
- p->oop_iterate_size(_scanningClosure, mr));
- } else {
- // A non-array may have been imprecisely marked; we need
- // to scan object in its entirety.
- size = CompactibleFreeListSpace::adjustObjectSize(
- p->oop_iterate_size(_scanningClosure));
- }
- #ifdef ASSERT
- size_t direct_size =
- CompactibleFreeListSpace::adjustObjectSize(p->size());
- assert(size == direct_size, "Inconsistency in size");
- assert(size >= 3, "Necessary for Printezis marks to work");
- HeapWord* start_pbit = addr + 1;
- HeapWord* end_pbit = addr + size - 1;
- assert(_bitMap->isMarked(start_pbit) == _bitMap->isMarked(end_pbit),
- "inconsistent Printezis mark");
- // Verify inner mark bits (between Printezis bits) are clear,
- // but don't repeat if there are multiple dirty regions for
- // the same object, to avoid potential O(N^2) performance.
- if (addr != _last_scanned_object) {
- _bitMap->verifyNoOneBitsInRange(start_pbit + 1, end_pbit);
- _last_scanned_object = addr;
- }
- #endif // ASSERT
- } else {
- // An uninitialized object.
- assert(_bitMap->isMarked(addr+1), "missing Printezis mark?");
- HeapWord* nextOneAddr = _bitMap->getNextMarkedWordAddress(addr + 2);
- size = pointer_delta(nextOneAddr + 1, addr);
- assert(size == CompactibleFreeListSpace::adjustObjectSize(size),
- "alignment problem");
- // Note that pre-cleaning needn't redirty the card. OopDesc::set_klass()
- // will dirty the card when the klass pointer is installed in the
- // object (signaling the completion of initialization).
- }
- } else {
- // Either a not yet marked object or an uninitialized object
- if (p->klass_or_null_acquire() == NULL) {
- // An uninitialized object, skip to the next card, since
- // we may not be able to read its P-bits yet.
- assert(size == 0, "Initial value");
- } else {
- // An object not (yet) reached by marking: we merely need to
- // compute its size so as to go look at the next block.
- assert(oopDesc::is_oop(p, true), "should be an oop");
- size = CompactibleFreeListSpace::adjustObjectSize(p->size());
- }
- }
- DEBUG_ONLY(_collector->verify_work_stacks_empty();)
- return size;
-}
-
-void ScanMarkedObjectsAgainCarefullyClosure::do_yield_work() {
- assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "CMS thread should hold CMS token");
- assert_lock_strong(_freelistLock);
- assert_lock_strong(_bitMap->lock());
- // relinquish the free_list_lock and bitMaplock()
- _bitMap->lock()->unlock();
- _freelistLock->unlock();
- ConcurrentMarkSweepThread::desynchronize(true);
- _collector->stopTimer();
- _collector->incrementYields();
-
- // See the comment in coordinator_yield()
- for (unsigned i = 0; i < CMSYieldSleepCount &&
- ConcurrentMarkSweepThread::should_yield() &&
- !CMSCollector::foregroundGCIsActive(); ++i) {
- os::naked_short_sleep(1);
- }
-
- ConcurrentMarkSweepThread::synchronize(true);
- _freelistLock->lock_without_safepoint_check();
- _bitMap->lock()->lock_without_safepoint_check();
- _collector->startTimer();
-}
-
-
-//////////////////////////////////////////////////////////////////
-// SurvivorSpacePrecleanClosure
-//////////////////////////////////////////////////////////////////
-// This (single-threaded) closure is used to preclean the oops in
-// the survivor spaces.
-size_t SurvivorSpacePrecleanClosure::do_object_careful(oop p) {
-
- HeapWord* addr = (HeapWord*)p;
- DEBUG_ONLY(_collector->verify_work_stacks_empty();)
- assert(!_span.contains(addr), "we are scanning the survivor spaces");
- assert(p->klass_or_null() != NULL, "object should be initialized");
- // an initialized object; ignore mark word in verification below
- // since we are running concurrent with mutators
- assert(oopDesc::is_oop(p, true), "should be an oop");
- // Note that we do not yield while we iterate over
- // the interior oops of p, pushing the relevant ones
- // on our marking stack.
- size_t size = p->oop_iterate_size(_scanning_closure);
- do_yield_check();
- // Observe that below, we do not abandon the preclean
- // phase as soon as we should; rather we empty the
- // marking stack before returning. This is to satisfy
- // some existing assertions. In general, it may be a
- // good idea to abort immediately and complete the marking
- // from the grey objects at a later time.
- while (!_mark_stack->isEmpty()) {
- oop new_oop = _mark_stack->pop();
- assert(new_oop != NULL && oopDesc::is_oop(new_oop), "Expected an oop");
- assert(_bit_map->isMarked((HeapWord*)new_oop),
- "only grey objects on this stack");
- // iterate over the oops in this oop, marking and pushing
- // the ones in CMS heap (i.e. in _span).
- new_oop->oop_iterate(_scanning_closure);
- // check if it's time to yield
- do_yield_check();
- }
- unsigned int after_count =
- CMSHeap::heap()->total_collections();
- bool abort = (_before_count != after_count) ||
- _collector->should_abort_preclean();
- return abort ? 0 : size;
-}
-
-void SurvivorSpacePrecleanClosure::do_yield_work() {
- assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "CMS thread should hold CMS token");
- assert_lock_strong(_bit_map->lock());
- // Relinquish the bit map lock
- _bit_map->lock()->unlock();
- ConcurrentMarkSweepThread::desynchronize(true);
- _collector->stopTimer();
- _collector->incrementYields();
-
- // See the comment in coordinator_yield()
- for (unsigned i = 0; i < CMSYieldSleepCount &&
- ConcurrentMarkSweepThread::should_yield() &&
- !CMSCollector::foregroundGCIsActive(); ++i) {
- os::naked_short_sleep(1);
- }
-
- ConcurrentMarkSweepThread::synchronize(true);
- _bit_map->lock()->lock_without_safepoint_check();
- _collector->startTimer();
-}
-
-// This closure is used to rescan the marked objects on the dirty cards
-// in the mod union table and the card table proper. In the parallel
-// case, although the bitMap is shared, we do a single read so the
-// isMarked() query is "safe".
-bool ScanMarkedObjectsAgainClosure::do_object_bm(oop p, MemRegion mr) {
- // Ignore mark word because we are running concurrent with mutators
- assert(oopDesc::is_oop_or_null(p, true), "Expected an oop or NULL at " PTR_FORMAT, p2i(p));
- HeapWord* addr = (HeapWord*)p;
- assert(_span.contains(addr), "we are scanning the CMS generation");
- bool is_obj_array = false;
- #ifdef ASSERT
- if (!_parallel) {
- assert(_mark_stack->isEmpty(), "pre-condition (eager drainage)");
- assert(_collector->overflow_list_is_empty(),
- "overflow list should be empty");
-
- }
- #endif // ASSERT
- if (_bit_map->isMarked(addr)) {
- // Obj arrays are precisely marked, non-arrays are not;
- // so we scan objArrays precisely and non-arrays in their
- // entirety.
- if (p->is_objArray()) {
- is_obj_array = true;
- if (_parallel) {
- p->oop_iterate(_par_scan_closure, mr);
- } else {
- p->oop_iterate(_scan_closure, mr);
- }
- } else {
- if (_parallel) {
- p->oop_iterate(_par_scan_closure);
- } else {
- p->oop_iterate(_scan_closure);
- }
- }
- }
- #ifdef ASSERT
- if (!_parallel) {
- assert(_mark_stack->isEmpty(), "post-condition (eager drainage)");
- assert(_collector->overflow_list_is_empty(),
- "overflow list should be empty");
-
- }
- #endif // ASSERT
- return is_obj_array;
-}
-
-MarkFromRootsClosure::MarkFromRootsClosure(CMSCollector* collector,
- MemRegion span,
- CMSBitMap* bitMap, CMSMarkStack* markStack,
- bool should_yield, bool verifying):
- _collector(collector),
- _span(span),
- _bitMap(bitMap),
- _mut(&collector->_modUnionTable),
- _markStack(markStack),
- _yield(should_yield),
- _skipBits(0)
-{
- assert(_markStack->isEmpty(), "stack should be empty");
- _finger = _bitMap->startWord();
- _threshold = _finger;
- assert(_collector->_restart_addr == NULL, "Sanity check");
- assert(_span.contains(_finger), "Out of bounds _finger?");
- DEBUG_ONLY(_verifying = verifying;)
-}
-
-void MarkFromRootsClosure::reset(HeapWord* addr) {
- assert(_markStack->isEmpty(), "would cause duplicates on stack");
- assert(_span.contains(addr), "Out of bounds _finger?");
- _finger = addr;
- _threshold = align_up(_finger, CardTable::card_size);
-}
-
-// Should revisit to see if this should be restructured for
-// greater efficiency.
-bool MarkFromRootsClosure::do_bit(size_t offset) {
- if (_skipBits > 0) {
- _skipBits--;
- return true;
- }
- // convert offset into a HeapWord*
- HeapWord* addr = _bitMap->startWord() + offset;
- assert(_bitMap->endWord() && addr < _bitMap->endWord(),
- "address out of range");
- assert(_bitMap->isMarked(addr), "tautology");
- if (_bitMap->isMarked(addr+1)) {
- // this is an allocated but not yet initialized object
- assert(_skipBits == 0, "tautology");
- _skipBits = 2; // skip next two marked bits ("Printezis-marks")
- oop p = oop(addr);
- if (p->klass_or_null_acquire() == NULL) {
- DEBUG_ONLY(if (!_verifying) {)
- // We re-dirty the cards on which this object lies and increase
- // the _threshold so that we'll come back to scan this object
- // during the preclean or remark phase. (CMSCleanOnEnter)
- if (CMSCleanOnEnter) {
- size_t sz = _collector->block_size_using_printezis_bits(addr);
- HeapWord* end_card_addr = align_up(addr + sz, CardTable::card_size);
- MemRegion redirty_range = MemRegion(addr, end_card_addr);
- assert(!redirty_range.is_empty(), "Arithmetical tautology");
- // Bump _threshold to end_card_addr; note that
- // _threshold cannot possibly exceed end_card_addr, anyhow.
- // This prevents future clearing of the card as the scan proceeds
- // to the right.
- assert(_threshold <= end_card_addr,
- "Because we are just scanning into this object");
- if (_threshold < end_card_addr) {
- _threshold = end_card_addr;
- }
- if (p->klass_or_null_acquire() != NULL) {
- // Redirty the range of cards...
- _mut->mark_range(redirty_range);
- } // ...else the setting of klass will dirty the card anyway.
- }
- DEBUG_ONLY(})
- return true;
- }
- }
- scanOopsInOop(addr);
- return true;
-}
-
-// We take a break if we've been at this for a while,
-// so as to avoid monopolizing the locks involved.
-void MarkFromRootsClosure::do_yield_work() {
- // First give up the locks, then yield, then re-lock
- // We should probably use a constructor/destructor idiom to
- // do this unlock/lock or modify the MutexUnlocker class to
- // serve our purpose. XXX
- assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "CMS thread should hold CMS token");
- assert_lock_strong(_bitMap->lock());
- _bitMap->lock()->unlock();
- ConcurrentMarkSweepThread::desynchronize(true);
- _collector->stopTimer();
- _collector->incrementYields();
-
- // See the comment in coordinator_yield()
- for (unsigned i = 0; i < CMSYieldSleepCount &&
- ConcurrentMarkSweepThread::should_yield() &&
- !CMSCollector::foregroundGCIsActive(); ++i) {
- os::naked_short_sleep(1);
- }
-
- ConcurrentMarkSweepThread::synchronize(true);
- _bitMap->lock()->lock_without_safepoint_check();
- _collector->startTimer();
-}
-
-void MarkFromRootsClosure::scanOopsInOop(HeapWord* ptr) {
- assert(_bitMap->isMarked(ptr), "expected bit to be set");
- assert(_markStack->isEmpty(),
- "should drain stack to limit stack usage");
- // convert ptr to an oop preparatory to scanning
- oop obj = oop(ptr);
- // Ignore mark word in verification below, since we
- // may be running concurrent with mutators.
- assert(oopDesc::is_oop(obj, true), "should be an oop");
- assert(_finger <= ptr, "_finger runneth ahead");
- // advance the finger to right end of this object
- _finger = ptr + obj->size();
- assert(_finger > ptr, "we just incremented it above");
- // On large heaps, it may take us some time to get through
- // the marking phase. During
- // this time it's possible that a lot of mutations have
- // accumulated in the card table and the mod union table --
- // these mutation records are redundant until we have
- // actually traced into the corresponding card.
- // Here, we check whether advancing the finger would make
- // us cross into a new card, and if so clear corresponding
- // cards in the MUT (preclean them in the card-table in the
- // future).
-
- DEBUG_ONLY(if (!_verifying) {)
- // The clean-on-enter optimization is disabled by default,
- // until we fix 6178663.
- if (CMSCleanOnEnter && (_finger > _threshold)) {
- // [_threshold, _finger) represents the interval
- // of cards to be cleared in MUT (or precleaned in card table).
- // The set of cards to be cleared is all those that overlap
- // with the interval [_threshold, _finger); note that
- // _threshold is always kept card-aligned but _finger isn't
- // always card-aligned.
- HeapWord* old_threshold = _threshold;
- assert(is_aligned(old_threshold, CardTable::card_size),
- "_threshold should always be card-aligned");
- _threshold = align_up(_finger, CardTable::card_size);
- MemRegion mr(old_threshold, _threshold);
- assert(!mr.is_empty(), "Control point invariant");
- assert(_span.contains(mr), "Should clear within span");
- _mut->clear_range(mr);
- }
- DEBUG_ONLY(})
- // Note: the finger doesn't advance while we drain
- // the stack below.
- PushOrMarkClosure pushOrMarkClosure(_collector,
- _span, _bitMap, _markStack,
- _finger, this);
- bool res = _markStack->push(obj);
- assert(res, "Empty non-zero size stack should have space for single push");
- while (!_markStack->isEmpty()) {
- oop new_oop = _markStack->pop();
- // Skip verifying header mark word below because we are
- // running concurrent with mutators.
- assert(oopDesc::is_oop(new_oop, true), "Oops! expected to pop an oop");
- // now scan this oop's oops
- new_oop->oop_iterate(&pushOrMarkClosure);
- do_yield_check();
- }
- assert(_markStack->isEmpty(), "tautology, emphasizing post-condition");
-}
-
-ParMarkFromRootsClosure::ParMarkFromRootsClosure(CMSConcMarkingTask* task,
- CMSCollector* collector, MemRegion span,
- CMSBitMap* bit_map,
- OopTaskQueue* work_queue,
- CMSMarkStack* overflow_stack):
- _collector(collector),
- _whole_span(collector->_span),
- _span(span),
- _bit_map(bit_map),
- _mut(&collector->_modUnionTable),
- _work_queue(work_queue),
- _overflow_stack(overflow_stack),
- _skip_bits(0),
- _task(task)
-{
- assert(_work_queue->size() == 0, "work_queue should be empty");
- _finger = span.start();
- _threshold = _finger; // XXX Defer clear-on-enter optimization for now
- assert(_span.contains(_finger), "Out of bounds _finger?");
-}
-
-// Should revisit to see if this should be restructured for
-// greater efficiency.
-bool ParMarkFromRootsClosure::do_bit(size_t offset) {
- if (_skip_bits > 0) {
- _skip_bits--;
- return true;
- }
- // convert offset into a HeapWord*
- HeapWord* addr = _bit_map->startWord() + offset;
- assert(_bit_map->endWord() && addr < _bit_map->endWord(),
- "address out of range");
- assert(_bit_map->isMarked(addr), "tautology");
- if (_bit_map->isMarked(addr+1)) {
- // this is an allocated object that might not yet be initialized
- assert(_skip_bits == 0, "tautology");
- _skip_bits = 2; // skip next two marked bits ("Printezis-marks")
- oop p = oop(addr);
- if (p->klass_or_null_acquire() == NULL) {
- // in the case of Clean-on-Enter optimization, redirty card
- // and avoid clearing card by increasing the threshold.
- return true;
- }
- }
- scan_oops_in_oop(addr);
- return true;
-}
-
-void ParMarkFromRootsClosure::scan_oops_in_oop(HeapWord* ptr) {
- assert(_bit_map->isMarked(ptr), "expected bit to be set");
- // Should we assert that our work queue is empty or
- // below some drain limit?
- assert(_work_queue->size() == 0,
- "should drain stack to limit stack usage");
- // convert ptr to an oop preparatory to scanning
- oop obj = oop(ptr);
- // Ignore mark word in verification below, since we
- // may be running concurrent with mutators.
- assert(oopDesc::is_oop(obj, true), "should be an oop");
- assert(_finger <= ptr, "_finger runneth ahead");
- // advance the finger to right end of this object
- _finger = ptr + obj->size();
- assert(_finger > ptr, "we just incremented it above");
- // On large heaps, it may take us some time to get through
- // the marking phase. During
- // this time it's possible that a lot of mutations have
- // accumulated in the card table and the mod union table --
- // these mutation records are redundant until we have
- // actually traced into the corresponding card.
- // Here, we check whether advancing the finger would make
- // us cross into a new card, and if so clear corresponding
- // cards in the MUT (preclean them in the card-table in the
- // future).
-
- // The clean-on-enter optimization is disabled by default,
- // until we fix 6178663.
- if (CMSCleanOnEnter && (_finger > _threshold)) {
- // [_threshold, _finger) represents the interval
- // of cards to be cleared in MUT (or precleaned in card table).
- // The set of cards to be cleared is all those that overlap
- // with the interval [_threshold, _finger); note that
- // _threshold is always kept card-aligned but _finger isn't
- // always card-aligned.
- HeapWord* old_threshold = _threshold;
- assert(is_aligned(old_threshold, CardTable::card_size),
- "_threshold should always be card-aligned");
- _threshold = align_up(_finger, CardTable::card_size);
- MemRegion mr(old_threshold, _threshold);
- assert(!mr.is_empty(), "Control point invariant");
- assert(_span.contains(mr), "Should clear within span"); // _whole_span ??
- _mut->clear_range(mr);
- }
-
- // Note: the local finger doesn't advance while we drain
- // the stack below, but the global finger sure can and will.
- HeapWord* volatile* gfa = _task->global_finger_addr();
- ParPushOrMarkClosure pushOrMarkClosure(_collector,
- _span, _bit_map,
- _work_queue,
- _overflow_stack,
- _finger,
- gfa, this);
- bool res = _work_queue->push(obj); // overflow could occur here
- assert(res, "Will hold once we use workqueues");
- while (true) {
- oop new_oop;
- if (!_work_queue->pop_local(new_oop)) {
- // We emptied our work_queue; check if there's stuff that can
- // be gotten from the overflow stack.
- if (CMSConcMarkingTask::get_work_from_overflow_stack(
- _overflow_stack, _work_queue)) {
- do_yield_check();
- continue;
- } else { // done
- break;
- }
- }
- // Skip verifying header mark word below because we are
- // running concurrent with mutators.
- assert(oopDesc::is_oop(new_oop, true), "Oops! expected to pop an oop");
- // now scan this oop's oops
- new_oop->oop_iterate(&pushOrMarkClosure);
- do_yield_check();
- }
- assert(_work_queue->size() == 0, "tautology, emphasizing post-condition");
-}
-
-// Yield in response to a request from VM Thread or
-// from mutators.
-void ParMarkFromRootsClosure::do_yield_work() {
- assert(_task != NULL, "sanity");
- _task->yield();
-}
-
-// A variant of the above used for verifying CMS marking work.
-MarkFromRootsVerifyClosure::MarkFromRootsVerifyClosure(CMSCollector* collector,
- MemRegion span,
- CMSBitMap* verification_bm, CMSBitMap* cms_bm,
- CMSMarkStack* mark_stack):
- _collector(collector),
- _span(span),
- _verification_bm(verification_bm),
- _cms_bm(cms_bm),
- _mark_stack(mark_stack),
- _pam_verify_closure(collector, span, verification_bm, cms_bm,
- mark_stack)
-{
- assert(_mark_stack->isEmpty(), "stack should be empty");
- _finger = _verification_bm->startWord();
- assert(_collector->_restart_addr == NULL, "Sanity check");
- assert(_span.contains(_finger), "Out of bounds _finger?");
-}
-
-void MarkFromRootsVerifyClosure::reset(HeapWord* addr) {
- assert(_mark_stack->isEmpty(), "would cause duplicates on stack");
- assert(_span.contains(addr), "Out of bounds _finger?");
- _finger = addr;
-}
-
-// Should revisit to see if this should be restructured for
-// greater efficiency.
-bool MarkFromRootsVerifyClosure::do_bit(size_t offset) {
- // convert offset into a HeapWord*
- HeapWord* addr = _verification_bm->startWord() + offset;
- assert(_verification_bm->endWord() && addr < _verification_bm->endWord(),
- "address out of range");
- assert(_verification_bm->isMarked(addr), "tautology");
- assert(_cms_bm->isMarked(addr), "tautology");
-
- assert(_mark_stack->isEmpty(),
- "should drain stack to limit stack usage");
- // convert addr to an oop preparatory to scanning
- oop obj = oop(addr);
- assert(oopDesc::is_oop(obj), "should be an oop");
- assert(_finger <= addr, "_finger runneth ahead");
- // advance the finger to right end of this object
- _finger = addr + obj->size();
- assert(_finger > addr, "we just incremented it above");
- // Note: the finger doesn't advance while we drain
- // the stack below.
- bool res = _mark_stack->push(obj);
- assert(res, "Empty non-zero size stack should have space for single push");
- while (!_mark_stack->isEmpty()) {
- oop new_oop = _mark_stack->pop();
- assert(oopDesc::is_oop(new_oop), "Oops! expected to pop an oop");
- // now scan this oop's oops
- new_oop->oop_iterate(&_pam_verify_closure);
- }
- assert(_mark_stack->isEmpty(), "tautology, emphasizing post-condition");
- return true;
-}
-
-PushAndMarkVerifyClosure::PushAndMarkVerifyClosure(
- CMSCollector* collector, MemRegion span,
- CMSBitMap* verification_bm, CMSBitMap* cms_bm,
- CMSMarkStack* mark_stack):
- MetadataVisitingOopIterateClosure(collector->ref_processor()),
- _collector(collector),
- _span(span),
- _verification_bm(verification_bm),
- _cms_bm(cms_bm),
- _mark_stack(mark_stack)
-{ }
-
-template <class T> void PushAndMarkVerifyClosure::do_oop_work(T *p) {
- oop obj = RawAccess<>::oop_load(p);
- do_oop(obj);
-}
-
-void PushAndMarkVerifyClosure::do_oop(oop* p) { PushAndMarkVerifyClosure::do_oop_work(p); }
-void PushAndMarkVerifyClosure::do_oop(narrowOop* p) { PushAndMarkVerifyClosure::do_oop_work(p); }
-
-// Upon stack overflow, we discard (part of) the stack,
-// remembering the least address amongst those discarded
-// in CMSCollector's _restart_address.
-void PushAndMarkVerifyClosure::handle_stack_overflow(HeapWord* lost) {
- // Remember the least grey address discarded
- HeapWord* ra = (HeapWord*)_mark_stack->least_value(lost);
- _collector->lower_restart_addr(ra);
- _mark_stack->reset(); // discard stack contents
- _mark_stack->expand(); // expand the stack if possible
-}
-
-void PushAndMarkVerifyClosure::do_oop(oop obj) {
- assert(oopDesc::is_oop_or_null(obj), "Expected an oop or NULL at " PTR_FORMAT, p2i(obj));
- HeapWord* addr = (HeapWord*)obj;
- if (_span.contains(addr) && !_verification_bm->isMarked(addr)) {
- // Oop lies in _span and isn't yet grey or black
- _verification_bm->mark(addr); // now grey
- if (!_cms_bm->isMarked(addr)) {
- Log(gc, verify) log;
- ResourceMark rm;
- LogStream ls(log.error());
- oop(addr)->print_on(&ls);
- log.error(" (" INTPTR_FORMAT " should have been marked)", p2i(addr));
- fatal("... aborting");
- }
-
- if (!_mark_stack->push(obj)) { // stack overflow
- log_trace(gc)("CMS marking stack overflow (benign) at " SIZE_FORMAT, _mark_stack->capacity());
- assert(_mark_stack->isFull(), "Else push should have succeeded");
- handle_stack_overflow(addr);
- }
- // anything including and to the right of _finger
- // will be scanned as we iterate over the remainder of the
- // bit map
- }
-}
-
-PushOrMarkClosure::PushOrMarkClosure(CMSCollector* collector,
- MemRegion span,
- CMSBitMap* bitMap, CMSMarkStack* markStack,
- HeapWord* finger, MarkFromRootsClosure* parent) :
- MetadataVisitingOopIterateClosure(collector->ref_processor()),
- _collector(collector),
- _span(span),
- _bitMap(bitMap),
- _markStack(markStack),
- _finger(finger),
- _parent(parent)
-{ }
-
-ParPushOrMarkClosure::ParPushOrMarkClosure(CMSCollector* collector,
- MemRegion span,
- CMSBitMap* bit_map,
- OopTaskQueue* work_queue,
- CMSMarkStack* overflow_stack,
- HeapWord* finger,
- HeapWord* volatile* global_finger_addr,
- ParMarkFromRootsClosure* parent) :
- MetadataVisitingOopIterateClosure(collector->ref_processor()),
- _collector(collector),
- _whole_span(collector->_span),
- _span(span),
- _bit_map(bit_map),
- _work_queue(work_queue),
- _overflow_stack(overflow_stack),
- _finger(finger),
- _global_finger_addr(global_finger_addr),
- _parent(parent)
-{ }
-
-// Assumes thread-safe access by callers, who are
-// responsible for mutual exclusion.
-void CMSCollector::lower_restart_addr(HeapWord* low) {
- assert(_span.contains(low), "Out of bounds addr");
- if (_restart_addr == NULL) {
- _restart_addr = low;
- } else {
- _restart_addr = MIN2(_restart_addr, low);
- }
-}
-
-// Upon stack overflow, we discard (part of) the stack,
-// remembering the least address amongst those discarded
-// in CMSCollector's _restart_address.
-void PushOrMarkClosure::handle_stack_overflow(HeapWord* lost) {
- // Remember the least grey address discarded
- HeapWord* ra = (HeapWord*)_markStack->least_value(lost);
- _collector->lower_restart_addr(ra);
- _markStack->reset(); // discard stack contents
- _markStack->expand(); // expand the stack if possible
-}
-
-// Upon stack overflow, we discard (part of) the stack,
-// remembering the least address amongst those discarded
-// in CMSCollector's _restart_address.
-void ParPushOrMarkClosure::handle_stack_overflow(HeapWord* lost) {
- // We need to do this under a mutex to prevent other
- // workers from interfering with the work done below.
- MutexLocker ml(_overflow_stack->par_lock(),
- Mutex::_no_safepoint_check_flag);
- // Remember the least grey address discarded
- HeapWord* ra = (HeapWord*)_overflow_stack->least_value(lost);
- _collector->lower_restart_addr(ra);
- _overflow_stack->reset(); // discard stack contents
- _overflow_stack->expand(); // expand the stack if possible
-}
-
-void PushOrMarkClosure::do_oop(oop obj) {
- // Ignore mark word because we are running concurrent with mutators.
- assert(oopDesc::is_oop_or_null(obj, true), "Expected an oop or NULL at " PTR_FORMAT, p2i(obj));
- HeapWord* addr = (HeapWord*)obj;
- if (_span.contains(addr) && !_bitMap->isMarked(addr)) {
- // Oop lies in _span and isn't yet grey or black
- _bitMap->mark(addr); // now grey
- if (addr < _finger) {
- // the bit map iteration has already either passed, or
- // sampled, this bit in the bit map; we'll need to
- // use the marking stack to scan this oop's oops.
- bool simulate_overflow = false;
- NOT_PRODUCT(
- if (CMSMarkStackOverflowALot &&
- _collector->simulate_overflow()) {
- // simulate a stack overflow
- simulate_overflow = true;
- }
- )
- if (simulate_overflow || !_markStack->push(obj)) { // stack overflow
- log_trace(gc)("CMS marking stack overflow (benign) at " SIZE_FORMAT, _markStack->capacity());
- assert(simulate_overflow || _markStack->isFull(), "Else push should have succeeded");
- handle_stack_overflow(addr);
- }
- }
- // anything including and to the right of _finger
- // will be scanned as we iterate over the remainder of the
- // bit map
- do_yield_check();
- }
-}
-
-void ParPushOrMarkClosure::do_oop(oop obj) {
- // Ignore mark word because we are running concurrent with mutators.
- assert(oopDesc::is_oop_or_null(obj, true), "Expected an oop or NULL at " PTR_FORMAT, p2i(obj));
- HeapWord* addr = (HeapWord*)obj;
- if (_whole_span.contains(addr) && !_bit_map->isMarked(addr)) {
- // Oop lies in _span and isn't yet grey or black
- // We read the global_finger (volatile read) strictly after marking oop
- bool res = _bit_map->par_mark(addr); // now grey
- volatile HeapWord** gfa = (volatile HeapWord**)_global_finger_addr;
- // Should we push this marked oop on our stack?
- // -- if someone else marked it, nothing to do
- // -- if target oop is above global finger nothing to do
- // -- if target oop is in chunk and above local finger
- // then nothing to do
- // -- else push on work queue
- if ( !res // someone else marked it, they will deal with it
- || (addr >= *gfa) // will be scanned in a later task
- || (_span.contains(addr) && addr >= _finger)) { // later in this chunk
- return;
- }
- // the bit map iteration has already either passed, or
- // sampled, this bit in the bit map; we'll need to
- // use the marking stack to scan this oop's oops.
- bool simulate_overflow = false;
- NOT_PRODUCT(
- if (CMSMarkStackOverflowALot &&
- _collector->simulate_overflow()) {
- // simulate a stack overflow
- simulate_overflow = true;
- }
- )
- if (simulate_overflow ||
- !(_work_queue->push(obj) || _overflow_stack->par_push(obj))) {
- // stack overflow
- log_trace(gc)("CMS marking stack overflow (benign) at " SIZE_FORMAT, _overflow_stack->capacity());
- // We cannot assert that the overflow stack is full because
- // it may have been emptied since.
- assert(simulate_overflow ||
- _work_queue->size() == _work_queue->max_elems(),
- "Else push should have succeeded");
- handle_stack_overflow(addr);
- }
- do_yield_check();
- }
-}
-
-PushAndMarkClosure::PushAndMarkClosure(CMSCollector* collector,
- MemRegion span,
- ReferenceDiscoverer* rd,
- CMSBitMap* bit_map,
- CMSBitMap* mod_union_table,
- CMSMarkStack* mark_stack,
- bool concurrent_precleaning):
- MetadataVisitingOopIterateClosure(rd),
- _collector(collector),
- _span(span),
- _bit_map(bit_map),
- _mod_union_table(mod_union_table),
- _mark_stack(mark_stack),
- _concurrent_precleaning(concurrent_precleaning)
-{
- assert(ref_discoverer() != NULL, "ref_discoverer shouldn't be NULL");
-}
-
-// Grey object rescan during pre-cleaning and second checkpoint phases --
-// the non-parallel version (the parallel version appears further below.)
-void PushAndMarkClosure::do_oop(oop obj) {
- // Ignore mark word verification. If during concurrent precleaning,
- // the object monitor may be locked. If during the checkpoint
- // phases, the object may already have been reached by a different
- // path and may be at the end of the global overflow list (so
- // the mark word may be NULL).
- assert(oopDesc::is_oop_or_null(obj, true /* ignore mark word */),
- "Expected an oop or NULL at " PTR_FORMAT, p2i(obj));
- HeapWord* addr = (HeapWord*)obj;
- // Check if oop points into the CMS generation
- // and is not marked
- if (_span.contains(addr) && !_bit_map->isMarked(addr)) {
- // a white object ...
- _bit_map->mark(addr); // ... now grey
- // push on the marking stack (grey set)
- bool simulate_overflow = false;
- NOT_PRODUCT(
- if (CMSMarkStackOverflowALot &&
- _collector->simulate_overflow()) {
- // simulate a stack overflow
- simulate_overflow = true;
- }
- )
- if (simulate_overflow || !_mark_stack->push(obj)) {
- if (_concurrent_precleaning) {
- // During precleaning we can just dirty the appropriate card(s)
- // in the mod union table, thus ensuring that the object remains
- // in the grey set and continue. In the case of object arrays
- // we need to dirty all of the cards that the object spans,
- // since the rescan of object arrays will be limited to the
- // dirty cards.
- // Note that no one can be interfering with us in this action
- // of dirtying the mod union table, so no locking or atomics
- // are required.
- if (obj->is_objArray()) {
- size_t sz = obj->size();
- HeapWord* end_card_addr = align_up(addr + sz, CardTable::card_size);
- MemRegion redirty_range = MemRegion(addr, end_card_addr);
- assert(!redirty_range.is_empty(), "Arithmetical tautology");
- _mod_union_table->mark_range(redirty_range);
- } else {
- _mod_union_table->mark(addr);
- }
- _collector->_ser_pmc_preclean_ovflw++;
- } else {
- // During the remark phase, we need to remember this oop
- // in the overflow list.
- _collector->push_on_overflow_list(obj);
- _collector->_ser_pmc_remark_ovflw++;
- }
- }
- }
-}
-
-ParPushAndMarkClosure::ParPushAndMarkClosure(CMSCollector* collector,
- MemRegion span,
- ReferenceDiscoverer* rd,
- CMSBitMap* bit_map,
- OopTaskQueue* work_queue):
- MetadataVisitingOopIterateClosure(rd),
- _collector(collector),
- _span(span),
- _bit_map(bit_map),
- _work_queue(work_queue)
-{
- assert(ref_discoverer() != NULL, "ref_discoverer shouldn't be NULL");
-}
-
-// Grey object rescan during second checkpoint phase --
-// the parallel version.
-void ParPushAndMarkClosure::do_oop(oop obj) {
- // In the assert below, we ignore the mark word because
- // this oop may point to an already visited object that is
- // on the overflow stack (in which case the mark word has
- // been hijacked for chaining into the overflow stack --
- // if this is the last object in the overflow stack then
- // its mark word will be NULL). Because this object may
- // have been subsequently popped off the global overflow
- // stack, and the mark word possibly restored to the prototypical
- // value, by the time we get to examined this failing assert in
- // the debugger, is_oop_or_null(false) may subsequently start
- // to hold.
- assert(oopDesc::is_oop_or_null(obj, true),
- "Expected an oop or NULL at " PTR_FORMAT, p2i(obj));
- HeapWord* addr = (HeapWord*)obj;
- // Check if oop points into the CMS generation
- // and is not marked
- if (_span.contains(addr) && !_bit_map->isMarked(addr)) {
- // a white object ...
- // If we manage to "claim" the object, by being the
- // first thread to mark it, then we push it on our
- // marking stack
- if (_bit_map->par_mark(addr)) { // ... now grey
- // push on work queue (grey set)
- bool simulate_overflow = false;
- NOT_PRODUCT(
- if (CMSMarkStackOverflowALot &&
- _collector->par_simulate_overflow()) {
- // simulate a stack overflow
- simulate_overflow = true;
- }
- )
- if (simulate_overflow || !_work_queue->push(obj)) {
- _collector->par_push_on_overflow_list(obj);
- _collector->_par_pmc_remark_ovflw++; // imprecise OK: no need to CAS
- }
- } // Else, some other thread got there first
- }
-}
-
-void CMSPrecleanRefsYieldClosure::do_yield_work() {
- Mutex* bml = _collector->bitMapLock();
- assert_lock_strong(bml);
- assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "CMS thread should hold CMS token");
-
- bml->unlock();
- ConcurrentMarkSweepThread::desynchronize(true);
-
- _collector->stopTimer();
- _collector->incrementYields();
-
- // See the comment in coordinator_yield()
- for (unsigned i = 0; i < CMSYieldSleepCount &&
- ConcurrentMarkSweepThread::should_yield() &&
- !CMSCollector::foregroundGCIsActive(); ++i) {
- os::naked_short_sleep(1);
- }
-
- ConcurrentMarkSweepThread::synchronize(true);
- bml->lock_without_safepoint_check();
-
- _collector->startTimer();
-}
-
-bool CMSPrecleanRefsYieldClosure::should_return() {
- if (ConcurrentMarkSweepThread::should_yield()) {
- do_yield_work();
- }
- return _collector->foregroundGCIsActive();
-}
-
-void MarkFromDirtyCardsClosure::do_MemRegion(MemRegion mr) {
- assert(((size_t)mr.start())%CardTable::card_size_in_words == 0,
- "mr should be aligned to start at a card boundary");
- // We'd like to assert:
- // assert(mr.word_size()%CardTable::card_size_in_words == 0,
- // "mr should be a range of cards");
- // However, that would be too strong in one case -- the last
- // partition ends at _unallocated_block which, in general, can be
- // an arbitrary boundary, not necessarily card aligned.
- _num_dirty_cards += mr.word_size()/CardTable::card_size_in_words;
- _space->object_iterate_mem(mr, &_scan_cl);
-}
-
-SweepClosure::SweepClosure(CMSCollector* collector,
- ConcurrentMarkSweepGeneration* g,
- CMSBitMap* bitMap, bool should_yield) :
- _collector(collector),
- _g(g),
- _sp(g->cmsSpace()),
- _limit(_sp->sweep_limit()),
- _freelistLock(_sp->freelistLock()),
- _bitMap(bitMap),
- _inFreeRange(false), // No free range at beginning of sweep
- _freeRangeInFreeLists(false), // No free range at beginning of sweep
- _lastFreeRangeCoalesced(false),
- _yield(should_yield),
- _freeFinger(g->used_region().start())
-{
- NOT_PRODUCT(
- _numObjectsFreed = 0;
- _numWordsFreed = 0;
- _numObjectsLive = 0;
- _numWordsLive = 0;
- _numObjectsAlreadyFree = 0;
- _numWordsAlreadyFree = 0;
- _last_fc = NULL;
-
- _sp->initializeIndexedFreeListArrayReturnedBytes();
- _sp->dictionary()->initialize_dict_returned_bytes();
- )
- assert(_limit >= _sp->bottom() && _limit <= _sp->end(),
- "sweep _limit out of bounds");
- log_develop_trace(gc, sweep)("====================");
- log_develop_trace(gc, sweep)("Starting new sweep with limit " PTR_FORMAT, p2i(_limit));
-}
-
-void SweepClosure::print_on(outputStream* st) const {
- st->print_cr("_sp = [" PTR_FORMAT "," PTR_FORMAT ")",
- p2i(_sp->bottom()), p2i(_sp->end()));
- st->print_cr("_limit = " PTR_FORMAT, p2i(_limit));
- st->print_cr("_freeFinger = " PTR_FORMAT, p2i(_freeFinger));
- NOT_PRODUCT(st->print_cr("_last_fc = " PTR_FORMAT, p2i(_last_fc));)
- st->print_cr("_inFreeRange = %d, _freeRangeInFreeLists = %d, _lastFreeRangeCoalesced = %d",
- _inFreeRange, _freeRangeInFreeLists, _lastFreeRangeCoalesced);
-}
-
-#ifndef PRODUCT
-// Assertion checking only: no useful work in product mode --
-// however, if any of the flags below become product flags,
-// you may need to review this code to see if it needs to be
-// enabled in product mode.
-SweepClosure::~SweepClosure() {
- assert_lock_strong(_freelistLock);
- assert(_limit >= _sp->bottom() && _limit <= _sp->end(),
- "sweep _limit out of bounds");
- if (inFreeRange()) {
- Log(gc, sweep) log;
- log.error("inFreeRange() should have been reset; dumping state of SweepClosure");
- ResourceMark rm;
- LogStream ls(log.error());
- print_on(&ls);
- ShouldNotReachHere();
- }
-
- if (log_is_enabled(Debug, gc, sweep)) {
- log_debug(gc, sweep)("Collected " SIZE_FORMAT " objects, " SIZE_FORMAT " bytes",
- _numObjectsFreed, _numWordsFreed*sizeof(HeapWord));
- log_debug(gc, sweep)("Live " SIZE_FORMAT " objects, " SIZE_FORMAT " bytes Already free " SIZE_FORMAT " objects, " SIZE_FORMAT " bytes",
- _numObjectsLive, _numWordsLive*sizeof(HeapWord), _numObjectsAlreadyFree, _numWordsAlreadyFree*sizeof(HeapWord));
- size_t totalBytes = (_numWordsFreed + _numWordsLive + _numWordsAlreadyFree) * sizeof(HeapWord);
- log_debug(gc, sweep)("Total sweep: " SIZE_FORMAT " bytes", totalBytes);
- }
-
- if (log_is_enabled(Trace, gc, sweep) && CMSVerifyReturnedBytes) {
- size_t indexListReturnedBytes = _sp->sumIndexedFreeListArrayReturnedBytes();
- size_t dict_returned_bytes = _sp->dictionary()->sum_dict_returned_bytes();
- size_t returned_bytes = indexListReturnedBytes + dict_returned_bytes;
- log_trace(gc, sweep)("Returned " SIZE_FORMAT " bytes Indexed List Returned " SIZE_FORMAT " bytes Dictionary Returned " SIZE_FORMAT " bytes",
- returned_bytes, indexListReturnedBytes, dict_returned_bytes);
- }
- log_develop_trace(gc, sweep)("end of sweep with _limit = " PTR_FORMAT, p2i(_limit));
- log_develop_trace(gc, sweep)("================");
-}
-#endif // PRODUCT
-
-void SweepClosure::initialize_free_range(HeapWord* freeFinger,
- bool freeRangeInFreeLists) {
- log_develop_trace(gc, sweep)("---- Start free range at " PTR_FORMAT " with free block (%d)",
- p2i(freeFinger), freeRangeInFreeLists);
- assert(!inFreeRange(), "Trampling existing free range");
- set_inFreeRange(true);
- set_lastFreeRangeCoalesced(false);
-
- set_freeFinger(freeFinger);
- set_freeRangeInFreeLists(freeRangeInFreeLists);
- if (CMSTestInFreeList) {
- if (freeRangeInFreeLists) {
- FreeChunk* fc = (FreeChunk*) freeFinger;
- assert(fc->is_free(), "A chunk on the free list should be free.");
- assert(fc->size() > 0, "Free range should have a size");
- assert(_sp->verify_chunk_in_free_list(fc), "Chunk is not in free lists");
- }
- }
-}
-
-// Note that the sweeper runs concurrently with mutators. Thus,
-// it is possible for direct allocation in this generation to happen
-// in the middle of the sweep. Note that the sweeper also coalesces
-// contiguous free blocks. Thus, unless the sweeper and the allocator
-// synchronize appropriately freshly allocated blocks may get swept up.
-// This is accomplished by the sweeper locking the free lists while
-// it is sweeping. Thus blocks that are determined to be free are
-// indeed free. There is however one additional complication:
-// blocks that have been allocated since the final checkpoint and
-// mark, will not have been marked and so would be treated as
-// unreachable and swept up. To prevent this, the allocator marks
-// the bit map when allocating during the sweep phase. This leads,
-// however, to a further complication -- objects may have been allocated
-// but not yet initialized -- in the sense that the header isn't yet
-// installed. The sweeper can not then determine the size of the block
-// in order to skip over it. To deal with this case, we use a technique
-// (due to Printezis) to encode such uninitialized block sizes in the
-// bit map. Since the bit map uses a bit per every HeapWord, but the
-// CMS generation has a minimum object size of 3 HeapWords, it follows
-// that "normal marks" won't be adjacent in the bit map (there will
-// always be at least two 0 bits between successive 1 bits). We make use
-// of these "unused" bits to represent uninitialized blocks -- the bit
-// corresponding to the start of the uninitialized object and the next
-// bit are both set. Finally, a 1 bit marks the end of the object that
-// started with the two consecutive 1 bits to indicate its potentially
-// uninitialized state.
-
-size_t SweepClosure::do_blk_careful(HeapWord* addr) {
- FreeChunk* fc = (FreeChunk*)addr;
- size_t res;
-
- // Check if we are done sweeping. Below we check "addr >= _limit" rather
- // than "addr == _limit" because although _limit was a block boundary when
- // we started the sweep, it may no longer be one because heap expansion
- // may have caused us to coalesce the block ending at the address _limit
- // with a newly expanded chunk (this happens when _limit was set to the
- // previous _end of the space), so we may have stepped past _limit:
- // see the following Zeno-like trail of CRs 6977970, 7008136, 7042740.
- if (addr >= _limit) { // we have swept up to or past the limit: finish up
- assert(_limit >= _sp->bottom() && _limit <= _sp->end(),
- "sweep _limit out of bounds");
- assert(addr < _sp->end(), "addr out of bounds");
- // Flush any free range we might be holding as a single
- // coalesced chunk to the appropriate free list.
- if (inFreeRange()) {
- assert(freeFinger() >= _sp->bottom() && freeFinger() < _limit,
- "freeFinger() " PTR_FORMAT " is out of bounds", p2i(freeFinger()));
- flush_cur_free_chunk(freeFinger(),
- pointer_delta(addr, freeFinger()));
- log_develop_trace(gc, sweep)("Sweep: last chunk: put_free_blk " PTR_FORMAT " (" SIZE_FORMAT ") [coalesced:%d]",
- p2i(freeFinger()), pointer_delta(addr, freeFinger()),
- lastFreeRangeCoalesced() ? 1 : 0);
- }
-
- // help the iterator loop finish
- return pointer_delta(_sp->end(), addr);
- }
-
- assert(addr < _limit, "sweep invariant");
- // check if we should yield
- do_yield_check(addr);
- if (fc->is_free()) {
- // Chunk that is already free
- res = fc->size();
- do_already_free_chunk(fc);
- debug_only(_sp->verifyFreeLists());
- // If we flush the chunk at hand in lookahead_and_flush()
- // and it's coalesced with a preceding chunk, then the
- // process of "mangling" the payload of the coalesced block
- // will cause erasure of the size information from the
- // (erstwhile) header of all the coalesced blocks but the
- // first, so the first disjunct in the assert will not hold
- // in that specific case (in which case the second disjunct
- // will hold).
- assert(res == fc->size() || ((HeapWord*)fc) + res >= _limit,
- "Otherwise the size info doesn't change at this step");
- NOT_PRODUCT(
- _numObjectsAlreadyFree++;
- _numWordsAlreadyFree += res;
- )
- NOT_PRODUCT(_last_fc = fc;)
- } else if (!_bitMap->isMarked(addr)) {
- // Chunk is fresh garbage
- res = do_garbage_chunk(fc);
- debug_only(_sp->verifyFreeLists());
- NOT_PRODUCT(
- _numObjectsFreed++;
- _numWordsFreed += res;
- )
- } else {
- // Chunk that is alive.
- res = do_live_chunk(fc);
- debug_only(_sp->verifyFreeLists());
- NOT_PRODUCT(
- _numObjectsLive++;
- _numWordsLive += res;
- )
- }
- return res;
-}
-
-// For the smart allocation, record following
-// split deaths - a free chunk is removed from its free list because
-// it is being split into two or more chunks.
-// split birth - a free chunk is being added to its free list because
-// a larger free chunk has been split and resulted in this free chunk.
-// coal death - a free chunk is being removed from its free list because
-// it is being coalesced into a large free chunk.
-// coal birth - a free chunk is being added to its free list because
-// it was created when two or more free chunks where coalesced into
-// this free chunk.
-//
-// These statistics are used to determine the desired number of free
-// chunks of a given size. The desired number is chosen to be relative
-// to the end of a CMS sweep. The desired number at the end of a sweep
-// is the
-// count-at-end-of-previous-sweep (an amount that was enough)
-// - count-at-beginning-of-current-sweep (the excess)
-// + split-births (gains in this size during interval)
-// - split-deaths (demands on this size during interval)
-// where the interval is from the end of one sweep to the end of the
-// next.
-//
-// When sweeping the sweeper maintains an accumulated chunk which is
-// the chunk that is made up of chunks that have been coalesced. That
-// will be termed the left-hand chunk. A new chunk of garbage that
-// is being considered for coalescing will be referred to as the
-// right-hand chunk.
-//
-// When making a decision on whether to coalesce a right-hand chunk with
-// the current left-hand chunk, the current count vs. the desired count
-// of the left-hand chunk is considered. Also if the right-hand chunk
-// is near the large chunk at the end of the heap (see
-// ConcurrentMarkSweepGeneration::isNearLargestChunk()), then the
-// left-hand chunk is coalesced.
-//
-// When making a decision about whether to split a chunk, the desired count
-// vs. the current count of the candidate to be split is also considered.
-// If the candidate is underpopulated (currently fewer chunks than desired)
-// a chunk of an overpopulated (currently more chunks than desired) size may
-// be chosen. The "hint" associated with a free list, if non-null, points
-// to a free list which may be overpopulated.
-//
-
-void SweepClosure::do_already_free_chunk(FreeChunk* fc) {
- const size_t size = fc->size();
- // Chunks that cannot be coalesced are not in the
- // free lists.
- if (CMSTestInFreeList && !fc->cantCoalesce()) {
- assert(_sp->verify_chunk_in_free_list(fc),
- "free chunk should be in free lists");
- }
- // a chunk that is already free, should not have been
- // marked in the bit map
- HeapWord* const addr = (HeapWord*) fc;
- assert(!_bitMap->isMarked(addr), "free chunk should be unmarked");
- // Verify that the bit map has no bits marked between
- // addr and purported end of this block.
- _bitMap->verifyNoOneBitsInRange(addr + 1, addr + size);
-
- // Some chunks cannot be coalesced under any circumstances.
- // See the definition of cantCoalesce().
- if (!fc->cantCoalesce()) {
- // This chunk can potentially be coalesced.
- // All the work is done in
- do_post_free_or_garbage_chunk(fc, size);
- // Note that if the chunk is not coalescable (the else arm
- // below), we unconditionally flush, without needing to do
- // a "lookahead," as we do below.
- if (inFreeRange()) lookahead_and_flush(fc, size);
- } else {
- // Code path common to both original and adaptive free lists.
-
- // cant coalesce with previous block; this should be treated
- // as the end of a free run if any
- if (inFreeRange()) {
- // we kicked some butt; time to pick up the garbage
- assert(freeFinger() < addr, "freeFinger points too high");
- flush_cur_free_chunk(freeFinger(), pointer_delta(addr, freeFinger()));
- }
- // else, nothing to do, just continue
- }
-}
-
-size_t SweepClosure::do_garbage_chunk(FreeChunk* fc) {
- // This is a chunk of garbage. It is not in any free list.
- // Add it to a free list or let it possibly be coalesced into
- // a larger chunk.
- HeapWord* const addr = (HeapWord*) fc;
- const size_t size = CompactibleFreeListSpace::adjustObjectSize(oop(addr)->size());
-
- // Verify that the bit map has no bits marked between
- // addr and purported end of just dead object.
- _bitMap->verifyNoOneBitsInRange(addr + 1, addr + size);
- do_post_free_or_garbage_chunk(fc, size);
-
- assert(_limit >= addr + size,
- "A freshly garbage chunk can't possibly straddle over _limit");
- if (inFreeRange()) lookahead_and_flush(fc, size);
- return size;
-}
-
-size_t SweepClosure::do_live_chunk(FreeChunk* fc) {
- HeapWord* addr = (HeapWord*) fc;
- // The sweeper has just found a live object. Return any accumulated
- // left hand chunk to the free lists.
- if (inFreeRange()) {
- assert(freeFinger() < addr, "freeFinger points too high");
- flush_cur_free_chunk(freeFinger(), pointer_delta(addr, freeFinger()));
- }
-
- // This object is live: we'd normally expect this to be
- // an oop, and like to assert the following:
- // assert(oopDesc::is_oop(oop(addr)), "live block should be an oop");
- // However, as we commented above, this may be an object whose
- // header hasn't yet been initialized.
- size_t size;
- assert(_bitMap->isMarked(addr), "Tautology for this control point");
- if (_bitMap->isMarked(addr + 1)) {
- // Determine the size from the bit map, rather than trying to
- // compute it from the object header.
- HeapWord* nextOneAddr = _bitMap->getNextMarkedWordAddress(addr + 2);
- size = pointer_delta(nextOneAddr + 1, addr);
- assert(size == CompactibleFreeListSpace::adjustObjectSize(size),
- "alignment problem");
-
-#ifdef ASSERT
- if (oop(addr)->klass_or_null_acquire() != NULL) {
- // Ignore mark word because we are running concurrent with mutators
- assert(oopDesc::is_oop(oop(addr), true), "live block should be an oop");
- assert(size ==
- CompactibleFreeListSpace::adjustObjectSize(oop(addr)->size()),
- "P-mark and computed size do not agree");
- }
-#endif
-
- } else {
- // This should be an initialized object that's alive.
- assert(oop(addr)->klass_or_null_acquire() != NULL,
- "Should be an initialized object");
- // Ignore mark word because we are running concurrent with mutators
- assert(oopDesc::is_oop(oop(addr), true), "live block should be an oop");
- // Verify that the bit map has no bits marked between
- // addr and purported end of this block.
- size = CompactibleFreeListSpace::adjustObjectSize(oop(addr)->size());
- assert(size >= 3, "Necessary for Printezis marks to work");
- assert(!_bitMap->isMarked(addr+1), "Tautology for this control point");
- DEBUG_ONLY(_bitMap->verifyNoOneBitsInRange(addr+2, addr+size);)
- }
- return size;
-}
-
-void SweepClosure::do_post_free_or_garbage_chunk(FreeChunk* fc,
- size_t chunkSize) {
- // do_post_free_or_garbage_chunk() should only be called in the case
- // of the adaptive free list allocator.
- const bool fcInFreeLists = fc->is_free();
- assert((HeapWord*)fc <= _limit, "sweep invariant");
- if (CMSTestInFreeList && fcInFreeLists) {
- assert(_sp->verify_chunk_in_free_list(fc), "free chunk is not in free lists");
- }
-
- log_develop_trace(gc, sweep)(" -- pick up another chunk at " PTR_FORMAT " (" SIZE_FORMAT ")", p2i(fc), chunkSize);
-
- HeapWord* const fc_addr = (HeapWord*) fc;
-
- bool coalesce = false;
- const size_t left = pointer_delta(fc_addr, freeFinger());
- const size_t right = chunkSize;
- switch (FLSCoalescePolicy) {
- // numeric value forms a coalition aggressiveness metric
- case 0: { // never coalesce
- coalesce = false;
- break;
- }
- case 1: { // coalesce if left & right chunks on overpopulated lists
- coalesce = _sp->coalOverPopulated(left) &&
- _sp->coalOverPopulated(right);
- break;
- }
- case 2: { // coalesce if left chunk on overpopulated list (default)
- coalesce = _sp->coalOverPopulated(left);
- break;
- }
- case 3: { // coalesce if left OR right chunk on overpopulated list
- coalesce = _sp->coalOverPopulated(left) ||
- _sp->coalOverPopulated(right);
- break;
- }
- case 4: { // always coalesce
- coalesce = true;
- break;
- }
- default:
- ShouldNotReachHere();
- }
-
- // Should the current free range be coalesced?
- // If the chunk is in a free range and either we decided to coalesce above
- // or the chunk is near the large block at the end of the heap
- // (isNearLargestChunk() returns true), then coalesce this chunk.
- const bool doCoalesce = inFreeRange()
- && (coalesce || _g->isNearLargestChunk(fc_addr));
- if (doCoalesce) {
- // Coalesce the current free range on the left with the new
- // chunk on the right. If either is on a free list,
- // it must be removed from the list and stashed in the closure.
- if (freeRangeInFreeLists()) {
- FreeChunk* const ffc = (FreeChunk*)freeFinger();
- assert(ffc->size() == pointer_delta(fc_addr, freeFinger()),
- "Size of free range is inconsistent with chunk size.");
- if (CMSTestInFreeList) {
- assert(_sp->verify_chunk_in_free_list(ffc),
- "Chunk is not in free lists");
- }
- _sp->coalDeath(ffc->size());
- _sp->removeFreeChunkFromFreeLists(ffc);
- set_freeRangeInFreeLists(false);
- }
- if (fcInFreeLists) {
- _sp->coalDeath(chunkSize);
- assert(fc->size() == chunkSize,
- "The chunk has the wrong size or is not in the free lists");
- _sp->removeFreeChunkFromFreeLists(fc);
- }
- set_lastFreeRangeCoalesced(true);
- print_free_block_coalesced(fc);
- } else { // not in a free range and/or should not coalesce
- // Return the current free range and start a new one.
- if (inFreeRange()) {
- // In a free range but cannot coalesce with the right hand chunk.
- // Put the current free range into the free lists.
- flush_cur_free_chunk(freeFinger(),
- pointer_delta(fc_addr, freeFinger()));
- }
- // Set up for new free range. Pass along whether the right hand
- // chunk is in the free lists.
- initialize_free_range((HeapWord*)fc, fcInFreeLists);
- }
-}
-
-// Lookahead flush:
-// If we are tracking a free range, and this is the last chunk that
-// we'll look at because its end crosses past _limit, we'll preemptively
-// flush it along with any free range we may be holding on to. Note that
-// this can be the case only for an already free or freshly garbage
-// chunk. If this block is an object, it can never straddle
-// over _limit. The "straddling" occurs when _limit is set at
-// the previous end of the space when this cycle started, and
-// a subsequent heap expansion caused the previously co-terminal
-// free block to be coalesced with the newly expanded portion,
-// thus rendering _limit a non-block-boundary making it dangerous
-// for the sweeper to step over and examine.
-void SweepClosure::lookahead_and_flush(FreeChunk* fc, size_t chunk_size) {
- assert(inFreeRange(), "Should only be called if currently in a free range.");
- HeapWord* const eob = ((HeapWord*)fc) + chunk_size;
- assert(_sp->used_region().contains(eob - 1),
- "eob = " PTR_FORMAT " eob-1 = " PTR_FORMAT " _limit = " PTR_FORMAT
- " out of bounds wrt _sp = [" PTR_FORMAT "," PTR_FORMAT ")"
- " when examining fc = " PTR_FORMAT "(" SIZE_FORMAT ")",
- p2i(eob), p2i(eob-1), p2i(_limit), p2i(_sp->bottom()), p2i(_sp->end()), p2i(fc), chunk_size);
- if (eob >= _limit) {
- assert(eob == _limit || fc->is_free(), "Only a free chunk should allow us to cross over the limit");
- log_develop_trace(gc, sweep)("_limit " PTR_FORMAT " reached or crossed by block "
- "[" PTR_FORMAT "," PTR_FORMAT ") in space "
- "[" PTR_FORMAT "," PTR_FORMAT ")",
- p2i(_limit), p2i(fc), p2i(eob), p2i(_sp->bottom()), p2i(_sp->end()));
- // Return the storage we are tracking back into the free lists.
- log_develop_trace(gc, sweep)("Flushing ... ");
- assert(freeFinger() < eob, "Error");
- flush_cur_free_chunk( freeFinger(), pointer_delta(eob, freeFinger()));
- }
-}
-
-void SweepClosure::flush_cur_free_chunk(HeapWord* chunk, size_t size) {
- assert(inFreeRange(), "Should only be called if currently in a free range.");
- assert(size > 0,
- "A zero sized chunk cannot be added to the free lists.");
- if (!freeRangeInFreeLists()) {
- if (CMSTestInFreeList) {
- FreeChunk* fc = (FreeChunk*) chunk;
- fc->set_size(size);
- assert(!_sp->verify_chunk_in_free_list(fc),
- "chunk should not be in free lists yet");
- }
- log_develop_trace(gc, sweep)(" -- add free block " PTR_FORMAT " (" SIZE_FORMAT ") to free lists", p2i(chunk), size);
- // A new free range is going to be starting. The current
- // free range has not been added to the free lists yet or
- // was removed so add it back.
- // If the current free range was coalesced, then the death
- // of the free range was recorded. Record a birth now.
- if (lastFreeRangeCoalesced()) {
- _sp->coalBirth(size);
- }
- _sp->addChunkAndRepairOffsetTable(chunk, size,
- lastFreeRangeCoalesced());
- } else {
- log_develop_trace(gc, sweep)("Already in free list: nothing to flush");
- }
- set_inFreeRange(false);
- set_freeRangeInFreeLists(false);
-}
-
-// We take a break if we've been at this for a while,
-// so as to avoid monopolizing the locks involved.
-void SweepClosure::do_yield_work(HeapWord* addr) {
- // Return current free chunk being used for coalescing (if any)
- // to the appropriate freelist. After yielding, the next
- // free block encountered will start a coalescing range of
- // free blocks. If the next free block is adjacent to the
- // chunk just flushed, they will need to wait for the next
- // sweep to be coalesced.
- if (inFreeRange()) {
- flush_cur_free_chunk(freeFinger(), pointer_delta(addr, freeFinger()));
- }
-
- // First give up the locks, then yield, then re-lock.
- // We should probably use a constructor/destructor idiom to
- // do this unlock/lock or modify the MutexUnlocker class to
- // serve our purpose. XXX
- assert_lock_strong(_bitMap->lock());
- assert_lock_strong(_freelistLock);
- assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
- "CMS thread should hold CMS token");
- _bitMap->lock()->unlock();
- _freelistLock->unlock();
- ConcurrentMarkSweepThread::desynchronize(true);
- _collector->stopTimer();
- _collector->incrementYields();
-
- // See the comment in coordinator_yield()
- for (unsigned i = 0; i < CMSYieldSleepCount &&
- ConcurrentMarkSweepThread::should_yield() &&
- !CMSCollector::foregroundGCIsActive(); ++i) {
- os::naked_short_sleep(1);
- }
-
- ConcurrentMarkSweepThread::synchronize(true);
- _freelistLock->lock_without_safepoint_check();
- _bitMap->lock()->lock_without_safepoint_check();
- _collector->startTimer();
-}
-
-#ifndef PRODUCT
-// This is actually very useful in a product build if it can
-// be called from the debugger. Compile it into the product
-// as needed.
-bool debug_verify_chunk_in_free_list(FreeChunk* fc) {
- return debug_cms_space->verify_chunk_in_free_list(fc);
-}
-#endif
-
-void SweepClosure::print_free_block_coalesced(FreeChunk* fc) const {
- log_develop_trace(gc, sweep)("Sweep:coal_free_blk " PTR_FORMAT " (" SIZE_FORMAT ")",
- p2i(fc), fc->size());
-}
-
-// CMSIsAliveClosure
-bool CMSIsAliveClosure::do_object_b(oop obj) {
- HeapWord* addr = (HeapWord*)obj;
- return addr != NULL &&
- (!_span.contains(addr) || _bit_map->isMarked(addr));
-}
-
-CMSKeepAliveClosure::CMSKeepAliveClosure( CMSCollector* collector,
- MemRegion span,
- CMSBitMap* bit_map, CMSMarkStack* mark_stack,
- bool cpc):
- _collector(collector),
- _span(span),
- _mark_stack(mark_stack),
- _bit_map(bit_map),
- _concurrent_precleaning(cpc) {
- assert(!_span.is_empty(), "Empty span could spell trouble");
-}
-
-
-// CMSKeepAliveClosure: the serial version
-void CMSKeepAliveClosure::do_oop(oop obj) {
- HeapWord* addr = (HeapWord*)obj;
- if (_span.contains(addr) &&
- !_bit_map->isMarked(addr)) {
- _bit_map->mark(addr);
- bool simulate_overflow = false;
- NOT_PRODUCT(
- if (CMSMarkStackOverflowALot &&
- _collector->simulate_overflow()) {
- // simulate a stack overflow
- simulate_overflow = true;
- }
- )
- if (simulate_overflow || !_mark_stack->push(obj)) {
- if (_concurrent_precleaning) {
- // We dirty the overflown object and let the remark
- // phase deal with it.
- assert(_collector->overflow_list_is_empty(), "Error");
- // In the case of object arrays, we need to dirty all of
- // the cards that the object spans. No locking or atomics
- // are needed since no one else can be mutating the mod union
- // table.
- if (obj->is_objArray()) {
- size_t sz = obj->size();
- HeapWord* end_card_addr = align_up(addr + sz, CardTable::card_size);
- MemRegion redirty_range = MemRegion(addr, end_card_addr);
- assert(!redirty_range.is_empty(), "Arithmetical tautology");
- _collector->_modUnionTable.mark_range(redirty_range);
- } else {
- _collector->_modUnionTable.mark(addr);
- }
- _collector->_ser_kac_preclean_ovflw++;
- } else {
- _collector->push_on_overflow_list(obj);
- _collector->_ser_kac_ovflw++;
- }
- }
- }
-}
-
-// CMSParKeepAliveClosure: a parallel version of the above.
-// The work queues are private to each closure (thread),
-// but (may be) available for stealing by other threads.
-void CMSParKeepAliveClosure::do_oop(oop obj) {
- HeapWord* addr = (HeapWord*)obj;
- if (_span.contains(addr) &&
- !_bit_map->isMarked(addr)) {
- // In general, during recursive tracing, several threads
- // may be concurrently getting here; the first one to
- // "tag" it, claims it.
- if (_bit_map->par_mark(addr)) {
- bool res = _work_queue->push(obj);
- assert(res, "Low water mark should be much less than capacity");
- // Do a recursive trim in the hope that this will keep
- // stack usage lower, but leave some oops for potential stealers
- trim_queue(_low_water_mark);
- } // Else, another thread got there first
- }
-}
-
-void CMSParKeepAliveClosure::trim_queue(uint max) {
- while (_work_queue->size() > max) {
- oop new_oop;
- if (_work_queue->pop_local(new_oop)) {
- assert(new_oop != NULL && oopDesc::is_oop(new_oop), "Expected an oop");
- assert(_bit_map->isMarked((HeapWord*)new_oop),
- "no white objects on this stack!");
- assert(_span.contains((HeapWord*)new_oop), "Out of bounds oop");
- // iterate over the oops in this oop, marking and pushing
- // the ones in CMS heap (i.e. in _span).
- new_oop->oop_iterate(&_mark_and_push);
- }
- }
-}
-
-CMSInnerParMarkAndPushClosure::CMSInnerParMarkAndPushClosure(
- CMSCollector* collector,
- MemRegion span, CMSBitMap* bit_map,
- OopTaskQueue* work_queue):
- _collector(collector),
- _span(span),
- _work_queue(work_queue),
- _bit_map(bit_map) { }
-
-void CMSInnerParMarkAndPushClosure::do_oop(oop obj) {
- HeapWord* addr = (HeapWord*)obj;
- if (_span.contains(addr) &&
- !_bit_map->isMarked(addr)) {
- if (_bit_map->par_mark(addr)) {
- bool simulate_overflow = false;
- NOT_PRODUCT(
- if (CMSMarkStackOverflowALot &&
- _collector->par_simulate_overflow()) {
- // simulate a stack overflow
- simulate_overflow = true;
- }
- )
- if (simulate_overflow || !_work_queue->push(obj)) {
- _collector->par_push_on_overflow_list(obj);
- _collector->_par_kac_ovflw++;
- }
- } // Else another thread got there already
- }
-}
-
-//////////////////////////////////////////////////////////////////
-// CMSExpansionCause /////////////////////////////
-//////////////////////////////////////////////////////////////////
-const char* CMSExpansionCause::to_string(CMSExpansionCause::Cause cause) {
- switch (cause) {
- case _no_expansion:
- return "No expansion";
- case _satisfy_free_ratio:
- return "Free ratio";
- case _satisfy_promotion:
- return "Satisfy promotion";
- case _satisfy_allocation:
- return "allocation";
- case _allocate_par_lab:
- return "Par LAB";
- case _allocate_par_spooling_space:
- return "Par Spooling Space";
- case _adaptive_size_policy:
- return "Ergonomics";
- default:
- return "unknown";
- }
-}
-
-void CMSDrainMarkingStackClosure::do_void() {
- // the max number to take from overflow list at a time
- const size_t num = _mark_stack->capacity()/4;
- assert(!_concurrent_precleaning || _collector->overflow_list_is_empty(),
- "Overflow list should be NULL during concurrent phases");
- while (!_mark_stack->isEmpty() ||
- // if stack is empty, check the overflow list
- _collector->take_from_overflow_list(num, _mark_stack)) {
- oop obj = _mark_stack->pop();
- HeapWord* addr = (HeapWord*)obj;
- assert(_span.contains(addr), "Should be within span");
- assert(_bit_map->isMarked(addr), "Should be marked");
- assert(oopDesc::is_oop(obj), "Should be an oop");
- obj->oop_iterate(_keep_alive);
- }
-}
-
-void CMSParDrainMarkingStackClosure::do_void() {
- // drain queue
- trim_queue(0);
-}
-
-// Trim our work_queue so its length is below max at return
-void CMSParDrainMarkingStackClosure::trim_queue(uint max) {
- while (_work_queue->size() > max) {
- oop new_oop;
- if (_work_queue->pop_local(new_oop)) {
- assert(oopDesc::is_oop(new_oop), "Expected an oop");
- assert(_bit_map->isMarked((HeapWord*)new_oop),
- "no white objects on this stack!");
- assert(_span.contains((HeapWord*)new_oop), "Out of bounds oop");
- // iterate over the oops in this oop, marking and pushing
- // the ones in CMS heap (i.e. in _span).
- new_oop->oop_iterate(&_mark_and_push);
- }
- }
-}
-
-////////////////////////////////////////////////////////////////////
-// Support for Marking Stack Overflow list handling and related code
-////////////////////////////////////////////////////////////////////
-// Much of the following code is similar in shape and spirit to the
-// code used in ParNewGC. We should try and share that code
-// as much as possible in the future.
-
-#ifndef PRODUCT
-// Debugging support for CMSStackOverflowALot
-
-// It's OK to call this multi-threaded; the worst thing
-// that can happen is that we'll get a bunch of closely
-// spaced simulated overflows, but that's OK, in fact
-// probably good as it would exercise the overflow code
-// under contention.
-bool CMSCollector::simulate_overflow() {
- if (_overflow_counter-- <= 0) { // just being defensive
- _overflow_counter = CMSMarkStackOverflowInterval;
- return true;
- } else {
- return false;
- }
-}
-
-bool CMSCollector::par_simulate_overflow() {
- return simulate_overflow();
-}
-#endif
-
-// Single-threaded
-bool CMSCollector::take_from_overflow_list(size_t num, CMSMarkStack* stack) {
- assert(stack->isEmpty(), "Expected precondition");
- assert(stack->capacity() > num, "Shouldn't bite more than can chew");
- size_t i = num;
- oop cur = _overflow_list;
- const markWord proto = markWord::prototype();
- NOT_PRODUCT(ssize_t n = 0;)
- for (oop next; i > 0 && cur != NULL; cur = next, i--) {
- next = oop(cur->mark_raw().to_pointer());
- cur->set_mark_raw(proto); // until proven otherwise
- assert(oopDesc::is_oop(cur), "Should be an oop");
- bool res = stack->push(cur);
- assert(res, "Bit off more than can chew?");
- NOT_PRODUCT(n++;)
- }
- _overflow_list = cur;
-#ifndef PRODUCT
- assert(_num_par_pushes >= n, "Too many pops?");
- _num_par_pushes -=n;
-#endif
- return !stack->isEmpty();
-}
-
-#define BUSY (cast_to_oop<intptr_t>(0x1aff1aff))
-// (MT-safe) Get a prefix of at most "num" from the list.
-// The overflow list is chained through the mark word of
-// each object in the list. We fetch the entire list,
-// break off a prefix of the right size and return the
-// remainder. If other threads try to take objects from
-// the overflow list at that time, they will wait for
-// some time to see if data becomes available. If (and
-// only if) another thread places one or more object(s)
-// on the global list before we have returned the suffix
-// to the global list, we will walk down our local list
-// to find its end and append the global list to
-// our suffix before returning it. This suffix walk can
-// prove to be expensive (quadratic in the amount of traffic)
-// when there are many objects in the overflow list and
-// there is much producer-consumer contention on the list.
-// *NOTE*: The overflow list manipulation code here and
-// in ParNewGeneration:: are very similar in shape,
-// except that in the ParNew case we use the old (from/eden)
-// copy of the object to thread the list via its klass word.
-// Because of the common code, if you make any changes in
-// the code below, please check the ParNew version to see if
-// similar changes might be needed.
-// CR 6797058 has been filed to consolidate the common code.
-bool CMSCollector::par_take_from_overflow_list(size_t num,
- OopTaskQueue* work_q,
- int no_of_gc_threads) {
- assert(work_q->size() == 0, "First empty local work queue");
- assert(num < work_q->max_elems(), "Can't bite more than we can chew");
- if (_overflow_list == NULL) {
- return false;
- }
- // Grab the entire list; we'll put back a suffix
- oop prefix = cast_to_oop(Atomic::xchg((oopDesc*)BUSY, &_overflow_list));
- // Before "no_of_gc_threads" was introduced CMSOverflowSpinCount was
- // set to ParallelGCThreads.
- size_t CMSOverflowSpinCount = (size_t) no_of_gc_threads; // was ParallelGCThreads;
- size_t sleep_time_millis = MAX2((size_t)1, num/100);
- // If the list is busy, we spin for a short while,
- // sleeping between attempts to get the list.
- for (size_t spin = 0; prefix == BUSY && spin < CMSOverflowSpinCount; spin++) {
- os::naked_sleep(sleep_time_millis);
- if (_overflow_list == NULL) {
- // Nothing left to take
- return false;
- } else if (_overflow_list != BUSY) {
- // Try and grab the prefix
- prefix = cast_to_oop(Atomic::xchg((oopDesc*)BUSY, &_overflow_list));
- }
- }
- // If the list was found to be empty, or we spun long
- // enough, we give up and return empty-handed. If we leave
- // the list in the BUSY state below, it must be the case that
- // some other thread holds the overflow list and will set it
- // to a non-BUSY state in the future.
- if (prefix == NULL || prefix == BUSY) {
- // Nothing to take or waited long enough
- if (prefix == NULL) {
- // Write back the NULL in case we overwrote it with BUSY above
- // and it is still the same value.
- Atomic::cmpxchg((oopDesc*)NULL, &_overflow_list, (oopDesc*)BUSY);
- }
- return false;
- }
- assert(prefix != NULL && prefix != BUSY, "Error");
- size_t i = num;
- oop cur = prefix;
- // Walk down the first "num" objects, unless we reach the end.
- for (; i > 1 && cur->mark_raw().to_pointer() != NULL; cur = oop(cur->mark_raw().to_pointer()), i--);
- if (cur->mark_raw().to_pointer() == NULL) {
- // We have "num" or fewer elements in the list, so there
- // is nothing to return to the global list.
- // Write back the NULL in lieu of the BUSY we wrote
- // above, if it is still the same value.
- if (_overflow_list == BUSY) {
- Atomic::cmpxchg((oopDesc*)NULL, &_overflow_list, (oopDesc*)BUSY);
- }
- } else {
- // Chop off the suffix and return it to the global list.
- assert(cur->mark_raw().to_pointer() != (void*)BUSY, "Error");
- oop suffix_head = oop(cur->mark_raw().to_pointer()); // suffix will be put back on global list
- cur->set_mark_raw(markWord::from_pointer(NULL)); // break off suffix
- // It's possible that the list is still in the empty(busy) state
- // we left it in a short while ago; in that case we may be
- // able to place back the suffix without incurring the cost
- // of a walk down the list.
- oop observed_overflow_list = _overflow_list;
- oop cur_overflow_list = observed_overflow_list;
- bool attached = false;
- while (observed_overflow_list == BUSY || observed_overflow_list == NULL) {
- observed_overflow_list =
- Atomic::cmpxchg((oopDesc*)suffix_head, &_overflow_list, (oopDesc*)cur_overflow_list);
- if (cur_overflow_list == observed_overflow_list) {
- attached = true;
- break;
- } else cur_overflow_list = observed_overflow_list;
- }
- if (!attached) {
- // Too bad, someone else sneaked in (at least) an element; we'll need
- // to do a splice. Find tail of suffix so we can prepend suffix to global
- // list.
- for (cur = suffix_head; cur->mark_raw().to_pointer() != NULL; cur = (oop)(cur->mark_raw().to_pointer()));
- oop suffix_tail = cur;
- assert(suffix_tail != NULL && suffix_tail->mark_raw().to_pointer() == NULL,
- "Tautology");
- observed_overflow_list = _overflow_list;
- do {
- cur_overflow_list = observed_overflow_list;
- if (cur_overflow_list != BUSY) {
- // Do the splice ...
- suffix_tail->set_mark_raw(markWord::from_pointer((void*)cur_overflow_list));
- } else { // cur_overflow_list == BUSY
- suffix_tail->set_mark_raw(markWord::from_pointer(NULL));
- }
- // ... and try to place spliced list back on overflow_list ...
- observed_overflow_list =
- Atomic::cmpxchg((oopDesc*)suffix_head, &_overflow_list, (oopDesc*)cur_overflow_list);
- } while (cur_overflow_list != observed_overflow_list);
- // ... until we have succeeded in doing so.
- }
- }
-
- // Push the prefix elements on work_q
- assert(prefix != NULL, "control point invariant");
- const markWord proto = markWord::prototype();
- oop next;
- NOT_PRODUCT(ssize_t n = 0;)
- for (cur = prefix; cur != NULL; cur = next) {
- next = oop(cur->mark_raw().to_pointer());
- cur->set_mark_raw(proto); // until proven otherwise
- assert(oopDesc::is_oop(cur), "Should be an oop");
- bool res = work_q->push(cur);
- assert(res, "Bit off more than we can chew?");
- NOT_PRODUCT(n++;)
- }
-#ifndef PRODUCT
- assert(_num_par_pushes >= n, "Too many pops?");
- Atomic::sub(n, &_num_par_pushes);
-#endif
- return true;
-}
-
-// Single-threaded
-void CMSCollector::push_on_overflow_list(oop p) {
- NOT_PRODUCT(_num_par_pushes++;)
- assert(oopDesc::is_oop(p), "Not an oop");
- preserve_mark_if_necessary(p);
- p->set_mark_raw(markWord::from_pointer(_overflow_list));
- _overflow_list = p;
-}
-
-// Multi-threaded; use CAS to prepend to overflow list
-void CMSCollector::par_push_on_overflow_list(oop p) {
- NOT_PRODUCT(Atomic::inc(&_num_par_pushes);)
- assert(oopDesc::is_oop(p), "Not an oop");
- par_preserve_mark_if_necessary(p);
- oop observed_overflow_list = _overflow_list;
- oop cur_overflow_list;
- do {
- cur_overflow_list = observed_overflow_list;
- if (cur_overflow_list != BUSY) {
- p->set_mark_raw(markWord::from_pointer((void*)cur_overflow_list));
- } else {
- p->set_mark_raw(markWord::from_pointer(NULL));
- }
- observed_overflow_list =
- Atomic::cmpxchg((oopDesc*)p, &_overflow_list, (oopDesc*)cur_overflow_list);
- } while (cur_overflow_list != observed_overflow_list);
-}
-#undef BUSY
-
-// Single threaded
-// General Note on GrowableArray: pushes may silently fail
-// because we are (temporarily) out of C-heap for expanding
-// the stack. The problem is quite ubiquitous and affects
-// a lot of code in the JVM. The prudent thing for GrowableArray
-// to do (for now) is to exit with an error. However, that may
-// be too draconian in some cases because the caller may be
-// able to recover without much harm. For such cases, we
-// should probably introduce a "soft_push" method which returns
-// an indication of success or failure with the assumption that
-// the caller may be able to recover from a failure; code in
-// the VM can then be changed, incrementally, to deal with such
-// failures where possible, thus, incrementally hardening the VM
-// in such low resource situations.
-void CMSCollector::preserve_mark_work(oop p, markWord m) {
- _preserved_oop_stack.push(p);
- _preserved_mark_stack.push(m);
- assert(m == p->mark_raw(), "Mark word changed");
- assert(_preserved_oop_stack.size() == _preserved_mark_stack.size(),
- "bijection");
-}
-
-// Single threaded
-void CMSCollector::preserve_mark_if_necessary(oop p) {
- markWord m = p->mark_raw();
- if (p->mark_must_be_preserved(m)) {
- preserve_mark_work(p, m);
- }
-}
-
-void CMSCollector::par_preserve_mark_if_necessary(oop p) {
- markWord m = p->mark_raw();
- if (p->mark_must_be_preserved(m)) {
- MutexLocker x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag);
- // Even though we read the mark word without holding
- // the lock, we are assured that it will not change
- // because we "own" this oop, so no other thread can
- // be trying to push it on the overflow list; see
- // the assertion in preserve_mark_work() that checks
- // that m == p->mark_raw().
- preserve_mark_work(p, m);
- }
-}
-
-// We should be able to do this multi-threaded,
-// a chunk of stack being a task (this is
-// correct because each oop only ever appears
-// once in the overflow list. However, it's
-// not very easy to completely overlap this with
-// other operations, so will generally not be done
-// until all work's been completed. Because we
-// expect the preserved oop stack (set) to be small,
-// it's probably fine to do this single-threaded.
-// We can explore cleverer concurrent/overlapped/parallel
-// processing of preserved marks if we feel the
-// need for this in the future. Stack overflow should
-// be so rare in practice and, when it happens, its
-// effect on performance so great that this will
-// likely just be in the noise anyway.
-void CMSCollector::restore_preserved_marks_if_any() {
- assert(SafepointSynchronize::is_at_safepoint(),
- "world should be stopped");
- assert(Thread::current()->is_ConcurrentGC_thread() ||
- Thread::current()->is_VM_thread(),
- "should be single-threaded");
- assert(_preserved_oop_stack.size() == _preserved_mark_stack.size(),
- "bijection");
-
- while (!_preserved_oop_stack.is_empty()) {
- oop p = _preserved_oop_stack.pop();
- assert(oopDesc::is_oop(p), "Should be an oop");
- assert(_span.contains(p), "oop should be in _span");
- assert(p->mark_raw() == markWord::prototype(),
- "Set when taken from overflow list");
- markWord m = _preserved_mark_stack.pop();
- p->set_mark_raw(m);
- }
- assert(_preserved_mark_stack.is_empty() && _preserved_oop_stack.is_empty(),
- "stacks were cleared above");
-}
-
-#ifndef PRODUCT
-bool CMSCollector::no_preserved_marks() const {
- return _preserved_mark_stack.is_empty() && _preserved_oop_stack.is_empty();
-}
-#endif
-
-// Transfer some number of overflown objects to usual marking
-// stack. Return true if some objects were transferred.
-bool MarkRefsIntoAndScanClosure::take_from_overflow_list() {
- size_t num = MIN2((size_t)(_mark_stack->capacity() - _mark_stack->length())/4,
- (size_t)ParGCDesiredObjsFromOverflowList);
-
- bool res = _collector->take_from_overflow_list(num, _mark_stack);
- assert(_collector->overflow_list_is_empty() || res,
- "If list is not empty, we should have taken something");
- assert(!res || !_mark_stack->isEmpty(),
- "If we took something, it should now be on our stack");
- return res;
-}
-
-size_t MarkDeadObjectsClosure::do_blk(HeapWord* addr) {
- size_t res = _sp->block_size_no_stall(addr, _collector);
- if (_sp->block_is_obj(addr)) {
- if (_live_bit_map->isMarked(addr)) {
- // It can't have been dead in a previous cycle
- guarantee(!_dead_bit_map->isMarked(addr), "No resurrection!");
- } else {
- _dead_bit_map->mark(addr); // mark the dead object
- }
- }
- // Could be 0, if the block size could not be computed without stalling.
- return res;
-}
-
-TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase, GCCause::Cause cause): TraceMemoryManagerStats() {
- GCMemoryManager* manager = CMSHeap::heap()->old_manager();
- switch (phase) {
- case CMSCollector::InitialMarking:
- initialize(manager /* GC manager */ ,
- cause /* cause of the GC */,
- true /* allMemoryPoolsAffected */,
- true /* recordGCBeginTime */,
- true /* recordPreGCUsage */,
- false /* recordPeakUsage */,
- false /* recordPostGCusage */,
- true /* recordAccumulatedGCTime */,
- false /* recordGCEndTime */,
- false /* countCollection */ );
- break;
-
- case CMSCollector::FinalMarking:
- initialize(manager /* GC manager */ ,
- cause /* cause of the GC */,
- true /* allMemoryPoolsAffected */,
- false /* recordGCBeginTime */,
- false /* recordPreGCUsage */,
- false /* recordPeakUsage */,
- false /* recordPostGCusage */,
- true /* recordAccumulatedGCTime */,
- false /* recordGCEndTime */,
- false /* countCollection */ );
- break;
-
- case CMSCollector::Sweeping:
- initialize(manager /* GC manager */ ,
- cause /* cause of the GC */,
- true /* allMemoryPoolsAffected */,
- false /* recordGCBeginTime */,
- false /* recordPreGCUsage */,
- true /* recordPeakUsage */,
- true /* recordPostGCusage */,
- false /* recordAccumulatedGCTime */,
- true /* recordGCEndTime */,
- true /* countCollection */ );
- break;
-
- default:
- ShouldNotReachHere();
- }
-}
--- a/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1796 +0,0 @@
-/*
- * Copyright (c) 2001, 2019, 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_GC_CMS_CONCURRENTMARKSWEEPGENERATION_HPP
-#define SHARE_GC_CMS_CONCURRENTMARKSWEEPGENERATION_HPP
-
-#include "gc/cms/cmsOopClosures.hpp"
-#include "gc/cms/gSpaceCounters.hpp"
-#include "gc/cms/yieldingWorkgroup.hpp"
-#include "gc/shared/cardGeneration.hpp"
-#include "gc/shared/gcHeapSummary.hpp"
-#include "gc/shared/gcStats.hpp"
-#include "gc/shared/gcWhen.hpp"
-#include "gc/shared/generationCounters.hpp"
-#include "gc/shared/space.hpp"
-#include "gc/shared/taskqueue.hpp"
-#include "logging/log.hpp"
-#include "memory/iterator.hpp"
-#include "memory/virtualspace.hpp"
-#include "runtime/mutexLocker.hpp"
-#include "services/memoryService.hpp"
-#include "utilities/bitMap.hpp"
-#include "utilities/stack.hpp"
-
-// ConcurrentMarkSweepGeneration is in support of a concurrent
-// mark-sweep old generation in the Detlefs-Printezis--Boehm-Demers-Schenker
-// style. We assume, for now, that this generation is always the
-// seniormost generation and for simplicity
-// in the first implementation, that this generation is a single compactible
-// space. Neither of these restrictions appears essential, and will be
-// relaxed in the future when more time is available to implement the
-// greater generality (and there's a need for it).
-//
-// Concurrent mode failures are currently handled by
-// means of a sliding mark-compact.
-
-class AdaptiveSizePolicy;
-class CMSCollector;
-class CMSConcMarkingTask;
-class CMSGCAdaptivePolicyCounters;
-class CMSTracer;
-class ConcurrentGCTimer;
-class ConcurrentMarkSweepGeneration;
-class ConcurrentMarkSweepPolicy;
-class ConcurrentMarkSweepThread;
-class CompactibleFreeListSpace;
-class FreeChunk;
-class ParNewGeneration;
-class PromotionInfo;
-class ScanMarkedObjectsAgainCarefullyClosure;
-class SerialOldTracer;
-
-// A generic CMS bit map. It's the basis for both the CMS marking bit map
-// as well as for the mod union table (in each case only a subset of the
-// methods are used). This is essentially a wrapper around the BitMap class,
-// with one bit per (1<<_shifter) HeapWords. (i.e. for the marking bit map,
-// we have _shifter == 0. and for the mod union table we have
-// shifter == CardTable::card_shift - LogHeapWordSize.)
-// XXX 64-bit issues in BitMap?
-class CMSBitMap {
- friend class VMStructs;
-
- HeapWord* _bmStartWord; // base address of range covered by map
- size_t _bmWordSize; // map size (in #HeapWords covered)
- const int _shifter; // shifts to convert HeapWord to bit position
- VirtualSpace _virtual_space; // underlying the bit map
- BitMapView _bm; // the bit map itself
- Mutex* const _lock; // mutex protecting _bm;
-
- public:
- // constructor
- CMSBitMap(int shifter, int mutex_rank, const char* mutex_name);
-
- // allocates the actual storage for the map
- bool allocate(MemRegion mr);
- // field getter
- Mutex* lock() const { return _lock; }
- // locking verifier convenience function
- void assert_locked() const PRODUCT_RETURN;
-
- // inquiries
- HeapWord* startWord() const { return _bmStartWord; }
- size_t sizeInWords() const { return _bmWordSize; }
- size_t sizeInBits() const { return _bm.size(); }
- // the following is one past the last word in space
- HeapWord* endWord() const { return _bmStartWord + _bmWordSize; }
-
- // reading marks
- bool isMarked(HeapWord* addr) const;
- bool par_isMarked(HeapWord* addr) const; // do not lock checks
- bool isUnmarked(HeapWord* addr) const;
- bool isAllClear() const;
-
- // writing marks
- void mark(HeapWord* addr);
- // For marking by parallel GC threads;
- // returns true if we did, false if another thread did
- bool par_mark(HeapWord* addr);
-
- void mark_range(MemRegion mr);
- void par_mark_range(MemRegion mr);
- void mark_large_range(MemRegion mr);
- void par_mark_large_range(MemRegion mr);
- void par_clear(HeapWord* addr); // For unmarking by parallel GC threads.
- void clear_range(MemRegion mr);
- void par_clear_range(MemRegion mr);
- void clear_large_range(MemRegion mr);
- void par_clear_large_range(MemRegion mr);
- void clear_all();
- void clear_all_incrementally(); // Not yet implemented!!
-
- NOT_PRODUCT(
- // checks the memory region for validity
- void region_invariant(MemRegion mr);
- )
-
- // iteration
- void iterate(BitMapClosure* cl) {
- _bm.iterate(cl);
- }
- void iterate(BitMapClosure* cl, HeapWord* left, HeapWord* right);
- void dirty_range_iterate_clear(MemRegionClosure* cl);
- void dirty_range_iterate_clear(MemRegion mr, MemRegionClosure* cl);
-
- // auxiliary support for iteration
- HeapWord* getNextMarkedWordAddress(HeapWord* addr) const;
- HeapWord* getNextMarkedWordAddress(HeapWord* start_addr,
- HeapWord* end_addr) const;
- HeapWord* getNextUnmarkedWordAddress(HeapWord* addr) const;
- HeapWord* getNextUnmarkedWordAddress(HeapWord* start_addr,
- HeapWord* end_addr) const;
- MemRegion getAndClearMarkedRegion(HeapWord* addr);
- MemRegion getAndClearMarkedRegion(HeapWord* start_addr,
- HeapWord* end_addr);
-
- // conversion utilities
- HeapWord* offsetToHeapWord(size_t offset) const;
- size_t heapWordToOffset(HeapWord* addr) const;
- size_t heapWordDiffToOffsetDiff(size_t diff) const;
-
- void print_on_error(outputStream* st, const char* prefix) const;
-
- // debugging
- // is this address range covered by the bit-map?
- NOT_PRODUCT(
- bool covers(MemRegion mr) const;
- bool covers(HeapWord* start, size_t size = 0) const;
- )
- void verifyNoOneBitsInRange(HeapWord* left, HeapWord* right) PRODUCT_RETURN;
-};
-
-// Represents a marking stack used by the CMS collector.
-// Ideally this should be GrowableArray<> just like MSC's marking stack(s).
-class CMSMarkStack: public CHeapObj<mtGC> {
- friend class CMSCollector; // To get at expansion stats further below.
-
- VirtualSpace _virtual_space; // Space for the stack
- oop* _base; // Bottom of stack
- size_t _index; // One more than last occupied index
- size_t _capacity; // Max #elements
- Mutex _par_lock; // An advisory lock used in case of parallel access
- NOT_PRODUCT(size_t _max_depth;) // Max depth plumbed during run
-
- protected:
- size_t _hit_limit; // We hit max stack size limit
- size_t _failed_double; // We failed expansion before hitting limit
-
- public:
- CMSMarkStack():
- _par_lock(Mutex::event, "CMSMarkStack._par_lock", true,
- Monitor::_safepoint_check_never),
- _hit_limit(0),
- _failed_double(0) {}
-
- bool allocate(size_t size);
-
- size_t capacity() const { return _capacity; }
-
- oop pop() {
- if (!isEmpty()) {
- return _base[--_index] ;
- }
- return NULL;
- }
-
- bool push(oop ptr) {
- if (isFull()) {
- return false;
- } else {
- _base[_index++] = ptr;
- NOT_PRODUCT(_max_depth = MAX2(_max_depth, _index));
- return true;
- }
- }
-
- bool isEmpty() const { return _index == 0; }
- bool isFull() const {
- assert(_index <= _capacity, "buffer overflow");
- return _index == _capacity;
- }
-
- size_t length() { return _index; }
-
- // "Parallel versions" of some of the above
- oop par_pop() {
- // lock and pop
- MutexLocker x(&_par_lock, Mutex::_no_safepoint_check_flag);
- return pop();
- }
-
- bool par_push(oop ptr) {
- // lock and push
- MutexLocker x(&_par_lock, Mutex::_no_safepoint_check_flag);
- return push(ptr);
- }
-
- // Forcibly reset the stack, losing all of its contents.
- void reset() {
- _index = 0;
- }
-
- // Expand the stack, typically in response to an overflow condition.
- void expand();
-
- // Compute the least valued stack element.
- oop least_value(HeapWord* low) {
- HeapWord* least = low;
- for (size_t i = 0; i < _index; i++) {
- least = MIN2(least, (HeapWord*)_base[i]);
- }
- return (oop)least;
- }
-
- // Exposed here to allow stack expansion in || case.
- Mutex* par_lock() { return &_par_lock; }
-};
-
-class CardTableRS;
-class CMSParGCThreadState;
-
-class ModUnionClosure: public MemRegionClosure {
- protected:
- CMSBitMap* _t;
- public:
- ModUnionClosure(CMSBitMap* t): _t(t) { }
- void do_MemRegion(MemRegion mr);
-};
-
-class ModUnionClosurePar: public ModUnionClosure {
- public:
- ModUnionClosurePar(CMSBitMap* t): ModUnionClosure(t) { }
- void do_MemRegion(MemRegion mr);
-};
-
-// Survivor Chunk Array in support of parallelization of
-// Survivor Space rescan.
-class ChunkArray: public CHeapObj<mtGC> {
- size_t _index;
- size_t _capacity;
- size_t _overflows;
- HeapWord** _array; // storage for array
-
- public:
- ChunkArray() : _index(0), _capacity(0), _overflows(0), _array(NULL) {}
- ChunkArray(HeapWord** a, size_t c):
- _index(0), _capacity(c), _overflows(0), _array(a) {}
-
- HeapWord** array() { return _array; }
- void set_array(HeapWord** a) { _array = a; }
-
- size_t capacity() { return _capacity; }
- void set_capacity(size_t c) { _capacity = c; }
-
- size_t end() {
- assert(_index <= capacity(),
- "_index (" SIZE_FORMAT ") > _capacity (" SIZE_FORMAT "): out of bounds",
- _index, _capacity);
- return _index;
- } // exclusive
-
- HeapWord* nth(size_t n) {
- assert(n < end(), "Out of bounds access");
- return _array[n];
- }
-
- void reset() {
- _index = 0;
- if (_overflows > 0) {
- log_trace(gc)("CMS: ChunkArray[" SIZE_FORMAT "] overflowed " SIZE_FORMAT " times", _capacity, _overflows);
- }
- _overflows = 0;
- }
-
- void record_sample(HeapWord* p, size_t sz) {
- // For now we do not do anything with the size
- if (_index < _capacity) {
- _array[_index++] = p;
- } else {
- ++_overflows;
- assert(_index == _capacity,
- "_index (" SIZE_FORMAT ") > _capacity (" SIZE_FORMAT
- "): out of bounds at overflow#" SIZE_FORMAT,
- _index, _capacity, _overflows);
- }
- }
-};
-
-//
-// Timing, allocation and promotion statistics for gc scheduling and incremental
-// mode pacing. Most statistics are exponential averages.
-//
-class CMSStats {
- private:
- ConcurrentMarkSweepGeneration* const _cms_gen; // The cms (old) gen.
-
- // The following are exponential averages with factor alpha:
- // avg = (100 - alpha) * avg + alpha * cur_sample
- //
- // The durations measure: end_time[n] - start_time[n]
- // The periods measure: start_time[n] - start_time[n-1]
- //
- // The cms period and duration include only concurrent collections; time spent
- // in foreground cms collections due to System.gc() or because of a failure to
- // keep up are not included.
- //
- // There are 3 alphas to "bootstrap" the statistics. The _saved_alpha is the
- // real value, but is used only after the first period. A value of 100 is
- // used for the first sample so it gets the entire weight.
- unsigned int _saved_alpha; // 0-100
- unsigned int _gc0_alpha;
- unsigned int _cms_alpha;
-
- double _gc0_duration;
- double _gc0_period;
- size_t _gc0_promoted; // bytes promoted per gc0
- double _cms_duration;
- double _cms_duration_pre_sweep; // time from initiation to start of sweep
- double _cms_period;
- size_t _cms_allocated; // bytes of direct allocation per gc0 period
-
- // Timers.
- elapsedTimer _cms_timer;
- TimeStamp _gc0_begin_time;
- TimeStamp _cms_begin_time;
- TimeStamp _cms_end_time;
-
- // Snapshots of the amount used in the CMS generation.
- size_t _cms_used_at_gc0_begin;
- size_t _cms_used_at_gc0_end;
- size_t _cms_used_at_cms_begin;
-
- // Used to prevent the duty cycle from being reduced in the middle of a cms
- // cycle.
- bool _allow_duty_cycle_reduction;
-
- enum {
- _GC0_VALID = 0x1,
- _CMS_VALID = 0x2,
- _ALL_VALID = _GC0_VALID | _CMS_VALID
- };
-
- unsigned int _valid_bits;
-
- protected:
- // In support of adjusting of cms trigger ratios based on history
- // of concurrent mode failure.
- double cms_free_adjustment_factor(size_t free) const;
- void adjust_cms_free_adjustment_factor(bool fail, size_t free);
-
- public:
- CMSStats(ConcurrentMarkSweepGeneration* cms_gen,
- unsigned int alpha = CMSExpAvgFactor);
-
- // Whether or not the statistics contain valid data; higher level statistics
- // cannot be called until this returns true (they require at least one young
- // gen and one cms cycle to have completed).
- bool valid() const;
-
- // Record statistics.
- void record_gc0_begin();
- void record_gc0_end(size_t cms_gen_bytes_used);
- void record_cms_begin();
- void record_cms_end();
-
- // Allow management of the cms timer, which must be stopped/started around
- // yield points.
- elapsedTimer& cms_timer() { return _cms_timer; }
- void start_cms_timer() { _cms_timer.start(); }
- void stop_cms_timer() { _cms_timer.stop(); }
-
- // Basic statistics; units are seconds or bytes.
- double gc0_period() const { return _gc0_period; }
- double gc0_duration() const { return _gc0_duration; }
- size_t gc0_promoted() const { return _gc0_promoted; }
- double cms_period() const { return _cms_period; }
- double cms_duration() const { return _cms_duration; }
- size_t cms_allocated() const { return _cms_allocated; }
-
- size_t cms_used_at_gc0_end() const { return _cms_used_at_gc0_end;}
-
- // Seconds since the last background cms cycle began or ended.
- double cms_time_since_begin() const;
- double cms_time_since_end() const;
-
- // Higher level statistics--caller must check that valid() returns true before
- // calling.
-
- // Returns bytes promoted per second of wall clock time.
- double promotion_rate() const;
-
- // Returns bytes directly allocated per second of wall clock time.
- double cms_allocation_rate() const;
-
- // Rate at which space in the cms generation is being consumed (sum of the
- // above two).
- double cms_consumption_rate() const;
-
- // Returns an estimate of the number of seconds until the cms generation will
- // fill up, assuming no collection work is done.
- double time_until_cms_gen_full() const;
-
- // Returns an estimate of the number of seconds remaining until
- // the cms generation collection should start.
- double time_until_cms_start() const;
-
- // End of higher level statistics.
-
- // Debugging.
- void print_on(outputStream* st) const PRODUCT_RETURN;
- void print() const { print_on(tty); }
-};
-
-// A closure related to weak references processing which
-// we embed in the CMSCollector, since we need to pass
-// it to the reference processor for secondary filtering
-// of references based on reachability of referent;
-// see role of _is_alive_non_header closure in the
-// ReferenceProcessor class.
-// For objects in the CMS generation, this closure checks
-// if the object is "live" (reachable). Used in weak
-// reference processing.
-class CMSIsAliveClosure: public BoolObjectClosure {
- const MemRegion _span;
- const CMSBitMap* _bit_map;
-
- friend class CMSCollector;
- public:
- CMSIsAliveClosure(MemRegion span,
- CMSBitMap* bit_map):
- _span(span),
- _bit_map(bit_map) {
- assert(!span.is_empty(), "Empty span could spell trouble");
- }
-
- bool do_object_b(oop obj);
-};
-
-
-// Implements AbstractRefProcTaskExecutor for CMS.
-class CMSRefProcTaskExecutor: public AbstractRefProcTaskExecutor {
-public:
-
- CMSRefProcTaskExecutor(CMSCollector& collector)
- : _collector(collector)
- { }
-
- // Executes a task using worker threads.
- virtual void execute(ProcessTask& task, uint ergo_workers);
-private:
- CMSCollector& _collector;
-};
-
-
-class CMSCollector: public CHeapObj<mtGC> {
- friend class VMStructs;
- friend class ConcurrentMarkSweepThread;
- friend class ConcurrentMarkSweepGeneration;
- friend class CompactibleFreeListSpace;
- friend class CMSParMarkTask;
- friend class CMSParInitialMarkTask;
- friend class CMSParRemarkTask;
- friend class CMSConcMarkingTask;
- friend class CMSRefProcTaskProxy;
- friend class CMSRefProcTaskExecutor;
- friend class ScanMarkedObjectsAgainCarefullyClosure; // for sampling eden
- friend class SurvivorSpacePrecleanClosure; // --- ditto -------
- friend class PushOrMarkClosure; // to access _restart_addr
- friend class ParPushOrMarkClosure; // to access _restart_addr
- friend class MarkFromRootsClosure; // -- ditto --
- // ... and for clearing cards
- friend class ParMarkFromRootsClosure; // to access _restart_addr
- // ... and for clearing cards
- friend class ParConcMarkingClosure; // to access _restart_addr etc.
- friend class MarkFromRootsVerifyClosure; // to access _restart_addr
- friend class PushAndMarkVerifyClosure; // -- ditto --
- friend class MarkRefsIntoAndScanClosure; // to access _overflow_list
- friend class PushAndMarkClosure; // -- ditto --
- friend class ParPushAndMarkClosure; // -- ditto --
- friend class CMSKeepAliveClosure; // -- ditto --
- friend class CMSDrainMarkingStackClosure; // -- ditto --
- friend class CMSInnerParMarkAndPushClosure; // -- ditto --
- NOT_PRODUCT(friend class ScanMarkedObjectsAgainClosure;) // assertion on _overflow_list
- friend class ReleaseForegroundGC; // to access _foregroundGCShouldWait
- friend class VM_CMS_Operation;
- friend class VM_CMS_Initial_Mark;
- friend class VM_CMS_Final_Remark;
- friend class TraceCMSMemoryManagerStats;
-
- private:
- jlong _time_of_last_gc;
- void update_time_of_last_gc(jlong now) {
- _time_of_last_gc = now;
- }
-
- OopTaskQueueSet* _task_queues;
-
- // Overflow list of grey objects, threaded through mark-word
- // Manipulated with CAS in the parallel/multi-threaded case.
- oopDesc* volatile _overflow_list;
- // The following array-pair keeps track of mark words
- // displaced for accommodating overflow list above.
- // This code will likely be revisited under RFE#4922830.
- Stack<oop, mtGC> _preserved_oop_stack;
- Stack<markWord, mtGC> _preserved_mark_stack;
-
- // In support of multi-threaded concurrent phases
- YieldingFlexibleWorkGang* _conc_workers;
-
- // Performance Counters
- CollectorCounters* _gc_counters;
- CollectorCounters* _cgc_counters;
-
- // Initialization Errors
- bool _completed_initialization;
-
- // In support of ExplicitGCInvokesConcurrent
- static bool _full_gc_requested;
- static GCCause::Cause _full_gc_cause;
- unsigned int _collection_count_start;
-
- // Should we unload classes this concurrent cycle?
- bool _should_unload_classes;
- unsigned int _concurrent_cycles_since_last_unload;
- unsigned int concurrent_cycles_since_last_unload() const {
- return _concurrent_cycles_since_last_unload;
- }
- // Did we (allow) unload classes in the previous concurrent cycle?
- bool unloaded_classes_last_cycle() const {
- return concurrent_cycles_since_last_unload() == 0;
- }
- // Root scanning options for perm gen
- int _roots_scanning_options;
- int roots_scanning_options() const { return _roots_scanning_options; }
- void add_root_scanning_option(int o) { _roots_scanning_options |= o; }
- void remove_root_scanning_option(int o) { _roots_scanning_options &= ~o; }
-
- // Verification support
- CMSBitMap _verification_mark_bm;
- void verify_after_remark_work_1();
- void verify_after_remark_work_2();
-
- // True if any verification flag is on.
- bool _verifying;
- bool verifying() const { return _verifying; }
- void set_verifying(bool v) { _verifying = v; }
-
- void set_did_compact(bool v);
-
- // XXX Move these to CMSStats ??? FIX ME !!!
- elapsedTimer _inter_sweep_timer; // Time between sweeps
- elapsedTimer _intra_sweep_timer; // Time _in_ sweeps
- // Padded decaying average estimates of the above
- AdaptivePaddedAverage _inter_sweep_estimate;
- AdaptivePaddedAverage _intra_sweep_estimate;
-
- CMSTracer* _gc_tracer_cm;
- ConcurrentGCTimer* _gc_timer_cm;
-
- bool _cms_start_registered;
-
- GCHeapSummary _last_heap_summary;
- MetaspaceSummary _last_metaspace_summary;
-
- void register_gc_start(GCCause::Cause cause);
- void register_gc_end();
- void save_heap_summary();
- void report_heap_summary(GCWhen::Type when);
-
- protected:
- ConcurrentMarkSweepGeneration* _cmsGen; // Old gen (CMS)
- MemRegion _span; // Span covering above
- CardTableRS* _ct; // Card table
-
- // CMS marking support structures
- CMSBitMap _markBitMap;
- CMSBitMap _modUnionTable;
- CMSMarkStack _markStack;
-
- HeapWord* _restart_addr; // In support of marking stack overflow
- void lower_restart_addr(HeapWord* low);
-
- // Counters in support of marking stack / work queue overflow handling:
- // a non-zero value indicates certain types of overflow events during
- // the current CMS cycle and could lead to stack resizing efforts at
- // an opportune future time.
- size_t _ser_pmc_preclean_ovflw;
- size_t _ser_pmc_remark_ovflw;
- size_t _par_pmc_remark_ovflw;
- size_t _ser_kac_preclean_ovflw;
- size_t _ser_kac_ovflw;
- size_t _par_kac_ovflw;
- NOT_PRODUCT(ssize_t _num_par_pushes;)
-
- // ("Weak") Reference processing support.
- SpanSubjectToDiscoveryClosure _span_based_discoverer;
- ReferenceProcessor* _ref_processor;
- CMSIsAliveClosure _is_alive_closure;
- // Keep this textually after _markBitMap and _span; c'tor dependency.
-
- ConcurrentMarkSweepThread* _cmsThread; // The thread doing the work
- ModUnionClosurePar _modUnionClosurePar;
-
- // CMS abstract state machine
- // initial_state: Idling
- // next_state(Idling) = {Marking}
- // next_state(Marking) = {Precleaning, Sweeping}
- // next_state(Precleaning) = {AbortablePreclean, FinalMarking}
- // next_state(AbortablePreclean) = {FinalMarking}
- // next_state(FinalMarking) = {Sweeping}
- // next_state(Sweeping) = {Resizing}
- // next_state(Resizing) = {Resetting}
- // next_state(Resetting) = {Idling}
- // The numeric values below are chosen so that:
- // . _collectorState <= Idling == post-sweep && pre-mark
- // . _collectorState in (Idling, Sweeping) == {initial,final}marking ||
- // precleaning || abortablePrecleanb
- public:
- enum CollectorState {
- Resizing = 0,
- Resetting = 1,
- Idling = 2,
- InitialMarking = 3,
- Marking = 4,
- Precleaning = 5,
- AbortablePreclean = 6,
- FinalMarking = 7,
- Sweeping = 8
- };
- protected:
- static CollectorState _collectorState;
-
- // State related to prologue/epilogue invocation for my generations
- bool _between_prologue_and_epilogue;
-
- // Signaling/State related to coordination between fore- and background GC
- // Note: When the baton has been passed from background GC to foreground GC,
- // _foregroundGCIsActive is true and _foregroundGCShouldWait is false.
- static bool _foregroundGCIsActive; // true iff foreground collector is active or
- // wants to go active
- static bool _foregroundGCShouldWait; // true iff background GC is active and has not
- // yet passed the baton to the foreground GC
-
- // Support for CMSScheduleRemark (abortable preclean)
- bool _abort_preclean;
- bool _start_sampling;
-
- int _numYields;
- size_t _numDirtyCards;
- size_t _sweep_count;
-
- // Occupancy used for bootstrapping stats
- double _bootstrap_occupancy;
-
- // Timer
- elapsedTimer _timer;
-
- // Timing, allocation and promotion statistics, used for scheduling.
- CMSStats _stats;
-
- enum CMS_op_type {
- CMS_op_checkpointRootsInitial,
- CMS_op_checkpointRootsFinal
- };
-
- void do_CMS_operation(CMS_op_type op, GCCause::Cause gc_cause);
- bool stop_world_and_do(CMS_op_type op);
-
- OopTaskQueueSet* task_queues() { return _task_queues; }
- YieldingFlexibleWorkGang* conc_workers() { return _conc_workers; }
-
- // Support for parallelizing Eden rescan in CMS remark phase
- void sample_eden(); // ... sample Eden space top
-
- private:
- // Support for parallelizing young gen rescan in CMS remark phase
- ParNewGeneration* _young_gen;
-
- HeapWord* volatile* _top_addr; // ... Top of Eden
- HeapWord** _end_addr; // ... End of Eden
- Mutex* _eden_chunk_lock;
- HeapWord** _eden_chunk_array; // ... Eden partitioning array
- size_t _eden_chunk_index; // ... top (exclusive) of array
- size_t _eden_chunk_capacity; // ... max entries in array
-
- // Support for parallelizing survivor space rescan
- HeapWord** _survivor_chunk_array;
- size_t _survivor_chunk_index;
- size_t _survivor_chunk_capacity;
- size_t* _cursor;
- ChunkArray* _survivor_plab_array;
-
- // Support for marking stack overflow handling
- bool take_from_overflow_list(size_t num, CMSMarkStack* to_stack);
- bool par_take_from_overflow_list(size_t num,
- OopTaskQueue* to_work_q,
- int no_of_gc_threads);
- void push_on_overflow_list(oop p);
- void par_push_on_overflow_list(oop p);
- // The following is, obviously, not, in general, "MT-stable"
- bool overflow_list_is_empty() const;
-
- void preserve_mark_if_necessary(oop p);
- void par_preserve_mark_if_necessary(oop p);
- void preserve_mark_work(oop p, markWord m);
- void restore_preserved_marks_if_any();
- NOT_PRODUCT(bool no_preserved_marks() const;)
- // In support of testing overflow code
- NOT_PRODUCT(int _overflow_counter;)
- NOT_PRODUCT(bool simulate_overflow();) // Sequential
- NOT_PRODUCT(bool par_simulate_overflow();) // MT version
-
- // CMS work methods
- void checkpointRootsInitialWork(); // Initial checkpoint work
-
- // A return value of false indicates failure due to stack overflow
- bool markFromRootsWork(); // Concurrent marking work
-
- public: // FIX ME!!! only for testing
- bool do_marking_st(); // Single-threaded marking
- bool do_marking_mt(); // Multi-threaded marking
-
- private:
-
- // Concurrent precleaning work
- size_t preclean_mod_union_table(ConcurrentMarkSweepGeneration* old_gen,
- ScanMarkedObjectsAgainCarefullyClosure* cl);
- size_t preclean_card_table(ConcurrentMarkSweepGeneration* old_gen,
- ScanMarkedObjectsAgainCarefullyClosure* cl);
- // Does precleaning work, returning a quantity indicative of
- // the amount of "useful work" done.
- size_t preclean_work(bool clean_refs, bool clean_survivors);
- void preclean_cld(MarkRefsIntoAndScanClosure* cl, Mutex* freelistLock);
- void abortable_preclean(); // Preclean while looking for possible abort
- void initialize_sequential_subtasks_for_young_gen_rescan(int i);
- // Helper function for above; merge-sorts the per-thread plab samples
- void merge_survivor_plab_arrays(ContiguousSpace* surv, int no_of_gc_threads);
- // Resets (i.e. clears) the per-thread plab sample vectors
- void reset_survivor_plab_arrays();
-
- // Final (second) checkpoint work
- void checkpointRootsFinalWork();
- // Work routine for parallel version of remark
- void do_remark_parallel();
- // Work routine for non-parallel version of remark
- void do_remark_non_parallel();
- // Reference processing work routine (during second checkpoint)
- void refProcessingWork();
-
- // Concurrent sweeping work
- void sweepWork(ConcurrentMarkSweepGeneration* old_gen);
-
- // Concurrent resetting of support data structures
- void reset_concurrent();
- // Resetting of support data structures from a STW full GC
- void reset_stw();
-
- // Clear _expansion_cause fields of constituent generations
- void clear_expansion_cause();
-
- // An auxiliary method used to record the ends of
- // used regions of each generation to limit the extent of sweep
- void save_sweep_limits();
-
- // A work method used by the foreground collector to do
- // a mark-sweep-compact.
- void do_compaction_work(bool clear_all_soft_refs);
-
- // Work methods for reporting concurrent mode interruption or failure
- bool is_external_interruption();
- void report_concurrent_mode_interruption();
-
- // If the background GC is active, acquire control from the background
- // GC and do the collection.
- void acquire_control_and_collect(bool full, bool clear_all_soft_refs);
-
- // For synchronizing passing of control from background to foreground
- // GC. waitForForegroundGC() is called by the background
- // collector. It if had to wait for a foreground collection,
- // it returns true and the background collection should assume
- // that the collection was finished by the foreground
- // collector.
- bool waitForForegroundGC();
-
- size_t block_size_using_printezis_bits(HeapWord* addr) const;
- size_t block_size_if_printezis_bits(HeapWord* addr) const;
- HeapWord* next_card_start_after_block(HeapWord* addr) const;
-
- void setup_cms_unloading_and_verification_state();
- public:
- CMSCollector(ConcurrentMarkSweepGeneration* cmsGen,
- CardTableRS* ct);
- ConcurrentMarkSweepThread* cmsThread() { return _cmsThread; }
-
- MemRegion ref_processor_span() const { return _span_based_discoverer.span(); }
- ReferenceProcessor* ref_processor() { return _ref_processor; }
- void ref_processor_init();
-
- Mutex* bitMapLock() const { return _markBitMap.lock(); }
- static CollectorState abstract_state() { return _collectorState; }
-
- bool should_abort_preclean() const; // Whether preclean should be aborted.
- size_t get_eden_used() const;
- size_t get_eden_capacity() const;
-
- ConcurrentMarkSweepGeneration* cmsGen() { return _cmsGen; }
-
- // Locking checks
- NOT_PRODUCT(static bool have_cms_token();)
-
- bool shouldConcurrentCollect();
-
- void collect(bool full,
- bool clear_all_soft_refs,
- size_t size,
- bool tlab);
- void collect_in_background(GCCause::Cause cause);
-
- // In support of ExplicitGCInvokesConcurrent
- static void request_full_gc(unsigned int full_gc_count, GCCause::Cause cause);
- // Should we unload classes in a particular concurrent cycle?
- bool should_unload_classes() const {
- return _should_unload_classes;
- }
- void update_should_unload_classes();
-
- void direct_allocated(HeapWord* start, size_t size);
-
- // Object is dead if not marked and current phase is sweeping.
- bool is_dead_obj(oop obj) const;
-
- // After a promotion (of "start"), do any necessary marking.
- // If "par", then it's being done by a parallel GC thread.
- // The last two args indicate if we need precise marking
- // and if so the size of the object so it can be dirtied
- // in its entirety.
- void promoted(bool par, HeapWord* start,
- bool is_obj_array, size_t obj_size);
-
- void getFreelistLocks() const;
- void releaseFreelistLocks() const;
- bool haveFreelistLocks() const;
-
- // Adjust size of underlying generation
- void compute_new_size();
-
- // GC prologue and epilogue
- void gc_prologue(bool full);
- void gc_epilogue(bool full);
-
- jlong time_of_last_gc(jlong now) {
- if (_collectorState <= Idling) {
- // gc not in progress
- return _time_of_last_gc;
- } else {
- // collection in progress
- return now;
- }
- }
-
- // Support for parallel remark of survivor space
- void* get_data_recorder(int thr_num);
- void sample_eden_chunk();
-
- CMSBitMap* markBitMap() { return &_markBitMap; }
- void directAllocated(HeapWord* start, size_t size);
-
- // Main CMS steps and related support
- void checkpointRootsInitial();
- bool markFromRoots(); // a return value of false indicates failure
- // due to stack overflow
- void preclean();
- void checkpointRootsFinal();
- void sweep();
-
- // Check that the currently executing thread is the expected
- // one (foreground collector or background collector).
- static void check_correct_thread_executing() PRODUCT_RETURN;
-
- NOT_PRODUCT(bool is_cms_reachable(HeapWord* addr);)
-
- // Performance Counter Support
- CollectorCounters* counters() { return _gc_counters; }
- CollectorCounters* cgc_counters() { return _cgc_counters; }
-
- // Timer stuff
- void startTimer() { assert(!_timer.is_active(), "Error"); _timer.start(); }
- void stopTimer() { assert( _timer.is_active(), "Error"); _timer.stop(); }
- void resetTimer() { assert(!_timer.is_active(), "Error"); _timer.reset(); }
- jlong timerTicks() { assert(!_timer.is_active(), "Error"); return _timer.ticks(); }
-
- int yields() { return _numYields; }
- void resetYields() { _numYields = 0; }
- void incrementYields() { _numYields++; }
- void resetNumDirtyCards() { _numDirtyCards = 0; }
- void incrementNumDirtyCards(size_t num) { _numDirtyCards += num; }
- size_t numDirtyCards() { return _numDirtyCards; }
-
- static bool foregroundGCShouldWait() { return _foregroundGCShouldWait; }
- static void set_foregroundGCShouldWait(bool v) { _foregroundGCShouldWait = v; }
- static bool foregroundGCIsActive() { return _foregroundGCIsActive; }
- static void set_foregroundGCIsActive(bool v) { _foregroundGCIsActive = v; }
- size_t sweep_count() const { return _sweep_count; }
- void increment_sweep_count() { _sweep_count++; }
-
- // Timers/stats for gc scheduling and incremental mode pacing.
- CMSStats& stats() { return _stats; }
-
- // Adaptive size policy
- AdaptiveSizePolicy* size_policy();
-
- static void print_on_error(outputStream* st);
-
- // Debugging
- void verify();
- bool verify_after_remark();
- void verify_ok_to_terminate() const PRODUCT_RETURN;
- void verify_work_stacks_empty() const PRODUCT_RETURN;
- void verify_overflow_empty() const PRODUCT_RETURN;
-
- // Convenience methods in support of debugging
- static const size_t skip_header_HeapWords() PRODUCT_RETURN0;
- HeapWord* block_start(const void* p) const PRODUCT_RETURN0;
-
- // Accessors
- CMSMarkStack* verification_mark_stack() { return &_markStack; }
- CMSBitMap* verification_mark_bm() { return &_verification_mark_bm; }
-
- // Initialization errors
- bool completed_initialization() { return _completed_initialization; }
-
- void print_eden_and_survivor_chunk_arrays();
-
- ConcurrentGCTimer* gc_timer_cm() const { return _gc_timer_cm; }
-};
-
-class CMSExpansionCause : public AllStatic {
- public:
- enum Cause {
- _no_expansion,
- _satisfy_free_ratio,
- _satisfy_promotion,
- _satisfy_allocation,
- _allocate_par_lab,
- _allocate_par_spooling_space,
- _adaptive_size_policy
- };
- // Return a string describing the cause of the expansion.
- static const char* to_string(CMSExpansionCause::Cause cause);
-};
-
-class ConcurrentMarkSweepGeneration: public CardGeneration {
- friend class VMStructs;
- friend class ConcurrentMarkSweepThread;
- friend class ConcurrentMarkSweep;
- friend class CMSCollector;
- protected:
- static CMSCollector* _collector; // the collector that collects us
- CompactibleFreeListSpace* _cmsSpace; // underlying space (only one for now)
-
- // Performance Counters
- GenerationCounters* _gen_counters;
- GSpaceCounters* _space_counters;
-
- // Words directly allocated, used by CMSStats.
- size_t _direct_allocated_words;
-
- // Non-product stat counters
- NOT_PRODUCT(
- size_t _numObjectsPromoted;
- size_t _numWordsPromoted;
- size_t _numObjectsAllocated;
- size_t _numWordsAllocated;
- )
-
- // Used for sizing decisions
- bool _incremental_collection_failed;
- bool incremental_collection_failed() {
- return _incremental_collection_failed;
- }
- void set_incremental_collection_failed() {
- _incremental_collection_failed = true;
- }
- void clear_incremental_collection_failed() {
- _incremental_collection_failed = false;
- }
-
- // accessors
- void set_expansion_cause(CMSExpansionCause::Cause v) { _expansion_cause = v;}
- CMSExpansionCause::Cause expansion_cause() const { return _expansion_cause; }
-
- // Accessing spaces
- CompactibleSpace* space() const { return (CompactibleSpace*)_cmsSpace; }
-
- private:
- // For parallel young-gen GC support.
- CMSParGCThreadState** _par_gc_thread_states;
-
- // Reason generation was expanded
- CMSExpansionCause::Cause _expansion_cause;
-
- // In support of MinChunkSize being larger than min object size
- const double _dilatation_factor;
-
- // True if a compacting collection was done.
- bool _did_compact;
- bool did_compact() { return _did_compact; }
-
- // Fraction of current occupancy at which to start a CMS collection which
- // will collect this generation (at least).
- double _initiating_occupancy;
-
- protected:
- // Shrink generation by specified size (returns false if unable to shrink)
- void shrink_free_list_by(size_t bytes);
-
- // Update statistics for GC
- virtual void update_gc_stats(Generation* current_generation, bool full);
-
- // Maximum available space in the generation (including uncommitted)
- // space.
- size_t max_available() const;
-
- // getter and initializer for _initiating_occupancy field.
- double initiating_occupancy() const { return _initiating_occupancy; }
- void init_initiating_occupancy(intx io, uintx tr);
-
- void expand_for_gc_cause(size_t bytes, size_t expand_bytes, CMSExpansionCause::Cause cause);
-
- void assert_correct_size_change_locking();
-
- public:
- ConcurrentMarkSweepGeneration(ReservedSpace rs,
- size_t initial_byte_size,
- size_t min_byte_size,
- size_t max_byte_size,
- CardTableRS* ct);
-
- // Accessors
- CMSCollector* collector() const { return _collector; }
- static void set_collector(CMSCollector* collector) {
- assert(_collector == NULL, "already set");
- _collector = collector;
- }
- CompactibleFreeListSpace* cmsSpace() const { return _cmsSpace; }
-
- Mutex* freelistLock() const;
-
- virtual Generation::Name kind() { return Generation::ConcurrentMarkSweep; }
-
- void set_did_compact(bool v) { _did_compact = v; }
-
- bool refs_discovery_is_atomic() const { return false; }
- bool refs_discovery_is_mt() const {
- // Note: CMS does MT-discovery during the parallel-remark
- // phases. Use ReferenceProcessorMTMutator to make refs
- // discovery MT-safe during such phases or other parallel
- // discovery phases in the future. This may all go away
- // if/when we decide that refs discovery is sufficiently
- // rare that the cost of the CAS's involved is in the
- // noise. That's a measurement that should be done, and
- // the code simplified if that turns out to be the case.
- return ConcGCThreads > 1;
- }
-
- // Override
- virtual void ref_processor_init();
-
- void clear_expansion_cause() { _expansion_cause = CMSExpansionCause::_no_expansion; }
-
- // Space enquiries
- double occupancy() const { return ((double)used())/((double)capacity()); }
- size_t contiguous_available() const;
- size_t unsafe_max_alloc_nogc() const;
- size_t used_stable() const;
-
- // over-rides
- MemRegion used_region_at_save_marks() const;
-
- // Adjust quantities in the generation affected by
- // the compaction.
- void reset_after_compaction();
-
- // Allocation support
- HeapWord* allocate(size_t size, bool tlab);
- HeapWord* have_lock_and_allocate(size_t size, bool tlab);
- oop promote(oop obj, size_t obj_size);
- HeapWord* par_allocate(size_t size, bool tlab) {
- return allocate(size, tlab);
- }
-
-
- // Used by CMSStats to track direct allocation. The value is sampled and
- // reset after each young gen collection.
- size_t direct_allocated_words() const { return _direct_allocated_words; }
- void reset_direct_allocated_words() { _direct_allocated_words = 0; }
-
- // Overrides for parallel promotion.
- virtual oop par_promote(int thread_num,
- oop obj, markWord m, size_t word_sz);
- virtual void par_promote_alloc_done(int thread_num);
- virtual void par_oop_since_save_marks_iterate_done(int thread_num);
-
- virtual bool promotion_attempt_is_safe(size_t promotion_in_bytes) const;
-
- // Inform this (old) generation that a promotion failure was
- // encountered during a collection of the young generation.
- virtual void promotion_failure_occurred();
-
- bool should_collect(bool full, size_t size, bool tlab);
- virtual bool should_concurrent_collect() const;
- virtual bool is_too_full() const;
- void collect(bool full,
- bool clear_all_soft_refs,
- size_t size,
- bool tlab);
-
- HeapWord* expand_and_allocate(size_t word_size,
- bool tlab,
- bool parallel = false);
-
- // GC prologue and epilogue
- void gc_prologue(bool full);
- void gc_prologue_work(bool full, bool registerClosure,
- ModUnionClosure* modUnionClosure);
- void gc_epilogue(bool full);
- void gc_epilogue_work(bool full);
-
- // Time since last GC of this generation
- jlong time_of_last_gc(jlong now) {
- return collector()->time_of_last_gc(now);
- }
- void update_time_of_last_gc(jlong now) {
- collector()-> update_time_of_last_gc(now);
- }
-
- // Allocation failure
- void shrink(size_t bytes);
- HeapWord* expand_and_par_lab_allocate(CMSParGCThreadState* ps, size_t word_sz);
- bool expand_and_ensure_spooling_space(PromotionInfo* promo);
-
- // Iteration support and related enquiries
- void save_marks();
- bool no_allocs_since_save_marks();
-
- // Iteration support specific to CMS generations
- void save_sweep_limit();
-
- // More iteration support
- virtual void oop_iterate(OopIterateClosure* cl);
- virtual void safe_object_iterate(ObjectClosure* cl);
- virtual void object_iterate(ObjectClosure* cl);
-
- template <typename OopClosureType>
- void oop_since_save_marks_iterate(OopClosureType* cl);
-
- // Smart allocation XXX -- move to CFLSpace?
- void setNearLargestChunk();
- bool isNearLargestChunk(HeapWord* addr);
-
- // Get the chunk at the end of the space. Delegates to
- // the space.
- FreeChunk* find_chunk_at_end();
-
- void post_compact();
-
- // Debugging
- void prepare_for_verify();
- void verify();
- void print_statistics() PRODUCT_RETURN;
-
- // Performance Counters support
- virtual void update_counters();
- virtual void update_counters(size_t used);
- void initialize_performance_counters(size_t min_old_size, size_t max_old_size);
- CollectorCounters* counters() { return collector()->counters(); }
-
- // Support for parallel remark of survivor space
- void* get_data_recorder(int thr_num) {
- //Delegate to collector
- return collector()->get_data_recorder(thr_num);
- }
- void sample_eden_chunk() {
- //Delegate to collector
- return collector()->sample_eden_chunk();
- }
-
- // Printing
- const char* name() const;
- virtual const char* short_name() const { return "CMS"; }
- void print() const;
-
- // Resize the generation after a compacting GC. The
- // generation can be treated as a contiguous space
- // after the compaction.
- virtual void compute_new_size();
- // Resize the generation after a non-compacting
- // collection.
- void compute_new_size_free_list();
-};
-
-//
-// Closures of various sorts used by CMS to accomplish its work
-//
-
-// This closure is used to do concurrent marking from the roots
-// following the first checkpoint.
-class MarkFromRootsClosure: public BitMapClosure {
- CMSCollector* _collector;
- MemRegion _span;
- CMSBitMap* _bitMap;
- CMSBitMap* _mut;
- CMSMarkStack* _markStack;
- bool _yield;
- int _skipBits;
- HeapWord* _finger;
- HeapWord* _threshold;
- DEBUG_ONLY(bool _verifying;)
-
- public:
- MarkFromRootsClosure(CMSCollector* collector, MemRegion span,
- CMSBitMap* bitMap,
- CMSMarkStack* markStack,
- bool should_yield, bool verifying = false);
- bool do_bit(size_t offset);
- void reset(HeapWord* addr);
- inline void do_yield_check();
-
- private:
- void scanOopsInOop(HeapWord* ptr);
- void do_yield_work();
-};
-
-// This closure is used to do concurrent multi-threaded
-// marking from the roots following the first checkpoint.
-// XXX This should really be a subclass of The serial version
-// above, but i have not had the time to refactor things cleanly.
-class ParMarkFromRootsClosure: public BitMapClosure {
- CMSCollector* _collector;
- MemRegion _whole_span;
- MemRegion _span;
- CMSBitMap* _bit_map;
- CMSBitMap* _mut;
- OopTaskQueue* _work_queue;
- CMSMarkStack* _overflow_stack;
- int _skip_bits;
- HeapWord* _finger;
- HeapWord* _threshold;
- CMSConcMarkingTask* _task;
- public:
- ParMarkFromRootsClosure(CMSConcMarkingTask* task, CMSCollector* collector,
- MemRegion span,
- CMSBitMap* bit_map,
- OopTaskQueue* work_queue,
- CMSMarkStack* overflow_stack);
- bool do_bit(size_t offset);
- inline void do_yield_check();
-
- private:
- void scan_oops_in_oop(HeapWord* ptr);
- void do_yield_work();
- bool get_work_from_overflow_stack();
-};
-
-// The following closures are used to do certain kinds of verification of
-// CMS marking.
-class PushAndMarkVerifyClosure: public MetadataVisitingOopIterateClosure {
- CMSCollector* _collector;
- MemRegion _span;
- CMSBitMap* _verification_bm;
- CMSBitMap* _cms_bm;
- CMSMarkStack* _mark_stack;
- protected:
- void do_oop(oop p);
- template <class T> void do_oop_work(T *p);
-
- public:
- PushAndMarkVerifyClosure(CMSCollector* cms_collector,
- MemRegion span,
- CMSBitMap* verification_bm,
- CMSBitMap* cms_bm,
- CMSMarkStack* mark_stack);
- void do_oop(oop* p);
- void do_oop(narrowOop* p);
-
- // Deal with a stack overflow condition
- void handle_stack_overflow(HeapWord* lost);
-};
-
-class MarkFromRootsVerifyClosure: public BitMapClosure {
- CMSCollector* _collector;
- MemRegion _span;
- CMSBitMap* _verification_bm;
- CMSBitMap* _cms_bm;
- CMSMarkStack* _mark_stack;
- HeapWord* _finger;
- PushAndMarkVerifyClosure _pam_verify_closure;
- public:
- MarkFromRootsVerifyClosure(CMSCollector* collector, MemRegion span,
- CMSBitMap* verification_bm,
- CMSBitMap* cms_bm,
- CMSMarkStack* mark_stack);
- bool do_bit(size_t offset);
- void reset(HeapWord* addr);
-};
-
-
-// This closure is used to check that a certain set of bits is
-// "empty" (i.e. the bit vector doesn't have any 1-bits).
-class FalseBitMapClosure: public BitMapClosure {
- public:
- bool do_bit(size_t offset) {
- guarantee(false, "Should not have a 1 bit");
- return true;
- }
-};
-
-// A version of ObjectClosure with "memory" (see _previous_address below)
-class UpwardsObjectClosure: public BoolObjectClosure {
- HeapWord* _previous_address;
- public:
- UpwardsObjectClosure() : _previous_address(NULL) { }
- void set_previous(HeapWord* addr) { _previous_address = addr; }
- HeapWord* previous() { return _previous_address; }
- // A return value of "true" can be used by the caller to decide
- // if this object's end should *NOT* be recorded in
- // _previous_address above.
- virtual bool do_object_bm(oop obj, MemRegion mr) = 0;
-};
-
-// This closure is used during the second checkpointing phase
-// to rescan the marked objects on the dirty cards in the mod
-// union table and the card table proper. It's invoked via
-// MarkFromDirtyCardsClosure below. It uses either
-// [Par_]MarkRefsIntoAndScanClosure (Par_ in the parallel case)
-// declared in genOopClosures.hpp to accomplish some of its work.
-// In the parallel case the bitMap is shared, so access to
-// it needs to be suitably synchronized for updates by embedded
-// closures that update it; however, this closure itself only
-// reads the bit_map and because it is idempotent, is immune to
-// reading stale values.
-class ScanMarkedObjectsAgainClosure: public UpwardsObjectClosure {
- #ifdef ASSERT
- CMSCollector* _collector;
- MemRegion _span;
- union {
- CMSMarkStack* _mark_stack;
- OopTaskQueue* _work_queue;
- };
- #endif // ASSERT
- bool _parallel;
- CMSBitMap* _bit_map;
- union {
- MarkRefsIntoAndScanClosure* _scan_closure;
- ParMarkRefsIntoAndScanClosure* _par_scan_closure;
- };
-
- public:
- ScanMarkedObjectsAgainClosure(CMSCollector* collector,
- MemRegion span,
- ReferenceProcessor* rp,
- CMSBitMap* bit_map,
- CMSMarkStack* mark_stack,
- MarkRefsIntoAndScanClosure* cl):
- #ifdef ASSERT
- _collector(collector),
- _span(span),
- _mark_stack(mark_stack),
- #endif // ASSERT
- _parallel(false),
- _bit_map(bit_map),
- _scan_closure(cl) { }
-
- ScanMarkedObjectsAgainClosure(CMSCollector* collector,
- MemRegion span,
- ReferenceProcessor* rp,
- CMSBitMap* bit_map,
- OopTaskQueue* work_queue,
- ParMarkRefsIntoAndScanClosure* cl):
- #ifdef ASSERT
- _collector(collector),
- _span(span),
- _work_queue(work_queue),
- #endif // ASSERT
- _parallel(true),
- _bit_map(bit_map),
- _par_scan_closure(cl) { }
-
- bool do_object_b(oop obj) {
- guarantee(false, "Call do_object_b(oop, MemRegion) form instead");
- return false;
- }
- bool do_object_bm(oop p, MemRegion mr);
-};
-
-// This closure is used during the second checkpointing phase
-// to rescan the marked objects on the dirty cards in the mod
-// union table and the card table proper. It invokes
-// ScanMarkedObjectsAgainClosure above to accomplish much of its work.
-// In the parallel case, the bit map is shared and requires
-// synchronized access.
-class MarkFromDirtyCardsClosure: public MemRegionClosure {
- CompactibleFreeListSpace* _space;
- ScanMarkedObjectsAgainClosure _scan_cl;
- size_t _num_dirty_cards;
-
- public:
- MarkFromDirtyCardsClosure(CMSCollector* collector,
- MemRegion span,
- CompactibleFreeListSpace* space,
- CMSBitMap* bit_map,
- CMSMarkStack* mark_stack,
- MarkRefsIntoAndScanClosure* cl):
- _space(space),
- _scan_cl(collector, span, collector->ref_processor(), bit_map,
- mark_stack, cl),
- _num_dirty_cards(0) { }
-
- MarkFromDirtyCardsClosure(CMSCollector* collector,
- MemRegion span,
- CompactibleFreeListSpace* space,
- CMSBitMap* bit_map,
- OopTaskQueue* work_queue,
- ParMarkRefsIntoAndScanClosure* cl):
- _space(space),
- _scan_cl(collector, span, collector->ref_processor(), bit_map,
- work_queue, cl),
- _num_dirty_cards(0) { }
-
- void do_MemRegion(MemRegion mr);
- void set_space(CompactibleFreeListSpace* space) { _space = space; }
- size_t num_dirty_cards() { return _num_dirty_cards; }
-};
-
-// This closure is used in the non-product build to check
-// that there are no MemRegions with a certain property.
-class FalseMemRegionClosure: public MemRegionClosure {
- void do_MemRegion(MemRegion mr) {
- guarantee(!mr.is_empty(), "Shouldn't be empty");
- guarantee(false, "Should never be here");
- }
-};
-
-// This closure is used during the precleaning phase
-// to "carefully" rescan marked objects on dirty cards.
-// It uses MarkRefsIntoAndScanClosure declared in genOopClosures.hpp
-// to accomplish some of its work.
-class ScanMarkedObjectsAgainCarefullyClosure: public ObjectClosureCareful {
- CMSCollector* _collector;
- MemRegion _span;
- bool _yield;
- Mutex* _freelistLock;
- CMSBitMap* _bitMap;
- CMSMarkStack* _markStack;
- MarkRefsIntoAndScanClosure* _scanningClosure;
- DEBUG_ONLY(HeapWord* _last_scanned_object;)
-
- public:
- ScanMarkedObjectsAgainCarefullyClosure(CMSCollector* collector,
- MemRegion span,
- CMSBitMap* bitMap,
- CMSMarkStack* markStack,
- MarkRefsIntoAndScanClosure* cl,
- bool should_yield):
- _collector(collector),
- _span(span),
- _yield(should_yield),
- _bitMap(bitMap),
- _markStack(markStack),
- _scanningClosure(cl)
- DEBUG_ONLY(COMMA _last_scanned_object(NULL))
- { }
-
- void do_object(oop p) {
- guarantee(false, "call do_object_careful instead");
- }
-
- size_t do_object_careful(oop p) {
- guarantee(false, "Unexpected caller");
- return 0;
- }
-
- size_t do_object_careful_m(oop p, MemRegion mr);
-
- void setFreelistLock(Mutex* m) {
- _freelistLock = m;
- _scanningClosure->set_freelistLock(m);
- }
-
- private:
- inline bool do_yield_check();
-
- void do_yield_work();
-};
-
-class SurvivorSpacePrecleanClosure: public ObjectClosureCareful {
- CMSCollector* _collector;
- MemRegion _span;
- bool _yield;
- CMSBitMap* _bit_map;
- CMSMarkStack* _mark_stack;
- PushAndMarkClosure* _scanning_closure;
- unsigned int _before_count;
-
- public:
- SurvivorSpacePrecleanClosure(CMSCollector* collector,
- MemRegion span,
- CMSBitMap* bit_map,
- CMSMarkStack* mark_stack,
- PushAndMarkClosure* cl,
- unsigned int before_count,
- bool should_yield):
- _collector(collector),
- _span(span),
- _yield(should_yield),
- _bit_map(bit_map),
- _mark_stack(mark_stack),
- _scanning_closure(cl),
- _before_count(before_count)
- { }
-
- void do_object(oop p) {
- guarantee(false, "call do_object_careful instead");
- }
-
- size_t do_object_careful(oop p);
-
- size_t do_object_careful_m(oop p, MemRegion mr) {
- guarantee(false, "Unexpected caller");
- return 0;
- }
-
- private:
- inline void do_yield_check();
- void do_yield_work();
-};
-
-// This closure is used to accomplish the sweeping work
-// after the second checkpoint but before the concurrent reset
-// phase.
-//
-// Terminology
-// left hand chunk (LHC) - block of one or more chunks currently being
-// coalesced. The LHC is available for coalescing with a new chunk.
-// right hand chunk (RHC) - block that is currently being swept that is
-// free or garbage that can be coalesced with the LHC.
-// _inFreeRange is true if there is currently a LHC
-// _lastFreeRangeCoalesced is true if the LHC consists of more than one chunk.
-// _freeRangeInFreeLists is true if the LHC is in the free lists.
-// _freeFinger is the address of the current LHC
-class SweepClosure: public BlkClosureCareful {
- CMSCollector* _collector; // collector doing the work
- ConcurrentMarkSweepGeneration* _g; // Generation being swept
- CompactibleFreeListSpace* _sp; // Space being swept
- HeapWord* _limit;// the address at or above which the sweep should stop
- // because we do not expect newly garbage blocks
- // eligible for sweeping past that address.
- Mutex* _freelistLock; // Free list lock (in space)
- CMSBitMap* _bitMap; // Marking bit map (in
- // generation)
- bool _inFreeRange; // Indicates if we are in the
- // midst of a free run
- bool _freeRangeInFreeLists;
- // Often, we have just found
- // a free chunk and started
- // a new free range; we do not
- // eagerly remove this chunk from
- // the free lists unless there is
- // a possibility of coalescing.
- // When true, this flag indicates
- // that the _freeFinger below
- // points to a potentially free chunk
- // that may still be in the free lists
- bool _lastFreeRangeCoalesced;
- // free range contains chunks
- // coalesced
- bool _yield;
- // Whether sweeping should be
- // done with yields. For instance
- // when done by the foreground
- // collector we shouldn't yield.
- HeapWord* _freeFinger; // When _inFreeRange is set, the
- // pointer to the "left hand
- // chunk"
- size_t _freeRangeSize;
- // When _inFreeRange is set, this
- // indicates the accumulated size
- // of the "left hand chunk"
- NOT_PRODUCT(
- size_t _numObjectsFreed;
- size_t _numWordsFreed;
- size_t _numObjectsLive;
- size_t _numWordsLive;
- size_t _numObjectsAlreadyFree;
- size_t _numWordsAlreadyFree;
- FreeChunk* _last_fc;
- )
- private:
- // Code that is common to a free chunk or garbage when
- // encountered during sweeping.
- void do_post_free_or_garbage_chunk(FreeChunk *fc, size_t chunkSize);
- // Process a free chunk during sweeping.
- void do_already_free_chunk(FreeChunk *fc);
- // Work method called when processing an already free or a
- // freshly garbage chunk to do a lookahead and possibly a
- // preemptive flush if crossing over _limit.
- void lookahead_and_flush(FreeChunk* fc, size_t chunkSize);
- // Process a garbage chunk during sweeping.
- size_t do_garbage_chunk(FreeChunk *fc);
- // Process a live chunk during sweeping.
- size_t do_live_chunk(FreeChunk* fc);
-
- // Accessors.
- HeapWord* freeFinger() const { return _freeFinger; }
- void set_freeFinger(HeapWord* v) { _freeFinger = v; }
- bool inFreeRange() const { return _inFreeRange; }
- void set_inFreeRange(bool v) { _inFreeRange = v; }
- bool lastFreeRangeCoalesced() const { return _lastFreeRangeCoalesced; }
- void set_lastFreeRangeCoalesced(bool v) { _lastFreeRangeCoalesced = v; }
- bool freeRangeInFreeLists() const { return _freeRangeInFreeLists; }
- void set_freeRangeInFreeLists(bool v) { _freeRangeInFreeLists = v; }
-
- // Initialize a free range.
- void initialize_free_range(HeapWord* freeFinger, bool freeRangeInFreeLists);
- // Return this chunk to the free lists.
- void flush_cur_free_chunk(HeapWord* chunk, size_t size);
-
- // Check if we should yield and do so when necessary.
- inline void do_yield_check(HeapWord* addr);
-
- // Yield
- void do_yield_work(HeapWord* addr);
-
- // Debugging/Printing
- void print_free_block_coalesced(FreeChunk* fc) const;
-
- public:
- SweepClosure(CMSCollector* collector, ConcurrentMarkSweepGeneration* g,
- CMSBitMap* bitMap, bool should_yield);
- ~SweepClosure() PRODUCT_RETURN;
-
- size_t do_blk_careful(HeapWord* addr);
- void print() const { print_on(tty); }
- void print_on(outputStream *st) const;
-};
-
-// Closures related to weak references processing
-
-// During CMS' weak reference processing, this is a
-// work-routine/closure used to complete transitive
-// marking of objects as live after a certain point
-// in which an initial set has been completely accumulated.
-// This closure is currently used both during the final
-// remark stop-world phase, as well as during the concurrent
-// precleaning of the discovered reference lists.
-class CMSDrainMarkingStackClosure: public VoidClosure {
- CMSCollector* _collector;
- MemRegion _span;
- CMSMarkStack* _mark_stack;
- CMSBitMap* _bit_map;
- CMSKeepAliveClosure* _keep_alive;
- bool _concurrent_precleaning;
- public:
- CMSDrainMarkingStackClosure(CMSCollector* collector, MemRegion span,
- CMSBitMap* bit_map, CMSMarkStack* mark_stack,
- CMSKeepAliveClosure* keep_alive,
- bool cpc):
- _collector(collector),
- _span(span),
- _mark_stack(mark_stack),
- _bit_map(bit_map),
- _keep_alive(keep_alive),
- _concurrent_precleaning(cpc) {
- assert(_concurrent_precleaning == _keep_alive->concurrent_precleaning(),
- "Mismatch");
- }
-
- void do_void();
-};
-
-// A parallel version of CMSDrainMarkingStackClosure above.
-class CMSParDrainMarkingStackClosure: public VoidClosure {
- CMSCollector* _collector;
- MemRegion _span;
- OopTaskQueue* _work_queue;
- CMSBitMap* _bit_map;
- CMSInnerParMarkAndPushClosure _mark_and_push;
-
- public:
- CMSParDrainMarkingStackClosure(CMSCollector* collector,
- MemRegion span, CMSBitMap* bit_map,
- OopTaskQueue* work_queue):
- _collector(collector),
- _span(span),
- _work_queue(work_queue),
- _bit_map(bit_map),
- _mark_and_push(collector, span, bit_map, work_queue) { }
-
- public:
- void trim_queue(uint max);
- void do_void();
-};
-
-// Allow yielding or short-circuiting of reference list
-// precleaning work.
-class CMSPrecleanRefsYieldClosure: public YieldClosure {
- CMSCollector* _collector;
- void do_yield_work();
- public:
- CMSPrecleanRefsYieldClosure(CMSCollector* collector):
- _collector(collector) {}
- virtual bool should_return();
-};
-
-
-// Convenience class that locks free list locks for given CMS collector
-class FreelistLocker: public StackObj {
- private:
- CMSCollector* _collector;
- public:
- FreelistLocker(CMSCollector* collector):
- _collector(collector) {
- _collector->getFreelistLocks();
- }
-
- ~FreelistLocker() {
- _collector->releaseFreelistLocks();
- }
-};
-
-// Mark all dead objects in a given space.
-class MarkDeadObjectsClosure: public BlkClosure {
- const CMSCollector* _collector;
- const CompactibleFreeListSpace* _sp;
- CMSBitMap* _live_bit_map;
- CMSBitMap* _dead_bit_map;
-public:
- MarkDeadObjectsClosure(const CMSCollector* collector,
- const CompactibleFreeListSpace* sp,
- CMSBitMap *live_bit_map,
- CMSBitMap *dead_bit_map) :
- _collector(collector),
- _sp(sp),
- _live_bit_map(live_bit_map),
- _dead_bit_map(dead_bit_map) {}
- size_t do_blk(HeapWord* addr);
-};
-
-class TraceCMSMemoryManagerStats : public TraceMemoryManagerStats {
-
- public:
- TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase, GCCause::Cause cause);
-};
-
-
-#endif // SHARE_GC_CMS_CONCURRENTMARKSWEEPGENERATION_HPP
--- a/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,472 +0,0 @@
-/*
- * Copyright (c) 2001, 2019, 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_GC_CMS_CONCURRENTMARKSWEEPGENERATION_INLINE_HPP
-#define SHARE_GC_CMS_CONCURRENTMARKSWEEPGENERATION_INLINE_HPP
-
-#include "gc/cms/cmsHeap.hpp"
-#include "gc/cms/cmsLockVerifier.hpp"
-#include "gc/cms/compactibleFreeListSpace.inline.hpp"
-#include "gc/cms/concurrentMarkSweepGeneration.hpp"
-#include "gc/cms/concurrentMarkSweepThread.hpp"
-#include "gc/cms/parNewGeneration.hpp"
-#include "gc/shared/gcUtil.hpp"
-#include "utilities/align.hpp"
-#include "utilities/bitMap.inline.hpp"
-
-inline void CMSBitMap::clear_all() {
- assert_locked();
- // CMS bitmaps are usually cover large memory regions
- _bm.clear_large();
- return;
-}
-
-inline size_t CMSBitMap::heapWordToOffset(HeapWord* addr) const {
- return (pointer_delta(addr, _bmStartWord)) >> _shifter;
-}
-
-inline HeapWord* CMSBitMap::offsetToHeapWord(size_t offset) const {
- return _bmStartWord + (offset << _shifter);
-}
-
-inline size_t CMSBitMap::heapWordDiffToOffsetDiff(size_t diff) const {
- assert((diff & ((1 << _shifter) - 1)) == 0, "argument check");
- return diff >> _shifter;
-}
-
-inline void CMSBitMap::mark(HeapWord* addr) {
- assert_locked();
- assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize),
- "outside underlying space?");
- _bm.set_bit(heapWordToOffset(addr));
-}
-
-inline bool CMSBitMap::par_mark(HeapWord* addr) {
- assert_locked();
- assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize),
- "outside underlying space?");
- return _bm.par_at_put(heapWordToOffset(addr), true);
-}
-
-inline void CMSBitMap::par_clear(HeapWord* addr) {
- assert_locked();
- assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize),
- "outside underlying space?");
- _bm.par_at_put(heapWordToOffset(addr), false);
-}
-
-inline void CMSBitMap::mark_range(MemRegion mr) {
- NOT_PRODUCT(region_invariant(mr));
- // Range size is usually just 1 bit.
- _bm.set_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
- BitMap::small_range);
-}
-
-inline void CMSBitMap::clear_range(MemRegion mr) {
- NOT_PRODUCT(region_invariant(mr));
- // Range size is usually just 1 bit.
- _bm.clear_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
- BitMap::small_range);
-}
-
-inline void CMSBitMap::par_mark_range(MemRegion mr) {
- NOT_PRODUCT(region_invariant(mr));
- // Range size is usually just 1 bit.
- _bm.par_set_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
- BitMap::small_range);
-}
-
-inline void CMSBitMap::par_clear_range(MemRegion mr) {
- NOT_PRODUCT(region_invariant(mr));
- // Range size is usually just 1 bit.
- _bm.par_clear_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
- BitMap::small_range);
-}
-
-inline void CMSBitMap::mark_large_range(MemRegion mr) {
- NOT_PRODUCT(region_invariant(mr));
- // Range size must be greater than 32 bytes.
- _bm.set_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
- BitMap::large_range);
-}
-
-inline void CMSBitMap::clear_large_range(MemRegion mr) {
- NOT_PRODUCT(region_invariant(mr));
- // Range size must be greater than 32 bytes.
- _bm.clear_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
- BitMap::large_range);
-}
-
-inline void CMSBitMap::par_mark_large_range(MemRegion mr) {
- NOT_PRODUCT(region_invariant(mr));
- // Range size must be greater than 32 bytes.
- _bm.par_set_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
- BitMap::large_range);
-}
-
-inline void CMSBitMap::par_clear_large_range(MemRegion mr) {
- NOT_PRODUCT(region_invariant(mr));
- // Range size must be greater than 32 bytes.
- _bm.par_clear_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
- BitMap::large_range);
-}
-
-// Starting at "addr" (inclusive) return a memory region
-// corresponding to the first maximally contiguous marked ("1") region.
-inline MemRegion CMSBitMap::getAndClearMarkedRegion(HeapWord* addr) {
- return getAndClearMarkedRegion(addr, endWord());
-}
-
-// Starting at "start_addr" (inclusive) return a memory region
-// corresponding to the first maximal contiguous marked ("1") region
-// strictly less than end_addr.
-inline MemRegion CMSBitMap::getAndClearMarkedRegion(HeapWord* start_addr,
- HeapWord* end_addr) {
- HeapWord *start, *end;
- assert_locked();
- start = getNextMarkedWordAddress (start_addr, end_addr);
- end = getNextUnmarkedWordAddress(start, end_addr);
- assert(start <= end, "Consistency check");
- MemRegion mr(start, end);
- if (!mr.is_empty()) {
- clear_range(mr);
- }
- return mr;
-}
-
-inline bool CMSBitMap::isMarked(HeapWord* addr) const {
- assert_locked();
- assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize),
- "outside underlying space?");
- return _bm.at(heapWordToOffset(addr));
-}
-
-// The same as isMarked() but without a lock check.
-inline bool CMSBitMap::par_isMarked(HeapWord* addr) const {
- assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize),
- "outside underlying space?");
- return _bm.at(heapWordToOffset(addr));
-}
-
-
-inline bool CMSBitMap::isUnmarked(HeapWord* addr) const {
- assert_locked();
- assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize),
- "outside underlying space?");
- return !_bm.at(heapWordToOffset(addr));
-}
-
-// Return the HeapWord address corresponding to next "1" bit
-// (inclusive).
-inline HeapWord* CMSBitMap::getNextMarkedWordAddress(HeapWord* addr) const {
- return getNextMarkedWordAddress(addr, endWord());
-}
-
-// Return the least HeapWord address corresponding to next "1" bit
-// starting at start_addr (inclusive) but strictly less than end_addr.
-inline HeapWord* CMSBitMap::getNextMarkedWordAddress(
- HeapWord* start_addr, HeapWord* end_addr) const {
- assert_locked();
- size_t nextOffset = _bm.get_next_one_offset(
- heapWordToOffset(start_addr),
- heapWordToOffset(end_addr));
- HeapWord* nextAddr = offsetToHeapWord(nextOffset);
- assert(nextAddr >= start_addr &&
- nextAddr <= end_addr, "get_next_one postcondition");
- assert((nextAddr == end_addr) ||
- isMarked(nextAddr), "get_next_one postcondition");
- return nextAddr;
-}
-
-
-// Return the HeapWord address corresponding to the next "0" bit
-// (inclusive).
-inline HeapWord* CMSBitMap::getNextUnmarkedWordAddress(HeapWord* addr) const {
- return getNextUnmarkedWordAddress(addr, endWord());
-}
-
-// Return the HeapWord address corresponding to the next "0" bit
-// (inclusive).
-inline HeapWord* CMSBitMap::getNextUnmarkedWordAddress(
- HeapWord* start_addr, HeapWord* end_addr) const {
- assert_locked();
- size_t nextOffset = _bm.get_next_zero_offset(
- heapWordToOffset(start_addr),
- heapWordToOffset(end_addr));
- HeapWord* nextAddr = offsetToHeapWord(nextOffset);
- assert(nextAddr >= start_addr &&
- nextAddr <= end_addr, "get_next_zero postcondition");
- assert((nextAddr == end_addr) ||
- isUnmarked(nextAddr), "get_next_zero postcondition");
- return nextAddr;
-}
-
-inline bool CMSBitMap::isAllClear() const {
- assert_locked();
- return getNextMarkedWordAddress(startWord()) >= endWord();
-}
-
-inline void CMSBitMap::iterate(BitMapClosure* cl, HeapWord* left,
- HeapWord* right) {
- assert_locked();
- left = MAX2(_bmStartWord, left);
- right = MIN2(_bmStartWord + _bmWordSize, right);
- if (right > left) {
- _bm.iterate(cl, heapWordToOffset(left), heapWordToOffset(right));
- }
-}
-
-inline void CMSCollector::save_sweep_limits() {
- _cmsGen->save_sweep_limit();
-}
-
-inline bool CMSCollector::is_dead_obj(oop obj) const {
- HeapWord* addr = (HeapWord*)obj;
- assert((_cmsGen->cmsSpace()->is_in_reserved(addr)
- && _cmsGen->cmsSpace()->block_is_obj(addr)),
- "must be object");
- return should_unload_classes() &&
- _collectorState == Sweeping &&
- !_markBitMap.isMarked(addr);
-}
-
-inline bool CMSCollector::should_abort_preclean() const {
- // We are in the midst of an "abortable preclean" and either
- // scavenge is done or foreground GC wants to take over collection
- return _collectorState == AbortablePreclean &&
- (_abort_preclean || _foregroundGCIsActive ||
- CMSHeap::heap()->incremental_collection_will_fail(true /* consult_young */));
-}
-
-inline size_t CMSCollector::get_eden_used() const {
- return _young_gen->eden()->used();
-}
-
-inline size_t CMSCollector::get_eden_capacity() const {
- return _young_gen->eden()->capacity();
-}
-
-inline bool CMSStats::valid() const {
- return _valid_bits == _ALL_VALID;
-}
-
-inline void CMSStats::record_gc0_begin() {
- if (_gc0_begin_time.is_updated()) {
- float last_gc0_period = _gc0_begin_time.seconds();
- _gc0_period = AdaptiveWeightedAverage::exp_avg(_gc0_period,
- last_gc0_period, _gc0_alpha);
- _gc0_alpha = _saved_alpha;
- _valid_bits |= _GC0_VALID;
- }
- _cms_used_at_gc0_begin = _cms_gen->cmsSpace()->used();
-
- _gc0_begin_time.update();
-}
-
-inline void CMSStats::record_gc0_end(size_t cms_gen_bytes_used) {
- float last_gc0_duration = _gc0_begin_time.seconds();
- _gc0_duration = AdaptiveWeightedAverage::exp_avg(_gc0_duration,
- last_gc0_duration, _gc0_alpha);
-
- // Amount promoted.
- _cms_used_at_gc0_end = cms_gen_bytes_used;
-
- size_t promoted_bytes = 0;
- if (_cms_used_at_gc0_end >= _cms_used_at_gc0_begin) {
- promoted_bytes = _cms_used_at_gc0_end - _cms_used_at_gc0_begin;
- }
-
- // If the young gen collection was skipped, then the
- // number of promoted bytes will be 0 and adding it to the
- // average will incorrectly lessen the average. It is, however,
- // also possible that no promotion was needed.
- //
- // _gc0_promoted used to be calculated as
- // _gc0_promoted = AdaptiveWeightedAverage::exp_avg(_gc0_promoted,
- // promoted_bytes, _gc0_alpha);
- _cms_gen->gc_stats()->avg_promoted()->sample(promoted_bytes);
- _gc0_promoted = (size_t) _cms_gen->gc_stats()->avg_promoted()->average();
-
- // Amount directly allocated.
- size_t allocated_bytes = _cms_gen->direct_allocated_words() * HeapWordSize;
- _cms_gen->reset_direct_allocated_words();
- _cms_allocated = AdaptiveWeightedAverage::exp_avg(_cms_allocated,
- allocated_bytes, _gc0_alpha);
-}
-
-inline void CMSStats::record_cms_begin() {
- _cms_timer.stop();
-
- // This is just an approximate value, but is good enough.
- _cms_used_at_cms_begin = _cms_used_at_gc0_end;
-
- _cms_period = AdaptiveWeightedAverage::exp_avg((float)_cms_period,
- (float) _cms_timer.seconds(), _cms_alpha);
- _cms_begin_time.update();
-
- _cms_timer.reset();
- _cms_timer.start();
-}
-
-inline void CMSStats::record_cms_end() {
- _cms_timer.stop();
-
- float cur_duration = _cms_timer.seconds();
- _cms_duration = AdaptiveWeightedAverage::exp_avg(_cms_duration,
- cur_duration, _cms_alpha);
-
- _cms_end_time.update();
- _cms_alpha = _saved_alpha;
- _allow_duty_cycle_reduction = true;
- _valid_bits |= _CMS_VALID;
-
- _cms_timer.start();
-}
-
-inline double CMSStats::cms_time_since_begin() const {
- return _cms_begin_time.seconds();
-}
-
-inline double CMSStats::cms_time_since_end() const {
- return _cms_end_time.seconds();
-}
-
-inline double CMSStats::promotion_rate() const {
- assert(valid(), "statistics not valid yet");
- return gc0_promoted() / gc0_period();
-}
-
-inline double CMSStats::cms_allocation_rate() const {
- assert(valid(), "statistics not valid yet");
- return cms_allocated() / gc0_period();
-}
-
-inline double CMSStats::cms_consumption_rate() const {
- assert(valid(), "statistics not valid yet");
- return (gc0_promoted() + cms_allocated()) / gc0_period();
-}
-
-inline void ConcurrentMarkSweepGeneration::save_sweep_limit() {
- cmsSpace()->save_sweep_limit();
-}
-
-inline MemRegion ConcurrentMarkSweepGeneration::used_region_at_save_marks() const {
- return _cmsSpace->used_region_at_save_marks();
-}
-
-template <typename OopClosureType>
-void ConcurrentMarkSweepGeneration::oop_since_save_marks_iterate(OopClosureType* cl) {
- cl->set_generation(this);
- cmsSpace()->oop_since_save_marks_iterate(cl);
- cl->reset_generation();
- save_marks();
-}
-
-inline void MarkFromRootsClosure::do_yield_check() {
- if (ConcurrentMarkSweepThread::should_yield() &&
- !_collector->foregroundGCIsActive() &&
- _yield) {
- do_yield_work();
- }
-}
-
-inline void ParMarkFromRootsClosure::do_yield_check() {
- if (ConcurrentMarkSweepThread::should_yield() &&
- !_collector->foregroundGCIsActive()) {
- do_yield_work();
- }
-}
-
-inline void PushOrMarkClosure::do_yield_check() {
- _parent->do_yield_check();
-}
-
-inline void ParPushOrMarkClosure::do_yield_check() {
- _parent->do_yield_check();
-}
-
-// Return value of "true" indicates that the on-going preclean
-// should be aborted.
-inline bool ScanMarkedObjectsAgainCarefullyClosure::do_yield_check() {
- if (ConcurrentMarkSweepThread::should_yield() &&
- !_collector->foregroundGCIsActive() &&
- _yield) {
- // Sample young gen size before and after yield
- _collector->sample_eden();
- do_yield_work();
- _collector->sample_eden();
- return _collector->should_abort_preclean();
- }
- return false;
-}
-
-inline void SurvivorSpacePrecleanClosure::do_yield_check() {
- if (ConcurrentMarkSweepThread::should_yield() &&
- !_collector->foregroundGCIsActive() &&
- _yield) {
- // Sample young gen size before and after yield
- _collector->sample_eden();
- do_yield_work();
- _collector->sample_eden();
- }
-}
-
-inline void SweepClosure::do_yield_check(HeapWord* addr) {
- if (ConcurrentMarkSweepThread::should_yield() &&
- !_collector->foregroundGCIsActive() &&
- _yield) {
- do_yield_work(addr);
- }
-}
-
-inline void MarkRefsIntoAndScanClosure::do_yield_check() {
- // The conditions are ordered for the remarking phase
- // when _yield is false.
- if (_yield &&
- !_collector->foregroundGCIsActive() &&
- ConcurrentMarkSweepThread::should_yield()) {
- do_yield_work();
- }
-}
-
-
-inline void ModUnionClosure::do_MemRegion(MemRegion mr) {
- // Align the end of mr so it's at a card boundary.
- // This is superfluous except at the end of the space;
- // we should do better than this XXX
- MemRegion mr2(mr.start(), align_up(mr.end(),
- CardTable::card_size /* bytes */));
- _t->mark_range(mr2);
-}
-
-inline void ModUnionClosurePar::do_MemRegion(MemRegion mr) {
- // Align the end of mr so it's at a card boundary.
- // This is superfluous except at the end of the space;
- // we should do better than this XXX
- MemRegion mr2(mr.start(), align_up(mr.end(),
- CardTable::card_size /* bytes */));
- _t->par_mark_range(mr2);
-}
-
-#endif // SHARE_GC_CMS_CONCURRENTMARKSWEEPGENERATION_INLINE_HPP
--- a/src/hotspot/share/gc/cms/concurrentMarkSweepThread.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,311 +0,0 @@
-/*
- * Copyright (c) 2001, 2019, 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/systemDictionary.hpp"
-#include "gc/cms/cmsHeap.hpp"
-#include "gc/cms/concurrentMarkSweepGeneration.inline.hpp"
-#include "gc/cms/concurrentMarkSweepThread.hpp"
-#include "gc/shared/gcId.hpp"
-#include "memory/universe.hpp"
-#include "oops/oop.inline.hpp"
-#include "runtime/init.hpp"
-#include "runtime/java.hpp"
-#include "runtime/javaCalls.hpp"
-#include "runtime/mutexLocker.hpp"
-#include "runtime/os.hpp"
-#include "runtime/vmThread.hpp"
-
-// ======= Concurrent Mark Sweep Thread ========
-
-ConcurrentMarkSweepThread* ConcurrentMarkSweepThread::_cmst = NULL;
-CMSCollector* ConcurrentMarkSweepThread::_collector = NULL;
-int ConcurrentMarkSweepThread::_CMS_flag = CMS_nil;
-
-volatile jint ConcurrentMarkSweepThread::_pending_yields = 0;
-
-ConcurrentMarkSweepThread::ConcurrentMarkSweepThread(CMSCollector* collector)
- : ConcurrentGCThread() {
- assert(UseConcMarkSweepGC, "UseConcMarkSweepGC should be set");
- assert(_cmst == NULL, "CMS thread already created");
- _cmst = this;
- assert(_collector == NULL, "Collector already set");
- _collector = collector;
-
- set_name("CMS Main Thread");
-
- // An old comment here said: "Priority should be just less
- // than that of VMThread". Since the VMThread runs at
- // NearMaxPriority, the old comment was inaccurate, but
- // changing the default priority to NearMaxPriority-1
- // could change current behavior, so the default of
- // NearMaxPriority stays in place.
- //
- // Note that there's a possibility of the VMThread
- // starving if UseCriticalCMSThreadPriority is on.
- // That won't happen on Solaris for various reasons,
- // but may well happen on non-Solaris platforms.
- create_and_start(UseCriticalCMSThreadPriority ? CriticalPriority : NearMaxPriority);
-}
-
-void ConcurrentMarkSweepThread::run_service() {
- assert(this == cmst(), "just checking");
-
- if (BindCMSThreadToCPU && !os::bind_to_processor(CPUForCMSThread)) {
- log_warning(gc)("Couldn't bind CMS thread to processor " UINTX_FORMAT, CPUForCMSThread);
- }
-
- while (!should_terminate()) {
- sleepBeforeNextCycle();
- if (should_terminate()) break;
- GCIdMark gc_id_mark;
- GCCause::Cause cause = _collector->_full_gc_requested ?
- _collector->_full_gc_cause : GCCause::_cms_concurrent_mark;
- _collector->collect_in_background(cause);
- }
-
- // Check that the state of any protocol for synchronization
- // between background (CMS) and foreground collector is "clean"
- // (i.e. will not potentially block the foreground collector,
- // requiring action by us).
- verify_ok_to_terminate();
-}
-
-#ifndef PRODUCT
-void ConcurrentMarkSweepThread::verify_ok_to_terminate() const {
- assert(!(CGC_lock->owned_by_self() || cms_thread_has_cms_token() ||
- cms_thread_wants_cms_token()),
- "Must renounce all worldly possessions and desires for nirvana");
- _collector->verify_ok_to_terminate();
-}
-#endif
-
-// create and start a new ConcurrentMarkSweep Thread for given CMS generation
-ConcurrentMarkSweepThread* ConcurrentMarkSweepThread::start(CMSCollector* collector) {
- guarantee(_cmst == NULL, "start() called twice!");
- ConcurrentMarkSweepThread* th = new ConcurrentMarkSweepThread(collector);
- assert(_cmst == th, "Where did the just-created CMS thread go?");
- return th;
-}
-
-void ConcurrentMarkSweepThread::stop_service() {
- // Now post a notify on CGC_lock so as to nudge
- // CMS thread(s) that might be slumbering in
- // sleepBeforeNextCycle.
- MutexLocker x(CGC_lock, Mutex::_no_safepoint_check_flag);
- CGC_lock->notify_all();
-}
-
-void ConcurrentMarkSweepThread::threads_do(ThreadClosure* tc) {
- assert(tc != NULL, "Null ThreadClosure");
- if (cmst() != NULL && !cmst()->has_terminated()) {
- tc->do_thread(cmst());
- }
- assert(Universe::is_fully_initialized(),
- "Called too early, make sure heap is fully initialized");
- if (_collector != NULL) {
- AbstractWorkGang* gang = _collector->conc_workers();
- if (gang != NULL) {
- gang->threads_do(tc);
- }
- }
-}
-
-void ConcurrentMarkSweepThread::print_all_on(outputStream* st) {
- if (cmst() != NULL && !cmst()->has_terminated()) {
- cmst()->print_on(st);
- st->cr();
- }
- if (_collector != NULL) {
- AbstractWorkGang* gang = _collector->conc_workers();
- if (gang != NULL) {
- gang->print_worker_threads_on(st);
- }
- }
-}
-
-void ConcurrentMarkSweepThread::synchronize(bool is_cms_thread) {
- assert(UseConcMarkSweepGC, "just checking");
-
- MutexLocker x(CGC_lock, Mutex::_no_safepoint_check_flag);
- if (!is_cms_thread) {
- assert(Thread::current()->is_VM_thread(), "Not a VM thread");
- CMSSynchronousYieldRequest yr;
- while (CMS_flag_is_set(CMS_cms_has_token)) {
- // indicate that we want to get the token
- set_CMS_flag(CMS_vm_wants_token);
- CGC_lock->wait_without_safepoint_check();
- }
- // claim the token and proceed
- clear_CMS_flag(CMS_vm_wants_token);
- set_CMS_flag(CMS_vm_has_token);
- } else {
- assert(Thread::current()->is_ConcurrentGC_thread(),
- "Not a CMS thread");
- // The following barrier assumes there's only one CMS thread.
- // This will need to be modified is there are more CMS threads than one.
- while (CMS_flag_is_set(CMS_vm_has_token | CMS_vm_wants_token)) {
- set_CMS_flag(CMS_cms_wants_token);
- CGC_lock->wait_without_safepoint_check();
- }
- // claim the token
- clear_CMS_flag(CMS_cms_wants_token);
- set_CMS_flag(CMS_cms_has_token);
- }
-}
-
-void ConcurrentMarkSweepThread::desynchronize(bool is_cms_thread) {
- assert(UseConcMarkSweepGC, "just checking");
-
- MutexLocker x(CGC_lock, Mutex::_no_safepoint_check_flag);
- if (!is_cms_thread) {
- assert(Thread::current()->is_VM_thread(), "Not a VM thread");
- assert(CMS_flag_is_set(CMS_vm_has_token), "just checking");
- clear_CMS_flag(CMS_vm_has_token);
- if (CMS_flag_is_set(CMS_cms_wants_token)) {
- // wake-up a waiting CMS thread
- CGC_lock->notify();
- }
- assert(!CMS_flag_is_set(CMS_vm_has_token | CMS_vm_wants_token),
- "Should have been cleared");
- } else {
- assert(Thread::current()->is_ConcurrentGC_thread(),
- "Not a CMS thread");
- assert(CMS_flag_is_set(CMS_cms_has_token), "just checking");
- clear_CMS_flag(CMS_cms_has_token);
- if (CMS_flag_is_set(CMS_vm_wants_token)) {
- // wake-up a waiting VM thread
- CGC_lock->notify();
- }
- assert(!CMS_flag_is_set(CMS_cms_has_token | CMS_cms_wants_token),
- "Should have been cleared");
- }
-}
-
-// Wait until any cms_lock event
-void ConcurrentMarkSweepThread::wait_on_cms_lock(long t_millis) {
- MutexLocker x(CGC_lock, Mutex::_no_safepoint_check_flag);
- if (should_terminate() || _collector->_full_gc_requested) {
- return;
- }
- set_CMS_flag(CMS_cms_wants_token); // to provoke notifies
- CGC_lock->wait_without_safepoint_check(t_millis);
- clear_CMS_flag(CMS_cms_wants_token);
- assert(!CMS_flag_is_set(CMS_cms_has_token | CMS_cms_wants_token),
- "Should not be set");
-}
-
-// Wait until the next synchronous GC, a concurrent full gc request,
-// or a timeout, whichever is earlier.
-void ConcurrentMarkSweepThread::wait_on_cms_lock_for_scavenge(long t_millis) {
- // Wait time in millis or 0 value representing infinite wait for a scavenge
- assert(t_millis >= 0, "Wait time for scavenge should be 0 or positive");
-
- CMSHeap* heap = CMSHeap::heap();
- double start_time_secs = os::elapsedTime();
- double end_time_secs = start_time_secs + (t_millis / ((double) MILLIUNITS));
-
- // Total collections count before waiting loop
- unsigned int before_count;
- {
- MutexLocker hl(Heap_lock, Mutex::_no_safepoint_check_flag);
- before_count = heap->total_collections();
- }
-
- unsigned int loop_count = 0;
-
- while(!should_terminate()) {
- double now_time = os::elapsedTime();
- long wait_time_millis;
-
- if(t_millis != 0) {
- // New wait limit
- wait_time_millis = (long) ((end_time_secs - now_time) * MILLIUNITS);
- if(wait_time_millis <= 0) {
- // Wait time is over
- break;
- }
- } else {
- // No wait limit, wait if necessary forever
- wait_time_millis = 0;
- }
-
- // Wait until the next event or the remaining timeout
- {
- MutexLocker x(CGC_lock, Mutex::_no_safepoint_check_flag);
-
- if (should_terminate() || _collector->_full_gc_requested) {
- return;
- }
- set_CMS_flag(CMS_cms_wants_token); // to provoke notifies
- assert(t_millis == 0 || wait_time_millis > 0, "Sanity");
- CGC_lock->wait_without_safepoint_check(wait_time_millis);
- clear_CMS_flag(CMS_cms_wants_token);
- assert(!CMS_flag_is_set(CMS_cms_has_token | CMS_cms_wants_token),
- "Should not be set");
- }
-
- // Extra wait time check before entering the heap lock to get the collection count
- if(t_millis != 0 && os::elapsedTime() >= end_time_secs) {
- // Wait time is over
- break;
- }
-
- // Total collections count after the event
- unsigned int after_count;
- {
- MutexLocker hl(Heap_lock, Mutex::_no_safepoint_check_flag);
- after_count = heap->total_collections();
- }
-
- if(before_count != after_count) {
- // There was a collection - success
- break;
- }
-
- // Too many loops warning
- if(++loop_count == 0) {
- log_warning(gc)("wait_on_cms_lock_for_scavenge() has looped %u times", loop_count - 1);
- }
- }
-}
-
-void ConcurrentMarkSweepThread::sleepBeforeNextCycle() {
- while (!should_terminate()) {
- if(CMSWaitDuration >= 0) {
- // Wait until the next synchronous GC, a concurrent full gc
- // request or a timeout, whichever is earlier.
- wait_on_cms_lock_for_scavenge(CMSWaitDuration);
- } else {
- // Wait until any cms_lock event or check interval not to call shouldConcurrentCollect permanently
- wait_on_cms_lock(CMSCheckInterval);
- }
- // Check if we should start a CMS collection cycle
- if (_collector->shouldConcurrentCollect()) {
- return;
- }
- // .. collection criterion not yet met, let's go back
- // and wait some more
- }
-}
--- a/src/hotspot/share/gc/cms/concurrentMarkSweepThread.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,169 +0,0 @@
-/*
- * Copyright (c) 2001, 2019, 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_GC_CMS_CONCURRENTMARKSWEEPTHREAD_HPP
-#define SHARE_GC_CMS_CONCURRENTMARKSWEEPTHREAD_HPP
-
-#include "gc/cms/concurrentMarkSweepGeneration.hpp"
-#include "gc/shared/concurrentGCThread.hpp"
-#include "runtime/thread.hpp"
-
-class ConcurrentMarkSweepGeneration;
-class CMSCollector;
-
-// The Concurrent Mark Sweep GC Thread
-class ConcurrentMarkSweepThread: public ConcurrentGCThread {
- friend class VMStructs;
- friend class ConcurrentMarkSweepGeneration; // XXX should remove friendship
- friend class CMSCollector;
-
- private:
- static ConcurrentMarkSweepThread* _cmst;
- static CMSCollector* _collector;
-
- enum CMS_flag_type {
- CMS_nil = NoBits,
- CMS_cms_wants_token = nth_bit(0),
- CMS_cms_has_token = nth_bit(1),
- CMS_vm_wants_token = nth_bit(2),
- CMS_vm_has_token = nth_bit(3)
- };
-
- static int _CMS_flag;
-
- static bool CMS_flag_is_set(int b) { return (_CMS_flag & b) != 0; }
- static bool set_CMS_flag(int b) { return (_CMS_flag |= b) != 0; }
- static bool clear_CMS_flag(int b) { return (_CMS_flag &= ~b) != 0; }
- void sleepBeforeNextCycle();
-
- // CMS thread should yield for a young gen collection and direct allocations
- static char _pad_1[64 - sizeof(jint)]; // prevent cache-line sharing
- static volatile jint _pending_yields;
- static char _pad_2[64 - sizeof(jint)]; // prevent cache-line sharing
-
- // debugging
- void verify_ok_to_terminate() const PRODUCT_RETURN;
-
- void run_service();
- void stop_service();
-
- public:
- // Constructor
- ConcurrentMarkSweepThread(CMSCollector* collector);
-
- static void threads_do(ThreadClosure* tc);
-
- // Printing
- static void print_all_on(outputStream* st);
- static void print_all() { print_all_on(tty); }
-
- // Returns the CMS Thread
- static ConcurrentMarkSweepThread* cmst() { return _cmst; }
- static CMSCollector* collector() { return _collector; }
-
- // Create and start the CMS Thread, or stop it on shutdown
- static ConcurrentMarkSweepThread* start(CMSCollector* collector);
-
- // Synchronization using CMS token
- static void synchronize(bool is_cms_thread);
- static void desynchronize(bool is_cms_thread);
- static bool vm_thread_has_cms_token() {
- return CMS_flag_is_set(CMS_vm_has_token);
- }
- static bool cms_thread_has_cms_token() {
- return CMS_flag_is_set(CMS_cms_has_token);
- }
- static bool vm_thread_wants_cms_token() {
- return CMS_flag_is_set(CMS_vm_wants_token);
- }
- static bool cms_thread_wants_cms_token() {
- return CMS_flag_is_set(CMS_cms_wants_token);
- }
-
- // Wait on CMS lock until the next synchronous GC
- // or given timeout, whichever is earlier. A timeout value
- // of 0 indicates that there is no upper bound on the wait time.
- // A concurrent full gc request terminates the wait.
- void wait_on_cms_lock(long t_millis);
-
- // Wait on CMS lock until the next synchronous GC
- // or given timeout, whichever is earlier. A timeout value
- // of 0 indicates that there is no upper bound on the wait time.
- // A concurrent full gc request terminates the wait.
- void wait_on_cms_lock_for_scavenge(long t_millis);
-
- // The CMS thread will yield during the work portion of its cycle
- // only when requested to.
- // A synchronous request is used for young gen collections and
- // for direct allocations. The requesting thread increments
- // _pending_yields at the beginning of an operation, and decrements
- // _pending_yields when that operation is completed.
- // In turn, the CMS thread yields when _pending_yields is positive,
- // and continues to yield until the value reverts to 0.
-
- static void increment_pending_yields() {
- Atomic::inc(&_pending_yields);
- assert(_pending_yields >= 0, "can't be negative");
- }
- static void decrement_pending_yields() {
- Atomic::dec(&_pending_yields);
- assert(_pending_yields >= 0, "can't be negative");
- }
- static bool should_yield() { return _pending_yields > 0; }
-};
-
-// For scoped increment/decrement of (synchronous) yield requests
-class CMSSynchronousYieldRequest: public StackObj {
- public:
- CMSSynchronousYieldRequest() {
- ConcurrentMarkSweepThread::increment_pending_yields();
- }
- ~CMSSynchronousYieldRequest() {
- ConcurrentMarkSweepThread::decrement_pending_yields();
- }
-};
-
-// Used to emit a warning in case of unexpectedly excessive
-// looping (in "apparently endless loops") in CMS code.
-class CMSLoopCountWarn: public StackObj {
- private:
- const char* _src;
- const char* _msg;
- const intx _threshold;
- intx _ticks;
-
- public:
- inline CMSLoopCountWarn(const char* src, const char* msg,
- const intx threshold) :
- _src(src), _msg(msg), _threshold(threshold), _ticks(0) { }
-
- inline void tick() {
- _ticks++;
- if (CMSLoopWarn && _ticks % _threshold == 0) {
- log_warning(gc)("%s has looped " INTX_FORMAT " times %s", _src, _ticks, _msg);
- }
- }
-};
-
-#endif // SHARE_GC_CMS_CONCURRENTMARKSWEEPTHREAD_HPP
--- a/src/hotspot/share/gc/cms/freeChunk.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2001, 2017, 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/cms/freeChunk.hpp"
-#include "utilities/copy.hpp"
-
-#ifndef PRODUCT
-
-#define baadbabeHeapWord badHeapWordVal
-#define deadbeefHeapWord 0xdeadbeef
-
-size_t const FreeChunk::header_size() {
- return sizeof(FreeChunk)/HeapWordSize;
-}
-
-void FreeChunk::mangleAllocated(size_t size) {
- // mangle all but the header of a just-allocated block
- // of storage
- assert(size >= MinChunkSize, "smallest size of object");
- // we can't assert that _size == size because this may be an
- // allocation out of a linear allocation block
- assert(sizeof(FreeChunk) % HeapWordSize == 0,
- "shouldn't write beyond chunk");
- HeapWord* addr = (HeapWord*)this;
- size_t hdr = header_size();
- Copy::fill_to_words(addr + hdr, size - hdr, baadbabeHeapWord);
-}
-
-void FreeChunk::mangleFreed(size_t sz) {
- assert(baadbabeHeapWord != deadbeefHeapWord, "Need distinct patterns");
- // mangle all but the header of a just-freed block of storage
- // just prior to passing it to the storage dictionary
- assert(sz >= MinChunkSize, "smallest size of object");
- assert(sz == size(), "just checking");
- HeapWord* addr = (HeapWord*)this;
- size_t hdr = header_size();
- Copy::fill_to_words(addr + hdr, sz - hdr, deadbeefHeapWord);
-}
-
-void FreeChunk::verifyList() const {
- FreeChunk* nextFC = next();
- if (nextFC != NULL) {
- assert(this == nextFC->prev(), "broken chain");
- assert(size() == nextFC->size(), "wrong size");
- nextFC->verifyList();
- }
-}
-#endif
-
-void FreeChunk::print_on(outputStream* st) {
- st->print_cr("Next: " PTR_FORMAT " Prev: " PTR_FORMAT " %s",
- p2i(next()), p2i(prev()), cantCoalesce() ? "[can't coalesce]" : "");
-}
--- a/src/hotspot/share/gc/cms/freeChunk.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,150 +0,0 @@
-/*
- * Copyright (c) 2001, 2019, 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_GC_CMS_FREECHUNK_HPP
-#define SHARE_GC_CMS_FREECHUNK_HPP
-
-#include "memory/memRegion.hpp"
-#include "oops/markWord.hpp"
-#include "runtime/mutex.hpp"
-#include "runtime/orderAccess.hpp"
-#include "utilities/debug.hpp"
-#include "utilities/globalDefinitions.hpp"
-#include "utilities/ostream.hpp"
-
-//
-// Free block maintenance for Concurrent Mark Sweep Generation
-//
-// The main data structure for free blocks are
-// . an indexed array of small free blocks, and
-// . a dictionary of large free blocks
-//
-
-// No virtuals in FreeChunk (don't want any vtables).
-
-// A FreeChunk is merely a chunk that can be in a doubly linked list
-// and has a size field. NOTE: FreeChunks are distinguished from allocated
-// objects in two ways (by the sweeper), depending on whether the VM is 32 or
-// 64 bits.
-// In 32 bits or 64 bits without CompressedOops, the second word (prev) has the
-// LSB set to indicate a free chunk; allocated objects' klass() pointers
-// don't have their LSB set. The corresponding bit in the CMSBitMap is
-// set when the chunk is allocated. There are also blocks that "look free"
-// but are not part of the free list and should not be coalesced into larger
-// free blocks. These free blocks have their two LSB's set.
-
-class FreeChunk {
- friend class VMStructs;
- // For 64 bit compressed oops, the markWord encodes both the size and the
- // indication that this is a FreeChunk and not an object.
- volatile size_t _size;
- FreeChunk* _prev;
- FreeChunk* _next;
-
- markWord mark() const volatile { return markWord((uintptr_t)_size); }
- void set_mark(markWord m) { _size = (size_t)m.value(); }
-
- public:
- NOT_PRODUCT(static const size_t header_size();)
-
- // Returns "true" if the address indicates that the block represents
- // a free chunk.
- static bool indicatesFreeChunk(const HeapWord* addr) {
- // Force volatile read from addr because value might change between
- // calls. We really want the read of _mark and _prev from this pointer
- // to be volatile but making the fields volatile causes all sorts of
- // compilation errors.
- return ((volatile FreeChunk*)addr)->is_free();
- }
-
- bool is_free() const volatile {
- LP64_ONLY(if (UseCompressedOops) return mark().is_cms_free_chunk(); else)
- return (((intptr_t)_prev) & 0x1) == 0x1;
- }
- bool cantCoalesce() const {
- assert(is_free(), "can't get coalesce bit on not free");
- return (((intptr_t)_prev) & 0x2) == 0x2;
- }
- void dontCoalesce() {
- // the block should be free
- assert(is_free(), "Should look like a free block");
- _prev = (FreeChunk*)(((intptr_t)_prev) | 0x2);
- }
- FreeChunk* prev() const {
- return (FreeChunk*)(((intptr_t)_prev) & ~(0x3));
- }
-
- debug_only(void* prev_addr() const { return (void*)&_prev; })
- debug_only(void* next_addr() const { return (void*)&_next; })
- debug_only(void* size_addr() const { return (void*)&_size; })
-
- size_t size() const volatile {
- LP64_ONLY(if (UseCompressedOops) return mark().get_size(); else )
- return _size;
- }
- void set_size(size_t sz) {
- LP64_ONLY(if (UseCompressedOops) set_mark(markWord::set_size_and_free(sz)); else )
- _size = sz;
- }
-
- FreeChunk* next() const { return _next; }
-
- void link_after(FreeChunk* ptr) {
- link_next(ptr);
- if (ptr != NULL) ptr->link_prev(this);
- }
- void link_next(FreeChunk* ptr) { _next = ptr; }
- void link_prev(FreeChunk* ptr) {
- LP64_ONLY(if (UseCompressedOops) _prev = ptr; else)
- _prev = (FreeChunk*)((intptr_t)ptr | 0x1);
- }
- void clear_next() { _next = NULL; }
- void markNotFree() {
- // Set _prev (klass) to null before (if) clearing the mark word below
- _prev = NULL;
-#ifdef _LP64
- if (UseCompressedOops) {
- OrderAccess::storestore();
- set_mark(markWord::prototype());
- }
-#endif
- assert(!is_free(), "Error");
- }
-
- // Return the address past the end of this chunk
- uintptr_t* end() const { return ((uintptr_t*) this) + size(); }
-
- // debugging
- void verify() const PRODUCT_RETURN;
- void verifyList() const PRODUCT_RETURN;
- void mangleAllocated(size_t size) PRODUCT_RETURN;
- void mangleFreed(size_t size) PRODUCT_RETURN;
-
- void print_on(outputStream* st);
-};
-
-extern size_t MinChunkSize;
-
-
-#endif // SHARE_GC_CMS_FREECHUNK_HPP
--- a/src/hotspot/share/gc/cms/gSpaceCounters.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2002, 2019, 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/cms/gSpaceCounters.hpp"
-#include "gc/shared/generation.hpp"
-#include "memory/allocation.inline.hpp"
-#include "memory/resourceArea.hpp"
-#include "utilities/macros.hpp"
-
-GSpaceCounters::GSpaceCounters(const char* name, int ordinal, size_t max_size,
- Generation* g, GenerationCounters* gc,
- bool sampled) :
- _gen(g) {
-
- if (UsePerfData) {
- EXCEPTION_MARK;
- ResourceMark rm;
-
- const char* cns = PerfDataManager::name_space(gc->name_space(), "space",
- ordinal);
-
- _name_space = NEW_C_HEAP_ARRAY(char, strlen(cns)+1, mtGC);
- strcpy(_name_space, cns);
-
- const char* cname = PerfDataManager::counter_name(_name_space, "name");
- PerfDataManager::create_string_constant(SUN_GC, cname, name, CHECK);
-
- cname = PerfDataManager::counter_name(_name_space, "maxCapacity");
- PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes,
- (jlong)max_size, CHECK);
-
- cname = PerfDataManager::counter_name(_name_space, "capacity");
- _capacity = PerfDataManager::create_variable(SUN_GC, cname,
- PerfData::U_Bytes,
- _gen->capacity(), CHECK);
-
- cname = PerfDataManager::counter_name(_name_space, "used");
- if (sampled) {
- _used = PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes,
- new GenerationUsedHelper(_gen),
- CHECK);
- }
- else {
- _used = PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes,
- (jlong)0, CHECK);
- }
-
- cname = PerfDataManager::counter_name(_name_space, "initCapacity");
- PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes,
- _gen->capacity(), CHECK);
- }
-}
-
-GSpaceCounters::~GSpaceCounters() {
- FREE_C_HEAP_ARRAY(char, _name_space);
-}
--- a/src/hotspot/share/gc/cms/gSpaceCounters.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2002, 2019, 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_GC_CMS_GSPACECOUNTERS_HPP
-#define SHARE_GC_CMS_GSPACECOUNTERS_HPP
-
-#include "gc/shared/generation.hpp"
-#include "gc/shared/generationCounters.hpp"
-#include "runtime/perfData.hpp"
-#include "utilities/macros.hpp"
-
-// A GSpaceCounter is a holder class for performance counters
-// that track a space;
-
-class GSpaceCounters: public CHeapObj<mtGC> {
- friend class VMStructs;
-
- private:
- PerfVariable* _capacity;
- PerfVariable* _used;
-
- // Constant PerfData types don't need to retain a reference.
- // However, it's a good idea to document them here.
- // PerfConstant* _size;
-
- Generation* _gen;
- char* _name_space;
-
- public:
-
- GSpaceCounters(const char* name, int ordinal, size_t max_size, Generation* g,
- GenerationCounters* gc, bool sampled=true);
-
- ~GSpaceCounters();
-
- inline void update_capacity() {
- _capacity->set_value(_gen->capacity());
- }
-
- inline void update_used() {
- _used->set_value(_gen->used_stable());
- }
-
- // special version of update_used() to allow the used value to be
- // passed as a parameter. This method can can be used in cases were
- // the utilization is already known and/or when the _gen->used()
- // method is known to be expensive and we want to avoid unnecessary
- // calls to it.
- //
- inline void update_used(size_t used) {
- _used->set_value(used);
- }
-
- inline void inc_used(size_t size) {
- _used->inc(size);
- }
-
- debug_only(
- // for security reasons, we do not allow arbitrary reads from
- // the counters as they may live in shared memory.
- jlong used() {
- return _used->get_value();
- }
- jlong capacity() {
- return _used->get_value();
- }
- )
-
- inline void update_all() {
- update_used();
- update_capacity();
- }
-
- const char* name_space() const { return _name_space; }
-};
-
-class GenerationUsedHelper : public PerfLongSampleHelper {
- private:
- Generation* _gen;
-
- public:
- GenerationUsedHelper(Generation* g) : _gen(g) { }
-
- inline jlong take_sample() {
- return _gen->used_stable();
- }
-};
-
-#endif // SHARE_GC_CMS_GSPACECOUNTERS_HPP
--- a/src/hotspot/share/gc/cms/jvmFlagConstraintsCMS.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,238 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, 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/cms/jvmFlagConstraintsCMS.hpp"
-#include "gc/cms/concurrentMarkSweepGeneration.inline.hpp"
-#include "gc/shared/cardTableRS.hpp"
-#include "gc/shared/collectedHeap.hpp"
-#include "gc/shared/genCollectedHeap.hpp"
-#include "gc/shared/jvmFlagConstraintsGC.hpp"
-#include "memory/universe.hpp"
-#include "runtime/globals_extension.hpp"
-#include "utilities/globalDefinitions.hpp"
-
-static JVMFlag::Error ParallelGCThreadsAndCMSWorkQueueDrainThreshold(uint threads, uintx threshold, bool verbose) {
- // CMSWorkQueueDrainThreshold is verified to be less than max_juint
- if (UseConcMarkSweepGC && (threads > (uint)(max_jint / (uint)threshold))) {
- JVMFlag::printError(verbose,
- "ParallelGCThreads (" UINT32_FORMAT ") or CMSWorkQueueDrainThreshold ("
- UINTX_FORMAT ") is too large\n",
- threads, threshold);
- return JVMFlag::VIOLATES_CONSTRAINT;
- }
- return JVMFlag::SUCCESS;
-}
-
-JVMFlag::Error ParallelGCThreadsConstraintFuncCMS(uint value, bool verbose) {
- // To avoid overflow at ParScanClosure::do_oop_work.
- if (UseConcMarkSweepGC && (value > (max_jint / 10))) {
- JVMFlag::printError(verbose,
- "ParallelGCThreads (" UINT32_FORMAT ") must be "
- "less than or equal to " UINT32_FORMAT " for CMS GC\n",
- value, (max_jint / 10));
- return JVMFlag::VIOLATES_CONSTRAINT;
- }
- return ParallelGCThreadsAndCMSWorkQueueDrainThreshold(value, CMSWorkQueueDrainThreshold, verbose);
-}
-JVMFlag::Error ParGCStridesPerThreadConstraintFunc(uintx value, bool verbose) {
- if (UseConcMarkSweepGC && (value > ((uintx)max_jint / (uintx)ParallelGCThreads))) {
- JVMFlag::printError(verbose,
- "ParGCStridesPerThread (" UINTX_FORMAT ") must be "
- "less than or equal to ergonomic maximum (" UINTX_FORMAT ")\n",
- value, ((uintx)max_jint / (uintx)ParallelGCThreads));
- return JVMFlag::VIOLATES_CONSTRAINT;
- }
- return JVMFlag::SUCCESS;
-}
-
-JVMFlag::Error ParGCCardsPerStrideChunkConstraintFunc(intx value, bool verbose) {
- if (UseConcMarkSweepGC) {
- // ParGCCardsPerStrideChunk should be compared with card table size.
- size_t heap_size = CMSHeap::heap()->reserved_region().word_size();
- CardTableRS* ct = GenCollectedHeap::heap()->rem_set();
- size_t card_table_size = ct->cards_required(heap_size) - 1; // Valid card table size
-
- if ((size_t)value > card_table_size) {
- JVMFlag::printError(verbose,
- "ParGCCardsPerStrideChunk (" INTX_FORMAT ") is too large for the heap size and "
- "must be less than or equal to card table size (" SIZE_FORMAT ")\n",
- value, card_table_size);
- return JVMFlag::VIOLATES_CONSTRAINT;
- }
-
- // ParGCCardsPerStrideChunk is used with n_strides(ParallelGCThreads*ParGCStridesPerThread)
- // from CardTableRS::process_stride(). Note that ParGCStridesPerThread is already checked
- // not to make an overflow with ParallelGCThreads from its constraint function.
- uintx n_strides = ParallelGCThreads * ParGCStridesPerThread;
- uintx ergo_max = max_uintx / n_strides;
- if ((uintx)value > ergo_max) {
- JVMFlag::printError(verbose,
- "ParGCCardsPerStrideChunk (" INTX_FORMAT ") must be "
- "less than or equal to ergonomic maximum (" UINTX_FORMAT ")\n",
- value, ergo_max);
- return JVMFlag::VIOLATES_CONSTRAINT;
- }
- }
- return JVMFlag::SUCCESS;
-}
-
-JVMFlag::Error CMSOldPLABMinConstraintFunc(size_t value, bool verbose) {
- JVMFlag::Error status = JVMFlag::SUCCESS;
-
- if (UseConcMarkSweepGC) {
- if (value > CMSOldPLABMax) {
- JVMFlag::printError(verbose,
- "CMSOldPLABMin (" SIZE_FORMAT ") must be "
- "less than or equal to CMSOldPLABMax (" SIZE_FORMAT ")\n",
- value, CMSOldPLABMax);
- return JVMFlag::VIOLATES_CONSTRAINT;
- }
- status = MaxPLABSizeBounds("CMSOldPLABMin", value, verbose);
- }
- return status;
-}
-
-JVMFlag::Error CMSOldPLABMaxConstraintFunc(size_t value, bool verbose) {
- JVMFlag::Error status = JVMFlag::SUCCESS;
-
- if (UseConcMarkSweepGC) {
- status = MaxPLABSizeBounds("CMSOldPLABMax", value, verbose);
- }
- return status;
-}
-
-static JVMFlag::Error CMSReservedAreaConstraintFunc(const char* name, size_t value, bool verbose) {
- if (UseConcMarkSweepGC) {
- ConcurrentMarkSweepGeneration* cms = CMSHeap::heap()->old_gen();
- const size_t ergo_max = cms->cmsSpace()->max_flag_size_for_task_size();
- if (value > ergo_max) {
- JVMFlag::printError(verbose,
- "%s (" SIZE_FORMAT ") must be "
- "less than or equal to ergonomic maximum (" SIZE_FORMAT ") "
- "which is based on the maximum size of the old generation of the Java heap\n",
- name, value, ergo_max);
- return JVMFlag::VIOLATES_CONSTRAINT;
- }
- }
- return JVMFlag::SUCCESS;
-}
-
-JVMFlag::Error CMSRescanMultipleConstraintFunc(size_t value, bool verbose) {
- JVMFlag::Error status = CMSReservedAreaConstraintFunc("CMSRescanMultiple", value, verbose);
-
- if (status == JVMFlag::SUCCESS && UseConcMarkSweepGC) {
- // CMSParRemarkTask::do_dirty_card_rescan_tasks requires CompactibleFreeListSpace::rescan_task_size()
- // to be aligned to CardTable::card_size * BitsPerWord.
- // Note that rescan_task_size() will be aligned if CMSRescanMultiple is a multiple of 'HeapWordSize'
- // because rescan_task_size() is CardTable::card_size / HeapWordSize * BitsPerWord.
- if (value % HeapWordSize != 0) {
- JVMFlag::printError(verbose,
- "CMSRescanMultiple (" SIZE_FORMAT ") must be "
- "a multiple of %d\n",
- value, HeapWordSize);
- status = JVMFlag::VIOLATES_CONSTRAINT;
- }
- }
- return status;
-}
-
-JVMFlag::Error CMSConcMarkMultipleConstraintFunc(size_t value, bool verbose) {
- return CMSReservedAreaConstraintFunc("CMSConcMarkMultiple", value, verbose);
-}
-
-JVMFlag::Error CMSPrecleanDenominatorConstraintFunc(uintx value, bool verbose) {
- if (UseConcMarkSweepGC && (value <= CMSPrecleanNumerator)) {
- JVMFlag::printError(verbose,
- "CMSPrecleanDenominator (" UINTX_FORMAT ") must be "
- "strickly greater than CMSPrecleanNumerator (" UINTX_FORMAT ")\n",
- value, CMSPrecleanNumerator);
- return JVMFlag::VIOLATES_CONSTRAINT;
- }
- return JVMFlag::SUCCESS;
-}
-
-JVMFlag::Error CMSPrecleanNumeratorConstraintFunc(uintx value, bool verbose) {
- if (UseConcMarkSweepGC && (value >= CMSPrecleanDenominator)) {
- JVMFlag::printError(verbose,
- "CMSPrecleanNumerator (" UINTX_FORMAT ") must be "
- "less than CMSPrecleanDenominator (" UINTX_FORMAT ")\n",
- value, CMSPrecleanDenominator);
- return JVMFlag::VIOLATES_CONSTRAINT;
- }
- return JVMFlag::SUCCESS;
-}
-
-JVMFlag::Error CMSSamplingGrainConstraintFunc(uintx value, bool verbose) {
- if (UseConcMarkSweepGC) {
- size_t max_capacity = CMSHeap::heap()->young_gen()->max_capacity();
- if (value > max_uintx - max_capacity) {
- JVMFlag::printError(verbose,
- "CMSSamplingGrain (" UINTX_FORMAT ") must be "
- "less than or equal to ergonomic maximum (" SIZE_FORMAT ")\n",
- value, max_uintx - max_capacity);
- return JVMFlag::VIOLATES_CONSTRAINT;
- }
- }
- return JVMFlag::SUCCESS;
-}
-
-JVMFlag::Error CMSWorkQueueDrainThresholdConstraintFunc(uintx value, bool verbose) {
- if (UseConcMarkSweepGC) {
- return ParallelGCThreadsAndCMSWorkQueueDrainThreshold(ParallelGCThreads, value, verbose);
- }
- return JVMFlag::SUCCESS;
-}
-
-JVMFlag::Error CMSBitMapYieldQuantumConstraintFunc(size_t value, bool verbose) {
- // Skip for current default value.
- if (UseConcMarkSweepGC && FLAG_IS_CMDLINE(CMSBitMapYieldQuantum)) {
- // CMSBitMapYieldQuantum should be compared with mark bitmap size.
- ConcurrentMarkSweepGeneration* cms = CMSHeap::heap()->old_gen();
- size_t bitmap_size = cms->collector()->markBitMap()->sizeInWords();
-
- if (value > bitmap_size) {
- JVMFlag::printError(verbose,
- "CMSBitMapYieldQuantum (" SIZE_FORMAT ") must "
- "be less than or equal to bitmap size (" SIZE_FORMAT ") "
- "whose size corresponds to the size of old generation of the Java heap\n",
- value, bitmap_size);
- return JVMFlag::VIOLATES_CONSTRAINT;
- }
- }
- return JVMFlag::SUCCESS;
-}
-
-JVMFlag::Error OldPLABSizeConstraintFuncCMS(size_t value, bool verbose) {
- if (value == 0) {
- JVMFlag::printError(verbose,
- "OldPLABSize (" SIZE_FORMAT ") must be greater than 0",
- value);
- return JVMFlag::VIOLATES_CONSTRAINT;
- }
- // For CMS, OldPLABSize is the number of free blocks of a given size that are used when
- // replenishing the local per-worker free list caches.
- // For more details, please refer to Arguments::set_cms_and_parnew_gc_flags().
- return MaxPLABSizeBounds("OldPLABSize", value, verbose);
-}
--- a/src/hotspot/share/gc/cms/jvmFlagConstraintsCMS.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2015, 2019, 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_GC_CMS_JVMFLAGCONSTRAINTSCMS_HPP
-#define SHARE_GC_CMS_JVMFLAGCONSTRAINTSCMS_HPP
-
-#include "runtime/flags/jvmFlag.hpp"
-#include "utilities/globalDefinitions.hpp"
-
-// CMS Flag Constraints
-JVMFlag::Error ParGCStridesPerThreadConstraintFunc(uintx value, bool verbose);
-JVMFlag::Error ParGCCardsPerStrideChunkConstraintFunc(intx value, bool verbose);
-JVMFlag::Error CMSOldPLABMinConstraintFunc(size_t value, bool verbose);
-JVMFlag::Error CMSOldPLABMaxConstraintFunc(size_t value, bool verbose);
-JVMFlag::Error CMSRescanMultipleConstraintFunc(size_t value, bool verbose);
-JVMFlag::Error CMSConcMarkMultipleConstraintFunc(size_t value, bool verbose);
-JVMFlag::Error CMSPrecleanDenominatorConstraintFunc(uintx value, bool verbose);
-JVMFlag::Error CMSPrecleanNumeratorConstraintFunc(uintx value, bool verbose);
-JVMFlag::Error CMSSamplingGrainConstraintFunc(uintx value, bool verbose);
-JVMFlag::Error CMSWorkQueueDrainThresholdConstraintFunc(uintx value, bool verbose);
-JVMFlag::Error CMSBitMapYieldQuantumConstraintFunc(size_t value, bool verbose);
-
-// CMS Subconstraints
-JVMFlag::Error ParallelGCThreadsConstraintFuncCMS(uint value, bool verbose);
-JVMFlag::Error OldPLABSizeConstraintFuncCMS(size_t value, bool verbose);
-
-#endif // SHARE_GC_CMS_JVMFLAGCONSTRAINTSCMS_HPP
--- a/src/hotspot/share/gc/cms/parNewGeneration.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1446 +0,0 @@
-/*
- * Copyright (c) 2001, 2019, 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/stringTable.hpp"
-#include "gc/cms/cmsHeap.inline.hpp"
-#include "gc/cms/compactibleFreeListSpace.hpp"
-#include "gc/cms/concurrentMarkSweepGeneration.hpp"
-#include "gc/cms/parNewGeneration.inline.hpp"
-#include "gc/cms/parOopClosures.inline.hpp"
-#include "gc/serial/defNewGeneration.inline.hpp"
-#include "gc/shared/adaptiveSizePolicy.hpp"
-#include "gc/shared/ageTable.inline.hpp"
-#include "gc/shared/copyFailedInfo.hpp"
-#include "gc/shared/gcHeapSummary.hpp"
-#include "gc/shared/gcTimer.hpp"
-#include "gc/shared/gcTrace.hpp"
-#include "gc/shared/gcTraceTime.inline.hpp"
-#include "gc/shared/genOopClosures.inline.hpp"
-#include "gc/shared/generation.hpp"
-#include "gc/shared/plab.inline.hpp"
-#include "gc/shared/preservedMarks.inline.hpp"
-#include "gc/shared/referencePolicy.hpp"
-#include "gc/shared/referenceProcessorPhaseTimes.hpp"
-#include "gc/shared/space.hpp"
-#include "gc/shared/spaceDecorator.inline.hpp"
-#include "gc/shared/strongRootsScope.hpp"
-#include "gc/shared/taskqueue.inline.hpp"
-#include "gc/shared/weakProcessor.hpp"
-#include "gc/shared/workgroup.hpp"
-#include "gc/shared/workerPolicy.hpp"
-#include "logging/log.hpp"
-#include "logging/logStream.hpp"
-#include "memory/iterator.inline.hpp"
-#include "memory/resourceArea.hpp"
-#include "oops/access.inline.hpp"
-#include "oops/compressedOops.inline.hpp"
-#include "oops/objArrayOop.hpp"
-#include "oops/oop.inline.hpp"
-#include "runtime/atomic.hpp"
-#include "runtime/handles.inline.hpp"
-#include "runtime/java.hpp"
-#include "runtime/thread.inline.hpp"
-#include "utilities/copy.hpp"
-#include "utilities/globalDefinitions.hpp"
-#include "utilities/stack.inline.hpp"
-
-ParScanThreadState::ParScanThreadState(Space* to_space_,
- ParNewGeneration* young_gen_,
- Generation* old_gen_,
- int thread_num_,
- ObjToScanQueueSet* work_queue_set_,
- Stack<oop, mtGC>* overflow_stacks_,
- PreservedMarks* preserved_marks_,
- size_t desired_plab_sz_,
- TaskTerminator& term_) :
- _work_queue(work_queue_set_->queue(thread_num_)),
- _overflow_stack(overflow_stacks_ ? overflow_stacks_ + thread_num_ : NULL),
- _preserved_marks(preserved_marks_),
- _to_space_alloc_buffer(desired_plab_sz_),
- _to_space_closure(young_gen_, this),
- _old_gen_closure(young_gen_, this),
- _to_space_root_closure(young_gen_, this),
- _older_gen_closure(young_gen_, this),
- _old_gen_root_closure(young_gen_, this),
- _evacuate_followers(this, &_to_space_closure, &_old_gen_closure,
- &_to_space_root_closure, young_gen_, &_old_gen_root_closure,
- work_queue_set_, term_.terminator()),
- _is_alive_closure(young_gen_),
- _scan_weak_ref_closure(young_gen_, this),
- _keep_alive_closure(&_scan_weak_ref_closure),
- _to_space(to_space_),
- _young_gen(young_gen_),
- _old_gen(old_gen_),
- _young_old_boundary(NULL),
- _thread_num(thread_num_),
- _ageTable(false), // false ==> not the global age table, no perf data.
- _to_space_full(false),
- _strong_roots_time(0.0),
- _term_time(0.0)
-{
- #if TASKQUEUE_STATS
- _term_attempts = 0;
- _overflow_refills = 0;
- _overflow_refill_objs = 0;
- #endif // TASKQUEUE_STATS
-
- _survivor_chunk_array = (ChunkArray*) old_gen()->get_data_recorder(thread_num());
- _start = os::elapsedTime();
- _old_gen_closure.set_generation(old_gen_);
- _old_gen_root_closure.set_generation(old_gen_);
-}
-
-void ParScanThreadState::record_survivor_plab(HeapWord* plab_start,
- size_t plab_word_size) {
- ChunkArray* sca = survivor_chunk_array();
- if (sca != NULL) {
- // A non-null SCA implies that we want the PLAB data recorded.
- sca->record_sample(plab_start, plab_word_size);
- }
-}
-
-bool ParScanThreadState::should_be_partially_scanned(oop new_obj, oop old_obj) const {
- return new_obj->is_objArray() &&
- arrayOop(new_obj)->length() > ParGCArrayScanChunk &&
- new_obj != old_obj;
-}
-
-void ParScanThreadState::scan_partial_array_and_push_remainder(oop old) {
- assert(old->is_objArray(), "must be obj array");
- assert(old->is_forwarded(), "must be forwarded");
- assert(CMSHeap::heap()->is_in_reserved(old), "must be in heap.");
- assert(!old_gen()->is_in(old), "must be in young generation.");
-
- objArrayOop obj = objArrayOop(old->forwardee());
- // Process ParGCArrayScanChunk elements now
- // and push the remainder back onto queue
- int start = arrayOop(old)->length();
- int end = obj->length();
- int remainder = end - start;
- assert(start <= end, "just checking");
- if (remainder > 2 * ParGCArrayScanChunk) {
- // Test above combines last partial chunk with a full chunk
- end = start + ParGCArrayScanChunk;
- arrayOop(old)->set_length(end);
- // Push remainder.
- bool ok = work_queue()->push(old);
- assert(ok, "just popped, push must be okay");
- } else {
- // Restore length so that it can be used if there
- // is a promotion failure and forwarding pointers
- // must be removed.
- arrayOop(old)->set_length(end);
- }
-
- // process our set of indices (include header in first chunk)
- // should make sure end is even (aligned to HeapWord in case of compressed oops)
- if ((HeapWord *)obj < young_old_boundary()) {
- // object is in to_space
- obj->oop_iterate_range(&_to_space_closure, start, end);
- } else {
- // object is in old generation
- obj->oop_iterate_range(&_old_gen_closure, start, end);
- }
-}
-
-void ParScanThreadState::trim_queues(int max_size) {
- ObjToScanQueue* queue = work_queue();
- do {
- while (queue->size() > (juint)max_size) {
- oop obj_to_scan;
- if (queue->pop_local(obj_to_scan)) {
- if ((HeapWord *)obj_to_scan < young_old_boundary()) {
- if (obj_to_scan->is_objArray() &&
- obj_to_scan->is_forwarded() &&
- obj_to_scan->forwardee() != obj_to_scan) {
- scan_partial_array_and_push_remainder(obj_to_scan);
- } else {
- // object is in to_space
- obj_to_scan->oop_iterate(&_to_space_closure);
- }
- } else {
- // object is in old generation
- obj_to_scan->oop_iterate(&_old_gen_closure);
- }
- }
- }
- // For the case of compressed oops, we have a private, non-shared
- // overflow stack, so we eagerly drain it so as to more evenly
- // distribute load early. Note: this may be good to do in
- // general rather than delay for the final stealing phase.
- // If applicable, we'll transfer a set of objects over to our
- // work queue, allowing them to be stolen and draining our
- // private overflow stack.
- } while (ParGCTrimOverflow && young_gen()->take_from_overflow_list(this));
-}
-
-bool ParScanThreadState::take_from_overflow_stack() {
- assert(ParGCUseLocalOverflow, "Else should not call");
- assert(young_gen()->overflow_list() == NULL, "Error");
- ObjToScanQueue* queue = work_queue();
- Stack<oop, mtGC>* const of_stack = overflow_stack();
- const size_t num_overflow_elems = of_stack->size();
- const size_t space_available = queue->max_elems() - queue->size();
- const size_t num_take_elems = MIN3(space_available / 4,
- (size_t)ParGCDesiredObjsFromOverflowList,
- num_overflow_elems);
- // Transfer the most recent num_take_elems from the overflow
- // stack to our work queue.
- for (size_t i = 0; i != num_take_elems; i++) {
- oop cur = of_stack->pop();
- oop obj_to_push = cur->forwardee();
- assert(CMSHeap::heap()->is_in_reserved(cur), "Should be in heap");
- assert(!old_gen()->is_in_reserved(cur), "Should be in young gen");
- assert(CMSHeap::heap()->is_in_reserved(obj_to_push), "Should be in heap");
- if (should_be_partially_scanned(obj_to_push, cur)) {
- assert(arrayOop(cur)->length() == 0, "entire array remaining to be scanned");
- obj_to_push = cur;
- }
- bool ok = queue->push(obj_to_push);
- assert(ok, "Should have succeeded");
- }
- assert(young_gen()->overflow_list() == NULL, "Error");
- return num_take_elems > 0; // was something transferred?
-}
-
-void ParScanThreadState::push_on_overflow_stack(oop p) {
- assert(ParGCUseLocalOverflow, "Else should not call");
- overflow_stack()->push(p);
- assert(young_gen()->overflow_list() == NULL, "Error");
-}
-
-HeapWord* ParScanThreadState::alloc_in_to_space_slow(size_t word_sz) {
- // If the object is small enough, try to reallocate the buffer.
- HeapWord* obj = NULL;
- if (!_to_space_full) {
- PLAB* const plab = to_space_alloc_buffer();
- Space* const sp = to_space();
- if (word_sz * 100 < ParallelGCBufferWastePct * plab->word_sz()) {
- // Is small enough; abandon this buffer and start a new one.
- plab->retire();
- // The minimum size has to be twice SurvivorAlignmentInBytes to
- // allow for padding used in the alignment of 1 word. A padding
- // of 1 is too small for a filler word so the padding size will
- // be increased by SurvivorAlignmentInBytes.
- size_t min_usable_size = 2 * static_cast<size_t>(SurvivorAlignmentInBytes >> LogHeapWordSize);
- size_t buf_size = MAX2(plab->word_sz(), min_usable_size);
- HeapWord* buf_space = sp->par_allocate(buf_size);
- if (buf_space == NULL) {
- const size_t min_bytes = MAX2(PLAB::min_size(), min_usable_size) << LogHeapWordSize;
- size_t free_bytes = sp->free();
- while(buf_space == NULL && free_bytes >= min_bytes) {
- buf_size = free_bytes >> LogHeapWordSize;
- assert(buf_size == (size_t)align_object_size(buf_size), "Invariant");
- buf_space = sp->par_allocate(buf_size);
- free_bytes = sp->free();
- }
- }
- if (buf_space != NULL) {
- plab->set_buf(buf_space, buf_size);
- record_survivor_plab(buf_space, buf_size);
- obj = plab->allocate_aligned(word_sz, SurvivorAlignmentInBytes);
- // Note that we cannot compare buf_size < word_sz below
- // because of AlignmentReserve (see PLAB::allocate()).
- assert(obj != NULL || plab->words_remaining() < word_sz,
- "Else should have been able to allocate requested object size "
- SIZE_FORMAT ", PLAB size " SIZE_FORMAT ", SurvivorAlignmentInBytes "
- SIZE_FORMAT ", words_remaining " SIZE_FORMAT,
- word_sz, buf_size, SurvivorAlignmentInBytes, plab->words_remaining());
- // It's conceivable that we may be able to use the
- // buffer we just grabbed for subsequent small requests
- // even if not for this one.
- } else {
- // We're used up.
- _to_space_full = true;
- }
- } else {
- // Too large; allocate the object individually.
- obj = sp->par_allocate(word_sz);
- }
- }
- return obj;
-}
-
-void ParScanThreadState::undo_alloc_in_to_space(HeapWord* obj, size_t word_sz) {
- to_space_alloc_buffer()->undo_allocation(obj, word_sz);
-}
-
-void ParScanThreadState::print_promotion_failure_size() {
- if (_promotion_failed_info.has_failed()) {
- log_trace(gc, promotion)(" (%d: promotion failure size = " SIZE_FORMAT ") ",
- _thread_num, _promotion_failed_info.first_size());
- }
-}
-
-class ParScanThreadStateSet: StackObj {
-public:
- // Initializes states for the specified number of threads;
- ParScanThreadStateSet(int num_threads,
- Space& to_space,
- ParNewGeneration& young_gen,
- Generation& old_gen,
- ObjToScanQueueSet& queue_set,
- Stack<oop, mtGC>* overflow_stacks_,
- PreservedMarksSet& preserved_marks_set,
- size_t desired_plab_sz,
- TaskTerminator& term);
-
- ~ParScanThreadStateSet() { TASKQUEUE_STATS_ONLY(reset_stats()); }
-
- inline ParScanThreadState& thread_state(int i);
-
- void trace_promotion_failed(const YoungGCTracer* gc_tracer);
- void reset(uint active_workers, bool promotion_failed);
- void flush();
-
- #if TASKQUEUE_STATS
- static void
- print_termination_stats_hdr(outputStream* const st);
- void print_termination_stats();
- static void
- print_taskqueue_stats_hdr(outputStream* const st);
- void print_taskqueue_stats();
- void reset_stats();
- #endif // TASKQUEUE_STATS
-
-private:
- TaskTerminator& _term;
- ParNewGeneration& _young_gen;
- Generation& _old_gen;
- ParScanThreadState* _per_thread_states;
- const int _num_threads;
- public:
- bool is_valid(int id) const { return id < _num_threads; }
- ParallelTaskTerminator* terminator() { return _term.terminator(); }
-};
-
-ParScanThreadStateSet::ParScanThreadStateSet(int num_threads,
- Space& to_space,
- ParNewGeneration& young_gen,
- Generation& old_gen,
- ObjToScanQueueSet& queue_set,
- Stack<oop, mtGC>* overflow_stacks,
- PreservedMarksSet& preserved_marks_set,
- size_t desired_plab_sz,
- TaskTerminator& term)
- : _term(term),
- _young_gen(young_gen),
- _old_gen(old_gen),
- _per_thread_states(NEW_RESOURCE_ARRAY(ParScanThreadState, num_threads)),
- _num_threads(num_threads)
-{
- assert(num_threads > 0, "sanity check!");
- assert(ParGCUseLocalOverflow == (overflow_stacks != NULL),
- "overflow_stack allocation mismatch");
- // Initialize states.
- for (int i = 0; i < num_threads; ++i) {
- new(_per_thread_states + i)
- ParScanThreadState(&to_space, &young_gen, &old_gen, i, &queue_set,
- overflow_stacks, preserved_marks_set.get(i),
- desired_plab_sz, term);
- }
-}
-
-inline ParScanThreadState& ParScanThreadStateSet::thread_state(int i) {
- assert(i >= 0 && i < _num_threads, "sanity check!");
- return _per_thread_states[i];
-}
-
-void ParScanThreadStateSet::trace_promotion_failed(const YoungGCTracer* gc_tracer) {
- for (int i = 0; i < _num_threads; ++i) {
- if (thread_state(i).promotion_failed()) {
- gc_tracer->report_promotion_failed(thread_state(i).promotion_failed_info());
- thread_state(i).promotion_failed_info().reset();
- }
- }
-}
-
-void ParScanThreadStateSet::reset(uint active_threads, bool promotion_failed) {
- _term.terminator()->reset_for_reuse(active_threads);
- if (promotion_failed) {
- for (int i = 0; i < _num_threads; ++i) {
- thread_state(i).print_promotion_failure_size();
- }
- }
-}
-
-#if TASKQUEUE_STATS
-void ParScanThreadState::reset_stats() {
- taskqueue_stats().reset();
- _term_attempts = 0;
- _overflow_refills = 0;
- _overflow_refill_objs = 0;
-}
-
-void ParScanThreadStateSet::reset_stats() {
- for (int i = 0; i < _num_threads; ++i) {
- thread_state(i).reset_stats();
- }
-}
-
-void ParScanThreadStateSet::print_termination_stats_hdr(outputStream* const st) {
- st->print_raw_cr("GC Termination Stats");
- st->print_raw_cr(" elapsed --strong roots-- -------termination-------");
- st->print_raw_cr("thr ms ms % ms % attempts");
- st->print_raw_cr("--- --------- --------- ------ --------- ------ --------");
-}
-
-void ParScanThreadStateSet::print_termination_stats() {
- Log(gc, task, stats) log;
- if (!log.is_debug()) {
- return;
- }
-
- ResourceMark rm;
- LogStream ls(log.debug());
- outputStream* st = &ls;
-
- print_termination_stats_hdr(st);
-
- for (int i = 0; i < _num_threads; ++i) {
- const ParScanThreadState & pss = thread_state(i);
- const double elapsed_ms = pss.elapsed_time() * 1000.0;
- const double s_roots_ms = pss.strong_roots_time() * 1000.0;
- const double term_ms = pss.term_time() * 1000.0;
- st->print_cr("%3d %9.2f %9.2f %6.2f %9.2f %6.2f " SIZE_FORMAT_W(8),
- i, elapsed_ms, s_roots_ms, s_roots_ms * 100 / elapsed_ms,
- term_ms, term_ms * 100 / elapsed_ms, pss.term_attempts());
- }
-}
-
-// Print stats related to work queue activity.
-void ParScanThreadStateSet::print_taskqueue_stats_hdr(outputStream* const st) {
- st->print_raw_cr("GC Task Stats");
- st->print_raw("thr "); TaskQueueStats::print_header(1, st); st->cr();
- st->print_raw("--- "); TaskQueueStats::print_header(2, st); st->cr();
-}
-
-void ParScanThreadStateSet::print_taskqueue_stats() {
- if (!log_is_enabled(Trace, gc, task, stats)) {
- return;
- }
- Log(gc, task, stats) log;
- ResourceMark rm;
- LogStream ls(log.trace());
- outputStream* st = &ls;
- print_taskqueue_stats_hdr(st);
-
- TaskQueueStats totals;
- for (int i = 0; i < _num_threads; ++i) {
- const ParScanThreadState & pss = thread_state(i);
- const TaskQueueStats & stats = pss.taskqueue_stats();
- st->print("%3d ", i); stats.print(st); st->cr();
- totals += stats;
-
- if (pss.overflow_refills() > 0) {
- st->print_cr(" " SIZE_FORMAT_W(10) " overflow refills "
- SIZE_FORMAT_W(10) " overflow objects",
- pss.overflow_refills(), pss.overflow_refill_objs());
- }
- }
- st->print("tot "); totals.print(st); st->cr();
-
- DEBUG_ONLY(totals.verify());
-}
-#endif // TASKQUEUE_STATS
-
-void ParScanThreadStateSet::flush() {
- // Work in this loop should be kept as lightweight as
- // possible since this might otherwise become a bottleneck
- // to scaling. Should we add heavy-weight work into this
- // loop, consider parallelizing the loop into the worker threads.
- for (int i = 0; i < _num_threads; ++i) {
- ParScanThreadState& par_scan_state = thread_state(i);
-
- // Flush stats related to To-space PLAB activity and
- // retire the last buffer.
- par_scan_state.to_space_alloc_buffer()->flush_and_retire_stats(_young_gen.plab_stats());
-
- // Every thread has its own age table. We need to merge
- // them all into one.
- AgeTable *local_table = par_scan_state.age_table();
- _young_gen.age_table()->merge(local_table);
-
- // Inform old gen that we're done.
- _old_gen.par_promote_alloc_done(i);
- }
-
- if (UseConcMarkSweepGC) {
- // We need to call this even when ResizeOldPLAB is disabled
- // so as to avoid breaking some asserts. While we may be able
- // to avoid this by reorganizing the code a bit, I am loathe
- // to do that unless we find cases where ergo leads to bad
- // performance.
- CompactibleFreeListSpaceLAB::compute_desired_plab_size();
- }
-}
-
-ParScanClosure::ParScanClosure(ParNewGeneration* g,
- ParScanThreadState* par_scan_state) :
- OopsInClassLoaderDataOrGenClosure(g), _par_scan_state(par_scan_state), _g(g) {
- _boundary = _g->reserved().end();
-}
-
-void ParRootScanWithBarrierTwoGensClosure::do_oop(oop* p) { ParScanClosure::do_oop_work(p, true, true); }
-void ParRootScanWithBarrierTwoGensClosure::do_oop(narrowOop* p) { ParScanClosure::do_oop_work(p, true, true); }
-
-void ParRootScanWithoutBarrierClosure::do_oop(oop* p) { ParScanClosure::do_oop_work(p, false, true); }
-void ParRootScanWithoutBarrierClosure::do_oop(narrowOop* p) { ParScanClosure::do_oop_work(p, false, true); }
-
-ParScanWeakRefClosure::ParScanWeakRefClosure(ParNewGeneration* g,
- ParScanThreadState* par_scan_state)
- : ScanWeakRefClosure(g), _par_scan_state(par_scan_state)
-{}
-
-#ifdef WIN32
-#pragma warning(disable: 4786) /* identifier was truncated to '255' characters in the browser information */
-#endif
-
-ParEvacuateFollowersClosure::ParEvacuateFollowersClosure(
- ParScanThreadState* par_scan_state_,
- ParScanWithoutBarrierClosure* to_space_closure_,
- ParScanWithBarrierClosure* old_gen_closure_,
- ParRootScanWithoutBarrierClosure* to_space_root_closure_,
- ParNewGeneration* par_gen_,
- ParRootScanWithBarrierTwoGensClosure* old_gen_root_closure_,
- ObjToScanQueueSet* task_queues_,
- ParallelTaskTerminator* terminator_) :
-
- _par_scan_state(par_scan_state_),
- _to_space_closure(to_space_closure_),
- _to_space_root_closure(to_space_root_closure_),
- _old_gen_closure(old_gen_closure_),
- _old_gen_root_closure(old_gen_root_closure_),
- _par_gen(par_gen_),
- _task_queues(task_queues_),
- _terminator(terminator_)
-{}
-
-void ParEvacuateFollowersClosure::do_void() {
- ObjToScanQueue* work_q = par_scan_state()->work_queue();
-
- while (true) {
- // Scan to-space and old-gen objs until we run out of both.
- oop obj_to_scan;
- par_scan_state()->trim_queues(0);
-
- // We have no local work, attempt to steal from other threads.
-
- // Attempt to steal work from promoted.
- if (task_queues()->steal(par_scan_state()->thread_num(),
- obj_to_scan)) {
- bool res = work_q->push(obj_to_scan);
- assert(res, "Empty queue should have room for a push.");
-
- // If successful, goto Start.
- continue;
-
- // Try global overflow list.
- } else if (par_gen()->take_from_overflow_list(par_scan_state())) {
- continue;
- }
-
- // Otherwise, offer termination.
- par_scan_state()->start_term_time();
- if (terminator()->offer_termination()) break;
- par_scan_state()->end_term_time();
- }
- assert(par_gen()->_overflow_list == NULL && par_gen()->_num_par_pushes == 0,
- "Broken overflow list?");
- // Finish the last termination pause.
- par_scan_state()->end_term_time();
-}
-
-ParNewGenTask::ParNewGenTask(ParNewGeneration* young_gen,
- Generation* old_gen,
- HeapWord* young_old_boundary,
- ParScanThreadStateSet* state_set,
- StrongRootsScope* strong_roots_scope) :
- AbstractGangTask("ParNewGeneration collection"),
- _young_gen(young_gen), _old_gen(old_gen),
- _young_old_boundary(young_old_boundary),
- _state_set(state_set),
- _strong_roots_scope(strong_roots_scope)
-{}
-
-void ParNewGenTask::work(uint worker_id) {
- CMSHeap* heap = CMSHeap::heap();
- // Since this is being done in a separate thread, need new resource
- // and handle marks.
- ResourceMark rm;
- HandleMark hm;
-
- ParScanThreadState& par_scan_state = _state_set->thread_state(worker_id);
- assert(_state_set->is_valid(worker_id), "Should not have been called");
-
- par_scan_state.set_young_old_boundary(_young_old_boundary);
-
- CLDScanClosure cld_scan_closure(&par_scan_state.to_space_root_closure(),
- heap->rem_set()->cld_rem_set()->accumulate_modified_oops());
-
- par_scan_state.start_strong_roots();
- heap->young_process_roots(_strong_roots_scope,
- &par_scan_state.to_space_root_closure(),
- &par_scan_state.older_gen_closure(),
- &cld_scan_closure);
-
- par_scan_state.end_strong_roots();
-
- // "evacuate followers".
- par_scan_state.evacuate_followers_closure().do_void();
-
- // This will collapse this worker's promoted object list that's
- // created during the main ParNew parallel phase of ParNew. This has
- // to be called after all workers have finished promoting objects
- // and scanning promoted objects. It should be safe calling it from
- // here, given that we can only reach here after all thread have
- // offered termination, i.e., after there is no more work to be
- // done. It will also disable promotion tracking for the rest of
- // this GC as it's not necessary to be on during reference processing.
- _old_gen->par_oop_since_save_marks_iterate_done((int) worker_id);
-}
-
-ParNewGeneration::ParNewGeneration(ReservedSpace rs,
- size_t initial_byte_size,
- size_t min_byte_size,
- size_t max_byte_size)
- : DefNewGeneration(rs, initial_byte_size, min_byte_size, max_byte_size, "CMS young collection pauses"),
- _plab_stats("Young", YoungPLABSize, PLABWeight),
- _overflow_list(NULL),
- _is_alive_closure(this)
-{
- NOT_PRODUCT(_overflow_counter = ParGCWorkQueueOverflowInterval;)
- NOT_PRODUCT(_num_par_pushes = 0;)
- _task_queues = new ObjToScanQueueSet(ParallelGCThreads);
- guarantee(_task_queues != NULL, "task_queues allocation failure.");
-
- for (uint i = 0; i < ParallelGCThreads; i++) {
- ObjToScanQueue *q = new ObjToScanQueue();
- guarantee(q != NULL, "work_queue Allocation failure.");
- _task_queues->register_queue(i, q);
- }
-
- for (uint i = 0; i < ParallelGCThreads; i++) {
- _task_queues->queue(i)->initialize();
- }
-
- _overflow_stacks = NULL;
- if (ParGCUseLocalOverflow) {
- // typedef to workaround NEW_C_HEAP_ARRAY macro, which can not deal with ','
- typedef Stack<oop, mtGC> GCOopStack;
-
- _overflow_stacks = NEW_C_HEAP_ARRAY(GCOopStack, ParallelGCThreads, mtGC);
- for (size_t i = 0; i < ParallelGCThreads; ++i) {
- new (_overflow_stacks + i) Stack<oop, mtGC>();
- }
- }
-
- if (UsePerfData) {
- EXCEPTION_MARK;
- ResourceMark rm;
-
- const char* cname =
- PerfDataManager::counter_name(_gen_counters->name_space(), "threads");
- PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_None,
- ParallelGCThreads, CHECK);
- }
-}
-
-// ParNewGeneration::
-ParKeepAliveClosure::ParKeepAliveClosure(ParScanWeakRefClosure* cl) :
- DefNewGeneration::KeepAliveClosure(cl), _par_cl(cl) {}
-
-template <class T>
-void /*ParNewGeneration::*/ParKeepAliveClosure::do_oop_work(T* p) {
-#ifdef ASSERT
- {
- oop obj = RawAccess<IS_NOT_NULL>::oop_load(p);
- // We never expect to see a null reference being processed
- // as a weak reference.
- assert(oopDesc::is_oop(obj), "expected an oop while scanning weak refs");
- }
-#endif // ASSERT
-
- Devirtualizer::do_oop_no_verify(_par_cl, p);
-
- if (CMSHeap::heap()->is_in_reserved(p)) {
- oop obj = RawAccess<IS_NOT_NULL>::oop_load(p);;
- _rs->write_ref_field_gc_par(p, obj);
- }
-}
-
-void /*ParNewGeneration::*/ParKeepAliveClosure::do_oop(oop* p) { ParKeepAliveClosure::do_oop_work(p); }
-void /*ParNewGeneration::*/ParKeepAliveClosure::do_oop(narrowOop* p) { ParKeepAliveClosure::do_oop_work(p); }
-
-// ParNewGeneration::
-KeepAliveClosure::KeepAliveClosure(ScanWeakRefClosure* cl) :
- DefNewGeneration::KeepAliveClosure(cl) {}
-
-template <class T>
-void /*ParNewGeneration::*/KeepAliveClosure::do_oop_work(T* p) {
-#ifdef ASSERT
- {
- oop obj = RawAccess<IS_NOT_NULL>::oop_load(p);
- // We never expect to see a null reference being processed
- // as a weak reference.
- assert(oopDesc::is_oop(obj), "expected an oop while scanning weak refs");
- }
-#endif // ASSERT
-
- Devirtualizer::do_oop_no_verify(_cl, p);
-
- if (CMSHeap::heap()->is_in_reserved(p)) {
- oop obj = RawAccess<IS_NOT_NULL>::oop_load(p);
- _rs->write_ref_field_gc_par(p, obj);
- }
-}
-
-void /*ParNewGeneration::*/KeepAliveClosure::do_oop(oop* p) { KeepAliveClosure::do_oop_work(p); }
-void /*ParNewGeneration::*/KeepAliveClosure::do_oop(narrowOop* p) { KeepAliveClosure::do_oop_work(p); }
-
-template <class T> void ScanClosureWithParBarrier::do_oop_work(T* p) {
- T heap_oop = RawAccess<>::oop_load(p);
- if (!CompressedOops::is_null(heap_oop)) {
- oop obj = CompressedOops::decode_not_null(heap_oop);
- if ((HeapWord*)obj < _boundary) {
- assert(!_g->to()->is_in_reserved(obj), "Scanning field twice?");
- oop new_obj = obj->is_forwarded()
- ? obj->forwardee()
- : _g->DefNewGeneration::copy_to_survivor_space(obj);
- RawAccess<IS_NOT_NULL>::oop_store(p, new_obj);
- }
- if (_gc_barrier) {
- // If p points to a younger generation, mark the card.
- if ((HeapWord*)obj < _gen_boundary) {
- _rs->write_ref_field_gc_par(p, obj);
- }
- }
- }
-}
-
-void ScanClosureWithParBarrier::do_oop(oop* p) { ScanClosureWithParBarrier::do_oop_work(p); }
-void ScanClosureWithParBarrier::do_oop(narrowOop* p) { ScanClosureWithParBarrier::do_oop_work(p); }
-
-class ParNewRefProcTaskProxy: public AbstractGangTask {
- typedef AbstractRefProcTaskExecutor::ProcessTask ProcessTask;
-public:
- ParNewRefProcTaskProxy(ProcessTask& task,
- ParNewGeneration& young_gen,
- Generation& old_gen,
- HeapWord* young_old_boundary,
- ParScanThreadStateSet& state_set);
-
-private:
- virtual void work(uint worker_id);
-private:
- ParNewGeneration& _young_gen;
- ProcessTask& _task;
- Generation& _old_gen;
- HeapWord* _young_old_boundary;
- ParScanThreadStateSet& _state_set;
-};
-
-ParNewRefProcTaskProxy::ParNewRefProcTaskProxy(ProcessTask& task,
- ParNewGeneration& young_gen,
- Generation& old_gen,
- HeapWord* young_old_boundary,
- ParScanThreadStateSet& state_set)
- : AbstractGangTask("ParNewGeneration parallel reference processing"),
- _young_gen(young_gen),
- _task(task),
- _old_gen(old_gen),
- _young_old_boundary(young_old_boundary),
- _state_set(state_set)
-{ }
-
-void ParNewRefProcTaskProxy::work(uint worker_id) {
- ResourceMark rm;
- HandleMark hm;
- ParScanThreadState& par_scan_state = _state_set.thread_state(worker_id);
- par_scan_state.set_young_old_boundary(_young_old_boundary);
- _task.work(worker_id, par_scan_state.is_alive_closure(),
- par_scan_state.keep_alive_closure(),
- par_scan_state.evacuate_followers_closure());
-}
-
-void ParNewRefProcTaskExecutor::execute(ProcessTask& task, uint ergo_workers) {
- CMSHeap* gch = CMSHeap::heap();
- WorkGang* workers = gch->workers();
- assert(workers != NULL, "Need parallel worker threads.");
- assert(workers->active_workers() == ergo_workers,
- "Ergonomically chosen workers (%u) must be equal to active workers (%u)",
- ergo_workers, workers->active_workers());
- _state_set.reset(workers->active_workers(), _young_gen.promotion_failed());
- ParNewRefProcTaskProxy rp_task(task, _young_gen, _old_gen,
- _young_gen.reserved().end(), _state_set);
- workers->run_task(&rp_task, workers->active_workers());
- _state_set.reset(0 /* bad value in debug if not reset */,
- _young_gen.promotion_failed());
-}
-
-void ParNewRefProcTaskExecutor::set_single_threaded_mode() {
- _state_set.flush();
- CMSHeap* heap = CMSHeap::heap();
- heap->save_marks();
-}
-
-ScanClosureWithParBarrier::
-ScanClosureWithParBarrier(ParNewGeneration* g, bool gc_barrier) :
- OopsInClassLoaderDataOrGenClosure(g), _g(g), _boundary(g->reserved().end()), _gc_barrier(gc_barrier)
-{ }
-
-template <typename OopClosureType1, typename OopClosureType2>
-EvacuateFollowersClosureGeneral<OopClosureType1, OopClosureType2>::
-EvacuateFollowersClosureGeneral(CMSHeap* heap,
- OopClosureType1* cur,
- OopClosureType2* older) :
- _heap(heap),
- _scan_cur_or_nonheap(cur), _scan_older(older)
-{ }
-
-template <typename OopClosureType1, typename OopClosureType2>
-void EvacuateFollowersClosureGeneral<OopClosureType1, OopClosureType2>::do_void() {
- do {
- _heap->oop_since_save_marks_iterate(_scan_cur_or_nonheap,
- _scan_older);
- } while (!_heap->no_allocs_since_save_marks());
-}
-
-// A Generation that does parallel young-gen collection.
-
-void ParNewGeneration::handle_promotion_failed(CMSHeap* gch, ParScanThreadStateSet& thread_state_set) {
- assert(_promo_failure_scan_stack.is_empty(), "post condition");
- _promo_failure_scan_stack.clear(true); // Clear cached segments.
-
- remove_forwarding_pointers();
- log_info(gc, promotion)("Promotion failed");
- // All the spaces are in play for mark-sweep.
- swap_spaces(); // Make life simpler for CMS || rescan; see 6483690.
- from()->set_next_compaction_space(to());
- gch->set_incremental_collection_failed();
- // Inform the next generation that a promotion failure occurred.
- _old_gen->promotion_failure_occurred();
-
- // Trace promotion failure in the parallel GC threads
- thread_state_set.trace_promotion_failed(gc_tracer());
- // Single threaded code may have reported promotion failure to the global state
- if (_promotion_failed_info.has_failed()) {
- _gc_tracer.report_promotion_failed(_promotion_failed_info);
- }
- // Reset the PromotionFailureALot counters.
- NOT_PRODUCT(gch->reset_promotion_should_fail();)
-}
-
-void ParNewGeneration::collect(bool full,
- bool clear_all_soft_refs,
- size_t size,
- bool is_tlab) {
- assert(full || size > 0, "otherwise we don't want to collect");
-
- CMSHeap* gch = CMSHeap::heap();
-
- _gc_timer->register_gc_start();
-
- AdaptiveSizePolicy* size_policy = gch->size_policy();
- WorkGang* workers = gch->workers();
- assert(workers != NULL, "Need workgang for parallel work");
- uint active_workers =
- WorkerPolicy::calc_active_workers(workers->total_workers(),
- workers->active_workers(),
- Threads::number_of_non_daemon_threads());
- active_workers = workers->update_active_workers(active_workers);
- log_info(gc,task)("Using %u workers of %u for evacuation", active_workers, workers->total_workers());
-
- _old_gen = gch->old_gen();
-
- // If the next generation is too full to accommodate worst-case promotion
- // from this generation, pass on collection; let the next generation
- // do it.
- if (!collection_attempt_is_safe()) {
- gch->set_incremental_collection_failed(); // slight lie, in that we did not even attempt one
- return;
- }
- assert(to()->is_empty(), "Else not collection_attempt_is_safe");
-
- _gc_tracer.report_gc_start(gch->gc_cause(), _gc_timer->gc_start());
- gch->trace_heap_before_gc(gc_tracer());
-
- init_assuming_no_promotion_failure();
-
- GCTraceTime(Trace, gc, phases) t1("ParNew", NULL, gch->gc_cause());
-
- age_table()->clear();
- to()->clear(SpaceDecorator::Mangle);
-
- gch->save_marks();
-
- // Set the correct parallelism (number of queues) in the reference processor
- ref_processor()->set_active_mt_degree(active_workers);
-
- // Need to initialize the preserved marks before the ThreadStateSet c'tor.
- _preserved_marks_set.init(active_workers);
-
- // Always set the terminator for the active number of workers
- // because only those workers go through the termination protocol.
- TaskTerminator _term(active_workers, task_queues());
- ParScanThreadStateSet thread_state_set(active_workers,
- *to(), *this, *_old_gen, *task_queues(),
- _overflow_stacks, _preserved_marks_set,
- desired_plab_sz(), _term);
-
- thread_state_set.reset(active_workers, promotion_failed());
-
- {
- StrongRootsScope srs(active_workers);
-
- ParNewGenTask tsk(this, _old_gen, reserved().end(), &thread_state_set, &srs);
- gch->rem_set()->prepare_for_younger_refs_iterate(true);
- // It turns out that even when we're using 1 thread, doing the work in a
- // separate thread causes wide variance in run times. We can't help this
- // in the multi-threaded case, but we special-case n=1 here to get
- // repeatable measurements of the 1-thread overhead of the parallel code.
- // Might multiple workers ever be used? If yes, initialization
- // has been done such that the single threaded path should not be used.
- if (workers->total_workers() > 1) {
- workers->run_task(&tsk);
- } else {
- tsk.work(0);
- }
- }
-
- thread_state_set.reset(0 /* Bad value in debug if not reset */,
- promotion_failed());
-
- // Trace and reset failed promotion info.
- if (promotion_failed()) {
- thread_state_set.trace_promotion_failed(gc_tracer());
- }
-
- // Process (weak) reference objects found during scavenge.
- ReferenceProcessor* rp = ref_processor();
- IsAliveClosure is_alive(this);
- ScanWeakRefClosure scan_weak_ref(this);
- KeepAliveClosure keep_alive(&scan_weak_ref);
- ScanClosure scan_without_gc_barrier(this, false);
- ScanClosureWithParBarrier scan_with_gc_barrier(this, true);
- set_promo_failure_scan_stack_closure(&scan_without_gc_barrier);
- EvacuateFollowersClosureGeneral<ScanClosure, ScanClosureWithParBarrier> evacuate_followers(
- gch, &scan_without_gc_barrier, &scan_with_gc_barrier);
- rp->setup_policy(clear_all_soft_refs);
- // Can the mt_degree be set later (at run_task() time would be best)?
- rp->set_active_mt_degree(active_workers);
- ReferenceProcessorStats stats;
- ReferenceProcessorPhaseTimes pt(_gc_timer, rp->max_num_queues());
- if (rp->processing_is_mt()) {
- ParNewRefProcTaskExecutor task_executor(*this, *_old_gen, thread_state_set);
- stats = rp->process_discovered_references(&is_alive, &keep_alive,
- &evacuate_followers, &task_executor,
- &pt);
- } else {
- thread_state_set.flush();
- gch->save_marks();
- stats = rp->process_discovered_references(&is_alive, &keep_alive,
- &evacuate_followers, NULL,
- &pt);
- }
- _gc_tracer.report_gc_reference_stats(stats);
- _gc_tracer.report_tenuring_threshold(tenuring_threshold());
- pt.print_all_references();
-
- assert(gch->no_allocs_since_save_marks(), "evacuation should be done at this point");
-
- WeakProcessor::weak_oops_do(&is_alive, &keep_alive);
-
- // Verify that the usage of keep_alive only forwarded
- // the oops and did not find anything new to copy.
- assert(gch->no_allocs_since_save_marks(), "unexpectedly copied objects");
-
- if (!promotion_failed()) {
- // Swap the survivor spaces.
- eden()->clear(SpaceDecorator::Mangle);
- from()->clear(SpaceDecorator::Mangle);
- if (ZapUnusedHeapArea) {
- // This is now done here because of the piece-meal mangling which
- // can check for valid mangling at intermediate points in the
- // collection(s). When a young collection fails to collect
- // sufficient space resizing of the young generation can occur
- // and redistribute the spaces in the young generation. Mangle
- // here so that unzapped regions don't get distributed to
- // other spaces.
- to()->mangle_unused_area();
- }
- swap_spaces();
-
- // A successful scavenge should restart the GC time limit count which is
- // for full GC's.
- size_policy->reset_gc_overhead_limit_count();
-
- assert(to()->is_empty(), "to space should be empty now");
-
- adjust_desired_tenuring_threshold();
- } else {
- handle_promotion_failed(gch, thread_state_set);
- }
- _preserved_marks_set.reclaim();
- // set new iteration safe limit for the survivor spaces
- from()->set_concurrent_iteration_safe_limit(from()->top());
- to()->set_concurrent_iteration_safe_limit(to()->top());
-
- plab_stats()->adjust_desired_plab_sz();
-
- TASKQUEUE_STATS_ONLY(thread_state_set.print_termination_stats());
- TASKQUEUE_STATS_ONLY(thread_state_set.print_taskqueue_stats());
-
- // We need to use a monotonically non-decreasing time in ms
- // or we will see time-warp warnings and os::javaTimeMillis()
- // does not guarantee monotonicity.
- jlong now = os::javaTimeNanos() / NANOSECS_PER_MILLISEC;
- update_time_of_last_gc(now);
-
- rp->set_enqueuing_is_done(true);
- rp->verify_no_references_recorded();
-
- gch->trace_heap_after_gc(gc_tracer());
-
- _gc_timer->register_gc_end();
-
- _gc_tracer.report_gc_end(_gc_timer->gc_end(), _gc_timer->time_partitions());
-}
-
-size_t ParNewGeneration::desired_plab_sz() {
- return _plab_stats.desired_plab_sz(CMSHeap::heap()->workers()->active_workers());
-}
-
-static int sum;
-void ParNewGeneration::waste_some_time() {
- for (int i = 0; i < 100; i++) {
- sum += i;
- }
-}
-
-static const oop ClaimedForwardPtr = cast_to_oop<intptr_t>(0x4);
-
-// Because of concurrency, there are times where an object for which
-// "is_forwarded()" is true contains an "interim" forwarding pointer
-// value. Such a value will soon be overwritten with a real value.
-// This method requires "obj" to have a forwarding pointer, and waits, if
-// necessary for a real one to be inserted, and returns it.
-
-oop ParNewGeneration::real_forwardee(oop obj) {
- oop forward_ptr = obj->forwardee();
- if (forward_ptr != ClaimedForwardPtr) {
- return forward_ptr;
- } else {
- return real_forwardee_slow(obj);
- }
-}
-
-oop ParNewGeneration::real_forwardee_slow(oop obj) {
- // Spin-read if it is claimed but not yet written by another thread.
- oop forward_ptr = obj->forwardee();
- while (forward_ptr == ClaimedForwardPtr) {
- waste_some_time();
- assert(obj->is_forwarded(), "precondition");
- forward_ptr = obj->forwardee();
- }
- return forward_ptr;
-}
-
-// Multiple GC threads may try to promote an object. If the object
-// is successfully promoted, a forwarding pointer will be installed in
-// the object in the young generation. This method claims the right
-// to install the forwarding pointer before it copies the object,
-// thus avoiding the need to undo the copy as in
-// copy_to_survivor_space_avoiding_with_undo.
-
-oop ParNewGeneration::copy_to_survivor_space(ParScanThreadState* par_scan_state,
- oop old,
- size_t sz,
- markWord m) {
- // In the sequential version, this assert also says that the object is
- // not forwarded. That might not be the case here. It is the case that
- // the caller observed it to be not forwarded at some time in the past.
- assert(is_in_reserved(old), "shouldn't be scavenging this oop");
-
- // The sequential code read "old->age()" below. That doesn't work here,
- // since the age is in the mark word, and that might be overwritten with
- // a forwarding pointer by a parallel thread. So we must save the mark
- // word in a local and then analyze it.
- oopDesc dummyOld;
- dummyOld.set_mark_raw(m);
- assert(!dummyOld.is_forwarded(),
- "should not be called with forwarding pointer mark word.");
-
- oop new_obj = NULL;
- oop forward_ptr;
-
- // Try allocating obj in to-space (unless too old)
- if (dummyOld.age() < tenuring_threshold()) {
- new_obj = (oop)par_scan_state->alloc_in_to_space(sz);
- }
-
- if (new_obj == NULL) {
- // Either to-space is full or we decided to promote try allocating obj tenured
-
- // Attempt to install a null forwarding pointer (atomically),
- // to claim the right to install the real forwarding pointer.
- forward_ptr = old->forward_to_atomic(ClaimedForwardPtr, m);
- if (forward_ptr != NULL) {
- // someone else beat us to it.
- return real_forwardee(old);
- }
-
- if (!_promotion_failed) {
- new_obj = _old_gen->par_promote(par_scan_state->thread_num(),
- old, m, sz);
- }
-
- if (new_obj == NULL) {
- // promotion failed, forward to self
- _promotion_failed = true;
- new_obj = old;
-
- par_scan_state->preserved_marks()->push_if_necessary(old, m);
- par_scan_state->register_promotion_failure(sz);
- }
-
- old->forward_to(new_obj);
- forward_ptr = NULL;
- } else {
- // Is in to-space; do copying ourselves.
- Copy::aligned_disjoint_words((HeapWord*)old, (HeapWord*)new_obj, sz);
- assert(CMSHeap::heap()->is_in_reserved(new_obj), "illegal forwarding pointer value.");
- forward_ptr = old->forward_to_atomic(new_obj, m);
- // Restore the mark word copied above.
- new_obj->set_mark_raw(m);
- // Increment age if obj still in new generation
- new_obj->incr_age();
- par_scan_state->age_table()->add(new_obj, sz);
- }
- assert(new_obj != NULL, "just checking");
-
- // This code must come after the CAS test, or it will print incorrect
- // information.
- log_develop_trace(gc, scavenge)("{%s %s " PTR_FORMAT " -> " PTR_FORMAT " (%d)}",
- is_in_reserved(new_obj) ? "copying" : "tenuring",
- new_obj->klass()->internal_name(), p2i(old), p2i(new_obj), new_obj->size());
-
- if (forward_ptr == NULL) {
- oop obj_to_push = new_obj;
- if (par_scan_state->should_be_partially_scanned(obj_to_push, old)) {
- // Length field used as index of next element to be scanned.
- // Real length can be obtained from real_forwardee()
- arrayOop(old)->set_length(0);
- obj_to_push = old;
- assert(obj_to_push->is_forwarded() && obj_to_push->forwardee() != obj_to_push,
- "push forwarded object");
- }
- // Push it on one of the queues of to-be-scanned objects.
- bool simulate_overflow = false;
- NOT_PRODUCT(
- if (ParGCWorkQueueOverflowALot && should_simulate_overflow()) {
- // simulate a stack overflow
- simulate_overflow = true;
- }
- )
- if (simulate_overflow || !par_scan_state->work_queue()->push(obj_to_push)) {
- // Add stats for overflow pushes.
- log_develop_trace(gc)("Queue Overflow");
- push_on_overflow_list(old, par_scan_state);
- TASKQUEUE_STATS_ONLY(par_scan_state->taskqueue_stats().record_overflow(0));
- }
-
- return new_obj;
- }
-
- // Oops. Someone beat us to it. Undo the allocation. Where did we
- // allocate it?
- if (is_in_reserved(new_obj)) {
- // Must be in to_space.
- assert(to()->is_in_reserved(new_obj), "Checking");
- if (forward_ptr == ClaimedForwardPtr) {
- // Wait to get the real forwarding pointer value.
- forward_ptr = real_forwardee(old);
- }
- par_scan_state->undo_alloc_in_to_space((HeapWord*)new_obj, sz);
- }
-
- return forward_ptr;
-}
-
-#ifndef PRODUCT
-// It's OK to call this multi-threaded; the worst thing
-// that can happen is that we'll get a bunch of closely
-// spaced simulated overflows, but that's OK, in fact
-// probably good as it would exercise the overflow code
-// under contention.
-bool ParNewGeneration::should_simulate_overflow() {
- if (_overflow_counter-- <= 0) { // just being defensive
- _overflow_counter = ParGCWorkQueueOverflowInterval;
- return true;
- } else {
- return false;
- }
-}
-#endif
-
-// In case we are using compressed oops, we need to be careful.
-// If the object being pushed is an object array, then its length
-// field keeps track of the "grey boundary" at which the next
-// incremental scan will be done (see ParGCArrayScanChunk).
-// When using compressed oops, this length field is kept in the
-// lower 32 bits of the erstwhile klass word and cannot be used
-// for the overflow chaining pointer (OCP below). As such the OCP
-// would itself need to be compressed into the top 32-bits in this
-// case. Unfortunately, see below, in the event that we have a
-// promotion failure, the node to be pushed on the list can be
-// outside of the Java heap, so the heap-based pointer compression
-// would not work (we would have potential aliasing between C-heap
-// and Java-heap pointers). For this reason, when using compressed
-// oops, we simply use a worker-thread-local, non-shared overflow
-// list in the form of a growable array, with a slightly different
-// overflow stack draining strategy. If/when we start using fat
-// stacks here, we can go back to using (fat) pointer chains
-// (although some performance comparisons would be useful since
-// single global lists have their own performance disadvantages
-// as we were made painfully aware not long ago, see 6786503).
-#define BUSY (cast_to_oop<intptr_t>(0x1aff1aff))
-void ParNewGeneration::push_on_overflow_list(oop from_space_obj, ParScanThreadState* par_scan_state) {
- assert(is_in_reserved(from_space_obj), "Should be from this generation");
- if (ParGCUseLocalOverflow) {
- // In the case of compressed oops, we use a private, not-shared
- // overflow stack.
- par_scan_state->push_on_overflow_stack(from_space_obj);
- } else {
- assert(!UseCompressedOops, "Error");
- // if the object has been forwarded to itself, then we cannot
- // use the klass pointer for the linked list. Instead we have
- // to allocate an oopDesc in the C-Heap and use that for the linked list.
- // XXX This is horribly inefficient when a promotion failure occurs
- // and should be fixed. XXX FIX ME !!!
-#ifndef PRODUCT
- Atomic::inc(&_num_par_pushes);
- assert(_num_par_pushes > 0, "Tautology");
-#endif
- if (from_space_obj->forwardee() == from_space_obj) {
- oopDesc* listhead = NEW_C_HEAP_OBJ(oopDesc, mtGC);
- listhead->forward_to(from_space_obj);
- from_space_obj = listhead;
- }
- oop observed_overflow_list = _overflow_list;
- oop cur_overflow_list;
- do {
- cur_overflow_list = observed_overflow_list;
- if (cur_overflow_list != BUSY) {
- from_space_obj->set_klass_to_list_ptr(cur_overflow_list);
- } else {
- from_space_obj->set_klass_to_list_ptr(NULL);
- }
- observed_overflow_list =
- Atomic::cmpxchg((oopDesc*)from_space_obj, &_overflow_list, (oopDesc*)cur_overflow_list);
- } while (cur_overflow_list != observed_overflow_list);
- }
-}
-
-bool ParNewGeneration::take_from_overflow_list(ParScanThreadState* par_scan_state) {
- bool res;
-
- if (ParGCUseLocalOverflow) {
- res = par_scan_state->take_from_overflow_stack();
- } else {
- assert(!UseCompressedOops, "Error");
- res = take_from_overflow_list_work(par_scan_state);
- }
- return res;
-}
-
-
-// *NOTE*: The overflow list manipulation code here and
-// in CMSCollector:: are very similar in shape,
-// except that in the CMS case we thread the objects
-// directly into the list via their mark word, and do
-// not need to deal with special cases below related
-// to chunking of object arrays and promotion failure
-// handling.
-// CR 6797058 has been filed to attempt consolidation of
-// the common code.
-// Because of the common code, if you make any changes in
-// the code below, please check the CMS version to see if
-// similar changes might be needed.
-// See CMSCollector::par_take_from_overflow_list() for
-// more extensive documentation comments.
-bool ParNewGeneration::take_from_overflow_list_work(ParScanThreadState* par_scan_state) {
- ObjToScanQueue* work_q = par_scan_state->work_queue();
- // How many to take?
- size_t objsFromOverflow = MIN2((size_t)(work_q->max_elems() - work_q->size())/4,
- (size_t)ParGCDesiredObjsFromOverflowList);
-
- assert(!UseCompressedOops, "Error");
- assert(par_scan_state->overflow_stack() == NULL, "Error");
- if (_overflow_list == NULL) return false;
-
- // Otherwise, there was something there; try claiming the list.
- oop prefix = cast_to_oop(Atomic::xchg((oopDesc*)BUSY, &_overflow_list));
- // Trim off a prefix of at most objsFromOverflow items
- size_t spin_count = ParallelGCThreads;
- size_t sleep_time_millis = MAX2((size_t)1, objsFromOverflow/100);
- for (size_t spin = 0; prefix == BUSY && spin < spin_count; spin++) {
- // someone grabbed it before we did ...
- // ... we spin/block for a short while...
- os::naked_sleep(sleep_time_millis);
- if (_overflow_list == NULL) {
- // nothing left to take
- return false;
- } else if (_overflow_list != BUSY) {
- // try and grab the prefix
- prefix = cast_to_oop(Atomic::xchg((oopDesc*)BUSY, &_overflow_list));
- }
- }
- if (prefix == NULL || prefix == BUSY) {
- // Nothing to take or waited long enough
- if (prefix == NULL) {
- // Write back the NULL in case we overwrote it with BUSY above
- // and it is still the same value.
- (void) Atomic::cmpxchg((oopDesc*)NULL, &_overflow_list, (oopDesc*)BUSY);
- }
- return false;
- }
- assert(prefix != NULL && prefix != BUSY, "Error");
- oop cur = prefix;
- for (size_t i = 1; i < objsFromOverflow; ++i) {
- oop next = cur->list_ptr_from_klass();
- if (next == NULL) break;
- cur = next;
- }
- assert(cur != NULL, "Loop postcondition");
-
- // Reattach remaining (suffix) to overflow list
- oop suffix = cur->list_ptr_from_klass();
- if (suffix == NULL) {
- // Write back the NULL in lieu of the BUSY we wrote
- // above and it is still the same value.
- if (_overflow_list == BUSY) {
- (void) Atomic::cmpxchg((oopDesc*)NULL, &_overflow_list, (oopDesc*)BUSY);
- }
- } else {
- assert(suffix != BUSY, "Error");
- // suffix will be put back on global list
- cur->set_klass_to_list_ptr(NULL); // break off suffix
- // It's possible that the list is still in the empty(busy) state
- // we left it in a short while ago; in that case we may be
- // able to place back the suffix.
- oop observed_overflow_list = _overflow_list;
- oop cur_overflow_list = observed_overflow_list;
- bool attached = false;
- while (observed_overflow_list == BUSY || observed_overflow_list == NULL) {
- observed_overflow_list =
- Atomic::cmpxchg((oopDesc*)suffix, &_overflow_list, (oopDesc*)cur_overflow_list);
- if (cur_overflow_list == observed_overflow_list) {
- attached = true;
- break;
- } else cur_overflow_list = observed_overflow_list;
- }
- if (!attached) {
- // Too bad, someone else got in in between; we'll need to do a splice.
- // Find the last item of suffix list
- oop last = suffix;
- while (true) {
- oop next = last->list_ptr_from_klass();
- if (next == NULL) break;
- last = next;
- }
- // Atomically prepend suffix to current overflow list
- observed_overflow_list = _overflow_list;
- do {
- cur_overflow_list = observed_overflow_list;
- if (cur_overflow_list != BUSY) {
- // Do the splice ...
- last->set_klass_to_list_ptr(cur_overflow_list);
- } else { // cur_overflow_list == BUSY
- last->set_klass_to_list_ptr(NULL);
- }
- observed_overflow_list =
- Atomic::cmpxchg((oopDesc*)suffix, &_overflow_list, (oopDesc*)cur_overflow_list);
- } while (cur_overflow_list != observed_overflow_list);
- }
- }
-
- // Push objects on prefix list onto this thread's work queue
- assert(prefix != NULL && prefix != BUSY, "program logic");
- cur = prefix;
- ssize_t n = 0;
- while (cur != NULL) {
- oop obj_to_push = cur->forwardee();
- oop next = cur->list_ptr_from_klass();
- cur->set_klass(obj_to_push->klass());
- // This may be an array object that is self-forwarded. In that case, the list pointer
- // space, cur, is not in the Java heap, but rather in the C-heap and should be freed.
- if (!is_in_reserved(cur)) {
- // This can become a scaling bottleneck when there is work queue overflow coincident
- // with promotion failure.
- oopDesc* f = cur;
- FREE_C_HEAP_OBJ(f);
- } else if (par_scan_state->should_be_partially_scanned(obj_to_push, cur)) {
- assert(arrayOop(cur)->length() == 0, "entire array remaining to be scanned");
- obj_to_push = cur;
- }
- bool ok = work_q->push(obj_to_push);
- assert(ok, "Should have succeeded");
- cur = next;
- n++;
- }
- TASKQUEUE_STATS_ONLY(par_scan_state->note_overflow_refill(n));
-#ifndef PRODUCT
- assert(_num_par_pushes >= n, "Too many pops?");
- Atomic::sub(n, &_num_par_pushes);
-#endif
- return true;
-}
-#undef BUSY
-
-void ParNewGeneration::ref_processor_init() {
- if (_ref_processor == NULL) {
- // Allocate and initialize a reference processor
- _span_based_discoverer.set_span(_reserved);
- _ref_processor =
- new ReferenceProcessor(&_span_based_discoverer, // span
- ParallelRefProcEnabled && (ParallelGCThreads > 1), // mt processing
- ParallelGCThreads, // mt processing degree
- refs_discovery_is_mt(), // mt discovery
- ParallelGCThreads, // mt discovery degree
- refs_discovery_is_atomic(), // atomic_discovery
- NULL, // is_alive_non_header
- false); // disable adjusting number of processing threads
- }
-}
-
-const char* ParNewGeneration::name() const {
- return "par new generation";
-}
-
-void ParNewGeneration::restore_preserved_marks() {
- SharedRestorePreservedMarksTaskExecutor task_executor(CMSHeap::heap()->workers());
- _preserved_marks_set.restore(&task_executor);
-}
--- a/src/hotspot/share/gc/cms/parNewGeneration.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,420 +0,0 @@
-/*
- * Copyright (c) 2001, 2019, 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_GC_CMS_PARNEWGENERATION_HPP
-#define SHARE_GC_CMS_PARNEWGENERATION_HPP
-
-#include "gc/cms/parOopClosures.hpp"
-#include "gc/serial/defNewGeneration.hpp"
-#include "gc/shared/copyFailedInfo.hpp"
-#include "gc/shared/gcTrace.hpp"
-#include "gc/shared/oopStorageParState.hpp"
-#include "gc/shared/plab.hpp"
-#include "gc/shared/preservedMarks.hpp"
-#include "gc/shared/taskqueue.hpp"
-#include "memory/padded.hpp"
-
-class ChunkArray;
-class CMSHeap;
-class ParScanWithoutBarrierClosure;
-class ParScanWithBarrierClosure;
-class ParRootScanWithoutBarrierClosure;
-class ParRootScanWithBarrierTwoGensClosure;
-class ParEvacuateFollowersClosure;
-class StrongRootsScope;
-
-// It would be better if these types could be kept local to the .cpp file,
-// but they must be here to allow ParScanClosure::do_oop_work to be defined
-// in genOopClosures.inline.hpp.
-
-typedef Padded<OopTaskQueue> ObjToScanQueue;
-typedef GenericTaskQueueSet<ObjToScanQueue, mtGC> ObjToScanQueueSet;
-
-class ParKeepAliveClosure: public DefNewGeneration::KeepAliveClosure {
- private:
- ParScanWeakRefClosure* _par_cl;
- protected:
- template <class T> void do_oop_work(T* p);
- public:
- ParKeepAliveClosure(ParScanWeakRefClosure* cl);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-// The state needed by thread performing parallel young-gen collection.
-class ParScanThreadState {
- friend class ParScanThreadStateSet;
- private:
- ObjToScanQueue *_work_queue;
- Stack<oop, mtGC>* const _overflow_stack;
- PreservedMarks* const _preserved_marks;
-
- PLAB _to_space_alloc_buffer;
-
- ParScanWithoutBarrierClosure _to_space_closure; // scan_without_gc_barrier
- ParScanWithBarrierClosure _old_gen_closure; // scan_with_gc_barrier
- ParRootScanWithoutBarrierClosure _to_space_root_closure; // scan_root_without_gc_barrier
- // Will be passed to process_roots to set its generation.
- ParRootScanWithBarrierTwoGensClosure _older_gen_closure;
- // This closure will always be bound to the old gen; it will be used
- // in evacuate_followers.
- ParRootScanWithBarrierTwoGensClosure _old_gen_root_closure; // scan_old_root_with_gc_barrier
- ParEvacuateFollowersClosure _evacuate_followers;
- DefNewGeneration::IsAliveClosure _is_alive_closure;
- ParScanWeakRefClosure _scan_weak_ref_closure;
- ParKeepAliveClosure _keep_alive_closure;
-
- Space* _to_space;
- Space* to_space() { return _to_space; }
-
- ParNewGeneration* _young_gen;
- ParNewGeneration* young_gen() const { return _young_gen; }
-
- Generation* _old_gen;
- Generation* old_gen() { return _old_gen; }
-
- HeapWord *_young_old_boundary;
-
- int _thread_num;
- AgeTable _ageTable;
-
- bool _to_space_full;
-
-#if TASKQUEUE_STATS
- size_t _term_attempts;
- size_t _overflow_refills;
- size_t _overflow_refill_objs;
-#endif // TASKQUEUE_STATS
-
- // Stats for promotion failure
- PromotionFailedInfo _promotion_failed_info;
-
- // Timing numbers.
- double _start;
- double _start_strong_roots;
- double _strong_roots_time;
- double _start_term;
- double _term_time;
-
- // Helper for trim_queues. Scans subset of an array and makes
- // remainder available for work stealing.
- void scan_partial_array_and_push_remainder(oop obj);
-
- // In support of CMS' parallel rescan of survivor space.
- ChunkArray* _survivor_chunk_array;
- ChunkArray* survivor_chunk_array() { return _survivor_chunk_array; }
-
- void record_survivor_plab(HeapWord* plab_start, size_t plab_word_size);
-
- ParScanThreadState(Space* to_space_, ParNewGeneration* gen_,
- Generation* old_gen_, int thread_num_,
- ObjToScanQueueSet* work_queue_set_,
- Stack<oop, mtGC>* overflow_stacks_,
- PreservedMarks* preserved_marks_,
- size_t desired_plab_sz_,
- TaskTerminator& term_);
-
- public:
- AgeTable* age_table() {return &_ageTable;}
-
- ObjToScanQueue* work_queue() { return _work_queue; }
-
- PreservedMarks* preserved_marks() const { return _preserved_marks; }
-
- PLAB* to_space_alloc_buffer() {
- return &_to_space_alloc_buffer;
- }
-
- ParEvacuateFollowersClosure& evacuate_followers_closure() { return _evacuate_followers; }
- DefNewGeneration::IsAliveClosure& is_alive_closure() { return _is_alive_closure; }
- ParScanWeakRefClosure& scan_weak_ref_closure() { return _scan_weak_ref_closure; }
- ParKeepAliveClosure& keep_alive_closure() { return _keep_alive_closure; }
- ParScanClosure& older_gen_closure() { return _older_gen_closure; }
- ParRootScanWithoutBarrierClosure& to_space_root_closure() { return _to_space_root_closure; };
-
- // Decrease queue size below "max_size".
- void trim_queues(int max_size);
-
- // Private overflow stack usage
- Stack<oop, mtGC>* overflow_stack() { return _overflow_stack; }
- bool take_from_overflow_stack();
- void push_on_overflow_stack(oop p);
-
- // Is new_obj a candidate for scan_partial_array_and_push_remainder method.
- inline bool should_be_partially_scanned(oop new_obj, oop old_obj) const;
-
- int thread_num() { return _thread_num; }
-
- // Allocate a to-space block of size "sz", or else return NULL.
- HeapWord* alloc_in_to_space_slow(size_t word_sz);
-
- inline HeapWord* alloc_in_to_space(size_t word_sz);
-
- HeapWord* young_old_boundary() { return _young_old_boundary; }
-
- void set_young_old_boundary(HeapWord *boundary) {
- _young_old_boundary = boundary;
- }
-
- // Undo the most recent allocation ("obj", of "word_sz").
- void undo_alloc_in_to_space(HeapWord* obj, size_t word_sz);
-
- // Promotion failure stats
- void register_promotion_failure(size_t sz) {
- _promotion_failed_info.register_copy_failure(sz);
- }
- PromotionFailedInfo& promotion_failed_info() {
- return _promotion_failed_info;
- }
- bool promotion_failed() {
- return _promotion_failed_info.has_failed();
- }
- void print_promotion_failure_size();
-
-#if TASKQUEUE_STATS
- TaskQueueStats & taskqueue_stats() const { return _work_queue->stats; }
-
- size_t term_attempts() const { return _term_attempts; }
- size_t overflow_refills() const { return _overflow_refills; }
- size_t overflow_refill_objs() const { return _overflow_refill_objs; }
-
- void note_term_attempt() { ++_term_attempts; }
- void note_overflow_refill(size_t objs) {
- ++_overflow_refills; _overflow_refill_objs += objs;
- }
-
- void reset_stats();
-#endif // TASKQUEUE_STATS
-
- void start_strong_roots() {
- _start_strong_roots = os::elapsedTime();
- }
- void end_strong_roots() {
- _strong_roots_time += (os::elapsedTime() - _start_strong_roots);
- }
- double strong_roots_time() const { return _strong_roots_time; }
- void start_term_time() {
- TASKQUEUE_STATS_ONLY(note_term_attempt());
- _start_term = os::elapsedTime();
- }
- void end_term_time() {
- _term_time += (os::elapsedTime() - _start_term);
- }
- double term_time() const { return _term_time; }
-
- double elapsed_time() const {
- return os::elapsedTime() - _start;
- }
-};
-
-class ParNewGenTask: public AbstractGangTask {
- private:
- ParNewGeneration* _young_gen;
- Generation* _old_gen;
- HeapWord* _young_old_boundary;
- class ParScanThreadStateSet* _state_set;
- StrongRootsScope* _strong_roots_scope;
-
-public:
- ParNewGenTask(ParNewGeneration* young_gen,
- Generation* old_gen,
- HeapWord* young_old_boundary,
- ParScanThreadStateSet* state_set,
- StrongRootsScope* strong_roots_scope);
-
- HeapWord* young_old_boundary() { return _young_old_boundary; }
-
- void work(uint worker_id);
-};
-
-class KeepAliveClosure: public DefNewGeneration::KeepAliveClosure {
- protected:
- template <class T> void do_oop_work(T* p);
- public:
- KeepAliveClosure(ScanWeakRefClosure* cl);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-template <typename OopClosureType1, typename OopClosureType2>
-class EvacuateFollowersClosureGeneral: public VoidClosure {
- private:
- CMSHeap* _heap;
- OopClosureType1* _scan_cur_or_nonheap;
- OopClosureType2* _scan_older;
- public:
- EvacuateFollowersClosureGeneral(CMSHeap* heap,
- OopClosureType1* cur,
- OopClosureType2* older);
- virtual void do_void();
-};
-
-// Closure for scanning ParNewGeneration.
-// Same as ScanClosure, except does parallel GC barrier.
-class ScanClosureWithParBarrier: public OopsInClassLoaderDataOrGenClosure {
- private:
- ParNewGeneration* _g;
- HeapWord* _boundary;
- bool _gc_barrier;
-
- template <class T> void do_oop_work(T* p);
-
- public:
- ScanClosureWithParBarrier(ParNewGeneration* g, bool gc_barrier);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-// Implements AbstractRefProcTaskExecutor for ParNew.
-class ParNewRefProcTaskExecutor: public AbstractRefProcTaskExecutor {
- private:
- ParNewGeneration& _young_gen;
- Generation& _old_gen;
- ParScanThreadStateSet& _state_set;
- public:
- ParNewRefProcTaskExecutor(ParNewGeneration& young_gen,
- Generation& old_gen,
- ParScanThreadStateSet& state_set)
- : _young_gen(young_gen), _old_gen(old_gen), _state_set(state_set)
- { }
-
- // Executes a task using worker threads.
- virtual void execute(ProcessTask& task, uint ergo_workers);
- // Switch to single threaded mode.
- virtual void set_single_threaded_mode();
-};
-
-
-// A Generation that does parallel young-gen collection.
-
-class ParNewGeneration: public DefNewGeneration {
- friend class ParNewGenTask;
- friend class ParNewRefProcTask;
- friend class ParNewRefProcTaskExecutor;
- friend class ParScanThreadStateSet;
- friend class ParEvacuateFollowersClosure;
-
- private:
- // The per-worker-thread work queues
- ObjToScanQueueSet* _task_queues;
-
- // Per-worker-thread local overflow stacks
- Stack<oop, mtGC>* _overflow_stacks;
-
- // Desired size of survivor space plab's
- PLABStats _plab_stats;
-
- // A list of from-space images of to-be-scanned objects, threaded through
- // klass-pointers (klass information already copied to the forwarded
- // image.) Manipulated with CAS.
- oopDesc* volatile _overflow_list;
- NOT_PRODUCT(ssize_t _num_par_pushes;)
-
- // This closure is used by the reference processor to filter out
- // references to live referent.
- DefNewGeneration::IsAliveClosure _is_alive_closure;
-
- // GC tracer that should be used during collection.
- ParNewTracer _gc_tracer;
-
- static oop real_forwardee_slow(oop obj);
- static void waste_some_time();
-
- void handle_promotion_failed(CMSHeap* gch, ParScanThreadStateSet& thread_state_set);
-
- protected:
-
- void restore_preserved_marks();
-
- public:
- ParNewGeneration(ReservedSpace rs,
- size_t initial_byte_size,
- size_t min_byte_size,
- size_t max_byte_size);
-
- ~ParNewGeneration() {
- for (uint i = 0; i < ParallelGCThreads; i++)
- delete _task_queues->queue(i);
-
- delete _task_queues;
- }
-
- virtual void ref_processor_init();
- virtual Generation::Name kind() { return Generation::ParNew; }
- virtual const char* name() const;
- virtual const char* short_name() const { return "ParNew"; }
-
- // override
- virtual bool refs_discovery_is_mt() const {
- return ParallelGCThreads > 1;
- }
-
- // Make the collection virtual.
- virtual void collect(bool full,
- bool clear_all_soft_refs,
- size_t size,
- bool is_tlab);
-
- // This needs to be visible to the closure function.
- // "obj" is the object to be copied, "m" is a recent value of its mark
- // that must not contain a forwarding pointer (though one might be
- // inserted in "obj"s mark word by a parallel thread).
- oop copy_to_survivor_space(ParScanThreadState* par_scan_state,
- oop obj, size_t obj_sz, markWord m);
-
- // in support of testing overflow code
- NOT_PRODUCT(int _overflow_counter;)
- NOT_PRODUCT(bool should_simulate_overflow();)
-
- // Accessor for overflow list
- oop overflow_list() { return _overflow_list; }
-
- // Push the given (from-space) object on the global overflow list.
- void push_on_overflow_list(oop from_space_obj, ParScanThreadState* par_scan_state);
-
- // If the global overflow list is non-empty, move some tasks from it
- // onto "work_q" (which need not be empty). No more than 1/4 of the
- // available space on "work_q" is used.
- bool take_from_overflow_list(ParScanThreadState* par_scan_state);
- bool take_from_overflow_list_work(ParScanThreadState* par_scan_state);
-
- // The task queues to be used by parallel GC threads.
- ObjToScanQueueSet* task_queues() {
- return _task_queues;
- }
-
- PLABStats* plab_stats() {
- return &_plab_stats;
- }
-
- size_t desired_plab_sz();
-
- const ParNewTracer* gc_tracer() const {
- return &_gc_tracer;
- }
-
- static oop real_forwardee(oop obj);
-};
-
-#endif // SHARE_GC_CMS_PARNEWGENERATION_HPP
--- a/src/hotspot/share/gc/cms/parNewGeneration.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2015, 2019, 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_GC_CMS_PARNEWGENERATION_INLINE_HPP
-#define SHARE_GC_CMS_PARNEWGENERATION_INLINE_HPP
-
-#include "gc/cms/parNewGeneration.hpp"
-#include "gc/shared/plab.inline.hpp"
-#include "utilities/globalDefinitions.hpp"
-
-inline HeapWord* ParScanThreadState::alloc_in_to_space(size_t word_sz) {
- HeapWord* obj = to_space_alloc_buffer()->allocate_aligned(word_sz, SurvivorAlignmentInBytes);
- if (obj != NULL) return obj;
- else return alloc_in_to_space_slow(word_sz);
-}
-#endif // SHARE_GC_CMS_PARNEWGENERATION_INLINE_HPP
--- a/src/hotspot/share/gc/cms/parOopClosures.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-/*
- * Copyright (c) 2007, 2019, 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_GC_CMS_PAROOPCLOSURES_HPP
-#define SHARE_GC_CMS_PAROOPCLOSURES_HPP
-
-#include "gc/shared/genOopClosures.hpp"
-#include "gc/shared/taskqueue.hpp"
-#include "memory/padded.hpp"
-
-// Closures for ParNewGeneration
-
-class ParScanThreadState;
-class ParNewGeneration;
-typedef Padded<OopTaskQueue> ObjToScanQueue;
-typedef GenericTaskQueueSet<ObjToScanQueue, mtGC> ObjToScanQueueSet;
-class ParallelTaskTerminator;
-
-class ParScanClosure: public OopsInClassLoaderDataOrGenClosure {
- protected:
- ParScanThreadState* _par_scan_state;
- ParNewGeneration* _g;
- HeapWord* _boundary;
- template <class T> void inline par_do_barrier(T* p);
- template <class T> void inline do_oop_work(T* p,
- bool gc_barrier,
- bool root_scan);
- public:
- ParScanClosure(ParNewGeneration* g, ParScanThreadState* par_scan_state);
-};
-
-class ParScanWithBarrierClosure: public ParScanClosure {
- public:
- ParScanWithBarrierClosure(ParNewGeneration* g,
- ParScanThreadState* par_scan_state) :
- ParScanClosure(g, par_scan_state) {}
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-class ParScanWithoutBarrierClosure: public ParScanClosure {
- public:
- ParScanWithoutBarrierClosure(ParNewGeneration* g,
- ParScanThreadState* par_scan_state) :
- ParScanClosure(g, par_scan_state) {}
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-class ParRootScanWithBarrierTwoGensClosure: public ParScanClosure {
- public:
- ParRootScanWithBarrierTwoGensClosure(ParNewGeneration* g,
- ParScanThreadState* par_scan_state) :
- ParScanClosure(g, par_scan_state) {}
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-class ParRootScanWithoutBarrierClosure: public ParScanClosure {
- public:
- ParRootScanWithoutBarrierClosure(ParNewGeneration* g,
- ParScanThreadState* par_scan_state) :
- ParScanClosure(g, par_scan_state) {}
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-class ParScanWeakRefClosure: public ScanWeakRefClosure {
- protected:
- ParScanThreadState* _par_scan_state;
- template <class T> inline void do_oop_work(T* p);
- public:
- ParScanWeakRefClosure(ParNewGeneration* g,
- ParScanThreadState* par_scan_state);
- virtual void do_oop(oop* p);
- virtual void do_oop(narrowOop* p);
-};
-
-class ParEvacuateFollowersClosure: public VoidClosure {
- private:
- ParScanThreadState* _par_scan_state;
- ParScanThreadState* par_scan_state() { return _par_scan_state; }
-
- // We want to preserve the specific types here (rather than "OopClosure")
- // for later de-virtualization of do_oop calls.
- ParScanWithoutBarrierClosure* _to_space_closure;
- ParScanWithoutBarrierClosure* to_space_closure() {
- return _to_space_closure;
- }
- ParRootScanWithoutBarrierClosure* _to_space_root_closure;
- ParRootScanWithoutBarrierClosure* to_space_root_closure() {
- return _to_space_root_closure;
- }
-
- ParScanWithBarrierClosure* _old_gen_closure;
- ParScanWithBarrierClosure* old_gen_closure () {
- return _old_gen_closure;
- }
- ParRootScanWithBarrierTwoGensClosure* _old_gen_root_closure;
- ParRootScanWithBarrierTwoGensClosure* old_gen_root_closure () {
- return _old_gen_root_closure;
- }
-
- ParNewGeneration* _par_gen;
- ParNewGeneration* par_gen() { return _par_gen; }
-
- ObjToScanQueueSet* _task_queues;
- ObjToScanQueueSet* task_queues() { return _task_queues; }
-
- ParallelTaskTerminator* _terminator;
- ParallelTaskTerminator* terminator() { return _terminator; }
- public:
- ParEvacuateFollowersClosure(
- ParScanThreadState* par_scan_state_,
- ParScanWithoutBarrierClosure* to_space_closure_,
- ParScanWithBarrierClosure* old_gen_closure_,
- ParRootScanWithoutBarrierClosure* to_space_root_closure_,
- ParNewGeneration* par_gen_,
- ParRootScanWithBarrierTwoGensClosure* old_gen_root_closure_,
- ObjToScanQueueSet* task_queues_,
- ParallelTaskTerminator* terminator_);
- virtual void do_void();
-};
-
-#endif // SHARE_GC_CMS_PAROOPCLOSURES_HPP
--- a/src/hotspot/share/gc/cms/parOopClosures.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-/*
- * Copyright (c) 2007, 2019, 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_GC_CMS_PAROOPCLOSURES_INLINE_HPP
-#define SHARE_GC_CMS_PAROOPCLOSURES_INLINE_HPP
-
-#include "gc/cms/cmsHeap.hpp"
-#include "gc/cms/parNewGeneration.hpp"
-#include "gc/cms/parOopClosures.hpp"
-#include "gc/shared/cardTableRS.hpp"
-#include "gc/shared/genOopClosures.inline.hpp"
-#include "logging/log.hpp"
-#include "logging/logStream.hpp"
-#include "oops/access.inline.hpp"
-#include "oops/compressedOops.inline.hpp"
-#include "oops/oop.inline.hpp"
-
-template <class T> inline void ParScanWeakRefClosure::do_oop_work(T* p) {
- oop obj = RawAccess<IS_NOT_NULL>::oop_load(p);
- // weak references are sometimes scanned twice; must check
- // that to-space doesn't already contain this object
- if ((HeapWord*)obj < _boundary && !_g->to()->is_in_reserved(obj)) {
- // we need to ensure that it is copied (see comment in
- // ParScanClosure::do_oop_work).
- Klass* objK = obj->klass();
- markWord m = obj->mark_raw();
- oop new_obj;
- if (m.is_marked()) { // Contains forwarding pointer.
- new_obj = ParNewGeneration::real_forwardee(obj);
- } else {
- size_t obj_sz = obj->size_given_klass(objK);
- new_obj = ((ParNewGeneration*)_g)->copy_to_survivor_space(_par_scan_state,
- obj, obj_sz, m);
- }
- RawAccess<IS_NOT_NULL>::oop_store(p, new_obj);
- }
-}
-
-inline void ParScanWeakRefClosure::do_oop(oop* p) { ParScanWeakRefClosure::do_oop_work(p); }
-inline void ParScanWeakRefClosure::do_oop(narrowOop* p) { ParScanWeakRefClosure::do_oop_work(p); }
-
-template <class T> inline void ParScanClosure::par_do_barrier(T* p) {
- assert(generation()->is_in_reserved(p), "expected ref in generation");
- oop obj = RawAccess<IS_NOT_NULL>::oop_load(p);
- // If p points to a younger generation, mark the card.
- if ((HeapWord*)obj < gen_boundary()) {
- rs()->write_ref_field_gc_par(p, obj);
- }
-}
-
-template <class T>
-inline void ParScanClosure::do_oop_work(T* p,
- bool gc_barrier,
- bool root_scan) {
- assert((!CMSHeap::heap()->is_in_reserved(p) ||
- generation()->is_in_reserved(p))
- && (CMSHeap::heap()->is_young_gen(generation()) || gc_barrier),
- "The gen must be right, and we must be doing the barrier "
- "in older generations.");
- T heap_oop = RawAccess<>::oop_load(p);
- if (!CompressedOops::is_null(heap_oop)) {
- oop obj = CompressedOops::decode_not_null(heap_oop);
- if ((HeapWord*)obj < _boundary) {
-#ifndef PRODUCT
- if (_g->to()->is_in_reserved(obj)) {
- Log(gc) log;
- log.error("Scanning field (" PTR_FORMAT ") twice?", p2i(p));
- CMSHeap* heap = CMSHeap::heap();
- Space* sp = heap->space_containing(p);
- oop obj = oop(sp->block_start(p));
- assert((HeapWord*)obj < (HeapWord*)p, "Error");
- log.error("Object: " PTR_FORMAT, p2i((void *)obj));
- log.error("-------");
- LogStream ls(log.error());
- obj->print_on(&ls);
- log.error("-----");
- log.error("Heap:");
- log.error("-----");
- heap->print_on(&ls);
- ShouldNotReachHere();
- }
-#endif
- // OK, we need to ensure that it is copied.
- // We read the klass and mark in this order, so that we can reliably
- // get the size of the object: if the mark we read is not a
- // forwarding pointer, then the klass is valid: the klass is only
- // overwritten with an overflow next pointer after the object is
- // forwarded.
- Klass* objK = obj->klass();
- markWord m = obj->mark_raw();
- oop new_obj;
- if (m.is_marked()) { // Contains forwarding pointer.
- new_obj = ParNewGeneration::real_forwardee(obj);
- RawAccess<IS_NOT_NULL>::oop_store(p, new_obj);
- log_develop_trace(gc, scavenge)("{%s %s ( " PTR_FORMAT " ) " PTR_FORMAT " -> " PTR_FORMAT " (%d)}",
- "forwarded ",
- new_obj->klass()->internal_name(), p2i(p), p2i((void *)obj), p2i((void *)new_obj), new_obj->size());
- } else {
- size_t obj_sz = obj->size_given_klass(objK);
- new_obj = _g->copy_to_survivor_space(_par_scan_state, obj, obj_sz, m);
- RawAccess<IS_NOT_NULL>::oop_store(p, new_obj);
- if (root_scan) {
- // This may have pushed an object. If we have a root
- // category with a lot of roots, can't let the queue get too
- // full:
- (void)_par_scan_state->trim_queues(10 * ParallelGCThreads);
- }
- }
- if (is_scanning_a_cld()) {
- do_cld_barrier();
- } else if (gc_barrier) {
- // Now call parent closure
- par_do_barrier(p);
- }
- }
- }
-}
-
-inline void ParScanWithBarrierClosure::do_oop(oop* p) { ParScanClosure::do_oop_work(p, true, false); }
-inline void ParScanWithBarrierClosure::do_oop(narrowOop* p) { ParScanClosure::do_oop_work(p, true, false); }
-
-inline void ParScanWithoutBarrierClosure::do_oop(oop* p) { ParScanClosure::do_oop_work(p, false, false); }
-inline void ParScanWithoutBarrierClosure::do_oop(narrowOop* p) { ParScanClosure::do_oop_work(p, false, false); }
-
-#endif // SHARE_GC_CMS_PAROOPCLOSURES_INLINE_HPP
--- a/src/hotspot/share/gc/cms/promotionInfo.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,308 +0,0 @@
-/*
- * Copyright (c) 2010, 2018, 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/cms/compactibleFreeListSpace.hpp"
-#include "gc/cms/promotionInfo.hpp"
-#include "gc/shared/genOopClosures.hpp"
-#include "oops/compressedOops.inline.hpp"
-#include "oops/markWord.inline.hpp"
-#include "oops/oop.inline.hpp"
-
-/////////////////////////////////////////////////////////////////////////
-//// PromotionInfo
-/////////////////////////////////////////////////////////////////////////
-
-
-PromotedObject* PromotedObject::next() const {
- assert(!((FreeChunk*)this)->is_free(), "Error");
- PromotedObject* res;
- if (UseCompressedOops) {
- // The next pointer is a compressed oop stored in the top 32 bits
- res = (PromotedObject*)CompressedOops::decode(_data._narrow_next);
- } else {
- res = (PromotedObject*)(_next & next_mask);
- }
- assert(oopDesc::is_oop_or_null(oop(res), true /* ignore mark word */), "Expected an oop or NULL at " PTR_FORMAT, p2i(oop(res)));
- return res;
-}
-
-inline void PromotedObject::setNext(PromotedObject* x) {
- assert(((intptr_t)x & ~next_mask) == 0, "Conflict in bit usage, "
- "or insufficient alignment of objects");
- if (UseCompressedOops) {
- assert(_data._narrow_next == 0, "Overwrite?");
- _data._narrow_next = CompressedOops::encode(oop(x));
- } else {
- _next |= (intptr_t)x;
- }
- assert(!((FreeChunk*)this)->is_free(), "Error");
-}
-
-// Return the next displaced header, incrementing the pointer and
-// recycling spool area as necessary.
-markWord PromotionInfo::nextDisplacedHeader() {
- assert(_spoolHead != NULL, "promotionInfo inconsistency");
- assert(_spoolHead != _spoolTail || _firstIndex < _nextIndex,
- "Empty spool space: no displaced header can be fetched");
- assert(_spoolHead->bufferSize > _firstIndex, "Off by one error at head?");
- markWord hdr = _spoolHead->displacedHdr[_firstIndex];
- // Spool forward
- if (++_firstIndex == _spoolHead->bufferSize) { // last location in this block
- // forward to next block, recycling this block into spare spool buffer
- SpoolBlock* tmp = _spoolHead->nextSpoolBlock;
- assert(_spoolHead != _spoolTail, "Spooling storage mix-up");
- _spoolHead->nextSpoolBlock = _spareSpool;
- _spareSpool = _spoolHead;
- _spoolHead = tmp;
- _firstIndex = 1;
- NOT_PRODUCT(
- if (_spoolHead == NULL) { // all buffers fully consumed
- assert(_spoolTail == NULL && _nextIndex == 1,
- "spool buffers processing inconsistency");
- }
- )
- }
- return hdr;
-}
-
-void PromotionInfo::track(PromotedObject* trackOop) {
- track(trackOop, oop(trackOop)->klass());
-}
-
-void PromotionInfo::track(PromotedObject* trackOop, Klass* klassOfOop) {
- // make a copy of header as it may need to be spooled
- markWord mark = oop(trackOop)->mark_raw();
- trackOop->clear_next();
- if (mark.must_be_preserved_for_cms_scavenge(klassOfOop)) {
- // save non-prototypical header, and mark oop
- saveDisplacedHeader(mark);
- trackOop->setDisplacedMark();
- } else {
- // we'd like to assert something like the following:
- // assert(mark == markWord::prototype(), "consistency check");
- // ... but the above won't work because the age bits have not (yet) been
- // cleared. The remainder of the check would be identical to the
- // condition checked in must_be_preserved() above, so we don't really
- // have anything useful to check here!
- }
- if (_promoTail != NULL) {
- assert(_promoHead != NULL, "List consistency");
- _promoTail->setNext(trackOop);
- _promoTail = trackOop;
- } else {
- assert(_promoHead == NULL, "List consistency");
- _promoHead = _promoTail = trackOop;
- }
- // Mask as newly promoted, so we can skip over such objects
- // when scanning dirty cards
- assert(!trackOop->hasPromotedMark(), "Should not have been marked");
- trackOop->setPromotedMark();
-}
-
-// Save the given displaced header, incrementing the pointer and
-// obtaining more spool area as necessary.
-void PromotionInfo::saveDisplacedHeader(markWord hdr) {
- assert(_spoolHead != NULL && _spoolTail != NULL,
- "promotionInfo inconsistency");
- assert(_spoolTail->bufferSize > _nextIndex, "Off by one error at tail?");
- _spoolTail->displacedHdr[_nextIndex] = hdr;
- // Spool forward
- if (++_nextIndex == _spoolTail->bufferSize) { // last location in this block
- // get a new spooling block
- assert(_spoolTail->nextSpoolBlock == NULL, "tail should terminate spool list");
- _splice_point = _spoolTail; // save for splicing
- _spoolTail->nextSpoolBlock = getSpoolBlock(); // might fail
- _spoolTail = _spoolTail->nextSpoolBlock; // might become NULL ...
- // ... but will attempt filling before next promotion attempt
- _nextIndex = 1;
- }
-}
-
-// Ensure that spooling space exists. Return false if spooling space
-// could not be obtained.
-bool PromotionInfo::ensure_spooling_space_work() {
- assert(!has_spooling_space(), "Only call when there is no spooling space");
- // Try and obtain more spooling space
- SpoolBlock* newSpool = getSpoolBlock();
- assert(newSpool == NULL ||
- (newSpool->bufferSize != 0 && newSpool->nextSpoolBlock == NULL),
- "getSpoolBlock() sanity check");
- if (newSpool == NULL) {
- return false;
- }
- _nextIndex = 1;
- if (_spoolTail == NULL) {
- _spoolTail = newSpool;
- if (_spoolHead == NULL) {
- _spoolHead = newSpool;
- _firstIndex = 1;
- } else {
- assert(_splice_point != NULL && _splice_point->nextSpoolBlock == NULL,
- "Splice point invariant");
- // Extra check that _splice_point is connected to list
- #ifdef ASSERT
- {
- SpoolBlock* blk = _spoolHead;
- for (; blk->nextSpoolBlock != NULL;
- blk = blk->nextSpoolBlock);
- assert(blk != NULL && blk == _splice_point,
- "Splice point incorrect");
- }
- #endif // ASSERT
- _splice_point->nextSpoolBlock = newSpool;
- }
- } else {
- assert(_spoolHead != NULL, "spool list consistency");
- _spoolTail->nextSpoolBlock = newSpool;
- _spoolTail = newSpool;
- }
- return true;
-}
-
-// Get a free spool buffer from the free pool, getting a new block
-// from the heap if necessary.
-SpoolBlock* PromotionInfo::getSpoolBlock() {
- SpoolBlock* res;
- if ((res = _spareSpool) != NULL) {
- _spareSpool = _spareSpool->nextSpoolBlock;
- res->nextSpoolBlock = NULL;
- } else { // spare spool exhausted, get some from heap
- res = (SpoolBlock*)(space()->allocateScratch(refillSize()));
- if (res != NULL) {
- res->init();
- }
- }
- assert(res == NULL || res->nextSpoolBlock == NULL, "postcondition");
- return res;
-}
-
-void PromotionInfo::startTrackingPromotions() {
- assert(noPromotions(), "sanity");
- assert(_spoolHead == _spoolTail && _firstIndex == _nextIndex,
- "spooling inconsistency?");
- _firstIndex = _nextIndex = 1;
- _tracking = true;
-}
-
-void PromotionInfo::stopTrackingPromotions() {
- assert(noPromotions(), "we should have torn down the lists by now");
- assert(_spoolHead == _spoolTail && _firstIndex == _nextIndex,
- "spooling inconsistency?");
- _firstIndex = _nextIndex = 1;
- _tracking = false;
-}
-
-// When _spoolTail is not NULL, then the slot <_spoolTail, _nextIndex>
-// points to the next slot available for filling.
-// The set of slots holding displaced headers are then all those in the
-// right-open interval denoted by:
-//
-// [ <_spoolHead, _firstIndex>, <_spoolTail, _nextIndex> )
-//
-// When _spoolTail is NULL, then the set of slots with displaced headers
-// is all those starting at the slot <_spoolHead, _firstIndex> and
-// going up to the last slot of last block in the linked list.
-// In this latter case, _splice_point points to the tail block of
-// this linked list of blocks holding displaced headers.
-void PromotionInfo::verify() const {
- // Verify the following:
- // 1. the number of displaced headers matches the number of promoted
- // objects that have displaced headers
- // 2. each promoted object lies in this space
- debug_only(
- PromotedObject* junk = NULL;
- assert(junk->next_addr() == (void*)(oop(junk)->mark_addr_raw()),
- "Offset of PromotedObject::_next is expected to align with "
- " the OopDesc::_mark within OopDesc");
- )
- // FIXME: guarantee????
- guarantee(_spoolHead == NULL || _spoolTail != NULL ||
- _splice_point != NULL, "list consistency");
- guarantee(_promoHead == NULL || _promoTail != NULL, "list consistency");
- // count the number of objects with displaced headers
- size_t numObjsWithDisplacedHdrs = 0;
- for (PromotedObject* curObj = _promoHead; curObj != NULL; curObj = curObj->next()) {
- guarantee(space()->is_in_reserved((HeapWord*)curObj), "Containment");
- // the last promoted object may fail the mark() != NULL test of is_oop().
- guarantee(curObj->next() == NULL || oopDesc::is_oop(oop(curObj)), "must be an oop");
- if (curObj->hasDisplacedMark()) {
- numObjsWithDisplacedHdrs++;
- }
- }
- // Count the number of displaced headers
- size_t numDisplacedHdrs = 0;
- for (SpoolBlock* curSpool = _spoolHead;
- curSpool != _spoolTail && curSpool != NULL;
- curSpool = curSpool->nextSpoolBlock) {
- // the first entry is just a self-pointer; indices 1 through
- // bufferSize - 1 are occupied (thus, bufferSize - 1 slots).
- guarantee((void*)curSpool->displacedHdr == (void*)&curSpool->displacedHdr,
- "first entry of displacedHdr should be self-referential");
- numDisplacedHdrs += curSpool->bufferSize - 1;
- }
- guarantee((_spoolHead == _spoolTail) == (numDisplacedHdrs == 0),
- "internal consistency");
- guarantee(_spoolTail != NULL || _nextIndex == 1,
- "Inconsistency between _spoolTail and _nextIndex");
- // We overcounted (_firstIndex-1) worth of slots in block
- // _spoolHead and we undercounted (_nextIndex-1) worth of
- // slots in block _spoolTail. We make an appropriate
- // adjustment by subtracting the first and adding the
- // second: - (_firstIndex - 1) + (_nextIndex - 1)
- numDisplacedHdrs += (_nextIndex - _firstIndex);
- guarantee(numDisplacedHdrs == numObjsWithDisplacedHdrs, "Displaced hdr count");
-}
-
-void PromotionInfo::print_on(outputStream* st) const {
- SpoolBlock* curSpool = NULL;
- size_t i = 0;
- st->print_cr(" start & end indices: [" SIZE_FORMAT ", " SIZE_FORMAT ")",
- _firstIndex, _nextIndex);
- for (curSpool = _spoolHead; curSpool != _spoolTail && curSpool != NULL;
- curSpool = curSpool->nextSpoolBlock) {
- curSpool->print_on(st);
- st->print_cr(" active ");
- i++;
- }
- for (curSpool = _spoolTail; curSpool != NULL;
- curSpool = curSpool->nextSpoolBlock) {
- curSpool->print_on(st);
- st->print_cr(" inactive ");
- i++;
- }
- for (curSpool = _spareSpool; curSpool != NULL;
- curSpool = curSpool->nextSpoolBlock) {
- curSpool->print_on(st);
- st->print_cr(" free ");
- i++;
- }
- st->print_cr(" " SIZE_FORMAT " header spooling blocks", i);
-}
-
-void SpoolBlock::print_on(outputStream* st) const {
- st->print("[" PTR_FORMAT "," PTR_FORMAT "), " SIZE_FORMAT " HeapWords -> " PTR_FORMAT,
- p2i(this), p2i((HeapWord*)displacedHdr + bufferSize),
- bufferSize, p2i(nextSpoolBlock));
-}
--- a/src/hotspot/share/gc/cms/promotionInfo.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,188 +0,0 @@
-/*
- * Copyright (c) 2010, 2019, 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_GC_CMS_PROMOTIONINFO_HPP
-#define SHARE_GC_CMS_PROMOTIONINFO_HPP
-
-#include "gc/cms/freeChunk.hpp"
-
-// Forward declarations
-class CompactibleFreeListSpace;
-
-class PromotedObject {
- private:
- enum {
- promoted_mask = right_n_bits(2), // i.e. 0x3
- displaced_mark = nth_bit(2), // i.e. 0x4
- next_mask = ~(right_n_bits(3)) // i.e. ~(0x7)
- };
-
- // Below, we want _narrow_next in the "higher" 32 bit slot,
- // whose position will depend on endian-ness of the platform.
- // This is so that there is no interference with the
- // cms_free_bit occupying bit position 7 (lsb == 0)
- // when we are using compressed oops; see FreeChunk::is_free().
- // We cannot move the cms_free_bit down because currently
- // biased locking code assumes that age bits are contiguous
- // with the lock bits. Even if that assumption were relaxed,
- // the least position we could move this bit to would be
- // to bit position 3, which would require 16 byte alignment.
- typedef struct {
-#ifdef VM_LITTLE_ENDIAN
- LP64_ONLY(narrowOop _pad;)
- narrowOop _narrow_next;
-#else
- narrowOop _narrow_next;
- LP64_ONLY(narrowOop _pad;)
-#endif
- } Data;
-
- union {
- intptr_t _next;
- Data _data;
- };
- public:
- PromotedObject* next() const;
- void setNext(PromotedObject* x);
- inline void setPromotedMark() {
- _next |= promoted_mask;
- assert(!((FreeChunk*)this)->is_free(), "Error");
- }
- inline bool hasPromotedMark() const {
- assert(!((FreeChunk*)this)->is_free(), "Error");
- return (_next & promoted_mask) == promoted_mask;
- }
- inline void setDisplacedMark() {
- _next |= displaced_mark;
- assert(!((FreeChunk*)this)->is_free(), "Error");
- }
- inline bool hasDisplacedMark() const {
- assert(!((FreeChunk*)this)->is_free(), "Error");
- return (_next & displaced_mark) != 0;
- }
- inline void clear_next() {
- _next = 0;
- assert(!((FreeChunk*)this)->is_free(), "Error");
- }
- debug_only(void *next_addr() { return (void *) &_next; })
-};
-
-class SpoolBlock: public FreeChunk {
- friend class PromotionInfo;
- protected:
- SpoolBlock* nextSpoolBlock;
- size_t bufferSize; // number of usable words in this block
- markWord* displacedHdr; // the displaced headers start here
-
- // Note about bufferSize: it denotes the number of entries available plus 1;
- // legal indices range from 1 through BufferSize - 1. See the verification
- // code verify() that counts the number of displaced headers spooled.
- size_t computeBufferSize() {
- return (size() * sizeof(HeapWord) - sizeof(*this)) / sizeof(markWord);
- }
-
- public:
- void init() {
- bufferSize = computeBufferSize();
- displacedHdr = (markWord*)&displacedHdr;
- nextSpoolBlock = NULL;
- }
-
- void print_on(outputStream* st) const;
- void print() const { print_on(tty); }
-};
-
-class PromotionInfo {
- bool _tracking; // set if tracking
- CompactibleFreeListSpace* _space; // the space to which this belongs
- PromotedObject* _promoHead; // head of list of promoted objects
- PromotedObject* _promoTail; // tail of list of promoted objects
- SpoolBlock* _spoolHead; // first spooling block
- SpoolBlock* _spoolTail; // last non-full spooling block or null
- SpoolBlock* _splice_point; // when _spoolTail is null, holds list tail
- SpoolBlock* _spareSpool; // free spool buffer
- size_t _firstIndex; // first active index in
- // first spooling block (_spoolHead)
- size_t _nextIndex; // last active index + 1 in last
- // spooling block (_spoolTail)
- private:
- // ensure that spooling space exists; return true if there is spooling space
- bool ensure_spooling_space_work();
-
- public:
- PromotionInfo() :
- _tracking(0), _space(NULL),
- _promoHead(NULL), _promoTail(NULL),
- _spoolHead(NULL), _spoolTail(NULL),
- _spareSpool(NULL), _firstIndex(1),
- _nextIndex(1) {}
-
- bool noPromotions() const {
- assert(_promoHead != NULL || _promoTail == NULL, "list inconsistency");
- return _promoHead == NULL;
- }
- void startTrackingPromotions();
- void stopTrackingPromotions();
- bool tracking() const { return _tracking; }
- void track(PromotedObject* trackOop); // keep track of a promoted oop
- // The following variant must be used when trackOop is not fully
- // initialized and has a NULL klass:
- void track(PromotedObject* trackOop, Klass* klassOfOop); // keep track of a promoted oop
- void setSpace(CompactibleFreeListSpace* sp) { _space = sp; }
- CompactibleFreeListSpace* space() const { return _space; }
- markWord nextDisplacedHeader(); // get next header & forward spool pointer
- void saveDisplacedHeader(markWord hdr);
- // save header and forward spool
-
- inline size_t refillSize() const;
-
- SpoolBlock* getSpoolBlock(); // return a free spooling block
- inline bool has_spooling_space() {
- return _spoolTail != NULL && _spoolTail->bufferSize > _nextIndex;
- }
- // ensure that spooling space exists
- bool ensure_spooling_space() {
- return has_spooling_space() || ensure_spooling_space_work();
- }
-
- template <typename OopClosureType>
- void promoted_oops_iterate(OopClosureType* cl);
-
- void verify() const;
- void reset() {
- _promoHead = NULL;
- _promoTail = NULL;
- _spoolHead = NULL;
- _spoolTail = NULL;
- _spareSpool = NULL;
- _firstIndex = 0;
- _nextIndex = 0;
-
- }
-
- void print_on(outputStream* st) const;
-};
-
-
-#endif // SHARE_GC_CMS_PROMOTIONINFO_HPP
--- a/src/hotspot/share/gc/cms/promotionInfo.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) 2010, 2019, 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_GC_CMS_PROMOTIONINFO_INLINE_HPP
-#define SHARE_GC_CMS_PROMOTIONINFO_INLINE_HPP
-
-#include "gc/cms/promotionInfo.hpp"
-#include "oops/oop.inline.hpp"
-#include "utilities/debug.hpp"
-#include "utilities/macros.hpp"
-
-//////////////////////////////////////////////////////////////////////////////
-// We go over the list of promoted objects, removing each from the list,
-// and applying the closure (this may, in turn, add more elements to
-// the tail of the promoted list, and these newly added objects will
-// also be processed) until the list is empty.
-// To aid verification and debugging, in the non-product builds
-// we actually forward _promoHead each time we process a promoted oop.
-// Note that this is not necessary in general (i.e. when we don't need to
-// call PromotionInfo::verify()) because oop_iterate can only add to the
-// end of _promoTail, and never needs to look at _promoHead.
-
-template <typename OopClosureType>
-void PromotionInfo::promoted_oops_iterate(OopClosureType* cl) {
- NOT_PRODUCT(verify());
- PromotedObject *curObj, *nextObj;
- for (curObj = _promoHead; curObj != NULL; curObj = nextObj) {
- if ((nextObj = curObj->next()) == NULL) {
- /* protect ourselves against additions due to closure application
- below by resetting the list. */
- assert(_promoTail == curObj, "Should have been the tail");
- _promoHead = _promoTail = NULL;
- }
- if (curObj->hasDisplacedMark()) {
- /* restore displaced header */
- oop(curObj)->set_mark_raw(nextDisplacedHeader());
- } else {
- /* restore prototypical header */
- oop(curObj)->init_mark_raw();
- }
- /* The "promoted_mark" should now not be set */
- assert(!curObj->hasPromotedMark(),
- "Should have been cleared by restoring displaced mark-word");
- NOT_PRODUCT(_promoHead = nextObj);
- if (cl != NULL) oop(curObj)->oop_iterate(cl);
- if (nextObj == NULL) { /* start at head of list reset above */
- nextObj = _promoHead;
- }
- }
- assert(noPromotions(), "post-condition violation");
- assert(_promoHead == NULL && _promoTail == NULL, "emptied promoted list");
- assert(_spoolHead == _spoolTail, "emptied spooling buffers");
- assert(_firstIndex == _nextIndex, "empty buffer");
-}
-
-#endif // SHARE_GC_CMS_PROMOTIONINFO_INLINE_HPP
--- a/src/hotspot/share/gc/cms/vmStructs_cms.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 2007, 2019, 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_GC_CMS_VMSTRUCTS_CMS_HPP
-#define SHARE_GC_CMS_VMSTRUCTS_CMS_HPP
-
-#include "gc/cms/cmsHeap.hpp"
-#include "gc/cms/compactibleFreeListSpace.hpp"
-#include "gc/cms/concurrentMarkSweepGeneration.hpp"
-#include "gc/cms/concurrentMarkSweepThread.hpp"
-#include "gc/cms/parNewGeneration.hpp"
-
-#define VM_STRUCTS_CMSGC(nonstatic_field, \
- volatile_nonstatic_field, \
- static_field) \
- nonstatic_field(CompactibleFreeListSpace, _collector, CMSCollector*) \
- nonstatic_field(CompactibleFreeListSpace, _bt, BlockOffsetArrayNonContigSpace) \
- static_field(CompactibleFreeListSpace, _min_chunk_size_in_bytes, size_t) \
- nonstatic_field(CMSBitMap, _bmStartWord, HeapWord*) \
- nonstatic_field(CMSBitMap, _bmWordSize, size_t) \
- nonstatic_field(CMSBitMap, _shifter, const int) \
- nonstatic_field(CMSBitMap, _bm, BitMapView) \
- nonstatic_field(CMSBitMap, _virtual_space, VirtualSpace) \
- nonstatic_field(CMSCollector, _markBitMap, CMSBitMap) \
- nonstatic_field(ConcurrentMarkSweepGeneration, _cmsSpace, CompactibleFreeListSpace*) \
- static_field(ConcurrentMarkSweepThread, _collector, CMSCollector*) \
- nonstatic_field(LinearAllocBlock, _word_size, size_t) \
- nonstatic_field(AFLBinaryTreeDictionary, _total_size, size_t) \
- nonstatic_field(CompactibleFreeListSpace, _dictionary, AFLBinaryTreeDictionary*) \
- nonstatic_field(CompactibleFreeListSpace, _indexedFreeList[0], AdaptiveFreeList<FreeChunk>) \
- nonstatic_field(CompactibleFreeListSpace, _smallLinearAllocBlock, LinearAllocBlock) \
- volatile_nonstatic_field(FreeChunk, _size, size_t) \
- nonstatic_field(FreeChunk, _next, FreeChunk*) \
- nonstatic_field(FreeChunk, _prev, FreeChunk*) \
- nonstatic_field(AdaptiveFreeList<FreeChunk>, _size, size_t) \
- nonstatic_field(AdaptiveFreeList<FreeChunk>, _count, ssize_t)
-
-
-
-#define VM_TYPES_CMSGC(declare_type, \
- declare_toplevel_type, \
- declare_integer_type) \
- \
- declare_type(CMSHeap, GenCollectedHeap) \
- declare_type(ConcurrentMarkSweepGeneration,CardGeneration) \
- declare_type(ParNewGeneration, DefNewGeneration) \
- declare_type(CompactibleFreeListSpace, CompactibleSpace) \
- declare_type(ConcurrentMarkSweepThread, NamedThread) \
- declare_toplevel_type(CMSCollector) \
- declare_toplevel_type(CMSBitMap) \
- declare_toplevel_type(FreeChunk) \
- declare_toplevel_type(metaspace::Metablock) \
- declare_toplevel_type(ConcurrentMarkSweepThread*) \
- declare_toplevel_type(ConcurrentMarkSweepGeneration*) \
- declare_toplevel_type(CompactibleFreeListSpace*) \
- declare_toplevel_type(CMSCollector*) \
- declare_toplevel_type(AFLBinaryTreeDictionary) \
- declare_toplevel_type(LinearAllocBlock) \
- declare_toplevel_type(FreeChunk*) \
- declare_toplevel_type(AdaptiveFreeList<FreeChunk>*) \
- declare_toplevel_type(AdaptiveFreeList<FreeChunk>)
-
-
-#define VM_INT_CONSTANTS_CMSGC(declare_constant, \
- declare_constant_with_value) \
- declare_constant(CompactibleFreeListSpace::IndexSetSize) \
- declare_constant(Generation::ConcurrentMarkSweep) \
- declare_constant(Generation::ParNew)
-
-#endif // SHARE_GC_CMS_VMSTRUCTS_CMS_HPP
--- a/src/hotspot/share/gc/cms/yieldingWorkgroup.cpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,399 +0,0 @@
-/*
- * Copyright (c) 2005, 2019, 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/cms/yieldingWorkgroup.hpp"
-#include "gc/shared/gcId.hpp"
-#include "utilities/macros.hpp"
-
-YieldingFlexibleGangWorker::YieldingFlexibleGangWorker(YieldingFlexibleWorkGang* gang, int id)
- : AbstractGangWorker(gang, id) {}
-
-YieldingFlexibleWorkGang::YieldingFlexibleWorkGang(
- const char* name, uint workers, bool are_GC_task_threads) :
- AbstractWorkGang(name, workers, are_GC_task_threads, false),
- _yielded_workers(0),
- _started_workers(0),
- _finished_workers(0),
- _sequence_number(0),
- _task(NULL) {
-
- // Other initialization.
- _monitor = new Monitor(/* priority */ Mutex::leaf,
- /* name */ "WorkGroup monitor",
- /* allow_vm_block */ are_GC_task_threads,
- Monitor::_safepoint_check_never);
-
- assert(monitor() != NULL, "Failed to allocate monitor");
-}
-
-AbstractGangWorker* YieldingFlexibleWorkGang::allocate_worker(uint which) {
- return new YieldingFlexibleGangWorker(this, which);
-}
-
-void YieldingFlexibleWorkGang::internal_worker_poll(YieldingWorkData* data) const {
- assert(data != NULL, "worker data is null");
- data->set_task(task());
- data->set_sequence_number(sequence_number());
-}
-
-void YieldingFlexibleWorkGang::internal_note_start() {
- assert(monitor()->owned_by_self(), "note_finish is an internal method");
- _started_workers += 1;
-}
-
-void YieldingFlexibleWorkGang::internal_note_finish() {
- assert(monitor()->owned_by_self(), "note_finish is an internal method");
- _finished_workers += 1;
-}
-
-// Run a task; returns when the task is done, or the workers yield,
-// or the task is aborted.
-// A task that has been yielded can be continued via this interface
-// by using the same task repeatedly as the argument to the call.
-// It is expected that the YieldingFlexibleGangTask carries the appropriate
-// continuation information used by workers to continue the task
-// from its last yield point. Thus, a completed task will return
-// immediately with no actual work having been done by the workers.
-/////////////////////
-// Implementatiuon notes: remove before checking XXX
-/*
-Each gang is working on a task at a certain time.
-Some subset of workers may have yielded and some may
-have finished their quota of work. Until this task has
-been completed, the workers are bound to that task.
-Once the task has been completed, the gang unbounds
-itself from the task.
-
-The yielding work gang thus exports two invokation
-interfaces: run_task() and continue_task(). The
-first is used to initiate a new task and bind it
-to the workers; the second is used to continue an
-already bound task that has yielded. Upon completion
-the binding is released and a new binding may be
-created.
-
-The shape of a yielding work gang is as follows:
-
-Overseer invokes run_task(*task).
- Lock gang monitor
- Check that there is no existing binding for the gang
- If so, abort with an error
- Else, create a new binding of this gang to the given task
- Set number of active workers (as asked)
- Notify workers that work is ready to be done
- [the requisite # workers would then start up
- and do the task]
- Wait on the monitor until either
- all work is completed or the task has yielded
- -- this is normally done through
- yielded + completed == active
- [completed workers are rest to idle state by overseer?]
- return appropriate status to caller
-
-Overseer invokes continue_task(*task),
- Lock gang monitor
- Check that task is the same as current binding
- If not, abort with an error
- Else, set the number of active workers as requested?
- Notify workers that they can continue from yield points
- New workers can also start up as required
- while satisfying the constraint that
- active + yielded does not exceed required number
- Wait (as above).
-
-NOTE: In the above, for simplicity in a first iteration
- our gangs will be of fixed population and will not
- therefore be flexible work gangs, just yielding work
- gangs. Once this works well, we will in a second
- iteration.refinement introduce flexibility into
- the work gang.
-
-NOTE: we can always create a new gang per each iteration
- in order to get the flexibility, but we will for now
- desist that simplified route.
-
- */
-/////////////////////
-void YieldingFlexibleWorkGang::start_task(YieldingFlexibleGangTask* new_task) {
- MutexLocker ml(monitor(), Mutex::_no_safepoint_check_flag);
- assert(task() == NULL, "Gang currently tied to a task");
- assert(new_task != NULL, "Null task");
- // Bind task to gang
- _task = new_task;
- new_task->set_gang(this); // Establish 2-way binding to support yielding
- _sequence_number++;
-
- uint requested_size = new_task->requested_size();
- if (requested_size != 0) {
- _active_workers = MIN2(requested_size, total_workers());
- } else {
- _active_workers = active_workers();
- }
- new_task->set_actual_size(_active_workers);
- new_task->set_for_termination(_active_workers);
-
- assert(_started_workers == 0, "Tabula rasa non");
- assert(_finished_workers == 0, "Tabula rasa non");
- assert(_yielded_workers == 0, "Tabula rasa non");
- yielding_task()->set_status(ACTIVE);
-
- // Wake up all the workers, the first few will get to work,
- // and the rest will go back to sleep
- monitor()->notify_all();
- wait_for_gang();
-}
-
-void YieldingFlexibleWorkGang::wait_for_gang() {
-
- assert(monitor()->owned_by_self(), "Data race");
- // Wait for task to complete or yield
- for (Status status = yielding_task()->status();
- status != COMPLETED && status != YIELDED && status != ABORTED;
- status = yielding_task()->status()) {
- assert(started_workers() <= active_workers(), "invariant");
- assert(finished_workers() <= active_workers(), "invariant");
- assert(yielded_workers() <= active_workers(), "invariant");
- monitor()->wait_without_safepoint_check();
- }
- switch (yielding_task()->status()) {
- case COMPLETED:
- case ABORTED: {
- assert(finished_workers() == active_workers(), "Inconsistent status");
- assert(yielded_workers() == 0, "Invariant");
- reset(); // for next task; gang<->task binding released
- break;
- }
- case YIELDED: {
- assert(yielded_workers() > 0, "Invariant");
- assert(yielded_workers() + finished_workers() == active_workers(),
- "Inconsistent counts");
- break;
- }
- case ACTIVE:
- case INACTIVE:
- case COMPLETING:
- case YIELDING:
- case ABORTING:
- default:
- ShouldNotReachHere();
- }
-}
-
-void YieldingFlexibleWorkGang::continue_task(
- YieldingFlexibleGangTask* gang_task) {
-
- MutexLocker ml(monitor(), Mutex::_no_safepoint_check_flag);
- assert(task() != NULL && task() == gang_task, "Incorrect usage");
- assert(_started_workers == _active_workers, "Precondition");
- assert(_yielded_workers > 0 && yielding_task()->status() == YIELDED,
- "Else why are we calling continue_task()");
- // Restart the yielded gang workers
- yielding_task()->set_status(ACTIVE);
- monitor()->notify_all();
- wait_for_gang();
-}
-
-void YieldingFlexibleWorkGang::reset() {
- _started_workers = 0;
- _finished_workers = 0;
- yielding_task()->set_gang(NULL);
- _task = NULL; // unbind gang from task
-}
-
-void YieldingFlexibleWorkGang::yield() {
- assert(task() != NULL, "Inconsistency; should have task binding");
- MutexLocker ml(monitor(), Mutex::_no_safepoint_check_flag);
- assert(yielded_workers() < active_workers(), "Consistency check");
- if (yielding_task()->status() == ABORTING) {
- // Do not yield; we need to abort as soon as possible
- // XXX NOTE: This can cause a performance pathology in the
- // current implementation in Mustang, as of today, and
- // pre-Mustang in that as soon as an overflow occurs,
- // yields will not be honoured. The right way to proceed
- // of course is to fix bug # TBF, so that abort's cause
- // us to return at each potential yield point.
- return;
- }
- if (++_yielded_workers + finished_workers() == active_workers()) {
- yielding_task()->set_status(YIELDED);
- monitor()->notify_all();
- } else {
- yielding_task()->set_status(YIELDING);
- }
-
- while (true) {
- switch (yielding_task()->status()) {
- case YIELDING:
- case YIELDED: {
- monitor()->wait_without_safepoint_check();
- break; // from switch
- }
- case ACTIVE:
- case ABORTING:
- case COMPLETING: {
- assert(_yielded_workers > 0, "Else why am i here?");
- _yielded_workers--;
- return;
- }
- case INACTIVE:
- case ABORTED:
- case COMPLETED:
- default: {
- ShouldNotReachHere();
- }
- }
- }
- // Only return is from inside switch statement above
- ShouldNotReachHere();
-}
-
-void YieldingFlexibleWorkGang::abort() {
- assert(task() != NULL, "Inconsistency; should have task binding");
- MutexLocker ml(monitor(), Mutex::_no_safepoint_check_flag);
- assert(yielded_workers() < active_workers(), "Consistency check");
- #ifndef PRODUCT
- switch (yielding_task()->status()) {
- // allowed states
- case ACTIVE:
- case ABORTING:
- case COMPLETING:
- case YIELDING:
- break;
- // not allowed states
- case INACTIVE:
- case ABORTED:
- case COMPLETED:
- case YIELDED:
- default:
- ShouldNotReachHere();
- }
- #endif // !PRODUCT
- Status prev_status = yielding_task()->status();
- yielding_task()->set_status(ABORTING);
- if (prev_status == YIELDING) {
- assert(yielded_workers() > 0, "Inconsistency");
- // At least one thread has yielded, wake it up
- // so it can go back to waiting stations ASAP.
- monitor()->notify_all();
- }
-}
-
-///////////////////////////////
-// YieldingFlexibleGangTask
-///////////////////////////////
-void YieldingFlexibleGangTask::yield() {
- assert(gang() != NULL, "No gang to signal");
- gang()->yield();
-}
-
-void YieldingFlexibleGangTask::abort() {
- assert(gang() != NULL, "No gang to signal");
- gang()->abort();
-}
-
-///////////////////////////////
-// YieldingFlexibleGangWorker
-///////////////////////////////
-void YieldingFlexibleGangWorker::loop() {
- int previous_sequence_number = 0;
- Monitor* gang_monitor = yf_gang()->monitor();
- MutexLocker ml(gang_monitor, Mutex::_no_safepoint_check_flag);
- YieldingWorkData data;
- int id;
- while (true) {
- // Check if there is work to do.
- yf_gang()->internal_worker_poll(&data);
- if (data.task() != NULL && data.sequence_number() != previous_sequence_number) {
- // There is work to be done.
- // First check if we need to become active or if there
- // are already the requisite number of workers
- if (yf_gang()->started_workers() == yf_gang()->active_workers()) {
- // There are already enough workers, we do not need to
- // to run; fall through and wait on monitor.
- } else {
- // We need to pitch in and do the work.
- assert(yf_gang()->started_workers() < yf_gang()->active_workers(),
- "Unexpected state");
- id = yf_gang()->started_workers();
- yf_gang()->internal_note_start();
- // Now, release the gang mutex and do the work.
- {
- MutexUnlocker mul(gang_monitor, Mutex::_no_safepoint_check_flag);
- GCIdMark gc_id_mark(data.task()->gc_id());
- data.task()->work(id); // This might include yielding
- }
- // Reacquire monitor and note completion of this worker
- yf_gang()->internal_note_finish();
- // Update status of task based on whether all workers have
- // finished or some have yielded
- assert(data.task() == yf_gang()->task(), "Confused task binding");
- if (yf_gang()->finished_workers() == yf_gang()->active_workers()) {
- switch (data.yf_task()->status()) {
- case ABORTING: {
- data.yf_task()->set_status(ABORTED);
- break;
- }
- case ACTIVE:
- case COMPLETING: {
- data.yf_task()->set_status(COMPLETED);
- break;
- }
- default:
- ShouldNotReachHere();
- }
- gang_monitor->notify_all(); // Notify overseer
- } else { // at least one worker is still working or yielded
- assert(yf_gang()->finished_workers() < yf_gang()->active_workers(),
- "Counts inconsistent");
- switch (data.yf_task()->status()) {
- case ACTIVE: {
- // first, but not only thread to complete
- data.yf_task()->set_status(COMPLETING);
- break;
- }
- case YIELDING: {
- if (yf_gang()->finished_workers() + yf_gang()->yielded_workers()
- == yf_gang()->active_workers()) {
- data.yf_task()->set_status(YIELDED);
- gang_monitor->notify_all(); // notify overseer
- }
- break;
- }
- case ABORTING:
- case COMPLETING: {
- break; // nothing to do
- }
- default: // everything else: INACTIVE, YIELDED, ABORTED, COMPLETED
- ShouldNotReachHere();
- }
- }
- }
- }
- // Remember the sequence number
- previous_sequence_number = data.sequence_number();
- // Wait for more work
- gang_monitor->wait_without_safepoint_check();
- }
-}
--- a/src/hotspot/share/gc/cms/yieldingWorkgroup.hpp Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,272 +0,0 @@
-/*
- * Copyright (c) 2005, 2019, 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_GC_CMS_YIELDINGWORKGROUP_HPP
-#define SHARE_GC_CMS_YIELDINGWORKGROUP_HPP
-
-#include "gc/shared/workgroup.hpp"
-#include "utilities/macros.hpp"
-
-// Forward declarations
-class YieldingFlexibleGangTask;
-class YieldingFlexibleWorkGang;
-
-// Status of tasks
-enum Status {
- INACTIVE,
- ACTIVE,
- YIELDING,
- YIELDED,
- ABORTING,
- ABORTED,
- COMPLETING,
- COMPLETED
-};
-
-class YieldingWorkData: public StackObj {
- // This would be a struct, but I want accessor methods.
-private:
- AbstractGangTask* _task;
- int _sequence_number;
-public:
- // Constructor and destructor
- YieldingWorkData() : _task(NULL), _sequence_number(0) {}
- ~YieldingWorkData() {}
-
- // Accessors and modifiers
- AbstractGangTask* task() const { return _task; }
- void set_task(AbstractGangTask* value) { _task = value; }
- int sequence_number() const { return _sequence_number; }
- void set_sequence_number(int value) { _sequence_number = value; }
-
- YieldingFlexibleGangTask* yf_task() const {
- return (YieldingFlexibleGangTask*)_task;
- }
-};
-
-// Class YieldingFlexibleGangWorker:
-// Several instances of this class run in parallel as workers for a gang.
-class YieldingFlexibleGangWorker: public AbstractGangWorker {
-public:
- YieldingFlexibleGangWorker(YieldingFlexibleWorkGang* gang, int id);
-
-public:
- YieldingFlexibleWorkGang* yf_gang() const
- { return (YieldingFlexibleWorkGang*)gang(); }
-
-protected: // Override from parent class
- virtual void loop();
-};
-
-class FlexibleGangTask: public AbstractGangTask {
- int _actual_size; // size of gang obtained
-protected:
- int _requested_size; // size of gang requested
-public:
- FlexibleGangTask(const char* name): AbstractGangTask(name),
- _requested_size(0) {}
-
- // The abstract work method.
- // The argument tells you which member of the gang you are.
- virtual void work(uint worker_id) = 0;
-
- int requested_size() const { return _requested_size; }
- int actual_size() const { return _actual_size; }
-
- void set_requested_size(int sz) { _requested_size = sz; }
- void set_actual_size(int sz) { _actual_size = sz; }
-};
-
-// An abstract task to be worked on by a flexible work gang,
-// and where the workers will periodically yield, usually
-// in response to some condition that is signalled by means
-// that are specific to the task at hand.
-// You subclass this to supply your own work() method.
-// A second feature of this kind of work gang is that
-// it allows for the signalling of certain exceptional
-// conditions that may be encountered during the performance
-// of the task and that may require the task at hand to be
-// `aborted' forthwith. Finally, these gangs are `flexible'
-// in that they can operate at partial capacity with some
-// gang workers waiting on the bench; in other words, the
-// size of the active worker pool can flex (up to an apriori
-// maximum) in response to task requests at certain points.
-// The last part (the flexible part) has not yet been fully
-// fleshed out and is a work in progress.
-class YieldingFlexibleGangTask: public FlexibleGangTask {
- Status _status;
- YieldingFlexibleWorkGang* _gang;
-
-protected:
- // Constructor and desctructor: only construct subclasses.
- YieldingFlexibleGangTask(const char* name): FlexibleGangTask(name),
- _status(INACTIVE),
- _gang(NULL) { }
-
- ~YieldingFlexibleGangTask() { }
-
- friend class YieldingFlexibleWorkGang;
- friend class YieldingFlexibleGangWorker;
-
- void set_status(Status s) {
- _status = s;
- }
- YieldingFlexibleWorkGang* gang() {
- return _gang;
- }
- void set_gang(YieldingFlexibleWorkGang* gang) {
- assert(_gang == NULL || gang == NULL, "Clobber without intermediate reset?");
- _gang = gang;
- }
-
-public:
- // The abstract work method.
- // The argument tells you which member of the gang you are.
- virtual void work(uint worker_id) = 0;
-
- // Subclasses should call the parent's yield() method
- // after having done any work specific to the subclass.
- virtual void yield();
-
- // An abstract method supplied by
- // a concrete sub-class which is used by the coordinator
- // to do any "central yielding" work.
- virtual void coordinator_yield() = 0;
-
- // Subclasses should call the parent's abort() method
- // after having done any work specific to the sunbclass.
- virtual void abort();
-
- Status status() const { return _status; }
- bool yielding() const { return _status == YIELDING; }
- bool yielded() const { return _status == YIELDED; }
- bool completed() const { return _status == COMPLETED; }
- bool aborted() const { return _status == ABORTED; }
- bool active() const { return _status == ACTIVE; }
-
- // This method configures the task for proper termination.
- // Some tasks do not have any requirements on termination
- // and may inherit this method that does nothing. Some
- // tasks do some coordination on termination and override
- // this method to implement that coordination.
- virtual void set_for_termination(uint active_workers) {}
-};
-// Class YieldingWorkGang: A subclass of WorkGang.
-// In particular, a YieldingWorkGang is made up of
-// YieldingGangWorkers, and provides infrastructure
-// supporting yielding to the "GangOverseer",
-// being the thread that orchestrates the WorkGang via run_task().
-class YieldingFlexibleWorkGang: public AbstractWorkGang {
- // Here's the public interface to this class.
-public:
- // Constructor and destructor.
- YieldingFlexibleWorkGang(const char* name, uint workers,
- bool are_GC_task_threads);
-
- YieldingFlexibleGangTask* yielding_task() const {
- return task();
- }
- // Allocate a worker and return a pointer to it.
- AbstractGangWorker* allocate_worker(uint which);
-
- // Run a task; returns when the task is done, or the workers yield,
- // or the task is aborted.
- // A task that has been yielded can be continued via this same interface
- // by using the same task repeatedly as the argument to the call.
- // It is expected that the YieldingFlexibleGangTask carries the appropriate
- // continuation information used by workers to continue the task
- // from its last yield point. Thus, a completed task will return
- // immediately with no actual work having been done by the workers.
- void run_task(AbstractGangTask* task) {
- guarantee(false, "Use start_task instead");
- }
- void start_task(YieldingFlexibleGangTask* new_task);
- void continue_task(YieldingFlexibleGangTask* gang_task);
-
- // Abort a currently running task, if any; returns when all the workers
- // have stopped working on the current task and have returned to their
- // waiting stations.
- void abort_task();
-
- // Yield: workers wait at their current working stations
- // until signalled to proceed by the overseer.
- void yield();
-
- // Abort: workers are expected to return to their waiting
- // stations, whence they are ready for the next task dispatched
- // by the overseer.
- void abort();
-
-private:
- uint _yielded_workers;
- void wait_for_gang();
-
-public:
- // Accessors for fields
- uint yielded_workers() const {
- return _yielded_workers;
- }
-
-private:
- friend class YieldingFlexibleGangWorker;
- void reset(); // NYI
-
-
- // The monitor which protects these data,
- // and notifies of changes in it.
- Monitor* _monitor;
- // Accessors for fields
- Monitor* monitor() const {
- return _monitor;
- }
-
- // The number of started workers.
- uint _started_workers;
- // The number of finished workers.
- uint _finished_workers;
-
- uint started_workers() const {
- return _started_workers;
- }
- uint finished_workers() const {
- return _finished_workers;
- }
-
- // A sequence number for the current task.
- int _sequence_number;
- int sequence_number() const {
- return _sequence_number;
- }
-
- YieldingFlexibleGangTask* _task;
- YieldingFlexibleGangTask* task() const {
- return _task;
- }
-
- void internal_worker_poll(YieldingWorkData* data) const;
- void internal_note_start();
- void internal_note_finish();
-};
-
-#endif // SHARE_GC_CMS_YIELDINGWORKGROUP_HPP
--- a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -298,11 +298,7 @@
*
* In the case of slow allocation the allocation code must handle the barrier
* as part of the allocation in the case the allocated object is not located
- * in the nursery, this would happen for humongous objects. This is similar to
- * how CMS is required to handle this case, see the comments for the method
- * CollectedHeap::new_deferred_store_barrier and OptoRuntime::new_deferred_store_barrier.
- * A deferred card mark is required for these objects and handled in the above
- * mentioned methods.
+ * in the nursery; this would happen for humongous objects.
*
* Returns true if the post barrier can be removed
*/
--- a/src/hotspot/share/gc/g1/g1AllocRegion.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1AllocRegion.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -250,17 +250,19 @@
#endif // PRODUCT
G1AllocRegion::G1AllocRegion(const char* name,
- bool bot_updates)
+ bool bot_updates,
+ uint node_index)
: _alloc_region(NULL),
_count(0),
_used_bytes_before(0),
_bot_updates(bot_updates),
- _name(name)
+ _name(name),
+ _node_index(node_index)
{ }
HeapRegion* MutatorAllocRegion::allocate_new_region(size_t word_size,
bool force) {
- return _g1h->new_mutator_alloc_region(word_size, force);
+ return _g1h->new_mutator_alloc_region(word_size, force, _node_index);
}
void MutatorAllocRegion::retire_region(HeapRegion* alloc_region,
@@ -347,7 +349,7 @@
HeapRegion* G1GCAllocRegion::allocate_new_region(size_t word_size,
bool force) {
assert(!force, "not supported for GC alloc regions");
- return _g1h->new_gc_alloc_region(word_size, _purpose);
+ return _g1h->new_gc_alloc_region(word_size, _purpose, _node_index);
}
void G1GCAllocRegion::retire_region(HeapRegion* alloc_region,
--- a/src/hotspot/share/gc/g1/g1AllocRegion.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1AllocRegion.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -28,6 +28,7 @@
#include "gc/g1/heapRegion.hpp"
#include "gc/g1/g1EvacStats.hpp"
#include "gc/g1/g1HeapRegionAttr.hpp"
+#include "gc/g1/g1NUMA.hpp"
class G1CollectedHeap;
@@ -38,7 +39,7 @@
// and a lock will need to be taken when the active region needs to be
// replaced.
-class G1AllocRegion {
+class G1AllocRegion : public CHeapObj<mtGC> {
private:
// The active allocating region we are currently allocating out
@@ -91,6 +92,9 @@
HeapWord* new_alloc_region_and_allocate(size_t word_size, bool force);
protected:
+ // The memory node index this allocation region belongs to.
+ uint _node_index;
+
// Reset the alloc region to point a the dummy region.
void reset_alloc_region();
@@ -131,7 +135,7 @@
virtual void retire_region(HeapRegion* alloc_region,
size_t allocated_bytes) = 0;
- G1AllocRegion(const char* name, bool bot_updates);
+ G1AllocRegion(const char* name, bool bot_updates, uint node_index);
public:
static void setup(G1CollectedHeap* g1h, HeapRegion* dummy_region);
@@ -220,8 +224,8 @@
virtual void retire_region(HeapRegion* alloc_region, size_t allocated_bytes);
virtual size_t retire(bool fill_up);
public:
- MutatorAllocRegion()
- : G1AllocRegion("Mutator Alloc Region", false /* bot_updates */),
+ MutatorAllocRegion(uint node_index)
+ : G1AllocRegion("Mutator Alloc Region", false /* bot_updates */, node_index),
_wasted_bytes(0),
_retained_alloc_region(NULL) { }
@@ -245,6 +249,7 @@
virtual void init();
};
+
// Common base class for allocation regions used during GC.
class G1GCAllocRegion : public G1AllocRegion {
protected:
@@ -256,16 +261,17 @@
virtual size_t retire(bool fill_up);
- G1GCAllocRegion(const char* name, bool bot_updates, G1EvacStats* stats, G1HeapRegionAttr::region_type_t purpose)
- : G1AllocRegion(name, bot_updates), _stats(stats), _purpose(purpose) {
+ G1GCAllocRegion(const char* name, bool bot_updates, G1EvacStats* stats,
+ G1HeapRegionAttr::region_type_t purpose, uint node_index = G1NUMA::AnyNodeIndex)
+ : G1AllocRegion(name, bot_updates, node_index), _stats(stats), _purpose(purpose) {
assert(stats != NULL, "Must pass non-NULL PLAB statistics");
}
};
class SurvivorGCAllocRegion : public G1GCAllocRegion {
public:
- SurvivorGCAllocRegion(G1EvacStats* stats)
- : G1GCAllocRegion("Survivor GC Alloc Region", false /* bot_updates */, stats, G1HeapRegionAttr::Young) { }
+ SurvivorGCAllocRegion(G1EvacStats* stats, uint node_index)
+ : G1GCAllocRegion("Survivor GC Alloc Region", false /* bot_updates */, stats, G1HeapRegionAttr::Young, node_index) { }
};
class OldGCAllocRegion : public G1GCAllocRegion {
--- a/src/hotspot/share/gc/g1/g1Allocator.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1Allocator.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -28,6 +28,7 @@
#include "gc/g1/g1EvacStats.inline.hpp"
#include "gc/g1/g1EvacuationInfo.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
+#include "gc/g1/g1NUMA.hpp"
#include "gc/g1/g1Policy.hpp"
#include "gc/g1/heapRegion.inline.hpp"
#include "gc/g1/heapRegionSet.inline.hpp"
@@ -36,22 +37,53 @@
G1Allocator::G1Allocator(G1CollectedHeap* heap) :
_g1h(heap),
+ _numa(heap->numa()),
_survivor_is_full(false),
_old_is_full(false),
- _mutator_alloc_region(),
- _survivor_gc_alloc_region(heap->alloc_buffer_stats(G1HeapRegionAttr::Young)),
+ _num_alloc_regions(_numa->num_active_nodes()),
+ _mutator_alloc_regions(NULL),
+ _survivor_gc_alloc_regions(NULL),
_old_gc_alloc_region(heap->alloc_buffer_stats(G1HeapRegionAttr::Old)),
_retained_old_gc_alloc_region(NULL) {
+
+ _mutator_alloc_regions = NEW_C_HEAP_ARRAY(MutatorAllocRegion, _num_alloc_regions, mtGC);
+ _survivor_gc_alloc_regions = NEW_C_HEAP_ARRAY(SurvivorGCAllocRegion, _num_alloc_regions, mtGC);
+ G1EvacStats* stat = heap->alloc_buffer_stats(G1HeapRegionAttr::Young);
+
+ for (uint i = 0; i < _num_alloc_regions; i++) {
+ ::new(_mutator_alloc_regions + i) MutatorAllocRegion(i);
+ ::new(_survivor_gc_alloc_regions + i) SurvivorGCAllocRegion(stat, i);
+ }
}
-void G1Allocator::init_mutator_alloc_region() {
- assert(_mutator_alloc_region.get() == NULL, "pre-condition");
- _mutator_alloc_region.init();
+G1Allocator::~G1Allocator() {
+ for (uint i = 0; i < _num_alloc_regions; i++) {
+ _mutator_alloc_regions[i].~MutatorAllocRegion();
+ _survivor_gc_alloc_regions[i].~SurvivorGCAllocRegion();
+ }
+ FREE_C_HEAP_ARRAY(MutatorAllocRegion, _mutator_alloc_regions);
+ FREE_C_HEAP_ARRAY(SurvivorGCAllocRegion, _survivor_gc_alloc_regions);
}
-void G1Allocator::release_mutator_alloc_region() {
- _mutator_alloc_region.release();
- assert(_mutator_alloc_region.get() == NULL, "post-condition");
+#ifdef ASSERT
+bool G1Allocator::has_mutator_alloc_region() {
+ uint node_index = current_node_index();
+ return mutator_alloc_region(node_index)->get() != NULL;
+}
+#endif
+
+void G1Allocator::init_mutator_alloc_regions() {
+ for (uint i = 0; i < _num_alloc_regions; i++) {
+ assert(mutator_alloc_region(i)->get() == NULL, "pre-condition");
+ mutator_alloc_region(i)->init();
+ }
+}
+
+void G1Allocator::release_mutator_alloc_regions() {
+ for (uint i = 0; i < _num_alloc_regions; i++) {
+ mutator_alloc_region(i)->release();
+ assert(mutator_alloc_region(i)->get() == NULL, "post-condition");
+ }
}
bool G1Allocator::is_retained_old_region(HeapRegion* hr) {
@@ -97,7 +129,10 @@
_survivor_is_full = false;
_old_is_full = false;
- _survivor_gc_alloc_region.init();
+ for (uint i = 0; i < _num_alloc_regions; i++) {
+ survivor_gc_alloc_region(i)->init();
+ }
+
_old_gc_alloc_region.init();
reuse_retained_old_region(evacuation_info,
&_old_gc_alloc_region,
@@ -105,9 +140,14 @@
}
void G1Allocator::release_gc_alloc_regions(G1EvacuationInfo& evacuation_info) {
- evacuation_info.set_allocation_regions(survivor_gc_alloc_region()->count() +
+ uint survivor_region_count = 0;
+ for (uint node_index = 0; node_index < _num_alloc_regions; node_index++) {
+ survivor_region_count += survivor_gc_alloc_region(node_index)->count();
+ survivor_gc_alloc_region(node_index)->release();
+ }
+ evacuation_info.set_allocation_regions(survivor_region_count +
old_gc_alloc_region()->count());
- survivor_gc_alloc_region()->release();
+
// If we have an old GC alloc region to release, we'll save it in
// _retained_old_gc_alloc_region. If we don't
// _retained_old_gc_alloc_region will become NULL. This is what we
@@ -117,7 +157,9 @@
}
void G1Allocator::abandon_gc_alloc_regions() {
- assert(survivor_gc_alloc_region()->get() == NULL, "pre-condition");
+ for (uint i = 0; i < _num_alloc_regions; i++) {
+ assert(survivor_gc_alloc_region(i)->get() == NULL, "pre-condition");
+ }
assert(old_gc_alloc_region()->get() == NULL, "pre-condition");
_retained_old_gc_alloc_region = NULL;
}
@@ -146,7 +188,8 @@
// since we can't allow tlabs to grow big enough to accommodate
// humongous objects.
- HeapRegion* hr = mutator_alloc_region()->get();
+ uint node_index = current_node_index();
+ HeapRegion* hr = mutator_alloc_region(node_index)->get();
size_t max_tlab = _g1h->max_tlab_size() * wordSize;
if (hr == NULL) {
return max_tlab;
@@ -157,14 +200,19 @@
size_t G1Allocator::used_in_alloc_regions() {
assert(Heap_lock->owner() != NULL, "Should be owned on this thread's behalf.");
- return mutator_alloc_region()->used_in_alloc_regions();
+ size_t used = 0;
+ for (uint i = 0; i < _num_alloc_regions; i++) {
+ used += mutator_alloc_region(i)->used_in_alloc_regions();
+ }
+ return used;
}
HeapWord* G1Allocator::par_allocate_during_gc(G1HeapRegionAttr dest,
- size_t word_size) {
+ size_t word_size,
+ uint node_index) {
size_t temp = 0;
- HeapWord* result = par_allocate_during_gc(dest, word_size, word_size, &temp);
+ HeapWord* result = par_allocate_during_gc(dest, word_size, word_size, &temp, node_index);
assert(result == NULL || temp == word_size,
"Requested " SIZE_FORMAT " words, but got " SIZE_FORMAT " at " PTR_FORMAT,
word_size, temp, p2i(result));
@@ -174,10 +222,11 @@
HeapWord* G1Allocator::par_allocate_during_gc(G1HeapRegionAttr dest,
size_t min_word_size,
size_t desired_word_size,
- size_t* actual_word_size) {
+ size_t* actual_word_size,
+ uint node_index) {
switch (dest.type()) {
case G1HeapRegionAttr::Young:
- return survivor_attempt_allocation(min_word_size, desired_word_size, actual_word_size);
+ return survivor_attempt_allocation(min_word_size, desired_word_size, actual_word_size, node_index);
case G1HeapRegionAttr::Old:
return old_attempt_allocation(min_word_size, desired_word_size, actual_word_size);
default:
@@ -188,18 +237,19 @@
HeapWord* G1Allocator::survivor_attempt_allocation(size_t min_word_size,
size_t desired_word_size,
- size_t* actual_word_size) {
+ size_t* actual_word_size,
+ uint node_index) {
assert(!_g1h->is_humongous(desired_word_size),
"we should not be seeing humongous-size allocations in this path");
- HeapWord* result = survivor_gc_alloc_region()->attempt_allocation(min_word_size,
- desired_word_size,
- actual_word_size);
+ HeapWord* result = survivor_gc_alloc_region(node_index)->attempt_allocation(min_word_size,
+ desired_word_size,
+ actual_word_size);
if (result == NULL && !survivor_is_full()) {
MutexLocker x(FreeList_lock, Mutex::_no_safepoint_check_flag);
- result = survivor_gc_alloc_region()->attempt_allocation_locked(min_word_size,
- desired_word_size,
- actual_word_size);
+ result = survivor_gc_alloc_region(node_index)->attempt_allocation_locked(min_word_size,
+ desired_word_size,
+ actual_word_size);
if (result == NULL) {
set_survivor_full();
}
@@ -246,15 +296,25 @@
G1PLABAllocator::G1PLABAllocator(G1Allocator* allocator) :
_g1h(G1CollectedHeap::heap()),
_allocator(allocator),
- _surviving_alloc_buffer(_g1h->desired_plab_sz(G1HeapRegionAttr::Young)),
- _tenured_alloc_buffer(_g1h->desired_plab_sz(G1HeapRegionAttr::Old)),
_survivor_alignment_bytes(calc_survivor_alignment_bytes()) {
- for (uint state = 0; state < G1HeapRegionAttr::Num; state++) {
+ for (region_type_t state = 0; state < G1HeapRegionAttr::Num; state++) {
_direct_allocated[state] = 0;
- _alloc_buffers[state] = NULL;
+ uint length = alloc_buffers_length(state);
+ _alloc_buffers[state] = NEW_C_HEAP_ARRAY(PLAB*, length, mtGC);
+ for (uint node_index = 0; node_index < length; node_index++) {
+ _alloc_buffers[state][node_index] = new PLAB(_g1h->desired_plab_sz(state));
+ }
}
- _alloc_buffers[G1HeapRegionAttr::Young] = &_surviving_alloc_buffer;
- _alloc_buffers[G1HeapRegionAttr::Old] = &_tenured_alloc_buffer;
+}
+
+G1PLABAllocator::~G1PLABAllocator() {
+ for (region_type_t state = 0; state < G1HeapRegionAttr::Num; state++) {
+ uint length = alloc_buffers_length(state);
+ for (uint node_index = 0; node_index < length; node_index++) {
+ delete _alloc_buffers[state][node_index];
+ }
+ FREE_C_HEAP_ARRAY(PLAB*, _alloc_buffers[state]);
+ }
}
bool G1PLABAllocator::may_throw_away_buffer(size_t const allocation_word_sz, size_t const buffer_size) const {
@@ -263,7 +323,8 @@
HeapWord* G1PLABAllocator::allocate_direct_or_new_plab(G1HeapRegionAttr dest,
size_t word_sz,
- bool* plab_refill_failed) {
+ bool* plab_refill_failed,
+ uint node_index) {
size_t plab_word_size = _g1h->desired_plab_sz(dest);
size_t required_in_plab = PLAB::size_required_for_allocation(word_sz);
@@ -272,14 +333,15 @@
if ((required_in_plab <= plab_word_size) &&
may_throw_away_buffer(required_in_plab, plab_word_size)) {
- PLAB* alloc_buf = alloc_buffer(dest);
+ PLAB* alloc_buf = alloc_buffer(dest, node_index);
alloc_buf->retire();
size_t actual_plab_size = 0;
HeapWord* buf = _allocator->par_allocate_during_gc(dest,
required_in_plab,
plab_word_size,
- &actual_plab_size);
+ &actual_plab_size,
+ node_index);
assert(buf == NULL || ((actual_plab_size >= required_in_plab) && (actual_plab_size <= plab_word_size)),
"Requested at minimum " SIZE_FORMAT ", desired " SIZE_FORMAT " words, but got " SIZE_FORMAT " at " PTR_FORMAT,
@@ -298,35 +360,39 @@
*plab_refill_failed = true;
}
// Try direct allocation.
- HeapWord* result = _allocator->par_allocate_during_gc(dest, word_sz);
+ HeapWord* result = _allocator->par_allocate_during_gc(dest, word_sz, node_index);
if (result != NULL) {
_direct_allocated[dest.type()] += word_sz;
}
return result;
}
-void G1PLABAllocator::undo_allocation(G1HeapRegionAttr dest, HeapWord* obj, size_t word_sz) {
- alloc_buffer(dest)->undo_allocation(obj, word_sz);
+void G1PLABAllocator::undo_allocation(G1HeapRegionAttr dest, HeapWord* obj, size_t word_sz, uint node_index) {
+ alloc_buffer(dest, node_index)->undo_allocation(obj, word_sz);
}
void G1PLABAllocator::flush_and_retire_stats() {
- for (uint state = 0; state < G1HeapRegionAttr::Num; state++) {
- PLAB* const buf = _alloc_buffers[state];
- if (buf != NULL) {
- G1EvacStats* stats = _g1h->alloc_buffer_stats(state);
- buf->flush_and_retire_stats(stats);
- stats->add_direct_allocated(_direct_allocated[state]);
- _direct_allocated[state] = 0;
+ for (region_type_t state = 0; state < G1HeapRegionAttr::Num; state++) {
+ G1EvacStats* stats = _g1h->alloc_buffer_stats(state);
+ for (uint node_index = 0; node_index < alloc_buffers_length(state); node_index++) {
+ PLAB* const buf = alloc_buffer(state, node_index);
+ if (buf != NULL) {
+ buf->flush_and_retire_stats(stats);
+ }
}
+ stats->add_direct_allocated(_direct_allocated[state]);
+ _direct_allocated[state] = 0;
}
}
size_t G1PLABAllocator::waste() const {
size_t result = 0;
- for (uint state = 0; state < G1HeapRegionAttr::Num; state++) {
- PLAB * const buf = _alloc_buffers[state];
- if (buf != NULL) {
- result += buf->waste();
+ for (region_type_t state = 0; state < G1HeapRegionAttr::Num; state++) {
+ for (uint node_index = 0; node_index < alloc_buffers_length(state); node_index++) {
+ PLAB* const buf = alloc_buffer(state, node_index);
+ if (buf != NULL) {
+ result += buf->waste();
+ }
}
}
return result;
@@ -334,10 +400,12 @@
size_t G1PLABAllocator::undo_waste() const {
size_t result = 0;
- for (uint state = 0; state < G1HeapRegionAttr::Num; state++) {
- PLAB * const buf = _alloc_buffers[state];
- if (buf != NULL) {
- result += buf->undo_waste();
+ for (region_type_t state = 0; state < G1HeapRegionAttr::Num; state++) {
+ for (uint node_index = 0; node_index < alloc_buffers_length(state); node_index++) {
+ PLAB* const buf = alloc_buffer(state, node_index);
+ if (buf != NULL) {
+ result += buf->undo_waste();
+ }
}
}
return result;
--- a/src/hotspot/share/gc/g1/g1Allocator.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1Allocator.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -31,6 +31,7 @@
#include "gc/shared/plab.hpp"
class G1EvacuationInfo;
+class G1NUMA;
// Interface to keep track of which regions G1 is currently allocating into. Provides
// some accessors (e.g. allocating into them, or getting their occupancy).
@@ -40,16 +41,20 @@
private:
G1CollectedHeap* _g1h;
+ G1NUMA* _numa;
bool _survivor_is_full;
bool _old_is_full;
+ // The number of MutatorAllocRegions used, one per memory node.
+ size_t _num_alloc_regions;
+
// Alloc region used to satisfy mutator allocation requests.
- MutatorAllocRegion _mutator_alloc_region;
+ MutatorAllocRegion* _mutator_alloc_regions;
// Alloc region used to satisfy allocation requests by the GC for
// survivor objects.
- SurvivorGCAllocRegion _survivor_gc_alloc_region;
+ SurvivorGCAllocRegion* _survivor_gc_alloc_regions;
// Alloc region used to satisfy allocation requests by the GC for
// old objects.
@@ -68,29 +73,37 @@
HeapRegion** retained);
// Accessors to the allocation regions.
- inline MutatorAllocRegion* mutator_alloc_region();
- inline SurvivorGCAllocRegion* survivor_gc_alloc_region();
+ inline MutatorAllocRegion* mutator_alloc_region(uint node_index);
+ inline SurvivorGCAllocRegion* survivor_gc_alloc_region(uint node_index);
inline OldGCAllocRegion* old_gc_alloc_region();
// Allocation attempt during GC for a survivor object / PLAB.
HeapWord* survivor_attempt_allocation(size_t min_word_size,
- size_t desired_word_size,
- size_t* actual_word_size);
+ size_t desired_word_size,
+ size_t* actual_word_size,
+ uint node_index);
// Allocation attempt during GC for an old object / PLAB.
HeapWord* old_attempt_allocation(size_t min_word_size,
- size_t desired_word_size,
- size_t* actual_word_size);
+ size_t desired_word_size,
+ size_t* actual_word_size);
+
+ // Node index of current thread.
+ inline uint current_node_index() const;
+
public:
G1Allocator(G1CollectedHeap* heap);
+ ~G1Allocator();
+
+ uint num_nodes() { return (uint)_num_alloc_regions; }
#ifdef ASSERT
// Do we currently have an active mutator region to allocate into?
- bool has_mutator_alloc_region() { return mutator_alloc_region()->get() != NULL; }
+ bool has_mutator_alloc_region();
#endif
- void init_mutator_alloc_region();
- void release_mutator_alloc_region();
+ void init_mutator_alloc_regions();
+ void release_mutator_alloc_regions();
void init_gc_alloc_regions(G1EvacuationInfo& evacuation_info);
void release_gc_alloc_regions(G1EvacuationInfo& evacuation_info);
@@ -113,12 +126,14 @@
// heap, and then allocate a block of the given size. The block
// may not be a humongous - it must fit into a single heap region.
HeapWord* par_allocate_during_gc(G1HeapRegionAttr dest,
- size_t word_size);
+ size_t word_size,
+ uint node_index);
HeapWord* par_allocate_during_gc(G1HeapRegionAttr dest,
size_t min_word_size,
size_t desired_word_size,
- size_t* actual_word_size);
+ size_t* actual_word_size,
+ uint node_index);
};
// Manages the PLABs used during garbage collection. Interface for allocation from PLABs.
@@ -127,12 +142,12 @@
class G1PLABAllocator : public CHeapObj<mtGC> {
friend class G1ParScanThreadState;
private:
+ typedef G1HeapRegionAttr::region_type_t region_type_t;
+
G1CollectedHeap* _g1h;
G1Allocator* _allocator;
- PLAB _surviving_alloc_buffer;
- PLAB _tenured_alloc_buffer;
- PLAB* _alloc_buffers[G1HeapRegionAttr::Num];
+ PLAB** _alloc_buffers[G1HeapRegionAttr::Num];
// The survivor alignment in effect in bytes.
// == 0 : don't align survivors
@@ -145,7 +160,13 @@
size_t _direct_allocated[G1HeapRegionAttr::Num];
void flush_and_retire_stats();
- inline PLAB* alloc_buffer(G1HeapRegionAttr dest);
+ inline PLAB* alloc_buffer(G1HeapRegionAttr dest, uint node_index) const;
+ inline PLAB* alloc_buffer(region_type_t dest, uint node_index) const;
+
+ // Returns the number of allocation buffers for the given dest.
+ // There is only 1 buffer for Old while Young may have multiple buffers depending on
+ // active NUMA nodes.
+ inline uint alloc_buffers_length(region_type_t dest) const;
// Calculate the survivor space object alignment in bytes. Returns that or 0 if
// there are no restrictions on survivor alignment.
@@ -154,6 +175,7 @@
bool may_throw_away_buffer(size_t const allocation_word_sz, size_t const buffer_size) const;
public:
G1PLABAllocator(G1Allocator* allocator);
+ ~G1PLABAllocator();
size_t waste() const;
size_t undo_waste() const;
@@ -164,18 +186,21 @@
// PLAB failed or not.
HeapWord* allocate_direct_or_new_plab(G1HeapRegionAttr dest,
size_t word_sz,
- bool* plab_refill_failed);
+ bool* plab_refill_failed,
+ uint node_index);
// Allocate word_sz words in the PLAB of dest. Returns the address of the
// allocated memory, NULL if not successful.
inline HeapWord* plab_allocate(G1HeapRegionAttr dest,
- size_t word_sz);
+ size_t word_sz,
+ uint node_index);
inline HeapWord* allocate(G1HeapRegionAttr dest,
size_t word_sz,
- bool* refill_failed);
+ bool* refill_failed,
+ uint node_index);
- void undo_allocation(G1HeapRegionAttr dest, HeapWord* obj, size_t word_sz);
+ void undo_allocation(G1HeapRegionAttr dest, HeapWord* obj, size_t word_sz, uint node_index);
};
// G1ArchiveRegionMap is a boolean array used to mark G1 regions as
--- a/src/hotspot/share/gc/g1/g1Allocator.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1Allocator.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -30,12 +30,18 @@
#include "gc/shared/plab.inline.hpp"
#include "memory/universe.hpp"
-inline MutatorAllocRegion* G1Allocator::mutator_alloc_region() {
- return &_mutator_alloc_region;
+inline uint G1Allocator::current_node_index() const {
+ return _numa->index_of_current_thread();
}
-inline SurvivorGCAllocRegion* G1Allocator::survivor_gc_alloc_region() {
- return &_survivor_gc_alloc_region;
+inline MutatorAllocRegion* G1Allocator::mutator_alloc_region(uint node_index) {
+ assert(node_index < _num_alloc_regions, "Invalid index: %u", node_index);
+ return &_mutator_alloc_regions[node_index];
+}
+
+inline SurvivorGCAllocRegion* G1Allocator::survivor_gc_alloc_region(uint node_index) {
+ assert(node_index < _num_alloc_regions, "Invalid index: %u", node_index);
+ return &_survivor_gc_alloc_regions[node_index];
}
inline OldGCAllocRegion* G1Allocator::old_gc_alloc_region() {
@@ -45,35 +51,60 @@
inline HeapWord* G1Allocator::attempt_allocation(size_t min_word_size,
size_t desired_word_size,
size_t* actual_word_size) {
- HeapWord* result = mutator_alloc_region()->attempt_retained_allocation(min_word_size, desired_word_size, actual_word_size);
+ uint node_index = current_node_index();
+ HeapWord* result = mutator_alloc_region(node_index)->attempt_retained_allocation(min_word_size, desired_word_size, actual_word_size);
if (result != NULL) {
return result;
}
- return mutator_alloc_region()->attempt_allocation(min_word_size, desired_word_size, actual_word_size);
+ return mutator_alloc_region(node_index)->attempt_allocation(min_word_size, desired_word_size, actual_word_size);
}
inline HeapWord* G1Allocator::attempt_allocation_locked(size_t word_size) {
- HeapWord* result = mutator_alloc_region()->attempt_allocation_locked(word_size);
- assert(result != NULL || mutator_alloc_region()->get() == NULL,
- "Must not have a mutator alloc region if there is no memory, but is " PTR_FORMAT, p2i(mutator_alloc_region()->get()));
+ uint node_index = current_node_index();
+ HeapWord* result = mutator_alloc_region(node_index)->attempt_allocation_locked(word_size);
+ assert(result != NULL || mutator_alloc_region(node_index)->get() == NULL,
+ "Must not have a mutator alloc region if there is no memory, but is " PTR_FORMAT, p2i(mutator_alloc_region(node_index)->get()));
return result;
}
inline HeapWord* G1Allocator::attempt_allocation_force(size_t word_size) {
- return mutator_alloc_region()->attempt_allocation_force(word_size);
+ uint node_index = current_node_index();
+ return mutator_alloc_region(node_index)->attempt_allocation_force(word_size);
}
-inline PLAB* G1PLABAllocator::alloc_buffer(G1HeapRegionAttr dest) {
+inline PLAB* G1PLABAllocator::alloc_buffer(G1HeapRegionAttr dest, uint node_index) const {
assert(dest.is_valid(),
"Allocation buffer index out of bounds: %s", dest.get_type_str());
assert(_alloc_buffers[dest.type()] != NULL,
"Allocation buffer is NULL: %s", dest.get_type_str());
- return _alloc_buffers[dest.type()];
+ return alloc_buffer(dest.type(), node_index);
+}
+
+inline PLAB* G1PLABAllocator::alloc_buffer(region_type_t dest, uint node_index) const {
+ assert(dest < G1HeapRegionAttr::Num,
+ "Allocation buffer index out of bounds: %u", dest);
+
+ if (dest == G1HeapRegionAttr::Young) {
+ assert(node_index < alloc_buffers_length(dest),
+ "Allocation buffer index out of bounds: %u, %u", dest, node_index);
+ return _alloc_buffers[dest][node_index];
+ } else {
+ return _alloc_buffers[dest][0];
+ }
+}
+
+inline uint G1PLABAllocator::alloc_buffers_length(region_type_t dest) const {
+ if (dest == G1HeapRegionAttr::Young) {
+ return _allocator->num_nodes();
+ } else {
+ return 1;
+ }
}
inline HeapWord* G1PLABAllocator::plab_allocate(G1HeapRegionAttr dest,
- size_t word_sz) {
- PLAB* buffer = alloc_buffer(dest);
+ size_t word_sz,
+ uint node_index) {
+ PLAB* buffer = alloc_buffer(dest, node_index);
if (_survivor_alignment_bytes == 0 || !dest.is_young()) {
return buffer->allocate(word_sz);
} else {
@@ -83,12 +114,13 @@
inline HeapWord* G1PLABAllocator::allocate(G1HeapRegionAttr dest,
size_t word_sz,
- bool* refill_failed) {
- HeapWord* const obj = plab_allocate(dest, word_sz);
+ bool* refill_failed,
+ uint node_index) {
+ HeapWord* const obj = plab_allocate(dest, word_sz, node_index);
if (obj != NULL) {
return obj;
}
- return allocate_direct_or_new_plab(dest, word_sz, refill_failed);
+ return allocate_direct_or_new_plab(dest, word_sz, refill_failed, node_index);
}
// Create the maps which is used to identify archive objects.
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -169,12 +169,15 @@
// Private methods.
-HeapRegion* G1CollectedHeap::new_region(size_t word_size, HeapRegionType type, bool do_expand) {
+HeapRegion* G1CollectedHeap::new_region(size_t word_size,
+ HeapRegionType type,
+ bool do_expand,
+ uint node_index) {
assert(!is_humongous(word_size) || word_size <= HeapRegion::GrainWords,
"the only time we use this to allocate a humongous region is "
"when we are allocating a single humongous region");
- HeapRegion* res = _hrm->allocate_free_region(type);
+ HeapRegion* res = _hrm->allocate_free_region(type, node_index);
if (res == NULL && do_expand && _expand_heap_after_alloc_failure) {
// Currently, only attempts to allocate GC alloc regions set
@@ -186,12 +189,15 @@
log_debug(gc, ergo, heap)("Attempt heap expansion (region allocation request failed). Allocation request: " SIZE_FORMAT "B",
word_size * HeapWordSize);
- if (expand(word_size * HeapWordSize)) {
- // Given that expand() succeeded in expanding the heap, and we
+ assert(word_size * HeapWordSize < HeapRegion::GrainBytes,
+ "This kind of expansion should never be more than one region. Size: " SIZE_FORMAT,
+ word_size * HeapWordSize);
+ if (expand_single_region(node_index)) {
+ // Given that expand_single_region() succeeded in expanding the heap, and we
// always expand the heap by an amount aligned to the heap
// region size, the free list should in theory not be empty.
// In either case allocate_free_region() will check for NULL.
- res = _hrm->allocate_free_region(type);
+ res = _hrm->allocate_free_region(type, node_index);
} else {
_expand_heap_after_alloc_failure = false;
}
@@ -1020,7 +1026,7 @@
void G1CollectedHeap::prepare_heap_for_full_collection() {
// Make sure we'll choose a new allocation region afterwards.
- _allocator->release_mutator_alloc_region();
+ _allocator->release_mutator_alloc_regions();
_allocator->abandon_gc_alloc_regions();
// We may have added regions to the current incremental collection
@@ -1064,7 +1070,7 @@
// Start a new incremental collection set for the next pause
start_new_collection_set();
- _allocator->init_mutator_alloc_region();
+ _allocator->init_mutator_alloc_regions();
// Post collection state updates.
MetaspaceGC::compute_new_size();
@@ -1381,6 +1387,19 @@
return regions_to_expand > 0;
}
+bool G1CollectedHeap::expand_single_region(uint node_index) {
+ uint expanded_by = _hrm->expand_on_preferred_node(node_index);
+
+ if (expanded_by == 0) {
+ assert(is_maximal_no_gc(), "Should be no regions left, available: %u", _hrm->available());
+ log_debug(gc, ergo, heap)("Did not expand the heap (heap already fully expanded)");
+ return false;
+ }
+
+ policy()->record_new_heap_size(num_regions());
+ return true;
+}
+
void G1CollectedHeap::shrink_helper(size_t shrink_bytes) {
size_t aligned_shrink_bytes =
ReservedSpace::page_align_size_down(shrink_bytes);
@@ -1391,7 +1410,6 @@
uint num_regions_removed = _hrm->shrink_by(num_regions_to_remove);
size_t shrunk_bytes = num_regions_removed * HeapRegion::GrainBytes;
-
log_debug(gc, ergo, heap)("Shrink the heap. requested shrinking amount: " SIZE_FORMAT "B aligned shrinking amount: " SIZE_FORMAT "B attempted shrinking amount: " SIZE_FORMAT "B",
shrink_bytes, aligned_shrink_bytes, shrunk_bytes);
if (num_regions_removed > 0) {
@@ -1493,6 +1511,7 @@
_humongous_set("Humongous Region Set", new HumongousRegionSetChecker()),
_bot(NULL),
_listener(),
+ _numa(G1NUMA::create()),
_hrm(NULL),
_allocator(NULL),
_verifier(NULL),
@@ -1775,6 +1794,8 @@
}
_workers->initialize_workers();
+ _numa->set_region_info(HeapRegion::GrainBytes, page_size);
+
// Create the G1ConcurrentMark data structure and thread.
// (Must do this late, so that "max_regions" is defined.)
_cm = new G1ConcurrentMark(this, prev_bitmap_storage, next_bitmap_storage);
@@ -1822,7 +1843,7 @@
dummy_region->set_top(dummy_region->end());
G1AllocRegion::setup(this, dummy_region);
- _allocator->init_mutator_alloc_region();
+ _allocator->init_mutator_alloc_regions();
// Do create of the monitoring and management support so that
// values in the heap have been properly initialized.
@@ -2368,6 +2389,15 @@
st->print("%u survivors (" SIZE_FORMAT "K)", survivor_regions,
(size_t) survivor_regions * HeapRegion::GrainBytes / K);
st->cr();
+ if (_numa->is_enabled()) {
+ uint num_nodes = _numa->num_active_nodes();
+ st->print(" remaining free region(s) on each NUMA node: ");
+ const int* node_ids = _numa->node_ids();
+ for (uint node_index = 0; node_index < num_nodes; node_index++) {
+ st->print("%d=%u ", node_ids[node_index], _hrm->num_free_regions(node_index));
+ }
+ st->cr();
+ }
MetaspaceUtils::print_on(st);
}
@@ -2557,6 +2587,20 @@
// We have just completed a GC. Update the soft reference
// policy with the new heap occupancy
Universe::update_heap_info_at_gc();
+
+ // Print NUMA statistics.
+ _numa->print_statistics();
+}
+
+void G1CollectedHeap::verify_numa_regions(const char* desc) {
+ LogTarget(Trace, gc, heap, verify) lt;
+
+ if (lt.is_enabled()) {
+ LogStream ls(lt);
+ // Iterate all heap regions to print matching between preferred numa id and actual numa id.
+ G1NodeIndexCheckClosure cl(desc, _numa, &ls);
+ heap_region_iterate(&cl);
+ }
}
HeapWord* G1CollectedHeap::do_collection_pause(size_t word_size,
@@ -2866,6 +2910,7 @@
}
_verifier->verify_before_gc(type);
_verifier->check_bitmaps("GC Start");
+ verify_numa_regions("GC Start");
}
void G1CollectedHeap::verify_after_young_collection(G1HeapVerifier::G1VerifyType type) {
@@ -2876,6 +2921,7 @@
}
_verifier->verify_after_gc(type);
_verifier->check_bitmaps("GC End");
+ verify_numa_regions("GC End");
}
void G1CollectedHeap::expand_heap_after_young_collection(){
@@ -3005,7 +3051,7 @@
// Forget the current allocation region (we might even choose it to be part
// of the collection set!).
- _allocator->release_mutator_alloc_region();
+ _allocator->release_mutator_alloc_regions();
calculate_collection_set(evacuation_info, target_pause_time_ms);
@@ -3042,7 +3088,7 @@
allocate_dummy_regions();
- _allocator->init_mutator_alloc_region();
+ _allocator->init_mutator_alloc_regions();
expand_heap_after_young_collection();
@@ -4538,13 +4584,15 @@
// Methods for the mutator alloc region
HeapRegion* G1CollectedHeap::new_mutator_alloc_region(size_t word_size,
- bool force) {
+ bool force,
+ uint node_index) {
assert_heap_locked_or_at_safepoint(true /* should_be_vm_thread */);
bool should_allocate = policy()->should_allocate_mutator_region();
if (force || should_allocate) {
HeapRegion* new_alloc_region = new_region(word_size,
HeapRegionType::Eden,
- false /* do_expand */);
+ false /* do_expand */,
+ node_index);
if (new_alloc_region != NULL) {
set_region_short_lived_locked(new_alloc_region);
_hr_printer.alloc(new_alloc_region, !should_allocate);
@@ -4582,7 +4630,7 @@
}
}
-HeapRegion* G1CollectedHeap::new_gc_alloc_region(size_t word_size, G1HeapRegionAttr dest) {
+HeapRegion* G1CollectedHeap::new_gc_alloc_region(size_t word_size, G1HeapRegionAttr dest, uint node_index) {
assert(FreeList_lock->owned_by_self(), "pre-condition");
if (!has_more_regions(dest)) {
@@ -4598,7 +4646,8 @@
HeapRegion* new_alloc_region = new_region(word_size,
type,
- true /* do_expand */);
+ true /* do_expand */,
+ node_index);
if (new_alloc_region != NULL) {
if (type.is_survivor()) {
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -41,6 +41,7 @@
#include "gc/g1/g1HRPrinter.hpp"
#include "gc/g1/g1HeapRegionAttr.hpp"
#include "gc/g1/g1MonitoringSupport.hpp"
+#include "gc/g1/g1NUMA.hpp"
#include "gc/g1/g1RedirtyCardsQueue.hpp"
#include "gc/g1/g1SurvivorRegions.hpp"
#include "gc/g1/g1YCTypes.hpp"
@@ -191,6 +192,9 @@
// Callback for region mapping changed events.
G1RegionMappingChangedListener _listener;
+ // Handle G1 NUMA support.
+ G1NUMA* _numa;
+
// The sequence of all heap regions in the heap.
HeapRegionManager* _hrm;
@@ -387,7 +391,10 @@
// attempt to expand the heap if necessary to satisfy the allocation
// request. 'type' takes the type of region to be allocated. (Use constants
// Old, Eden, Humongous, Survivor defined in HeapRegionType.)
- HeapRegion* new_region(size_t word_size, HeapRegionType type, bool do_expand);
+ HeapRegion* new_region(size_t word_size,
+ HeapRegionType type,
+ bool do_expand,
+ uint node_index = G1NUMA::AnyNodeIndex);
// Initialize a contiguous set of free regions of length num_regions
// and starting at index first so that they appear as a single
@@ -462,13 +469,13 @@
// These methods are the "callbacks" from the G1AllocRegion class.
// For mutator alloc regions.
- HeapRegion* new_mutator_alloc_region(size_t word_size, bool force);
+ HeapRegion* new_mutator_alloc_region(size_t word_size, bool force, uint node_index);
void retire_mutator_alloc_region(HeapRegion* alloc_region,
size_t allocated_bytes);
// For GC alloc regions.
bool has_more_regions(G1HeapRegionAttr dest);
- HeapRegion* new_gc_alloc_region(size_t word_size, G1HeapRegionAttr dest);
+ HeapRegion* new_gc_alloc_region(size_t word_size, G1HeapRegionAttr dest, uint node_index);
void retire_gc_alloc_region(HeapRegion* alloc_region,
size_t allocated_bytes, G1HeapRegionAttr dest);
@@ -523,6 +530,9 @@
// Merges the information gathered on a per-thread basis for all worker threads
// during GC into global variables.
void merge_per_thread_state_info(G1ParScanThreadStateSet* per_thread_states);
+
+ void verify_numa_regions(const char* desc);
+
public:
G1YoungRemSetSamplingThread* sampling_thread() const { return _young_gen_sampling_thread; }
@@ -547,11 +557,14 @@
void resize_heap_if_necessary();
+ G1NUMA* numa() const { return _numa; }
+
// Expand the garbage-first heap by at least the given size (in bytes!).
// Returns true if the heap was expanded by the requested amount;
// false otherwise.
// (Rounds up to a HeapRegion boundary.)
bool expand(size_t expand_bytes, WorkGang* pretouch_workers = NULL, double* expand_time_ms = NULL);
+ bool expand_single_region(uint node_index);
// Returns the PLAB statistics for a given destination.
inline G1EvacStats* alloc_buffer_stats(G1HeapRegionAttr dest);
@@ -1274,7 +1287,9 @@
const G1SurvivorRegions* survivor() const { return &_survivor; }
uint eden_regions_count() const { return _eden.length(); }
+ uint eden_regions_count(uint node_index) const { return _eden.regions_on_node(node_index); }
uint survivor_regions_count() const { return _survivor.length(); }
+ uint survivor_regions_count(uint node_index) const { return _survivor.regions_on_node(node_index); }
size_t eden_regions_used_bytes() const { return _eden.used_bytes(); }
size_t survivor_regions_used_bytes() const { return _survivor.used_bytes(); }
uint young_regions_count() const { return _eden.length() + _survivor.length(); }
--- a/src/hotspot/share/gc/g1/g1EdenRegions.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1EdenRegions.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -25,6 +25,7 @@
#ifndef SHARE_GC_G1_G1EDENREGIONS_HPP
#define SHARE_GC_G1_G1EDENREGIONS_HPP
+#include "gc/g1/g1RegionsOnNodes.hpp"
#include "gc/g1/heapRegion.hpp"
#include "runtime/globals.hpp"
#include "utilities/debug.hpp"
@@ -35,18 +36,25 @@
// Sum of used bytes from all retired eden regions.
// I.e. updated when mutator regions are retired.
volatile size_t _used_bytes;
+ G1RegionsOnNodes _regions_on_node;
public:
- G1EdenRegions() : _length(0), _used_bytes(0) { }
+ G1EdenRegions() : _length(0), _used_bytes(0), _regions_on_node() { }
- void add(HeapRegion* hr) {
+ virtual uint add(HeapRegion* hr) {
assert(!hr->is_eden(), "should not already be set");
_length++;
+ return _regions_on_node.add(hr);
}
- void clear() { _length = 0; _used_bytes = 0; }
+ void clear() {
+ _length = 0;
+ _used_bytes = 0;
+ _regions_on_node.clear();
+ }
uint length() const { return _length; }
+ uint regions_on_node(uint node_index) const { return _regions_on_node.count(node_index); }
size_t used_bytes() const { return _used_bytes; }
--- a/src/hotspot/share/gc/g1/g1HeapTransition.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1HeapTransition.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -26,15 +26,38 @@
#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/g1/g1HeapTransition.hpp"
#include "gc/g1/g1Policy.hpp"
-#include "logging/log.hpp"
+#include "logging/logStream.hpp"
#include "memory/metaspace.hpp"
-G1HeapTransition::Data::Data(G1CollectedHeap* g1_heap) {
- _eden_length = g1_heap->eden_regions_count();
- _survivor_length = g1_heap->survivor_regions_count();
- _old_length = g1_heap->old_regions_count();
- _archive_length = g1_heap->archive_regions_count();
- _humongous_length = g1_heap->humongous_regions_count();
+G1HeapTransition::Data::Data(G1CollectedHeap* g1_heap) :
+ _eden_length(g1_heap->eden_regions_count()),
+ _survivor_length(g1_heap->survivor_regions_count()),
+ _old_length(g1_heap->old_regions_count()),
+ _archive_length(g1_heap->archive_regions_count()),
+ _humongous_length(g1_heap->humongous_regions_count()),
+ _eden_length_per_node(NULL),
+ _survivor_length_per_node(NULL) {
+
+ uint node_count = G1NUMA::numa()->num_active_nodes();
+
+ if (node_count > 1) {
+ LogTarget(Debug, gc, heap, numa) lt;
+
+ if (lt.is_enabled()) {
+ _eden_length_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC);
+ _survivor_length_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC);
+
+ for (uint i = 0; i < node_count; i++) {
+ _eden_length_per_node[i] = g1_heap->eden_regions_count(i);
+ _survivor_length_per_node[i] = g1_heap->survivor_regions_count(i);
+ }
+ }
+ }
+}
+
+G1HeapTransition::Data::~Data() {
+ FREE_C_HEAP_ARRAY(uint, _eden_length_per_node);
+ FREE_C_HEAP_ARRAY(uint, _survivor_length_per_node);
}
G1HeapTransition::G1HeapTransition(G1CollectedHeap* g1_heap) : _g1_heap(g1_heap), _before(g1_heap) { }
@@ -84,6 +107,34 @@
}
};
+static void log_regions(const char* msg, size_t before_length, size_t after_length, size_t capacity,
+ uint* before_per_node_length, uint* after_per_node_length) {
+ LogTarget(Info, gc, heap) lt;
+
+ if (lt.is_enabled()) {
+ LogStream ls(lt);
+
+ ls.print("%s regions: " SIZE_FORMAT "->" SIZE_FORMAT "(" SIZE_FORMAT ")",
+ msg, before_length, after_length, capacity);
+ // Not NULL only if gc+heap+numa at Debug level is enabled.
+ if (before_per_node_length != NULL && after_per_node_length != NULL) {
+ G1NUMA* numa = G1NUMA::numa();
+ uint num_nodes = numa->num_active_nodes();
+ const int* node_ids = numa->node_ids();
+ ls.print(" (");
+ for (uint i = 0; i < num_nodes; i++) {
+ ls.print("%d: %u->%u", node_ids[i], before_per_node_length[i], after_per_node_length[i]);
+ // Skip adding below if it is the last one.
+ if (i != num_nodes - 1) {
+ ls.print(", ");
+ }
+ }
+ ls.print(")");
+ }
+ ls.print_cr("");
+ }
+}
+
void G1HeapTransition::print() {
Data after(_g1_heap);
@@ -106,12 +157,12 @@
after._humongous_length, usage._humongous_region_count);
}
- log_info(gc, heap)("Eden regions: " SIZE_FORMAT "->" SIZE_FORMAT "(" SIZE_FORMAT ")",
- _before._eden_length, after._eden_length, eden_capacity_length_after_gc);
+ log_regions("Eden", _before._eden_length, after._eden_length, eden_capacity_length_after_gc,
+ _before._eden_length_per_node, after._eden_length_per_node);
log_trace(gc, heap)(" Used: 0K, Waste: 0K");
- log_info(gc, heap)("Survivor regions: " SIZE_FORMAT "->" SIZE_FORMAT "(" SIZE_FORMAT ")",
- _before._survivor_length, after._survivor_length, survivor_capacity_length_before_gc);
+ log_regions("Survivor", _before._survivor_length, after._survivor_length, survivor_capacity_length_before_gc,
+ _before._survivor_length_per_node, after._survivor_length_per_node);
log_trace(gc, heap)(" Used: " SIZE_FORMAT "K, Waste: " SIZE_FORMAT "K",
usage._survivor_used / K, ((after._survivor_length * HeapRegion::GrainBytes) - usage._survivor_used) / K);
--- a/src/hotspot/share/gc/g1/g1HeapTransition.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1HeapTransition.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -39,7 +39,13 @@
size_t _humongous_length;
const metaspace::MetaspaceSizesSnapshot _meta_sizes;
+ // Only includes current eden regions.
+ uint* _eden_length_per_node;
+ // Only includes current survivor regions.
+ uint* _survivor_length_per_node;
+
Data(G1CollectedHeap* g1_heap);
+ ~Data();
};
G1CollectedHeap* _g1_heap;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1NUMA.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2019, 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/g1/g1NUMA.hpp"
+#include "logging/logStream.hpp"
+#include "runtime/globals.hpp"
+#include "runtime/os.hpp"
+
+G1NUMA* G1NUMA::_inst = NULL;
+
+size_t G1NUMA::region_size() const {
+ assert(_region_size > 0, "Heap region size is not yet set");
+ return _region_size;
+}
+
+size_t G1NUMA::page_size() const {
+ assert(_page_size > 0, "Page size not is yet set");
+ return _page_size;
+}
+
+bool G1NUMA::is_enabled() const { return num_active_nodes() > 1; }
+
+G1NUMA* G1NUMA::create() {
+ guarantee(_inst == NULL, "Should be called once.");
+ _inst = new G1NUMA();
+
+ // NUMA only supported on Linux.
+#ifdef LINUX
+ _inst->initialize(UseNUMA);
+#else
+ _inst->initialize(false);
+#endif /* LINUX */
+
+ return _inst;
+}
+
+ // Returns memory node ids
+const int* G1NUMA::node_ids() const {
+ return _node_ids;
+}
+
+uint G1NUMA::index_of_node_id(int node_id) const {
+ assert(node_id >= 0, "invalid node id %d", node_id);
+ assert(node_id < _len_node_id_to_index_map, "invalid node id %d", node_id);
+ uint node_index = _node_id_to_index_map[node_id];
+ assert(node_index != G1NUMA::UnknownNodeIndex,
+ "invalid node id %d", node_id);
+ return node_index;
+}
+
+G1NUMA::G1NUMA() :
+ _node_id_to_index_map(NULL), _len_node_id_to_index_map(0),
+ _node_ids(NULL), _num_active_node_ids(0),
+ _region_size(0), _page_size(0), _stats(NULL) {
+}
+
+void G1NUMA::initialize_without_numa() {
+ // If NUMA is not enabled or supported, initialize as having a singel node.
+ _num_active_node_ids = 1;
+ _node_ids = NEW_C_HEAP_ARRAY(int, _num_active_node_ids, mtGC);
+ _node_ids[0] = 0;
+ // Map index 0 to node 0
+ _len_node_id_to_index_map = 1;
+ _node_id_to_index_map = NEW_C_HEAP_ARRAY(uint, _len_node_id_to_index_map, mtGC);
+ _node_id_to_index_map[0] = 0;
+}
+
+void G1NUMA::initialize(bool use_numa) {
+ if (!use_numa) {
+ initialize_without_numa();
+ return;
+ }
+
+ assert(UseNUMA, "Invariant");
+ size_t num_node_ids = os::numa_get_groups_num();
+
+ // Create an array of active node ids.
+ _node_ids = NEW_C_HEAP_ARRAY(int, num_node_ids, mtGC);
+ _num_active_node_ids = (uint)os::numa_get_leaf_groups(_node_ids, num_node_ids);
+
+ int max_node_id = 0;
+ for (uint i = 0; i < _num_active_node_ids; i++) {
+ max_node_id = MAX2(max_node_id, _node_ids[i]);
+ }
+
+ // Create a mapping between node_id and index.
+ _len_node_id_to_index_map = max_node_id + 1;
+ _node_id_to_index_map = NEW_C_HEAP_ARRAY(uint, _len_node_id_to_index_map, mtGC);
+
+ // Set all indices with unknown node id.
+ for (int i = 0; i < _len_node_id_to_index_map; i++) {
+ _node_id_to_index_map[i] = G1NUMA::UnknownNodeIndex;
+ }
+
+ // Set the indices for the actually retrieved node ids.
+ for (uint i = 0; i < _num_active_node_ids; i++) {
+ _node_id_to_index_map[_node_ids[i]] = i;
+ }
+
+ _stats = new G1NUMAStats(_node_ids, _num_active_node_ids);
+}
+
+G1NUMA::~G1NUMA() {
+ delete _stats;
+ FREE_C_HEAP_ARRAY(int, _node_id_to_index_map);
+ FREE_C_HEAP_ARRAY(int, _node_ids);
+}
+
+void G1NUMA::set_region_info(size_t region_size, size_t page_size) {
+ _region_size = region_size;
+ _page_size = page_size;
+}
+
+uint G1NUMA::num_active_nodes() const {
+ assert(_num_active_node_ids > 0, "just checking");
+ return _num_active_node_ids;
+}
+
+uint G1NUMA::index_of_current_thread() const {
+ if (!is_enabled()) {
+ return 0;
+ }
+ return index_of_node_id(os::numa_get_group_id());
+}
+
+uint G1NUMA::preferred_node_index_for_index(uint region_index) const {
+ if (region_size() >= page_size()) {
+ // Simple case, pages are smaller than the region so we
+ // can just alternate over the nodes.
+ return region_index % _num_active_node_ids;
+ } else {
+ // Multiple regions in one page, so we need to make sure the
+ // regions within a page is preferred on the same node.
+ size_t regions_per_page = page_size() / region_size();
+ return (region_index / regions_per_page) % _num_active_node_ids;
+ }
+}
+
+int G1NUMA::numa_id(int index) const {
+ assert(index < _len_node_id_to_index_map, "Index %d out of range: [0,%d)",
+ index, _len_node_id_to_index_map);
+ return _node_ids[index];
+}
+
+uint G1NUMA::index_of_address(HeapWord *address) const {
+ int numa_id = os::numa_get_group_id_for_address((const void*)address);
+ if (numa_id == -1) {
+ return UnknownNodeIndex;
+ } else {
+ return index_of_node_id(numa_id);
+ }
+}
+
+uint G1NUMA::index_for_region(HeapRegion* hr) const {
+ if (!is_enabled()) {
+ return 0;
+ }
+
+ if (AlwaysPreTouch) {
+ // If we already pretouched, we can check actual node index here.
+ // However, if node index is still unknown, use preferred node index.
+ uint node_index = index_of_address(hr->bottom());
+ if (node_index != UnknownNodeIndex) {
+ return node_index;
+ }
+ }
+
+ return preferred_node_index_for_index(hr->hrm_index());
+}
+
+// Request to spread the given memory evenly across the available NUMA
+// nodes. Which node to request for a given address is given by the
+// region size and the page size. Below are two examples on 4 NUMA nodes system:
+// 1. G1HeapRegionSize(_region_size) is larger than or equal to page size.
+// * Page #: |-0--||-1--||-2--||-3--||-4--||-5--||-6--||-7--||-8--||-9--||-10-||-11-||-12-||-13-||-14-||-15-|
+// * HeapRegion #: |----#0----||----#1----||----#2----||----#3----||----#4----||----#5----||----#6----||----#7----|
+// * NUMA node #: |----#0----||----#1----||----#2----||----#3----||----#0----||----#1----||----#2----||----#3----|
+// 2. G1HeapRegionSize(_region_size) is smaller than page size.
+// Memory will be touched one page at a time because G1RegionToSpaceMapper commits
+// pages one by one.
+// * Page #: |-----0----||-----1----||-----2----||-----3----||-----4----||-----5----||-----6----||-----7----|
+// * HeapRegion #: |-#0-||-#1-||-#2-||-#3-||-#4-||-#5-||-#6-||-#7-||-#8-||-#9-||#10-||#11-||#12-||#13-||#14-||#15-|
+// * NUMA node #: |----#0----||----#1----||----#2----||----#3----||----#0----||----#1----||----#2----||----#3----|
+void G1NUMA::request_memory_on_node(void* aligned_address, size_t size_in_bytes, uint region_index) {
+ if (!is_enabled()) {
+ return;
+ }
+
+ if (size_in_bytes == 0) {
+ return;
+ }
+
+ uint node_index = preferred_node_index_for_index(region_index);
+
+ assert(is_aligned(aligned_address, page_size()), "Given address (" PTR_FORMAT ") should be aligned.", p2i(aligned_address));
+ assert(is_aligned(size_in_bytes, page_size()), "Given size (" SIZE_FORMAT ") should be aligned.", size_in_bytes);
+
+ log_trace(gc, heap, numa)("Request memory [" PTR_FORMAT ", " PTR_FORMAT ") to be NUMA id (%d)",
+ p2i(aligned_address), p2i((char*)aligned_address + size_in_bytes), _node_ids[node_index]);
+ os::numa_make_local((char*)aligned_address, size_in_bytes, _node_ids[node_index]);
+}
+
+uint G1NUMA::max_search_depth() const {
+ // Multiple of 3 is just random number to limit iterations.
+ // There would be some cases that 1 page may be consisted of multiple HeapRegions.
+ return 3 * MAX2((uint)(page_size() / region_size()), (uint)1) * num_active_nodes();
+}
+
+void G1NUMA::update_statistics(G1NUMAStats::NodeDataItems phase,
+ uint requested_node_index,
+ uint allocated_node_index) {
+ if (_stats == NULL) {
+ return;
+ }
+
+ uint converted_req_index;
+ if(requested_node_index < _num_active_node_ids) {
+ converted_req_index = requested_node_index;
+ } else {
+ assert(requested_node_index == AnyNodeIndex,
+ "Requested node index %u should be AnyNodeIndex.", requested_node_index);
+ converted_req_index = _num_active_node_ids;
+ }
+ _stats->update(phase, converted_req_index, allocated_node_index);
+}
+
+void G1NUMA::copy_statistics(G1NUMAStats::NodeDataItems phase,
+ uint requested_node_index,
+ size_t* allocated_stat) {
+ if (_stats == NULL) {
+ return;
+ }
+
+ _stats->copy(phase, requested_node_index, allocated_stat);
+}
+
+void G1NUMA::print_statistics() const {
+ if (_stats == NULL) {
+ return;
+ }
+
+ _stats->print_statistics();
+}
+
+G1NodeIndexCheckClosure::G1NodeIndexCheckClosure(const char* desc, G1NUMA* numa, LogStream* ls) :
+ _desc(desc), _numa(numa), _ls(ls) {
+
+ uint num_nodes = _numa->num_active_nodes();
+ _matched = NEW_C_HEAP_ARRAY(uint, num_nodes, mtGC);
+ _mismatched = NEW_C_HEAP_ARRAY(uint, num_nodes, mtGC);
+ _total = NEW_C_HEAP_ARRAY(uint, num_nodes, mtGC);
+ memset(_matched, 0, sizeof(uint) * num_nodes);
+ memset(_mismatched, 0, sizeof(uint) * num_nodes);
+ memset(_total, 0, sizeof(uint) * num_nodes);
+}
+
+G1NodeIndexCheckClosure::~G1NodeIndexCheckClosure() {
+ _ls->print("%s: NUMA region verification (id: matched/mismatched/total): ", _desc);
+ const int* numa_ids = _numa->node_ids();
+ for (uint i = 0; i < _numa->num_active_nodes(); i++) {
+ _ls->print("%d: %u/%u/%u ", numa_ids[i], _matched[i], _mismatched[i], _total[i]);
+ }
+
+ FREE_C_HEAP_ARRAY(uint, _matched);
+ FREE_C_HEAP_ARRAY(uint, _mismatched);
+ FREE_C_HEAP_ARRAY(uint, _total);
+}
+
+bool G1NodeIndexCheckClosure::do_heap_region(HeapRegion* hr) {
+ // Preferred node index will only have valid node index.
+ uint preferred_node_index = _numa->preferred_node_index_for_index(hr->hrm_index());
+ // Active node index may have UnknownNodeIndex.
+ uint active_node_index = _numa->index_of_address(hr->bottom());
+
+ if (preferred_node_index == active_node_index) {
+ _matched[preferred_node_index]++;
+ } else if (active_node_index != G1NUMA::UnknownNodeIndex) {
+ _mismatched[preferred_node_index]++;
+ }
+ _total[preferred_node_index]++;
+
+ return false;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1NUMA.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2019, 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_G1_NUMA_HPP
+#define SHARE_VM_GC_G1_NUMA_HPP
+
+#include "gc/g1/g1NUMAStats.hpp"
+#include "gc/g1/heapRegion.hpp"
+#include "memory/allocation.hpp"
+#include "runtime/os.hpp"
+
+class LogStream;
+
+class G1NUMA: public CHeapObj<mtGC> {
+ // Mapping of available node ids to 0-based index which can be used for
+ // fast resource management. I.e. for every node id provides a unique value in
+ // the range from [0, {# of nodes-1}].
+ // For invalid node id, return UnknownNodeIndex.
+ uint* _node_id_to_index_map;
+ // Length of _num_active_node_ids_id to index map.
+ int _len_node_id_to_index_map;
+
+ // Current active node ids.
+ int* _node_ids;
+ // Total number of node ids.
+ uint _num_active_node_ids;
+
+ // HeapRegion size
+ size_t _region_size;
+ // Necessary when touching memory.
+ size_t _page_size;
+
+ // Stores statistic data.
+ G1NUMAStats* _stats;
+
+ size_t region_size() const;
+ size_t page_size() const;
+
+ // Returns node index of the given node id.
+ // Precondition: node_id is an active node id.
+ inline uint index_of_node_id(int node_id) const;
+
+ // Creates node id and node index mapping table of _node_id_to_index_map.
+ void init_node_id_to_index_map(const int* node_ids, uint num_node_ids);
+
+ static G1NUMA* _inst;
+
+ G1NUMA();
+ void initialize(bool use_numa);
+ void initialize_without_numa();
+
+public:
+ static const uint UnknownNodeIndex = UINT_MAX;
+ static const uint AnyNodeIndex = UnknownNodeIndex - 1;
+
+ static G1NUMA* numa() { return _inst; }
+
+ static G1NUMA* create();
+
+ ~G1NUMA();
+
+ // Sets heap region size and page size after those values
+ // are determined at G1CollectedHeap::initialize().
+ void set_region_info(size_t region_size, size_t page_size);
+
+ // Returns active memory node count.
+ uint num_active_nodes() const;
+
+ bool is_enabled() const;
+
+ int numa_id(int index) const;
+
+ // Returns memory node ids
+ const int* node_ids() const;
+
+ // Returns node index of current calling thread.
+ uint index_of_current_thread() const;
+
+ // Returns the preferred index for the given HeapRegion index.
+ // This assumes that HeapRegions are evenly spit, so we can decide preferred index
+ // with the given HeapRegion index.
+ // Result is less than num_active_nodes().
+ uint preferred_node_index_for_index(uint region_index) const;
+
+ // Retrieves node index of the given address.
+ // Result is less than num_active_nodes() or is UnknownNodeIndex.
+ // Precondition: address is in reserved range for heap.
+ uint index_of_address(HeapWord* address) const;
+
+ // If AlwaysPreTouch is enabled, return actual node index via system call.
+ // If disabled, return preferred node index of the given heap region.
+ uint index_for_region(HeapRegion* hr) const;
+
+ // Requests the given memory area to be located at the given node index.
+ void request_memory_on_node(void* aligned_address, size_t size_in_bytes, uint region_index);
+
+ // Returns maximum search depth which is used to limit heap region search iterations.
+ // The number of active nodes, page size and heap region size are considered.
+ uint max_search_depth() const;
+
+ // Update the given phase of requested and allocated node index.
+ void update_statistics(G1NUMAStats::NodeDataItems phase, uint requested_node_index, uint allocated_node_index);
+
+ // Copy all allocated statistics of the given phase and requested node.
+ // Precondition: allocated_stat should have same length of active nodes.
+ void copy_statistics(G1NUMAStats::NodeDataItems phase, uint requested_node_index, size_t* allocated_stat);
+
+ // Print all statistics.
+ void print_statistics() const;
+};
+
+class G1NodeIndexCheckClosure : public HeapRegionClosure {
+ const char* _desc;
+ G1NUMA* _numa;
+ // Records matched count of each node.
+ uint* _matched;
+ // Records mismatched count of each node.
+ uint* _mismatched;
+ // Records total count of each node.
+ // Total = matched + mismatched + unknown.
+ uint* _total;
+ LogStream* _ls;
+
+public:
+ G1NodeIndexCheckClosure(const char* desc, G1NUMA* numa, LogStream* ls);
+ ~G1NodeIndexCheckClosure();
+
+ bool do_heap_region(HeapRegion* hr);
+};
+
+#endif // SHARE_VM_GC_G1_NUMA_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1NUMAStats.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2019, 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/g1/g1NUMAStats.hpp"
+#include "logging/logStream.hpp"
+
+double G1NUMAStats::Stat::rate() const {
+ return _requested == 0 ? 0 : (double)_hit / _requested * 100;
+}
+
+G1NUMAStats::NodeDataArray::NodeDataArray(uint num_nodes) {
+ guarantee(num_nodes > 1, "Number of nodes (%u) should be set", num_nodes);
+
+ // The row represents the number of nodes.
+ _num_column = num_nodes;
+ // +1 for G1MemoryNodeManager::AnyNodeIndex.
+ _num_row = num_nodes + 1;
+
+ _data = NEW_C_HEAP_ARRAY(size_t*, _num_row, mtGC);
+ for (uint row = 0; row < _num_row; row++) {
+ _data[row] = NEW_C_HEAP_ARRAY(size_t, _num_column, mtGC);
+ }
+
+ clear();
+}
+
+G1NUMAStats::NodeDataArray::~NodeDataArray() {
+ for (uint row = 0; row < _num_row; row++) {
+ FREE_C_HEAP_ARRAY(size_t, _data[row]);
+ }
+ FREE_C_HEAP_ARRAY(size_t*, _data);
+}
+
+void G1NUMAStats::NodeDataArray::create_hit_rate(Stat* result) const {
+ size_t requested = 0;
+ size_t hit = 0;
+
+ for (size_t row = 0; row < _num_row; row++) {
+ for (size_t column = 0; column < _num_column; column++) {
+ requested += _data[row][column];
+ if (row == column) {
+ hit += _data[row][column];
+ }
+ }
+ }
+
+ assert(result != NULL, "Invariant");
+ result->_hit = hit;
+ result->_requested = requested;
+}
+
+void G1NUMAStats::NodeDataArray::create_hit_rate(Stat* result, uint req_index) const {
+ size_t requested = 0;
+ size_t hit = _data[req_index][req_index];
+
+ for (size_t column = 0; column < _num_column; column++) {
+ requested += _data[req_index][column];
+ }
+
+ assert(result != NULL, "Invariant");
+ result->_hit = hit;
+ result->_requested = requested;
+}
+
+size_t G1NUMAStats::NodeDataArray::sum(uint req_index) const {
+ size_t sum = 0;
+ for (size_t column = 0; column < _num_column; column++) {
+ sum += _data[req_index][column];
+ }
+
+ return sum;
+}
+
+void G1NUMAStats::NodeDataArray::increase(uint req_index, uint alloc_index) {
+ assert(req_index < _num_row,
+ "Requested index %u should be less than the row size %u",
+ req_index, _num_row);
+ assert(alloc_index < _num_column,
+ "Allocated index %u should be less than the column size %u",
+ alloc_index, _num_column);
+ _data[req_index][alloc_index] += 1;
+}
+
+void G1NUMAStats::NodeDataArray::clear() {
+ for (uint row = 0; row < _num_row; row++) {
+ memset((void*)_data[row], 0, sizeof(size_t) * _num_column);
+ }
+}
+
+size_t G1NUMAStats::NodeDataArray::get(uint req_index, uint alloc_index) {
+ return _data[req_index][alloc_index];
+}
+
+void G1NUMAStats::NodeDataArray::copy(uint req_index, size_t* stat) {
+ assert(stat != NULL, "Invariant");
+
+ for (uint column = 0; column < _num_column; column++) {
+ _data[req_index][column] += stat[column];
+ }
+}
+
+G1NUMAStats::G1NUMAStats(const int* node_ids, uint num_node_ids) :
+ _node_ids(node_ids), _num_node_ids(num_node_ids), _node_data() {
+
+ assert(_num_node_ids > 1, "Should have more than one active memory nodes %u", _num_node_ids);
+
+ for (int i = 0; i < NodeDataItemsSentinel; i++) {
+ _node_data[i] = new NodeDataArray(_num_node_ids);
+ }
+}
+
+G1NUMAStats::~G1NUMAStats() {
+ for (int i = 0; i < NodeDataItemsSentinel; i++) {
+ delete _node_data[i];
+ }
+}
+
+void G1NUMAStats::clear(G1NUMAStats::NodeDataItems phase) {
+ _node_data[phase]->clear();
+}
+
+void G1NUMAStats::update(G1NUMAStats::NodeDataItems phase,
+ uint requested_node_index,
+ uint allocated_node_index) {
+ _node_data[phase]->increase(requested_node_index, allocated_node_index);
+}
+
+void G1NUMAStats::copy(G1NUMAStats::NodeDataItems phase,
+ uint requested_node_index,
+ size_t* allocated_stat) {
+ _node_data[phase]->copy(requested_node_index, allocated_stat);
+}
+
+static const char* phase_to_explanatory_string(G1NUMAStats::NodeDataItems phase) {
+ switch(phase) {
+ case G1NUMAStats::NewRegionAlloc:
+ return "Placement match ratio";
+ case G1NUMAStats::LocalObjProcessAtCopyToSurv:
+ return "Worker task locality match ratio";
+ default:
+ return "";
+ }
+}
+
+#define RATE_TOTAL_FORMAT "%0.0f%% " SIZE_FORMAT "/" SIZE_FORMAT
+
+void G1NUMAStats::print_info(G1NUMAStats::NodeDataItems phase) {
+ LogTarget(Info, gc, heap, numa) lt;
+
+ if (lt.is_enabled()) {
+ LogStream ls(lt);
+ Stat result;
+ size_t array_width = _num_node_ids;
+
+ _node_data[phase]->create_hit_rate(&result);
+
+ ls.print("%s: " RATE_TOTAL_FORMAT " (",
+ phase_to_explanatory_string(phase), result.rate(), result._hit, result._requested);
+
+ for (uint i = 0; i < array_width; i++) {
+ if (i != 0) {
+ ls.print(", ");
+ }
+ _node_data[phase]->create_hit_rate(&result, i);
+ ls.print("%d: " RATE_TOTAL_FORMAT,
+ _node_ids[i], result.rate(), result._hit, result._requested);
+ }
+ ls.print_cr(")");
+ }
+}
+
+void G1NUMAStats::print_mutator_alloc_stat_debug() {
+ LogTarget(Debug, gc, heap, numa) lt;
+
+ if (lt.is_enabled()) {
+ LogStream ls(lt);
+ uint array_width = _num_node_ids;
+
+ ls.print("Allocated NUMA ids ");
+ for (uint i = 0; i < array_width; i++) {
+ ls.print("%8d", _node_ids[i]);
+ }
+ ls.print_cr(" Total");
+
+ ls.print("Requested NUMA id ");
+ for (uint req = 0; req < array_width; req++) {
+ ls.print("%3d ", _node_ids[req]);
+ for (uint alloc = 0; alloc < array_width; alloc++) {
+ ls.print(SIZE_FORMAT_W(8), _node_data[NewRegionAlloc]->get(req, alloc));
+ }
+ ls.print(SIZE_FORMAT_W(8), _node_data[NewRegionAlloc]->sum(req));
+ ls.print_cr("");
+ // Add padding to align with the string 'Requested NUMA id'.
+ ls.print(" ");
+ }
+ ls.print("Any ");
+ for (uint alloc = 0; alloc < array_width; alloc++) {
+ ls.print(SIZE_FORMAT_W(8), _node_data[NewRegionAlloc]->get(array_width, alloc));
+ }
+ ls.print(SIZE_FORMAT_W(8), _node_data[NewRegionAlloc]->sum(array_width));
+ ls.print_cr("");
+ }
+}
+
+void G1NUMAStats::print_statistics() {
+ print_info(NewRegionAlloc);
+ print_mutator_alloc_stat_debug();
+
+ print_info(LocalObjProcessAtCopyToSurv);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1NUMAStats.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2019, 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_G1_NODE_TIMES_HPP
+#define SHARE_VM_GC_G1_NODE_TIMES_HPP
+
+#include "memory/allocation.hpp"
+
+// Manages statistics of multi nodes.
+class G1NUMAStats : public CHeapObj<mtGC> {
+ struct Stat {
+ // Hit count: if requested id equals to returned id.
+ size_t _hit;
+ // Total request count
+ size_t _requested;
+
+ // Hit count / total request count
+ double rate() const;
+ };
+
+ // Holds data array which has a size of (node count) * (node count + 1) to
+ // represent request node * allocated node. The request node includes any node case.
+ // All operations are NOT thread-safe.
+ // The row index indicates a requested node index while the column node index
+ // indicates an allocated node index. The last row is for any node index request.
+ // E.g. (req, alloc) = (0,0) (1,0) (2,0) (0,1) (Any, 3) (0,2) (0,3) (0,3) (3,3)
+ // Allocated node index 0 1 2 3 Total
+ // Requested node index 0 1 1 1 2 5
+ // 1 1 0 0 0 1
+ // 2 1 0 0 0 1
+ // 3 0 0 0 1 1
+ // Any 0 0 0 1 1
+ class NodeDataArray : public CHeapObj<mtGC> {
+ // The number of nodes.
+ uint _num_column;
+ // The number of nodes + 1 (for any node request)
+ uint _num_row;
+ // 2-dimension array that holds count of allocated / requested node index.
+ size_t** _data;
+
+ public:
+ NodeDataArray(uint num_nodes);
+ ~NodeDataArray();
+
+ // Create Stat result of hit count, requested count and hit rate.
+ // The result is copied to the given result parameter.
+ void create_hit_rate(Stat* result) const;
+ // Create Stat result of hit count, requested count and hit rate of the given index.
+ // The result is copied to the given result parameter.
+ void create_hit_rate(Stat* result, uint req_index) const;
+ // Return sum of the given index.
+ size_t sum(uint req_index) const;
+ // Increase at the request / allocated index.
+ void increase(uint req_index, uint alloc_index);
+ // Clear all data.
+ void clear();
+ // Return current value of the given request / allocated index.
+ size_t get(uint req_index, uint alloc_index);
+ // Copy values of the given request index.
+ void copy(uint req_index, size_t* stat);
+ };
+
+public:
+ enum NodeDataItems {
+ // Statistics of a new region allocation.
+ NewRegionAlloc,
+ // Statistics of object processing during copy to survivor region.
+ LocalObjProcessAtCopyToSurv,
+ NodeDataItemsSentinel
+ };
+
+private:
+ const int* _node_ids;
+ uint _num_node_ids;
+
+ NodeDataArray* _node_data[NodeDataItemsSentinel];
+
+ void print_info(G1NUMAStats::NodeDataItems phase);
+
+ void print_mutator_alloc_stat_debug();
+
+public:
+ G1NUMAStats(const int* node_ids, uint num_node_ids);
+ ~G1NUMAStats();
+
+ void clear(G1NUMAStats::NodeDataItems phase);
+
+ // Update the given phase of requested and allocated node index.
+ void update(G1NUMAStats::NodeDataItems phase, uint requested_node_index, uint allocated_node_index);
+
+ // Copy all allocated statistics of the given phase and requested node.
+ // Precondition: allocated_stat should have same length of active nodes.
+ void copy(G1NUMAStats::NodeDataItems phase, uint requested_node_index, size_t* allocated_stat);
+
+ void print_statistics();
+};
+
+#endif // SHARE_VM_GC_G1_NODE_TIMES_HPP
--- a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -124,6 +124,11 @@
return _low_boundary + index * _page_size;
}
+size_t G1PageBasedVirtualSpace::page_size() const {
+ assert(_page_size > 0, "Page size is not yet initialized.");
+ return _page_size;
+}
+
bool G1PageBasedVirtualSpace::is_after_last_page(size_t index) const {
guarantee(index <= _committed.size(),
"Given boundary page " SIZE_FORMAT " is beyond managed page count " SIZE_FORMAT, index, _committed.size());
--- a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -92,8 +92,6 @@
// Returns the index of the page which contains the given address.
size_t addr_to_page_index(char* addr) const;
- // Returns the address of the given page index.
- char* page_start(size_t index) const;
// Is the given page index the last page?
bool is_last_page(size_t index) const { return index == (_committed.size() - 1); }
@@ -147,6 +145,10 @@
void check_for_contiguity() PRODUCT_RETURN;
+ // Returns the address of the given page index.
+ char* page_start(size_t index) const;
+ size_t page_size() const;
+
// Debugging
void print_on(outputStream* out) PRODUCT_RETURN;
void print();
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -57,7 +57,9 @@
_stack_trim_lower_threshold(GCDrainStackTargetSize),
_trim_ticks(),
_old_gen_is_full(false),
- _num_optional_regions(optional_cset_length)
+ _num_optional_regions(optional_cset_length),
+ _numa(g1h->numa()),
+ _obj_alloc_stat(NULL)
{
// We allocate number of young gen regions in the collection set plus one
// entries, since entry 0 keeps track of surviving bytes for non-young regions.
@@ -79,6 +81,8 @@
_closures = G1EvacuationRootClosures::create_root_closures(this, _g1h);
_oops_into_optional_regions = new G1OopStarChunkedList[_num_optional_regions];
+
+ initialize_numa_stats();
}
// Pass locally gathered statistics to global state.
@@ -92,6 +96,7 @@
for (uint i = 0; i < length; i++) {
surviving_young_words[i] += _surviving_young_words[i];
}
+ flush_numa_stats();
}
G1ParScanThreadState::~G1ParScanThreadState() {
@@ -99,6 +104,7 @@
delete _closures;
FREE_C_HEAP_ARRAY(size_t, _surviving_young_words_base);
delete[] _oops_into_optional_regions;
+ FREE_C_HEAP_ARRAY(size_t, _obj_alloc_stat);
}
size_t G1ParScanThreadState::lab_waste_words() const {
@@ -152,11 +158,11 @@
} while (!_refs->is_empty());
}
-HeapWord* G1ParScanThreadState::allocate_in_next_plab(G1HeapRegionAttr const region_attr,
- G1HeapRegionAttr* dest,
+HeapWord* G1ParScanThreadState::allocate_in_next_plab(G1HeapRegionAttr* dest,
size_t word_sz,
- bool previous_plab_refill_failed) {
- assert(region_attr.is_in_cset_or_humongous(), "Unexpected region attr type: %s", region_attr.get_type_str());
+ bool previous_plab_refill_failed,
+ uint node_index) {
+
assert(dest->is_in_cset_or_humongous(), "Unexpected dest: %s region attr", dest->get_type_str());
// Right now we only have two types of regions (young / old) so
@@ -165,7 +171,8 @@
bool plab_refill_in_old_failed = false;
HeapWord* const obj_ptr = _plab_allocator->allocate(G1HeapRegionAttr::Old,
word_sz,
- &plab_refill_in_old_failed);
+ &plab_refill_in_old_failed,
+ node_index);
// Make sure that we won't attempt to copy any other objects out
// of a survivor region (given that apparently we cannot allocate
// any new ones) to avoid coming into this slow path again and again.
@@ -204,8 +211,8 @@
void G1ParScanThreadState::report_promotion_event(G1HeapRegionAttr const dest_attr,
oop const old, size_t word_sz, uint age,
- HeapWord * const obj_ptr) const {
- PLAB* alloc_buf = _plab_allocator->alloc_buffer(dest_attr);
+ HeapWord * const obj_ptr, uint node_index) const {
+ PLAB* alloc_buf = _plab_allocator->alloc_buffer(dest_attr, node_index);
if (alloc_buf->contains(obj_ptr)) {
_g1h->_gc_tracer_stw->report_promotion_in_new_plab_event(old->klass(), word_sz * HeapWordSize, age,
dest_attr.type() == G1HeapRegionAttr::Old,
@@ -228,24 +235,30 @@
if (_old_gen_is_full && dest_attr.is_old()) {
return handle_evacuation_failure_par(old, old_mark);
}
- HeapWord* obj_ptr = _plab_allocator->plab_allocate(dest_attr, word_sz);
+ HeapRegion* const from_region = _g1h->heap_region_containing(old);
+ uint node_index = from_region->node_index();
+
+ HeapWord* obj_ptr = _plab_allocator->plab_allocate(dest_attr, word_sz, node_index);
// PLAB allocations should succeed most of the time, so we'll
// normally check against NULL once and that's it.
if (obj_ptr == NULL) {
bool plab_refill_failed = false;
- obj_ptr = _plab_allocator->allocate_direct_or_new_plab(dest_attr, word_sz, &plab_refill_failed);
+ obj_ptr = _plab_allocator->allocate_direct_or_new_plab(dest_attr, word_sz, &plab_refill_failed, node_index);
if (obj_ptr == NULL) {
- obj_ptr = allocate_in_next_plab(region_attr, &dest_attr, word_sz, plab_refill_failed);
+ assert(region_attr.is_in_cset(), "Unexpected region attr type: %s", region_attr.get_type_str());
+ obj_ptr = allocate_in_next_plab(&dest_attr, word_sz, plab_refill_failed, node_index);
if (obj_ptr == NULL) {
// This will either forward-to-self, or detect that someone else has
// installed a forwarding pointer.
return handle_evacuation_failure_par(old, old_mark);
}
}
+ update_numa_stats(node_index);
+
if (_g1h->_gc_tracer_stw->should_report_promotion_events()) {
// The events are checked individually as part of the actual commit
- report_promotion_event(dest_attr, old, word_sz, age, obj_ptr);
+ report_promotion_event(dest_attr, old, word_sz, age, obj_ptr, node_index);
}
}
@@ -257,7 +270,7 @@
if (_g1h->evacuation_should_fail()) {
// Doing this after all the allocation attempts also tests the
// undo_allocation() method too.
- _plab_allocator->undo_allocation(dest_attr, obj_ptr, word_sz);
+ _plab_allocator->undo_allocation(dest_attr, obj_ptr, word_sz, node_index);
return handle_evacuation_failure_par(old, old_mark);
}
#endif // !PRODUCT
@@ -270,7 +283,6 @@
if (forward_ptr == NULL) {
Copy::aligned_disjoint_words((HeapWord*) old, obj_ptr, word_sz);
- HeapRegion* const from_region = _g1h->heap_region_containing(old);
const uint young_index = from_region->young_index_in_cset();
assert((from_region->is_young() && young_index > 0) ||
@@ -323,7 +335,7 @@
}
return obj;
} else {
- _plab_allocator->undo_allocation(dest_attr, obj_ptr, word_sz);
+ _plab_allocator->undo_allocation(dest_attr, obj_ptr, word_sz, node_index);
return forward_ptr;
}
}
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -95,6 +95,13 @@
size_t _num_optional_regions;
G1OopStarChunkedList* _oops_into_optional_regions;
+ G1NUMA* _numa;
+
+ // Records how many object allocations happened at each node during copy to survivor.
+ // Only starts recording when log of gc+heap+numa is enabled and its data is
+ // transferred when flushed.
+ size_t* _obj_alloc_stat;
+
public:
G1ParScanThreadState(G1CollectedHeap* g1h,
G1RedirtyCardsQueueSet* rdcqs,
@@ -187,27 +194,32 @@
inline void dispatch_reference(StarTask ref);
// Tries to allocate word_sz in the PLAB of the next "generation" after trying to
- // allocate into dest. State is the original (source) cset state for the object
- // that is allocated for. Previous_plab_refill_failed indicates whether previously
- // a PLAB refill into "state" failed.
+ // allocate into dest. Previous_plab_refill_failed indicates whether previous
+ // PLAB refill for the original (source) object failed.
// Returns a non-NULL pointer if successful, and updates dest if required.
// Also determines whether we should continue to try to allocate into the various
// generations or just end trying to allocate.
- HeapWord* allocate_in_next_plab(G1HeapRegionAttr const region_attr,
- G1HeapRegionAttr* dest,
+ HeapWord* allocate_in_next_plab(G1HeapRegionAttr* dest,
size_t word_sz,
- bool previous_plab_refill_failed);
+ bool previous_plab_refill_failed,
+ uint node_index);
inline G1HeapRegionAttr next_region_attr(G1HeapRegionAttr const region_attr, markWord const m, uint& age);
void report_promotion_event(G1HeapRegionAttr const dest_attr,
oop const old, size_t word_sz, uint age,
- HeapWord * const obj_ptr) const;
+ HeapWord * const obj_ptr, uint node_index) const;
inline bool needs_partial_trimming() const;
inline bool is_partially_trimmed() const;
inline void trim_queue_to_threshold(uint threshold);
+
+ // NUMA statistics related methods.
+ inline void initialize_numa_stats();
+ inline void flush_numa_stats();
+ inline void update_numa_stats(uint node_index);
+
public:
oop copy_to_survivor_space(G1HeapRegionAttr const region_attr, oop const obj, markWord const old_mark);
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -230,4 +230,30 @@
return &_oops_into_optional_regions[hr->index_in_opt_cset()];
}
+void G1ParScanThreadState::initialize_numa_stats() {
+ if (_numa->is_enabled()) {
+ LogTarget(Info, gc, heap, numa) lt;
+
+ if (lt.is_enabled()) {
+ uint num_nodes = _numa->num_active_nodes();
+ // Record only if there are multiple active nodes.
+ _obj_alloc_stat = NEW_C_HEAP_ARRAY(size_t, num_nodes, mtGC);
+ memset(_obj_alloc_stat, 0, sizeof(size_t) * num_nodes);
+ }
+ }
+}
+
+void G1ParScanThreadState::flush_numa_stats() {
+ if (_obj_alloc_stat != NULL) {
+ uint node_index = _numa->index_of_current_thread();
+ _numa->copy_statistics(G1NUMAStats::LocalObjProcessAtCopyToSurv, node_index, _obj_alloc_stat);
+ }
+}
+
+void G1ParScanThreadState::update_numa_stats(uint node_index) {
+ if (_obj_alloc_stat != NULL) {
+ _obj_alloc_stat[node_index]++;
+ }
+}
+
#endif // SHARE_GC_G1_G1PARSCANTHREADSTATE_INLINE_HPP
--- a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "gc/g1/g1BiasedArray.hpp"
+#include "gc/g1/g1NUMA.hpp"
#include "gc/g1/g1RegionToSpaceMapper.hpp"
#include "logging/log.hpp"
#include "memory/allocation.inline.hpp"
@@ -44,7 +45,8 @@
_listener(NULL),
_storage(rs, used_size, page_size),
_region_granularity(region_granularity),
- _commit_map(rs.size() * commit_factor / region_granularity, mtGC) {
+ _commit_map(rs.size() * commit_factor / region_granularity, mtGC),
+ _memory_type(type) {
guarantee(is_power_of_2(page_size), "must be");
guarantee(is_power_of_2(region_granularity), "must be");
@@ -72,10 +74,18 @@
}
virtual void commit_regions(uint start_idx, size_t num_regions, WorkGang* pretouch_gang) {
- size_t const start_page = (size_t)start_idx * _pages_per_region;
- bool zero_filled = _storage.commit(start_page, num_regions * _pages_per_region);
+ const size_t start_page = (size_t)start_idx * _pages_per_region;
+ const size_t size_in_pages = num_regions * _pages_per_region;
+ bool zero_filled = _storage.commit(start_page, size_in_pages);
+ if (_memory_type == mtJavaHeap) {
+ for (uint region_index = start_idx; region_index < start_idx + num_regions; region_index++ ) {
+ void* address = _storage.page_start(region_index * _pages_per_region);
+ size_t size_in_bytes = _storage.page_size() * _pages_per_region;
+ G1NUMA::numa()->request_memory_on_node(address, size_in_bytes, region_index);
+ }
+ }
if (AlwaysPreTouch) {
- _storage.pretouch(start_page, num_regions * _pages_per_region, pretouch_gang);
+ _storage.pretouch(start_page, size_in_pages, pretouch_gang);
}
_commit_map.set_range(start_idx, start_idx + num_regions);
fire_on_commit(start_idx, num_regions, zero_filled);
@@ -126,26 +136,32 @@
size_t num_committed = 0;
bool all_zero_filled = true;
+ G1NUMA* numa = G1NUMA::numa();
- for (uint i = start_idx; i < start_idx + num_regions; i++) {
- assert(!_commit_map.at(i), "Trying to commit storage at region %u that is already committed", i);
- size_t idx = region_idx_to_page_idx(i);
- uint old_refcount = _refcounts.get_by_index(idx);
+ for (uint region_idx = start_idx; region_idx < start_idx + num_regions; region_idx++) {
+ assert(!_commit_map.at(region_idx), "Trying to commit storage at region %u that is already committed", region_idx);
+ size_t page_idx = region_idx_to_page_idx(region_idx);
+ uint old_refcount = _refcounts.get_by_index(page_idx);
bool zero_filled = false;
if (old_refcount == 0) {
if (first_committed == NoPage) {
- first_committed = idx;
+ first_committed = page_idx;
num_committed = 1;
} else {
num_committed++;
}
- zero_filled = _storage.commit(idx, 1);
+ zero_filled = _storage.commit(page_idx, 1);
+ if (_memory_type == mtJavaHeap) {
+ void* address = _storage.page_start(page_idx);
+ size_t size_in_bytes = _storage.page_size();
+ numa->request_memory_on_node(address, size_in_bytes, region_idx);
+ }
}
all_zero_filled &= zero_filled;
- _refcounts.set_by_index(idx, old_refcount + 1);
- _commit_map.set_bit(i);
+ _refcounts.set_by_index(page_idx, old_refcount + 1);
+ _commit_map.set_bit(region_idx);
}
if (AlwaysPreTouch && num_committed > 0) {
_storage.pretouch(first_committed, num_committed, pretouch_gang);
--- a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -53,6 +53,8 @@
// Mapping management
CHeapBitMap _commit_map;
+ MemoryType _memory_type;
+
G1RegionToSpaceMapper(ReservedSpace rs, size_t used_size, size_t page_size, size_t region_granularity, size_t commit_factor, MemoryType type);
void fire_on_commit(uint start_idx, size_t num_regions, bool zero_filled);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1RegionsOnNodes.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019, 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/g1/g1NUMA.hpp"
+#include "gc/g1/g1RegionsOnNodes.hpp"
+#include "gc/g1/heapRegion.hpp"
+
+G1RegionsOnNodes::G1RegionsOnNodes() : _count_per_node(NULL), _numa(G1NUMA::numa()) {
+ _count_per_node = NEW_C_HEAP_ARRAY(uint, _numa->num_active_nodes(), mtGC);
+ clear();
+}
+
+G1RegionsOnNodes::~G1RegionsOnNodes() {
+ FREE_C_HEAP_ARRAY(uint, _count_per_node);
+}
+
+uint G1RegionsOnNodes::add(HeapRegion* hr) {
+ uint node_index = hr->node_index();
+
+ // Update only if the node index is valid.
+ if (node_index < _numa->num_active_nodes()) {
+ *(_count_per_node + node_index) += 1;
+ return node_index;
+ }
+
+ return G1NUMA::UnknownNodeIndex;
+}
+
+void G1RegionsOnNodes::clear() {
+ for (uint i = 0; i < _numa->num_active_nodes(); i++) {
+ _count_per_node[i] = 0;
+ }
+}
+
+uint G1RegionsOnNodes::count(uint node_index) const {
+ return _count_per_node[node_index];
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019, 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_G1_G1REGIONS_HPP
+#define SHARE_VM_GC_G1_G1REGIONS_HPP
+
+#include "memory/allocation.hpp"
+
+class G1NUMA;
+class HeapRegion;
+
+// Contains per node index region count
+class G1RegionsOnNodes : public StackObj {
+ volatile uint* _count_per_node;
+ G1NUMA* _numa;
+
+public:
+ G1RegionsOnNodes();
+
+ ~G1RegionsOnNodes();
+
+ // Increase _count_per_node for the node of given heap region and returns node index.
+ uint add(HeapRegion* hr);
+
+ void clear();
+
+ uint count(uint node_index) const;
+};
+
+#endif // SHARE_VM_GC_G1_G1REGIONS_HPP
--- a/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -30,17 +30,23 @@
G1SurvivorRegions::G1SurvivorRegions() :
_regions(new (ResourceObj::C_HEAP, mtGC) GrowableArray<HeapRegion*>(8, true, mtGC)),
- _used_bytes(0) {}
+ _used_bytes(0),
+ _regions_on_node() {}
-void G1SurvivorRegions::add(HeapRegion* hr) {
+uint G1SurvivorRegions::add(HeapRegion* hr) {
assert(hr->is_survivor(), "should be flagged as survivor region");
_regions->append(hr);
+ return _regions_on_node.add(hr);
}
uint G1SurvivorRegions::length() const {
return (uint)_regions->length();
}
+uint G1SurvivorRegions::regions_on_node(uint node_index) const {
+ return _regions_on_node.count(node_index);
+}
+
void G1SurvivorRegions::convert_to_eden() {
for (GrowableArrayIterator<HeapRegion*> it = _regions->begin();
it != _regions->end();
@@ -54,6 +60,7 @@
void G1SurvivorRegions::clear() {
_regions->clear();
_used_bytes = 0;
+ _regions_on_node.clear();
}
void G1SurvivorRegions::add_used_bytes(size_t used_bytes) {
--- a/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -25,6 +25,7 @@
#ifndef SHARE_GC_G1_G1SURVIVORREGIONS_HPP
#define SHARE_GC_G1_G1SURVIVORREGIONS_HPP
+#include "gc/g1/g1RegionsOnNodes.hpp"
#include "runtime/globals.hpp"
template <typename T>
@@ -35,17 +36,19 @@
private:
GrowableArray<HeapRegion*>* _regions;
volatile size_t _used_bytes;
+ G1RegionsOnNodes _regions_on_node;
public:
G1SurvivorRegions();
- void add(HeapRegion* hr);
+ virtual uint add(HeapRegion* hr);
void convert_to_eden();
void clear();
uint length() const;
+ uint regions_on_node(uint node_index) const;
const GrowableArray<HeapRegion*>* regions() const {
return _regions;
--- a/src/hotspot/share/gc/g1/heapRegion.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/heapRegion.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -28,6 +28,7 @@
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1CollectionSet.hpp"
#include "gc/g1/g1HeapRegionTraceType.hpp"
+#include "gc/g1/g1NUMA.hpp"
#include "gc/g1/g1OopClosures.inline.hpp"
#include "gc/g1/heapRegion.inline.hpp"
#include "gc/g1/heapRegionBounds.inline.hpp"
@@ -252,7 +253,8 @@
_index_in_opt_cset(InvalidCSetIndex), _young_index_in_cset(-1),
_surv_rate_group(NULL), _age_index(-1),
_prev_top_at_mark_start(NULL), _next_top_at_mark_start(NULL),
- _recorded_rs_length(0), _predicted_elapsed_time_ms(0)
+ _recorded_rs_length(0), _predicted_elapsed_time_ms(0),
+ _node_index(G1NUMA::UnknownNodeIndex)
{
_rem_set = new HeapRegionRemSet(bot, this);
@@ -470,8 +472,17 @@
} else {
st->print("| ");
}
- st->print_cr("|TAMS " PTR_FORMAT ", " PTR_FORMAT "| %s ",
+ st->print("|TAMS " PTR_FORMAT ", " PTR_FORMAT "| %s ",
p2i(prev_top_at_mark_start()), p2i(next_top_at_mark_start()), rem_set()->get_state_str());
+ if (UseNUMA) {
+ G1NUMA* numa = G1NUMA::numa();
+ if (node_index() < numa->num_active_nodes()) {
+ st->print("|%d", numa->numa_id(node_index()));
+ } else {
+ st->print("|-");
+ }
+ }
+ st->print_cr("");
}
class G1VerificationClosure : public BasicOopIterateClosure {
--- a/src/hotspot/share/gc/g1/heapRegion.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/heapRegion.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -253,6 +253,8 @@
// for the collection set.
double _predicted_elapsed_time_ms;
+ uint _node_index;
+
// Iterate over the references covered by the given MemRegion in a humongous
// object and apply the given closure to them.
// Humongous objects are allocated directly in the old-gen. So we need special
@@ -643,6 +645,9 @@
// the strong code roots list for this region
void strong_code_roots_do(CodeBlobClosure* blk) const;
+ uint node_index() const { return _node_index; }
+ void set_node_index(uint node_index) { _node_index = node_index; }
+
// Verify that the entries on the strong code root list for this
// region are live and include at least one pointer into this region.
void verify_strong_code_roots(VerifyOption vo, bool* failures) const;
--- a/src/hotspot/share/gc/g1/heapRegionManager.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/heapRegionManager.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -26,10 +26,12 @@
#include "gc/g1/g1Arguments.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1ConcurrentRefine.hpp"
+#include "gc/g1/g1NUMAStats.hpp"
#include "gc/g1/heapRegion.hpp"
#include "gc/g1/heapRegionManager.inline.hpp"
#include "gc/g1/heapRegionSet.inline.hpp"
#include "gc/g1/heterogeneousHeapRegionManager.hpp"
+#include "logging/logStream.hpp"
#include "memory/allocation.hpp"
#include "utilities/bitMap.inline.hpp"
@@ -103,6 +105,34 @@
return _available_map.at(region);
}
+HeapRegion* HeapRegionManager::allocate_free_region(HeapRegionType type, uint requested_node_index) {
+ HeapRegion* hr = NULL;
+ bool from_head = !type.is_young();
+ G1NUMA* numa = G1NUMA::numa();
+
+ if (requested_node_index != G1NUMA::AnyNodeIndex && numa->is_enabled()) {
+ // Try to allocate with requested node index.
+ hr = _free_list.remove_region_with_node_index(from_head, requested_node_index);
+ }
+
+ if (hr == NULL) {
+ // If there's a single active node or we did not get a region from our requested node,
+ // try without requested node index.
+ hr = _free_list.remove_region(from_head);
+ }
+
+ if (hr != NULL) {
+ assert(hr->next() == NULL, "Single region should not have next");
+ assert(is_available(hr->hrm_index()), "Must be committed");
+
+ if (numa->is_enabled() && hr->node_index() < numa->num_active_nodes()) {
+ numa->update_statistics(G1NUMAStats::NewRegionAlloc, requested_node_index, hr->node_index());
+ }
+ }
+
+ return hr;
+}
+
#ifdef ASSERT
bool HeapRegionManager::is_free(HeapRegion* hr) const {
return _free_list.contains(hr);
@@ -139,6 +169,11 @@
guarantee(num_regions >= 1, "Need to specify at least one region to uncommit, tried to uncommit zero regions at %u", start);
guarantee(_num_committed >= num_regions, "pre-condition");
+ // Reset node index to distinguish with committed regions.
+ for (uint i = start; i < start + num_regions; i++) {
+ at(i)->set_node_index(G1NUMA::UnknownNodeIndex);
+ }
+
// Print before uncommitting.
if (G1CollectedHeap::heap()->hr_printer()->is_active()) {
for (uint i = start; i < start + num_regions; i++) {
@@ -186,6 +221,7 @@
MemRegion mr(bottom, bottom + HeapRegion::GrainWords);
hr->initialize(mr);
+ hr->set_node_index(G1NUMA::numa()->index_for_region(hr));
insert_into_free_list(at(i));
}
}
@@ -235,6 +271,35 @@
return expanded;
}
+uint HeapRegionManager::expand_on_preferred_node(uint preferred_index) {
+ uint expand_candidate = UINT_MAX;
+ for (uint i = 0; i < max_length(); i++) {
+ if (is_available(i)) {
+ // Already in use continue
+ continue;
+ }
+ // Always save the candidate so we can expand later on.
+ expand_candidate = i;
+ if (is_on_preferred_index(expand_candidate, preferred_index)) {
+ // We have found a candidate on the preffered node, break.
+ break;
+ }
+ }
+
+ if (expand_candidate == UINT_MAX) {
+ // No regions left, expand failed.
+ return 0;
+ }
+
+ make_regions_available(expand_candidate, 1, NULL);
+ return 1;
+}
+
+bool HeapRegionManager::is_on_preferred_index(uint region_index, uint preferred_node_index) {
+ uint region_node_index = G1NUMA::numa()->preferred_node_index_for_index(region_index);
+ return region_node_index == preferred_node_index;
+}
+
uint HeapRegionManager::find_contiguous(size_t num, bool empty_only) {
uint found = 0;
size_t length_found = 0;
--- a/src/hotspot/share/gc/g1/heapRegionManager.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/heapRegionManager.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -108,6 +108,9 @@
// sequence could be found, otherwise res_idx contains the start index of this range.
uint find_empty_from_idx_reverse(uint start_idx, uint* res_idx) const;
+ // Checks the G1MemoryNodeManager to see if this region is on the preferred node.
+ bool is_on_preferred_index(uint region_index, uint preferred_node_index);
+
protected:
G1HeapRegionTable _regions;
G1RegionToSpaceMapper* _heap_mapper;
@@ -174,15 +177,8 @@
_free_list.add_ordered(list);
}
- virtual HeapRegion* allocate_free_region(HeapRegionType type) {
- HeapRegion* hr = _free_list.remove_region(!type.is_young());
-
- if (hr != NULL) {
- assert(hr->next() == NULL, "Single region should not have next");
- assert(is_available(hr->hrm_index()), "Must be committed");
- }
- return hr;
- }
+ // Allocate a free region with specific node index. If fails allocate with next node index.
+ virtual HeapRegion* allocate_free_region(HeapRegionType type, uint requested_node_index);
inline void allocate_free_regions_starting_at(uint first, uint num_regions);
@@ -196,6 +192,10 @@
return _free_list.length();
}
+ uint num_free_regions(uint node_index) const {
+ return _free_list.length(node_index);
+ }
+
size_t total_free_bytes() const {
return num_free_regions() * HeapRegion::GrainBytes;
}
@@ -227,6 +227,9 @@
// this.
virtual uint expand_at(uint start, uint num_regions, WorkGang* pretouch_workers);
+ // Try to expand on the given node index.
+ virtual uint expand_on_preferred_node(uint node_index);
+
// Find a contiguous set of empty regions of length num. Returns the start index of
// that set, or G1_NO_HRM_INDEX.
virtual uint find_contiguous_only_empty(size_t num) { return find_contiguous(num, true); }
--- a/src/hotspot/share/gc/g1/heapRegionSet.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/heapRegionSet.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
+#include "gc/g1/g1NUMA.hpp"
#include "gc/g1/heapRegionRemSet.hpp"
#include "gc/g1/heapRegionSet.inline.hpp"
@@ -101,6 +102,9 @@
curr->set_next(NULL);
curr->set_prev(NULL);
curr->set_containing_set(NULL);
+
+ decrease_length(curr->node_index());
+
curr = next;
}
clear();
@@ -119,6 +123,10 @@
return;
}
+ if (_node_info != NULL && from_list->_node_info != NULL) {
+ _node_info->add(from_list->_node_info);
+ }
+
#ifdef ASSERT
FreeRegionListIterator iter(from_list);
while (iter.more_available()) {
@@ -220,6 +228,9 @@
remove(curr);
count++;
+
+ decrease_length(curr->node_index());
+
curr = next;
}
@@ -267,6 +278,10 @@
_head = NULL;
_tail = NULL;
_last = NULL;
+
+ if (_node_info!= NULL) {
+ _node_info->clear();
+ }
}
void FreeRegionList::verify_list() {
@@ -303,3 +318,41 @@
guarantee(_tail == NULL || _tail->next() == NULL, "_tail should not have a next");
guarantee(length() == count, "%s count mismatch. Expected %u, actual %u.", name(), length(), count);
}
+
+
+FreeRegionList::FreeRegionList(const char* name, HeapRegionSetChecker* checker):
+ HeapRegionSetBase(name, checker),
+ _node_info(G1NUMA::numa()->is_enabled() ? new NodeInfo() : NULL) {
+
+ clear();
+}
+
+FreeRegionList::~FreeRegionList() {
+ if (_node_info != NULL) {
+ delete _node_info;
+ }
+}
+
+FreeRegionList::NodeInfo::NodeInfo() : _numa(G1NUMA::numa()), _length_of_node(NULL),
+ _num_nodes(_numa->num_active_nodes()) {
+ assert(UseNUMA, "Invariant");
+
+ _length_of_node = NEW_C_HEAP_ARRAY(uint, _num_nodes, mtGC);
+}
+
+FreeRegionList::NodeInfo::~NodeInfo() {
+ FREE_C_HEAP_ARRAY(uint, _length_of_node);
+}
+
+void FreeRegionList::NodeInfo::clear() {
+ for (uint i = 0; i < _num_nodes; ++i) {
+ _length_of_node[i] = 0;
+ }
+}
+
+void FreeRegionList::NodeInfo::add(NodeInfo* info) {
+ for (uint i = 0; i < _num_nodes; ++i) {
+ _length_of_node[i] += info->_length_of_node[i];
+ }
+}
+
--- a/src/hotspot/share/gc/g1/heapRegionSet.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/heapRegionSet.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -136,11 +136,33 @@
// add / remove one region at a time or concatenate two lists.
class FreeRegionListIterator;
+class G1NUMA;
class FreeRegionList : public HeapRegionSetBase {
friend class FreeRegionListIterator;
private:
+
+ // This class is only initialized if there are multiple active nodes.
+ class NodeInfo : public CHeapObj<mtGC> {
+ G1NUMA* _numa;
+ uint* _length_of_node;
+ uint _num_nodes;
+
+ public:
+ NodeInfo();
+ ~NodeInfo();
+
+ inline void increase_length(uint node_index);
+ inline void decrease_length(uint node_index);
+
+ inline uint length(uint index) const;
+
+ void clear();
+
+ void add(NodeInfo* info);
+ };
+
HeapRegion* _head;
HeapRegion* _tail;
@@ -148,20 +170,23 @@
// time. It helps to improve performance when adding several ordered items in a row.
HeapRegion* _last;
+ NodeInfo* _node_info;
+
static uint _unrealistically_long_length;
inline HeapRegion* remove_from_head_impl();
inline HeapRegion* remove_from_tail_impl();
+ inline void increase_length(uint node_index);
+ inline void decrease_length(uint node_index);
+
protected:
// See the comment for HeapRegionSetBase::clear()
virtual void clear();
public:
- FreeRegionList(const char* name, HeapRegionSetChecker* checker = NULL):
- HeapRegionSetBase(name, checker) {
- clear();
- }
+ FreeRegionList(const char* name, HeapRegionSetChecker* checker = NULL);
+ ~FreeRegionList();
void verify_list();
@@ -181,6 +206,9 @@
// Removes from head or tail based on the given argument.
HeapRegion* remove_region(bool from_head);
+ HeapRegion* remove_region_with_node_index(bool from_head,
+ uint requested_node_index);
+
// Merge two ordered lists. The result is also ordered. The order is
// determined by hrm_index.
void add_ordered(FreeRegionList* from_list);
@@ -196,6 +224,9 @@
virtual void verify();
uint num_of_regions_in_range(uint start, uint end) const;
+
+ using HeapRegionSetBase::length;
+ uint length(uint node_index) const;
};
// Iterator class that provides a convenient way to iterate over the
--- a/src/hotspot/share/gc/g1/heapRegionSet.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/heapRegionSet.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -25,6 +25,7 @@
#ifndef SHARE_GC_G1_HEAPREGIONSET_INLINE_HPP
#define SHARE_GC_G1_HEAPREGIONSET_INLINE_HPP
+#include "gc/g1/g1NUMA.hpp"
#include "gc/g1/heapRegionSet.hpp"
inline void HeapRegionSetBase::add(HeapRegion* hr) {
@@ -94,6 +95,8 @@
_head = hr;
}
_last = hr;
+
+ increase_length(hr->node_index());
}
inline HeapRegion* FreeRegionList::remove_from_head_impl() {
@@ -144,7 +147,107 @@
// remove() will verify the region and check mt safety.
remove(hr);
+
+ decrease_length(hr->node_index());
+
return hr;
}
+inline HeapRegion* FreeRegionList::remove_region_with_node_index(bool from_head,
+ uint requested_node_index) {
+ assert(UseNUMA, "Invariant");
+
+ const uint max_search_depth = G1NUMA::numa()->max_search_depth();
+ HeapRegion* cur;
+
+ // Find the region to use, searching from _head or _tail as requested.
+ size_t cur_depth = 0;
+ if (from_head) {
+ for (cur = _head;
+ cur != NULL && cur_depth < max_search_depth;
+ cur = cur->next(), ++cur_depth) {
+ if (requested_node_index == cur->node_index()) {
+ break;
+ }
+ }
+ } else {
+ for (cur = _tail;
+ cur != NULL && cur_depth < max_search_depth;
+ cur = cur->prev(), ++cur_depth) {
+ if (requested_node_index == cur->node_index()) {
+ break;
+ }
+ }
+ }
+
+ // Didn't find a region to use.
+ if (cur == NULL || cur_depth >= max_search_depth) {
+ return NULL;
+ }
+
+ // Splice the region out of the list.
+ HeapRegion* prev = cur->prev();
+ HeapRegion* next = cur->next();
+ if (prev == NULL) {
+ _head = next;
+ } else {
+ prev->set_next(next);
+ }
+ if (next == NULL) {
+ _tail = prev;
+ } else {
+ next->set_prev(prev);
+ }
+ cur->set_prev(NULL);
+ cur->set_next(NULL);
+
+ if (_last == cur) {
+ _last = NULL;
+ }
+
+ remove(cur);
+ decrease_length(cur->node_index());
+
+ return cur;
+}
+
+inline void FreeRegionList::NodeInfo::increase_length(uint node_index) {
+ if (node_index < _num_nodes) {
+ _length_of_node[node_index] += 1;
+ }
+}
+
+inline void FreeRegionList::NodeInfo::decrease_length(uint node_index) {
+ if (node_index < _num_nodes) {
+ assert(_length_of_node[node_index] > 0,
+ "Current length %u should be greater than zero for node %u",
+ _length_of_node[node_index], node_index);
+ _length_of_node[node_index] -= 1;
+ }
+}
+
+inline uint FreeRegionList::NodeInfo::length(uint node_index) const {
+ return _length_of_node[node_index];
+}
+
+inline void FreeRegionList::increase_length(uint node_index) {
+ if (_node_info != NULL) {
+ return _node_info->increase_length(node_index);
+ }
+}
+
+inline void FreeRegionList::decrease_length(uint node_index) {
+ if (_node_info != NULL) {
+ return _node_info->decrease_length(node_index);
+ }
+}
+
+inline uint FreeRegionList::length(uint node_index) const {
+ if (_node_info != NULL) {
+ return _node_info->length(node_index);
+ } else {
+ return 0;
+ }
+}
+
#endif // SHARE_GC_G1_HEAPREGIONSET_INLINE_HPP
--- a/src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -263,7 +263,7 @@
return num_regions_found;
}
-HeapRegion* HeterogeneousHeapRegionManager::allocate_free_region(HeapRegionType type) {
+HeapRegion* HeterogeneousHeapRegionManager::allocate_free_region(HeapRegionType type, uint node_index) {
// We want to prevent mutators from proceeding when we have borrowed regions from the last collection. This
// will force a full collection to remedy the situation.
--- a/src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -119,7 +119,7 @@
void prepare_for_full_collection_start();
void prepare_for_full_collection_end();
- virtual HeapRegion* allocate_free_region(HeapRegionType type);
+ virtual HeapRegion* allocate_free_region(HeapRegionType type, uint node_index);
// Return maximum number of regions that heap can expand to.
uint max_expandable_length() const;
--- a/src/hotspot/share/gc/parallel/psPromotionManager.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/parallel/psPromotionManager.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -102,8 +102,7 @@
// partially-scanned arrays (in the latter case, we push an oop to
// the from-space image of the array and the length on the
// from-space image indicates how many entries on the array we still
- // need to scan; this is basically how ParNew does partial array
- // scanning too). To be able to distinguish between reference
+ // need to scan. To be able to distinguish between reference
// locations and partially-scanned array oops we simply mask the
// latter oops with 0x01. The next three methods do the masking,
// unmasking, and checking whether the oop is masked or not. Notice
--- a/src/hotspot/share/gc/serial/defNewGeneration.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -66,8 +66,7 @@
// Methods of protected closure types.
DefNewGeneration::IsAliveClosure::IsAliveClosure(Generation* young_gen) : _young_gen(young_gen) {
- assert(_young_gen->kind() == Generation::ParNew ||
- _young_gen->kind() == Generation::DefNew, "Expected the young generation here");
+ assert(_young_gen->kind() == Generation::DefNew, "Expected the young generation here");
}
bool DefNewGeneration::IsAliveClosure::do_object_b(oop p) {
@@ -884,7 +883,6 @@
log_trace(gc)("DefNewEpilogue: cause(%s), not full, seen_failed, will_clear_seen_failed",
GCCause::to_string(gch->gc_cause()));
assert(gch->gc_cause() == GCCause::_scavenge_alot ||
- (GCCause::is_user_requested_gc(gch->gc_cause()) && UseConcMarkSweepGC && ExplicitGCInvokesConcurrent) ||
!gch->incremental_collection_failed(),
"Twice in a row");
seen_incremental_collection_failed = false;
--- a/src/hotspot/share/gc/serial/tenuredGeneration.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/serial/tenuredGeneration.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -39,9 +39,6 @@
#include "oops/oop.inline.hpp"
#include "runtime/java.hpp"
#include "utilities/macros.hpp"
-#if INCLUDE_CMSGC
-#include "gc/cms/parOopClosures.hpp"
-#endif
TenuredGeneration::TenuredGeneration(ReservedSpace rs,
size_t initial_byte_size,
--- a/src/hotspot/share/gc/shared/adaptiveSizePolicy.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/adaptiveSizePolicy.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -39,13 +39,11 @@
class AdaptiveSizePolicy : public CHeapObj<mtGC> {
friend class GCAdaptivePolicyCounters;
friend class PSGCAdaptivePolicyCounters;
- friend class CMSGCAdaptivePolicyCounters;
protected:
enum GCPolicyKind {
_gc_adaptive_size_policy,
- _gc_ps_adaptive_size_policy,
- _gc_cms_adaptive_size_policy
+ _gc_ps_adaptive_size_policy
};
virtual GCPolicyKind kind() const { return _gc_adaptive_size_policy; }
@@ -77,7 +75,7 @@
// Last calculated sizes, in bytes, and aligned
size_t _eden_size; // calculated eden free space in bytes
- size_t _promo_size; // calculated cms gen free space in bytes
+ size_t _promo_size; // calculated promoted free space in bytes
size_t _survivor_size; // calculated survivor size in bytes
@@ -122,7 +120,7 @@
// Variables for estimating the major and minor collection costs
// minor collection time vs. young gen size
LinearLeastSquareFit* _minor_collection_estimator;
- // major collection time vs. cms gen size
+ // major collection time vs. old gen size
LinearLeastSquareFit* _major_collection_estimator;
// These record the most recent collection times. They
@@ -326,9 +324,6 @@
double gc_pause_goal_sec,
uint gc_cost_ratio);
- bool is_gc_cms_adaptive_size_policy() {
- return kind() == _gc_cms_adaptive_size_policy;
- }
bool is_gc_ps_adaptive_size_policy() {
return kind() == _gc_ps_adaptive_size_policy;
}
--- a/src/hotspot/share/gc/shared/blockOffsetTable.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/blockOffsetTable.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, 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
@@ -352,306 +352,6 @@
}
//////////////////////////////////////////////////////////////////////
-// BlockOffsetArrayNonContigSpace
-//////////////////////////////////////////////////////////////////////
-
-// The block [blk_start, blk_end) has been allocated;
-// adjust the block offset table to represent this information;
-// NOTE: Clients of BlockOffsetArrayNonContigSpace: consider using
-// the somewhat more lightweight split_block() or
-// (when init_to_zero()) mark_block() wherever possible.
-// right-open interval: [blk_start, blk_end)
-void
-BlockOffsetArrayNonContigSpace::alloc_block(HeapWord* blk_start,
- HeapWord* blk_end) {
- assert(blk_start != NULL && blk_end > blk_start,
- "phantom block");
- single_block(blk_start, blk_end);
- allocated(blk_start, blk_end);
-}
-
-// Adjust BOT to show that a previously whole block has been split
-// into two. We verify the BOT for the first part (prefix) and
-// update the BOT for the second part (suffix).
-// blk is the start of the block
-// blk_size is the size of the original block
-// left_blk_size is the size of the first part of the split
-void BlockOffsetArrayNonContigSpace::split_block(HeapWord* blk,
- size_t blk_size,
- size_t left_blk_size) {
- // Verify that the BOT shows [blk, blk + blk_size) to be one block.
- verify_single_block(blk, blk_size);
- // Update the BOT to indicate that [blk + left_blk_size, blk + blk_size)
- // is one single block.
- assert(blk_size > 0, "Should be positive");
- assert(left_blk_size > 0, "Should be positive");
- assert(left_blk_size < blk_size, "Not a split");
-
- // Start addresses of prefix block and suffix block.
- HeapWord* pref_addr = blk;
- HeapWord* suff_addr = blk + left_blk_size;
- HeapWord* end_addr = blk + blk_size;
-
- // Indices for starts of prefix block and suffix block.
- size_t pref_index = _array->index_for(pref_addr);
- if (_array->address_for_index(pref_index) != pref_addr) {
- // pref_addr does not begin pref_index
- pref_index++;
- }
-
- size_t suff_index = _array->index_for(suff_addr);
- if (_array->address_for_index(suff_index) != suff_addr) {
- // suff_addr does not begin suff_index
- suff_index++;
- }
-
- // Definition: A block B, denoted [B_start, B_end) __starts__
- // a card C, denoted [C_start, C_end), where C_start and C_end
- // are the heap addresses that card C covers, iff
- // B_start <= C_start < B_end.
- //
- // We say that a card C "is started by" a block B, iff
- // B "starts" C.
- //
- // Note that the cardinality of the set of cards {C}
- // started by a block B can be 0, 1, or more.
- //
- // Below, pref_index and suff_index are, respectively, the
- // first (least) card indices that the prefix and suffix of
- // the split start; end_index is one more than the index of
- // the last (greatest) card that blk starts.
- size_t end_index = _array->index_for(end_addr - 1) + 1;
-
- // Calculate the # cards that the prefix and suffix affect.
- size_t num_pref_cards = suff_index - pref_index;
-
- size_t num_suff_cards = end_index - suff_index;
- // Change the cards that need changing
- if (num_suff_cards > 0) {
- HeapWord* boundary = _array->address_for_index(suff_index);
- // Set the offset card for suffix block
- _array->set_offset_array(suff_index, boundary, suff_addr, true /* reducing */);
- // Change any further cards that need changing in the suffix
- if (num_pref_cards > 0) {
- if (num_pref_cards >= num_suff_cards) {
- // Unilaterally fix all of the suffix cards: closed card
- // index interval in args below.
- set_remainder_to_point_to_start_incl(suff_index + 1, end_index - 1, true /* reducing */);
- } else {
- // Unilaterally fix the first (num_pref_cards - 1) following
- // the "offset card" in the suffix block.
- const size_t right_most_fixed_index = suff_index + num_pref_cards - 1;
- set_remainder_to_point_to_start_incl(suff_index + 1,
- right_most_fixed_index, true /* reducing */);
- // Fix the appropriate cards in the remainder of the
- // suffix block -- these are the last num_pref_cards
- // cards in each power block of the "new" range plumbed
- // from suff_addr.
- bool more = true;
- uint i = 1;
- // Fix the first power block with back_by > num_pref_cards.
- while (more && (i < BOTConstants::N_powers)) {
- size_t back_by = BOTConstants::power_to_cards_back(i);
- size_t right_index = suff_index + back_by - 1;
- size_t left_index = right_index - num_pref_cards + 1;
- if (right_index >= end_index - 1) { // last iteration
- right_index = end_index - 1;
- more = false;
- }
- if (left_index <= right_most_fixed_index) {
- left_index = right_most_fixed_index + 1;
- }
- if (back_by > num_pref_cards) {
- // Fill in the remainder of this "power block", if it
- // is non-null.
- if (left_index <= right_index) {
- _array->set_offset_array(left_index, right_index,
- BOTConstants::N_words + i - 1, true /* reducing */);
- } else {
- more = false; // we are done
- assert((end_index - 1) == right_index, "Must be at the end.");
- }
- i++;
- break;
- }
- i++;
- }
- // Fix the rest of the power blocks.
- while (more && (i < BOTConstants::N_powers)) {
- size_t back_by = BOTConstants::power_to_cards_back(i);
- size_t right_index = suff_index + back_by - 1;
- size_t left_index = right_index - num_pref_cards + 1;
- if (right_index >= end_index - 1) { // last iteration
- right_index = end_index - 1;
- if (left_index > right_index) {
- break;
- }
- more = false;
- }
- assert(left_index <= right_index, "Error");
- _array->set_offset_array(left_index, right_index, BOTConstants::N_words + i - 1, true /* reducing */);
- i++;
- }
- }
- } // else no more cards to fix in suffix
- } // else nothing needs to be done
- // Verify that we did the right thing
- verify_single_block(pref_addr, left_blk_size);
- verify_single_block(suff_addr, blk_size - left_blk_size);
-}
-
-
-// Mark the BOT such that if [blk_start, blk_end) straddles a card
-// boundary, the card following the first such boundary is marked
-// with the appropriate offset.
-// NOTE: this method does _not_ adjust _unallocated_block or
-// any cards subsequent to the first one.
-void
-BlockOffsetArrayNonContigSpace::mark_block(HeapWord* blk_start,
- HeapWord* blk_end, bool reducing) {
- do_block_internal(blk_start, blk_end, Action_mark, reducing);
-}
-
-HeapWord* BlockOffsetArrayNonContigSpace::block_start_unsafe(
- const void* addr) const {
- assert(_array->offset_array(0) == 0, "objects can't cross covered areas");
- assert(_bottom <= addr && addr < _end,
- "addr must be covered by this Array");
- // Must read this exactly once because it can be modified by parallel
- // allocation.
- HeapWord* ub = _unallocated_block;
- if (BlockOffsetArrayUseUnallocatedBlock && addr >= ub) {
- assert(ub < _end, "tautology (see above)");
- return ub;
- }
-
- // Otherwise, find the block start using the table.
- size_t index = _array->index_for(addr);
- HeapWord* q = _array->address_for_index(index);
-
- uint offset = _array->offset_array(index); // Extend u_char to uint.
- while (offset >= BOTConstants::N_words) {
- // The excess of the offset from N_words indicates a power of Base
- // to go back by.
- size_t n_cards_back = BOTConstants::entry_to_cards_back(offset);
- q -= (BOTConstants::N_words * n_cards_back);
- assert(q >= _sp->bottom(),
- "q = " PTR_FORMAT " crossed below bottom = " PTR_FORMAT,
- p2i(q), p2i(_sp->bottom()));
- assert(q < _sp->end(),
- "q = " PTR_FORMAT " crossed above end = " PTR_FORMAT,
- p2i(q), p2i(_sp->end()));
- index -= n_cards_back;
- offset = _array->offset_array(index);
- }
- assert(offset < BOTConstants::N_words, "offset too large");
- index--;
- q -= offset;
- assert(q >= _sp->bottom(),
- "q = " PTR_FORMAT " crossed below bottom = " PTR_FORMAT,
- p2i(q), p2i(_sp->bottom()));
- assert(q < _sp->end(),
- "q = " PTR_FORMAT " crossed above end = " PTR_FORMAT,
- p2i(q), p2i(_sp->end()));
- HeapWord* n = q;
-
- while (n <= addr) {
- debug_only(HeapWord* last = q); // for debugging
- q = n;
- n += _sp->block_size(n);
- assert(n > q,
- "Looping at n = " PTR_FORMAT " with last = " PTR_FORMAT ","
- " while querying blk_start(" PTR_FORMAT ")"
- " on _sp = [" PTR_FORMAT "," PTR_FORMAT ")",
- p2i(n), p2i(last), p2i(addr), p2i(_sp->bottom()), p2i(_sp->end()));
- }
- assert(q <= addr,
- "wrong order for current (" INTPTR_FORMAT ")" " <= arg (" INTPTR_FORMAT ")",
- p2i(q), p2i(addr));
- assert(addr <= n,
- "wrong order for arg (" INTPTR_FORMAT ") <= next (" INTPTR_FORMAT ")",
- p2i(addr), p2i(n));
- return q;
-}
-
-HeapWord* BlockOffsetArrayNonContigSpace::block_start_careful(
- const void* addr) const {
- assert(_array->offset_array(0) == 0, "objects can't cross covered areas");
-
- assert(_bottom <= addr && addr < _end,
- "addr must be covered by this Array");
- // Must read this exactly once because it can be modified by parallel
- // allocation.
- HeapWord* ub = _unallocated_block;
- if (BlockOffsetArrayUseUnallocatedBlock && addr >= ub) {
- assert(ub < _end, "tautology (see above)");
- return ub;
- }
-
- // Otherwise, find the block start using the table, but taking
- // care (cf block_start_unsafe() above) not to parse any objects/blocks
- // on the cards themselves.
- size_t index = _array->index_for(addr);
- assert(_array->address_for_index(index) == addr,
- "arg should be start of card");
-
- HeapWord* q = (HeapWord*)addr;
- uint offset;
- do {
- offset = _array->offset_array(index);
- if (offset < BOTConstants::N_words) {
- q -= offset;
- } else {
- size_t n_cards_back = BOTConstants::entry_to_cards_back(offset);
- q -= (n_cards_back * BOTConstants::N_words);
- index -= n_cards_back;
- }
- } while (offset >= BOTConstants::N_words);
- assert(q <= addr, "block start should be to left of arg");
- return q;
-}
-
-#ifndef PRODUCT
-// Verification & debugging - ensure that the offset table reflects the fact
-// that the block [blk_start, blk_end) or [blk, blk + size) is a
-// single block of storage. NOTE: can't const this because of
-// call to non-const do_block_internal() below.
-void BlockOffsetArrayNonContigSpace::verify_single_block(
- HeapWord* blk_start, HeapWord* blk_end) {
- if (VerifyBlockOffsetArray) {
- do_block_internal(blk_start, blk_end, Action_check);
- }
-}
-
-void BlockOffsetArrayNonContigSpace::verify_single_block(
- HeapWord* blk, size_t size) {
- verify_single_block(blk, blk + size);
-}
-
-// Verify that the given block is before _unallocated_block
-void BlockOffsetArrayNonContigSpace::verify_not_unallocated(
- HeapWord* blk_start, HeapWord* blk_end) const {
- if (BlockOffsetArrayUseUnallocatedBlock) {
- assert(blk_start < blk_end, "Block inconsistency?");
- assert(blk_end <= _unallocated_block, "_unallocated_block problem");
- }
-}
-
-void BlockOffsetArrayNonContigSpace::verify_not_unallocated(
- HeapWord* blk, size_t size) const {
- verify_not_unallocated(blk, blk + size);
-}
-#endif // PRODUCT
-
-size_t BlockOffsetArrayNonContigSpace::last_active_index() const {
- if (_unallocated_block == _bottom) {
- return 0;
- } else {
- return _array->index_for(_unallocated_block - 1);
- }
-}
-
-//////////////////////////////////////////////////////////////////////
// BlockOffsetArrayContigSpace
//////////////////////////////////////////////////////////////////////
--- a/src/hotspot/share/gc/shared/blockOffsetTable.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/blockOffsetTable.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -41,7 +41,6 @@
//
// BlockOffsetTable (abstract)
// - BlockOffsetArray (abstract)
-// - BlockOffsetArrayNonContigSpace
// - BlockOffsetArrayContigSpace
//
@@ -155,12 +154,6 @@
void* start_ptr = &_offset_array[start];
// If collector is concurrent, special handling may be needed.
G1GC_ONLY(assert(!UseG1GC, "Shouldn't be here when using G1");)
-#if INCLUDE_CMSGC
- if (UseConcMarkSweepGC) {
- memset_with_concurrent_readers(start_ptr, offset, num_cards);
- return;
- }
-#endif // INCLUDE_CMSGC
memset(start_ptr, offset, num_cards);
}
@@ -389,111 +382,6 @@
////////////////////////////////////////////////////////////////////////////
// A subtype of BlockOffsetArray that takes advantage of the fact
-// that its underlying space is a NonContiguousSpace, so that some
-// specialized interfaces can be made available for spaces that
-// manipulate the table.
-////////////////////////////////////////////////////////////////////////////
-class BlockOffsetArrayNonContigSpace: public BlockOffsetArray {
- friend class VMStructs;
- private:
- // The portion [_unallocated_block, _sp.end()) of the space that
- // is a single block known not to contain any objects.
- // NOTE: See BlockOffsetArrayUseUnallocatedBlock flag.
- HeapWord* _unallocated_block;
-
- public:
- BlockOffsetArrayNonContigSpace(BlockOffsetSharedArray* array, MemRegion mr):
- BlockOffsetArray(array, mr, false),
- _unallocated_block(_bottom) { }
-
- // Accessor
- HeapWord* unallocated_block() const {
- assert(BlockOffsetArrayUseUnallocatedBlock,
- "_unallocated_block is not being maintained");
- return _unallocated_block;
- }
-
- void set_unallocated_block(HeapWord* block) {
- assert(BlockOffsetArrayUseUnallocatedBlock,
- "_unallocated_block is not being maintained");
- assert(block >= _bottom && block <= _end, "out of range");
- _unallocated_block = block;
- }
-
- // These methods expect to be called with [blk_start, blk_end)
- // representing a block of memory in the heap.
- void alloc_block(HeapWord* blk_start, HeapWord* blk_end);
- void alloc_block(HeapWord* blk, size_t size) {
- alloc_block(blk, blk + size);
- }
-
- // The following methods are useful and optimized for a
- // non-contiguous space.
-
- // Given a block [blk_start, blk_start + full_blk_size), and
- // a left_blk_size < full_blk_size, adjust the BOT to show two
- // blocks [blk_start, blk_start + left_blk_size) and
- // [blk_start + left_blk_size, blk_start + full_blk_size).
- // It is assumed (and verified in the non-product VM) that the
- // BOT was correct for the original block.
- void split_block(HeapWord* blk_start, size_t full_blk_size,
- size_t left_blk_size);
-
- // Adjust BOT to show that it has a block in the range
- // [blk_start, blk_start + size). Only the first card
- // of BOT is touched. It is assumed (and verified in the
- // non-product VM) that the remaining cards of the block
- // are correct.
- void mark_block(HeapWord* blk_start, HeapWord* blk_end, bool reducing = false);
- void mark_block(HeapWord* blk, size_t size, bool reducing = false) {
- mark_block(blk, blk + size, reducing);
- }
-
- // Adjust _unallocated_block to indicate that a particular
- // block has been newly allocated or freed. It is assumed (and
- // verified in the non-product VM) that the BOT is correct for
- // the given block.
- void allocated(HeapWord* blk_start, HeapWord* blk_end, bool reducing = false) {
- // Verify that the BOT shows [blk, blk + blk_size) to be one block.
- verify_single_block(blk_start, blk_end);
- if (BlockOffsetArrayUseUnallocatedBlock) {
- _unallocated_block = MAX2(_unallocated_block, blk_end);
- }
- }
-
- void allocated(HeapWord* blk, size_t size, bool reducing = false) {
- allocated(blk, blk + size, reducing);
- }
-
- void freed(HeapWord* blk_start, HeapWord* blk_end);
- void freed(HeapWord* blk, size_t size);
-
- HeapWord* block_start_unsafe(const void* addr) const;
-
- // Requires "addr" to be the start of a card and returns the
- // start of the block that contains the given address.
- HeapWord* block_start_careful(const void* addr) const;
-
- // Verification & debugging: ensure that the offset table reflects
- // the fact that the block [blk_start, blk_end) or [blk, blk + size)
- // is a single block of storage. NOTE: can't const this because of
- // call to non-const do_block_internal() below.
- void verify_single_block(HeapWord* blk_start, HeapWord* blk_end)
- PRODUCT_RETURN;
- void verify_single_block(HeapWord* blk, size_t size) PRODUCT_RETURN;
-
- // Verify that the given block is before _unallocated_block
- void verify_not_unallocated(HeapWord* blk_start, HeapWord* blk_end)
- const PRODUCT_RETURN;
- void verify_not_unallocated(HeapWord* blk, size_t size)
- const PRODUCT_RETURN;
-
- // Debugging support
- virtual size_t last_active_index() const;
-};
-
-////////////////////////////////////////////////////////////////////////////
-// A subtype of BlockOffsetArray that takes advantage of the fact
// that its underlying space is a ContiguousSpace, so that its "active"
// region can be more efficiently tracked (than for a non-contiguous space).
////////////////////////////////////////////////////////////////////////////
--- a/src/hotspot/share/gc/shared/blockOffsetTable.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/blockOffsetTable.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -70,30 +70,4 @@
ParGCRareEvent_lock->owned_by_self()), "Crack");
}
-//////////////////////////////////////////////////////////////////////////
-// BlockOffsetArrayNonContigSpace inlines
-//////////////////////////////////////////////////////////////////////////
-inline void BlockOffsetArrayNonContigSpace::freed(HeapWord* blk,
- size_t size) {
- freed(blk, blk + size);
-}
-
-inline void BlockOffsetArrayNonContigSpace::freed(HeapWord* blk_start,
- HeapWord* blk_end) {
- // Verify that the BOT shows [blk_start, blk_end) to be one block.
- verify_single_block(blk_start, blk_end);
- // adjust _unallocated_block upward or downward
- // as appropriate
- if (BlockOffsetArrayUseUnallocatedBlock) {
- assert(_unallocated_block <= _end,
- "Inconsistent value for _unallocated_block");
- if (blk_end >= _unallocated_block && blk_start <= _unallocated_block) {
- // CMS-specific note: a block abutting _unallocated_block to
- // its left is being freed, a new block is being added or
- // we are resetting following a compaction
- _unallocated_block = blk_start;
- }
- }
-}
-
#endif // SHARE_GC_SHARED_BLOCKOFFSETTABLE_INLINE_HPP
--- a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -45,7 +45,7 @@
}
}
-// vanilla/CMS post barrier
+// vanilla post barrier
// Insert a write-barrier store. This is to let generational GC work; we have
// to flag all oop-stores before the next GC point.
void CardTableBarrierSetC2::post_barrier(GraphKit* kit,
--- a/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -96,7 +96,7 @@
// to a newly allocated object along the fast-path. We
// compensate for such elided card-marks as follows:
// (a) Generational, non-concurrent collectors, such as
-// GenCollectedHeap(ParNew,DefNew,Tenured) and
+// GenCollectedHeap(DefNew,Tenured) and
// ParallelScavengeHeap(ParallelGC, ParallelOldGC)
// need the card-mark if and only if the region is
// in the old gen, and do not care if the card-mark
@@ -105,17 +105,7 @@
// scavenge. For all these cases, we can do a card mark
// at the point at which we do a slow path allocation
// in the old gen, i.e. in this call.
-// (b) GenCollectedHeap(ConcurrentMarkSweepGeneration) requires
-// in addition that the card-mark for an old gen allocated
-// object strictly follow any associated initializing stores.
-// In these cases, the memRegion remembered below is
-// used to card-mark the entire region either just before the next
-// slow-path allocation by this thread or just before the next scavenge or
-// CMS-associated safepoint, whichever of these events happens first.
-// (The implicit assumption is that the object has been fully
-// initialized by this point, a fact that we assert when doing the
-// card-mark.)
-// (c) G1CollectedHeap(G1) uses two kinds of write barriers. When a
+// (b) G1CollectedHeap(G1) uses two kinds of write barriers. When a
// G1 concurrent marking is in progress an SATB (pre-write-)barrier
// is used to remember the pre-value of any store. Initializing
// stores will not need this barrier, so we need not worry about
@@ -124,11 +114,8 @@
// which simply enqueues a (sequence of) dirty cards which may
// optionally be refined by the concurrent update threads. Note
// that this barrier need only be applied to a non-young write,
-// but, like in CMS, because of the presence of concurrent refinement
-// (much like CMS' precleaning), must strictly follow the oop-store.
-// Thus, using the same protocol for maintaining the intended
-// invariants turns out, serendepitously, to be the same for both
-// G1 and CMS.
+// but, because of the presence of concurrent refinement,
+// must strictly follow the oop-store.
//
// For any future collector, this code should be reexamined with
// that specific collector in mind, and the documentation above suitably
--- a/src/hotspot/share/gc/shared/collectedHeap.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/collectedHeap.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -335,9 +335,9 @@
#ifndef PRODUCT
void CollectedHeap::check_for_non_bad_heap_word_value(HeapWord* addr, size_t size) {
if (CheckMemoryInitialization && ZapUnusedHeapArea) {
- for (size_t slot = 0; slot < size; slot += 1) {
- assert((*(intptr_t*) (addr + slot)) == ((intptr_t) badHeapWordVal),
- "Found non badHeapWordValue in pre-allocation check");
+ // please note mismatch between size (in 32/64 bit words), and ju_addr that always point to a 32 bit word
+ for (juint* ju_addr = reinterpret_cast<juint*>(addr); ju_addr < reinterpret_cast<juint*>(addr + size); ++ju_addr) {
+ assert(*ju_addr == badHeapWordVal, "Found non badHeapWordValue in pre-allocation check");
}
}
}
--- a/src/hotspot/share/gc/shared/collectedHeap.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/collectedHeap.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -88,7 +88,6 @@
// CollectedHeap
// GenCollectedHeap
// SerialHeap
-// CMSHeap
// G1CollectedHeap
// ParallelScavengeHeap
// ShenandoahHeap
@@ -172,7 +171,6 @@
None,
Serial,
Parallel,
- CMS,
G1,
Epsilon,
Z,
--- a/src/hotspot/share/gc/shared/gcArguments.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/gcArguments.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -101,7 +101,7 @@
"AllocateHeapAt and AllocateOldGenAt cannot be used together.\n");
status = false;
}
- if (!FLAG_IS_DEFAULT(AllocateOldGenAt) && (UseSerialGC || UseConcMarkSweepGC || UseEpsilonGC || UseZGC)) {
+ if (!FLAG_IS_DEFAULT(AllocateOldGenAt) && (UseSerialGC || UseEpsilonGC || UseZGC)) {
jio_fprintf(defaultStream::error_stream(),
"AllocateOldGenAt is not supported for selected GC.\n");
status = false;
--- a/src/hotspot/share/gc/shared/gcCause.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/gcCause.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -78,18 +78,6 @@
case _metadata_GC_clear_soft_refs:
return "Metadata GC Clear Soft References";
- case _cms_generation_full:
- return "CMS Generation Full";
-
- case _cms_initial_mark:
- return "CMS Initial Mark";
-
- case _cms_final_remark:
- return "CMS Final Remark";
-
- case _cms_concurrent_mark:
- return "CMS Concurrent Mark";
-
case _old_generation_expanded_on_last_scavenge:
return "Old Generation Expanded On Last Scavenge";
--- a/src/hotspot/share/gc/shared/gcCause.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/gcCause.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -65,11 +65,6 @@
_metadata_GC_threshold,
_metadata_GC_clear_soft_refs,
- _cms_generation_full,
- _cms_initial_mark,
- _cms_final_remark,
- _cms_concurrent_mark,
-
_old_generation_expanded_on_last_scavenge,
_old_generation_too_full_to_scavenge,
_adaptive_size_policy,
@@ -114,13 +109,12 @@
cause != GCCause::_old_generation_expanded_on_last_scavenge,
"This GCCause may be correct but is not expected yet: %s",
to_string(cause));
- // _tenured_generation_full or _cms_generation_full for full tenured generations
+ // _tenured_generation_full for full tenured generations
// _adaptive_size_policy for a full collection after a young GC
// _allocation_failure is the generic cause a collection which could result
// in the collection of the tenured generation if there is not enough space
// in the tenured generation to support a young GC.
return (cause == GCCause::_tenured_generation_full ||
- cause == GCCause::_cms_generation_full ||
cause == GCCause::_adaptive_size_policy ||
cause == GCCause::_allocation_failure);
}
--- a/src/hotspot/share/gc/shared/gcConfig.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/gcConfig.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -28,9 +28,6 @@
#include "runtime/java.hpp"
#include "runtime/os.hpp"
#include "utilities/macros.hpp"
-#if INCLUDE_CMSGC
-#include "gc/cms/cmsArguments.hpp"
-#endif
#if INCLUDE_EPSILONGC
#include "gc/epsilon/epsilonArguments.hpp"
#endif
@@ -60,7 +57,6 @@
_flag(flag), _name(name), _arguments(arguments), _hs_err_name(hs_err_name) {}
};
- CMSGC_ONLY(static CMSArguments cmsArguments;)
EPSILONGC_ONLY(static EpsilonArguments epsilonArguments;)
G1GC_ONLY(static G1Arguments g1Arguments;)
PARALLELGC_ONLY(static ParallelArguments parallelArguments;)
@@ -71,7 +67,6 @@
// Table of supported GCs, for translating between command
// line flag, CollectedHeap::Name and GCArguments instance.
static const SupportedGC SupportedGCs[] = {
- CMSGC_ONLY_ARG(SupportedGC(UseConcMarkSweepGC, CollectedHeap::CMS, cmsArguments, "concurrent mark sweep gc"))
EPSILONGC_ONLY_ARG(SupportedGC(UseEpsilonGC, CollectedHeap::Epsilon, epsilonArguments, "epsilon gc"))
G1GC_ONLY_ARG(SupportedGC(UseG1GC, CollectedHeap::G1, g1Arguments, "g1 gc"))
PARALLELGC_ONLY_ARG(SupportedGC(UseParallelGC, CollectedHeap::Parallel, parallelArguments, "parallel gc"))
@@ -95,7 +90,6 @@
bool GCConfig::_gc_selected_ergonomically = false;
void GCConfig::fail_if_unsupported_gc_is_selected() {
- NOT_CMSGC( FAIL_IF_SELECTED(UseConcMarkSweepGC, true));
NOT_EPSILONGC( FAIL_IF_SELECTED(UseEpsilonGC, true));
NOT_G1GC( FAIL_IF_SELECTED(UseG1GC, true));
NOT_PARALLELGC( FAIL_IF_SELECTED(UseParallelGC, true));
--- a/src/hotspot/share/gc/shared/gcConfiguration.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/gcConfiguration.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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,10 +41,6 @@
return ParallelScavenge;
}
- if (UseConcMarkSweepGC) {
- return ParNew;
- }
-
if (UseZGC || UseShenandoahGC) {
return NA;
}
@@ -57,10 +53,6 @@
return G1Old;
}
- if (UseConcMarkSweepGC) {
- return ConcurrentMarkSweep;
- }
-
if (UseParallelOldGC) {
return ParallelOld;
}
--- a/src/hotspot/share/gc/shared/gcName.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/gcName.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -33,9 +33,7 @@
PSMarkSweep,
ParallelScavenge,
DefNew,
- ParNew,
G1New,
- ConcurrentMarkSweep,
G1Old,
G1Full,
Z,
@@ -53,9 +51,7 @@
case PSMarkSweep: return "PSMarkSweep";
case ParallelScavenge: return "ParallelScavenge";
case DefNew: return "DefNew";
- case ParNew: return "ParNew";
case G1New: return "G1New";
- case ConcurrentMarkSweep: return "ConcurrentMarkSweep";
case G1Old: return "G1Old";
case G1Full: return "G1Full";
case Z: return "Z";
--- a/src/hotspot/share/gc/shared/gcStats.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/gcStats.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -36,26 +36,7 @@
public:
GCStats();
- enum Name {
- GCStatsKind,
- CMSGCStatsKind
- };
-
- virtual Name kind() {
- return GCStatsKind;
- }
-
AdaptivePaddedNoZeroDevAverage* avg_promoted() const { return _avg_promoted; }
-
- // Average in bytes
- size_t average_promoted_in_bytes() const {
- return (size_t)_avg_promoted->average();
- }
-
- // Padded average in bytes
- size_t padded_average_promoted_in_bytes() const {
- return (size_t)_avg_promoted->padded_average();
- }
};
#endif // SHARE_GC_SHARED_GCSTATS_HPP
--- a/src/hotspot/share/gc/shared/gcTrace.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/gcTrace.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -207,14 +207,4 @@
DefNewTracer() : YoungGCTracer(DefNew) {}
};
-class ParNewTracer : public YoungGCTracer {
- public:
- ParNewTracer() : YoungGCTracer(ParNew) {}
-};
-
-class CMSTracer : public OldGCTracer {
- public:
- CMSTracer() : OldGCTracer(ConcurrentMarkSweep) {}
-};
-
#endif // SHARE_GC_SHARED_GCTRACE_HPP
--- a/src/hotspot/share/gc/shared/gcTraceSend.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/gcTraceSend.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -173,7 +173,7 @@
}
}
-// Common to CMS and G1
+// G1
void OldGCTracer::send_concurrent_mode_failure_event() {
EventConcurrentModeFailure e;
if (e.should_commit()) {
--- a/src/hotspot/share/gc/shared/gcVMOperations.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/gcVMOperations.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -192,13 +192,6 @@
// Returns true iff concurrent GCs unloads metadata.
bool VM_CollectForMetadataAllocation::initiate_concurrent_GC() {
-#if INCLUDE_CMSGC
- if (UseConcMarkSweepGC && CMSClassUnloadingEnabled) {
- MetaspaceGC::set_should_concurrent_collect(true);
- return true;
- }
-#endif
-
#if INCLUDE_G1GC
if (UseG1GC && ClassUnloadingWithConcurrentMark) {
G1CollectedHeap* g1h = G1CollectedHeap::heap();
@@ -238,13 +231,13 @@
}
if (initiate_concurrent_GC()) {
- // For CMS and G1 expand since the collection is going to be concurrent.
+ // For G1 expand since the collection is going to be concurrent.
_result = _loader_data->metaspace_non_null()->expand_and_allocate(_size, _mdtype);
if (_result != NULL) {
return;
}
- log_debug(gc)("%s full GC for Metaspace", UseConcMarkSweepGC ? "CMS" : "G1");
+ log_debug(gc)("G1 full GC for Metaspace");
}
// Don't clear the soft refs yet.
--- a/src/hotspot/share/gc/shared/gc_globals.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/gc_globals.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -27,9 +27,6 @@
#include "runtime/globals_shared.hpp"
#include "utilities/macros.hpp"
-#if INCLUDE_CMSGC
-#include "gc/cms/cms_globals.hpp"
-#endif
#if INCLUDE_EPSILONGC
#include "gc/epsilon/epsilon_globals.hpp"
#endif
@@ -64,22 +61,6 @@
constraint, \
writeable) \
\
- CMSGC_ONLY(GC_CMS_FLAGS( \
- develop, \
- develop_pd, \
- product, \
- product_pd, \
- diagnostic, \
- diagnostic_pd, \
- experimental, \
- notproduct, \
- manageable, \
- product_rw, \
- lp64_product, \
- range, \
- constraint, \
- writeable)) \
- \
EPSILONGC_ONLY(GC_EPSILON_FLAGS( \
develop, \
develop_pd, \
@@ -178,9 +159,6 @@
\
/* gc */ \
\
- product(bool, UseConcMarkSweepGC, false, \
- "Use Concurrent Mark-Sweep GC in the old generation") \
- \
product(bool, UseSerialGC, false, \
"Use the Serial garbage collector") \
\
@@ -286,14 +264,6 @@
"bigger than this") \
range(1, max_jint/3) \
\
- product(uintx, OldPLABWeight, 50, \
- "Percentage (0-100) used to weight the current sample when " \
- "computing exponentially decaying average for resizing " \
- "OldPLABSize") \
- range(0, 100) \
- \
- product(bool, ResizeOldPLAB, true, \
- "Dynamically resize (old gen) promotion LAB's") \
\
product(bool, AlwaysPreTouch, false, \
"Force all freshly committed pages to be pre-touched") \
@@ -311,13 +281,6 @@
"Size of marking stack") \
constraint(MarkStackSizeConstraintFunc,AfterErgo) \
\
- develop(bool, VerifyBlockOffsetArray, false, \
- "Do (expensive) block offset array verification") \
- \
- diagnostic(bool, BlockOffsetArrayUseUnallocatedBlock, false, \
- "Maintain _unallocated_block in BlockOffsetArray " \
- "(currently applicable only to CMS collector)") \
- \
product(intx, RefDiscoveryPolicy, 0, \
"Select type of reference discovery policy: " \
"reference-based(0) or referent-based(1)") \
@@ -364,9 +327,8 @@
"collection") \
\
develop(uintx, PromotionFailureALotCount, 1000, \
- "Number of promotion failures occurring at PLAB " \
- "refill attempts (ParNew) or promotion attempts " \
- "(other young collectors)") \
+ "Number of promotion failures occurring at PLAB promotion " \
+ "attempts at young collectors") \
\
develop(uintx, PromotionFailureALotInterval, 5, \
"Total collections between promotion failures a lot") \
@@ -759,8 +721,7 @@
constraint(YoungPLABSizeConstraintFunc,AfterMemoryInit) \
\
product(size_t, OldPLABSize, 1024, \
- "Size of old gen promotion LAB's (in HeapWords), or Number " \
- "of blocks to attempt to claim when refilling CMS LAB's") \
+ "Size of old gen promotion LAB's (in HeapWords)") \
constraint(OldPLABSizeConstraintFunc,AfterMemoryInit) \
\
product(uintx, TLABAllocationWeight, 35, \
@@ -827,7 +788,6 @@
"Percentage (0-100) of the old gen allowed as dead wood. " \
"Serial mark sweep treats this as both the minimum and maximum " \
"value. " \
- "CMS uses this value only if it falls back to mark sweep. " \
"Par compact uses a variable scale based on the density of the " \
"generation and treats this as the maximum value when the heap " \
"is either completely full or completely empty. Par compact " \
--- a/src/hotspot/share/gc/shared/genCollectedHeap.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/genCollectedHeap.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -418,35 +418,6 @@
}
}
-#ifndef PRODUCT
-// Override of memory state checking method in CollectedHeap:
-// Some collectors (CMS for example) can't have badHeapWordVal written
-// in the first two words of an object. (For instance , in the case of
-// CMS these words hold state used to synchronize between certain
-// (concurrent) GC steps and direct allocating mutators.)
-// The skip_header_HeapWords() method below, allows us to skip
-// over the requisite number of HeapWord's. Note that (for
-// generational collectors) this means that those many words are
-// skipped in each object, irrespective of the generation in which
-// that object lives. The resultant loss of precision seems to be
-// harmless and the pain of avoiding that imprecision appears somewhat
-// higher than we are prepared to pay for such rudimentary debugging
-// support.
-void GenCollectedHeap::check_for_non_bad_heap_word_value(HeapWord* addr,
- size_t size) {
- if (CheckMemoryInitialization && ZapUnusedHeapArea) {
- // We are asked to check a size in HeapWords,
- // but the memory is mangled in juint words.
- juint* start = (juint*) (addr + skip_header_HeapWords());
- juint* end = (juint*) (addr + size);
- for (juint* slot = start; slot < end; slot += 1) {
- assert(*slot == badHeapWordVal,
- "Found non badHeapWordValue in pre-allocation check");
- }
- }
-}
-#endif
-
HeapWord* GenCollectedHeap::attempt_allocation(size_t size,
bool is_tlab,
bool first_only) {
@@ -1229,8 +1200,7 @@
GenCollectedHeap* GenCollectedHeap::heap() {
CollectedHeap* heap = Universe::heap();
assert(heap != NULL, "Uninitialized access to GenCollectedHeap::heap()");
- assert(heap->kind() == CollectedHeap::Serial ||
- heap->kind() == CollectedHeap::CMS, "Invalid name");
+ assert(heap->kind() == CollectedHeap::Serial, "Invalid name");
return (GenCollectedHeap*) heap;
}
--- a/src/hotspot/share/gc/shared/genCollectedHeap.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/genCollectedHeap.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -45,8 +45,6 @@
friend class Generation;
friend class DefNewGeneration;
friend class TenuredGeneration;
- friend class ConcurrentMarkSweepGeneration;
- friend class CMSCollector;
friend class GenMarkSweep;
friend class VM_GenCollectForAllocation;
friend class VM_GenCollectFull;
@@ -386,11 +384,6 @@
CLDClosure* weak_cld_closure,
CodeBlobToOopClosure* code_roots);
- // Accessor for memory state verification support
- NOT_PRODUCT(
- virtual size_t skip_header_HeapWords() { return 0; }
- )
-
virtual void gc_prologue(bool full);
virtual void gc_epilogue(bool full);
@@ -465,10 +458,6 @@
bool is_tlab,
bool* gc_overhead_limit_was_exceeded);
- // Override
- void check_for_non_bad_heap_word_value(HeapWord* addr,
- size_t size) PRODUCT_RETURN;
-
#if INCLUDE_SERIALGC
// For use by mark-sweep. As implemented, mark-sweep-compact is global
// in an essential way: compaction is performed across generations, by
--- a/src/hotspot/share/gc/shared/generation.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/generation.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -68,12 +68,6 @@
return gch->old_gen_spec()->init_size();
}
-// This is for CMS. It returns stable monotonic used space size.
-// Remove this when CMS is removed.
-size_t Generation::used_stable() const {
- return used();
-}
-
size_t Generation::max_capacity() const {
return reserved().byte_size();
}
--- a/src/hotspot/share/gc/shared/generation.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/generation.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -41,20 +41,13 @@
//
// Generation - abstract base class
// - DefNewGeneration - allocation area (copy collected)
-// - ParNewGeneration - a DefNewGeneration that is collected by
-// several threads
// - CardGeneration - abstract class adding offset array behavior
// - TenuredGeneration - tenured (old object) space (markSweepCompact)
-// - ConcurrentMarkSweepGeneration - Mostly Concurrent Mark Sweep Generation
-// (Detlefs-Printezis refinement of
-// Boehm-Demers-Schenker)
//
-// The system configurations currently allowed are:
+// The system configuration currently allowed is:
//
// DefNewGeneration + TenuredGeneration
//
-// ParNewGeneration + ConcurrentMarkSweepGeneration
-//
class DefNewGeneration;
class GCMemoryManager;
@@ -122,9 +115,7 @@
// The set of possible generation kinds.
enum Name {
DefNew,
- ParNew,
MarkSweepCompact,
- ConcurrentMarkSweep,
Other
};
@@ -156,7 +147,6 @@
virtual size_t capacity() const = 0; // The maximum number of object bytes the
// generation can currently hold.
virtual size_t used() const = 0; // The number of used bytes in the gen.
- virtual size_t used_stable() const; // The number of used bytes for memory monitoring tools.
virtual size_t free() const = 0; // The number of free bytes in the gen.
// Support for java.lang.Runtime.maxMemory(); see CollectedHeap.
--- a/src/hotspot/share/gc/shared/generationSpec.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/generationSpec.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -29,10 +29,6 @@
#include "memory/filemap.hpp"
#include "runtime/java.hpp"
#include "utilities/macros.hpp"
-#if INCLUDE_CMSGC
-#include "gc/cms/concurrentMarkSweepGeneration.hpp"
-#include "gc/cms/parNewGeneration.hpp"
-#endif
#if INCLUDE_SERIALGC
#include "gc/serial/defNewGeneration.hpp"
#include "gc/serial/tenuredGeneration.hpp"
@@ -48,15 +44,6 @@
return new TenuredGeneration(rs, _init_size, _min_size, _max_size, remset);
#endif
-#if INCLUDE_CMSGC
- case Generation::ParNew:
- return new ParNewGeneration(rs, _init_size, _min_size, _max_size);
-
- case Generation::ConcurrentMarkSweep: {
- return new ConcurrentMarkSweepGeneration(rs, _init_size, _min_size, _max_size, remset);
- }
-#endif // INCLUDE_CMSGC
-
default:
guarantee(false, "unrecognized GenerationName");
return NULL;
--- a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -36,9 +36,6 @@
#include "runtime/thread.inline.hpp"
#include "utilities/align.hpp"
#include "utilities/macros.hpp"
-#if INCLUDE_CMSGC
-#include "gc/cms/jvmFlagConstraintsCMS.hpp"
-#endif
#if INCLUDE_G1GC
#include "gc/g1/jvmFlagConstraintsG1.hpp"
#endif
@@ -65,22 +62,14 @@
}
#endif
-#if INCLUDE_CMSGC
- status = ParallelGCThreadsConstraintFuncCMS(value, verbose);
- if (status != JVMFlag::SUCCESS) {
- return status;
- }
-#endif
-
return status;
}
// As ConcGCThreads should be smaller than ParallelGCThreads,
// we need constraint function.
JVMFlag::Error ConcGCThreadsConstraintFunc(uint value, bool verbose) {
- // CMS and G1 GCs use ConcGCThreads.
- if ((GCConfig::is_gc_selected(CollectedHeap::CMS) ||
- GCConfig::is_gc_selected(CollectedHeap::G1)) && (value > ParallelGCThreads)) {
+ // G1 GC use ConcGCThreads.
+ if (GCConfig::is_gc_selected(CollectedHeap::G1) && (value > ParallelGCThreads)) {
JVMFlag::printError(verbose,
"ConcGCThreads (" UINT32_FORMAT ") must be "
"less than or equal to ParallelGCThreads (" UINT32_FORMAT ")\n",
@@ -92,9 +81,8 @@
}
static JVMFlag::Error MinPLABSizeBounds(const char* name, size_t value, bool verbose) {
- if ((GCConfig::is_gc_selected(CollectedHeap::CMS) ||
- GCConfig::is_gc_selected(CollectedHeap::G1) ||
- GCConfig::is_gc_selected(CollectedHeap::Parallel)) && (value < PLAB::min_size())) {
+ if ((GCConfig::is_gc_selected(CollectedHeap::G1) || GCConfig::is_gc_selected(CollectedHeap::Parallel)) &&
+ (value < PLAB::min_size())) {
JVMFlag::printError(verbose,
"%s (" SIZE_FORMAT ") must be "
"greater than or equal to ergonomic PLAB minimum size (" SIZE_FORMAT ")\n",
@@ -106,8 +94,7 @@
}
JVMFlag::Error MaxPLABSizeBounds(const char* name, size_t value, bool verbose) {
- if ((GCConfig::is_gc_selected(CollectedHeap::CMS) ||
- GCConfig::is_gc_selected(CollectedHeap::G1) ||
+ if ((GCConfig::is_gc_selected(CollectedHeap::G1) ||
GCConfig::is_gc_selected(CollectedHeap::Parallel)) && (value > PLAB::max_size())) {
JVMFlag::printError(verbose,
"%s (" SIZE_FORMAT ") must be "
@@ -135,11 +122,6 @@
JVMFlag::Error OldPLABSizeConstraintFunc(size_t value, bool verbose) {
JVMFlag::Error status = JVMFlag::SUCCESS;
-#if INCLUDE_CMSGC
- if (UseConcMarkSweepGC) {
- return OldPLABSizeConstraintFuncCMS(value, verbose);
- } else
-#endif
{
status = MinMaxPLABSizeBounds("OldPLABSize", value, verbose);
}
--- a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -28,9 +28,6 @@
#include "runtime/flags/jvmFlag.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
-#if INCLUDE_CMSGC
-#include "gc/cms/jvmFlagConstraintsCMS.hpp"
-#endif
#if INCLUDE_G1GC
#include "gc/g1/jvmFlagConstraintsG1.hpp"
#endif
--- a/src/hotspot/share/gc/shared/preservedMarks.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/preservedMarks.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -128,7 +128,7 @@
// Iterate over all stacks, restore all preserved marks, and reclaim
// the memory taken up by the stack segments.
- // Supported executors: SharedRestorePreservedMarksTaskExecutor (Serial, CMS, G1),
+ // Supported executors: SharedRestorePreservedMarksTaskExecutor (Serial, G1),
// PSRestorePreservedMarksTaskExecutor (PS).
inline void restore(RestorePreservedMarksTaskExecutor* executor);
--- a/src/hotspot/share/gc/shared/referenceProcessor.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/referenceProcessor.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -333,7 +333,7 @@
log_develop_trace(gc, ref)("Enqueue %s reference (" INTPTR_FORMAT ": %s)",
reason, p2i(iter.obj()), iter.obj()->klass()->internal_name());
}
- assert(oopDesc::is_oop(iter.obj(), UseConcMarkSweepGC), "Adding a bad reference");
+ assert(oopDesc::is_oop(iter.obj()), "Adding a bad reference");
}
size_t ReferenceProcessor::process_soft_ref_reconsider_work(DiscoveredList& refs_list,
@@ -1154,7 +1154,7 @@
// Check assumption that an object is not potentially
// discovered twice except by concurrent collectors that potentially
// trace the same Reference object twice.
- assert(UseConcMarkSweepGC || UseG1GC || UseShenandoahGC,
+ assert(UseG1GC || UseShenandoahGC,
"Only possible with a concurrent marking collector");
return true;
}
--- a/src/hotspot/share/gc/shared/referenceProcessor.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/referenceProcessor.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -217,8 +217,7 @@
// For collectors that do not keep GC liveness information
// in the object header, this field holds a closure that
// helps the reference processor determine the reachability
- // of an oop. It is currently initialized to NULL for all
- // collectors except for CMS and G1.
+ // of an oop.
BoolObjectClosure* _is_alive_non_header;
// Soft ref clearing policies
--- a/src/hotspot/share/gc/shared/vmStructs_gc.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/vmStructs_gc.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -35,9 +35,6 @@
#include "gc/shared/generationSpec.hpp"
#include "gc/shared/oopStorage.hpp"
#include "gc/shared/space.hpp"
-#if INCLUDE_CMSGC
-#include "gc/cms/vmStructs_cms.hpp"
-#endif
#if INCLUDE_EPSILONGC
#include "gc/epsilon/vmStructs_epsilon.hpp"
#endif
@@ -62,9 +59,6 @@
volatile_nonstatic_field, \
static_field, \
unchecked_nonstatic_field) \
- CMSGC_ONLY(VM_STRUCTS_CMSGC(nonstatic_field, \
- volatile_nonstatic_field, \
- static_field)) \
EPSILONGC_ONLY(VM_STRUCTS_EPSILONGC(nonstatic_field, \
volatile_nonstatic_field, \
static_field)) \
@@ -107,8 +101,6 @@
nonstatic_field(BlockOffsetArrayContigSpace, _next_offset_threshold, HeapWord*) \
nonstatic_field(BlockOffsetArrayContigSpace, _next_offset_index, size_t) \
\
- nonstatic_field(BlockOffsetArrayNonContigSpace, _unallocated_block, HeapWord*) \
- \
nonstatic_field(CardGeneration, _rs, CardTableRS*) \
nonstatic_field(CardGeneration, _bts, BlockOffsetSharedArray*) \
nonstatic_field(CardGeneration, _shrink_factor, size_t) \
@@ -168,9 +160,6 @@
#define VM_TYPES_GC(declare_type, \
declare_toplevel_type, \
declare_integer_type) \
- CMSGC_ONLY(VM_TYPES_CMSGC(declare_type, \
- declare_toplevel_type, \
- declare_integer_type)) \
EPSILONGC_ONLY(VM_TYPES_EPSILONGC(declare_type, \
declare_toplevel_type, \
declare_integer_type)) \
@@ -213,7 +202,6 @@
declare_toplevel_type(BlockOffsetTable) \
declare_type(BlockOffsetArray, BlockOffsetTable) \
declare_type(BlockOffsetArrayContigSpace, BlockOffsetArray) \
- declare_type(BlockOffsetArrayNonContigSpace, BlockOffsetArray) \
\
/* Miscellaneous other GC types */ \
\
@@ -252,8 +240,6 @@
#define VM_INT_CONSTANTS_GC(declare_constant, \
declare_constant_with_value) \
- CMSGC_ONLY(VM_INT_CONSTANTS_CMSGC(declare_constant, \
- declare_constant_with_value)) \
EPSILONGC_ONLY(VM_INT_CONSTANTS_EPSILONGC(declare_constant, \
declare_constant_with_value)) \
G1GC_ONLY(VM_INT_CONSTANTS_G1GC(declare_constant, \
@@ -297,7 +283,6 @@
\
declare_constant(CollectedHeap::Serial) \
declare_constant(CollectedHeap::Parallel) \
- declare_constant(CollectedHeap::CMS) \
declare_constant(CollectedHeap::G1) \
\
/* constants from Generation::Name enum */ \
--- a/src/hotspot/share/gc/shared/workerPolicy.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/shared/workerPolicy.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -59,14 +59,11 @@
// Return number of GC threads to use in the next GC.
// This is called sparingly so as not to change the
// number of GC workers gratuitously.
- // For ParNew collections
// For PS scavenge and ParOld collections
// For G1 evacuation pauses (subject to update)
// For G1 Full GCs (subject to update)
// Other collection phases inherit the number of
- // GC workers from the calls above. For example,
- // a CMS parallel remark uses the same number of GC
- // workers as the most recent ParNew collection.
+ // GC workers from the calls above.
static uint calc_active_workers(uintx total_workers,
uintx active_workers,
uintx application_workers);
--- a/src/hotspot/share/gc/z/zBarrier.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zBarrier.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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,6 +41,8 @@
static const bool Publish = true;
static const bool Overflow = false;
+ static void self_heal(volatile oop* p, uintptr_t addr, uintptr_t heal_addr);
+
template <ZBarrierFastPath fast_path, ZBarrierSlowPath slow_path> static oop barrier(volatile oop* p, oop o);
template <ZBarrierFastPath fast_path, ZBarrierSlowPath slow_path> static oop weak_barrier(volatile oop* p, oop o);
template <ZBarrierFastPath fast_path, ZBarrierSlowPath slow_path> static void root_barrier(oop* p, oop o);
@@ -49,8 +51,6 @@
static bool is_good_or_null_fast_path(uintptr_t addr);
static bool is_weak_good_or_null_fast_path(uintptr_t addr);
- static bool is_resurrection_blocked(volatile oop* p, oop* o);
-
static bool during_mark();
static bool during_relocate();
template <bool finalizable> static bool should_mark_through(uintptr_t addr);
--- a/src/hotspot/share/gc/z/zBarrier.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zBarrier.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,11 +32,46 @@
#include "oops/oop.hpp"
#include "runtime/atomic.hpp"
+inline void ZBarrier::self_heal(volatile oop* p, uintptr_t addr, uintptr_t heal_addr) {
+ if (heal_addr == 0) {
+ // Never heal with null since it interacts badly with reference processing.
+ // A mutator clearing an oop would be similar to calling Reference.clear(),
+ // which would make the reference non-discoverable or silently dropped
+ // by the reference processor.
+ return;
+ }
+
+ for (;;) {
+ if (addr == heal_addr) {
+ // Already healed
+ return;
+ }
+
+ // Heal
+ const uintptr_t prev_addr = Atomic::cmpxchg(heal_addr, (volatile uintptr_t*)p, addr);
+ if (prev_addr == addr) {
+ // Success
+ return;
+ }
+
+ if (ZAddress::is_good_or_null(prev_addr)) {
+ // No need to heal
+ return;
+ }
+
+ // The oop location was healed by another barrier, but it is still not
+ // good or null. Re-apply healing to make sure the oop is not left with
+ // weaker (remapped or finalizable) metadata bits than what this barrier
+ // tried to apply.
+ assert(ZAddress::offset(prev_addr) == ZAddress::offset(heal_addr), "Invalid offset");
+ addr = prev_addr;
+ }
+}
+
template <ZBarrierFastPath fast_path, ZBarrierSlowPath slow_path>
inline oop ZBarrier::barrier(volatile oop* p, oop o) {
uintptr_t addr = ZOop::to_address(o);
-retry:
// Fast path
if (fast_path(addr)) {
return ZOop::from_address(addr);
@@ -45,17 +80,8 @@
// Slow path
const uintptr_t good_addr = slow_path(addr);
- // Self heal, but only if the address was actually updated by the slow path,
- // which might not be the case, e.g. when marking through an already good oop.
- if (p != NULL && good_addr != addr) {
- const uintptr_t prev_addr = Atomic::cmpxchg(good_addr, (volatile uintptr_t*)p, addr);
- if (prev_addr != addr) {
- // Some other thread overwrote the oop. If this oop was updated by a
- // weak barrier the new oop might not be good, in which case we need
- // to re-apply this barrier.
- addr = prev_addr;
- goto retry;
- }
+ if (p != NULL) {
+ self_heal(p, addr, good_addr);
}
return ZOop::from_address(good_addr);
@@ -73,28 +99,12 @@
}
// Slow path
- uintptr_t good_addr = slow_path(addr);
+ const uintptr_t good_addr = slow_path(addr);
- // Self heal unless the address returned from the slow path is null,
- // in which case resurrection was blocked and we must let the reference
- // processor clear the oop. Mutators are not allowed to clear oops in
- // these cases, since that would be similar to calling Reference.clear(),
- // which would make the reference non-discoverable or silently dropped
- // by the reference processor.
- if (p != NULL && good_addr != 0) {
- // The slow path returns a good/marked address, but we never mark oops
- // in a weak load barrier so we always self heal with the remapped address.
- const uintptr_t weak_good_addr = ZAddress::remapped(good_addr);
- const uintptr_t prev_addr = Atomic::cmpxchg(weak_good_addr, (volatile uintptr_t*)p, addr);
- if (prev_addr != addr) {
- // Some other thread overwrote the oop. The new
- // oop is guaranteed to be weak good or null.
- assert(ZAddress::is_weak_good_or_null(prev_addr), "Bad weak overwrite");
-
- // Return the good address instead of the weak good address
- // to ensure that the currently active heap view is used.
- good_addr = ZAddress::good_or_null(prev_addr);
- }
+ if (p != NULL) {
+ // The slow path returns a good/marked address or null, but we never mark
+ // oops in a weak load barrier so we always heal with the remapped address.
+ self_heal(p, addr, ZAddress::remapped_or_null(good_addr));
}
return ZOop::from_address(good_addr);
@@ -134,25 +144,6 @@
return ZAddress::is_weak_good_or_null(addr);
}
-inline bool ZBarrier::is_resurrection_blocked(volatile oop* p, oop* o) {
- const bool is_blocked = ZResurrection::is_blocked();
-
- // Reload oop after checking the resurrection blocked state. This is
- // done to prevent a race where we first load an oop, which is logically
- // null but not yet cleared, then this oop is cleared by the reference
- // processor and resurrection is unblocked. At this point the mutator
- // would see the unblocked state and pass this invalid oop through the
- // normal barrier path, which would incorrectly try to mark this oop.
- if (p != NULL) {
- // First assign to reloaded_o to avoid compiler warning about
- // implicit dereference of volatile oop.
- const oop reloaded_o = *p;
- *o = reloaded_o;
- }
-
- return is_blocked;
-}
-
//
// Load barrier
//
@@ -190,16 +181,16 @@
inline oop ZBarrier::load_barrier_on_weak_oop_field_preloaded(volatile oop* p, oop o) {
verify_on_weak(p);
- if (is_resurrection_blocked(p, &o)) {
- return weak_barrier<is_good_or_null_fast_path, weak_load_barrier_on_weak_oop_slow_path>(p, o);
+ if (ZResurrection::is_blocked()) {
+ return barrier<is_good_or_null_fast_path, weak_load_barrier_on_weak_oop_slow_path>(p, o);
}
return load_barrier_on_oop_field_preloaded(p, o);
}
inline oop ZBarrier::load_barrier_on_phantom_oop_field_preloaded(volatile oop* p, oop o) {
- if (is_resurrection_blocked(p, &o)) {
- return weak_barrier<is_good_or_null_fast_path, weak_load_barrier_on_phantom_oop_slow_path>(p, o);
+ if (ZResurrection::is_blocked()) {
+ return barrier<is_good_or_null_fast_path, weak_load_barrier_on_phantom_oop_slow_path>(p, o);
}
return load_barrier_on_oop_field_preloaded(p, o);
@@ -235,8 +226,8 @@
inline oop ZBarrier::weak_load_barrier_on_weak_oop_field_preloaded(volatile oop* p, oop o) {
verify_on_weak(p);
- if (is_resurrection_blocked(p, &o)) {
- return weak_barrier<is_good_or_null_fast_path, weak_load_barrier_on_weak_oop_slow_path>(p, o);
+ if (ZResurrection::is_blocked()) {
+ return barrier<is_good_or_null_fast_path, weak_load_barrier_on_weak_oop_slow_path>(p, o);
}
return weak_load_barrier_on_oop_field_preloaded(p, o);
@@ -252,8 +243,8 @@
}
inline oop ZBarrier::weak_load_barrier_on_phantom_oop_field_preloaded(volatile oop* p, oop o) {
- if (is_resurrection_blocked(p, &o)) {
- return weak_barrier<is_good_or_null_fast_path, weak_load_barrier_on_phantom_oop_slow_path>(p, o);
+ if (ZResurrection::is_blocked()) {
+ return barrier<is_good_or_null_fast_path, weak_load_barrier_on_phantom_oop_slow_path>(p, o);
}
return weak_load_barrier_on_oop_field_preloaded(p, o);
--- a/src/hotspot/share/gc/z/zHeap.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zHeap.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -40,6 +40,7 @@
#include "logging/log.hpp"
#include "memory/iterator.hpp"
#include "memory/resourceArea.hpp"
+#include "runtime/handshake.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/thread.hpp"
#include "utilities/debug.hpp"
@@ -315,7 +316,7 @@
// Process weak roots
_weak_roots_processor.process_weak_roots();
- // Prepare to unload unused classes and code
+ // Prepare to unload stale metadata and nmethods
_unload.prepare();
return true;
@@ -325,6 +326,11 @@
_reference_processor.set_soft_reference_policy(clear);
}
+class ZRendezvousClosure : public ThreadClosure {
+public:
+ virtual void do_thread(Thread* thread) {}
+};
+
void ZHeap::process_non_strong_references() {
// Process Soft/Weak/Final/PhantomReferences
_reference_processor.process_references();
@@ -332,8 +338,22 @@
// Process concurrent weak roots
_weak_roots_processor.process_concurrent_weak_roots();
- // Unload unused classes and code
- _unload.unload();
+ // Unlink stale metadata and nmethods
+ _unload.unlink();
+
+ // Perform a handshake. This is needed 1) to make sure that stale
+ // metadata and nmethods are no longer observable. And 2), to
+ // prevent the race where a mutator first loads an oop, which is
+ // logically null but not yet cleared. Then this oop gets cleared
+ // by the reference processor and resurrection is unblocked. At
+ // this point the mutator could see the unblocked state and pass
+ // this invalid oop through the normal barrier path, which would
+ // incorrectly try to mark the oop.
+ ZRendezvousClosure cl;
+ Handshake::execute(&cl);
+
+ // Purge stale metadata and nmethods that were unlinked
+ _unload.purge();
// Unblock resurrection of weak/phantom references
ZResurrection::unblock();
@@ -405,7 +425,7 @@
void ZHeap::relocate_start() {
assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint");
- // Finish unloading of classes and code
+ // Finish unloading stale metadata and nmethods
_unload.finish();
// Flip address view
--- a/src/hotspot/share/gc/z/zLiveMap.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zLiveMap.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -54,7 +54,9 @@
// Multiple threads can enter here, make sure only one of them
// resets the marking information while the others busy wait.
- for (uint32_t seqnum = _seqnum; seqnum != ZGlobalSeqNum; seqnum = _seqnum) {
+ for (uint32_t seqnum = OrderAccess::load_acquire(&_seqnum);
+ seqnum != ZGlobalSeqNum;
+ seqnum = OrderAccess::load_acquire(&_seqnum)) {
if ((seqnum != seqnum_initializing) &&
(Atomic::cmpxchg(seqnum_initializing, &_seqnum, seqnum) == seqnum)) {
// Reset marking information
@@ -65,13 +67,13 @@
segment_live_bits().clear();
segment_claim_bits().clear();
- // Make sure the newly reset marking information is
- // globally visible before updating the page seqnum.
- OrderAccess::storestore();
+ assert(_seqnum == seqnum_initializing, "Invalid");
- // Update seqnum
- assert(_seqnum == seqnum_initializing, "Invalid");
- _seqnum = ZGlobalSeqNum;
+ // Make sure the newly reset marking information is ordered
+ // before the update of the page seqnum, such that when the
+ // up-to-date seqnum is load acquired, the bit maps will not
+ // contain stale information.
+ OrderAccess::release_store(&_seqnum, ZGlobalSeqNum);
break;
}
@@ -93,10 +95,6 @@
if (!claim_segment(segment)) {
// Already claimed, wait for live bit to be set
while (!is_segment_live(segment)) {
- // Busy wait. The loadload barrier is needed to make
- // sure we re-read the live bit every time we loop.
- OrderAccess::loadload();
-
// Mark reset contention
if (!contention) {
// Count contention once
@@ -122,7 +120,7 @@
}
// Set live bit
- const bool success = set_segment_live_atomic(segment);
+ const bool success = set_segment_live(segment);
assert(success, "Should never fail");
}
--- a/src/hotspot/share/gc/z/zLiveMap.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zLiveMap.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -55,7 +55,7 @@
BitMap::idx_t segment_end(BitMap::idx_t segment) const;
bool is_segment_live(BitMap::idx_t segment) const;
- bool set_segment_live_atomic(BitMap::idx_t segment);
+ bool set_segment_live(BitMap::idx_t segment);
BitMap::idx_t first_live_segment() const;
BitMap::idx_t next_live_segment(BitMap::idx_t segment) const;
@@ -80,9 +80,9 @@
size_t live_bytes() const;
bool get(size_t index) const;
- bool set_atomic(size_t index, bool finalizable, bool& inc_live);
+ bool set(size_t index, bool finalizable, bool& inc_live);
- void inc_live_atomic(uint32_t objects, size_t bytes);
+ void inc_live(uint32_t objects, size_t bytes);
void iterate(ObjectClosure* cl, uintptr_t page_start, size_t page_object_alignment_shift);
};
--- a/src/hotspot/share/gc/z/zLiveMap.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zLiveMap.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -30,6 +30,7 @@
#include "gc/z/zOop.inline.hpp"
#include "gc/z/zUtils.inline.hpp"
#include "runtime/atomic.hpp"
+#include "runtime/orderAccess.hpp"
#include "utilities/bitMap.inline.hpp"
#include "utilities/debug.hpp"
@@ -38,7 +39,7 @@
}
inline bool ZLiveMap::is_marked() const {
- return _seqnum == ZGlobalSeqNum;
+ return OrderAccess::load_acquire(&_seqnum) == ZGlobalSeqNum;
}
inline uint32_t ZLiveMap::live_objects() const {
@@ -68,15 +69,15 @@
}
inline bool ZLiveMap::is_segment_live(BitMap::idx_t segment) const {
- return segment_live_bits().at(segment);
+ return segment_live_bits().par_at(segment);
}
-inline bool ZLiveMap::set_segment_live_atomic(BitMap::idx_t segment) {
- return segment_live_bits().par_set_bit(segment);
+inline bool ZLiveMap::set_segment_live(BitMap::idx_t segment) {
+ return segment_live_bits().par_set_bit(segment, memory_order_release);
}
inline bool ZLiveMap::claim_segment(BitMap::idx_t segment) {
- return segment_claim_bits().par_set_bit(segment);
+ return segment_claim_bits().par_set_bit(segment, memory_order_acq_rel);
}
inline BitMap::idx_t ZLiveMap::first_live_segment() const {
@@ -102,7 +103,7 @@
_bitmap.at(index); // Object is marked
}
-inline bool ZLiveMap::set_atomic(size_t index, bool finalizable, bool& inc_live) {
+inline bool ZLiveMap::set(size_t index, bool finalizable, bool& inc_live) {
if (!is_marked()) {
// First object to be marked during this
// cycle, reset marking information.
@@ -119,7 +120,7 @@
return _bitmap.par_set_bit_pair(index, finalizable, inc_live);
}
-inline void ZLiveMap::inc_live_atomic(uint32_t objects, size_t bytes) {
+inline void ZLiveMap::inc_live(uint32_t objects, size_t bytes) {
Atomic::add(objects, &_live_objects);
Atomic::add(bytes, &_live_bytes);
}
--- a/src/hotspot/share/gc/z/zMarkCache.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zMarkCache.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -44,7 +44,7 @@
inline void ZMarkCacheEntry::evict() {
if (_page != NULL) {
// Write cached data out to page
- _page->inc_live_atomic(_objects, _bytes);
+ _page->inc_live(_objects, _bytes);
_page = NULL;
}
}
--- a/src/hotspot/share/gc/z/zMarkStack.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zMarkStack.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -62,8 +62,8 @@
bool is_empty() const;
- void push_atomic(T* stack);
- T* pop_atomic();
+ void push(T* stack);
+ T* pop();
};
typedef ZStack<ZMarkStackEntry, ZMarkStackSlots> ZMarkStack;
--- a/src/hotspot/share/gc/z/zMarkStack.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zMarkStack.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -114,7 +114,7 @@
}
template <typename T>
-inline void ZStackList<T>::push_atomic(T* stack) {
+inline void ZStackList<T>::push(T* stack) {
T* vstack = _head;
uint32_t version = 0;
@@ -133,7 +133,7 @@
}
template <typename T>
-inline T* ZStackList<T>::pop_atomic() {
+inline T* ZStackList<T>::pop() {
T* vstack = _head;
T* stack = NULL;
uint32_t version = 0;
@@ -168,20 +168,20 @@
// contention between mutators and GC workers as much as possible, while
// still allowing GC workers to help out and steal work from each other.
if (publish) {
- _published.push_atomic(stack);
+ _published.push(stack);
} else {
- _overflowed.push_atomic(stack);
+ _overflowed.push(stack);
}
}
inline ZMarkStack* ZMarkStripe::steal_stack() {
// Steal overflowed stacks first, then published stacks
- ZMarkStack* const stack = _overflowed.pop_atomic();
+ ZMarkStack* const stack = _overflowed.pop();
if (stack != NULL) {
return stack;
}
- return _published.pop_atomic();
+ return _published.pop();
}
inline size_t ZMarkStripeSet::nstripes() const {
--- a/src/hotspot/share/gc/z/zMarkStackAllocator.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zMarkStackAllocator.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -166,7 +166,7 @@
ZMarkStackMagazine* ZMarkStackAllocator::alloc_magazine() {
// Try allocating from the free list first
- ZMarkStackMagazine* const magazine = _freelist.pop_atomic();
+ ZMarkStackMagazine* const magazine = _freelist.pop();
if (magazine != NULL) {
return magazine;
}
@@ -181,5 +181,5 @@
}
void ZMarkStackAllocator::free_magazine(ZMarkStackMagazine* magazine) {
- _freelist.push_atomic(magazine);
+ _freelist.push(magazine);
}
--- a/src/hotspot/share/gc/z/zPage.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zPage.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -96,7 +96,7 @@
bool is_object_strongly_live(uintptr_t addr) const;
bool mark_object(uintptr_t addr, bool finalizable, bool& inc_live);
- void inc_live_atomic(uint32_t objects, size_t bytes);
+ void inc_live(uint32_t objects, size_t bytes);
uint32_t live_objects() const;
size_t live_bytes() const;
--- a/src/hotspot/share/gc/z/zPage.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zPage.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -207,11 +207,11 @@
// Set mark bit
const size_t index = ((ZAddress::offset(addr) - start()) >> object_alignment_shift()) * 2;
- return _livemap.set_atomic(index, finalizable, inc_live);
+ return _livemap.set(index, finalizable, inc_live);
}
-inline void ZPage::inc_live_atomic(uint32_t objects, size_t bytes) {
- _livemap.inc_live_atomic(objects, bytes);
+inline void ZPage::inc_live(uint32_t objects, size_t bytes) {
+ _livemap.inc_live(objects, bytes);
}
inline uint32_t ZPage::live_objects() const {
--- a/src/hotspot/share/gc/z/zUnload.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zUnload.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -36,7 +36,8 @@
#include "gc/z/zUnload.hpp"
#include "oops/access.inline.hpp"
-static const ZStatSubPhase ZSubPhaseConcurrentClassesUnload("Concurrent Classes Unload");
+static const ZStatSubPhase ZSubPhaseConcurrentClassesUnlink("Concurrent Classes Unlink");
+static const ZStatSubPhase ZSubPhaseConcurrentClassesPurge("Concurrent Classes Purge");
class ZIsUnloadingOopClosure : public OopClosure {
private:
@@ -126,6 +127,11 @@
}
void ZUnload::unlink() {
+ if (!ClassUnloading) {
+ return;
+ }
+
+ ZStatTimer timer(ZSubPhaseConcurrentClassesUnlink);
SuspendibleThreadSetJoiner sts;
bool unloading_occurred;
@@ -135,13 +141,17 @@
}
Klass::clean_weak_klass_links(unloading_occurred);
-
ZNMethod::unlink(_workers, unloading_occurred);
-
DependencyContext::cleaning_end();
}
void ZUnload::purge() {
+ if (!ClassUnloading) {
+ return;
+ }
+
+ ZStatTimer timer(ZSubPhaseConcurrentClassesPurge);
+
{
SuspendibleThreadSetJoiner sts;
ZNMethod::purge(_workers);
@@ -151,29 +161,6 @@
CodeCache::purge_exception_caches();
}
-class ZUnloadRendezvousClosure : public ThreadClosure {
-public:
- void do_thread(Thread* thread) {}
-};
-
-void ZUnload::unload() {
- if (!ClassUnloading) {
- return;
- }
-
- ZStatTimer timer(ZSubPhaseConcurrentClassesUnload);
-
- // Unlink stale metadata and nmethods
- unlink();
-
- // Make sure stale metadata and nmethods are no longer observable
- ZUnloadRendezvousClosure cl;
- Handshake::execute(&cl);
-
- // Purge stale metadata and nmethods that were unlinked
- purge();
-}
-
void ZUnload::finish() {
// Resize and verify metaspace
MetaspaceGC::compute_new_size();
--- a/src/hotspot/share/gc/z/zUnload.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/gc/z/zUnload.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -30,14 +30,12 @@
private:
ZWorkers* const _workers;
- void unlink();
- void purge();
-
public:
ZUnload(ZWorkers* workers);
void prepare();
- void unload();
+ void unlink();
+ void purge();
void finish();
};
--- a/src/hotspot/share/interpreter/abstractInterpreter.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -267,7 +267,8 @@
}
assert(!invoke_bc.has_index_u4(code), "sanity");
int method_index = invoke_bc.get_index_u2_cpcache(code);
- Method* resolved_method = ConstantPool::method_at_if_loaded(cpool, method_index);
+ constantPoolHandle cp(Thread::current(), cpool);
+ Method* resolved_method = ConstantPool::method_at_if_loaded(cp, method_index);
return (resolved_method == NULL);
}
default: ShouldNotReachHere();
--- a/src/hotspot/share/interpreter/bytecode.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/interpreter/bytecode.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -151,7 +151,7 @@
}
-methodHandle Bytecode_invoke::static_target(TRAPS) {
+Method* Bytecode_invoke::static_target(TRAPS) {
constantPoolHandle constants(THREAD, this->constants());
Bytecodes::Code bc = invoke_code();
@@ -160,8 +160,10 @@
Handle Bytecode_invoke::appendix(TRAPS) {
ConstantPoolCacheEntry* cpce = cpcache_entry();
- if (cpce->has_appendix())
- return Handle(THREAD, cpce->appendix_if_resolved(constants()));
+ if (cpce->has_appendix()) {
+ constantPoolHandle cp(THREAD, constants());
+ return Handle(THREAD, cpce->appendix_if_resolved(cp));
+ }
return Handle(); // usual case
}
--- a/src/hotspot/share/interpreter/bytecode.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/interpreter/bytecode.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -209,7 +209,7 @@
void verify() const;
// Attributes
- methodHandle static_target(TRAPS); // "specified" method (from constant pool)
+ Method* static_target(TRAPS); // "specified" method (from constant pool)
Handle appendix(TRAPS); // if CPCE::has_appendix (from constant pool)
// Testers
--- a/src/hotspot/share/interpreter/interpreterRuntime.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -897,7 +897,7 @@
// (see also CallInfo::set_interface for details)
assert(info.call_kind() == CallInfo::vtable_call ||
info.call_kind() == CallInfo::direct_call, "");
- methodHandle rm = info.resolved_method();
+ Method* rm = info.resolved_method();
assert(rm->is_final() || info.has_vtable_index(),
"should have been set already");
} else if (!info.resolved_method()->has_itable_index()) {
@@ -921,25 +921,26 @@
// methods must be checked for every call.
InstanceKlass* sender = pool->pool_holder();
sender = sender->is_unsafe_anonymous() ? sender->unsafe_anonymous_host() : sender;
+ methodHandle resolved_method(THREAD, info.resolved_method());
switch (info.call_kind()) {
case CallInfo::direct_call:
cp_cache_entry->set_direct_call(
bytecode,
- info.resolved_method(),
+ resolved_method,
sender->is_interface());
break;
case CallInfo::vtable_call:
cp_cache_entry->set_vtable_call(
bytecode,
- info.resolved_method(),
+ resolved_method,
info.vtable_index());
break;
case CallInfo::itable_call:
cp_cache_entry->set_itable_call(
bytecode,
info.resolved_klass(),
- info.resolved_method(),
+ resolved_method,
info.itable_index());
break;
default: ShouldNotReachHere();
--- a/src/hotspot/share/interpreter/linkResolver.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/interpreter/linkResolver.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -140,8 +140,8 @@
}
_resolved_klass = resolved_klass;
_selected_klass = resolved_klass;
- _resolved_method = resolved_method;
- _selected_method = resolved_method;
+ _resolved_method = methodHandle(THREAD, resolved_method);
+ _selected_method = methodHandle(THREAD, resolved_method);
// classify:
CallKind kind = CallInfo::unknown_kind;
int index = resolved_method->vtable_index();
@@ -153,7 +153,7 @@
} else if (!resolved_klass->is_interface()) {
// A default or miranda method. Compute the vtable index.
index = LinkResolver::vtable_index_of_interface_method(resolved_klass,
- resolved_method);
+ _resolved_method);
assert(index >= 0 , "we should have valid vtable index at this point");
kind = CallInfo::vtable_call;
@@ -189,9 +189,8 @@
}
void CallInfo::set_resolved_method_name(TRAPS) {
- Method* m = _resolved_method();
- assert(m != NULL, "Should already have a Method*");
- oop rmethod_name = java_lang_invoke_ResolvedMethodName::find_resolved_method(m, CHECK);
+ assert(_resolved_method() != NULL, "Should already have a Method*");
+ oop rmethod_name = java_lang_invoke_ResolvedMethodName::find_resolved_method(_resolved_method, CHECK);
_resolved_method_name = Handle(THREAD, rmethod_name);
}
@@ -381,10 +380,10 @@
// returns first instance method
// Looks up method in classes, then looks up local default methods
-methodHandle LinkResolver::lookup_instance_method_in_klasses(Klass* klass,
- Symbol* name,
- Symbol* signature,
- Klass::PrivateLookupMode private_mode, TRAPS) {
+Method* LinkResolver::lookup_instance_method_in_klasses(Klass* klass,
+ Symbol* name,
+ Symbol* signature,
+ Klass::PrivateLookupMode private_mode, TRAPS) {
Method* result = klass->uncached_lookup_method(name, signature, Klass::find_overpass, private_mode);
while (result != NULL && result->is_static() && result->method_holder()->super() != NULL) {
@@ -394,7 +393,7 @@
if (klass->is_array_klass()) {
// Only consider klass and super klass for arrays
- return methodHandle(THREAD, result);
+ return result;
}
if (result == NULL) {
@@ -404,7 +403,7 @@
assert(result == NULL || !result->is_static(), "static defaults not allowed");
}
}
- return methodHandle(THREAD, result);
+ return result;
}
int LinkResolver::vtable_index_of_interface_method(Klass* klass,
@@ -441,10 +440,9 @@
return ik->lookup_method_in_all_interfaces(cp_info.name(), cp_info.signature(), Klass::skip_defaults);
}
-methodHandle LinkResolver::lookup_polymorphic_method(
- const LinkInfo& link_info,
- Handle *appendix_result_or_null,
- TRAPS) {
+Method* LinkResolver::lookup_polymorphic_method(const LinkInfo& link_info,
+ Handle *appendix_result_or_null,
+ TRAPS) {
Klass* klass = link_info.resolved_klass();
Symbol* name = link_info.name();
Symbol* full_signature = link_info.signature();
@@ -472,10 +470,10 @@
full_signature->as_C_string(),
basic_signature->as_C_string());
}
- methodHandle result = SystemDictionary::find_method_handle_intrinsic(iid,
+ Method* result = SystemDictionary::find_method_handle_intrinsic(iid,
basic_signature,
CHECK_NULL);
- if (result.not_null()) {
+ if (result != NULL) {
assert(result->is_method_handle_intrinsic(), "MH.invokeBasic or MH.linkTo* intrinsic");
assert(result->intrinsic_id() != vmIntrinsics::_invokeGeneric, "wrong place to find this");
assert(basic_signature == result->signature(), "predict the result signature");
@@ -505,7 +503,7 @@
Handle appendix;
Handle method_type;
- methodHandle result = SystemDictionary::find_method_handle_invoker(
+ Method* result = SystemDictionary::find_method_handle_invoker(
klass,
name,
full_signature,
@@ -520,7 +518,7 @@
if (appendix.is_null()) tty->print_cr("(none)");
else appendix->print_on(tty);
}
- if (result.not_null()) {
+ if (result != NULL) {
#ifdef ASSERT
ResourceMark rm(THREAD);
@@ -603,8 +601,8 @@
}
}
-methodHandle LinkResolver::resolve_method_statically(Bytecodes::Code code,
- const constantPoolHandle& pool, int index, TRAPS) {
+Method* LinkResolver::resolve_method_statically(Bytecodes::Code code,
+ const constantPoolHandle& pool, int index, TRAPS) {
// This method is used only
// (1) in C2 from InlineTree::ok_to_inline (via ciMethod::check_call),
// and
@@ -629,7 +627,7 @@
MethodHandles::is_signature_polymorphic_name(resolved_klass, link_info.name()))) {
Method* result = ConstantPool::method_at_if_loaded(pool, index);
if (result != NULL) {
- return methodHandle(THREAD, result);
+ return result;
}
}
@@ -714,8 +712,8 @@
}
}
-methodHandle LinkResolver::resolve_method(const LinkInfo& link_info,
- Bytecodes::Code code, TRAPS) {
+Method* LinkResolver::resolve_method(const LinkInfo& link_info,
+ Bytecodes::Code code, TRAPS) {
Handle nested_exception;
Klass* resolved_klass = link_info.resolved_klass();
@@ -748,7 +746,8 @@
if (resolved_method.is_null()) {
// JSR 292: see if this is an implicitly generated method MethodHandle.linkToVirtual(*...), etc
- resolved_method = lookup_polymorphic_method(link_info, (Handle*)NULL, THREAD);
+ Method* method = lookup_polymorphic_method(link_info, (Handle*)NULL, THREAD);
+ resolved_method = methodHandle(THREAD, method);
if (HAS_PENDING_EXCEPTION) {
nested_exception = Handle(THREAD, PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
@@ -783,13 +782,13 @@
check_method_loader_constraints(link_info, resolved_method, "method", CHECK_NULL);
}
- return resolved_method;
+ return resolved_method();
}
static void trace_method_resolution(const char* prefix,
Klass* klass,
Klass* resolved_klass,
- const methodHandle& method,
+ Method* method,
bool logitables,
int index = -1) {
#ifndef PRODUCT
@@ -821,7 +820,7 @@
}
// Do linktime resolution of a method in the interface within the context of the specied bytecode.
-methodHandle LinkResolver::resolve_interface_method(const LinkInfo& link_info, Bytecodes::Code code, TRAPS) {
+Method* LinkResolver::resolve_interface_method(const LinkInfo& link_info, Bytecodes::Code code, TRAPS) {
Klass* resolved_klass = link_info.resolved_klass();
@@ -892,11 +891,10 @@
char buf[200];
jio_snprintf(buf, sizeof(buf), "%s resolved interface method: caller-class:",
Bytecodes::name(code));
- trace_method_resolution(buf, link_info.current_klass(), resolved_klass,
- resolved_method, true);
+ trace_method_resolution(buf, link_info.current_klass(), resolved_klass, resolved_method(), true);
}
- return resolved_method;
+ return resolved_method();
}
//------------------------------------------------------------------------------------------------------------------------
@@ -999,11 +997,11 @@
}
if (fd.constants()->pool_holder()->major_version() >= 53) {
- methodHandle m = link_info.current_method();
- assert(!m.is_null(), "information about the current method must be available for 'put' bytecodes");
+ Method* m = link_info.current_method();
+ assert(m != NULL, "information about the current method must be available for 'put' bytecodes");
bool is_initialized_static_final_update = (byte == Bytecodes::_putstatic &&
fd.is_static() &&
- !m()->is_static_initializer());
+ !m->is_static_initializer());
bool is_initialized_instance_final_update = ((byte == Bytecodes::_putfield || byte == Bytecodes::_nofast_putfield) &&
!fd.is_static() &&
!m->is_object_initializer());
@@ -1011,7 +1009,7 @@
if (is_initialized_static_final_update || is_initialized_instance_final_update) {
ss.print("Update to %s final field %s.%s attempted from a different method (%s) than the initializer method %s ",
is_static ? "static" : "non-static", resolved_klass->external_name(), fd.name()->as_C_string(),
- m()->name()->as_C_string(),
+ m->name()->as_C_string(),
is_static ? "<clinit>" : "<init>");
THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), ss.as_string());
}
@@ -1052,7 +1050,7 @@
void LinkResolver::resolve_static_call(CallInfo& result,
const LinkInfo& link_info,
bool initialize_class, TRAPS) {
- methodHandle resolved_method = linktime_resolve_static_method(link_info, CHECK);
+ Method* resolved_method = linktime_resolve_static_method(link_info, CHECK);
// The resolved class can change as a result of this resolution.
Klass* resolved_klass = resolved_method->method_holder();
@@ -1068,14 +1066,14 @@
}
// setup result
- result.set_static(resolved_klass, resolved_method, CHECK);
+ result.set_static(resolved_klass, methodHandle(THREAD, resolved_method), CHECK);
}
// throws linktime exceptions
-methodHandle LinkResolver::linktime_resolve_static_method(const LinkInfo& link_info, TRAPS) {
+Method* LinkResolver::linktime_resolve_static_method(const LinkInfo& link_info, TRAPS) {
Klass* resolved_klass = link_info.resolved_klass();
- methodHandle resolved_method;
+ Method* resolved_method;
if (!resolved_klass->is_interface()) {
resolved_method = resolve_method(link_info, Bytecodes::_invokestatic, CHECK_NULL);
} else {
@@ -1088,7 +1086,7 @@
ResourceMark rm(THREAD);
stringStream ss;
ss.print("Expected static method '");
- resolved_method()->print_external_name(&ss);
+ resolved_method->print_external_name(&ss);
ss.print("'");
THROW_MSG_NULL(vmSymbols::java_lang_IncompatibleClassChangeError(), ss.as_string());
}
@@ -1100,13 +1098,12 @@
Handle recv,
const LinkInfo& link_info,
TRAPS) {
- methodHandle resolved_method = linktime_resolve_special_method(link_info, CHECK);
- runtime_resolve_special_method(result, link_info, resolved_method, recv, CHECK);
+ Method* resolved_method = linktime_resolve_special_method(link_info, CHECK);
+ runtime_resolve_special_method(result, link_info, methodHandle(THREAD, resolved_method), recv, CHECK);
}
// throws linktime exceptions
-methodHandle LinkResolver::linktime_resolve_special_method(const LinkInfo& link_info,
- TRAPS) {
+Method* LinkResolver::linktime_resolve_special_method(const LinkInfo& link_info, TRAPS) {
// Invokespecial is called for multiple special reasons:
// <init>
@@ -1115,7 +1112,7 @@
// and the selected method is recalculated relative to the direct superclass
// superinterface.method, which explicitly does not check shadowing
Klass* resolved_klass = link_info.resolved_klass();
- methodHandle resolved_method;
+ Method* resolved_method;
if (!resolved_klass->is_interface()) {
resolved_method = resolve_method(link_info, Bytecodes::_invokespecial, CHECK_NULL);
@@ -1210,10 +1207,12 @@
current_klass != resolved_klass) {
// Lookup super method
Klass* super_klass = current_klass->super();
- sel_method = lookup_instance_method_in_klasses(super_klass,
+ Method* instance_method = lookup_instance_method_in_klasses(super_klass,
resolved_method->name(),
resolved_method->signature(),
Klass::find_private, CHECK);
+ sel_method = methodHandle(THREAD, instance_method);
+
// check if found
if (sel_method.is_null()) {
ResourceMark rm(THREAD);
@@ -1272,7 +1271,7 @@
if (log_develop_is_enabled(Trace, itables)) {
trace_method_resolution("invokespecial selected method: resolved-class:",
- resolved_klass, resolved_klass, sel_method, true);
+ resolved_klass, resolved_klass, sel_method(), true);
}
// setup result
@@ -1282,18 +1281,18 @@
void LinkResolver::resolve_virtual_call(CallInfo& result, Handle recv, Klass* receiver_klass,
const LinkInfo& link_info,
bool check_null_and_abstract, TRAPS) {
- methodHandle resolved_method = linktime_resolve_virtual_method(link_info, CHECK);
- runtime_resolve_virtual_method(result, resolved_method,
+ Method* resolved_method = linktime_resolve_virtual_method(link_info, CHECK);
+ runtime_resolve_virtual_method(result, methodHandle(THREAD, resolved_method),
link_info.resolved_klass(),
recv, receiver_klass,
check_null_and_abstract, CHECK);
}
// throws linktime exceptions
-methodHandle LinkResolver::linktime_resolve_virtual_method(const LinkInfo& link_info,
+Method* LinkResolver::linktime_resolve_virtual_method(const LinkInfo& link_info,
TRAPS) {
// normal method resolution
- methodHandle resolved_method = resolve_method(link_info, Bytecodes::_invokevirtual, CHECK_NULL);
+ Method* resolved_method = resolve_method(link_info, Bytecodes::_invokevirtual, CHECK_NULL);
assert(resolved_method->name() != vmSymbols::object_initializer_name(), "should have been checked in verifier");
assert(resolved_method->name() != vmSymbols::class_initializer_name (), "should have been checked in verifier");
@@ -1392,7 +1391,7 @@
if (log_develop_is_enabled(Trace, vtables)) {
trace_method_resolution("invokevirtual selected method: receiver-class:",
- recv_klass, resolved_klass, selected_method,
+ recv_klass, resolved_klass, selected_method(),
false, vtable_index);
}
// setup result
@@ -1403,15 +1402,16 @@
const LinkInfo& link_info,
bool check_null_and_abstract, TRAPS) {
// throws linktime exceptions
- methodHandle resolved_method = linktime_resolve_interface_method(link_info, CHECK);
- runtime_resolve_interface_method(result, resolved_method,link_info.resolved_klass(),
+ Method* resolved_method = linktime_resolve_interface_method(link_info, CHECK);
+ methodHandle mh(THREAD, resolved_method);
+ runtime_resolve_interface_method(result, mh, link_info.resolved_klass(),
recv, recv_klass, check_null_and_abstract, CHECK);
}
-methodHandle LinkResolver::linktime_resolve_interface_method(const LinkInfo& link_info,
+Method* LinkResolver::linktime_resolve_interface_method(const LinkInfo& link_info,
TRAPS) {
// normal interface method resolution
- methodHandle resolved_method = resolve_interface_method(link_info, Bytecodes::_invokeinterface, CHECK_NULL);
+ Method* resolved_method = resolve_interface_method(link_info, Bytecodes::_invokeinterface, CHECK_NULL);
assert(resolved_method->name() != vmSymbols::object_initializer_name(), "should have been checked in verifier");
assert(resolved_method->name() != vmSymbols::class_initializer_name (), "should have been checked in verifier");
@@ -1449,10 +1449,11 @@
// This search must match the linktime preparation search for itable initialization
// to correctly enforce loader constraints for interface method inheritance.
// Private methods are skipped as the resolved method was not private.
- selected_method = lookup_instance_method_in_klasses(recv_klass,
- resolved_method->name(),
- resolved_method->signature(),
- Klass::skip_private, CHECK);
+ Method* method = lookup_instance_method_in_klasses(recv_klass,
+ resolved_method->name(),
+ resolved_method->signature(),
+ Klass::skip_private, CHECK);
+ selected_method = methodHandle(THREAD, method);
if (selected_method.is_null() && !check_null_and_abstract) {
// In theory this is a harmless placeholder value, but
@@ -1483,7 +1484,7 @@
if (log_develop_is_enabled(Trace, itables)) {
trace_method_resolution("invokeinterface selected method: receiver-class:",
- recv_klass, resolved_klass, selected_method, true);
+ recv_klass, resolved_klass, selected_method(), true);
}
// setup result
if (resolved_method->has_vtable_index()) {
@@ -1509,31 +1510,31 @@
}
-methodHandle LinkResolver::linktime_resolve_interface_method_or_null(
+Method* LinkResolver::linktime_resolve_interface_method_or_null(
const LinkInfo& link_info) {
EXCEPTION_MARK;
- methodHandle method_result = linktime_resolve_interface_method(link_info, THREAD);
+ Method* method_result = linktime_resolve_interface_method(link_info, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
- return methodHandle();
+ return NULL;
} else {
return method_result;
}
}
-methodHandle LinkResolver::linktime_resolve_virtual_method_or_null(
+Method* LinkResolver::linktime_resolve_virtual_method_or_null(
const LinkInfo& link_info) {
EXCEPTION_MARK;
- methodHandle method_result = linktime_resolve_virtual_method(link_info, THREAD);
+ Method* method_result = linktime_resolve_virtual_method(link_info, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
- return methodHandle();
+ return NULL;
} else {
return method_result;
}
}
-methodHandle LinkResolver::resolve_virtual_call_or_null(
+Method* LinkResolver::resolve_virtual_call_or_null(
Klass* receiver_klass,
const LinkInfo& link_info) {
EXCEPTION_MARK;
@@ -1541,12 +1542,12 @@
resolve_virtual_call(info, Handle(), receiver_klass, link_info, false, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
- return methodHandle();
+ return NULL;
}
return info.selected_method();
}
-methodHandle LinkResolver::resolve_interface_call_or_null(
+Method* LinkResolver::resolve_interface_call_or_null(
Klass* receiver_klass,
const LinkInfo& link_info) {
EXCEPTION_MARK;
@@ -1554,7 +1555,7 @@
resolve_interface_call(info, Handle(), receiver_klass, link_info, false, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
- return methodHandle();
+ return NULL;
}
return info.selected_method();
}
@@ -1572,24 +1573,24 @@
return info.vtable_index();
}
-methodHandle LinkResolver::resolve_static_call_or_null(const LinkInfo& link_info) {
+Method* LinkResolver::resolve_static_call_or_null(const LinkInfo& link_info) {
EXCEPTION_MARK;
CallInfo info;
resolve_static_call(info, link_info, /*initialize_class*/false, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
- return methodHandle();
+ return NULL;
}
return info.selected_method();
}
-methodHandle LinkResolver::resolve_special_call_or_null(const LinkInfo& link_info) {
+Method* LinkResolver::resolve_special_call_or_null(const LinkInfo& link_info) {
EXCEPTION_MARK;
CallInfo info;
resolve_special_call(info, Handle(), link_info, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
- return methodHandle();
+ return NULL;
}
return info.selected_method();
}
@@ -1690,8 +1691,8 @@
resolved_klass == SystemDictionary::VarHandle_klass(), "");
assert(MethodHandles::is_signature_polymorphic_name(link_info.name()), "");
Handle resolved_appendix;
- methodHandle resolved_method = lookup_polymorphic_method(link_info, &resolved_appendix, CHECK);
- result.set_handle(resolved_klass, resolved_method, resolved_appendix, CHECK);
+ Method* resolved_method = lookup_polymorphic_method(link_info, &resolved_appendix, CHECK);
+ result.set_handle(resolved_klass, methodHandle(THREAD, resolved_method), resolved_appendix, CHECK);
}
void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHandle& pool, int indy_index, TRAPS) {
--- a/src/hotspot/share/interpreter/linkResolver.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/interpreter/linkResolver.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -96,8 +96,8 @@
Klass* resolved_klass() const { return _resolved_klass; }
Klass* selected_klass() const { return _selected_klass; }
- methodHandle resolved_method() const { return _resolved_method; }
- methodHandle selected_method() const { return _selected_method; }
+ Method* resolved_method() const { return _resolved_method(); }
+ Method* selected_method() const { return _selected_method(); }
Handle resolved_appendix() const { return _resolved_appendix; }
Handle resolved_method_name() const { return _resolved_method_name; }
// Materialize a java.lang.invoke.ResolvedMethodName for this resolved_method
@@ -181,7 +181,7 @@
Symbol* signature() const { return _signature; }
Klass* resolved_klass() const { return _resolved_klass; }
Klass* current_klass() const { return _current_klass; }
- methodHandle current_method() const { return _current_method; }
+ Method* current_method() const { return _current_method(); }
constantTag tag() const { return _tag; }
bool check_access() const { return _check_access; }
@@ -205,12 +205,12 @@
bool in_imethod_resolve);
static Method* lookup_method_in_interfaces(const LinkInfo& link_info);
- static methodHandle lookup_polymorphic_method(const LinkInfo& link_info,
- Handle *appendix_result_or_null, TRAPS);
+ static Method* lookup_polymorphic_method(const LinkInfo& link_info,
+ Handle *appendix_result_or_null, TRAPS);
JVMCI_ONLY(public:) // Needed for CompilerToVM.resolveMethod()
// Not Linktime so doesn't take LinkInfo
- static methodHandle lookup_instance_method_in_klasses (Klass* klass, Symbol* name, Symbol* signature,
- Klass::PrivateLookupMode private_mode, TRAPS);
+ static Method* lookup_instance_method_in_klasses (Klass* klass, Symbol* name, Symbol* signature,
+ Klass::PrivateLookupMode private_mode, TRAPS);
JVMCI_ONLY(private:)
// Similar loader constraint checking functions that throw
@@ -222,13 +222,13 @@
Klass* current_klass,
Klass* sel_klass, TRAPS);
- static methodHandle resolve_interface_method(const LinkInfo& link_info, Bytecodes::Code code, TRAPS);
- static methodHandle resolve_method (const LinkInfo& link_info, Bytecodes::Code code, TRAPS);
+ static Method* resolve_interface_method(const LinkInfo& link_info, Bytecodes::Code code, TRAPS);
+ static Method* resolve_method (const LinkInfo& link_info, Bytecodes::Code code, TRAPS);
- static methodHandle linktime_resolve_static_method (const LinkInfo& link_info, TRAPS);
- static methodHandle linktime_resolve_special_method (const LinkInfo& link_info, TRAPS);
- static methodHandle linktime_resolve_virtual_method (const LinkInfo& link_info, TRAPS);
- static methodHandle linktime_resolve_interface_method (const LinkInfo& link_info, TRAPS);
+ static Method* linktime_resolve_static_method (const LinkInfo& link_info, TRAPS);
+ static Method* linktime_resolve_special_method (const LinkInfo& link_info, TRAPS);
+ static Method* linktime_resolve_virtual_method (const LinkInfo& link_info, TRAPS);
+ static Method* linktime_resolve_interface_method (const LinkInfo& link_info, TRAPS);
static void runtime_resolve_special_method (CallInfo& result,
const LinkInfo& link_info,
@@ -285,9 +285,9 @@
// static resolving calls (will not run any Java code);
// used only from Bytecode_invoke::static_target
- static methodHandle resolve_method_statically(Bytecodes::Code code,
- const constantPoolHandle& pool,
- int index, TRAPS);
+ static Method* resolve_method_statically(Bytecodes::Code code,
+ const constantPoolHandle& pool,
+ int index, TRAPS);
static void resolve_field_access(fieldDescriptor& result,
const constantPoolHandle& pool,
@@ -318,12 +318,12 @@
// same as above for compile-time resolution; but returns null handle instead of throwing
// an exception on error also, does not initialize klass (i.e., no side effects)
- static methodHandle resolve_virtual_call_or_null (Klass* receiver_klass,
- const LinkInfo& link_info);
- static methodHandle resolve_interface_call_or_null(Klass* receiver_klass,
- const LinkInfo& link_info);
- static methodHandle resolve_static_call_or_null (const LinkInfo& link_info);
- static methodHandle resolve_special_call_or_null (const LinkInfo& link_info);
+ static Method* resolve_virtual_call_or_null(Klass* receiver_klass,
+ const LinkInfo& link_info);
+ static Method* resolve_interface_call_or_null(Klass* receiver_klass,
+ const LinkInfo& link_info);
+ static Method* resolve_static_call_or_null(const LinkInfo& link_info);
+ static Method* resolve_special_call_or_null(const LinkInfo& link_info);
static int vtable_index_of_interface_method(Klass* klass, const methodHandle& resolved_method);
@@ -332,8 +332,8 @@
const LinkInfo& link_info);
// static resolving for compiler (does not throw exceptions, returns null handle if unsuccessful)
- static methodHandle linktime_resolve_virtual_method_or_null (const LinkInfo& link_info);
- static methodHandle linktime_resolve_interface_method_or_null(const LinkInfo& link_info);
+ static Method* linktime_resolve_virtual_method_or_null (const LinkInfo& link_info);
+ static Method* linktime_resolve_interface_method_or_null(const LinkInfo& link_info);
// runtime resolving from constant pool
static void resolve_invoke(CallInfo& result, Handle recv,
@@ -348,11 +348,11 @@
public:
// Only resolved method known.
static void throw_abstract_method_error(const methodHandle& resolved_method, TRAPS) {
- throw_abstract_method_error(resolved_method, NULL, NULL, CHECK);
+ throw_abstract_method_error(resolved_method, methodHandle(), NULL, CHECK);
}
// Resolved method and receiver klass know.
static void throw_abstract_method_error(const methodHandle& resolved_method, Klass *recv_klass, TRAPS) {
- throw_abstract_method_error(resolved_method, NULL, recv_klass, CHECK);
+ throw_abstract_method_error(resolved_method, methodHandle(), recv_klass, CHECK);
}
// Selected method is abstract.
static void throw_abstract_method_error(const methodHandle& resolved_method,
--- a/src/hotspot/share/interpreter/rewriter.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/interpreter/rewriter.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -80,13 +80,13 @@
}
// Unrewrite the bytecodes if an error occurs.
-void Rewriter::restore_bytecodes() {
+void Rewriter::restore_bytecodes(Thread* thread) {
int len = _methods->length();
bool invokespecial_error = false;
for (int i = len-1; i >= 0; i--) {
Method* method = _methods->at(i);
- scan_method(method, true, &invokespecial_error);
+ scan_method(thread, method, true, &invokespecial_error);
assert(!invokespecial_error, "reversing should not get an invokespecial error");
}
}
@@ -365,7 +365,7 @@
// Rewrites a method given the index_map information
-void Rewriter::scan_method(Method* method, bool reverse, bool* invokespecial_error) {
+void Rewriter::scan_method(Thread* thread, Method* method, bool reverse, bool* invokespecial_error) {
int nof_jsrs = 0;
bool has_monitor_bytecodes = false;
@@ -439,7 +439,7 @@
// succeeded. Therefore, the class is guaranteed to be well-formed.
InstanceKlass* klass = method->method_holder();
u2 bc_index = Bytes::get_Java_u2(bcp + prefix_length + 1);
- constantPoolHandle cp(method->constants());
+ constantPoolHandle cp(thread, method->constants());
Symbol* ref_class_name = cp->klass_name_at(cp->klass_ref_index_at(bc_index));
if (klass->name() == ref_class_name) {
@@ -548,7 +548,7 @@
for (int i = len-1; i >= 0; i--) {
Method* method = _methods->at(i);
- scan_method(method, false, &invokespecial_error);
+ scan_method(THREAD, method, false, &invokespecial_error);
if (invokespecial_error) {
// If you get an error here, there is no reversing bytecodes
// This exception is stored for this class and no further attempt is
@@ -570,7 +570,8 @@
assert(!klass->is_shared(), "archive methods must not be rewritten at run time");
}
ResourceMark rm(THREAD);
- Rewriter rw(klass, klass->constants(), klass->methods(), CHECK);
+ constantPoolHandle cpool(THREAD, klass->constants());
+ Rewriter rw(klass, cpool, klass->methods(), CHECK);
// (That's all, folks.)
}
@@ -592,7 +593,7 @@
// Stress restoring bytecodes
if (StressRewriter) {
- restore_bytecodes();
+ restore_bytecodes(THREAD);
rewrite_bytecodes(CHECK);
}
@@ -602,7 +603,7 @@
// Restore bytecodes to their unrewritten state if there are exceptions
// rewriting bytecodes or allocating the cpCache
if (HAS_PENDING_EXCEPTION) {
- restore_bytecodes();
+ restore_bytecodes(THREAD);
return;
}
@@ -620,7 +621,7 @@
// relocating bytecodes. If some are relocated, that is ok because that
// doesn't affect constant pool to cpCache rewriting.
if (HAS_PENDING_EXCEPTION) {
- restore_bytecodes();
+ restore_bytecodes(THREAD);
return;
}
// Method might have gotten rewritten.
--- a/src/hotspot/share/interpreter/rewriter.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/interpreter/rewriter.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -184,7 +184,7 @@
void compute_index_maps();
void make_constant_pool_cache(TRAPS);
- void scan_method(Method* m, bool reverse, bool* invokespecial_error);
+ void scan_method(Thread* thread, Method* m, bool reverse, bool* invokespecial_error);
void rewrite_Object_init(const methodHandle& m, TRAPS);
void rewrite_member_reference(address bcp, int offset, bool reverse);
void maybe_rewrite_invokehandle(address opc, int cp_index, int cache_index, bool reverse);
@@ -198,7 +198,7 @@
void rewrite_bytecodes(TRAPS);
// Revert bytecodes in case of an exception.
- void restore_bytecodes();
+ void restore_bytecodes(Thread* thread);
static methodHandle rewrite_jsrs(const methodHandle& m, TRAPS);
public:
--- a/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -28,7 +28,7 @@
#include "jfr/leakprofiler/chains/edgeStore.hpp"
#include "jfr/leakprofiler/chains/edgeUtils.hpp"
#include "jfr/leakprofiler/utilities/unifiedOop.hpp"
-#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/objArrayOop.inline.hpp"
#include "oops/oopsHierarchy.hpp"
--- a/src/hotspot/share/jvmci/compilerRuntime.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/jvmci/compilerRuntime.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -141,7 +141,7 @@
// Make sure it's resolved first
CallInfo callInfo;
- constantPoolHandle cp(holder->constants());
+ constantPoolHandle cp(THREAD, holder->constants());
ConstantPoolCacheEntry* cp_cache_entry = cp->cache()->entry_at(cp->decode_cpcache_index(index, true));
Bytecodes::Code invoke_code = bytecode.invoke_code();
if (!cp_cache_entry->is_resolved(invoke_code)) {
@@ -157,7 +157,7 @@
Handle appendix(THREAD, cp_cache_entry->appendix_if_resolved(cp));
Klass *appendix_klass = appendix.is_null() ? NULL : appendix->klass();
- methodHandle adapter_method(cp_cache_entry->f1_as_method());
+ methodHandle adapter_method(THREAD, cp_cache_entry->f1_as_method());
InstanceKlass *adapter_klass = adapter_method->method_holder();
if (appendix_klass != NULL && appendix_klass->is_instance_klass()) {
--- a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -203,8 +203,8 @@
result = JVMCIENV->get_jvmci_type(klass, JVMCI_CATCH);
} else if (h->is_method()) {
Method* method = (Method*) h;
- methodHandle mh(method);
- result = JVMCIENV->get_jvmci_method(method, JVMCI_CATCH);
+ methodHandle mh(THREAD, method);
+ result = JVMCIENV->get_jvmci_method(mh, JVMCI_CATCH);
}
jobject ref = JVMCIENV->get_jobject(result);
record_meta_ref(ref, index);
@@ -501,8 +501,8 @@
int length = JVMCIENV->get_length(methods);
for (int i = 0; i < length; ++i) {
JVMCIObject method_handle = JVMCIENV->get_object_at(methods, i);
- methodHandle method = jvmci_env()->asMethod(method_handle);
- _dependencies->assert_evol_method(method());
+ Method* method = jvmci_env()->asMethod(method_handle);
+ _dependencies->assert_evol_method(method);
}
}
}
@@ -620,13 +620,15 @@
jvmci_env()->set_compile_state(compile_state);
}
- methodHandle method = jvmci_env()->asMethod(jvmci_env()->get_HotSpotCompiledNmethod_method(compiled_code));
+ Thread* thread = Thread::current();
+
+ methodHandle method(thread, jvmci_env()->asMethod(jvmci_env()->get_HotSpotCompiledNmethod_method(compiled_code)));
jint entry_bci = jvmci_env()->get_HotSpotCompiledNmethod_entryBCI(compiled_code);
bool has_unsafe_access = jvmci_env()->get_HotSpotCompiledNmethod_hasUnsafeAccess(compiled_code) == JNI_TRUE;
jint id = jvmci_env()->get_HotSpotCompiledNmethod_id(compiled_code);
if (id == -1) {
// Make sure a valid compile_id is associated with every compile
- id = CompileBroker::assign_compile_id_unlocked(Thread::current(), method, entry_bci);
+ id = CompileBroker::assign_compile_id_unlocked(thread, method, entry_bci);
jvmci_env()->set_HotSpotCompiledNmethod_id(compiled_code, id);
}
if (!jvmci_env()->isa_HotSpotNmethod(installed_code)) {
@@ -659,7 +661,8 @@
void CodeInstaller::initialize_fields(JVMCIObject target, JVMCIObject compiled_code, JVMCI_TRAPS) {
if (jvmci_env()->isa_HotSpotCompiledNmethod(compiled_code)) {
JVMCIObject hotspotJavaMethod = jvmci_env()->get_HotSpotCompiledNmethod_method(compiled_code);
- methodHandle method = jvmci_env()->asMethod(hotspotJavaMethod);
+ Thread* thread = Thread::current();
+ methodHandle method(thread, jvmci_env()->asMethod(hotspotJavaMethod));
_parameter_count = method->size_of_parameters();
TRACE_jvmci_2("installing code for %s", method->name_and_sig_as_C_string());
} else {
@@ -937,10 +940,10 @@
JVMCIObject impl_handle = jvmci_env()->get_Assumptions_ConcreteMethod_impl(assumption);
JVMCIObject context_handle = jvmci_env()->get_Assumptions_ConcreteMethod_context(assumption);
- methodHandle impl = jvmci_env()->asMethod(impl_handle);
+ Method* impl = jvmci_env()->asMethod(impl_handle);
Klass* context = jvmci_env()->asKlass(context_handle);
- _dependencies->assert_unique_concrete_method(context, impl());
+ _dependencies->assert_unique_concrete_method(context, impl);
}
void CodeInstaller::assumption_CallSiteTargetValue(JVMCIObject assumption, JVMCI_TRAPS) {
@@ -1064,7 +1067,8 @@
}
JVMCIObject hotspot_method = jvmci_env()->get_BytecodePosition_method(position);
- Method* method = jvmci_env()->asMethod(hotspot_method);
+ Thread* thread = Thread::current();
+ methodHandle method(thread, jvmci_env()->asMethod(hotspot_method));
jint bci = map_jvmci_bci(jvmci_env()->get_BytecodePosition_bci(position));
if (bci == jvmci_env()->get_BytecodeFrame_BEFORE_BCI()) {
bci = SynchronizationEntryBCI;
@@ -1077,7 +1081,7 @@
if (bci < 0){
reexecute = false;
} else {
- Bytecodes::Code code = Bytecodes::java_code_at(method, method->bcp_from(bci));
+ Bytecodes::Code code = Bytecodes::java_code_at(method(), method->bcp_from(bci));
reexecute = bytecode_should_reexecute(code);
if (frame.is_non_null()) {
reexecute = (jvmci_env()->get_BytecodeFrame_duringCall(frame) == JNI_FALSE);
--- a/src/hotspot/share/jvmci/jvmciCompiler.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/jvmci/jvmciCompiler.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -67,7 +67,7 @@
// Initialize compile queue with a selected set of methods.
int len = objectMethods->length();
for (int i = 0; i < len; i++) {
- methodHandle mh = objectMethods->at(i);
+ methodHandle mh(THREAD, objectMethods->at(i));
if (!mh->is_native() && !mh->is_static() && !mh->is_initializer()) {
ResourceMark rm;
int hot_count = 10; // TODO: what's the appropriate value?
@@ -100,7 +100,7 @@
JVMCI::compiler_runtime()->bootstrap_finished(CHECK);
}
-bool JVMCICompiler::force_comp_at_level_simple(Method *method) {
+bool JVMCICompiler::force_comp_at_level_simple(const methodHandle& method) {
if (UseJVMCINativeLibrary) {
// This mechanism exists to force compilation of a JVMCI compiler by C1
// to reduces the compilation time spent on the JVMCI compiler itself. In
--- a/src/hotspot/share/jvmci/jvmciCompiler.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/jvmci/jvmciCompiler.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -84,7 +84,7 @@
void bootstrap(TRAPS);
// Should force compilation of method at CompLevel_simple?
- bool force_comp_at_level_simple(Method* method);
+ bool force_comp_at_level_simple(const methodHandle& method);
bool is_bootstrapping() const { return _bootstrapping; }
--- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -254,7 +254,7 @@
C2V_END
C2V_VMENTRY_NULL(jbyteArray, getBytecode, (JNIEnv* env, jobject, jobject jvmci_method))
- methodHandle method = JVMCIENV->asMethod(jvmci_method);
+ methodHandle method(THREAD, JVMCIENV->asMethod(jvmci_method));
int code_size = method->code_size();
jbyte* reconstituted_code = NEW_RESOURCE_ARRAY(jbyte, code_size);
@@ -332,12 +332,12 @@
C2V_END
C2V_VMENTRY_0(jint, getExceptionTableLength, (JNIEnv* env, jobject, jobject jvmci_method))
- methodHandle method = JVMCIENV->asMethod(jvmci_method);
+ Method* method = JVMCIENV->asMethod(jvmci_method);
return method->exception_table_length();
C2V_END
C2V_VMENTRY_0(jlong, getExceptionTableStart, (JNIEnv* env, jobject, jobject jvmci_method))
- methodHandle method = JVMCIENV->asMethod(jvmci_method);
+ Method* method = JVMCIENV->asMethod(jvmci_method);
if (method->exception_table_length() == 0) {
return 0L;
}
@@ -359,13 +359,13 @@
slot = java_lang_reflect_Method::slot(executable);
}
Klass* holder = java_lang_Class::as_Klass(mirror);
- methodHandle method = InstanceKlass::cast(holder)->method_with_idnum(slot);
+ methodHandle method (THREAD, InstanceKlass::cast(holder)->method_with_idnum(slot));
JVMCIObject result = JVMCIENV->get_jvmci_method(method, JVMCI_CHECK_NULL);
return JVMCIENV->get_jobject(result);
}
C2V_VMENTRY_NULL(jobject, getResolvedJavaMethod, (JNIEnv* env, jobject, jobject base, jlong offset))
- methodHandle method;
+ Method* method;
JVMCIObject base_object = JVMCIENV->wrap(base);
if (base_object.is_null()) {
method = *((Method**)(offset));
@@ -379,16 +379,16 @@
} else if (JVMCIENV->isa_HotSpotResolvedJavaMethodImpl(base_object)) {
method = JVMCIENV->asMethod(base_object);
}
- if (method.is_null()) {
+ if (method == NULL) {
JVMCI_THROW_MSG_NULL(IllegalArgumentException, err_msg("Unexpected type: %s", JVMCIENV->klass_name(base_object)));
}
- assert (method.is_null() || method->is_method(), "invalid read");
- JVMCIObject result = JVMCIENV->get_jvmci_method(method, JVMCI_CHECK_NULL);
+ assert (method->is_method(), "invalid read");
+ JVMCIObject result = JVMCIENV->get_jvmci_method(methodHandle(THREAD, method), JVMCI_CHECK_NULL);
return JVMCIENV->get_jobject(result);
}
C2V_VMENTRY_NULL(jobject, getConstantPool, (JNIEnv* env, jobject, jobject object_handle))
- constantPoolHandle cp;
+ ConstantPool* cp = NULL;
JVMCIObject object = JVMCIENV->wrap(object_handle);
if (object.is_null()) {
JVMCI_THROW_NULL(NullPointerException);
@@ -401,9 +401,9 @@
JVMCI_THROW_MSG_NULL(IllegalArgumentException,
err_msg("Unexpected type: %s", JVMCIENV->klass_name(object)));
}
- assert(!cp.is_null(), "npe");
+ assert(cp != NULL, "npe");
- JVMCIObject result = JVMCIENV->get_jvmci_constant_pool(cp, JVMCI_CHECK_NULL);
+ JVMCIObject result = JVMCIENV->get_jvmci_constant_pool(constantPoolHandle(THREAD, cp), JVMCI_CHECK_NULL);
return JVMCIENV->get_jobject(result);
}
@@ -451,7 +451,7 @@
}
C2V_VMENTRY_NULL(jobject, findUniqueConcreteMethod, (JNIEnv* env, jobject, jobject jvmci_type, jobject jvmci_method))
- methodHandle method = JVMCIENV->asMethod(jvmci_method);
+ methodHandle method (THREAD, JVMCIENV->asMethod(jvmci_method));
Klass* holder = JVMCIENV->asKlass(jvmci_type);
if (holder->is_interface()) {
JVMCI_THROW_MSG_NULL(InternalError, err_msg("Interface %s should be handled in Java code", holder->external_name()));
@@ -463,7 +463,7 @@
methodHandle ucm;
{
MutexLocker locker(Compile_lock);
- ucm = Dependencies::find_unique_concrete_method(holder, method());
+ ucm = methodHandle(THREAD, Dependencies::find_unique_concrete_method(holder, method()));
}
JVMCIObject result = JVMCIENV->get_jvmci_method(ucm, JVMCI_CHECK_NULL);
return JVMCIENV->get_jobject(result);
@@ -487,25 +487,25 @@
C2V_END
C2V_VMENTRY_0(jboolean, methodIsIgnoredBySecurityStackWalk,(JNIEnv* env, jobject, jobject jvmci_method))
- methodHandle method = JVMCIENV->asMethod(jvmci_method);
+ Method* method = JVMCIENV->asMethod(jvmci_method);
return method->is_ignored_by_security_stack_walk();
C2V_END
C2V_VMENTRY_0(jboolean, isCompilable,(JNIEnv* env, jobject, jobject jvmci_method))
- methodHandle method = JVMCIENV->asMethod(jvmci_method);
- constantPoolHandle cp = method->constMethod()->constants();
- assert(!cp.is_null(), "npe");
+ Method* method = JVMCIENV->asMethod(jvmci_method);
+ ConstantPool* cp = method->constMethod()->constants();
+ assert(cp != NULL, "npe");
// don't inline method when constant pool contains a CONSTANT_Dynamic
return !method->is_not_compilable(CompLevel_full_optimization) && !cp->has_dynamic_constant();
C2V_END
C2V_VMENTRY_0(jboolean, hasNeverInlineDirective,(JNIEnv* env, jobject, jobject jvmci_method))
- methodHandle method = JVMCIENV->asMethod(jvmci_method);
+ methodHandle method (THREAD, JVMCIENV->asMethod(jvmci_method));
return !Inline || CompilerOracle::should_not_inline(method) || method->dont_inline();
C2V_END
C2V_VMENTRY_0(jboolean, shouldInlineMethod,(JNIEnv* env, jobject, jobject jvmci_method))
- methodHandle method = JVMCIENV->asMethod(jvmci_method);
+ methodHandle method (THREAD, JVMCIENV->asMethod(jvmci_method));
return CompilerOracle::should_inline(method) || method->force_inline();
C2V_END
@@ -613,35 +613,35 @@
}
C2V_VMENTRY_NULL(jobject, resolvePossiblyCachedConstantInPool, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint index))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
oop result = cp->resolve_possibly_cached_constant_at(index, CHECK_NULL);
return JVMCIENV->get_jobject(JVMCIENV->get_object_constant(result));
C2V_END
C2V_VMENTRY_0(jint, lookupNameAndTypeRefIndexInPool, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint index))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
return cp->name_and_type_ref_index_at(index);
C2V_END
C2V_VMENTRY_NULL(jobject, lookupNameInPool, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint which))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
JVMCIObject sym = JVMCIENV->create_string(cp->name_ref_at(which), JVMCI_CHECK_NULL);
return JVMCIENV->get_jobject(sym);
C2V_END
C2V_VMENTRY_NULL(jobject, lookupSignatureInPool, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint which))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
JVMCIObject sym = JVMCIENV->create_string(cp->signature_ref_at(which), JVMCI_CHECK_NULL);
return JVMCIENV->get_jobject(sym);
C2V_END
C2V_VMENTRY_0(jint, lookupKlassRefIndexInPool, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint index))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
return cp->klass_ref_index_at(index);
C2V_END
C2V_VMENTRY_NULL(jobject, resolveTypeInPool, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint index))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
Klass* klass = cp->klass_at(index, CHECK_NULL);
JVMCIKlassHandle resolved_klass(THREAD, klass);
if (resolved_klass->is_instance_klass()) {
@@ -656,7 +656,7 @@
C2V_END
C2V_VMENTRY_NULL(jobject, lookupKlassInPool, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint index, jbyte opcode))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
Klass* loading_klass = cp->pool_holder();
bool is_accessible = false;
JVMCIKlassHandle klass(THREAD, JVMCIRuntime::get_klass_by_index(cp, index, is_accessible, loading_klass));
@@ -684,30 +684,31 @@
C2V_END
C2V_VMENTRY_NULL(jobject, lookupAppendixInPool, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint index))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
oop appendix_oop = ConstantPool::appendix_at_if_loaded(cp, index);
return JVMCIENV->get_jobject(JVMCIENV->get_object_constant(appendix_oop));
C2V_END
C2V_VMENTRY_NULL(jobject, lookupMethodInPool, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint index, jbyte opcode))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
InstanceKlass* pool_holder = cp->pool_holder();
Bytecodes::Code bc = (Bytecodes::Code) (((int) opcode) & 0xFF);
- methodHandle method = JVMCIRuntime::get_method_by_index(cp, index, bc, pool_holder);
+ methodHandle method(THREAD, JVMCIRuntime::get_method_by_index(cp, index, bc, pool_holder));
JVMCIObject result = JVMCIENV->get_jvmci_method(method, JVMCI_CHECK_NULL);
return JVMCIENV->get_jobject(result);
C2V_END
C2V_VMENTRY_0(jint, constantPoolRemapInstructionOperandFromCache, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint index))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
return cp->remap_instruction_operand_from_cache(index);
C2V_END
C2V_VMENTRY_NULL(jobject, resolveFieldInPool, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint index, jobject jvmci_method, jbyte opcode, jintArray info_handle))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
Bytecodes::Code code = (Bytecodes::Code)(((int) opcode) & 0xFF);
fieldDescriptor fd;
- LinkInfo link_info(cp, index, (jvmci_method != NULL) ? JVMCIENV->asMethod(jvmci_method) : NULL, CHECK_0);
+ methodHandle mh(THREAD, (jvmci_method != NULL) ? JVMCIENV->asMethod(jvmci_method) : NULL);
+ LinkInfo link_info(cp, index, mh, CHECK_0);
LinkResolver::resolve_field(fd, link_info, Bytecodes::java_code(code), false, CHECK_0);
JVMCIPrimitiveArray info = JVMCIENV->wrap(info_handle);
if (info.is_null() || JVMCIENV->get_length(info) != 3) {
@@ -723,7 +724,7 @@
C2V_VMENTRY_0(jint, getVtableIndexForInterfaceMethod, (JNIEnv* env, jobject, jobject jvmci_type, jobject jvmci_method))
Klass* klass = JVMCIENV->asKlass(jvmci_type);
- Method* method = JVMCIENV->asMethod(jvmci_method);
+ methodHandle method(THREAD, JVMCIENV->asMethod(jvmci_method));
if (klass->is_interface()) {
JVMCI_THROW_MSG_0(InternalError, err_msg("Interface %s should be handled in Java code", klass->external_name()));
}
@@ -742,7 +743,7 @@
C2V_VMENTRY_NULL(jobject, resolveMethod, (JNIEnv* env, jobject, jobject receiver_jvmci_type, jobject jvmci_method, jobject caller_jvmci_type))
Klass* recv_klass = JVMCIENV->asKlass(receiver_jvmci_type);
Klass* caller_klass = JVMCIENV->asKlass(caller_jvmci_type);
- methodHandle method = JVMCIENV->asMethod(jvmci_method);
+ methodHandle method(THREAD, JVMCIENV->asMethod(jvmci_method));
Klass* resolved = method->method_holder();
Symbol* h_name = method->name();
@@ -765,7 +766,7 @@
}
LinkInfo link_info(resolved, h_name, h_signature, caller_klass);
- methodHandle m;
+ Method* m = NULL;
// Only do exact lookup if receiver klass has been linked. Otherwise,
// the vtable has not been setup, and the LinkResolver will fail.
if (recv_klass->is_array_klass() ||
@@ -777,12 +778,12 @@
}
}
- if (m.is_null()) {
+ if (m == NULL) {
// Return NULL if there was a problem with lookup (uninitialized class, etc.)
return NULL;
}
- JVMCIObject result = JVMCIENV->get_jvmci_method(m, JVMCI_CHECK_NULL);
+ JVMCIObject result = JVMCIENV->get_jvmci_method(methodHandle(THREAD, m), JVMCI_CHECK_NULL);
return JVMCIENV->get_jobject(result);
C2V_END
@@ -798,7 +799,8 @@
return NULL;
}
InstanceKlass* iklass = InstanceKlass::cast(klass);
- JVMCIObject result = JVMCIENV->get_jvmci_method(iklass->class_initializer(), JVMCI_CHECK_NULL);
+ methodHandle clinit(THREAD, iklass->class_initializer());
+ JVMCIObject result = JVMCIENV->get_jvmci_method(clinit, JVMCI_CHECK_NULL);
return JVMCIENV->get_jobject(result);
C2V_END
@@ -813,7 +815,7 @@
C2V_END
C2V_VMENTRY(void, setNotInlinableOrCompilable,(JNIEnv* env, jobject, jobject jvmci_method))
- methodHandle method = JVMCIENV->asMethod(jvmci_method);
+ methodHandle method(THREAD, JVMCIENV->asMethod(jvmci_method));
method->set_not_c1_compilable();
method->set_not_c2_compilable();
method->set_dont_inline(true);
@@ -1009,7 +1011,7 @@
C2V_VMENTRY_NULL(jobject, getStackTraceElement, (JNIEnv* env, jobject, jobject jvmci_method, int bci))
HandleMark hm;
- methodHandle method = JVMCIENV->asMethod(jvmci_method);
+ methodHandle method(THREAD, JVMCIENV->asMethod(jvmci_method));
JVMCIObject element = JVMCIENV->new_StackTraceElement(method, bci, JVMCI_CHECK_NULL);
return JVMCIENV->get_jobject(element);
C2V_END
@@ -1026,7 +1028,7 @@
if (nm == NULL) {
JVMCI_THROW_NULL(InvalidInstalledCodeException);
}
- methodHandle mh = nm->method();
+ methodHandle mh(THREAD, nm->method());
Symbol* signature = mh->signature();
JavaCallArguments jca(mh->size_of_parameters());
@@ -1104,7 +1106,7 @@
C2V_END
C2V_VMENTRY(void, reprofile, (JNIEnv* env, jobject, jobject jvmci_method))
- Method* method = JVMCIENV->asMethod(jvmci_method);
+ methodHandle method(THREAD, JVMCIENV->asMethod(jvmci_method));
MethodCounters* mcs = method->method_counters();
if (mcs != NULL) {
mcs->clear_counters();
@@ -1161,7 +1163,7 @@
if (jvmci_method == NULL) {
JVMCI_THROW_0(NullPointerException);
}
- Method* method = JVMCIENV->asMethod(jvmci_method);
+ methodHandle method(THREAD, JVMCIENV->asMethod(jvmci_method));
if (entry_bci >= method->code_size() || entry_bci < -1) {
JVMCI_THROW_MSG_0(IllegalArgumentException, err_msg("Unexpected bci %d", entry_bci));
}
@@ -1203,7 +1205,7 @@
LinkInfo link_info(spec_klass, name, signature);
LinkResolver::resolve_interface_call(
callinfo, receiver, recvrKlass, link_info, true, CHECK);
- methodHandle method = callinfo.selected_method();
+ methodHandle method(THREAD, callinfo.selected_method());
assert(method.not_null(), "should have thrown exception");
// Invoke the method
@@ -1277,7 +1279,8 @@
locals = cvf->locals();
HotSpotJVMCI::HotSpotStackFrameReference::set_bci(JVMCIENV, frame_reference(), cvf->bci());
- JVMCIObject method = JVMCIENV->get_jvmci_method(cvf->method(), JVMCI_CHECK_NULL);
+ methodHandle mh(THREAD, cvf->method());
+ JVMCIObject method = JVMCIENV->get_jvmci_method(mh, JVMCI_CHECK_NULL);
HotSpotJVMCI::HotSpotStackFrameReference::set_method(JVMCIENV, frame_reference(), JNIHandles::resolve(method.as_jobject()));
}
}
@@ -1290,7 +1293,8 @@
} else {
locals = ivf->locals();
HotSpotJVMCI::HotSpotStackFrameReference::set_bci(JVMCIENV, frame_reference(), ivf->bci());
- JVMCIObject method = JVMCIENV->get_jvmci_method(ivf->method(), JVMCI_CHECK_NULL);
+ methodHandle mh(THREAD, ivf->method());
+ JVMCIObject method = JVMCIENV->get_jvmci_method(mh, JVMCI_CHECK_NULL);
HotSpotJVMCI::HotSpotStackFrameReference::set_method(JVMCIENV, frame_reference(), JNIHandles::resolve(method.as_jobject()));
HotSpotJVMCI::HotSpotStackFrameReference::set_localIsVirtual(JVMCIENV, frame_reference(), NULL);
}
@@ -1370,7 +1374,7 @@
C2V_END
C2V_VMENTRY(void, resolveInvokeDynamicInPool, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint index))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
CallInfo callInfo;
LinkResolver::resolve_invoke(callInfo, Handle(), cp, index, Bytecodes::_invokedynamic, CHECK);
ConstantPoolCacheEntry* cp_cache_entry = cp->invokedynamic_cp_cache_entry_at(index);
@@ -1378,7 +1382,7 @@
C2V_END
C2V_VMENTRY(void, resolveInvokeHandleInPool, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint index))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
Klass* holder = cp->klass_ref_at(index, CHECK);
Symbol* name = cp->name_ref_at(index);
if (MethodHandles::is_signature_polymorphic_name(holder, name)) {
@@ -1390,7 +1394,7 @@
C2V_END
C2V_VMENTRY_0(jint, isResolvedInvokeHandleInPool, (JNIEnv* env, jobject, jobject jvmci_constant_pool, jint index))
- constantPoolHandle cp = JVMCIENV->asConstantPool(jvmci_constant_pool);
+ constantPoolHandle cp(THREAD, JVMCIENV->asConstantPool(jvmci_constant_pool));
ConstantPoolCacheEntry* cp_cache_entry = cp->cache()->entry_at(cp->decode_cpcache_index(index));
if (cp_cache_entry->is_resolved(Bytecodes::_invokehandle)) {
// MethodHandle.invoke* --> LambdaForm?
@@ -1405,7 +1409,7 @@
vmassert(MethodHandles::is_method_handle_invoke_name(resolved_klass, name_sym), "!");
vmassert(MethodHandles::is_signature_polymorphic_name(resolved_klass, name_sym), "!");
- methodHandle adapter_method(cp_cache_entry->f1_as_method());
+ methodHandle adapter_method(THREAD, cp_cache_entry->f1_as_method());
methodHandle resolved_method(adapter_method);
@@ -1416,7 +1420,7 @@
vmassert(!MethodHandles::is_signature_polymorphic_static(resolved_method->intrinsic_id()), "!");
vmassert(cp_cache_entry->appendix_if_resolved(cp) == NULL, "!");
- methodHandle m(LinkResolver::linktime_resolve_virtual_method_or_null(link_info));
+ methodHandle m(THREAD, LinkResolver::linktime_resolve_virtual_method_or_null(link_info));
vmassert(m == resolved_method, "!!");
return -1;
}
@@ -1909,7 +1913,8 @@
}
JVMCIObjectArray methods = JVMCIENV->new_ResolvedJavaMethod_array(constructors_array.length(), JVMCI_CHECK_NULL);
for (int i = 0; i < constructors_array.length(); i++) {
- JVMCIObject method = JVMCIENV->get_jvmci_method(constructors_array.at(i), JVMCI_CHECK_NULL);
+ methodHandle ctor(THREAD, constructors_array.at(i));
+ JVMCIObject method = JVMCIENV->get_jvmci_method(ctor, JVMCI_CHECK_NULL);
JVMCIENV->put_object_at(methods, i, method);
}
return JVMCIENV->get_jobjectArray(methods);
@@ -1938,7 +1943,8 @@
}
JVMCIObjectArray methods = JVMCIENV->new_ResolvedJavaMethod_array(methods_array.length(), JVMCI_CHECK_NULL);
for (int i = 0; i < methods_array.length(); i++) {
- JVMCIObject method = JVMCIENV->get_jvmci_method(methods_array.at(i), JVMCI_CHECK_NULL);
+ methodHandle mh(THREAD, methods_array.at(i));
+ JVMCIObject method = JVMCIENV->get_jvmci_method(mh, JVMCI_CHECK_NULL);
JVMCIENV->put_object_at(methods, i, method);
}
return JVMCIENV->get_jobjectArray(methods);
@@ -2259,7 +2265,7 @@
InstanceKlass* iklass = InstanceKlass::cast(klass);
for (int i = 0; i < iklass->methods()->length(); i++) {
- Method* method = iklass->methods()->at(i);
+ methodHandle method(THREAD, iklass->methods()->at(i));
if (method->is_native()) {
// Compute argument size
@@ -2425,7 +2431,7 @@
JVMCIObject obj = thisEnv->wrap(obj_handle);
JVMCIObject result;
if (thisEnv->isa_HotSpotResolvedJavaMethodImpl(obj)) {
- Method* method = thisEnv->asMethod(obj);
+ methodHandle method(THREAD, thisEnv->asMethod(obj));
result = peerEnv->get_jvmci_method(method, JVMCI_CHECK_0);
} else if (thisEnv->isa_HotSpotResolvedObjectTypeImpl(obj)) {
Klass* klass = thisEnv->asKlass(obj);
@@ -2456,13 +2462,13 @@
}
if (result.is_null()) {
JVMCIObject methodObject = thisEnv->get_HotSpotNmethod_method(obj);
- methodHandle mh = thisEnv->asMethod(methodObject);
+ methodHandle mh(THREAD, thisEnv->asMethod(methodObject));
jboolean isDefault = thisEnv->get_HotSpotNmethod_isDefault(obj);
jlong compileIdSnapshot = thisEnv->get_HotSpotNmethod_compileIdSnapshot(obj);
JVMCIObject name_string = thisEnv->get_InstalledCode_name(obj);
const char* cstring = name_string.is_null() ? NULL : thisEnv->as_utf8_string(name_string);
// Create a new HotSpotNmethod instance in the peer runtime
- result = peerEnv->new_HotSpotNmethod(mh(), cstring, isDefault, compileIdSnapshot, JVMCI_CHECK_0);
+ result = peerEnv->new_HotSpotNmethod(mh, cstring, isDefault, compileIdSnapshot, JVMCI_CHECK_0);
if (nm == NULL) {
// nmethod must have been unloaded
} else {
@@ -2522,7 +2528,7 @@
C2V_VMENTRY_NULL(jobject, asReflectionExecutable, (JNIEnv* env, jobject, jobject jvmci_method))
requireInHotSpot("asReflectionExecutable", JVMCI_CHECK_NULL);
- methodHandle m = JVMCIENV->asMethod(jvmci_method);
+ methodHandle m(THREAD, JVMCIENV->asMethod(jvmci_method));
oop executable;
if (m->is_initializer()) {
if (m->is_static_initializer()) {
@@ -2587,7 +2593,7 @@
}
C2V_VMENTRY_0(jlong, getFailedSpeculationsAddress, (JNIEnv* env, jobject, jobject jvmci_method))
- methodHandle method = JVMCIENV->asMethod(jvmci_method);
+ methodHandle method(THREAD, JVMCIENV->asMethod(jvmci_method));
MethodData* method_data = method->method_data();
if (method_data == NULL) {
ClassLoaderData* loader_data = method->method_holder()->class_loader_data();
--- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -228,7 +228,6 @@
do_bool_flag(UseCompressedOops) \
X86_ONLY(do_bool_flag(UseCountLeadingZerosInstruction)) \
X86_ONLY(do_bool_flag(UseCountTrailingZerosInstruction)) \
- do_bool_flag(UseConcMarkSweepGC) \
do_bool_flag(UseG1GC) \
do_bool_flag(UseParallelGC) \
do_bool_flag(UseParallelOldGC) \
--- a/src/hotspot/share/jvmci/jvmciEnv.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/jvmci/jvmciEnv.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -952,7 +952,7 @@
JVMCIObject JVMCIEnv::new_HotSpotNmethod(const methodHandle& method, const char* name, jboolean isDefault, jlong compileId, JVMCI_TRAPS) {
JavaThread* THREAD = JavaThread::current();
- JVMCIObject methodObject = get_jvmci_method(method(), JVMCI_CHECK_(JVMCIObject()));
+ JVMCIObject methodObject = get_jvmci_method(method, JVMCI_CHECK_(JVMCIObject()));
if (is_hotspot()) {
InstanceKlass* ik = InstanceKlass::cast(HotSpotJVMCI::HotSpotNmethod::klass());
--- a/src/hotspot/share/jvmci/jvmciJavaClasses.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/jvmci/jvmciJavaClasses.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -87,7 +87,7 @@
#ifndef PRODUCT
static void check_resolve_method(const char* call_type, Klass* resolved_klass, Symbol* method_name, Symbol* method_signature, TRAPS) {
- methodHandle method;
+ Method* method;
LinkInfo link_info(resolved_klass, method_name, method_signature, NULL, LinkInfo::skip_access_check);
if (strcmp(call_type, "call_static") == 0) {
method = LinkResolver::resolve_static_call_or_null(link_info);
@@ -98,7 +98,7 @@
} else {
fatal("Unknown or unsupported call type: %s", call_type);
}
- if (method.is_null()) {
+ if (method == NULL) {
fatal("Could not resolve %s.%s%s", resolved_klass->external_name(), method_name->as_C_string(), method_signature->as_C_string());
}
}
--- a/src/hotspot/share/jvmci/jvmciRuntime.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1150,16 +1150,16 @@
// ------------------------------------------------------------------
// Perform an appropriate method lookup based on accessor, holder,
// name, signature, and bytecode.
-methodHandle JVMCIRuntime::lookup_method(InstanceKlass* accessor,
- Klass* holder,
- Symbol* name,
- Symbol* sig,
- Bytecodes::Code bc,
- constantTag tag) {
+Method* JVMCIRuntime::lookup_method(InstanceKlass* accessor,
+ Klass* holder,
+ Symbol* name,
+ Symbol* sig,
+ Bytecodes::Code bc,
+ constantTag tag) {
// Accessibility checks are performed in JVMCIEnv::get_method_by_index_impl().
assert(check_klass_accessibility(accessor, holder), "holder not accessible");
- methodHandle dest_method;
+ Method* dest_method;
LinkInfo link_info(holder, name, sig, accessor, LinkInfo::needs_access_check, tag);
switch (bc) {
case Bytecodes::_invokestatic:
@@ -1186,9 +1186,9 @@
// ------------------------------------------------------------------
-methodHandle JVMCIRuntime::get_method_by_index_impl(const constantPoolHandle& cpool,
- int index, Bytecodes::Code bc,
- InstanceKlass* accessor) {
+Method* JVMCIRuntime::get_method_by_index_impl(const constantPoolHandle& cpool,
+ int index, Bytecodes::Code bc,
+ InstanceKlass* accessor) {
if (bc == Bytecodes::_invokedynamic) {
ConstantPoolCacheEntry* cpce = cpool->invokedynamic_cp_cache_entry_at(index);
bool is_resolved = !cpce->is_f1_null();
@@ -1196,7 +1196,7 @@
// Get the invoker Method* from the constant pool.
// (The appendix argument, if any, will be noted in the method's signature.)
Method* adapter = cpce->f1_as_method();
- return methodHandle(adapter);
+ return adapter;
}
return NULL;
@@ -1235,8 +1235,8 @@
if (holder_is_accessible) { // Our declared holder is loaded.
constantTag tag = cpool->tag_ref_at(index);
- methodHandle m = lookup_method(accessor, holder, name_sym, sig_sym, bc, tag);
- if (!m.is_null()) {
+ Method* m = lookup_method(accessor, holder, name_sym, sig_sym, bc, tag);
+ if (m != NULL) {
// We found the method.
return m;
}
@@ -1265,7 +1265,7 @@
// ------------------------------------------------------------------
-methodHandle JVMCIRuntime::get_method_by_index(const constantPoolHandle& cpool,
+Method* JVMCIRuntime::get_method_by_index(const constantPoolHandle& cpool,
int index, Bytecodes::Code bc,
InstanceKlass* accessor) {
ResourceMark rm;
--- a/src/hotspot/share/jvmci/jvmciRuntime.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -117,18 +117,18 @@
Klass* loading_klass);
static void get_field_by_index_impl(InstanceKlass* loading_klass, fieldDescriptor& fd,
int field_index);
- static methodHandle get_method_by_index_impl(const constantPoolHandle& cpool,
- int method_index, Bytecodes::Code bc,
- InstanceKlass* loading_klass);
+ static Method* get_method_by_index_impl(const constantPoolHandle& cpool,
+ int method_index, Bytecodes::Code bc,
+ InstanceKlass* loading_klass);
// Helper methods
static bool check_klass_accessibility(Klass* accessing_klass, Klass* resolved_klass);
- static methodHandle lookup_method(InstanceKlass* accessor,
- Klass* holder,
- Symbol* name,
- Symbol* sig,
- Bytecodes::Code bc,
- constantTag tag);
+ static Method* lookup_method(InstanceKlass* accessor,
+ Klass* holder,
+ Symbol* name,
+ Symbol* sig,
+ Bytecodes::Code bc,
+ constantTag tag);
public:
JVMCIRuntime() {
@@ -194,9 +194,9 @@
Klass* loading_klass);
static void get_field_by_index(InstanceKlass* loading_klass, fieldDescriptor& fd,
int field_index);
- static methodHandle get_method_by_index(const constantPoolHandle& cpool,
- int method_index, Bytecodes::Code bc,
- InstanceKlass* loading_klass);
+ static Method* get_method_by_index(const constantPoolHandle& cpool,
+ int method_index, Bytecodes::Code bc,
+ InstanceKlass* loading_klass);
// converts the Klass* representing the holder of a method into a
// InstanceKlass*. This is needed since the holder of a method in
--- a/src/hotspot/share/logging/logPrefix.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/logging/logPrefix.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -57,6 +57,7 @@
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, ergo, ihop)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, ergo, refine)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, heap)) \
+ LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, heap, numa)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, heap, region)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, freelist)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, humongous)) \
--- a/src/hotspot/share/logging/logTag.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/logging/logTag.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -108,6 +108,7 @@
LOG_TAG(nestmates) \
LOG_TAG(nmethod) \
LOG_TAG(normalize) \
+ LOG_TAG(numa) \
LOG_TAG(objecttagging) \
LOG_TAG(obsolete) \
LOG_TAG(oldobject) \
--- a/src/hotspot/share/memory/dynamicArchive.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/memory/dynamicArchive.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -806,7 +806,7 @@
ik->set_class_loader_type(ClassLoader::APP_LOADER);
}
- MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(ik);
+ MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread::current(), ik);
ik->remove_unshareable_info();
assert(ik->array_klasses() == NULL, "sanity");
--- a/src/hotspot/share/memory/heapShared.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/memory/heapShared.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -38,7 +38,7 @@
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/compressedOops.inline.hpp"
-#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/safepointVerifiers.hpp"
--- a/src/hotspot/share/memory/metaspace.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/memory/metaspace.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -73,7 +73,6 @@
volatile size_t MetaspaceGC::_capacity_until_GC = 0;
uint MetaspaceGC::_shrink_factor = 0;
-bool MetaspaceGC::_should_concurrent_collect = false;
// BlockFreelist methods
--- a/src/hotspot/share/memory/metaspace.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/memory/metaspace.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -439,11 +439,6 @@
// When committed memory of all metaspaces reaches this value,
// a GC is induced and the value is increased. Size is in bytes.
static volatile size_t _capacity_until_GC;
-
- // For a CMS collection, signal that a concurrent collection should
- // be started.
- static bool _should_concurrent_collect;
-
static uint _shrink_factor;
static size_t shrink_factor() { return _shrink_factor; }
@@ -461,11 +456,6 @@
bool* can_retry = NULL);
static size_t dec_capacity_until_GC(size_t v);
- static bool should_concurrent_collect() { return _should_concurrent_collect; }
- static void set_should_concurrent_collect(bool v) {
- _should_concurrent_collect = v;
- }
-
// The amount to increase the high-water-mark (_capacity_until_GC)
static size_t delta_capacity_until_GC(size_t bytes);
--- a/src/hotspot/share/memory/metaspaceShared.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/memory/metaspaceShared.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -596,7 +596,7 @@
Universe::set_void_mirror(NULL);
}
-static void rewrite_nofast_bytecode(Method* method) {
+static void rewrite_nofast_bytecode(const methodHandle& method) {
BytecodeStream bcs(method);
while (!bcs.is_last_bytecode()) {
Bytecodes::Code opcode = bcs.next();
@@ -620,19 +620,19 @@
// [1] Rewrite all bytecodes as needed, so that the ConstMethod* will not be modified
// at run time by RewriteBytecodes/RewriteFrequentPairs
// [2] Assign a fingerprint, so one doesn't need to be assigned at run-time.
-static void rewrite_nofast_bytecodes_and_calculate_fingerprints() {
+static void rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread* thread) {
for (int i = 0; i < _global_klass_objects->length(); i++) {
Klass* k = _global_klass_objects->at(i);
if (k->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(k);
- MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(ik);
+ MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(thread, ik);
}
}
}
-void MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(InstanceKlass* ik) {
+void MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread* thread, InstanceKlass* ik) {
for (int i = 0; i < ik->methods()->length(); i++) {
- Method* m = ik->methods()->at(i);
+ methodHandle m(thread, ik->methods()->at(i));
rewrite_nofast_bytecode(m);
Fingerprinter fp(m);
// The side effect of this call sets method's fingerprint field.
@@ -1493,7 +1493,7 @@
// Ensure the ConstMethods won't be modified at run-time
tty->print("Updating ConstMethods ... ");
- rewrite_nofast_bytecodes_and_calculate_fingerprints();
+ rewrite_nofast_bytecodes_and_calculate_fingerprints(THREAD);
tty->print_cr("done. ");
// Remove all references outside the metadata
--- a/src/hotspot/share/memory/metaspaceShared.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/memory/metaspaceShared.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -307,7 +307,7 @@
static void pack_dump_space(DumpRegion* current, DumpRegion* next,
ReservedSpace* rs);
- static void rewrite_nofast_bytecodes_and_calculate_fingerprints(InstanceKlass* ik);
+ static void rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread* thread, InstanceKlass* ik);
#endif
// Allocate a block of memory from the "mc", "ro", or "rw" regions.
--- a/src/hotspot/share/memory/universe.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/memory/universe.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -395,13 +395,8 @@
// so we allocate wherever, and hope that the first collection
// moves these objects to the bottom of the old generation.
// We can allocate directly in the permanent generation, so we do.
- int size;
- if (UseConcMarkSweepGC) {
- log_warning(gc)("Using +FullGCALot with concurrent mark sweep gc will not force all objects to relocate");
- size = FullGCALotDummies;
- } else {
- size = FullGCALotDummies * 2;
- }
+ int size = FullGCALotDummies * 2;
+
objArrayOop naked_array = oopFactory::new_objArray(SystemDictionary::Object_klass(), size, CHECK);
objArrayHandle dummy_array(THREAD, naked_array);
int i = 0;
@@ -1224,10 +1219,10 @@
_fullgc_alot_dummy_array = NULL;
return false;
}
- if (!UseConcMarkSweepGC) {
- // Release dummy at bottom of old generation
- _fullgc_alot_dummy_array->obj_at_put(_fullgc_alot_dummy_next++, NULL);
- }
+
+ // Release dummy at bottom of old generation
+ _fullgc_alot_dummy_array->obj_at_put(_fullgc_alot_dummy_next++, NULL);
+
// Release dummy at bottom of permanent generation
_fullgc_alot_dummy_array->obj_at_put(_fullgc_alot_dummy_next++, NULL);
}
--- a/src/hotspot/share/oops/cpCache.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/oops/cpCache.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -401,7 +401,7 @@
return;
}
- const methodHandle adapter = call_info.resolved_method();
+ Method* adapter = call_info.resolved_method();
const Handle appendix = call_info.resolved_appendix();
const bool has_appendix = appendix.not_null();
@@ -419,7 +419,7 @@
invoke_code,
p2i(appendix()),
(has_appendix ? "" : " (unused)"),
- p2i(adapter()));
+ p2i(adapter));
adapter->print();
if (has_appendix) appendix()->print();
}
@@ -451,7 +451,7 @@
resolved_references->obj_at_put(appendix_index, appendix());
}
- release_set_f1(adapter()); // This must be the last one to set (see NOTE above)!
+ release_set_f1(adapter); // This must be the last one to set (see NOTE above)!
// The interpreter assembly code does not check byte_2,
// but it is used by is_resolved, method_if_resolved, etc.
@@ -723,10 +723,12 @@
bool* f2_used = NEW_RESOURCE_ARRAY(bool, length());
memset(f2_used, 0, sizeof(bool) * length());
+ Thread* THREAD = Thread::current();
+
// Find all the slots that we need to preserve f2
for (int i = 0; i < ik->methods()->length(); i++) {
Method* m = ik->methods()->at(i);
- RawBytecodeStream bcs(m);
+ RawBytecodeStream bcs(methodHandle(THREAD, m));
while (!bcs.is_last_bytecode()) {
Bytecodes::Code opcode = bcs.raw_next();
switch (opcode) {
--- a/src/hotspot/share/oops/fieldInfo.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/oops/fieldInfo.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -176,7 +176,7 @@
return (_shorts[low_packed_offset] & FIELDINFO_TAG_MASK) == FIELDINFO_TAG_OFFSET;
}
- Symbol* name(const constantPoolHandle& cp) const {
+ Symbol* name(ConstantPool* cp) const {
int index = name_index();
if (is_internal()) {
return lookup_symbol(index);
@@ -184,7 +184,7 @@
return cp->symbol_at(index);
}
- Symbol* signature(const constantPoolHandle& cp) const {
+ Symbol* signature(ConstantPool* cp) const {
int index = signature_index();
if (is_internal()) {
return lookup_symbol(index);
--- a/src/hotspot/share/oops/fieldStreams.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/oops/fieldStreams.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -79,34 +79,11 @@
return num_fields;
}
- FieldStreamBase(Array<u2>* fields, const constantPoolHandle& constants, int start, int limit) {
- _fields = fields;
- _constants = constants;
- _index = start;
- int num_fields = init_generic_signature_start_slot();
- if (limit < start) {
- _limit = num_fields;
- } else {
- _limit = limit;
- }
- }
+ inline FieldStreamBase(Array<u2>* fields, ConstantPool* constants, int start, int limit);
- FieldStreamBase(Array<u2>* fields, const constantPoolHandle& constants) {
- _fields = fields;
- _constants = constants;
- _index = 0;
- _limit = init_generic_signature_start_slot();
- }
-
+ inline FieldStreamBase(Array<u2>* fields, ConstantPool* constants);
public:
- FieldStreamBase(InstanceKlass* klass) {
- _fields = klass->fields();
- _constants = klass->constants();
- _index = 0;
- _limit = klass->java_fields_count();
- init_generic_signature_start_slot();
- assert(klass == field_holder(), "");
- }
+ inline FieldStreamBase(InstanceKlass* klass);
// accessors
int index() const { return _index; }
@@ -136,11 +113,11 @@
}
Symbol* name() const {
- return field()->name(_constants);
+ return field()->name(_constants());
}
Symbol* signature() const {
- return field()->signature(_constants);
+ return field()->signature(_constants());
}
Symbol* generic_signature() const {
@@ -242,7 +219,7 @@
class AllFieldStream : public FieldStreamBase {
public:
- AllFieldStream(Array<u2>* fields, const constantPoolHandle& constants): FieldStreamBase(fields, constants) {}
+ AllFieldStream(Array<u2>* fields, ConstantPool* constants): FieldStreamBase(fields, constants) {}
AllFieldStream(InstanceKlass* k): FieldStreamBase(k->fields(), k->constants()) {}
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/oops/fieldStreams.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019, 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_OOPS_FIELDSTREAMS_INLINE_HPP
+#define SHARE_OOPS_FIELDSTREAMS_INLINE_HPP
+
+#include "oops/fieldStreams.hpp"
+#include "runtime/thread.inline.hpp"
+
+FieldStreamBase::FieldStreamBase(Array<u2>* fields, ConstantPool* constants, int start, int limit) : _fields(fields),
+ _constants(constantPoolHandle(Thread::current(), constants)), _index(start) {
+ _index = start;
+ int num_fields = init_generic_signature_start_slot();
+ if (limit < start) {
+ _limit = num_fields;
+ } else {
+ _limit = limit;
+ }
+}
+
+FieldStreamBase::FieldStreamBase(Array<u2>* fields, ConstantPool* constants) : _fields(fields),
+ _constants(constantPoolHandle(Thread::current(), constants)), _index(0) {
+ _limit = init_generic_signature_start_slot();
+}
+
+FieldStreamBase::FieldStreamBase(InstanceKlass* klass) : _fields(klass->fields()),
+ _constants(constantPoolHandle(Thread::current(), klass->constants())), _index(0),
+ _limit(klass->java_fields_count()) {
+ init_generic_signature_start_slot();
+ assert(klass == field_holder(), "");
+}
+
+#endif // SHARE_OOPS_FIELDSTREAMS_INLINE_HPP
--- a/src/hotspot/share/oops/generateOopMap.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/oops/generateOopMap.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1875,7 +1875,7 @@
void GenerateOopMap::do_ldc(int bci) {
- Bytecode_loadconstant ldc(method(), bci);
+ Bytecode_loadconstant ldc(methodHandle(Thread::current(), method()), bci);
ConstantPool* cp = method()->constants();
constantTag tag = cp->tag_at(ldc.pool_index()); // idx is index in resolved_references
BasicType bt = ldc.result_type();
--- a/src/hotspot/share/oops/instanceKlass.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/oops/instanceKlass.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -54,7 +54,7 @@
#include "memory/oopFactory.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
-#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "oops/constantPool.hpp"
#include "oops/instanceClassLoaderKlass.hpp"
#include "oops/instanceKlass.inline.hpp"
--- a/src/hotspot/share/oops/klassVtable.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/oops/klassVtable.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -300,7 +300,7 @@
Symbol* signature = target_method()->signature();
assert(super_method->name() == name && super_method->signature() == signature, "vtable entry name/sig mismatch");
#endif
- if (supersuperklass->is_override(super_method, target_loader, target_classname, THREAD)) {
+ if (supersuperklass->is_override(methodHandle(THREAD, super_method), target_loader, target_classname, THREAD)) {
if (log_develop_is_enabled(Trace, vtables)) {
ResourceMark rm(THREAD);
LogTarget(Trace, vtables) lt;
@@ -461,7 +461,7 @@
// private methods are also never overridden
if (!super_method->is_private() &&
(is_default
- || ((super_klass->is_override(super_method, target_loader, target_classname, THREAD))
+ || ((super_klass->is_override(methodHandle(THREAD, super_method), target_loader, target_classname, THREAD))
|| ((klass->major_version() >= VTABLE_TRANSITIVE_OVERRIDE_VERSION)
&& ((super_klass = find_transitive_override(super_klass,
target_method, i, target_loader,
@@ -650,7 +650,7 @@
// methods that have less accessibility
if ((!super_method->is_static()) &&
(!super_method->is_private())) {
- if (superk->is_override(super_method, classloader, classname, THREAD)) {
+ if (superk->is_override(methodHandle(THREAD, super_method), classloader, classname, THREAD)) {
return false;
// else keep looking for transitive overrides
}
@@ -1197,7 +1197,7 @@
int ime_count = method_count_for_interface(interf);
for (int i = 0; i < nof_methods; i++) {
Method* m = methods->at(i);
- methodHandle target;
+ Method* target = NULL;
if (m->has_itable_index()) {
// This search must match the runtime resolution, i.e. selection search for invokeinterface
// to correctly enforce loader constraints for interface method inheritance.
@@ -1222,6 +1222,7 @@
// if checkconstraints requested
if (checkconstraints) {
Handle method_holder_loader (THREAD, target->method_holder()->class_loader());
+ InstanceKlass* method_holder = target->method_holder();
if (method_holder_loader() != interface_loader()) {
ResourceMark rm(THREAD);
Symbol* failed_type_symbol =
@@ -1240,12 +1241,12 @@
" different Class objects for the type %s used in the signature (%s; %s)",
interf->class_loader_data()->loader_name_and_id(),
interf->external_name(),
- target()->method_holder()->class_loader_data()->loader_name_and_id(),
- target()->method_holder()->external_kind(),
- target()->method_holder()->external_name(),
+ method_holder->class_loader_data()->loader_name_and_id(),
+ method_holder->external_kind(),
+ method_holder->external_name(),
failed_type_symbol->as_klass_external_name(),
interf->class_in_module_of_loader(false, true),
- target()->method_holder()->class_in_module_of_loader(false, true));
+ method_holder->class_in_module_of_loader(false, true));
THROW_MSG(vmSymbols::java_lang_LinkageError(), ss.as_string());
}
}
@@ -1254,18 +1255,18 @@
// ime may have moved during GC so recalculate address
int ime_num = m->itable_index();
assert(ime_num < ime_count, "oob");
- itableOffsetEntry::method_entry(_klass, method_table_offset)[ime_num].initialize(target());
+ itableOffsetEntry::method_entry(_klass, method_table_offset)[ime_num].initialize(target);
if (log_develop_is_enabled(Trace, itables)) {
ResourceMark rm(THREAD);
- if (target() != NULL) {
+ if (target != NULL) {
LogTarget(Trace, itables) lt;
LogStream ls(lt);
- char* sig = target()->name_and_sig_as_C_string();
+ char* sig = target->name_and_sig_as_C_string();
ls.print("interface: %s, ime_num: %d, target: %s, method_holder: %s ",
interf->internal_name(), ime_num, sig,
- target()->method_holder()->internal_name());
+ target->method_holder()->internal_name());
ls.print("target_method flags: ");
- target()->print_linkage_flags(&ls);
+ target->print_linkage_flags(&ls);
ls.cr();
}
}
--- a/src/hotspot/share/oops/markWord.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/oops/markWord.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -38,20 +38,11 @@
// --------
// hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object)
-// size:32 ------------------------------------------>| (CMS free block)
-// PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
// 64 bits:
// --------
-// unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)
-// JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object)
-// PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
-// size:64 ----------------------------------------------------->| (CMS free block)
-//
-// unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object)
-// JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object)
-// narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
-// unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
+// unused:25 hash:31 -->| unused_gap:1 age:4 biased_lock:1 lock:2 (normal object)
+// JavaThread*:54 epoch:2 unused_gap:1 age:4 biased_lock:1 lock:2 (biased object)
//
// - hash contains the identity hash value: largest value is
// 31 bits, see os::random(). Also, 64-bit vm's require
@@ -82,7 +73,7 @@
// performed. The runtime system aligns all JavaThread* pointers to
// a very large value (currently 128 bytes (32bVM) or 256 bytes (64bVM))
// to make room for the age bits & the epoch bits (used in support of
-// biased locking), and for the CMS "freeness" bit in the 64bVM (+COOPs).
+// biased locking).
//
// [JavaThread* | epoch | age | 1 | 01] lock is biased toward given thread
// [0 | epoch | age | 1 | 01] lock is anonymously biased
@@ -136,7 +127,7 @@
static const int biased_lock_bits = 1;
static const int max_hash_bits = BitsPerWord - age_bits - lock_bits - biased_lock_bits;
static const int hash_bits = max_hash_bits > 31 ? 31 : max_hash_bits;
- static const int cms_bits = LP64_ONLY(1) NOT_LP64(0);
+ static const int unused_gap_bits = LP64_ONLY(1) NOT_LP64(0);
static const int epoch_bits = 2;
// The biased locking code currently requires that the age bits be
@@ -144,8 +135,8 @@
static const int lock_shift = 0;
static const int biased_lock_shift = lock_bits;
static const int age_shift = lock_bits + biased_lock_bits;
- static const int cms_shift = age_shift + age_bits;
- static const int hash_shift = cms_shift + cms_bits;
+ static const int unused_gap_shift = age_shift + age_bits;
+ static const int hash_shift = unused_gap_shift + unused_gap_bits;
static const int epoch_shift = hash_shift;
static const uintptr_t lock_mask = right_n_bits(lock_bits);
@@ -157,8 +148,6 @@
static const uintptr_t age_mask_in_place = age_mask << age_shift;
static const uintptr_t epoch_mask = right_n_bits(epoch_bits);
static const uintptr_t epoch_mask_in_place = epoch_mask << epoch_shift;
- static const uintptr_t cms_mask = right_n_bits(cms_bits);
- static const uintptr_t cms_mask_in_place = cms_mask << cms_shift;
static const uintptr_t hash_mask = right_n_bits(hash_bits);
static const uintptr_t hash_mask_in_place = hash_mask << hash_shift;
@@ -269,12 +258,6 @@
template <typename KlassProxy>
inline bool must_be_preserved_for_promotion_failure(KlassProxy klass) const;
- // Should this header be preserved during a scavenge where CMS is
- // the old generation?
- // (This is basically the same body as must_be_preserved_for_promotion_failure(),
- // but takes the Klass* as argument instead)
- inline bool must_be_preserved_for_cms_scavenge(Klass* klass_of_obj_containing_mark) const;
-
// WARNING: The following routines are used EXCLUSIVELY by
// synchronization functions. They are not really gc safe.
// They must get updated if markWord layout get changed.
@@ -375,42 +358,6 @@
// Recover address of oop from encoded form used in mark
inline void* decode_pointer() { if (UseBiasedLocking && has_bias_pattern()) return NULL; return (void*)clear_lock_bits().value(); }
-
- // These markWords indicate cms free chunk blocks and not objects.
- // In 64 bit, the markWord is set to distinguish them from oops.
- // These are defined in 32 bit mode for vmStructs.
- const static uintptr_t cms_free_chunk_pattern = 0x1;
-
- // Constants for the size field.
- enum { size_shift = cms_shift + cms_bits,
- size_bits = 35 // need for compressed oops 32G
- };
- // These values are too big for Win64
- const static uintptr_t size_mask = LP64_ONLY(right_n_bits(size_bits))
- NOT_LP64(0);
- const static uintptr_t size_mask_in_place =
- (address_word)size_mask << size_shift;
-
-#ifdef _LP64
- static markWord cms_free_prototype() {
- return markWord((prototype().value() & ~cms_mask_in_place) |
- ((cms_free_chunk_pattern & cms_mask) << cms_shift));
- }
- uintptr_t cms_encoding() const {
- return mask_bits(value() >> cms_shift, cms_mask);
- }
- bool is_cms_free_chunk() const {
- return is_neutral() &&
- (cms_encoding() & cms_free_chunk_pattern) == cms_free_chunk_pattern;
- }
-
- size_t get_size() const { return (size_t)(value() >> size_shift); }
- static markWord set_size_and_free(size_t size) {
- assert((size & ~size_mask) == 0, "shouldn't overflow size field");
- return markWord((cms_free_prototype().value() & ~size_mask_in_place) |
- ((size & size_mask) << size_shift));
- }
-#endif // _LP64
};
// Support atomic operations.
--- a/src/hotspot/share/oops/markWord.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/oops/markWord.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -68,11 +68,6 @@
return (!is_unlocked() || !has_no_hash());
}
-// Same as must_be_preserved_for_promotion_failure().
-inline bool markWord::must_be_preserved_for_cms_scavenge(Klass* klass_of_obj_containing_mark) const {
- return must_be_preserved_for_promotion_failure(klass_of_obj_containing_mark);
-}
-
inline markWord markWord::prototype_for_klass(const Klass* klass) {
markWord prototype_header = klass->prototype_header();
assert(prototype_header == prototype() || prototype_header.has_bias_pattern(), "corrupt prototype header");
--- a/src/hotspot/share/oops/method.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/oops/method.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -546,7 +546,7 @@
return NULL;
}
- methodHandle mh(m);
+ methodHandle mh(THREAD, m);
MethodCounters* counters = MethodCounters::allocate(mh, THREAD);
if (HAS_PENDING_EXCEPTION) {
CompileBroker::log_metaspace_failure();
@@ -628,7 +628,7 @@
bool Method::compute_has_loops_flag() {
- BytecodeStream bcs(this);
+ BytecodeStream bcs(methodHandle(Thread::current(), this));
Bytecodes::Code bc;
while ((bc = bcs.next()) >= 0) {
@@ -986,7 +986,7 @@
set_not_c2_compilable();
}
CompilationPolicy::policy()->disable_compilation(this);
- assert(!CompilationPolicy::can_be_compiled(this, comp_level), "sanity check");
+ assert(!CompilationPolicy::can_be_compiled(methodHandle(Thread::current(), this), comp_level), "sanity check");
}
bool Method::is_not_osr_compilable(int comp_level) const {
@@ -1013,7 +1013,7 @@
set_not_c2_osr_compilable();
}
CompilationPolicy::policy()->disable_compilation(this);
- assert(!CompilationPolicy::can_be_osr_compiled(this, comp_level), "sanity check");
+ assert(!CompilationPolicy::can_be_osr_compiled(methodHandle(Thread::current(), this), comp_level), "sanity check");
}
// Revert to using the interpreter and clear out the nmethod
@@ -1058,7 +1058,7 @@
Arguments::assert_is_dumping_archive();
// Set the values to what they should be at run time. Note that
// this Method can no longer be executed during dump time.
- _i2i_entry = Interpreter::entry_for_cds_method(this);
+ _i2i_entry = Interpreter::entry_for_cds_method(methodHandle(Thread::current(), this));
_from_interpreted_entry = _i2i_entry;
if (DynamicDumpSharedSpaces) {
@@ -1570,14 +1570,14 @@
if (m->has_stackmap_table()) {
int code_attribute_length = m->stackmap_data()->length();
Array<u1>* stackmap_data =
- MetadataFactory::new_array<u1>(loader_data, code_attribute_length, 0, CHECK_NULL);
+ MetadataFactory::new_array<u1>(loader_data, code_attribute_length, 0, CHECK_(methodHandle()));
memcpy((void*)stackmap_data->adr_at(0),
(void*)m->stackmap_data()->adr_at(0), code_attribute_length);
newm->set_stackmap_data(stackmap_data);
}
// copy annotations over to new method
- newcm->copy_annotations_from(loader_data, cm, CHECK_NULL);
+ newcm->copy_annotations_from(loader_data, cm, CHECK_(methodHandle()));
return newm;
}
@@ -2218,6 +2218,11 @@
}
}
+jmethodID Method::jmethod_id() {
+ methodHandle mh(Thread::current(), this);
+ return method_holder()->get_jmethod_id(mh);
+}
+
// Mark a jmethodID as free. This is called when there is a data race in
// InstanceKlass while creating the jmethodID cache.
void Method::destroy_jmethod_id(ClassLoaderData* loader_data, jmethodID m) {
--- a/src/hotspot/share/oops/method.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/oops/method.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -856,7 +856,7 @@
static void print_jmethod_ids(const ClassLoaderData* loader_data, outputStream* out) PRODUCT_RETURN;
// Get this method's jmethodID -- allocate if it doesn't exist
- jmethodID jmethod_id() { return method_holder()->get_jmethod_id(this); }
+ jmethodID jmethod_id();
// Lookup the jmethodID for this method. Return NULL if not found.
// NOTE that this function can be called from a signal handler
--- a/src/hotspot/share/oops/methodData.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/oops/methodData.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -668,7 +668,7 @@
}
int ParametersTypeData::compute_cell_count(Method* m) {
- if (!MethodData::profile_parameters_for_method(m)) {
+ if (!MethodData::profile_parameters_for_method(methodHandle(Thread::current(), m))) {
return 0;
}
int max = TypeProfileParmsLimit == -1 ? INT_MAX : TypeProfileParmsLimit;
@@ -709,7 +709,7 @@
int size = MethodData::compute_allocation_size_in_words(method);
return new (loader_data, size, MetaspaceObj::MethodDataType, THREAD)
- MethodData(method(), size, THREAD);
+ MethodData(method, size, THREAD);
}
int MethodData::bytecode_cell_count(Bytecodes::Code code) {
@@ -1220,8 +1220,9 @@
}
void MethodData::initialize() {
+ Thread* thread = Thread::current();
NoSafepointVerifier no_safepoint; // init function atomic wrt GC
- ResourceMark rm;
+ ResourceMark rm(thread);
init();
set_creation_mileage(mileage_of(method()));
@@ -1231,7 +1232,7 @@
int data_size = 0;
int empty_bc_count = 0; // number of bytecodes lacking data
_data[0] = 0; // apparently not set below.
- BytecodeStream stream(method());
+ BytecodeStream stream(methodHandle(thread, method()));
Bytecodes::Code c;
bool needs_speculative_traps = false;
while ((c = stream.next()) >= 0) {
@@ -1284,7 +1285,7 @@
post_initialize(&stream);
- assert(object_size == compute_allocation_size_in_bytes(methodHandle(_method)), "MethodData: computed size != initialized size");
+ assert(object_size == compute_allocation_size_in_bytes(methodHandle(thread, _method)), "MethodData: computed size != initialized size");
set_size(object_size);
}
@@ -1296,7 +1297,8 @@
// Set per-method invoke- and backedge mask.
double scale = 1.0;
- CompilerOracle::has_option_value(_method, "CompileThresholdScaling", scale);
+ methodHandle mh(Thread::current(), _method);
+ CompilerOracle::has_option_value(mh, "CompileThresholdScaling", scale);
_invoke_mask = right_n_bits(CompilerConfig::scaled_freq_log(Tier0InvokeNotifyFreqLog, scale)) << InvocationCounter::count_shift;
_backedge_mask = right_n_bits(CompilerConfig::scaled_freq_log(Tier0BackedgeNotifyFreqLog, scale)) << InvocationCounter::count_shift;
@@ -1313,8 +1315,8 @@
#if INCLUDE_RTM_OPT
_rtm_state = NoRTM; // No RTM lock eliding by default
if (UseRTMLocking &&
- !CompilerOracle::has_option_string(_method, "NoRTMLockEliding")) {
- if (CompilerOracle::has_option_string(_method, "UseRTMLockEliding") || !UseRTMDeopt) {
+ !CompilerOracle::has_option_string(mh, "NoRTMLockEliding")) {
+ if (CompilerOracle::has_option_string(mh, "UseRTMLockEliding") || !UseRTMDeopt) {
// Generate RTM lock eliding code without abort ratio calculation code.
_rtm_state = UseRTM;
} else if (UseRTMDeopt) {
--- a/src/hotspot/share/oops/oop.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/oops/oop.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -45,7 +45,6 @@
class ScanClosure;
class FastScanClosure;
class FilteringClosure;
-class CMSIsAliveClosure;
class PSPromotionManager;
class ParCompactionManager;
@@ -93,9 +92,6 @@
inline int klass_gap() const;
inline void set_klass_gap(int z);
static inline void set_klass_gap(HeapWord* mem, int z);
- // For when the klass pointer is being used as a linked list "next" field.
- inline void set_klass_to_list_ptr(oop k);
- inline oop list_ptr_from_klass();
// size of object header, aligned to platform wordSize
static int header_size() { return sizeof(oopDesc)/HeapWordSize; }
--- a/src/hotspot/share/oops/oop.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/oops/oop.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -179,26 +179,6 @@
set_klass_gap((HeapWord*)this, v);
}
-void oopDesc::set_klass_to_list_ptr(oop k) {
- // This is only to be used during GC, for from-space objects, so no
- // barrier is needed.
- if (UseCompressedClassPointers) {
- _metadata._compressed_klass = (narrowKlass)CompressedOops::encode(k); // may be null (parnew overflow handling)
- } else {
- _metadata._klass = (Klass*)(address)k;
- }
-}
-
-oop oopDesc::list_ptr_from_klass() {
- // This is only to be used during GC, for from-space objects.
- if (UseCompressedClassPointers) {
- return CompressedOops::decode((narrowOop)_metadata._compressed_klass);
- } else {
- // Special case for GC
- return (oop)(address)_metadata._klass;
- }
-}
-
bool oopDesc::is_a(Klass* k) const {
return klass()->is_subtype_of(k);
}
@@ -244,25 +224,13 @@
// skipping the intermediate round to HeapWordSize.
s = (int)(align_up(size_in_bytes, MinObjAlignmentInBytes) / HeapWordSize);
- // ParNew (used by CMS), UseParallelGC and UseG1GC can change the length field
+ // UseParallelGC and UseG1GC can change the length field
// of an "old copy" of an object array in the young gen so it indicates
// the grey portion of an already copied array. This will cause the first
// disjunct below to fail if the two comparands are computed across such
// a concurrent change.
- // ParNew also runs with promotion labs (which look like int
- // filler arrays) which are subject to changing their declared size
- // when finally retiring a PLAB; this also can cause the first disjunct
- // to fail for another worker thread that is concurrently walking the block
- // offset table. Both these invariant failures are benign for their
- // current uses; we relax the assertion checking to cover these two cases below:
- // is_objArray() && is_forwarded() // covers first scenario above
- // || is_typeArray() // covers second scenario above
- // If and when UseParallelGC uses the same obj array oop stealing/chunking
- // technique, we will need to suitably modify the assertion.
assert((s == klass->oop_size(this)) ||
- (Universe::heap()->is_gc_active() &&
- ((is_typeArray() && UseConcMarkSweepGC) ||
- (is_objArray() && is_forwarded() && (UseConcMarkSweepGC || UseParallelGC || UseG1GC)))),
+ (Universe::heap()->is_gc_active() && is_objArray() && is_forwarded() && (UseParallelGC || UseG1GC)),
"wrong array object size");
} else {
// Must be zero, so bite the bullet and take the virtual call.
--- a/src/hotspot/share/opto/lcm.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/opto/lcm.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -958,7 +958,7 @@
ready_cnt.at_put(n->_idx, local); // Count em up
#ifdef ASSERT
- if( UseConcMarkSweepGC || UseG1GC ) {
+ if (UseG1GC) {
if( n->is_Mach() && n->as_Mach()->ideal_Opcode() == Op_StoreCM ) {
// Check the precedence edges
for (uint prec = n->req(); prec < n->len(); prec++) {
--- a/src/hotspot/share/opto/macro.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/opto/macro.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1399,7 +1399,7 @@
// other threads.
// Other threads include java threads and JVM internal threads
// (for example concurrent GC threads). Current concurrent GC
- // implementation: CMS and G1 will not scan newly created object,
+ // implementation: G1 will not scan newly created object,
// so it's safe to skip storestore barrier when allocation does
// not escape.
if (!alloc->does_not_escape_thread() &&
--- a/src/hotspot/share/opto/phaseX.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/opto/phaseX.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -891,7 +891,10 @@
}
bool PhaseGVN::is_dominator_helper(Node *d, Node *n, bool linear_only) {
- if (d->is_top() || n->is_top()) {
+ if (d->is_top() || (d->is_Proj() && d->in(0)->is_top())) {
+ return false;
+ }
+ if (n->is_top() || (n->is_Proj() && n->in(0)->is_top())) {
return false;
}
assert(d->is_CFG() && n->is_CFG(), "must have CFG nodes");
--- a/src/hotspot/share/prims/jvm.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/prims/jvm.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -48,7 +48,7 @@
#include "memory/universe.hpp"
#include "oops/access.inline.hpp"
#include "oops/constantPool.hpp"
-#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/method.hpp"
#include "oops/objArrayKlass.hpp"
@@ -1615,7 +1615,8 @@
for (int i = 0; i < num_params; i++) {
MethodParametersElement* params = mh->method_parameters_start();
int index = params[i].name_cp_index;
- bounds_check(mh->constants(), index, CHECK_NULL);
+ constantPoolHandle cp(THREAD, mh->constants());
+ bounds_check(cp, index, CHECK_NULL);
if (0 != index && !mh->constants()->tag_at(index).is_utf8()) {
THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(),
--- a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -26,7 +26,7 @@
#include "classfile/symbolTable.hpp"
#include "interpreter/bytecodeStream.hpp"
#include "memory/universe.hpp"
-#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "prims/jvmtiClassFileReconstituter.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/signature.hpp"
--- a/src/hotspot/share/prims/jvmtiCodeBlobEvents.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/prims/jvmtiCodeBlobEvents.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -253,7 +253,7 @@
// Generate line numbers using PcDesc and ScopeDesc info
- methodHandle mh(nm->method());
+ methodHandle mh(Thread::current(), nm->method());
if (!mh->is_native()) {
PcDesc *pcd;
--- a/src/hotspot/share/prims/jvmtiEnv.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/prims/jvmtiEnv.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -3152,7 +3152,7 @@
NULL_CHECK(method_oop, JVMTI_ERROR_INVALID_METHODID);
HandleMark hm;
- methodHandle method(method_oop);
+ methodHandle method(Thread::current(), method_oop);
jint size = (jint)method->code_size();
jvmtiError err = allocate(size, bytecodes_ptr);
if (err != JVMTI_ERROR_NONE) {
--- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -41,7 +41,7 @@
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/constantPool.hpp"
-#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "oops/klassVtable.hpp"
#include "oops/oop.inline.hpp"
#include "prims/jvmtiImpl.hpp"
@@ -3492,12 +3492,11 @@
// cached references to old methods so it doesn't need to be
// updated. We can simply start with the previous version(s) in
// that case.
- constantPoolHandle other_cp;
ConstantPoolCache* cp_cache;
if (!ik->is_being_redefined()) {
// this klass' constant pool cache may need adjustment
- other_cp = constantPoolHandle(ik->constants());
+ ConstantPool* other_cp = ik->constants();
cp_cache = other_cp->cache();
if (cp_cache != NULL) {
cp_cache->adjust_method_entries(&trace_name_printed);
@@ -3516,13 +3515,13 @@
}
}
-void VM_RedefineClasses::update_jmethod_ids() {
+void VM_RedefineClasses::update_jmethod_ids(Thread* thread) {
for (int j = 0; j < _matching_methods_length; ++j) {
Method* old_method = _matching_old_methods[j];
jmethodID jmid = old_method->find_jmethod_id_or_null();
if (jmid != NULL) {
// There is a jmethodID, change it to point to the new method
- methodHandle new_method_h(_matching_new_methods[j]);
+ methodHandle new_method_h(thread, _matching_new_methods[j]);
Method::change_method_associated_with_jmethod_id(jmid, new_method_h());
assert(Method::resolve_jmethod_id(jmid) == _matching_new_methods[j],
"should be replaced");
@@ -3961,7 +3960,7 @@
_new_methods = scratch_class->methods();
_the_class = the_class;
compute_added_deleted_matching_methods();
- update_jmethod_ids();
+ update_jmethod_ids(THREAD);
_any_class_has_resolved_methods = the_class->has_resolved_methods() || _any_class_has_resolved_methods;
--- a/src/hotspot/share/prims/jvmtiRedefineClasses.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/prims/jvmtiRedefineClasses.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -407,7 +407,7 @@
void compute_added_deleted_matching_methods();
// Change jmethodIDs to point to the new methods
- void update_jmethod_ids();
+ void update_jmethod_ids(Thread* thread);
// In addition to marking methods as old and/or obsolete, this routine
// counts the number of methods that are EMCP (Equivalent Module Constant Pool).
--- a/src/hotspot/share/prims/methodComparator.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/prims/methodComparator.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -52,8 +52,9 @@
_old_cp = old_method->constants();
_new_cp = new_method->constants();
- BytecodeStream s_old(old_method);
- BytecodeStream s_new(new_method);
+ Thread* THREAD = Thread::current();
+ BytecodeStream s_old(methodHandle(THREAD, old_method));
+ BytecodeStream s_new(methodHandle(THREAD, new_method));
_s_old = &s_old;
_s_new = &s_new;
Bytecodes::Code c_old, c_new;
--- a/src/hotspot/share/prims/methodHandles.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/prims/methodHandles.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -222,7 +222,7 @@
oop MethodHandles::init_method_MemberName(Handle mname, CallInfo& info) {
assert(info.resolved_appendix().is_null(), "only normal methods here");
- methodHandle m = info.resolved_method();
+ methodHandle m(Thread::current(), info.resolved_method());
assert(m.not_null(), "null method handle");
InstanceKlass* m_klass = m->method_holder();
assert(m_klass != NULL, "null holder for method handle");
--- a/src/hotspot/share/prims/nativeLookup.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/prims/nativeLookup.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -380,7 +380,7 @@
if (wrapper_method != NULL && !wrapper_method->is_native()) {
// we found a wrapper method, use its native entry
method->set_is_prefixed_native();
- return lookup_entry(wrapper_method, in_base_library, THREAD);
+ return lookup_entry(methodHandle(THREAD, wrapper_method), in_base_library, THREAD);
}
}
}
--- a/src/hotspot/share/prims/stackwalk.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/prims/stackwalk.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -156,7 +156,7 @@
method->external_name()));
}
// fill in StackFrameInfo and initialize MemberName
- stream.fill_frame(index, frames_array, method, CHECK_0);
+ stream.fill_frame(index, frames_array, methodHandle(THREAD, method), CHECK_0);
if (++frames_decoded >= max_nframes) break;
}
return frames_decoded;
--- a/src/hotspot/share/prims/unsafe.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/prims/unsafe.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -32,7 +32,7 @@
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
#include "oops/access.inline.hpp"
-#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "oops/objArrayOop.inline.hpp"
#include "oops/oop.inline.hpp"
#include "oops/typeArrayOop.inline.hpp"
--- a/src/hotspot/share/prims/whitebox.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/prims/whitebox.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -619,6 +619,29 @@
THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1AuxiliaryMemoryUsage: G1 GC is not enabled");
WB_END
+WB_ENTRY(jint, WB_G1ActiveMemoryNodeCount(JNIEnv* env, jobject o))
+ if (UseG1GC) {
+ G1NUMA* numa = G1NUMA::numa();
+ return (jint)numa->num_active_nodes();
+ }
+ THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1ActiveMemoryNodeCount: G1 GC is not enabled");
+WB_END
+
+WB_ENTRY(jintArray, WB_G1MemoryNodeIds(JNIEnv* env, jobject o))
+ if (UseG1GC) {
+ G1NUMA* numa = G1NUMA::numa();
+ int num_node_ids = (int)numa->num_active_nodes();
+ const int* node_ids = numa->node_ids();
+
+ typeArrayOop result = oopFactory::new_intArray(num_node_ids, CHECK_NULL);
+ for (int i = 0; i < num_node_ids; i++) {
+ result->int_at_put(i, (jint)node_ids[i]);
+ }
+ return (jintArray) JNIHandles::make_local(env, result);
+ }
+ THROW_MSG_NULL(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1MemoryNodeIds: G1 GC is not enabled");
+WB_END
+
class OldRegionsLivenessClosure: public HeapRegionClosure {
private:
@@ -1726,10 +1749,6 @@
return (jlong) MetaspaceGC::capacity_until_GC();
WB_END
-WB_ENTRY(jboolean, WB_MetaspaceShouldConcurrentCollect(JNIEnv* env, jobject wb))
- return MetaspaceGC::should_concurrent_collect();
-WB_END
-
WB_ENTRY(jlong, WB_MetaspaceReserveAlignment(JNIEnv* env, jobject wb))
return (jlong)Metaspace::reserve_alignment();
WB_END
@@ -2199,6 +2218,8 @@
{CC"g1StartConcMarkCycle", CC"()Z", (void*)&WB_G1StartMarkCycle },
{CC"g1AuxiliaryMemoryUsage", CC"()Ljava/lang/management/MemoryUsage;",
(void*)&WB_G1AuxiliaryMemoryUsage },
+ {CC"g1ActiveMemoryNodeCount", CC"()I", (void*)&WB_G1ActiveMemoryNodeCount },
+ {CC"g1MemoryNodeIds", CC"()[I", (void*)&WB_G1MemoryNodeIds },
{CC"g1GetMixedGCInfo", CC"(I)[J", (void*)&WB_G1GetMixedGCInfo },
#endif // INCLUDE_G1GC
#if INCLUDE_G1GC || INCLUDE_PARALLELGC
@@ -2309,7 +2330,6 @@
CC"(Ljava/lang/ClassLoader;JJ)V", (void*)&WB_FreeMetaspace },
{CC"incMetaspaceCapacityUntilGC", CC"(J)J", (void*)&WB_IncMetaspaceCapacityUntilGC },
{CC"metaspaceCapacityUntilGC", CC"()J", (void*)&WB_MetaspaceCapacityUntilGC },
- {CC"metaspaceShouldConcurrentCollect", CC"()Z", (void*)&WB_MetaspaceShouldConcurrentCollect },
{CC"metaspaceReserveAlignment", CC"()J", (void*)&WB_MetaspaceReserveAlignment },
{CC"getCPUFeatures", CC"()Ljava/lang/String;", (void*)&WB_GetCPUFeatures },
{CC"getNMethod0", CC"(Ljava/lang/reflect/Executable;Z)[Ljava/lang/Object;",
--- a/src/hotspot/share/runtime/arguments.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/arguments.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -515,7 +515,6 @@
// -------------- Deprecated Flags --------------
// --- Non-alias flags - sorted by obsolete_in then expired_in:
{ "MaxGCMinorPauseMillis", JDK_Version::jdk(8), JDK_Version::undefined(), JDK_Version::undefined() },
- { "UseConcMarkSweepGC", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::undefined() },
{ "MaxRAMFraction", JDK_Version::jdk(10), JDK_Version::undefined(), JDK_Version::undefined() },
{ "MinRAMFraction", JDK_Version::jdk(10), JDK_Version::undefined(), JDK_Version::undefined() },
{ "InitialRAMFraction", JDK_Version::jdk(10), JDK_Version::undefined(), JDK_Version::undefined() },
@@ -544,6 +543,81 @@
{ "CompilationPolicyChoice", JDK_Version::jdk(13), JDK_Version::jdk(14), JDK_Version::jdk(15) },
{ "TraceNMethodInstalls", JDK_Version::jdk(13), JDK_Version::jdk(14), JDK_Version::jdk(15) },
{ "FailOverToOldVerifier", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "UseConcMarkSweepGC", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSAbortSemantics", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSAbortablePrecleanMinWorkPerIteration", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSBitMapYieldQuantum", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSBootstrapOccupancy", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSClassUnloadingEnabled", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSClassUnloadingMaxInterval", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSCleanOnEnter", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSConcMarkMultiple", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSConcurrentMTEnabled", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSCoordinatorYieldSleepCount", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSEdenChunksRecordAlways", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSExpAvgFactor", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSExtrapolateSweep", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSIncrementalSafetyFactor", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSIndexedFreeListReplenish", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSInitiatingOccupancyFraction", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSIsTooFullPercentage", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSLargeCoalSurplusPercent", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSLargeSplitSurplusPercent", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSLoopWarn", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSMaxAbortablePrecleanLoops", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSMaxAbortablePrecleanTime", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSOldPLABMax", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSOldPLABMin", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSOldPLABNumRefills", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSOldPLABReactivityFactor", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSOldPLABResizeQuicker", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSOldPLABToleranceFactor", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSPLABRecordAlways", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSParallelInitialMarkEnabled", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSParallelRemarkEnabled", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSParallelSurvivorRemarkEnabled", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSPrecleanDenominator", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSPrecleanIter", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSPrecleanNumerator", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSPrecleanRefLists1", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSPrecleanRefLists2", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSPrecleanSurvivors1", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSPrecleanSurvivors2", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSPrecleanThreshold", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSPrecleaningEnabled", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSPrintChunksInDump", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSPrintObjectsInDump", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSRemarkVerifyVariant", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSReplenishIntermediate", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSRescanMultiple", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSSamplingGrain", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSScavengeBeforeRemark", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSScheduleRemarkEdenPenetration", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSScheduleRemarkEdenSizeThreshold", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSScheduleRemarkSamplingRatio", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSSmallCoalSurplusPercent", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSSmallSplitSurplusPercent", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSSplitIndexedFreeListBlocks", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSTriggerRatio", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSWorkQueueDrainThreshold", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSYield", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSYieldSleepCount", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMSYoungGenPerWorker", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMS_FLSPadding", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMS_FLSWeight", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMS_SweepPadding", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMS_SweepTimerThresholdMillis", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "CMS_SweepWeight", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "FLSAlwaysCoalesceLarge", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "FLSCoalescePolicy", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "FLSLargestBlockCoalesceProximity", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "OldPLABWeight", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "ParGCDesiredObjsFromOverflowList", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "ParGCTrimOverflow", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "ParGCUseLocalOverflow", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "ResizeOldPLAB", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "UseCMSBestFit", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
+ { "UseCMSInitiatingOccupancyOnly", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) },
{ "BindGCTaskThreadsToCPUs", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(16) },
{ "UseGCTaskAffinity", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(16) },
@@ -2547,16 +2621,10 @@
}
// -Xconcgc
} else if (match_option(option, "-Xconcgc")) {
- if (FLAG_SET_CMDLINE(UseConcMarkSweepGC, true) != JVMFlag::SUCCESS) {
- return JNI_EINVAL;
- }
- handle_extra_cms_flags("-Xconcgc uses UseConcMarkSweepGC");
+ warning("-Xconcgc uses UseConcMarkSweepGC; support was removed for both options in 14.0");
// -Xnoconcgc
} else if (match_option(option, "-Xnoconcgc")) {
- if (FLAG_SET_CMDLINE(UseConcMarkSweepGC, false) != JVMFlag::SUCCESS) {
- return JNI_EINVAL;
- }
- handle_extra_cms_flags("-Xnoconcgc uses UseConcMarkSweepGC");
+ warning("-Xnoconcgc uses UseConcMarkSweepGC; support was removed for both options in 14.0");
// -Xbatch
} else if (match_option(option, "-Xbatch")) {
if (FLAG_SET_CMDLINE(BackgroundCompilation, false) != JVMFlag::SUCCESS) {
@@ -3818,15 +3886,6 @@
return true;
}
-void Arguments::handle_extra_cms_flags(const char* msg) {
- SpecialFlag flag;
- const char *flag_name = "UseConcMarkSweepGC";
- if (lookup_special_flag(flag_name, flag)) {
- handle_aliases_and_deprecation(flag_name, /* print warning */ true);
- warning("%s", msg);
- }
-}
-
// Parse entry point called from JNI_CreateJavaVM
jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) {
@@ -4160,14 +4219,11 @@
FLAG_SET_DEFAULT(MinHeapDeltaBytes, 64*M);
}
}
- // UseNUMAInterleaving is set to ON for all collectors and
- // platforms when UseNUMA is set to ON. NUMA-aware collectors
- // such as the parallel collector for Linux and Solaris will
- // interleave old gen and survivor spaces on top of NUMA
- // allocation policy for the eden space.
- // Non NUMA-aware collectors such as CMS, G1 and Serial-GC on
- // all platforms and ParallelGC on Windows will interleave all
- // of the heap spaces across NUMA nodes.
+ // UseNUMAInterleaving is set to ON for all collectors and platforms when
+ // UseNUMA is set to ON. NUMA-aware collectors will interleave old gen and
+ // survivor spaces on top of NUMA allocation policy for the eden space.
+ // Non NUMA-aware collectors will interleave all of the heap spaces across
+ // NUMA nodes.
if (FLAG_IS_DEFAULT(UseNUMAInterleaving)) {
FLAG_SET_ERGO(UseNUMAInterleaving, true);
}
--- a/src/hotspot/share/runtime/arguments.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/arguments.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -425,8 +425,6 @@
static bool handle_deprecated_print_gc_flags();
- static void handle_extra_cms_flags(const char* msg);
-
static jint parse_vm_init_args(const JavaVMInitArgs *vm_options_args,
const JavaVMInitArgs *java_tool_options_args,
const JavaVMInitArgs *java_options_args,
--- a/src/hotspot/share/runtime/deoptimization.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/deoptimization.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -44,7 +44,7 @@
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.inline.hpp"
#include "oops/oop.inline.hpp"
-#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "oops/typeArrayOop.inline.hpp"
#include "oops/verifyOopClosure.hpp"
#include "prims/jvmtiThreadState.hpp"
@@ -428,7 +428,7 @@
// frame.
bool caller_was_method_handle = false;
if (deopt_sender.is_interpreted_frame()) {
- methodHandle method = deopt_sender.interpreter_frame_method();
+ methodHandle method(thread, deopt_sender.interpreter_frame_method());
Bytecode_invoke cur = Bytecode_invoke_check(method, deopt_sender.interpreter_frame_bci());
if (cur.is_invokedynamic() || cur.is_invokehandle()) {
// Method handle invokes may involve fairly arbitrary chains of
@@ -1536,7 +1536,7 @@
assert(caller_frame.cb()->as_compiled_method_or_null() == cm, "expect top frame compiled method");
Deoptimization::deoptimize(thread, caller_frame, ®_map, Deoptimization::Reason_not_compiled_exception_handler);
- MethodData* trap_mdo = get_method_data(thread, cm->method(), true);
+ MethodData* trap_mdo = get_method_data(thread, methodHandle(thread, cm->method()), true);
if (trap_mdo != NULL) {
trap_mdo->inc_trap_count(Deoptimization::Reason_not_compiled_exception_handler);
}
@@ -1701,7 +1701,7 @@
);
}
- methodHandle trap_method = trap_scope->method();
+ methodHandle trap_method(THREAD, trap_scope->method());
int trap_bci = trap_scope->bci();
#if INCLUDE_JVMCI
jlong speculation = thread->pending_failed_speculation();
@@ -1732,7 +1732,7 @@
methodHandle profiled_method;
#if INCLUDE_JVMCI
if (nm->is_compiled_by_jvmci()) {
- profiled_method = nm->method();
+ profiled_method = methodHandle(THREAD, nm->method());
} else {
profiled_method = trap_method;
}
--- a/src/hotspot/share/runtime/fieldDescriptor.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/fieldDescriptor.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -30,7 +30,7 @@
#include "oops/constantPool.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/oop.inline.hpp"
-#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/signature.hpp"
--- a/src/hotspot/share/runtime/fieldDescriptor.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/fieldDescriptor.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -31,11 +31,11 @@
// must be put in this file, as they require runtime/handles.inline.hpp.
inline Symbol* fieldDescriptor::name() const {
- return field()->name(_cp);
+ return field()->name(_cp());
}
inline Symbol* fieldDescriptor::signature() const {
- return field()->signature(_cp);
+ return field()->signature(_cp());
}
inline InstanceKlass* fieldDescriptor::field_holder() const {
--- a/src/hotspot/share/runtime/flags/jvmFlag.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/flags/jvmFlag.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -447,11 +447,11 @@
// an eye-pleasing tabular output is created.
//
// Sample output:
- // bool CMSScavengeBeforeRemark = false {product} {default}
- // uintx CMSScheduleRemarkEdenPenetration = 50 {product} {default}
- // size_t CMSScheduleRemarkEdenSizeThreshold = 2097152 {product} {default}
- // uintx CMSScheduleRemarkSamplingRatio = 5 {product} {default}
- // double CMSSmallCoalSurplusPercent = 1.050000 {product} {default}
+ // bool ThreadPriorityVerbose = false {product} {default}
+ // uintx ThresholdTolerance = 10 {product} {default}
+ // size_t TLABSize = 0 {product} {default}
+ // uintx SurvivorRatio = 8 {product} {default}
+ // double InitialRAMPercentage = 1.562500 {product} {default}
// ccstr CompileCommandFile = MyFile.cmd {product} {command line}
// ccstrlist CompileOnly = Method1
// CompileOnly += Method2 {product} {command line}
--- a/src/hotspot/share/runtime/frame.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/frame.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1164,7 +1164,7 @@
// Compute the actual expression stack size
InterpreterOopMap mask;
- OopMapCache::compute_one_oop_map(m, bci, &mask);
+ OopMapCache::compute_one_oop_map(methodHandle(Thread::current(), m), bci, &mask);
intptr_t* tos = NULL;
// Report each stack element and mark as owned by this frame
for (int e = 0; e < mask.expression_stack_size(); e++) {
--- a/src/hotspot/share/runtime/globals.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/globals.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -1947,9 +1947,6 @@
experimental(bool, UseCriticalCompilerThreadPriority, false, \
"Compiler thread(s) run at critical scheduling priority") \
\
- experimental(bool, UseCriticalCMSThreadPriority, false, \
- "ConcurrentMarkSweep thread runs at critical scheduling priority")\
- \
develop(intx, NewCodeParameter, 0, \
"Testing Only: Create a dedicated integer parameter before " \
"putback") \
--- a/src/hotspot/share/runtime/handles.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/handles.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -143,7 +143,6 @@
public: \
/* Constructors */ \
name##Handle () : _value(NULL), _thread(NULL) {} \
- name##Handle (type* obj); \
name##Handle (Thread* thread, type* obj); \
\
name##Handle (const name##Handle &h); \
--- a/src/hotspot/share/runtime/handles.inline.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/handles.inline.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -55,14 +55,6 @@
// Constructor for metadata handles
#define DEF_METADATA_HANDLE_FN(name, type) \
-inline name##Handle::name##Handle(type* obj) : _value(obj), _thread(NULL) { \
- if (obj != NULL) { \
- assert(((Metadata*)obj)->is_valid(), "obj is valid"); \
- _thread = Thread::current(); \
- assert (_thread->is_in_stack((address)this), "not on stack?"); \
- _thread->metadata_handles()->push((Metadata*)obj); \
- } \
-} \
inline name##Handle::name##Handle(Thread* thread, type* obj) : _value(obj), _thread(thread) { \
if (obj != NULL) { \
assert(((Metadata*)obj)->is_valid(), "obj is valid"); \
--- a/src/hotspot/share/runtime/java.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/java.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -733,6 +733,7 @@
return (e > o) ? 1 : ((e == o) ? 0 : -1);
}
+/* See JEP 223 */
void JDK_Version::to_string(char* buffer, size_t buflen) const {
assert(buffer && buflen > 0, "call with useful buffer");
size_t index = 0;
@@ -744,13 +745,12 @@
&buffer[index], buflen - index, "%d.%d", _major, _minor);
if (rc == -1) return;
index += rc;
- if (_security > 0) {
- rc = jio_snprintf(&buffer[index], buflen - index, ".%d", _security);
+ if (_patch > 0) {
+ rc = jio_snprintf(&buffer[index], buflen - index, ".%d.%d", _security, _patch);
if (rc == -1) return;
index += rc;
- }
- if (_patch > 0) {
- rc = jio_snprintf(&buffer[index], buflen - index, ".%d", _patch);
+ } else if (_security > 0) {
+ rc = jio_snprintf(&buffer[index], buflen - index, ".%d", _security);
if (rc == -1) return;
index += rc;
}
--- a/src/hotspot/share/runtime/javaCalls.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/javaCalls.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -186,7 +186,7 @@
LinkInfo link_info(spec_klass, name, signature);
LinkResolver::resolve_virtual_call(
callinfo, receiver, recvrKlass, link_info, true, CHECK);
- methodHandle method = callinfo.selected_method();
+ methodHandle method(THREAD, callinfo.selected_method());
assert(method.not_null(), "should have thrown exception");
// Invoke the method
@@ -222,7 +222,7 @@
CallInfo callinfo;
LinkInfo link_info(klass, name, signature);
LinkResolver::resolve_special_call(callinfo, args->receiver(), link_info, CHECK);
- methodHandle method = callinfo.selected_method();
+ methodHandle method(THREAD, callinfo.selected_method());
assert(method.not_null(), "should have thrown exception");
// Invoke the method
@@ -257,7 +257,7 @@
CallInfo callinfo;
LinkInfo link_info(klass, name, signature);
LinkResolver::resolve_static_call(callinfo, link_info, true, CHECK);
- methodHandle method = callinfo.selected_method();
+ methodHandle method(THREAD, callinfo.selected_method());
assert(method.not_null(), "should have thrown exception");
// Invoke the method
--- a/src/hotspot/share/runtime/mutexLocker.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/mutexLocker.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -263,10 +263,6 @@
def(PerfDataMemAlloc_lock , PaddedMutex , leaf, true, _safepoint_check_always); // used for allocating PerfData memory for performance data
def(PerfDataManager_lock , PaddedMutex , leaf, true, _safepoint_check_always); // used for synchronized access to PerfDataManager resources
- // CMS_modUnionTable_lock leaf
- // CMS_bitMap_lock leaf 1
- // CMS_freeList_lock leaf 2
-
def(Threads_lock , PaddedMonitor, barrier, true, _safepoint_check_always); // Used for safepoint protocol.
def(NonJavaThreadsList_lock , PaddedMutex, leaf, true, _safepoint_check_never);
def(NonJavaThreadsListSync_lock , PaddedMutex, leaf, true, _safepoint_check_never);
--- a/src/hotspot/share/runtime/os.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/os.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -374,6 +374,7 @@
static size_t numa_get_leaf_groups(int *ids, size_t size);
static bool numa_topology_changed();
static int numa_get_group_id();
+ static int numa_get_group_id_for_address(const void* address);
// Page manipulation
struct page_info {
--- a/src/hotspot/share/runtime/reflection.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/reflection.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -978,7 +978,7 @@
LinkInfo(klass, name, signature),
true,
CHECK_(methodHandle()));
- return info.selected_method();
+ return methodHandle(THREAD, info.selected_method());
}
// Conversion
--- a/src/hotspot/share/runtime/sharedRuntime.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1022,7 +1022,7 @@
return find_callee_info_helper(thread, vfst, bc, callinfo, THREAD);
}
-methodHandle SharedRuntime::extract_attached_method(vframeStream& vfst) {
+Method* SharedRuntime::extract_attached_method(vframeStream& vfst) {
CompiledMethod* caller = vfst.nm();
nmethodLocker caller_lock(caller);
@@ -1055,9 +1055,9 @@
int bytecode_index = bytecode.index();
bc = bytecode.invoke_code();
- methodHandle attached_method = extract_attached_method(vfst);
+ methodHandle attached_method(THREAD, extract_attached_method(vfst));
if (attached_method.not_null()) {
- methodHandle callee = bytecode.static_target(CHECK_NH);
+ Method* callee = bytecode.static_target(CHECK_NH);
vmIntrinsics::ID id = callee->intrinsic_id();
// When VM replaces MH.invokeBasic/linkTo* call with a direct/virtual call,
// it attaches statically resolved method to the call site.
@@ -1105,8 +1105,8 @@
frame callerFrame = stubFrame.sender(®_map2);
if (attached_method.is_null()) {
- methodHandle callee = bytecode.static_target(CHECK_NH);
- if (callee.is_null()) {
+ Method* callee = bytecode.static_target(CHECK_NH);
+ if (callee == NULL) {
THROW_(vmSymbols::java_lang_NoSuchMethodException(), nullHandle);
}
}
@@ -1144,7 +1144,6 @@
rk = constants->klass_ref_at(bytecode_index, CHECK_NH);
}
Klass* static_receiver_klass = rk;
- methodHandle callee = callinfo.selected_method();
assert(receiver_klass->is_subtype_of(static_receiver_klass),
"actual receiver must be subclass of static receiver klass");
if (receiver_klass->is_instance_klass()) {
@@ -1182,7 +1181,7 @@
Bytecodes::Code bc;
CallInfo callinfo;
find_callee_info_helper(thread, vfst, bc, callinfo, CHECK_(methodHandle()));
- callee_method = callinfo.selected_method();
+ callee_method = methodHandle(THREAD, callinfo.selected_method());
}
assert(callee_method()->is_method(), "must be");
return callee_method;
@@ -1325,7 +1324,7 @@
Bytecodes::Code invoke_code = Bytecodes::_illegal;
Handle receiver = find_callee_info(thread, invoke_code,
call_info, CHECK_(methodHandle()));
- methodHandle callee_method = call_info.selected_method();
+ methodHandle callee_method(THREAD, call_info.selected_method());
assert((!is_virtual && invoke_code == Bytecodes::_invokestatic ) ||
(!is_virtual && invoke_code == Bytecodes::_invokespecial) ||
@@ -1479,7 +1478,7 @@
// Get the called method from the invoke bytecode.
vframeStream vfst(thread, true);
assert(!vfst.at_end(), "Java frame must exist");
- methodHandle caller(vfst.method());
+ methodHandle caller(thread, vfst.method());
Bytecode_invoke invoke(caller, vfst.bci());
DEBUG_ONLY( invoke.verify(); )
@@ -1493,7 +1492,7 @@
// Install exception and return forward entry.
address res = StubRoutines::throw_AbstractMethodError_entry();
JRT_BLOCK
- methodHandle callee = invoke.static_target(thread);
+ methodHandle callee(thread, invoke.static_target(thread));
if (!callee.is_null()) {
oop recv = callerFrame.retrieve_receiver(®_map);
Klass *recv_klass = (recv != NULL) ? recv->klass() : NULL;
@@ -1657,7 +1656,7 @@
return callee_method;
}
- methodHandle callee_method = call_info.selected_method();
+ methodHandle callee_method(thread, call_info.selected_method());
#ifndef PRODUCT
Atomic::inc(&_ic_miss_ctr);
--- a/src/hotspot/share/runtime/sharedRuntime.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/sharedRuntime.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -351,7 +351,7 @@
Bytecodes::Code& bc,
CallInfo& callinfo, TRAPS);
- static methodHandle extract_attached_method(vframeStream& vfst);
+ static Method* extract_attached_method(vframeStream& vfst);
static address clean_virtual_call_entry();
static address clean_opt_virtual_call_entry();
--- a/src/hotspot/share/runtime/thread.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/thread.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -4367,7 +4367,7 @@
// + Call before_exit(), prepare for VM exit
// > run VM level shutdown hooks (they are registered through JVM_OnExit(),
// currently the only user of this mechanism is File.deleteOnExit())
-// > stop StatSampler, watcher thread, CMS threads,
+// > stop StatSampler, watcher thread,
// post thread end and vm death events to JVMTI,
// stop signal thread
// + Call JavaThread::exit(), it will:
--- a/src/hotspot/share/runtime/vframeArray.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/vframeArray.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -589,8 +589,8 @@
if (index == 0) {
callee_parameters = callee_locals = 0;
} else {
- methodHandle caller = elem->method();
- methodHandle callee = element(index - 1)->method();
+ methodHandle caller(THREAD, elem->method());
+ methodHandle callee(THREAD, element(index - 1)->method());
Bytecode_invoke inv(caller, elem->bci());
// invokedynamic instructions don't have a class but obviously don't have a MemberName appendix.
// NOTE: Use machinery here that avoids resolving of any kind.
--- a/src/hotspot/share/runtime/vmOperations.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/vmOperations.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -63,8 +63,6 @@
template(GenCollectForAllocation) \
template(ParallelGCFailedAllocation) \
template(ParallelGCSystemGC) \
- template(CMS_Initial_Mark) \
- template(CMS_Final_Remark) \
template(G1CollectForAllocation) \
template(G1CollectFull) \
template(G1Concurrent) \
--- a/src/hotspot/share/runtime/vmStructs.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/runtime/vmStructs.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -2674,11 +2674,6 @@
declare_constant(markWord::no_lock_in_place) \
declare_constant(markWord::max_age) \
\
- /* Constants in markWord used by CMS. */ \
- declare_constant(markWord::cms_shift) \
- declare_constant(markWord::cms_mask) \
- declare_constant(markWord::size_shift) \
- \
/* InvocationCounter constants */ \
declare_constant(InvocationCounter::count_increment) \
declare_constant(InvocationCounter::count_shift)
--- a/src/hotspot/share/utilities/debug.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/utilities/debug.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -642,6 +642,7 @@
tty->print_cr(" pns(void* sp, void* fp, void* pc) - print native (i.e. mixed) stack trace. E.g.");
tty->print_cr(" pns($sp, $rbp, $pc) on Linux/amd64 and Solaris/amd64 or");
tty->print_cr(" pns($sp, $ebp, $pc) on Linux/x86 or");
+ tty->print_cr(" pns($sp, $fp, $pc) on Linux/AArch64 or");
tty->print_cr(" pns($sp, 0, $pc) on Linux/ppc64 or");
tty->print_cr(" pns($sp, $s8, $pc) on Linux/mips or");
tty->print_cr(" pns($sp + 0x7ff, 0, $pc) on Solaris/SPARC");
--- a/src/hotspot/share/utilities/dtrace_disabled.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/utilities/dtrace_disabled.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -121,14 +121,6 @@
#define HOTSPOT_VMOPS_END_ENABLED() 0
/* hs_private provider probes */
-#define HS_PRIVATE_CMS_INITMARK_BEGIN()
-#define HS_PRIVATE_CMS_INITMARK_BEGIN_ENABLED() 0
-#define HS_PRIVATE_CMS_INITMARK_END()
-#define HS_PRIVATE_CMS_INITMARK_END_ENABLED() 0
-#define HS_PRIVATE_CMS_REMARK_BEGIN()
-#define HS_PRIVATE_CMS_REMARK_BEGIN_ENABLED() 0
-#define HS_PRIVATE_CMS_REMARK_END()
-#define HS_PRIVATE_CMS_REMARK_END_ENABLED() 0
#define HS_PRIVATE_HASHTABLE_NEW_ENTRY(arg0, arg1, arg2, arg3)
#define HS_PRIVATE_HASHTABLE_NEW_ENTRY_ENABLED() 0
#define HS_PRIVATE_SAFEPOINT_BEGIN()
--- a/src/hotspot/share/utilities/exceptions.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/utilities/exceptions.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -229,7 +229,7 @@
exception = Handle(THREAD, e); // fill_in_stack trace does gc
assert(k->is_initialized(), "need to increase java_thread_min_stack_allowed calculation");
if (StackTraceInThrowable) {
- java_lang_Throwable::fill_in_stack_trace(exception, method());
+ java_lang_Throwable::fill_in_stack_trace(exception, method);
}
// Increment counter for hs_err file reporting
Atomic::inc(&Exceptions::_stack_overflow_errors);
--- a/src/hotspot/share/utilities/macros.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/utilities/macros.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -131,24 +131,6 @@
#define NOT_MANAGEMENT_RETURN_(code) { return code; }
#endif // INCLUDE_MANAGEMENT
-#ifndef INCLUDE_CMSGC
-#define INCLUDE_CMSGC 1
-#endif // INCLUDE_CMSGC
-
-#if INCLUDE_CMSGC
-#define CMSGC_ONLY(x) x
-#define CMSGC_ONLY_ARG(arg) arg,
-#define NOT_CMSGC(x)
-#define NOT_CMSGC_RETURN /* next token must be ; */
-#define NOT_CMSGC_RETURN_(code) /* next token must be ; */
-#else
-#define CMSGC_ONLY(x)
-#define CMSGC_ONLY_ARG(x)
-#define NOT_CMSGC(x) x
-#define NOT_CMSGC_RETURN {}
-#define NOT_CMSGC_RETURN_(code) { return code; }
-#endif // INCLUDE_CMSGC
-
#ifndef INCLUDE_EPSILONGC
#define INCLUDE_EPSILONGC 1
#endif // INCLUDE_EPSILONGC
--- a/src/hotspot/share/utilities/xmlstream.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/utilities/xmlstream.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -385,9 +385,9 @@
// ------------------------------------------------------------------
// Output a method attribute, in the form " method='pkg/cls name sig'".
// This is used only when there is no ciMethod available.
-void xmlStream::method(const methodHandle& method) {
+void xmlStream::method(Method* method) {
assert_if_no_error(inside_attrs(), "printing attributes");
- if (method.is_null()) return;
+ if (method == NULL) return;
print_raw(" method='");
method_text(method);
print("' bytes='%d'", method->code_size());
@@ -413,10 +413,10 @@
}
}
-void xmlStream::method_text(const methodHandle& method) {
+void xmlStream::method_text(Method* method) {
ResourceMark rm;
assert_if_no_error(inside_attrs(), "printing attributes");
- if (method.is_null()) return;
+ if (method == NULL) return;
text()->print("%s", method->method_holder()->external_name());
print_raw(" "); // " " is easier for tools to parse than "::"
method->name()->print_symbol_on(text());
--- a/src/hotspot/share/utilities/xmlstream.hpp Tue Nov 12 10:25:55 2019 -0500
+++ b/src/hotspot/share/utilities/xmlstream.hpp Wed Nov 13 17:21:31 2019 -0500
@@ -137,14 +137,14 @@
// commonly used XML attributes
void stamp(); // stamp='1.234'
- void method(const methodHandle& m); // method='k n s' ...
+ void method(Method* m); // method='k n s' ...
void klass(Klass* k); // klass='name'
void name(const Symbol* s); // name='name'
void object(const char* attr, Metadata* val);
void object(const char* attr, Handle val);
// print the text alone (sans ''):
- void method_text(const methodHandle& m);
+ void method_text(Method* m);
void klass_text(Klass* k); // klass='name'
void name_text(const Symbol* s); // name='name'
void object_text(Metadata* x);
--- a/src/java.base/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java Wed Nov 13 17:21:31 2019 -0500
@@ -429,8 +429,9 @@
@Override
public NumberFormat getIntegerInstance(Locale locale) {
- return new DecimalFormat(getNumberPattern(NF_INTEGER, locale),
+ DecimalFormat format = new DecimalFormat(getNumberPattern(NF_INTEGER, locale),
DecimalFormatSymbols.getInstance(locale));
+ return HostLocaleProviderAdapter.makeIntegerFormatter(format);
}
@Override
--- a/src/java.base/share/classes/java/security/AlgorithmParameters.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/share/classes/java/security/AlgorithmParameters.java Wed Nov 13 17:21:31 2019 -0500
@@ -51,7 +51,6 @@
* following standard {@code AlgorithmParameters} algorithms:
* <ul>
* <li>{@code AES}</li>
- * <li>{@code DES}</li>
* <li>{@code DESede}</li>
* <li>{@code DiffieHellman}</li>
* <li>{@code DSA}</li>
--- a/src/java.base/share/classes/java/security/MessageDigest.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/share/classes/java/security/MessageDigest.java Wed Nov 13 17:21:31 2019 -0500
@@ -84,7 +84,6 @@
* <p> Every implementation of the Java platform is required to support
* the following standard {@code MessageDigest} algorithms:
* <ul>
- * <li>{@code MD5}</li>
* <li>{@code SHA-1}</li>
* <li>{@code SHA-256}</li>
* </ul>
--- a/src/java.base/share/classes/java/util/Arrays.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/share/classes/java/util/Arrays.java Wed Nov 13 17:21:31 2019 -0500
@@ -74,17 +74,658 @@
*/
public class Arrays {
- /**
- * The minimum array length below which a parallel sorting
- * algorithm will not further partition the sorting task. Using
- * smaller sizes typically results in memory contention across
- * tasks that makes parallel speedups unlikely.
- */
- private static final int MIN_ARRAY_SORT_GRAN = 1 << 13;
-
// Suppresses default constructor, ensuring non-instantiability.
private Arrays() {}
+ /*
+ * Sorting methods. Note that all public "sort" methods take the
+ * same form: performing argument checks if necessary, and then
+ * expanding arguments into those required for the internal
+ * implementation methods residing in other package-private
+ * classes (except for legacyMergeSort, included in this class).
+ */
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ */
+ public static void sort(int[] a) {
+ DualPivotQuicksort.sort(a, 0, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ */
+ public static void sort(int[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, 0, fromIndex, toIndex);
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ */
+ public static void sort(long[] a) {
+ DualPivotQuicksort.sort(a, 0, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ */
+ public static void sort(long[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, 0, fromIndex, toIndex);
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ */
+ public static void sort(short[] a) {
+ DualPivotQuicksort.sort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ */
+ public static void sort(short[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, fromIndex, toIndex);
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ */
+ public static void sort(char[] a) {
+ DualPivotQuicksort.sort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ */
+ public static void sort(char[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, fromIndex, toIndex);
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ */
+ public static void sort(byte[] a) {
+ DualPivotQuicksort.sort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ */
+ public static void sort(byte[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, fromIndex, toIndex);
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * <p>The {@code <} relation does not provide a total order on all float
+ * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN}
+ * value compares neither less than, greater than, nor equal to any value,
+ * even itself. This method uses the total order imposed by the method
+ * {@link Float#compareTo}: {@code -0.0f} is treated as less than value
+ * {@code 0.0f} and {@code Float.NaN} is considered greater than any
+ * other value and all {@code Float.NaN} values are considered equal.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ */
+ public static void sort(float[] a) {
+ DualPivotQuicksort.sort(a, 0, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * <p>The {@code <} relation does not provide a total order on all float
+ * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN}
+ * value compares neither less than, greater than, nor equal to any value,
+ * even itself. This method uses the total order imposed by the method
+ * {@link Float#compareTo}: {@code -0.0f} is treated as less than value
+ * {@code 0.0f} and {@code Float.NaN} is considered greater than any
+ * other value and all {@code Float.NaN} values are considered equal.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ */
+ public static void sort(float[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, 0, fromIndex, toIndex);
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * <p>The {@code <} relation does not provide a total order on all double
+ * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN}
+ * value compares neither less than, greater than, nor equal to any value,
+ * even itself. This method uses the total order imposed by the method
+ * {@link Double#compareTo}: {@code -0.0d} is treated as less than value
+ * {@code 0.0d} and {@code Double.NaN} is considered greater than any
+ * other value and all {@code Double.NaN} values are considered equal.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ */
+ public static void sort(double[] a) {
+ DualPivotQuicksort.sort(a, 0, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * <p>The {@code <} relation does not provide a total order on all double
+ * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN}
+ * value compares neither less than, greater than, nor equal to any value,
+ * even itself. This method uses the total order imposed by the method
+ * {@link Double#compareTo}: {@code -0.0d} is treated as less than value
+ * {@code 0.0d} and {@code Double.NaN} is considered greater than any
+ * other value and all {@code Double.NaN} values are considered equal.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort
+ * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ */
+ public static void sort(double[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, 0, fromIndex, toIndex);
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(byte[] a) {
+ DualPivotQuicksort.sort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending numerical order.
+ * The range to be sorted extends from the index {@code fromIndex},
+ * inclusive, to the index {@code toIndex}, exclusive. If
+ * {@code fromIndex == toIndex}, the range to be sorted is empty.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(byte[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, fromIndex, toIndex);
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(char[] a) {
+ DualPivotQuicksort.sort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending numerical order.
+ * The range to be sorted extends from the index {@code fromIndex},
+ * inclusive, to the index {@code toIndex}, exclusive. If
+ * {@code fromIndex == toIndex}, the range to be sorted is empty.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(char[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, fromIndex, toIndex);
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(short[] a) {
+ DualPivotQuicksort.sort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending numerical order.
+ * The range to be sorted extends from the index {@code fromIndex},
+ * inclusive, to the index {@code toIndex}, exclusive. If
+ * {@code fromIndex == toIndex}, the range to be sorted is empty.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(short[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, fromIndex, toIndex);
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(int[] a) {
+ DualPivotQuicksort.sort(a, ForkJoinPool.getCommonPoolParallelism(), 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending numerical order.
+ * The range to be sorted extends from the index {@code fromIndex},
+ * inclusive, to the index {@code toIndex}, exclusive. If
+ * {@code fromIndex == toIndex}, the range to be sorted is empty.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(int[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, ForkJoinPool.getCommonPoolParallelism(), fromIndex, toIndex);
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(long[] a) {
+ DualPivotQuicksort.sort(a, ForkJoinPool.getCommonPoolParallelism(), 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending numerical order.
+ * The range to be sorted extends from the index {@code fromIndex},
+ * inclusive, to the index {@code toIndex}, exclusive. If
+ * {@code fromIndex == toIndex}, the range to be sorted is empty.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(long[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, ForkJoinPool.getCommonPoolParallelism(), fromIndex, toIndex);
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * <p>The {@code <} relation does not provide a total order on all float
+ * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN}
+ * value compares neither less than, greater than, nor equal to any value,
+ * even itself. This method uses the total order imposed by the method
+ * {@link Float#compareTo}: {@code -0.0f} is treated as less than value
+ * {@code 0.0f} and {@code Float.NaN} is considered greater than any
+ * other value and all {@code Float.NaN} values are considered equal.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(float[] a) {
+ DualPivotQuicksort.sort(a, ForkJoinPool.getCommonPoolParallelism(), 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending numerical order.
+ * The range to be sorted extends from the index {@code fromIndex},
+ * inclusive, to the index {@code toIndex}, exclusive. If
+ * {@code fromIndex == toIndex}, the range to be sorted is empty.
+ *
+ * <p>The {@code <} relation does not provide a total order on all float
+ * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN}
+ * value compares neither less than, greater than, nor equal to any value,
+ * even itself. This method uses the total order imposed by the method
+ * {@link Float#compareTo}: {@code -0.0f} is treated as less than value
+ * {@code 0.0f} and {@code Float.NaN} is considered greater than any
+ * other value and all {@code Float.NaN} values are considered equal.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(float[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, ForkJoinPool.getCommonPoolParallelism(), fromIndex, toIndex);
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * <p>The {@code <} relation does not provide a total order on all double
+ * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN}
+ * value compares neither less than, greater than, nor equal to any value,
+ * even itself. This method uses the total order imposed by the method
+ * {@link Double#compareTo}: {@code -0.0d} is treated as less than value
+ * {@code 0.0d} and {@code Double.NaN} is considered greater than any
+ * other value and all {@code Double.NaN} values are considered equal.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(double[] a) {
+ DualPivotQuicksort.sort(a, ForkJoinPool.getCommonPoolParallelism(), 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending numerical order.
+ * The range to be sorted extends from the index {@code fromIndex},
+ * inclusive, to the index {@code toIndex}, exclusive. If
+ * {@code fromIndex == toIndex}, the range to be sorted is empty.
+ *
+ * <p>The {@code <} relation does not provide a total order on all double
+ * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN}
+ * value compares neither less than, greater than, nor equal to any value,
+ * even itself. This method uses the total order imposed by the method
+ * {@link Double#compareTo}: {@code -0.0d} is treated as less than value
+ * {@code 0.0d} and {@code Double.NaN} is considered greater than any
+ * other value and all {@code Double.NaN} values are considered equal.
+ *
+ * @implNote The sorting algorithm is a Dual-Pivot Quicksort by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
+ * faster than traditional (one-pivot) Quicksort implementations.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(double[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ DualPivotQuicksort.sort(a, ForkJoinPool.getCommonPoolParallelism(), fromIndex, toIndex);
+ }
+
+ /**
+ * Checks that {@code fromIndex} and {@code toIndex} are in
+ * the range and throws an exception if they aren't.
+ */
+ static void rangeCheck(int arrayLength, int fromIndex, int toIndex) {
+ if (fromIndex > toIndex) {
+ throw new IllegalArgumentException(
+ "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
+ }
+ if (fromIndex < 0) {
+ throw new ArrayIndexOutOfBoundsException(fromIndex);
+ }
+ if (toIndex > arrayLength) {
+ throw new ArrayIndexOutOfBoundsException(toIndex);
+ }
+ }
+
/**
* A comparator that implements the natural ordering of a group of
* mutually comparable elements. May be used when a supplied
@@ -109,863 +750,12 @@
}
/**
- * Checks that {@code fromIndex} and {@code toIndex} are in
- * the range and throws an exception if they aren't.
- */
- static void rangeCheck(int arrayLength, int fromIndex, int toIndex) {
- if (fromIndex > toIndex) {
- throw new IllegalArgumentException(
- "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
- }
- if (fromIndex < 0) {
- throw new ArrayIndexOutOfBoundsException(fromIndex);
- }
- if (toIndex > arrayLength) {
- throw new ArrayIndexOutOfBoundsException(toIndex);
- }
- }
-
- /*
- * Sorting methods. Note that all public "sort" methods take the
- * same form: Performing argument checks if necessary, and then
- * expanding arguments into those required for the internal
- * implementation methods residing in other package-private
- * classes (except for legacyMergeSort, included in this class).
- */
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- */
- public static void sort(int[] a) {
- DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
- }
-
- /**
- * Sorts the specified range of the array into ascending order. The range
- * to be sorted extends from the index {@code fromIndex}, inclusive, to
- * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
- * the range to be sorted is empty.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- */
- public static void sort(int[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
- }
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- */
- public static void sort(long[] a) {
- DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
- }
-
- /**
- * Sorts the specified range of the array into ascending order. The range
- * to be sorted extends from the index {@code fromIndex}, inclusive, to
- * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
- * the range to be sorted is empty.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- */
- public static void sort(long[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
- }
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- */
- public static void sort(short[] a) {
- DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
- }
-
- /**
- * Sorts the specified range of the array into ascending order. The range
- * to be sorted extends from the index {@code fromIndex}, inclusive, to
- * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
- * the range to be sorted is empty.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- */
- public static void sort(short[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
- }
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- */
- public static void sort(char[] a) {
- DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
- }
-
- /**
- * Sorts the specified range of the array into ascending order. The range
- * to be sorted extends from the index {@code fromIndex}, inclusive, to
- * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
- * the range to be sorted is empty.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- */
- public static void sort(char[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
- }
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- */
- public static void sort(byte[] a) {
- DualPivotQuicksort.sort(a, 0, a.length - 1);
- }
-
- /**
- * Sorts the specified range of the array into ascending order. The range
- * to be sorted extends from the index {@code fromIndex}, inclusive, to
- * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
- * the range to be sorted is empty.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- */
- public static void sort(byte[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1);
- }
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * <p>The {@code <} relation does not provide a total order on all float
- * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN}
- * value compares neither less than, greater than, nor equal to any value,
- * even itself. This method uses the total order imposed by the method
- * {@link Float#compareTo}: {@code -0.0f} is treated as less than value
- * {@code 0.0f} and {@code Float.NaN} is considered greater than any
- * other value and all {@code Float.NaN} values are considered equal.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- */
- public static void sort(float[] a) {
- DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
- }
-
- /**
- * Sorts the specified range of the array into ascending order. The range
- * to be sorted extends from the index {@code fromIndex}, inclusive, to
- * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
- * the range to be sorted is empty.
- *
- * <p>The {@code <} relation does not provide a total order on all float
- * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN}
- * value compares neither less than, greater than, nor equal to any value,
- * even itself. This method uses the total order imposed by the method
- * {@link Float#compareTo}: {@code -0.0f} is treated as less than value
- * {@code 0.0f} and {@code Float.NaN} is considered greater than any
- * other value and all {@code Float.NaN} values are considered equal.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- */
- public static void sort(float[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
- }
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * <p>The {@code <} relation does not provide a total order on all double
- * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN}
- * value compares neither less than, greater than, nor equal to any value,
- * even itself. This method uses the total order imposed by the method
- * {@link Double#compareTo}: {@code -0.0d} is treated as less than value
- * {@code 0.0d} and {@code Double.NaN} is considered greater than any
- * other value and all {@code Double.NaN} values are considered equal.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- */
- public static void sort(double[] a) {
- DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
- }
-
- /**
- * Sorts the specified range of the array into ascending order. The range
- * to be sorted extends from the index {@code fromIndex}, inclusive, to
- * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
- * the range to be sorted is empty.
- *
- * <p>The {@code <} relation does not provide a total order on all double
- * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN}
- * value compares neither less than, greater than, nor equal to any value,
- * even itself. This method uses the total order imposed by the method
- * {@link Double#compareTo}: {@code -0.0d} is treated as less than value
- * {@code 0.0d} and {@code Double.NaN} is considered greater than any
- * other value and all {@code Double.NaN} values are considered equal.
- *
- * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
- * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
- * faster than traditional (one-pivot) Quicksort implementations.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- */
- public static void sort(double[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
- }
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(byte[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(byte[]) Arrays.sort} method. The algorithm requires a
- * working space no greater than the size of the original array. The
- * {@link ForkJoinPool#commonPool() ForkJoin common pool} is used to
- * execute any parallel tasks.
- *
- * @param a the array to be sorted
- *
- * @since 1.8
- */
- public static void parallelSort(byte[] a) {
- int n = a.length, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, 0, n - 1);
- else
- new ArraysParallelSortHelpers.FJByte.Sorter
- (null, a, new byte[n], 0, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
-
- /**
- * Sorts the specified range of the array into ascending numerical order.
- * The range to be sorted extends from the index {@code fromIndex},
- * inclusive, to the index {@code toIndex}, exclusive. If
- * {@code fromIndex == toIndex}, the range to be sorted is empty.
- *
- * @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(byte[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(byte[]) Arrays.sort} method. The algorithm requires a working
- * space no greater than the size of the specified range of the original
- * array. The {@link ForkJoinPool#commonPool() ForkJoin common pool} is
- * used to execute any parallel tasks.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- *
- * @since 1.8
- */
- public static void parallelSort(byte[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- int n = toIndex - fromIndex, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1);
- else
- new ArraysParallelSortHelpers.FJByte.Sorter
- (null, a, new byte[n], fromIndex, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(char[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(char[]) Arrays.sort} method. The algorithm requires a
- * working space no greater than the size of the original array. The
- * {@link ForkJoinPool#commonPool() ForkJoin common pool} is used to
- * execute any parallel tasks.
- *
- * @param a the array to be sorted
- *
- * @since 1.8
- */
- public static void parallelSort(char[] a) {
- int n = a.length, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, 0, n - 1, null, 0, 0);
- else
- new ArraysParallelSortHelpers.FJChar.Sorter
- (null, a, new char[n], 0, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
-
- /**
- * Sorts the specified range of the array into ascending numerical order.
- * The range to be sorted extends from the index {@code fromIndex},
- * inclusive, to the index {@code toIndex}, exclusive. If
- * {@code fromIndex == toIndex}, the range to be sorted is empty.
- *
- @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(char[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(char[]) Arrays.sort} method. The algorithm requires a working
- * space no greater than the size of the specified range of the original
- * array. The {@link ForkJoinPool#commonPool() ForkJoin common pool} is
- * used to execute any parallel tasks.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- *
- * @since 1.8
- */
- public static void parallelSort(char[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- int n = toIndex - fromIndex, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
- else
- new ArraysParallelSortHelpers.FJChar.Sorter
- (null, a, new char[n], fromIndex, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(short[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(short[]) Arrays.sort} method. The algorithm requires a
- * working space no greater than the size of the original array. The
- * {@link ForkJoinPool#commonPool() ForkJoin common pool} is used to
- * execute any parallel tasks.
- *
- * @param a the array to be sorted
- *
- * @since 1.8
- */
- public static void parallelSort(short[] a) {
- int n = a.length, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, 0, n - 1, null, 0, 0);
- else
- new ArraysParallelSortHelpers.FJShort.Sorter
- (null, a, new short[n], 0, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
-
- /**
- * Sorts the specified range of the array into ascending numerical order.
- * The range to be sorted extends from the index {@code fromIndex},
- * inclusive, to the index {@code toIndex}, exclusive. If
- * {@code fromIndex == toIndex}, the range to be sorted is empty.
- *
- * @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(short[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(short[]) Arrays.sort} method. The algorithm requires a working
- * space no greater than the size of the specified range of the original
- * array. The {@link ForkJoinPool#commonPool() ForkJoin common pool} is
- * used to execute any parallel tasks.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- *
- * @since 1.8
- */
- public static void parallelSort(short[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- int n = toIndex - fromIndex, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
- else
- new ArraysParallelSortHelpers.FJShort.Sorter
- (null, a, new short[n], fromIndex, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(int[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(int[]) Arrays.sort} method. The algorithm requires a
- * working space no greater than the size of the original array. The
- * {@link ForkJoinPool#commonPool() ForkJoin common pool} is used to
- * execute any parallel tasks.
- *
- * @param a the array to be sorted
- *
- * @since 1.8
- */
- public static void parallelSort(int[] a) {
- int n = a.length, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, 0, n - 1, null, 0, 0);
- else
- new ArraysParallelSortHelpers.FJInt.Sorter
- (null, a, new int[n], 0, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
-
- /**
- * Sorts the specified range of the array into ascending numerical order.
- * The range to be sorted extends from the index {@code fromIndex},
- * inclusive, to the index {@code toIndex}, exclusive. If
- * {@code fromIndex == toIndex}, the range to be sorted is empty.
- *
- * @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(int[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(int[]) Arrays.sort} method. The algorithm requires a working
- * space no greater than the size of the specified range of the original
- * array. The {@link ForkJoinPool#commonPool() ForkJoin common pool} is
- * used to execute any parallel tasks.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- *
- * @since 1.8
- */
- public static void parallelSort(int[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- int n = toIndex - fromIndex, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
- else
- new ArraysParallelSortHelpers.FJInt.Sorter
- (null, a, new int[n], fromIndex, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(long[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(long[]) Arrays.sort} method. The algorithm requires a
- * working space no greater than the size of the original array. The
- * {@link ForkJoinPool#commonPool() ForkJoin common pool} is used to
- * execute any parallel tasks.
- *
- * @param a the array to be sorted
- *
- * @since 1.8
- */
- public static void parallelSort(long[] a) {
- int n = a.length, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, 0, n - 1, null, 0, 0);
- else
- new ArraysParallelSortHelpers.FJLong.Sorter
- (null, a, new long[n], 0, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
-
- /**
- * Sorts the specified range of the array into ascending numerical order.
- * The range to be sorted extends from the index {@code fromIndex},
- * inclusive, to the index {@code toIndex}, exclusive. If
- * {@code fromIndex == toIndex}, the range to be sorted is empty.
- *
- * @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(long[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(long[]) Arrays.sort} method. The algorithm requires a working
- * space no greater than the size of the specified range of the original
- * array. The {@link ForkJoinPool#commonPool() ForkJoin common pool} is
- * used to execute any parallel tasks.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- *
- * @since 1.8
- */
- public static void parallelSort(long[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- int n = toIndex - fromIndex, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
- else
- new ArraysParallelSortHelpers.FJLong.Sorter
- (null, a, new long[n], fromIndex, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * <p>The {@code <} relation does not provide a total order on all float
- * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN}
- * value compares neither less than, greater than, nor equal to any value,
- * even itself. This method uses the total order imposed by the method
- * {@link Float#compareTo}: {@code -0.0f} is treated as less than value
- * {@code 0.0f} and {@code Float.NaN} is considered greater than any
- * other value and all {@code Float.NaN} values are considered equal.
- *
- * @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(float[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(float[]) Arrays.sort} method. The algorithm requires a
- * working space no greater than the size of the original array. The
- * {@link ForkJoinPool#commonPool() ForkJoin common pool} is used to
- * execute any parallel tasks.
- *
- * @param a the array to be sorted
- *
- * @since 1.8
- */
- public static void parallelSort(float[] a) {
- int n = a.length, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, 0, n - 1, null, 0, 0);
- else
- new ArraysParallelSortHelpers.FJFloat.Sorter
- (null, a, new float[n], 0, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
-
- /**
- * Sorts the specified range of the array into ascending numerical order.
- * The range to be sorted extends from the index {@code fromIndex},
- * inclusive, to the index {@code toIndex}, exclusive. If
- * {@code fromIndex == toIndex}, the range to be sorted is empty.
- *
- * <p>The {@code <} relation does not provide a total order on all float
- * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN}
- * value compares neither less than, greater than, nor equal to any value,
- * even itself. This method uses the total order imposed by the method
- * {@link Float#compareTo}: {@code -0.0f} is treated as less than value
- * {@code 0.0f} and {@code Float.NaN} is considered greater than any
- * other value and all {@code Float.NaN} values are considered equal.
- *
- * @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(float[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(float[]) Arrays.sort} method. The algorithm requires a working
- * space no greater than the size of the specified range of the original
- * array. The {@link ForkJoinPool#commonPool() ForkJoin common pool} is
- * used to execute any parallel tasks.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- *
- * @since 1.8
- */
- public static void parallelSort(float[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- int n = toIndex - fromIndex, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
- else
- new ArraysParallelSortHelpers.FJFloat.Sorter
- (null, a, new float[n], fromIndex, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
-
- /**
- * Sorts the specified array into ascending numerical order.
- *
- * <p>The {@code <} relation does not provide a total order on all double
- * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN}
- * value compares neither less than, greater than, nor equal to any value,
- * even itself. This method uses the total order imposed by the method
- * {@link Double#compareTo}: {@code -0.0d} is treated as less than value
- * {@code 0.0d} and {@code Double.NaN} is considered greater than any
- * other value and all {@code Double.NaN} values are considered equal.
- *
- * @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(double[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(double[]) Arrays.sort} method. The algorithm requires a
- * working space no greater than the size of the original array. The
- * {@link ForkJoinPool#commonPool() ForkJoin common pool} is used to
- * execute any parallel tasks.
- *
- * @param a the array to be sorted
- *
- * @since 1.8
- */
- public static void parallelSort(double[] a) {
- int n = a.length, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, 0, n - 1, null, 0, 0);
- else
- new ArraysParallelSortHelpers.FJDouble.Sorter
- (null, a, new double[n], 0, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
-
- /**
- * Sorts the specified range of the array into ascending numerical order.
- * The range to be sorted extends from the index {@code fromIndex},
- * inclusive, to the index {@code toIndex}, exclusive. If
- * {@code fromIndex == toIndex}, the range to be sorted is empty.
- *
- * <p>The {@code <} relation does not provide a total order on all double
- * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN}
- * value compares neither less than, greater than, nor equal to any value,
- * even itself. This method uses the total order imposed by the method
- * {@link Double#compareTo}: {@code -0.0d} is treated as less than value
- * {@code 0.0d} and {@code Double.NaN} is considered greater than any
- * other value and all {@code Double.NaN} values are considered equal.
- *
- * @implNote The sorting algorithm is a parallel sort-merge that breaks the
- * array into sub-arrays that are themselves sorted and then merged. When
- * the sub-array length reaches a minimum granularity, the sub-array is
- * sorted using the appropriate {@link Arrays#sort(double[]) Arrays.sort}
- * method. If the length of the specified array is less than the minimum
- * granularity, then it is sorted using the appropriate {@link
- * Arrays#sort(double[]) Arrays.sort} method. The algorithm requires a working
- * space no greater than the size of the specified range of the original
- * array. The {@link ForkJoinPool#commonPool() ForkJoin common pool} is
- * used to execute any parallel tasks.
- *
- * @param a the array to be sorted
- * @param fromIndex the index of the first element, inclusive, to be sorted
- * @param toIndex the index of the last element, exclusive, to be sorted
- *
- * @throws IllegalArgumentException if {@code fromIndex > toIndex}
- * @throws ArrayIndexOutOfBoundsException
- * if {@code fromIndex < 0} or {@code toIndex > a.length}
- *
- * @since 1.8
- */
- public static void parallelSort(double[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);
- int n = toIndex - fromIndex, p, g;
- if (n <= MIN_ARRAY_SORT_GRAN ||
- (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
- DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
- else
- new ArraysParallelSortHelpers.FJDouble.Sorter
- (null, a, new double[n], fromIndex, n, 0,
- ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
- MIN_ARRAY_SORT_GRAN : g).invoke();
- }
+ * The minimum array length below which a parallel sorting
+ * algorithm will not further partition the sorting task. Using
+ * smaller sizes typically results in memory contention across
+ * tasks that makes parallel speedups unlikely.
+ */
+ private static final int MIN_ARRAY_SORT_GRAN = 1 << 13;
/**
* Sorts the specified array of objects into ascending order, according
@@ -2599,7 +2389,7 @@
* first array to be tested
* @param aToIndex the index (exclusive) of the last element in the
* first array to be tested
- * @param b the second array to be tested fro equality
+ * @param b the second array to be tested for equality
* @param bFromIndex the index (inclusive) of the first element in the
* second array to be tested
* @param bToIndex the index (exclusive) of the last element in the
@@ -2671,7 +2461,7 @@
* first array to be tested
* @param aToIndex the index (exclusive) of the last element in the
* first array to be tested
- * @param b the second array to be tested fro equality
+ * @param b the second array to be tested for equality
* @param bFromIndex the index (inclusive) of the first element in the
* second array to be tested
* @param bToIndex the index (exclusive) of the last element in the
@@ -2743,7 +2533,7 @@
* first array to be tested
* @param aToIndex the index (exclusive) of the last element in the
* first array to be tested
- * @param b the second array to be tested fro equality
+ * @param b the second array to be tested for equality
* @param bFromIndex the index (inclusive) of the first element in the
* second array to be tested
* @param bToIndex the index (exclusive) of the last element in the
@@ -2816,7 +2606,7 @@
* first array to be tested
* @param aToIndex the index (exclusive) of the last element in the
* first array to be tested
- * @param b the second array to be tested fro equality
+ * @param b the second array to be tested for equality
* @param bFromIndex the index (inclusive) of the first element in the
* second array to be tested
* @param bToIndex the index (exclusive) of the last element in the
@@ -2889,7 +2679,7 @@
* first array to be tested
* @param aToIndex the index (exclusive) of the last element in the
* first array to be tested
- * @param b the second array to be tested fro equality
+ * @param b the second array to be tested for equality
* @param bFromIndex the index (inclusive) of the first element in the
* second array to be tested
* @param bToIndex the index (exclusive) of the last element in the
@@ -2961,7 +2751,7 @@
* first array to be tested
* @param aToIndex the index (exclusive) of the last element in the
* first array to be tested
- * @param b the second array to be tested fro equality
+ * @param b the second array to be tested for equality
* @param bFromIndex the index (inclusive) of the first element in the
* second array to be tested
* @param bToIndex the index (exclusive) of the last element in the
@@ -3044,7 +2834,7 @@
* first array to be tested
* @param aToIndex the index (exclusive) of the last element in the
* first array to be tested
- * @param b the second array to be tested fro equality
+ * @param b the second array to be tested for equality
* @param bFromIndex the index (inclusive) of the first element in the
* second array to be tested
* @param bToIndex the index (exclusive) of the last element in the
@@ -3127,7 +2917,7 @@
* first array to be tested
* @param aToIndex the index (exclusive) of the last element in the
* first array to be tested
- * @param b the second array to be tested fro equality
+ * @param b the second array to be tested for equality
* @param bFromIndex the index (inclusive) of the first element in the
* second array to be tested
* @param bToIndex the index (exclusive) of the last element in the
@@ -3210,7 +3000,7 @@
* first array to be tested
* @param aToIndex the index (exclusive) of the last element in the
* first array to be tested
- * @param b the second array to be tested fro equality
+ * @param b the second array to be tested for equality
* @param bFromIndex the index (inclusive) of the first element in the
* second array to be tested
* @param bToIndex the index (exclusive) of the last element in the
@@ -3303,7 +3093,7 @@
* first array to be tested
* @param aToIndex the index (exclusive) of the last element in the
* first array to be tested
- * @param b the second array to be tested fro equality
+ * @param b the second array to be tested for equality
* @param bFromIndex the index (inclusive) of the first element in the
* second array to be tested
* @param bToIndex the index (exclusive) of the last element in the
--- a/src/java.base/share/classes/java/util/ArraysParallelSortHelpers.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/share/classes/java/util/ArraysParallelSortHelpers.java Wed Nov 13 17:21:31 2019 -0500
@@ -24,7 +24,6 @@
*/
package java.util;
-import java.util.concurrent.RecursiveAction;
import java.util.concurrent.CountedCompleter;
/**
@@ -36,7 +35,7 @@
* Sorter classes based mainly on CilkSort
* <A href="http://supertech.lcs.mit.edu/cilk/"> Cilk</A>:
* Basic algorithm:
- * if array size is small, just use a sequential quicksort (via Arrays.sort)
+ * if array size is small, just use a sequential sort (via Arrays.sort)
* Otherwise:
* 1. Break array in half.
* 2. For each half,
@@ -63,14 +62,10 @@
* need to keep track of the arrays, and are never themselves forked,
* so don't hold any task state.
*
- * The primitive class versions (FJByte... FJDouble) are
- * identical to each other except for type declarations.
- *
* The base sequential sorts rely on non-public versions of TimSort,
- * ComparableTimSort, and DualPivotQuicksort sort methods that accept
- * temp workspace array slices that we will have already allocated, so
- * avoids redundant allocation. (Except for DualPivotQuicksort byte[]
- * sort, that does not ever use a workspace array.)
+ * ComparableTimSort sort methods that accept temp workspace array
+ * slices that we will have already allocated, so avoids redundant
+ * allocation.
*/
/*package*/ class ArraysParallelSortHelpers {
@@ -142,7 +137,7 @@
Relay rc = new Relay(new Merger<>(fc, a, w, b+h, q,
b+u, n-u, wb+h, g, c));
new Sorter<>(rc, a, w, b+u, n-u, wb+u, g, c).fork();
- new Sorter<>(rc, a, w, b+h, q, wb+h, g, c).fork();;
+ new Sorter<>(rc, a, w, b+h, q, wb+h, g, c).fork();
Relay bc = new Relay(new Merger<>(fc, a, w, b, q,
b+q, h-q, wb, g, c));
new Sorter<>(bc, a, w, b+q, h-q, wb+q, g, c).fork();
@@ -239,799 +234,6 @@
tryComplete();
}
-
}
- } // FJObject
-
- /** byte support class */
- static final class FJByte {
- static final class Sorter extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final byte[] a, w;
- final int base, size, wbase, gran;
- Sorter(CountedCompleter<?> par, byte[] a, byte[] w, int base,
- int size, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w; this.base = base; this.size = size;
- this.wbase = wbase; this.gran = gran;
- }
- public final void compute() {
- CountedCompleter<?> s = this;
- byte[] a = this.a, w = this.w; // localize all params
- int b = this.base, n = this.size, wb = this.wbase, g = this.gran;
- while (n > g) {
- int h = n >>> 1, q = h >>> 1, u = h + q; // quartiles
- Relay fc = new Relay(new Merger(s, w, a, wb, h,
- wb+h, n-h, b, g));
- Relay rc = new Relay(new Merger(fc, a, w, b+h, q,
- b+u, n-u, wb+h, g));
- new Sorter(rc, a, w, b+u, n-u, wb+u, g).fork();
- new Sorter(rc, a, w, b+h, q, wb+h, g).fork();;
- Relay bc = new Relay(new Merger(fc, a, w, b, q,
- b+q, h-q, wb, g));
- new Sorter(bc, a, w, b+q, h-q, wb+q, g).fork();
- s = new EmptyCompleter(bc);
- n = q;
- }
- DualPivotQuicksort.sort(a, b, b + n - 1);
- s.tryComplete();
- }
- }
-
- static final class Merger extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final byte[] a, w; // main and workspace arrays
- final int lbase, lsize, rbase, rsize, wbase, gran;
- Merger(CountedCompleter<?> par, byte[] a, byte[] w,
- int lbase, int lsize, int rbase,
- int rsize, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w;
- this.lbase = lbase; this.lsize = lsize;
- this.rbase = rbase; this.rsize = rsize;
- this.wbase = wbase; this.gran = gran;
- }
-
- public final void compute() {
- byte[] a = this.a, w = this.w; // localize all params
- int lb = this.lbase, ln = this.lsize, rb = this.rbase,
- rn = this.rsize, k = this.wbase, g = this.gran;
- if (a == null || w == null || lb < 0 || rb < 0 || k < 0)
- throw new IllegalStateException(); // hoist checks
- for (int lh, rh;;) { // split larger, find point in smaller
- if (ln >= rn) {
- if (ln <= g)
- break;
- rh = rn;
- byte split = a[(lh = ln >>> 1) + lb];
- for (int lo = 0; lo < rh; ) {
- int rm = (lo + rh) >>> 1;
- if (split <= a[rm + rb])
- rh = rm;
- else
- lo = rm + 1;
- }
- }
- else {
- if (rn <= g)
- break;
- lh = ln;
- byte split = a[(rh = rn >>> 1) + rb];
- for (int lo = 0; lo < lh; ) {
- int lm = (lo + lh) >>> 1;
- if (split <= a[lm + lb])
- lh = lm;
- else
- lo = lm + 1;
- }
- }
- Merger m = new Merger(this, a, w, lb + lh, ln - lh,
- rb + rh, rn - rh,
- k + lh + rh, g);
- rn = rh;
- ln = lh;
- addToPendingCount(1);
- m.fork();
- }
-
- int lf = lb + ln, rf = rb + rn; // index bounds
- while (lb < lf && rb < rf) {
- byte t, al, ar;
- if ((al = a[lb]) <= (ar = a[rb])) {
- lb++; t = al;
- }
- else {
- rb++; t = ar;
- }
- w[k++] = t;
- }
- if (rb < rf)
- System.arraycopy(a, rb, w, k, rf - rb);
- else if (lb < lf)
- System.arraycopy(a, lb, w, k, lf - lb);
- tryComplete();
- }
- }
- } // FJByte
-
- /** char support class */
- static final class FJChar {
- static final class Sorter extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final char[] a, w;
- final int base, size, wbase, gran;
- Sorter(CountedCompleter<?> par, char[] a, char[] w, int base,
- int size, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w; this.base = base; this.size = size;
- this.wbase = wbase; this.gran = gran;
- }
- public final void compute() {
- CountedCompleter<?> s = this;
- char[] a = this.a, w = this.w; // localize all params
- int b = this.base, n = this.size, wb = this.wbase, g = this.gran;
- while (n > g) {
- int h = n >>> 1, q = h >>> 1, u = h + q; // quartiles
- Relay fc = new Relay(new Merger(s, w, a, wb, h,
- wb+h, n-h, b, g));
- Relay rc = new Relay(new Merger(fc, a, w, b+h, q,
- b+u, n-u, wb+h, g));
- new Sorter(rc, a, w, b+u, n-u, wb+u, g).fork();
- new Sorter(rc, a, w, b+h, q, wb+h, g).fork();;
- Relay bc = new Relay(new Merger(fc, a, w, b, q,
- b+q, h-q, wb, g));
- new Sorter(bc, a, w, b+q, h-q, wb+q, g).fork();
- s = new EmptyCompleter(bc);
- n = q;
- }
- DualPivotQuicksort.sort(a, b, b + n - 1, w, wb, n);
- s.tryComplete();
- }
- }
-
- static final class Merger extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final char[] a, w; // main and workspace arrays
- final int lbase, lsize, rbase, rsize, wbase, gran;
- Merger(CountedCompleter<?> par, char[] a, char[] w,
- int lbase, int lsize, int rbase,
- int rsize, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w;
- this.lbase = lbase; this.lsize = lsize;
- this.rbase = rbase; this.rsize = rsize;
- this.wbase = wbase; this.gran = gran;
- }
-
- public final void compute() {
- char[] a = this.a, w = this.w; // localize all params
- int lb = this.lbase, ln = this.lsize, rb = this.rbase,
- rn = this.rsize, k = this.wbase, g = this.gran;
- if (a == null || w == null || lb < 0 || rb < 0 || k < 0)
- throw new IllegalStateException(); // hoist checks
- for (int lh, rh;;) { // split larger, find point in smaller
- if (ln >= rn) {
- if (ln <= g)
- break;
- rh = rn;
- char split = a[(lh = ln >>> 1) + lb];
- for (int lo = 0; lo < rh; ) {
- int rm = (lo + rh) >>> 1;
- if (split <= a[rm + rb])
- rh = rm;
- else
- lo = rm + 1;
- }
- }
- else {
- if (rn <= g)
- break;
- lh = ln;
- char split = a[(rh = rn >>> 1) + rb];
- for (int lo = 0; lo < lh; ) {
- int lm = (lo + lh) >>> 1;
- if (split <= a[lm + lb])
- lh = lm;
- else
- lo = lm + 1;
- }
- }
- Merger m = new Merger(this, a, w, lb + lh, ln - lh,
- rb + rh, rn - rh,
- k + lh + rh, g);
- rn = rh;
- ln = lh;
- addToPendingCount(1);
- m.fork();
- }
-
- int lf = lb + ln, rf = rb + rn; // index bounds
- while (lb < lf && rb < rf) {
- char t, al, ar;
- if ((al = a[lb]) <= (ar = a[rb])) {
- lb++; t = al;
- }
- else {
- rb++; t = ar;
- }
- w[k++] = t;
- }
- if (rb < rf)
- System.arraycopy(a, rb, w, k, rf - rb);
- else if (lb < lf)
- System.arraycopy(a, lb, w, k, lf - lb);
- tryComplete();
- }
- }
- } // FJChar
-
- /** short support class */
- static final class FJShort {
- static final class Sorter extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final short[] a, w;
- final int base, size, wbase, gran;
- Sorter(CountedCompleter<?> par, short[] a, short[] w, int base,
- int size, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w; this.base = base; this.size = size;
- this.wbase = wbase; this.gran = gran;
- }
- public final void compute() {
- CountedCompleter<?> s = this;
- short[] a = this.a, w = this.w; // localize all params
- int b = this.base, n = this.size, wb = this.wbase, g = this.gran;
- while (n > g) {
- int h = n >>> 1, q = h >>> 1, u = h + q; // quartiles
- Relay fc = new Relay(new Merger(s, w, a, wb, h,
- wb+h, n-h, b, g));
- Relay rc = new Relay(new Merger(fc, a, w, b+h, q,
- b+u, n-u, wb+h, g));
- new Sorter(rc, a, w, b+u, n-u, wb+u, g).fork();
- new Sorter(rc, a, w, b+h, q, wb+h, g).fork();;
- Relay bc = new Relay(new Merger(fc, a, w, b, q,
- b+q, h-q, wb, g));
- new Sorter(bc, a, w, b+q, h-q, wb+q, g).fork();
- s = new EmptyCompleter(bc);
- n = q;
- }
- DualPivotQuicksort.sort(a, b, b + n - 1, w, wb, n);
- s.tryComplete();
- }
- }
-
- static final class Merger extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final short[] a, w; // main and workspace arrays
- final int lbase, lsize, rbase, rsize, wbase, gran;
- Merger(CountedCompleter<?> par, short[] a, short[] w,
- int lbase, int lsize, int rbase,
- int rsize, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w;
- this.lbase = lbase; this.lsize = lsize;
- this.rbase = rbase; this.rsize = rsize;
- this.wbase = wbase; this.gran = gran;
- }
-
- public final void compute() {
- short[] a = this.a, w = this.w; // localize all params
- int lb = this.lbase, ln = this.lsize, rb = this.rbase,
- rn = this.rsize, k = this.wbase, g = this.gran;
- if (a == null || w == null || lb < 0 || rb < 0 || k < 0)
- throw new IllegalStateException(); // hoist checks
- for (int lh, rh;;) { // split larger, find point in smaller
- if (ln >= rn) {
- if (ln <= g)
- break;
- rh = rn;
- short split = a[(lh = ln >>> 1) + lb];
- for (int lo = 0; lo < rh; ) {
- int rm = (lo + rh) >>> 1;
- if (split <= a[rm + rb])
- rh = rm;
- else
- lo = rm + 1;
- }
- }
- else {
- if (rn <= g)
- break;
- lh = ln;
- short split = a[(rh = rn >>> 1) + rb];
- for (int lo = 0; lo < lh; ) {
- int lm = (lo + lh) >>> 1;
- if (split <= a[lm + lb])
- lh = lm;
- else
- lo = lm + 1;
- }
- }
- Merger m = new Merger(this, a, w, lb + lh, ln - lh,
- rb + rh, rn - rh,
- k + lh + rh, g);
- rn = rh;
- ln = lh;
- addToPendingCount(1);
- m.fork();
- }
-
- int lf = lb + ln, rf = rb + rn; // index bounds
- while (lb < lf && rb < rf) {
- short t, al, ar;
- if ((al = a[lb]) <= (ar = a[rb])) {
- lb++; t = al;
- }
- else {
- rb++; t = ar;
- }
- w[k++] = t;
- }
- if (rb < rf)
- System.arraycopy(a, rb, w, k, rf - rb);
- else if (lb < lf)
- System.arraycopy(a, lb, w, k, lf - lb);
- tryComplete();
- }
- }
- } // FJShort
-
- /** int support class */
- static final class FJInt {
- static final class Sorter extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final int[] a, w;
- final int base, size, wbase, gran;
- Sorter(CountedCompleter<?> par, int[] a, int[] w, int base,
- int size, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w; this.base = base; this.size = size;
- this.wbase = wbase; this.gran = gran;
- }
- public final void compute() {
- CountedCompleter<?> s = this;
- int[] a = this.a, w = this.w; // localize all params
- int b = this.base, n = this.size, wb = this.wbase, g = this.gran;
- while (n > g) {
- int h = n >>> 1, q = h >>> 1, u = h + q; // quartiles
- Relay fc = new Relay(new Merger(s, w, a, wb, h,
- wb+h, n-h, b, g));
- Relay rc = new Relay(new Merger(fc, a, w, b+h, q,
- b+u, n-u, wb+h, g));
- new Sorter(rc, a, w, b+u, n-u, wb+u, g).fork();
- new Sorter(rc, a, w, b+h, q, wb+h, g).fork();;
- Relay bc = new Relay(new Merger(fc, a, w, b, q,
- b+q, h-q, wb, g));
- new Sorter(bc, a, w, b+q, h-q, wb+q, g).fork();
- s = new EmptyCompleter(bc);
- n = q;
- }
- DualPivotQuicksort.sort(a, b, b + n - 1, w, wb, n);
- s.tryComplete();
- }
- }
-
- static final class Merger extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final int[] a, w; // main and workspace arrays
- final int lbase, lsize, rbase, rsize, wbase, gran;
- Merger(CountedCompleter<?> par, int[] a, int[] w,
- int lbase, int lsize, int rbase,
- int rsize, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w;
- this.lbase = lbase; this.lsize = lsize;
- this.rbase = rbase; this.rsize = rsize;
- this.wbase = wbase; this.gran = gran;
- }
-
- public final void compute() {
- int[] a = this.a, w = this.w; // localize all params
- int lb = this.lbase, ln = this.lsize, rb = this.rbase,
- rn = this.rsize, k = this.wbase, g = this.gran;
- if (a == null || w == null || lb < 0 || rb < 0 || k < 0)
- throw new IllegalStateException(); // hoist checks
- for (int lh, rh;;) { // split larger, find point in smaller
- if (ln >= rn) {
- if (ln <= g)
- break;
- rh = rn;
- int split = a[(lh = ln >>> 1) + lb];
- for (int lo = 0; lo < rh; ) {
- int rm = (lo + rh) >>> 1;
- if (split <= a[rm + rb])
- rh = rm;
- else
- lo = rm + 1;
- }
- }
- else {
- if (rn <= g)
- break;
- lh = ln;
- int split = a[(rh = rn >>> 1) + rb];
- for (int lo = 0; lo < lh; ) {
- int lm = (lo + lh) >>> 1;
- if (split <= a[lm + lb])
- lh = lm;
- else
- lo = lm + 1;
- }
- }
- Merger m = new Merger(this, a, w, lb + lh, ln - lh,
- rb + rh, rn - rh,
- k + lh + rh, g);
- rn = rh;
- ln = lh;
- addToPendingCount(1);
- m.fork();
- }
-
- int lf = lb + ln, rf = rb + rn; // index bounds
- while (lb < lf && rb < rf) {
- int t, al, ar;
- if ((al = a[lb]) <= (ar = a[rb])) {
- lb++; t = al;
- }
- else {
- rb++; t = ar;
- }
- w[k++] = t;
- }
- if (rb < rf)
- System.arraycopy(a, rb, w, k, rf - rb);
- else if (lb < lf)
- System.arraycopy(a, lb, w, k, lf - lb);
- tryComplete();
- }
- }
- } // FJInt
-
- /** long support class */
- static final class FJLong {
- static final class Sorter extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final long[] a, w;
- final int base, size, wbase, gran;
- Sorter(CountedCompleter<?> par, long[] a, long[] w, int base,
- int size, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w; this.base = base; this.size = size;
- this.wbase = wbase; this.gran = gran;
- }
- public final void compute() {
- CountedCompleter<?> s = this;
- long[] a = this.a, w = this.w; // localize all params
- int b = this.base, n = this.size, wb = this.wbase, g = this.gran;
- while (n > g) {
- int h = n >>> 1, q = h >>> 1, u = h + q; // quartiles
- Relay fc = new Relay(new Merger(s, w, a, wb, h,
- wb+h, n-h, b, g));
- Relay rc = new Relay(new Merger(fc, a, w, b+h, q,
- b+u, n-u, wb+h, g));
- new Sorter(rc, a, w, b+u, n-u, wb+u, g).fork();
- new Sorter(rc, a, w, b+h, q, wb+h, g).fork();;
- Relay bc = new Relay(new Merger(fc, a, w, b, q,
- b+q, h-q, wb, g));
- new Sorter(bc, a, w, b+q, h-q, wb+q, g).fork();
- s = new EmptyCompleter(bc);
- n = q;
- }
- DualPivotQuicksort.sort(a, b, b + n - 1, w, wb, n);
- s.tryComplete();
- }
- }
-
- static final class Merger extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final long[] a, w; // main and workspace arrays
- final int lbase, lsize, rbase, rsize, wbase, gran;
- Merger(CountedCompleter<?> par, long[] a, long[] w,
- int lbase, int lsize, int rbase,
- int rsize, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w;
- this.lbase = lbase; this.lsize = lsize;
- this.rbase = rbase; this.rsize = rsize;
- this.wbase = wbase; this.gran = gran;
- }
-
- public final void compute() {
- long[] a = this.a, w = this.w; // localize all params
- int lb = this.lbase, ln = this.lsize, rb = this.rbase,
- rn = this.rsize, k = this.wbase, g = this.gran;
- if (a == null || w == null || lb < 0 || rb < 0 || k < 0)
- throw new IllegalStateException(); // hoist checks
- for (int lh, rh;;) { // split larger, find point in smaller
- if (ln >= rn) {
- if (ln <= g)
- break;
- rh = rn;
- long split = a[(lh = ln >>> 1) + lb];
- for (int lo = 0; lo < rh; ) {
- int rm = (lo + rh) >>> 1;
- if (split <= a[rm + rb])
- rh = rm;
- else
- lo = rm + 1;
- }
- }
- else {
- if (rn <= g)
- break;
- lh = ln;
- long split = a[(rh = rn >>> 1) + rb];
- for (int lo = 0; lo < lh; ) {
- int lm = (lo + lh) >>> 1;
- if (split <= a[lm + lb])
- lh = lm;
- else
- lo = lm + 1;
- }
- }
- Merger m = new Merger(this, a, w, lb + lh, ln - lh,
- rb + rh, rn - rh,
- k + lh + rh, g);
- rn = rh;
- ln = lh;
- addToPendingCount(1);
- m.fork();
- }
-
- int lf = lb + ln, rf = rb + rn; // index bounds
- while (lb < lf && rb < rf) {
- long t, al, ar;
- if ((al = a[lb]) <= (ar = a[rb])) {
- lb++; t = al;
- }
- else {
- rb++; t = ar;
- }
- w[k++] = t;
- }
- if (rb < rf)
- System.arraycopy(a, rb, w, k, rf - rb);
- else if (lb < lf)
- System.arraycopy(a, lb, w, k, lf - lb);
- tryComplete();
- }
- }
- } // FJLong
-
- /** float support class */
- static final class FJFloat {
- static final class Sorter extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final float[] a, w;
- final int base, size, wbase, gran;
- Sorter(CountedCompleter<?> par, float[] a, float[] w, int base,
- int size, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w; this.base = base; this.size = size;
- this.wbase = wbase; this.gran = gran;
- }
- public final void compute() {
- CountedCompleter<?> s = this;
- float[] a = this.a, w = this.w; // localize all params
- int b = this.base, n = this.size, wb = this.wbase, g = this.gran;
- while (n > g) {
- int h = n >>> 1, q = h >>> 1, u = h + q; // quartiles
- Relay fc = new Relay(new Merger(s, w, a, wb, h,
- wb+h, n-h, b, g));
- Relay rc = new Relay(new Merger(fc, a, w, b+h, q,
- b+u, n-u, wb+h, g));
- new Sorter(rc, a, w, b+u, n-u, wb+u, g).fork();
- new Sorter(rc, a, w, b+h, q, wb+h, g).fork();;
- Relay bc = new Relay(new Merger(fc, a, w, b, q,
- b+q, h-q, wb, g));
- new Sorter(bc, a, w, b+q, h-q, wb+q, g).fork();
- s = new EmptyCompleter(bc);
- n = q;
- }
- DualPivotQuicksort.sort(a, b, b + n - 1, w, wb, n);
- s.tryComplete();
- }
- }
-
- static final class Merger extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final float[] a, w; // main and workspace arrays
- final int lbase, lsize, rbase, rsize, wbase, gran;
- Merger(CountedCompleter<?> par, float[] a, float[] w,
- int lbase, int lsize, int rbase,
- int rsize, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w;
- this.lbase = lbase; this.lsize = lsize;
- this.rbase = rbase; this.rsize = rsize;
- this.wbase = wbase; this.gran = gran;
- }
-
- public final void compute() {
- float[] a = this.a, w = this.w; // localize all params
- int lb = this.lbase, ln = this.lsize, rb = this.rbase,
- rn = this.rsize, k = this.wbase, g = this.gran;
- if (a == null || w == null || lb < 0 || rb < 0 || k < 0)
- throw new IllegalStateException(); // hoist checks
- for (int lh, rh;;) { // split larger, find point in smaller
- if (ln >= rn) {
- if (ln <= g)
- break;
- rh = rn;
- float split = a[(lh = ln >>> 1) + lb];
- for (int lo = 0; lo < rh; ) {
- int rm = (lo + rh) >>> 1;
- if (split <= a[rm + rb])
- rh = rm;
- else
- lo = rm + 1;
- }
- }
- else {
- if (rn <= g)
- break;
- lh = ln;
- float split = a[(rh = rn >>> 1) + rb];
- for (int lo = 0; lo < lh; ) {
- int lm = (lo + lh) >>> 1;
- if (split <= a[lm + lb])
- lh = lm;
- else
- lo = lm + 1;
- }
- }
- Merger m = new Merger(this, a, w, lb + lh, ln - lh,
- rb + rh, rn - rh,
- k + lh + rh, g);
- rn = rh;
- ln = lh;
- addToPendingCount(1);
- m.fork();
- }
-
- int lf = lb + ln, rf = rb + rn; // index bounds
- while (lb < lf && rb < rf) {
- float t, al, ar;
- if ((al = a[lb]) <= (ar = a[rb])) {
- lb++; t = al;
- }
- else {
- rb++; t = ar;
- }
- w[k++] = t;
- }
- if (rb < rf)
- System.arraycopy(a, rb, w, k, rf - rb);
- else if (lb < lf)
- System.arraycopy(a, lb, w, k, lf - lb);
- tryComplete();
- }
- }
- } // FJFloat
-
- /** double support class */
- static final class FJDouble {
- static final class Sorter extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final double[] a, w;
- final int base, size, wbase, gran;
- Sorter(CountedCompleter<?> par, double[] a, double[] w, int base,
- int size, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w; this.base = base; this.size = size;
- this.wbase = wbase; this.gran = gran;
- }
- public final void compute() {
- CountedCompleter<?> s = this;
- double[] a = this.a, w = this.w; // localize all params
- int b = this.base, n = this.size, wb = this.wbase, g = this.gran;
- while (n > g) {
- int h = n >>> 1, q = h >>> 1, u = h + q; // quartiles
- Relay fc = new Relay(new Merger(s, w, a, wb, h,
- wb+h, n-h, b, g));
- Relay rc = new Relay(new Merger(fc, a, w, b+h, q,
- b+u, n-u, wb+h, g));
- new Sorter(rc, a, w, b+u, n-u, wb+u, g).fork();
- new Sorter(rc, a, w, b+h, q, wb+h, g).fork();;
- Relay bc = new Relay(new Merger(fc, a, w, b, q,
- b+q, h-q, wb, g));
- new Sorter(bc, a, w, b+q, h-q, wb+q, g).fork();
- s = new EmptyCompleter(bc);
- n = q;
- }
- DualPivotQuicksort.sort(a, b, b + n - 1, w, wb, n);
- s.tryComplete();
- }
- }
-
- static final class Merger extends CountedCompleter<Void> {
- @java.io.Serial
- static final long serialVersionUID = 2446542900576103244L;
- final double[] a, w; // main and workspace arrays
- final int lbase, lsize, rbase, rsize, wbase, gran;
- Merger(CountedCompleter<?> par, double[] a, double[] w,
- int lbase, int lsize, int rbase,
- int rsize, int wbase, int gran) {
- super(par);
- this.a = a; this.w = w;
- this.lbase = lbase; this.lsize = lsize;
- this.rbase = rbase; this.rsize = rsize;
- this.wbase = wbase; this.gran = gran;
- }
-
- public final void compute() {
- double[] a = this.a, w = this.w; // localize all params
- int lb = this.lbase, ln = this.lsize, rb = this.rbase,
- rn = this.rsize, k = this.wbase, g = this.gran;
- if (a == null || w == null || lb < 0 || rb < 0 || k < 0)
- throw new IllegalStateException(); // hoist checks
- for (int lh, rh;;) { // split larger, find point in smaller
- if (ln >= rn) {
- if (ln <= g)
- break;
- rh = rn;
- double split = a[(lh = ln >>> 1) + lb];
- for (int lo = 0; lo < rh; ) {
- int rm = (lo + rh) >>> 1;
- if (split <= a[rm + rb])
- rh = rm;
- else
- lo = rm + 1;
- }
- }
- else {
- if (rn <= g)
- break;
- lh = ln;
- double split = a[(rh = rn >>> 1) + rb];
- for (int lo = 0; lo < lh; ) {
- int lm = (lo + lh) >>> 1;
- if (split <= a[lm + lb])
- lh = lm;
- else
- lo = lm + 1;
- }
- }
- Merger m = new Merger(this, a, w, lb + lh, ln - lh,
- rb + rh, rn - rh,
- k + lh + rh, g);
- rn = rh;
- ln = lh;
- addToPendingCount(1);
- m.fork();
- }
-
- int lf = lb + ln, rf = rb + rn; // index bounds
- while (lb < lf && rb < rf) {
- double t, al, ar;
- if ((al = a[lb]) <= (ar = a[rb])) {
- lb++; t = al;
- }
- else {
- rb++; t = ar;
- }
- w[k++] = t;
- }
- if (rb < rf)
- System.arraycopy(a, rb, w, k, rf - rb);
- else if (lb < lf)
- System.arraycopy(a, lb, w, k, lf - lb);
- tryComplete();
- }
- }
- } // FJDouble
-
+ }
}
--- a/src/java.base/share/classes/java/util/DualPivotQuicksort.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/share/classes/java/util/DualPivotQuicksort.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2019, 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
@@ -25,24 +25,28 @@
package java.util;
+import java.util.concurrent.CountedCompleter;
+import java.util.concurrent.RecursiveTask;
+
/**
- * This class implements the Dual-Pivot Quicksort algorithm by
- * Vladimir Yaroslavskiy, Jon Bentley, and Josh Bloch. The algorithm
- * offers O(n log(n)) performance on many data sets that cause other
- * quicksorts to degrade to quadratic performance, and is typically
+ * This class implements powerful and fully optimized versions, both
+ * sequential and parallel, of the Dual-Pivot Quicksort algorithm by
+ * Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
+ * offers O(n log(n)) performance on all data sets, and is typically
* faster than traditional (one-pivot) Quicksort implementations.
*
- * All exposed methods are package-private, designed to be invoked
- * from public methods (in class Arrays) after performing any
- * necessary array bounds checks and expanding parameters into the
- * required forms.
+ * There are also additional algorithms, invoked from the Dual-Pivot
+ * Quicksort, such as mixed insertion sort, merging of runs and heap
+ * sort, counting sort and parallel merge sort.
*
* @author Vladimir Yaroslavskiy
* @author Jon Bentley
* @author Josh Bloch
+ * @author Doug Lea
*
- * @version 2011.02.11 m765.827.12i:5\7pm
- * @since 1.7
+ * @version 2018.08.18
+ *
+ * @since 1.7 * 14
*/
final class DualPivotQuicksort {
@@ -51,3131 +55,4107 @@
*/
private DualPivotQuicksort() {}
- /*
- * Tuning parameters.
+ /**
+ * Max array size to use mixed insertion sort.
+ */
+ private static final int MAX_MIXED_INSERTION_SORT_SIZE = 65;
+
+ /**
+ * Max array size to use insertion sort.
*/
+ private static final int MAX_INSERTION_SORT_SIZE = 44;
+
+ /**
+ * Min array size to perform sorting in parallel.
+ */
+ private static final int MIN_PARALLEL_SORT_SIZE = 4 << 10;
+
+ /**
+ * Min array size to try merging of runs.
+ */
+ private static final int MIN_TRY_MERGE_SIZE = 4 << 10;
/**
- * The maximum number of runs in merge sort.
+ * Min size of the first run to continue with scanning.
*/
- private static final int MAX_RUN_COUNT = 67;
+ private static final int MIN_FIRST_RUN_SIZE = 16;
+
+ /**
+ * Min factor for the first runs to continue scanning.
+ */
+ private static final int MIN_FIRST_RUNS_FACTOR = 7;
/**
- * If the length of an array to be sorted is less than this
- * constant, Quicksort is used in preference to merge sort.
+ * Max capacity of the index array for tracking runs.
+ */
+ private static final int MAX_RUN_CAPACITY = 5 << 10;
+
+ /**
+ * Min number of runs, required by parallel merging.
*/
- private static final int QUICKSORT_THRESHOLD = 286;
+ private static final int MIN_RUN_COUNT = 4;
+
+ /**
+ * Min array size to use parallel merging of parts.
+ */
+ private static final int MIN_PARALLEL_MERGE_PARTS_SIZE = 4 << 10;
/**
- * If the length of an array to be sorted is less than this
- * constant, insertion sort is used in preference to Quicksort.
+ * Min size of a byte array to use counting sort.
*/
- private static final int INSERTION_SORT_THRESHOLD = 47;
+ private static final int MIN_BYTE_COUNTING_SORT_SIZE = 64;
/**
- * If the length of a byte array to be sorted is greater than this
- * constant, counting sort is used in preference to insertion sort.
+ * Min size of a short or char array to use counting sort.
+ */
+ private static final int MIN_SHORT_OR_CHAR_COUNTING_SORT_SIZE = 1750;
+
+ /**
+ * Threshold of mixed insertion sort is incremented by this value.
*/
- private static final int COUNTING_SORT_THRESHOLD_FOR_BYTE = 29;
+ private static final int DELTA = 3 << 1;
+
+ /**
+ * Max recursive partitioning depth before using heap sort.
+ */
+ private static final int MAX_RECURSION_DEPTH = 64 * DELTA;
/**
- * If the length of a short or char array to be sorted is greater
- * than this constant, counting sort is used in preference to Quicksort.
+ * Calculates the double depth of parallel merging.
+ * Depth is negative, if tasks split before sorting.
+ *
+ * @param parallelism the parallelism level
+ * @param size the target size
+ * @return the depth of parallel merging
*/
- private static final int COUNTING_SORT_THRESHOLD_FOR_SHORT_OR_CHAR = 3200;
-
- /*
- * Sorting methods for seven primitive types.
- */
+ private static int getDepth(int parallelism, int size) {
+ int depth = 0;
+
+ while ((parallelism >>= 3) > 0 && (size >>= 2) > 0) {
+ depth -= 2;
+ }
+ return depth;
+ }
/**
- * Sorts the specified range of the array using the given
- * workspace array slice if possible for merging
+ * Sorts the specified range of the array using parallel merge
+ * sort and/or Dual-Pivot Quicksort.
+ *
+ * To balance the faster splitting and parallelism of merge sort
+ * with the faster element partitioning of Quicksort, ranges are
+ * subdivided in tiers such that, if there is enough parallelism,
+ * the four-way parallel merge is started, still ensuring enough
+ * parallelism to process the partitions.
*
* @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param work a workspace array (slice)
- * @param workBase origin of usable space in work array
- * @param workLen usable size of work array
+ * @param parallelism the parallelism level
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
*/
- static void sort(int[] a, int left, int right,
- int[] work, int workBase, int workLen) {
- // Use Quicksort on small arrays
- if (right - left < QUICKSORT_THRESHOLD) {
- sort(a, left, right, true);
- return;
+ static void sort(int[] a, int parallelism, int low, int high) {
+ int size = high - low;
+
+ if (parallelism > 1 && size > MIN_PARALLEL_SORT_SIZE) {
+ int depth = getDepth(parallelism, size >> 12);
+ int[] b = depth == 0 ? null : new int[size];
+ new Sorter(null, a, b, low, size, low, depth).invoke();
+ } else {
+ sort(null, a, 0, low, high);
}
-
- /*
- * Index run[i] is the start of i-th run
- * (ascending or descending sequence).
- */
- int[] run = new int[MAX_RUN_COUNT + 1];
- int count = 0; run[0] = left;
-
- // Check if the array is nearly sorted
- for (int k = left; k < right; run[count] = k) {
- // Equal items in the beginning of the sequence
- while (k < right && a[k] == a[k + 1])
- k++;
- if (k == right) break; // Sequence finishes with equal items
- if (a[k] < a[k + 1]) { // ascending
- while (++k <= right && a[k - 1] <= a[k]);
- } else if (a[k] > a[k + 1]) { // descending
- while (++k <= right && a[k - 1] >= a[k]);
- // Transform into an ascending sequence
- for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {
- int t = a[lo]; a[lo] = a[hi]; a[hi] = t;
- }
+ }
+
+ /**
+ * Sorts the specified array using the Dual-Pivot Quicksort and/or
+ * other sorts in special-cases, possibly with parallel partitions.
+ *
+ * @param sorter parallel context
+ * @param a the array to be sorted
+ * @param bits the combination of recursion depth and bit flag, where
+ * the right bit "0" indicates that array is the leftmost part
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ static void sort(Sorter sorter, int[] a, int bits, int low, int high) {
+ while (true) {
+ int end = high - 1, size = high - low;
+
+ /*
+ * Run mixed insertion sort on small non-leftmost parts.
+ */
+ if (size < MAX_MIXED_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
+ mixedInsertionSort(a, low, high - 3 * ((size >> 5) << 3), high);
+ return;
}
- // Merge a transformed descending sequence followed by an
- // ascending sequence
- if (run[count] > left && a[run[count]] >= a[run[count] - 1]) {
- count--;
+ /*
+ * Invoke insertion sort on small leftmost part.
+ */
+ if (size < MAX_INSERTION_SORT_SIZE) {
+ insertionSort(a, low, high);
+ return;
+ }
+
+ /*
+ * Check if the whole array or large non-leftmost
+ * parts are nearly sorted and then merge runs.
+ */
+ if ((bits == 0 || size > MIN_TRY_MERGE_SIZE && (bits & 1) > 0)
+ && tryMergeRuns(sorter, a, low, size)) {
+ return;
+ }
+
+ /*
+ * Switch to heap sort if execution
+ * time is becoming quadratic.
+ */
+ if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
+ heapSort(a, low, high);
+ return;
}
/*
- * The array is not highly structured,
- * use Quicksort instead of merge sort.
+ * Use an inexpensive approximation of the golden ratio
+ * to select five sample elements and determine pivots.
+ */
+ int step = (size >> 3) * 3 + 3;
+
+ /*
+ * Five elements around (and including) the central element
+ * will be used for pivot selection as described below. The
+ * unequal choice of spacing these elements was empirically
+ * determined to work well on a wide variety of inputs.
*/
- if (++count == MAX_RUN_COUNT) {
- sort(a, left, right, true);
- return;
+ int e1 = low + step;
+ int e5 = end - step;
+ int e3 = (e1 + e5) >>> 1;
+ int e2 = (e1 + e3) >>> 1;
+ int e4 = (e3 + e5) >>> 1;
+ int a3 = a[e3];
+
+ /*
+ * Sort these elements in place by the combination
+ * of 4-element sorting network and insertion sort.
+ *
+ * 5 ------o-----------o------------
+ * | |
+ * 4 ------|-----o-----o-----o------
+ * | | |
+ * 2 ------o-----|-----o-----o------
+ * | |
+ * 1 ------------o-----o------------
+ */
+ if (a[e5] < a[e2]) { int t = a[e5]; a[e5] = a[e2]; a[e2] = t; }
+ if (a[e4] < a[e1]) { int t = a[e4]; a[e4] = a[e1]; a[e1] = t; }
+ if (a[e5] < a[e4]) { int t = a[e5]; a[e5] = a[e4]; a[e4] = t; }
+ if (a[e2] < a[e1]) { int t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
+ if (a[e4] < a[e2]) { int t = a[e4]; a[e4] = a[e2]; a[e2] = t; }
+
+ if (a3 < a[e2]) {
+ if (a3 < a[e1]) {
+ a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = a3;
+ } else {
+ a[e3] = a[e2]; a[e2] = a3;
+ }
+ } else if (a3 > a[e4]) {
+ if (a3 > a[e5]) {
+ a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = a3;
+ } else {
+ a[e3] = a[e4]; a[e4] = a3;
+ }
}
- }
-
- // These invariants should hold true:
- // run[0] = 0
- // run[<last>] = right + 1; (terminator)
-
- if (count == 0) {
- // A single equal run
- return;
- } else if (count == 1 && run[count] > right) {
- // Either a single ascending or a transformed descending run.
- // Always check that a final run is a proper terminator, otherwise
- // we have an unterminated trailing run, to handle downstream.
- return;
- }
- right++;
- if (run[count] < right) {
- // Corner case: the final run is not a terminator. This may happen
- // if a final run is an equals run, or there is a single-element run
- // at the end. Fix up by adding a proper terminator at the end.
- // Note that we terminate with (right + 1), incremented earlier.
- run[++count] = right;
- }
-
- // Determine alternation base for merge
- byte odd = 0;
- for (int n = 1; (n <<= 1) < count; odd ^= 1);
-
- // Use or create temporary array b for merging
- int[] b; // temp array; alternates with a
- int ao, bo; // array offsets from 'left'
- int blen = right - left; // space needed for b
- if (work == null || workLen < blen || workBase + blen > work.length) {
- work = new int[blen];
- workBase = 0;
- }
- if (odd == 0) {
- System.arraycopy(a, left, work, workBase, blen);
- b = a;
- bo = 0;
- a = work;
- ao = workBase - left;
- } else {
- b = work;
- ao = 0;
- bo = workBase - left;
- }
-
- // Merging
- for (int last; count > 1; count = last) {
- for (int k = (last = 0) + 2; k <= count; k += 2) {
- int hi = run[k], mi = run[k - 1];
- for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
- if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {
- b[i + bo] = a[p++ + ao];
- } else {
- b[i + bo] = a[q++ + ao];
+
+ // Pointers
+ int lower = low; // The index of the last element of the left part
+ int upper = end; // The index of the first element of the right part
+
+ /*
+ * Partitioning with 2 pivots in case of different elements.
+ */
+ if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
+
+ /*
+ * Use the first and fifth of the five sorted elements as
+ * the pivots. These values are inexpensive approximation
+ * of tertiles. Note, that pivot1 < pivot2.
+ */
+ int pivot1 = a[e1];
+ int pivot2 = a[e5];
+
+ /*
+ * The first and the last elements to be sorted are moved
+ * to the locations formerly occupied by the pivots. When
+ * partitioning is completed, the pivots are swapped back
+ * into their final positions, and excluded from the next
+ * subsequent sorting.
+ */
+ a[e1] = a[lower];
+ a[e5] = a[upper];
+
+ /*
+ * Skip elements, which are less or greater than the pivots.
+ */
+ while (a[++lower] < pivot1);
+ while (a[--upper] > pivot2);
+
+ /*
+ * Backward 3-interval partitioning
+ *
+ * left part central part right part
+ * +------------------------------------------------------------+
+ * | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
+ * +------------------------------------------------------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Invariants:
+ *
+ * all in (low, lower] < pivot1
+ * pivot1 <= all in (k, upper) <= pivot2
+ * all in [upper, end) > pivot2
+ *
+ * Pointer k is the last index of ?-part
+ */
+ for (int unused = --lower, k = ++upper; --k > lower; ) {
+ int ak = a[k];
+
+ if (ak < pivot1) { // Move a[k] to the left side
+ while (lower < k) {
+ if (a[++lower] >= pivot1) {
+ if (a[lower] > pivot2) {
+ a[k] = a[--upper];
+ a[upper] = a[lower];
+ } else {
+ a[k] = a[lower];
+ }
+ a[lower] = ak;
+ break;
+ }
+ }
+ } else if (ak > pivot2) { // Move a[k] to the right side
+ a[k] = a[--upper];
+ a[upper] = ak;
}
}
- run[++last] = hi;
+
+ /*
+ * Swap the pivots into their final positions.
+ */
+ a[low] = a[lower]; a[lower] = pivot1;
+ a[end] = a[upper]; a[upper] = pivot2;
+
+ /*
+ * Sort non-left parts recursively (possibly in parallel),
+ * excluding known pivots.
+ */
+ if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
+ sorter.forkSorter(bits | 1, lower + 1, upper);
+ sorter.forkSorter(bits | 1, upper + 1, high);
+ } else {
+ sort(sorter, a, bits | 1, lower + 1, upper);
+ sort(sorter, a, bits | 1, upper + 1, high);
+ }
+
+ } else { // Use single pivot in case of many equal elements
+
+ /*
+ * Use the third of the five sorted elements as the pivot.
+ * This value is inexpensive approximation of the median.
+ */
+ int pivot = a[e3];
+
+ /*
+ * The first element to be sorted is moved to the
+ * location formerly occupied by the pivot. After
+ * completion of partitioning the pivot is swapped
+ * back into its final position, and excluded from
+ * the next subsequent sorting.
+ */
+ a[e3] = a[lower];
+
+ /*
+ * Traditional 3-way (Dutch National Flag) partitioning
+ *
+ * left part central part right part
+ * +------------------------------------------------------+
+ * | < pivot | ? | == pivot | > pivot |
+ * +------------------------------------------------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Invariants:
+ *
+ * all in (low, lower] < pivot
+ * all in (k, upper) == pivot
+ * all in [upper, end] > pivot
+ *
+ * Pointer k is the last index of ?-part
+ */
+ for (int k = ++upper; --k > lower; ) {
+ int ak = a[k];
+
+ if (ak != pivot) {
+ a[k] = pivot;
+
+ if (ak < pivot) { // Move a[k] to the left side
+ while (a[++lower] < pivot);
+
+ if (a[lower] > pivot) {
+ a[--upper] = a[lower];
+ }
+ a[lower] = ak;
+ } else { // ak > pivot - Move a[k] to the right side
+ a[--upper] = ak;
+ }
+ }
+ }
+
+ /*
+ * Swap the pivot into its final position.
+ */
+ a[low] = a[lower]; a[lower] = pivot;
+
+ /*
+ * Sort the right part (possibly in parallel), excluding
+ * known pivot. All elements from the central part are
+ * equal and therefore already sorted.
+ */
+ if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
+ sorter.forkSorter(bits | 1, upper, high);
+ } else {
+ sort(sorter, a, bits | 1, upper, high);
+ }
}
- if ((count & 1) != 0) {
- for (int i = right, lo = run[count - 1]; --i >= lo;
- b[i + bo] = a[i + ao]
- );
- run[++last] = right;
- }
- int[] t = a; a = b; b = t;
- int o = ao; ao = bo; bo = o;
+ high = lower; // Iterate along the left part
}
}
/**
- * Sorts the specified range of the array by Dual-Pivot Quicksort.
+ * Sorts the specified range of the array using mixed insertion sort.
+ *
+ * Mixed insertion sort is combination of simple insertion sort,
+ * pin insertion sort and pair insertion sort.
+ *
+ * In the context of Dual-Pivot Quicksort, the pivot element
+ * from the left part plays the role of sentinel, because it
+ * is less than any elements from the given part. Therefore,
+ * expensive check of the left range can be skipped on each
+ * iteration unless it is the leftmost call.
*
* @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param leftmost indicates if this part is the leftmost in the range
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param end the index of the last element for simple insertion sort
+ * @param high the index of the last element, exclusive, to be sorted
*/
- private static void sort(int[] a, int left, int right, boolean leftmost) {
- int length = right - left + 1;
-
- // Use insertion sort on tiny arrays
- if (length < INSERTION_SORT_THRESHOLD) {
- if (leftmost) {
- /*
- * Traditional (without sentinel) insertion sort,
- * optimized for server VM, is used in case of
- * the leftmost part.
- */
- for (int i = left, j = i; i < right; j = ++i) {
- int ai = a[i + 1];
- while (ai < a[j]) {
- a[j + 1] = a[j];
- if (j-- == left) {
- break;
- }
- }
- a[j + 1] = ai;
- }
- } else {
- /*
- * Skip the longest ascending sequence.
- */
- do {
- if (left >= right) {
- return;
- }
- } while (a[++left] >= a[left - 1]);
-
- /*
- * Every element from adjoining part plays the role
- * of sentinel, therefore this allows us to avoid the
- * left range check on each iteration. Moreover, we use
- * the more optimized algorithm, so called pair insertion
- * sort, which is faster (in the context of Quicksort)
- * than traditional implementation of insertion sort.
- */
- for (int k = left; ++left <= right; k = ++left) {
- int a1 = a[k], a2 = a[left];
-
- if (a1 < a2) {
- a2 = a1; a1 = a[left];
- }
- while (a1 < a[--k]) {
- a[k + 2] = a[k];
- }
- a[++k + 1] = a1;
-
- while (a2 < a[--k]) {
- a[k + 1] = a[k];
- }
- a[k + 1] = a2;
- }
- int last = a[right];
-
- while (last < a[--right]) {
- a[right + 1] = a[right];
- }
- a[right + 1] = last;
- }
- return;
- }
-
- // Inexpensive approximation of length / 7
- int seventh = (length >> 3) + (length >> 6) + 1;
-
- /*
- * Sort five evenly spaced elements around (and including) the
- * center element in the range. These elements will be used for
- * pivot selection as described below. The choice for spacing
- * these elements was empirically determined to work well on
- * a wide variety of inputs.
- */
- int e3 = (left + right) >>> 1; // The midpoint
- int e2 = e3 - seventh;
- int e1 = e2 - seventh;
- int e4 = e3 + seventh;
- int e5 = e4 + seventh;
-
- // Sort these elements using insertion sort
- if (a[e2] < a[e1]) { int t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
-
- if (a[e3] < a[e2]) { int t = a[e3]; a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- if (a[e4] < a[e3]) { int t = a[e4]; a[e4] = a[e3]; a[e3] = t;
- if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- }
- if (a[e5] < a[e4]) { int t = a[e5]; a[e5] = a[e4]; a[e4] = t;
- if (t < a[e3]) { a[e4] = a[e3]; a[e3] = t;
- if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- }
- }
-
- // Pointers
- int less = left; // The index of the first element of center part
- int great = right; // The index before the first element of right part
-
- if (a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5]) {
- /*
- * Use the second and fourth of the five sorted elements as pivots.
- * These values are inexpensive approximations of the first and
- * second terciles of the array. Note that pivot1 <= pivot2.
- */
- int pivot1 = a[e2];
- int pivot2 = a[e4];
+ private static void mixedInsertionSort(int[] a, int low, int end, int high) {
+ if (end == high) {
/*
- * The first and the last elements to be sorted are moved to the
- * locations formerly occupied by the pivots. When partitioning
- * is complete, the pivots are swapped back into their final
- * positions, and excluded from subsequent sorting.
+ * Invoke simple insertion sort on tiny array.
*/
- a[e2] = a[left];
- a[e4] = a[right];
-
- /*
- * Skip elements, which are less or greater than pivot values.
- */
- while (a[++less] < pivot1);
- while (a[--great] > pivot2);
+ for (int i; ++low < end; ) {
+ int ai = a[i = low];
+
+ while (ai < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = ai;
+ }
+ } else {
/*
- * Partitioning:
- *
- * left part center part right part
- * +--------------------------------------------------------------+
- * | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 |
- * +--------------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (left, less) < pivot1
- * pivot1 <= all in [less, k) <= pivot2
- * all in (great, right) > pivot2
+ * Start with pin insertion sort on small part.
*
- * Pointer k is the first index of ?-part.
- */
- outer:
- for (int k = less - 1; ++k <= great; ) {
- int ak = a[k];
- if (ak < pivot1) { // Move a[k] to left part
- a[k] = a[less];
- /*
- * Here and below we use "a[i] = b; i++;" instead
- * of "a[i++] = b;" due to performance issue.
- */
- a[less] = ak;
- ++less;
- } else if (ak > pivot2) { // Move a[k] to right part
- while (a[great] > pivot2) {
- if (great-- == k) {
- break outer;
- }
- }
- if (a[great] < pivot1) { // a[great] <= pivot2
- a[k] = a[less];
- a[less] = a[great];
- ++less;
- } else { // pivot1 <= a[great] <= pivot2
- a[k] = a[great];
- }
- /*
- * Here and below we use "a[i] = b; i--;" instead
- * of "a[i--] = b;" due to performance issue.
- */
- a[great] = ak;
- --great;
- }
- }
-
- // Swap pivots into their final positions
- a[left] = a[less - 1]; a[less - 1] = pivot1;
- a[right] = a[great + 1]; a[great + 1] = pivot2;
-
- // Sort left and right parts recursively, excluding known pivots
- sort(a, left, less - 2, leftmost);
- sort(a, great + 2, right, false);
-
- /*
- * If center part is too large (comprises > 4/7 of the array),
- * swap internal pivot values to ends.
+ * Pin insertion sort is extended simple insertion sort.
+ * The main idea of this sort is to put elements larger
+ * than an element called pin to the end of array (the
+ * proper area for such elements). It avoids expensive
+ * movements of these elements through the whole array.
*/
- if (less < e1 && e5 < great) {
- /*
- * Skip elements, which are equal to pivot values.
- */
- while (a[less] == pivot1) {
- ++less;
- }
-
- while (a[great] == pivot2) {
- --great;
- }
-
- /*
- * Partitioning:
- *
- * left part center part right part
- * +----------------------------------------------------------+
- * | == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 |
- * +----------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (*, less) == pivot1
- * pivot1 < all in [less, k) < pivot2
- * all in (great, *) == pivot2
- *
- * Pointer k is the first index of ?-part.
- */
- outer:
- for (int k = less - 1; ++k <= great; ) {
- int ak = a[k];
- if (ak == pivot1) { // Move a[k] to left part
- a[k] = a[less];
- a[less] = ak;
- ++less;
- } else if (ak == pivot2) { // Move a[k] to right part
- while (a[great] == pivot2) {
- if (great-- == k) {
- break outer;
- }
- }
- if (a[great] == pivot1) { // a[great] < pivot2
- a[k] = a[less];
- /*
- * Even though a[great] equals to pivot1, the
- * assignment a[less] = pivot1 may be incorrect,
- * if a[great] and pivot1 are floating-point zeros
- * of different signs. Therefore in float and
- * double sorting methods we have to use more
- * accurate assignment a[less] = a[great].
- */
- a[less] = pivot1;
- ++less;
- } else { // pivot1 < a[great] < pivot2
- a[k] = a[great];
- }
- a[great] = ak;
- --great;
+ int pin = a[end];
+
+ for (int i, p = high; ++low < end; ) {
+ int ai = a[i = low];
+
+ if (ai < a[i - 1]) { // Small element
+
+ /*
+ * Insert small element into sorted part.
+ */
+ a[i] = a[--i];
+
+ while (ai < a[--i]) {
+ a[i + 1] = a[i];
}
- }
- }
-
- // Sort center part recursively
- sort(a, less, great, false);
-
- } else { // Partitioning with one pivot
- /*
- * Use the third of the five sorted elements as pivot.
- * This value is inexpensive approximation of the median.
- */
- int pivot = a[e3];
-
- /*
- * Partitioning degenerates to the traditional 3-way
- * (or "Dutch National Flag") schema:
- *
- * left part center part right part
- * +-------------------------------------------------+
- * | < pivot | == pivot | ? | > pivot |
- * +-------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (left, less) < pivot
- * all in [less, k) == pivot
- * all in (great, right) > pivot
- *
- * Pointer k is the first index of ?-part.
- */
- for (int k = less; k <= great; ++k) {
- if (a[k] == pivot) {
- continue;
- }
- int ak = a[k];
- if (ak < pivot) { // Move a[k] to left part
- a[k] = a[less];
- a[less] = ak;
- ++less;
- } else { // a[k] > pivot - Move a[k] to right part
- while (a[great] > pivot) {
- --great;
+ a[i + 1] = ai;
+
+ } else if (p > i && ai > pin) { // Large element
+
+ /*
+ * Find element smaller than pin.
+ */
+ while (a[--p] > pin);
+
+ /*
+ * Swap it with large element.
+ */
+ if (p > i) {
+ ai = a[p];
+ a[p] = a[i];
}
- if (a[great] < pivot) { // a[great] <= pivot
- a[k] = a[less];
- a[less] = a[great];
- ++less;
- } else { // a[great] == pivot
- /*
- * Even though a[great] equals to pivot, the
- * assignment a[k] = pivot may be incorrect,
- * if a[great] and pivot are floating-point
- * zeros of different signs. Therefore in float
- * and double sorting methods we have to use
- * more accurate assignment a[k] = a[great].
- */
- a[k] = pivot;
+
+ /*
+ * Insert small element into sorted part.
+ */
+ while (ai < a[--i]) {
+ a[i + 1] = a[i];
}
- a[great] = ak;
- --great;
+ a[i + 1] = ai;
}
}
/*
- * Sort left and right parts recursively.
- * All elements from center part are equal
- * and, therefore, already sorted.
+ * Continue with pair insertion sort on remain part.
*/
- sort(a, left, less - 1, leftmost);
- sort(a, great + 1, right, false);
+ for (int i; low < high; ++low) {
+ int a1 = a[i = low], a2 = a[++low];
+
+ /*
+ * Insert two elements per iteration: at first, insert the
+ * larger element and then insert the smaller element, but
+ * from the position where the larger element was inserted.
+ */
+ if (a1 > a2) {
+
+ while (a1 < a[--i]) {
+ a[i + 2] = a[i];
+ }
+ a[++i + 1] = a1;
+
+ while (a2 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a2;
+
+ } else if (a1 < a[i - 1]) {
+
+ while (a2 < a[--i]) {
+ a[i + 2] = a[i];
+ }
+ a[++i + 1] = a2;
+
+ while (a1 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a1;
+ }
+ }
+ }
+ }
+
+ /**
+ * Sorts the specified range of the array using insertion sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void insertionSort(int[] a, int low, int high) {
+ for (int i, k = low; ++k < high; ) {
+ int ai = a[i = k];
+
+ if (ai < a[i - 1]) {
+ while (--i >= low && ai < a[i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = ai;
+ }
+ }
+ }
+
+ /**
+ * Sorts the specified range of the array using heap sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void heapSort(int[] a, int low, int high) {
+ for (int k = (low + high) >>> 1; k > low; ) {
+ pushDown(a, --k, a[k], low, high);
+ }
+ while (--high > low) {
+ int max = a[low];
+ pushDown(a, low, a[high], low, high);
+ a[high] = max;
}
}
/**
- * Sorts the specified range of the array using the given
- * workspace array slice if possible for merging
+ * Pushes specified element down during heap sort.
*
- * @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param work a workspace array (slice)
- * @param workBase origin of usable space in work array
- * @param workLen usable size of work array
+ * @param a the given array
+ * @param p the start index
+ * @param value the given element
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
*/
- static void sort(long[] a, int left, int right,
- long[] work, int workBase, int workLen) {
- // Use Quicksort on small arrays
- if (right - left < QUICKSORT_THRESHOLD) {
- sort(a, left, right, true);
- return;
- }
-
- /*
- * Index run[i] is the start of i-th run
- * (ascending or descending sequence).
- */
- int[] run = new int[MAX_RUN_COUNT + 1];
- int count = 0; run[0] = left;
-
- // Check if the array is nearly sorted
- for (int k = left; k < right; run[count] = k) {
- // Equal items in the beginning of the sequence
- while (k < right && a[k] == a[k + 1])
- k++;
- if (k == right) break; // Sequence finishes with equal items
- if (a[k] < a[k + 1]) { // ascending
- while (++k <= right && a[k - 1] <= a[k]);
- } else if (a[k] > a[k + 1]) { // descending
- while (++k <= right && a[k - 1] >= a[k]);
- // Transform into an ascending sequence
- for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {
- long t = a[lo]; a[lo] = a[hi]; a[hi] = t;
- }
+ private static void pushDown(int[] a, int p, int value, int low, int high) {
+ for (int k ;; a[p] = a[p = k]) {
+ k = (p << 1) - low + 2; // Index of the right child
+
+ if (k > high) {
+ break;
}
-
- // Merge a transformed descending sequence followed by an
- // ascending sequence
- if (run[count] > left && a[run[count]] >= a[run[count] - 1]) {
- count--;
+ if (k == high || a[k] < a[k - 1]) {
+ --k;
}
-
- /*
- * The array is not highly structured,
- * use Quicksort instead of merge sort.
- */
- if (++count == MAX_RUN_COUNT) {
- sort(a, left, right, true);
- return;
+ if (a[k] <= value) {
+ break;
}
}
-
- // These invariants should hold true:
- // run[0] = 0
- // run[<last>] = right + 1; (terminator)
-
- if (count == 0) {
- // A single equal run
- return;
- } else if (count == 1 && run[count] > right) {
- // Either a single ascending or a transformed descending run.
- // Always check that a final run is a proper terminator, otherwise
- // we have an unterminated trailing run, to handle downstream.
- return;
- }
- right++;
- if (run[count] < right) {
- // Corner case: the final run is not a terminator. This may happen
- // if a final run is an equals run, or there is a single-element run
- // at the end. Fix up by adding a proper terminator at the end.
- // Note that we terminate with (right + 1), incremented earlier.
- run[++count] = right;
- }
-
- // Determine alternation base for merge
- byte odd = 0;
- for (int n = 1; (n <<= 1) < count; odd ^= 1);
-
- // Use or create temporary array b for merging
- long[] b; // temp array; alternates with a
- int ao, bo; // array offsets from 'left'
- int blen = right - left; // space needed for b
- if (work == null || workLen < blen || workBase + blen > work.length) {
- work = new long[blen];
- workBase = 0;
- }
- if (odd == 0) {
- System.arraycopy(a, left, work, workBase, blen);
- b = a;
- bo = 0;
- a = work;
- ao = workBase - left;
- } else {
- b = work;
- ao = 0;
- bo = workBase - left;
- }
-
- // Merging
- for (int last; count > 1; count = last) {
- for (int k = (last = 0) + 2; k <= count; k += 2) {
- int hi = run[k], mi = run[k - 1];
- for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
- if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {
- b[i + bo] = a[p++ + ao];
- } else {
- b[i + bo] = a[q++ + ao];
- }
- }
- run[++last] = hi;
- }
- if ((count & 1) != 0) {
- for (int i = right, lo = run[count - 1]; --i >= lo;
- b[i + bo] = a[i + ao]
- );
- run[++last] = right;
- }
- long[] t = a; a = b; b = t;
- int o = ao; ao = bo; bo = o;
- }
+ a[p] = value;
}
/**
- * Sorts the specified range of the array by Dual-Pivot Quicksort.
+ * Tries to sort the specified range of the array.
*
+ * @param sorter parallel context
* @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param leftmost indicates if this part is the leftmost in the range
+ * @param low the index of the first element to be sorted
+ * @param size the array size
+ * @return true if finally sorted, false otherwise
*/
- private static void sort(long[] a, int left, int right, boolean leftmost) {
- int length = right - left + 1;
-
- // Use insertion sort on tiny arrays
- if (length < INSERTION_SORT_THRESHOLD) {
- if (leftmost) {
- /*
- * Traditional (without sentinel) insertion sort,
- * optimized for server VM, is used in case of
- * the leftmost part.
- */
- for (int i = left, j = i; i < right; j = ++i) {
- long ai = a[i + 1];
- while (ai < a[j]) {
- a[j + 1] = a[j];
- if (j-- == left) {
- break;
- }
- }
- a[j + 1] = ai;
- }
- } else {
- /*
- * Skip the longest ascending sequence.
- */
- do {
- if (left >= right) {
- return;
- }
- } while (a[++left] >= a[left - 1]);
-
- /*
- * Every element from adjoining part plays the role
- * of sentinel, therefore this allows us to avoid the
- * left range check on each iteration. Moreover, we use
- * the more optimized algorithm, so called pair insertion
- * sort, which is faster (in the context of Quicksort)
- * than traditional implementation of insertion sort.
- */
- for (int k = left; ++left <= right; k = ++left) {
- long a1 = a[k], a2 = a[left];
-
- if (a1 < a2) {
- a2 = a1; a1 = a[left];
- }
- while (a1 < a[--k]) {
- a[k + 2] = a[k];
- }
- a[++k + 1] = a1;
-
- while (a2 < a[--k]) {
- a[k + 1] = a[k];
- }
- a[k + 1] = a2;
- }
- long last = a[right];
-
- while (last < a[--right]) {
- a[right + 1] = a[right];
- }
- a[right + 1] = last;
- }
- return;
- }
-
- // Inexpensive approximation of length / 7
- int seventh = (length >> 3) + (length >> 6) + 1;
+ private static boolean tryMergeRuns(Sorter sorter, int[] a, int low, int size) {
/*
- * Sort five evenly spaced elements around (and including) the
- * center element in the range. These elements will be used for
- * pivot selection as described below. The choice for spacing
- * these elements was empirically determined to work well on
- * a wide variety of inputs.
+ * The run array is constructed only if initial runs are
+ * long enough to continue, run[i] then holds start index
+ * of the i-th sequence of elements in non-descending order.
*/
- int e3 = (left + right) >>> 1; // The midpoint
- int e2 = e3 - seventh;
- int e1 = e2 - seventh;
- int e4 = e3 + seventh;
- int e5 = e4 + seventh;
-
- // Sort these elements using insertion sort
- if (a[e2] < a[e1]) { long t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
-
- if (a[e3] < a[e2]) { long t = a[e3]; a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- if (a[e4] < a[e3]) { long t = a[e4]; a[e4] = a[e3]; a[e3] = t;
- if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- }
- if (a[e5] < a[e4]) { long t = a[e5]; a[e5] = a[e4]; a[e4] = t;
- if (t < a[e3]) { a[e4] = a[e3]; a[e3] = t;
- if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- }
- }
-
- // Pointers
- int less = left; // The index of the first element of center part
- int great = right; // The index before the first element of right part
-
- if (a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5]) {
- /*
- * Use the second and fourth of the five sorted elements as pivots.
- * These values are inexpensive approximations of the first and
- * second terciles of the array. Note that pivot1 <= pivot2.
- */
- long pivot1 = a[e2];
- long pivot2 = a[e4];
-
- /*
- * The first and the last elements to be sorted are moved to the
- * locations formerly occupied by the pivots. When partitioning
- * is complete, the pivots are swapped back into their final
- * positions, and excluded from subsequent sorting.
- */
- a[e2] = a[left];
- a[e4] = a[right];
-
- /*
- * Skip elements, which are less or greater than pivot values.
- */
- while (a[++less] < pivot1);
- while (a[--great] > pivot2);
+ int[] run = null;
+ int high = low + size;
+ int count = 1, last = low;
+
+ /*
+ * Identify all possible runs.
+ */
+ for (int k = low + 1; k < high; ) {
/*
- * Partitioning:
- *
- * left part center part right part
- * +--------------------------------------------------------------+
- * | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 |
- * +--------------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (left, less) < pivot1
- * pivot1 <= all in [less, k) <= pivot2
- * all in (great, right) > pivot2
- *
- * Pointer k is the first index of ?-part.
+ * Find the end index of the current run.
*/
- outer:
- for (int k = less - 1; ++k <= great; ) {
- long ak = a[k];
- if (ak < pivot1) { // Move a[k] to left part
- a[k] = a[less];
- /*
- * Here and below we use "a[i] = b; i++;" instead
- * of "a[i++] = b;" due to performance issue.
- */
- a[less] = ak;
- ++less;
- } else if (ak > pivot2) { // Move a[k] to right part
- while (a[great] > pivot2) {
- if (great-- == k) {
- break outer;
- }
- }
- if (a[great] < pivot1) { // a[great] <= pivot2
- a[k] = a[less];
- a[less] = a[great];
- ++less;
- } else { // pivot1 <= a[great] <= pivot2
- a[k] = a[great];
- }
- /*
- * Here and below we use "a[i] = b; i--;" instead
- * of "a[i--] = b;" due to performance issue.
- */
- a[great] = ak;
- --great;
- }
- }
-
- // Swap pivots into their final positions
- a[left] = a[less - 1]; a[less - 1] = pivot1;
- a[right] = a[great + 1]; a[great + 1] = pivot2;
-
- // Sort left and right parts recursively, excluding known pivots
- sort(a, left, less - 2, leftmost);
- sort(a, great + 2, right, false);
-
- /*
- * If center part is too large (comprises > 4/7 of the array),
- * swap internal pivot values to ends.
- */
- if (less < e1 && e5 < great) {
- /*
- * Skip elements, which are equal to pivot values.
- */
- while (a[less] == pivot1) {
- ++less;
- }
-
- while (a[great] == pivot2) {
- --great;
+ if (a[k - 1] < a[k]) {
+
+ // Identify ascending sequence
+ while (++k < high && a[k - 1] <= a[k]);
+
+ } else if (a[k - 1] > a[k]) {
+
+ // Identify descending sequence
+ while (++k < high && a[k - 1] >= a[k]);
+
+ // Reverse into ascending order
+ for (int i = last - 1, j = k; ++i < --j && a[i] > a[j]; ) {
+ int ai = a[i]; a[i] = a[j]; a[j] = ai;
}
-
- /*
- * Partitioning:
- *
- * left part center part right part
- * +----------------------------------------------------------+
- * | == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 |
- * +----------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (*, less) == pivot1
- * pivot1 < all in [less, k) < pivot2
- * all in (great, *) == pivot2
- *
- * Pointer k is the first index of ?-part.
- */
- outer:
- for (int k = less - 1; ++k <= great; ) {
- long ak = a[k];
- if (ak == pivot1) { // Move a[k] to left part
- a[k] = a[less];
- a[less] = ak;
- ++less;
- } else if (ak == pivot2) { // Move a[k] to right part
- while (a[great] == pivot2) {
- if (great-- == k) {
- break outer;
- }
- }
- if (a[great] == pivot1) { // a[great] < pivot2
- a[k] = a[less];
- /*
- * Even though a[great] equals to pivot1, the
- * assignment a[less] = pivot1 may be incorrect,
- * if a[great] and pivot1 are floating-point zeros
- * of different signs. Therefore in float and
- * double sorting methods we have to use more
- * accurate assignment a[less] = a[great].
- */
- a[less] = pivot1;
- ++less;
- } else { // pivot1 < a[great] < pivot2
- a[k] = a[great];
- }
- a[great] = ak;
- --great;
- }
- }
- }
-
- // Sort center part recursively
- sort(a, less, great, false);
-
- } else { // Partitioning with one pivot
- /*
- * Use the third of the five sorted elements as pivot.
- * This value is inexpensive approximation of the median.
- */
- long pivot = a[e3];
-
- /*
- * Partitioning degenerates to the traditional 3-way
- * (or "Dutch National Flag") schema:
- *
- * left part center part right part
- * +-------------------------------------------------+
- * | < pivot | == pivot | ? | > pivot |
- * +-------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (left, less) < pivot
- * all in [less, k) == pivot
- * all in (great, right) > pivot
- *
- * Pointer k is the first index of ?-part.
- */
- for (int k = less; k <= great; ++k) {
- if (a[k] == pivot) {
+ } else { // Identify constant sequence
+ for (int ak = a[k]; ++k < high && ak == a[k]; );
+
+ if (k < high) {
continue;
}
- long ak = a[k];
- if (ak < pivot) { // Move a[k] to left part
- a[k] = a[less];
- a[less] = ak;
- ++less;
- } else { // a[k] > pivot - Move a[k] to right part
- while (a[great] > pivot) {
- --great;
- }
- if (a[great] < pivot) { // a[great] <= pivot
- a[k] = a[less];
- a[less] = a[great];
- ++less;
- } else { // a[great] == pivot
- /*
- * Even though a[great] equals to pivot, the
- * assignment a[k] = pivot may be incorrect,
- * if a[great] and pivot are floating-point
- * zeros of different signs. Therefore in float
- * and double sorting methods we have to use
- * more accurate assignment a[k] = a[great].
- */
- a[k] = pivot;
- }
- a[great] = ak;
- --great;
- }
}
/*
- * Sort left and right parts recursively.
- * All elements from center part are equal
- * and, therefore, already sorted.
+ * Check special cases.
*/
- sort(a, left, less - 1, leftmost);
- sort(a, great + 1, right, false);
+ if (run == null) {
+ if (k == high) {
+
+ /*
+ * The array is monotonous sequence,
+ * and therefore already sorted.
+ */
+ return true;
+ }
+
+ if (k - low < MIN_FIRST_RUN_SIZE) {
+
+ /*
+ * The first run is too small
+ * to proceed with scanning.
+ */
+ return false;
+ }
+
+ run = new int[((size >> 10) | 0x7F) & 0x3FF];
+ run[0] = low;
+
+ } else if (a[last - 1] > a[last]) {
+
+ if (count > (k - low) >> MIN_FIRST_RUNS_FACTOR) {
+
+ /*
+ * The first runs are not long
+ * enough to continue scanning.
+ */
+ return false;
+ }
+
+ if (++count == MAX_RUN_CAPACITY) {
+
+ /*
+ * Array is not highly structured.
+ */
+ return false;
+ }
+
+ if (count == run.length) {
+
+ /*
+ * Increase capacity of index array.
+ */
+ run = Arrays.copyOf(run, count << 1);
+ }
+ }
+ run[count] = (last = k);
+ }
+
+ /*
+ * Merge runs of highly structured array.
+ */
+ if (count > 1) {
+ int[] b; int offset = low;
+
+ if (sorter == null || (b = (int[]) sorter.b) == null) {
+ b = new int[size];
+ } else {
+ offset = sorter.offset;
+ }
+ mergeRuns(a, b, offset, 1, sorter != null, run, 0, count);
+ }
+ return true;
+ }
+
+ /**
+ * Merges the specified runs.
+ *
+ * @param a the source array
+ * @param b the temporary buffer used in merging
+ * @param offset the start index in the source, inclusive
+ * @param aim specifies merging: to source ( > 0), buffer ( < 0) or any ( == 0)
+ * @param parallel indicates whether merging is performed in parallel
+ * @param run the start indexes of the runs, inclusive
+ * @param lo the start index of the first run, inclusive
+ * @param hi the start index of the last run, inclusive
+ * @return the destination where runs are merged
+ */
+ private static int[] mergeRuns(int[] a, int[] b, int offset,
+ int aim, boolean parallel, int[] run, int lo, int hi) {
+
+ if (hi - lo == 1) {
+ if (aim >= 0) {
+ return a;
+ }
+ for (int i = run[hi], j = i - offset, low = run[lo]; i > low;
+ b[--j] = a[--i]
+ );
+ return b;
+ }
+
+ /*
+ * Split into approximately equal parts.
+ */
+ int mi = lo, rmi = (run[lo] + run[hi]) >>> 1;
+ while (run[++mi + 1] <= rmi);
+
+ /*
+ * Merge the left and right parts.
+ */
+ int[] a1, a2;
+
+ if (parallel && hi - lo > MIN_RUN_COUNT) {
+ RunMerger merger = new RunMerger(a, b, offset, 0, run, mi, hi).forkMe();
+ a1 = mergeRuns(a, b, offset, -aim, true, run, lo, mi);
+ a2 = (int[]) merger.getDestination();
+ } else {
+ a1 = mergeRuns(a, b, offset, -aim, false, run, lo, mi);
+ a2 = mergeRuns(a, b, offset, 0, false, run, mi, hi);
+ }
+
+ int[] dst = a1 == a ? b : a;
+
+ int k = a1 == a ? run[lo] - offset : run[lo];
+ int lo1 = a1 == b ? run[lo] - offset : run[lo];
+ int hi1 = a1 == b ? run[mi] - offset : run[mi];
+ int lo2 = a2 == b ? run[mi] - offset : run[mi];
+ int hi2 = a2 == b ? run[hi] - offset : run[hi];
+
+ if (parallel) {
+ new Merger(null, dst, k, a1, lo1, hi1, a2, lo2, hi2).invoke();
+ } else {
+ mergeParts(null, dst, k, a1, lo1, hi1, a2, lo2, hi2);
+ }
+ return dst;
+ }
+
+ /**
+ * Merges the sorted parts.
+ *
+ * @param merger parallel context
+ * @param dst the destination where parts are merged
+ * @param k the start index of the destination, inclusive
+ * @param a1 the first part
+ * @param lo1 the start index of the first part, inclusive
+ * @param hi1 the end index of the first part, exclusive
+ * @param a2 the second part
+ * @param lo2 the start index of the second part, inclusive
+ * @param hi2 the end index of the second part, exclusive
+ */
+ private static void mergeParts(Merger merger, int[] dst, int k,
+ int[] a1, int lo1, int hi1, int[] a2, int lo2, int hi2) {
+
+ if (merger != null && a1 == a2) {
+
+ while (true) {
+
+ /*
+ * The first part must be larger.
+ */
+ if (hi1 - lo1 < hi2 - lo2) {
+ int lo = lo1; lo1 = lo2; lo2 = lo;
+ int hi = hi1; hi1 = hi2; hi2 = hi;
+ }
+
+ /*
+ * Small parts will be merged sequentially.
+ */
+ if (hi1 - lo1 < MIN_PARALLEL_MERGE_PARTS_SIZE) {
+ break;
+ }
+
+ /*
+ * Find the median of the larger part.
+ */
+ int mi1 = (lo1 + hi1) >>> 1;
+ int key = a1[mi1];
+ int mi2 = hi2;
+
+ /*
+ * Partition the smaller part.
+ */
+ for (int loo = lo2; loo < mi2; ) {
+ int t = (loo + mi2) >>> 1;
+
+ if (key > a2[t]) {
+ loo = t + 1;
+ } else {
+ mi2 = t;
+ }
+ }
+
+ int d = mi2 - lo2 + mi1 - lo1;
+
+ /*
+ * Merge the right sub-parts in parallel.
+ */
+ merger.forkMerger(dst, k + d, a1, mi1, hi1, a2, mi2, hi2);
+
+ /*
+ * Process the sub-left parts.
+ */
+ hi1 = mi1;
+ hi2 = mi2;
+ }
+ }
+
+ /*
+ * Merge small parts sequentially.
+ */
+ while (lo1 < hi1 && lo2 < hi2) {
+ dst[k++] = a1[lo1] < a2[lo2] ? a1[lo1++] : a2[lo2++];
+ }
+ if (dst != a1 || k < lo1) {
+ while (lo1 < hi1) {
+ dst[k++] = a1[lo1++];
+ }
+ }
+ if (dst != a2 || k < lo2) {
+ while (lo2 < hi2) {
+ dst[k++] = a2[lo2++];
+ }
+ }
+ }
+
+// [long]
+
+ /**
+ * Sorts the specified range of the array using parallel merge
+ * sort and/or Dual-Pivot Quicksort.
+ *
+ * To balance the faster splitting and parallelism of merge sort
+ * with the faster element partitioning of Quicksort, ranges are
+ * subdivided in tiers such that, if there is enough parallelism,
+ * the four-way parallel merge is started, still ensuring enough
+ * parallelism to process the partitions.
+ *
+ * @param a the array to be sorted
+ * @param parallelism the parallelism level
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ static void sort(long[] a, int parallelism, int low, int high) {
+ int size = high - low;
+
+ if (parallelism > 1 && size > MIN_PARALLEL_SORT_SIZE) {
+ int depth = getDepth(parallelism, size >> 12);
+ long[] b = depth == 0 ? null : new long[size];
+ new Sorter(null, a, b, low, size, low, depth).invoke();
+ } else {
+ sort(null, a, 0, low, high);
}
}
/**
- * Sorts the specified range of the array using the given
- * workspace array slice if possible for merging
+ * Sorts the specified array using the Dual-Pivot Quicksort and/or
+ * other sorts in special-cases, possibly with parallel partitions.
*
- * @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param work a workspace array (slice)
- * @param workBase origin of usable space in work array
- * @param workLen usable size of work array
- */
- static void sort(short[] a, int left, int right,
- short[] work, int workBase, int workLen) {
- // Use counting sort on large arrays
- if (right - left > COUNTING_SORT_THRESHOLD_FOR_SHORT_OR_CHAR) {
- int[] count = new int[NUM_SHORT_VALUES];
-
- for (int i = left - 1; ++i <= right;
- count[a[i] - Short.MIN_VALUE]++
- );
- for (int i = NUM_SHORT_VALUES, k = right + 1; k > left; ) {
- while (count[--i] == 0);
- short value = (short) (i + Short.MIN_VALUE);
- int s = count[i];
-
- do {
- a[--k] = value;
- } while (--s > 0);
- }
- } else { // Use Dual-Pivot Quicksort on small arrays
- doSort(a, left, right, work, workBase, workLen);
- }
- }
-
- /** The number of distinct short values. */
- private static final int NUM_SHORT_VALUES = 1 << 16;
-
- /**
- * Sorts the specified range of the array.
- *
+ * @param sorter parallel context
* @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param work a workspace array (slice)
- * @param workBase origin of usable space in work array
- * @param workLen usable size of work array
+ * @param bits the combination of recursion depth and bit flag, where
+ * the right bit "0" indicates that array is the leftmost part
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
*/
- private static void doSort(short[] a, int left, int right,
- short[] work, int workBase, int workLen) {
- // Use Quicksort on small arrays
- if (right - left < QUICKSORT_THRESHOLD) {
- sort(a, left, right, true);
- return;
- }
-
- /*
- * Index run[i] is the start of i-th run
- * (ascending or descending sequence).
- */
- int[] run = new int[MAX_RUN_COUNT + 1];
- int count = 0; run[0] = left;
-
- // Check if the array is nearly sorted
- for (int k = left; k < right; run[count] = k) {
- // Equal items in the beginning of the sequence
- while (k < right && a[k] == a[k + 1])
- k++;
- if (k == right) break; // Sequence finishes with equal items
- if (a[k] < a[k + 1]) { // ascending
- while (++k <= right && a[k - 1] <= a[k]);
- } else if (a[k] > a[k + 1]) { // descending
- while (++k <= right && a[k - 1] >= a[k]);
- // Transform into an ascending sequence
- for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {
- short t = a[lo]; a[lo] = a[hi]; a[hi] = t;
- }
+ static void sort(Sorter sorter, long[] a, int bits, int low, int high) {
+ while (true) {
+ int end = high - 1, size = high - low;
+
+ /*
+ * Run mixed insertion sort on small non-leftmost parts.
+ */
+ if (size < MAX_MIXED_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
+ mixedInsertionSort(a, low, high - 3 * ((size >> 5) << 3), high);
+ return;
}
- // Merge a transformed descending sequence followed by an
- // ascending sequence
- if (run[count] > left && a[run[count]] >= a[run[count] - 1]) {
- count--;
+ /*
+ * Invoke insertion sort on small leftmost part.
+ */
+ if (size < MAX_INSERTION_SORT_SIZE) {
+ insertionSort(a, low, high);
+ return;
+ }
+
+ /*
+ * Check if the whole array or large non-leftmost
+ * parts are nearly sorted and then merge runs.
+ */
+ if ((bits == 0 || size > MIN_TRY_MERGE_SIZE && (bits & 1) > 0)
+ && tryMergeRuns(sorter, a, low, size)) {
+ return;
+ }
+
+ /*
+ * Switch to heap sort if execution
+ * time is becoming quadratic.
+ */
+ if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
+ heapSort(a, low, high);
+ return;
}
/*
- * The array is not highly structured,
- * use Quicksort instead of merge sort.
+ * Use an inexpensive approximation of the golden ratio
+ * to select five sample elements and determine pivots.
+ */
+ int step = (size >> 3) * 3 + 3;
+
+ /*
+ * Five elements around (and including) the central element
+ * will be used for pivot selection as described below. The
+ * unequal choice of spacing these elements was empirically
+ * determined to work well on a wide variety of inputs.
*/
- if (++count == MAX_RUN_COUNT) {
- sort(a, left, right, true);
- return;
+ int e1 = low + step;
+ int e5 = end - step;
+ int e3 = (e1 + e5) >>> 1;
+ int e2 = (e1 + e3) >>> 1;
+ int e4 = (e3 + e5) >>> 1;
+ long a3 = a[e3];
+
+ /*
+ * Sort these elements in place by the combination
+ * of 4-element sorting network and insertion sort.
+ *
+ * 5 ------o-----------o------------
+ * | |
+ * 4 ------|-----o-----o-----o------
+ * | | |
+ * 2 ------o-----|-----o-----o------
+ * | |
+ * 1 ------------o-----o------------
+ */
+ if (a[e5] < a[e2]) { long t = a[e5]; a[e5] = a[e2]; a[e2] = t; }
+ if (a[e4] < a[e1]) { long t = a[e4]; a[e4] = a[e1]; a[e1] = t; }
+ if (a[e5] < a[e4]) { long t = a[e5]; a[e5] = a[e4]; a[e4] = t; }
+ if (a[e2] < a[e1]) { long t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
+ if (a[e4] < a[e2]) { long t = a[e4]; a[e4] = a[e2]; a[e2] = t; }
+
+ if (a3 < a[e2]) {
+ if (a3 < a[e1]) {
+ a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = a3;
+ } else {
+ a[e3] = a[e2]; a[e2] = a3;
+ }
+ } else if (a3 > a[e4]) {
+ if (a3 > a[e5]) {
+ a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = a3;
+ } else {
+ a[e3] = a[e4]; a[e4] = a3;
+ }
}
- }
-
- // These invariants should hold true:
- // run[0] = 0
- // run[<last>] = right + 1; (terminator)
-
- if (count == 0) {
- // A single equal run
- return;
- } else if (count == 1 && run[count] > right) {
- // Either a single ascending or a transformed descending run.
- // Always check that a final run is a proper terminator, otherwise
- // we have an unterminated trailing run, to handle downstream.
- return;
- }
- right++;
- if (run[count] < right) {
- // Corner case: the final run is not a terminator. This may happen
- // if a final run is an equals run, or there is a single-element run
- // at the end. Fix up by adding a proper terminator at the end.
- // Note that we terminate with (right + 1), incremented earlier.
- run[++count] = right;
- }
-
- // Determine alternation base for merge
- byte odd = 0;
- for (int n = 1; (n <<= 1) < count; odd ^= 1);
-
- // Use or create temporary array b for merging
- short[] b; // temp array; alternates with a
- int ao, bo; // array offsets from 'left'
- int blen = right - left; // space needed for b
- if (work == null || workLen < blen || workBase + blen > work.length) {
- work = new short[blen];
- workBase = 0;
- }
- if (odd == 0) {
- System.arraycopy(a, left, work, workBase, blen);
- b = a;
- bo = 0;
- a = work;
- ao = workBase - left;
- } else {
- b = work;
- ao = 0;
- bo = workBase - left;
- }
-
- // Merging
- for (int last; count > 1; count = last) {
- for (int k = (last = 0) + 2; k <= count; k += 2) {
- int hi = run[k], mi = run[k - 1];
- for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
- if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {
- b[i + bo] = a[p++ + ao];
- } else {
- b[i + bo] = a[q++ + ao];
+
+ // Pointers
+ int lower = low; // The index of the last element of the left part
+ int upper = end; // The index of the first element of the right part
+
+ /*
+ * Partitioning with 2 pivots in case of different elements.
+ */
+ if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
+
+ /*
+ * Use the first and fifth of the five sorted elements as
+ * the pivots. These values are inexpensive approximation
+ * of tertiles. Note, that pivot1 < pivot2.
+ */
+ long pivot1 = a[e1];
+ long pivot2 = a[e5];
+
+ /*
+ * The first and the last elements to be sorted are moved
+ * to the locations formerly occupied by the pivots. When
+ * partitioning is completed, the pivots are swapped back
+ * into their final positions, and excluded from the next
+ * subsequent sorting.
+ */
+ a[e1] = a[lower];
+ a[e5] = a[upper];
+
+ /*
+ * Skip elements, which are less or greater than the pivots.
+ */
+ while (a[++lower] < pivot1);
+ while (a[--upper] > pivot2);
+
+ /*
+ * Backward 3-interval partitioning
+ *
+ * left part central part right part
+ * +------------------------------------------------------------+
+ * | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
+ * +------------------------------------------------------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Invariants:
+ *
+ * all in (low, lower] < pivot1
+ * pivot1 <= all in (k, upper) <= pivot2
+ * all in [upper, end) > pivot2
+ *
+ * Pointer k is the last index of ?-part
+ */
+ for (int unused = --lower, k = ++upper; --k > lower; ) {
+ long ak = a[k];
+
+ if (ak < pivot1) { // Move a[k] to the left side
+ while (lower < k) {
+ if (a[++lower] >= pivot1) {
+ if (a[lower] > pivot2) {
+ a[k] = a[--upper];
+ a[upper] = a[lower];
+ } else {
+ a[k] = a[lower];
+ }
+ a[lower] = ak;
+ break;
+ }
+ }
+ } else if (ak > pivot2) { // Move a[k] to the right side
+ a[k] = a[--upper];
+ a[upper] = ak;
}
}
- run[++last] = hi;
+
+ /*
+ * Swap the pivots into their final positions.
+ */
+ a[low] = a[lower]; a[lower] = pivot1;
+ a[end] = a[upper]; a[upper] = pivot2;
+
+ /*
+ * Sort non-left parts recursively (possibly in parallel),
+ * excluding known pivots.
+ */
+ if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
+ sorter.forkSorter(bits | 1, lower + 1, upper);
+ sorter.forkSorter(bits | 1, upper + 1, high);
+ } else {
+ sort(sorter, a, bits | 1, lower + 1, upper);
+ sort(sorter, a, bits | 1, upper + 1, high);
+ }
+
+ } else { // Use single pivot in case of many equal elements
+
+ /*
+ * Use the third of the five sorted elements as the pivot.
+ * This value is inexpensive approximation of the median.
+ */
+ long pivot = a[e3];
+
+ /*
+ * The first element to be sorted is moved to the
+ * location formerly occupied by the pivot. After
+ * completion of partitioning the pivot is swapped
+ * back into its final position, and excluded from
+ * the next subsequent sorting.
+ */
+ a[e3] = a[lower];
+
+ /*
+ * Traditional 3-way (Dutch National Flag) partitioning
+ *
+ * left part central part right part
+ * +------------------------------------------------------+
+ * | < pivot | ? | == pivot | > pivot |
+ * +------------------------------------------------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Invariants:
+ *
+ * all in (low, lower] < pivot
+ * all in (k, upper) == pivot
+ * all in [upper, end] > pivot
+ *
+ * Pointer k is the last index of ?-part
+ */
+ for (int k = ++upper; --k > lower; ) {
+ long ak = a[k];
+
+ if (ak != pivot) {
+ a[k] = pivot;
+
+ if (ak < pivot) { // Move a[k] to the left side
+ while (a[++lower] < pivot);
+
+ if (a[lower] > pivot) {
+ a[--upper] = a[lower];
+ }
+ a[lower] = ak;
+ } else { // ak > pivot - Move a[k] to the right side
+ a[--upper] = ak;
+ }
+ }
+ }
+
+ /*
+ * Swap the pivot into its final position.
+ */
+ a[low] = a[lower]; a[lower] = pivot;
+
+ /*
+ * Sort the right part (possibly in parallel), excluding
+ * known pivot. All elements from the central part are
+ * equal and therefore already sorted.
+ */
+ if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
+ sorter.forkSorter(bits | 1, upper, high);
+ } else {
+ sort(sorter, a, bits | 1, upper, high);
+ }
}
- if ((count & 1) != 0) {
- for (int i = right, lo = run[count - 1]; --i >= lo;
- b[i + bo] = a[i + ao]
- );
- run[++last] = right;
- }
- short[] t = a; a = b; b = t;
- int o = ao; ao = bo; bo = o;
+ high = lower; // Iterate along the left part
}
}
/**
- * Sorts the specified range of the array by Dual-Pivot Quicksort.
+ * Sorts the specified range of the array using mixed insertion sort.
+ *
+ * Mixed insertion sort is combination of simple insertion sort,
+ * pin insertion sort and pair insertion sort.
+ *
+ * In the context of Dual-Pivot Quicksort, the pivot element
+ * from the left part plays the role of sentinel, because it
+ * is less than any elements from the given part. Therefore,
+ * expensive check of the left range can be skipped on each
+ * iteration unless it is the leftmost call.
*
* @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param leftmost indicates if this part is the leftmost in the range
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param end the index of the last element for simple insertion sort
+ * @param high the index of the last element, exclusive, to be sorted
*/
- private static void sort(short[] a, int left, int right, boolean leftmost) {
- int length = right - left + 1;
-
- // Use insertion sort on tiny arrays
- if (length < INSERTION_SORT_THRESHOLD) {
- if (leftmost) {
- /*
- * Traditional (without sentinel) insertion sort,
- * optimized for server VM, is used in case of
- * the leftmost part.
- */
- for (int i = left, j = i; i < right; j = ++i) {
- short ai = a[i + 1];
- while (ai < a[j]) {
- a[j + 1] = a[j];
- if (j-- == left) {
- break;
- }
- }
- a[j + 1] = ai;
- }
- } else {
- /*
- * Skip the longest ascending sequence.
- */
- do {
- if (left >= right) {
- return;
- }
- } while (a[++left] >= a[left - 1]);
-
- /*
- * Every element from adjoining part plays the role
- * of sentinel, therefore this allows us to avoid the
- * left range check on each iteration. Moreover, we use
- * the more optimized algorithm, so called pair insertion
- * sort, which is faster (in the context of Quicksort)
- * than traditional implementation of insertion sort.
- */
- for (int k = left; ++left <= right; k = ++left) {
- short a1 = a[k], a2 = a[left];
-
- if (a1 < a2) {
- a2 = a1; a1 = a[left];
- }
- while (a1 < a[--k]) {
- a[k + 2] = a[k];
- }
- a[++k + 1] = a1;
-
- while (a2 < a[--k]) {
- a[k + 1] = a[k];
- }
- a[k + 1] = a2;
- }
- short last = a[right];
-
- while (last < a[--right]) {
- a[right + 1] = a[right];
- }
- a[right + 1] = last;
- }
- return;
- }
-
- // Inexpensive approximation of length / 7
- int seventh = (length >> 3) + (length >> 6) + 1;
-
- /*
- * Sort five evenly spaced elements around (and including) the
- * center element in the range. These elements will be used for
- * pivot selection as described below. The choice for spacing
- * these elements was empirically determined to work well on
- * a wide variety of inputs.
- */
- int e3 = (left + right) >>> 1; // The midpoint
- int e2 = e3 - seventh;
- int e1 = e2 - seventh;
- int e4 = e3 + seventh;
- int e5 = e4 + seventh;
-
- // Sort these elements using insertion sort
- if (a[e2] < a[e1]) { short t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
-
- if (a[e3] < a[e2]) { short t = a[e3]; a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- if (a[e4] < a[e3]) { short t = a[e4]; a[e4] = a[e3]; a[e3] = t;
- if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- }
- if (a[e5] < a[e4]) { short t = a[e5]; a[e5] = a[e4]; a[e4] = t;
- if (t < a[e3]) { a[e4] = a[e3]; a[e3] = t;
- if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- }
- }
-
- // Pointers
- int less = left; // The index of the first element of center part
- int great = right; // The index before the first element of right part
-
- if (a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5]) {
- /*
- * Use the second and fourth of the five sorted elements as pivots.
- * These values are inexpensive approximations of the first and
- * second terciles of the array. Note that pivot1 <= pivot2.
- */
- short pivot1 = a[e2];
- short pivot2 = a[e4];
+ private static void mixedInsertionSort(long[] a, int low, int end, int high) {
+ if (end == high) {
/*
- * The first and the last elements to be sorted are moved to the
- * locations formerly occupied by the pivots. When partitioning
- * is complete, the pivots are swapped back into their final
- * positions, and excluded from subsequent sorting.
+ * Invoke simple insertion sort on tiny array.
*/
- a[e2] = a[left];
- a[e4] = a[right];
-
- /*
- * Skip elements, which are less or greater than pivot values.
- */
- while (a[++less] < pivot1);
- while (a[--great] > pivot2);
+ for (int i; ++low < end; ) {
+ long ai = a[i = low];
+
+ while (ai < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = ai;
+ }
+ } else {
/*
- * Partitioning:
- *
- * left part center part right part
- * +--------------------------------------------------------------+
- * | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 |
- * +--------------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (left, less) < pivot1
- * pivot1 <= all in [less, k) <= pivot2
- * all in (great, right) > pivot2
+ * Start with pin insertion sort on small part.
*
- * Pointer k is the first index of ?-part.
- */
- outer:
- for (int k = less - 1; ++k <= great; ) {
- short ak = a[k];
- if (ak < pivot1) { // Move a[k] to left part
- a[k] = a[less];
- /*
- * Here and below we use "a[i] = b; i++;" instead
- * of "a[i++] = b;" due to performance issue.
- */
- a[less] = ak;
- ++less;
- } else if (ak > pivot2) { // Move a[k] to right part
- while (a[great] > pivot2) {
- if (great-- == k) {
- break outer;
- }
- }
- if (a[great] < pivot1) { // a[great] <= pivot2
- a[k] = a[less];
- a[less] = a[great];
- ++less;
- } else { // pivot1 <= a[great] <= pivot2
- a[k] = a[great];
- }
- /*
- * Here and below we use "a[i] = b; i--;" instead
- * of "a[i--] = b;" due to performance issue.
- */
- a[great] = ak;
- --great;
- }
- }
-
- // Swap pivots into their final positions
- a[left] = a[less - 1]; a[less - 1] = pivot1;
- a[right] = a[great + 1]; a[great + 1] = pivot2;
-
- // Sort left and right parts recursively, excluding known pivots
- sort(a, left, less - 2, leftmost);
- sort(a, great + 2, right, false);
-
- /*
- * If center part is too large (comprises > 4/7 of the array),
- * swap internal pivot values to ends.
+ * Pin insertion sort is extended simple insertion sort.
+ * The main idea of this sort is to put elements larger
+ * than an element called pin to the end of array (the
+ * proper area for such elements). It avoids expensive
+ * movements of these elements through the whole array.
*/
- if (less < e1 && e5 < great) {
- /*
- * Skip elements, which are equal to pivot values.
- */
- while (a[less] == pivot1) {
- ++less;
- }
-
- while (a[great] == pivot2) {
- --great;
- }
-
- /*
- * Partitioning:
- *
- * left part center part right part
- * +----------------------------------------------------------+
- * | == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 |
- * +----------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (*, less) == pivot1
- * pivot1 < all in [less, k) < pivot2
- * all in (great, *) == pivot2
- *
- * Pointer k is the first index of ?-part.
- */
- outer:
- for (int k = less - 1; ++k <= great; ) {
- short ak = a[k];
- if (ak == pivot1) { // Move a[k] to left part
- a[k] = a[less];
- a[less] = ak;
- ++less;
- } else if (ak == pivot2) { // Move a[k] to right part
- while (a[great] == pivot2) {
- if (great-- == k) {
- break outer;
- }
- }
- if (a[great] == pivot1) { // a[great] < pivot2
- a[k] = a[less];
- /*
- * Even though a[great] equals to pivot1, the
- * assignment a[less] = pivot1 may be incorrect,
- * if a[great] and pivot1 are floating-point zeros
- * of different signs. Therefore in float and
- * double sorting methods we have to use more
- * accurate assignment a[less] = a[great].
- */
- a[less] = pivot1;
- ++less;
- } else { // pivot1 < a[great] < pivot2
- a[k] = a[great];
- }
- a[great] = ak;
- --great;
+ long pin = a[end];
+
+ for (int i, p = high; ++low < end; ) {
+ long ai = a[i = low];
+
+ if (ai < a[i - 1]) { // Small element
+
+ /*
+ * Insert small element into sorted part.
+ */
+ a[i] = a[--i];
+
+ while (ai < a[--i]) {
+ a[i + 1] = a[i];
}
- }
- }
-
- // Sort center part recursively
- sort(a, less, great, false);
-
- } else { // Partitioning with one pivot
- /*
- * Use the third of the five sorted elements as pivot.
- * This value is inexpensive approximation of the median.
- */
- short pivot = a[e3];
-
- /*
- * Partitioning degenerates to the traditional 3-way
- * (or "Dutch National Flag") schema:
- *
- * left part center part right part
- * +-------------------------------------------------+
- * | < pivot | == pivot | ? | > pivot |
- * +-------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (left, less) < pivot
- * all in [less, k) == pivot
- * all in (great, right) > pivot
- *
- * Pointer k is the first index of ?-part.
- */
- for (int k = less; k <= great; ++k) {
- if (a[k] == pivot) {
- continue;
- }
- short ak = a[k];
- if (ak < pivot) { // Move a[k] to left part
- a[k] = a[less];
- a[less] = ak;
- ++less;
- } else { // a[k] > pivot - Move a[k] to right part
- while (a[great] > pivot) {
- --great;
+ a[i + 1] = ai;
+
+ } else if (p > i && ai > pin) { // Large element
+
+ /*
+ * Find element smaller than pin.
+ */
+ while (a[--p] > pin);
+
+ /*
+ * Swap it with large element.
+ */
+ if (p > i) {
+ ai = a[p];
+ a[p] = a[i];
}
- if (a[great] < pivot) { // a[great] <= pivot
- a[k] = a[less];
- a[less] = a[great];
- ++less;
- } else { // a[great] == pivot
- /*
- * Even though a[great] equals to pivot, the
- * assignment a[k] = pivot may be incorrect,
- * if a[great] and pivot are floating-point
- * zeros of different signs. Therefore in float
- * and double sorting methods we have to use
- * more accurate assignment a[k] = a[great].
- */
- a[k] = pivot;
+
+ /*
+ * Insert small element into sorted part.
+ */
+ while (ai < a[--i]) {
+ a[i + 1] = a[i];
}
- a[great] = ak;
- --great;
+ a[i + 1] = ai;
}
}
/*
- * Sort left and right parts recursively.
- * All elements from center part are equal
- * and, therefore, already sorted.
+ * Continue with pair insertion sort on remain part.
*/
- sort(a, left, less - 1, leftmost);
- sort(a, great + 1, right, false);
+ for (int i; low < high; ++low) {
+ long a1 = a[i = low], a2 = a[++low];
+
+ /*
+ * Insert two elements per iteration: at first, insert the
+ * larger element and then insert the smaller element, but
+ * from the position where the larger element was inserted.
+ */
+ if (a1 > a2) {
+
+ while (a1 < a[--i]) {
+ a[i + 2] = a[i];
+ }
+ a[++i + 1] = a1;
+
+ while (a2 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a2;
+
+ } else if (a1 < a[i - 1]) {
+
+ while (a2 < a[--i]) {
+ a[i + 2] = a[i];
+ }
+ a[++i + 1] = a2;
+
+ while (a1 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a1;
+ }
+ }
+ }
+ }
+
+ /**
+ * Sorts the specified range of the array using insertion sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void insertionSort(long[] a, int low, int high) {
+ for (int i, k = low; ++k < high; ) {
+ long ai = a[i = k];
+
+ if (ai < a[i - 1]) {
+ while (--i >= low && ai < a[i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = ai;
+ }
+ }
+ }
+
+ /**
+ * Sorts the specified range of the array using heap sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void heapSort(long[] a, int low, int high) {
+ for (int k = (low + high) >>> 1; k > low; ) {
+ pushDown(a, --k, a[k], low, high);
+ }
+ while (--high > low) {
+ long max = a[low];
+ pushDown(a, low, a[high], low, high);
+ a[high] = max;
}
}
/**
- * Sorts the specified range of the array using the given
- * workspace array slice if possible for merging
+ * Pushes specified element down during heap sort.
*
- * @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param work a workspace array (slice)
- * @param workBase origin of usable space in work array
- * @param workLen usable size of work array
+ * @param a the given array
+ * @param p the start index
+ * @param value the given element
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
*/
- static void sort(char[] a, int left, int right,
- char[] work, int workBase, int workLen) {
- // Use counting sort on large arrays
- if (right - left > COUNTING_SORT_THRESHOLD_FOR_SHORT_OR_CHAR) {
- int[] count = new int[NUM_CHAR_VALUES];
-
- for (int i = left - 1; ++i <= right;
- count[a[i]]++
- );
- for (int i = NUM_CHAR_VALUES, k = right + 1; k > left; ) {
- while (count[--i] == 0);
- char value = (char) i;
- int s = count[i];
-
- do {
- a[--k] = value;
- } while (--s > 0);
+ private static void pushDown(long[] a, int p, long value, int low, int high) {
+ for (int k ;; a[p] = a[p = k]) {
+ k = (p << 1) - low + 2; // Index of the right child
+
+ if (k > high) {
+ break;
}
- } else { // Use Dual-Pivot Quicksort on small arrays
- doSort(a, left, right, work, workBase, workLen);
+ if (k == high || a[k] < a[k - 1]) {
+ --k;
+ }
+ if (a[k] <= value) {
+ break;
+ }
}
+ a[p] = value;
}
- /** The number of distinct char values. */
- private static final int NUM_CHAR_VALUES = 1 << 16;
-
/**
- * Sorts the specified range of the array.
+ * Tries to sort the specified range of the array.
*
+ * @param sorter parallel context
* @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param work a workspace array (slice)
- * @param workBase origin of usable space in work array
- * @param workLen usable size of work array
+ * @param low the index of the first element to be sorted
+ * @param size the array size
+ * @return true if finally sorted, false otherwise
*/
- private static void doSort(char[] a, int left, int right,
- char[] work, int workBase, int workLen) {
- // Use Quicksort on small arrays
- if (right - left < QUICKSORT_THRESHOLD) {
- sort(a, left, right, true);
- return;
+ private static boolean tryMergeRuns(Sorter sorter, long[] a, int low, int size) {
+
+ /*
+ * The run array is constructed only if initial runs are
+ * long enough to continue, run[i] then holds start index
+ * of the i-th sequence of elements in non-descending order.
+ */
+ int[] run = null;
+ int high = low + size;
+ int count = 1, last = low;
+
+ /*
+ * Identify all possible runs.
+ */
+ for (int k = low + 1; k < high; ) {
+
+ /*
+ * Find the end index of the current run.
+ */
+ if (a[k - 1] < a[k]) {
+
+ // Identify ascending sequence
+ while (++k < high && a[k - 1] <= a[k]);
+
+ } else if (a[k - 1] > a[k]) {
+
+ // Identify descending sequence
+ while (++k < high && a[k - 1] >= a[k]);
+
+ // Reverse into ascending order
+ for (int i = last - 1, j = k; ++i < --j && a[i] > a[j]; ) {
+ long ai = a[i]; a[i] = a[j]; a[j] = ai;
+ }
+ } else { // Identify constant sequence
+ for (long ak = a[k]; ++k < high && ak == a[k]; );
+
+ if (k < high) {
+ continue;
+ }
+ }
+
+ /*
+ * Check special cases.
+ */
+ if (run == null) {
+ if (k == high) {
+
+ /*
+ * The array is monotonous sequence,
+ * and therefore already sorted.
+ */
+ return true;
+ }
+
+ if (k - low < MIN_FIRST_RUN_SIZE) {
+
+ /*
+ * The first run is too small
+ * to proceed with scanning.
+ */
+ return false;
+ }
+
+ run = new int[((size >> 10) | 0x7F) & 0x3FF];
+ run[0] = low;
+
+ } else if (a[last - 1] > a[last]) {
+
+ if (count > (k - low) >> MIN_FIRST_RUNS_FACTOR) {
+
+ /*
+ * The first runs are not long
+ * enough to continue scanning.
+ */
+ return false;
+ }
+
+ if (++count == MAX_RUN_CAPACITY) {
+
+ /*
+ * Array is not highly structured.
+ */
+ return false;
+ }
+
+ if (count == run.length) {
+
+ /*
+ * Increase capacity of index array.
+ */
+ run = Arrays.copyOf(run, count << 1);
+ }
+ }
+ run[count] = (last = k);
}
/*
- * Index run[i] is the start of i-th run
- * (ascending or descending sequence).
+ * Merge runs of highly structured array.
+ */
+ if (count > 1) {
+ long[] b; int offset = low;
+
+ if (sorter == null || (b = (long[]) sorter.b) == null) {
+ b = new long[size];
+ } else {
+ offset = sorter.offset;
+ }
+ mergeRuns(a, b, offset, 1, sorter != null, run, 0, count);
+ }
+ return true;
+ }
+
+ /**
+ * Merges the specified runs.
+ *
+ * @param a the source array
+ * @param b the temporary buffer used in merging
+ * @param offset the start index in the source, inclusive
+ * @param aim specifies merging: to source ( > 0), buffer ( < 0) or any ( == 0)
+ * @param parallel indicates whether merging is performed in parallel
+ * @param run the start indexes of the runs, inclusive
+ * @param lo the start index of the first run, inclusive
+ * @param hi the start index of the last run, inclusive
+ * @return the destination where runs are merged
+ */
+ private static long[] mergeRuns(long[] a, long[] b, int offset,
+ int aim, boolean parallel, int[] run, int lo, int hi) {
+
+ if (hi - lo == 1) {
+ if (aim >= 0) {
+ return a;
+ }
+ for (int i = run[hi], j = i - offset, low = run[lo]; i > low;
+ b[--j] = a[--i]
+ );
+ return b;
+ }
+
+ /*
+ * Split into approximately equal parts.
+ */
+ int mi = lo, rmi = (run[lo] + run[hi]) >>> 1;
+ while (run[++mi + 1] <= rmi);
+
+ /*
+ * Merge the left and right parts.
*/
- int[] run = new int[MAX_RUN_COUNT + 1];
- int count = 0; run[0] = left;
-
- // Check if the array is nearly sorted
- for (int k = left; k < right; run[count] = k) {
- // Equal items in the beginning of the sequence
- while (k < right && a[k] == a[k + 1])
- k++;
- if (k == right) break; // Sequence finishes with equal items
- if (a[k] < a[k + 1]) { // ascending
- while (++k <= right && a[k - 1] <= a[k]);
- } else if (a[k] > a[k + 1]) { // descending
- while (++k <= right && a[k - 1] >= a[k]);
- // Transform into an ascending sequence
- for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {
- char t = a[lo]; a[lo] = a[hi]; a[hi] = t;
+ long[] a1, a2;
+
+ if (parallel && hi - lo > MIN_RUN_COUNT) {
+ RunMerger merger = new RunMerger(a, b, offset, 0, run, mi, hi).forkMe();
+ a1 = mergeRuns(a, b, offset, -aim, true, run, lo, mi);
+ a2 = (long[]) merger.getDestination();
+ } else {
+ a1 = mergeRuns(a, b, offset, -aim, false, run, lo, mi);
+ a2 = mergeRuns(a, b, offset, 0, false, run, mi, hi);
+ }
+
+ long[] dst = a1 == a ? b : a;
+
+ int k = a1 == a ? run[lo] - offset : run[lo];
+ int lo1 = a1 == b ? run[lo] - offset : run[lo];
+ int hi1 = a1 == b ? run[mi] - offset : run[mi];
+ int lo2 = a2 == b ? run[mi] - offset : run[mi];
+ int hi2 = a2 == b ? run[hi] - offset : run[hi];
+
+ if (parallel) {
+ new Merger(null, dst, k, a1, lo1, hi1, a2, lo2, hi2).invoke();
+ } else {
+ mergeParts(null, dst, k, a1, lo1, hi1, a2, lo2, hi2);
+ }
+ return dst;
+ }
+
+ /**
+ * Merges the sorted parts.
+ *
+ * @param merger parallel context
+ * @param dst the destination where parts are merged
+ * @param k the start index of the destination, inclusive
+ * @param a1 the first part
+ * @param lo1 the start index of the first part, inclusive
+ * @param hi1 the end index of the first part, exclusive
+ * @param a2 the second part
+ * @param lo2 the start index of the second part, inclusive
+ * @param hi2 the end index of the second part, exclusive
+ */
+ private static void mergeParts(Merger merger, long[] dst, int k,
+ long[] a1, int lo1, int hi1, long[] a2, int lo2, int hi2) {
+
+ if (merger != null && a1 == a2) {
+
+ while (true) {
+
+ /*
+ * The first part must be larger.
+ */
+ if (hi1 - lo1 < hi2 - lo2) {
+ int lo = lo1; lo1 = lo2; lo2 = lo;
+ int hi = hi1; hi1 = hi2; hi2 = hi;
}
- }
-
- // Merge a transformed descending sequence followed by an
- // ascending sequence
- if (run[count] > left && a[run[count]] >= a[run[count] - 1]) {
- count--;
- }
-
- /*
- * The array is not highly structured,
- * use Quicksort instead of merge sort.
- */
- if (++count == MAX_RUN_COUNT) {
- sort(a, left, right, true);
- return;
+
+ /*
+ * Small parts will be merged sequentially.
+ */
+ if (hi1 - lo1 < MIN_PARALLEL_MERGE_PARTS_SIZE) {
+ break;
+ }
+
+ /*
+ * Find the median of the larger part.
+ */
+ int mi1 = (lo1 + hi1) >>> 1;
+ long key = a1[mi1];
+ int mi2 = hi2;
+
+ /*
+ * Partition the smaller part.
+ */
+ for (int loo = lo2; loo < mi2; ) {
+ int t = (loo + mi2) >>> 1;
+
+ if (key > a2[t]) {
+ loo = t + 1;
+ } else {
+ mi2 = t;
+ }
+ }
+
+ int d = mi2 - lo2 + mi1 - lo1;
+
+ /*
+ * Merge the right sub-parts in parallel.
+ */
+ merger.forkMerger(dst, k + d, a1, mi1, hi1, a2, mi2, hi2);
+
+ /*
+ * Process the sub-left parts.
+ */
+ hi1 = mi1;
+ hi2 = mi2;
}
}
- // These invariants should hold true:
- // run[0] = 0
- // run[<last>] = right + 1; (terminator)
-
- if (count == 0) {
- // A single equal run
- return;
- } else if (count == 1 && run[count] > right) {
- // Either a single ascending or a transformed descending run.
- // Always check that a final run is a proper terminator, otherwise
- // we have an unterminated trailing run, to handle downstream.
- return;
+ /*
+ * Merge small parts sequentially.
+ */
+ while (lo1 < hi1 && lo2 < hi2) {
+ dst[k++] = a1[lo1] < a2[lo2] ? a1[lo1++] : a2[lo2++];
}
- right++;
- if (run[count] < right) {
- // Corner case: the final run is not a terminator. This may happen
- // if a final run is an equals run, or there is a single-element run
- // at the end. Fix up by adding a proper terminator at the end.
- // Note that we terminate with (right + 1), incremented earlier.
- run[++count] = right;
+ if (dst != a1 || k < lo1) {
+ while (lo1 < hi1) {
+ dst[k++] = a1[lo1++];
+ }
}
-
- // Determine alternation base for merge
- byte odd = 0;
- for (int n = 1; (n <<= 1) < count; odd ^= 1);
-
- // Use or create temporary array b for merging
- char[] b; // temp array; alternates with a
- int ao, bo; // array offsets from 'left'
- int blen = right - left; // space needed for b
- if (work == null || workLen < blen || workBase + blen > work.length) {
- work = new char[blen];
- workBase = 0;
+ if (dst != a2 || k < lo2) {
+ while (lo2 < hi2) {
+ dst[k++] = a2[lo2++];
+ }
}
- if (odd == 0) {
- System.arraycopy(a, left, work, workBase, blen);
- b = a;
- bo = 0;
- a = work;
- ao = workBase - left;
+ }
+
+// [byte]
+
+ /**
+ * Sorts the specified range of the array using
+ * counting sort or insertion sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ static void sort(byte[] a, int low, int high) {
+ if (high - low > MIN_BYTE_COUNTING_SORT_SIZE) {
+ countingSort(a, low, high);
} else {
- b = work;
- ao = 0;
- bo = workBase - left;
- }
-
- // Merging
- for (int last; count > 1; count = last) {
- for (int k = (last = 0) + 2; k <= count; k += 2) {
- int hi = run[k], mi = run[k - 1];
- for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
- if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {
- b[i + bo] = a[p++ + ao];
- } else {
- b[i + bo] = a[q++ + ao];
- }
- }
- run[++last] = hi;
- }
- if ((count & 1) != 0) {
- for (int i = right, lo = run[count - 1]; --i >= lo;
- b[i + bo] = a[i + ao]
- );
- run[++last] = right;
- }
- char[] t = a; a = b; b = t;
- int o = ao; ao = bo; bo = o;
+ insertionSort(a, low, high);
}
}
/**
- * Sorts the specified range of the array by Dual-Pivot Quicksort.
+ * Sorts the specified range of the array using insertion sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void insertionSort(byte[] a, int low, int high) {
+ for (int i, k = low; ++k < high; ) {
+ byte ai = a[i = k];
+
+ if (ai < a[i - 1]) {
+ while (--i >= low && ai < a[i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = ai;
+ }
+ }
+ }
+
+ /**
+ * The number of distinct byte values.
+ */
+ private static final int NUM_BYTE_VALUES = 1 << 8;
+
+ /**
+ * Max index of byte counter.
+ */
+ private static final int MAX_BYTE_INDEX = Byte.MAX_VALUE + NUM_BYTE_VALUES + 1;
+
+ /**
+ * Sorts the specified range of the array using counting sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void countingSort(byte[] a, int low, int high) {
+ int[] count = new int[NUM_BYTE_VALUES];
+
+ /*
+ * Compute a histogram with the number of each values.
+ */
+ for (int i = high; i > low; ++count[a[--i] & 0xFF]);
+
+ /*
+ * Place values on their final positions.
+ */
+ if (high - low > NUM_BYTE_VALUES) {
+ for (int i = MAX_BYTE_INDEX; --i > Byte.MAX_VALUE; ) {
+ int value = i & 0xFF;
+
+ for (low = high - count[value]; high > low;
+ a[--high] = (byte) value
+ );
+ }
+ } else {
+ for (int i = MAX_BYTE_INDEX; high > low; ) {
+ while (count[--i & 0xFF] == 0);
+
+ int value = i & 0xFF;
+ int c = count[value];
+
+ do {
+ a[--high] = (byte) value;
+ } while (--c > 0);
+ }
+ }
+ }
+
+// [char]
+
+ /**
+ * Sorts the specified range of the array using
+ * counting sort or Dual-Pivot Quicksort.
*
* @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param leftmost indicates if this part is the leftmost in the range
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ static void sort(char[] a, int low, int high) {
+ if (high - low > MIN_SHORT_OR_CHAR_COUNTING_SORT_SIZE) {
+ countingSort(a, low, high);
+ } else {
+ sort(a, 0, low, high);
+ }
+ }
+
+ /**
+ * Sorts the specified array using the Dual-Pivot Quicksort and/or
+ * other sorts in special-cases, possibly with parallel partitions.
+ *
+ * @param a the array to be sorted
+ * @param bits the combination of recursion depth and bit flag, where
+ * the right bit "0" indicates that array is the leftmost part
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
*/
- private static void sort(char[] a, int left, int right, boolean leftmost) {
- int length = right - left + 1;
-
- // Use insertion sort on tiny arrays
- if (length < INSERTION_SORT_THRESHOLD) {
- if (leftmost) {
- /*
- * Traditional (without sentinel) insertion sort,
- * optimized for server VM, is used in case of
- * the leftmost part.
- */
- for (int i = left, j = i; i < right; j = ++i) {
- char ai = a[i + 1];
- while (ai < a[j]) {
- a[j + 1] = a[j];
- if (j-- == left) {
- break;
- }
- }
- a[j + 1] = ai;
+ static void sort(char[] a, int bits, int low, int high) {
+ while (true) {
+ int end = high - 1, size = high - low;
+
+ /*
+ * Invoke insertion sort on small leftmost part.
+ */
+ if (size < MAX_INSERTION_SORT_SIZE) {
+ insertionSort(a, low, high);
+ return;
+ }
+
+ /*
+ * Switch to counting sort if execution
+ * time is becoming quadratic.
+ */
+ if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
+ countingSort(a, low, high);
+ return;
+ }
+
+ /*
+ * Use an inexpensive approximation of the golden ratio
+ * to select five sample elements and determine pivots.
+ */
+ int step = (size >> 3) * 3 + 3;
+
+ /*
+ * Five elements around (and including) the central element
+ * will be used for pivot selection as described below. The
+ * unequal choice of spacing these elements was empirically
+ * determined to work well on a wide variety of inputs.
+ */
+ int e1 = low + step;
+ int e5 = end - step;
+ int e3 = (e1 + e5) >>> 1;
+ int e2 = (e1 + e3) >>> 1;
+ int e4 = (e3 + e5) >>> 1;
+ char a3 = a[e3];
+
+ /*
+ * Sort these elements in place by the combination
+ * of 4-element sorting network and insertion sort.
+ *
+ * 5 ------o-----------o------------
+ * | |
+ * 4 ------|-----o-----o-----o------
+ * | | |
+ * 2 ------o-----|-----o-----o------
+ * | |
+ * 1 ------------o-----o------------
+ */
+ if (a[e5] < a[e2]) { char t = a[e5]; a[e5] = a[e2]; a[e2] = t; }
+ if (a[e4] < a[e1]) { char t = a[e4]; a[e4] = a[e1]; a[e1] = t; }
+ if (a[e5] < a[e4]) { char t = a[e5]; a[e5] = a[e4]; a[e4] = t; }
+ if (a[e2] < a[e1]) { char t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
+ if (a[e4] < a[e2]) { char t = a[e4]; a[e4] = a[e2]; a[e2] = t; }
+
+ if (a3 < a[e2]) {
+ if (a3 < a[e1]) {
+ a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = a3;
+ } else {
+ a[e3] = a[e2]; a[e2] = a3;
}
- } else {
- /*
- * Skip the longest ascending sequence.
- */
- do {
- if (left >= right) {
- return;
- }
- } while (a[++left] >= a[left - 1]);
-
- /*
- * Every element from adjoining part plays the role
- * of sentinel, therefore this allows us to avoid the
- * left range check on each iteration. Moreover, we use
- * the more optimized algorithm, so called pair insertion
- * sort, which is faster (in the context of Quicksort)
- * than traditional implementation of insertion sort.
- */
- for (int k = left; ++left <= right; k = ++left) {
- char a1 = a[k], a2 = a[left];
-
- if (a1 < a2) {
- a2 = a1; a1 = a[left];
- }
- while (a1 < a[--k]) {
- a[k + 2] = a[k];
- }
- a[++k + 1] = a1;
-
- while (a2 < a[--k]) {
- a[k + 1] = a[k];
- }
- a[k + 1] = a2;
- }
- char last = a[right];
-
- while (last < a[--right]) {
- a[right + 1] = a[right];
- }
- a[right + 1] = last;
- }
- return;
- }
-
- // Inexpensive approximation of length / 7
- int seventh = (length >> 3) + (length >> 6) + 1;
-
- /*
- * Sort five evenly spaced elements around (and including) the
- * center element in the range. These elements will be used for
- * pivot selection as described below. The choice for spacing
- * these elements was empirically determined to work well on
- * a wide variety of inputs.
- */
- int e3 = (left + right) >>> 1; // The midpoint
- int e2 = e3 - seventh;
- int e1 = e2 - seventh;
- int e4 = e3 + seventh;
- int e5 = e4 + seventh;
-
- // Sort these elements using insertion sort
- if (a[e2] < a[e1]) { char t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
-
- if (a[e3] < a[e2]) { char t = a[e3]; a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- if (a[e4] < a[e3]) { char t = a[e4]; a[e4] = a[e3]; a[e3] = t;
- if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- }
- if (a[e5] < a[e4]) { char t = a[e5]; a[e5] = a[e4]; a[e4] = t;
- if (t < a[e3]) { a[e4] = a[e3]; a[e3] = t;
- if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
+ } else if (a3 > a[e4]) {
+ if (a3 > a[e5]) {
+ a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = a3;
+ } else {
+ a[e3] = a[e4]; a[e4] = a3;
}
}
- }
-
- // Pointers
- int less = left; // The index of the first element of center part
- int great = right; // The index before the first element of right part
-
- if (a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5]) {
- /*
- * Use the second and fourth of the five sorted elements as pivots.
- * These values are inexpensive approximations of the first and
- * second terciles of the array. Note that pivot1 <= pivot2.
- */
- char pivot1 = a[e2];
- char pivot2 = a[e4];
-
- /*
- * The first and the last elements to be sorted are moved to the
- * locations formerly occupied by the pivots. When partitioning
- * is complete, the pivots are swapped back into their final
- * positions, and excluded from subsequent sorting.
- */
- a[e2] = a[left];
- a[e4] = a[right];
-
- /*
- * Skip elements, which are less or greater than pivot values.
- */
- while (a[++less] < pivot1);
- while (a[--great] > pivot2);
+
+ // Pointers
+ int lower = low; // The index of the last element of the left part
+ int upper = end; // The index of the first element of the right part
/*
- * Partitioning:
- *
- * left part center part right part
- * +--------------------------------------------------------------+
- * | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 |
- * +--------------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (left, less) < pivot1
- * pivot1 <= all in [less, k) <= pivot2
- * all in (great, right) > pivot2
- *
- * Pointer k is the first index of ?-part.
+ * Partitioning with 2 pivots in case of different elements.
*/
- outer:
- for (int k = less - 1; ++k <= great; ) {
- char ak = a[k];
- if (ak < pivot1) { // Move a[k] to left part
- a[k] = a[less];
- /*
- * Here and below we use "a[i] = b; i++;" instead
- * of "a[i++] = b;" due to performance issue.
- */
- a[less] = ak;
- ++less;
- } else if (ak > pivot2) { // Move a[k] to right part
- while (a[great] > pivot2) {
- if (great-- == k) {
- break outer;
+ if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
+
+ /*
+ * Use the first and fifth of the five sorted elements as
+ * the pivots. These values are inexpensive approximation
+ * of tertiles. Note, that pivot1 < pivot2.
+ */
+ char pivot1 = a[e1];
+ char pivot2 = a[e5];
+
+ /*
+ * The first and the last elements to be sorted are moved
+ * to the locations formerly occupied by the pivots. When
+ * partitioning is completed, the pivots are swapped back
+ * into their final positions, and excluded from the next
+ * subsequent sorting.
+ */
+ a[e1] = a[lower];
+ a[e5] = a[upper];
+
+ /*
+ * Skip elements, which are less or greater than the pivots.
+ */
+ while (a[++lower] < pivot1);
+ while (a[--upper] > pivot2);
+
+ /*
+ * Backward 3-interval partitioning
+ *
+ * left part central part right part
+ * +------------------------------------------------------------+
+ * | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
+ * +------------------------------------------------------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Invariants:
+ *
+ * all in (low, lower] < pivot1
+ * pivot1 <= all in (k, upper) <= pivot2
+ * all in [upper, end) > pivot2
+ *
+ * Pointer k is the last index of ?-part
+ */
+ for (int unused = --lower, k = ++upper; --k > lower; ) {
+ char ak = a[k];
+
+ if (ak < pivot1) { // Move a[k] to the left side
+ while (lower < k) {
+ if (a[++lower] >= pivot1) {
+ if (a[lower] > pivot2) {
+ a[k] = a[--upper];
+ a[upper] = a[lower];
+ } else {
+ a[k] = a[lower];
+ }
+ a[lower] = ak;
+ break;
+ }
}
+ } else if (ak > pivot2) { // Move a[k] to the right side
+ a[k] = a[--upper];
+ a[upper] = ak;
}
- if (a[great] < pivot1) { // a[great] <= pivot2
- a[k] = a[less];
- a[less] = a[great];
- ++less;
- } else { // pivot1 <= a[great] <= pivot2
- a[k] = a[great];
- }
- /*
- * Here and below we use "a[i] = b; i--;" instead
- * of "a[i--] = b;" due to performance issue.
- */
- a[great] = ak;
- --great;
- }
- }
-
- // Swap pivots into their final positions
- a[left] = a[less - 1]; a[less - 1] = pivot1;
- a[right] = a[great + 1]; a[great + 1] = pivot2;
-
- // Sort left and right parts recursively, excluding known pivots
- sort(a, left, less - 2, leftmost);
- sort(a, great + 2, right, false);
-
- /*
- * If center part is too large (comprises > 4/7 of the array),
- * swap internal pivot values to ends.
- */
- if (less < e1 && e5 < great) {
- /*
- * Skip elements, which are equal to pivot values.
- */
- while (a[less] == pivot1) {
- ++less;
- }
-
- while (a[great] == pivot2) {
- --great;
}
/*
- * Partitioning:
+ * Swap the pivots into their final positions.
+ */
+ a[low] = a[lower]; a[lower] = pivot1;
+ a[end] = a[upper]; a[upper] = pivot2;
+
+ /*
+ * Sort non-left parts recursively,
+ * excluding known pivots.
+ */
+ sort(a, bits | 1, lower + 1, upper);
+ sort(a, bits | 1, upper + 1, high);
+
+ } else { // Use single pivot in case of many equal elements
+
+ /*
+ * Use the third of the five sorted elements as the pivot.
+ * This value is inexpensive approximation of the median.
+ */
+ char pivot = a[e3];
+
+ /*
+ * The first element to be sorted is moved to the
+ * location formerly occupied by the pivot. After
+ * completion of partitioning the pivot is swapped
+ * back into its final position, and excluded from
+ * the next subsequent sorting.
+ */
+ a[e3] = a[lower];
+
+ /*
+ * Traditional 3-way (Dutch National Flag) partitioning
*
- * left part center part right part
- * +----------------------------------------------------------+
- * | == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 |
- * +----------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
+ * left part central part right part
+ * +------------------------------------------------------+
+ * | < pivot | ? | == pivot | > pivot |
+ * +------------------------------------------------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
*
* Invariants:
*
- * all in (*, less) == pivot1
- * pivot1 < all in [less, k) < pivot2
- * all in (great, *) == pivot2
+ * all in (low, lower] < pivot
+ * all in (k, upper) == pivot
+ * all in [upper, end] > pivot
*
- * Pointer k is the first index of ?-part.
+ * Pointer k is the last index of ?-part
*/
- outer:
- for (int k = less - 1; ++k <= great; ) {
+ for (int k = ++upper; --k > lower; ) {
char ak = a[k];
- if (ak == pivot1) { // Move a[k] to left part
- a[k] = a[less];
- a[less] = ak;
- ++less;
- } else if (ak == pivot2) { // Move a[k] to right part
- while (a[great] == pivot2) {
- if (great-- == k) {
- break outer;
+
+ if (ak != pivot) {
+ a[k] = pivot;
+
+ if (ak < pivot) { // Move a[k] to the left side
+ while (a[++lower] < pivot);
+
+ if (a[lower] > pivot) {
+ a[--upper] = a[lower];
}
+ a[lower] = ak;
+ } else { // ak > pivot - Move a[k] to the right side
+ a[--upper] = ak;
}
- if (a[great] == pivot1) { // a[great] < pivot2
- a[k] = a[less];
- /*
- * Even though a[great] equals to pivot1, the
- * assignment a[less] = pivot1 may be incorrect,
- * if a[great] and pivot1 are floating-point zeros
- * of different signs. Therefore in float and
- * double sorting methods we have to use more
- * accurate assignment a[less] = a[great].
- */
- a[less] = pivot1;
- ++less;
- } else { // pivot1 < a[great] < pivot2
- a[k] = a[great];
- }
- a[great] = ak;
- --great;
}
}
+
+ /*
+ * Swap the pivot into its final position.
+ */
+ a[low] = a[lower]; a[lower] = pivot;
+
+ /*
+ * Sort the right part, excluding known pivot.
+ * All elements from the central part are
+ * equal and therefore already sorted.
+ */
+ sort(a, bits | 1, upper, high);
}
-
- // Sort center part recursively
- sort(a, less, great, false);
-
- } else { // Partitioning with one pivot
- /*
- * Use the third of the five sorted elements as pivot.
- * This value is inexpensive approximation of the median.
- */
- char pivot = a[e3];
-
- /*
- * Partitioning degenerates to the traditional 3-way
- * (or "Dutch National Flag") schema:
- *
- * left part center part right part
- * +-------------------------------------------------+
- * | < pivot | == pivot | ? | > pivot |
- * +-------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (left, less) < pivot
- * all in [less, k) == pivot
- * all in (great, right) > pivot
- *
- * Pointer k is the first index of ?-part.
- */
- for (int k = less; k <= great; ++k) {
- if (a[k] == pivot) {
- continue;
- }
- char ak = a[k];
- if (ak < pivot) { // Move a[k] to left part
- a[k] = a[less];
- a[less] = ak;
- ++less;
- } else { // a[k] > pivot - Move a[k] to right part
- while (a[great] > pivot) {
- --great;
- }
- if (a[great] < pivot) { // a[great] <= pivot
- a[k] = a[less];
- a[less] = a[great];
- ++less;
- } else { // a[great] == pivot
- /*
- * Even though a[great] equals to pivot, the
- * assignment a[k] = pivot may be incorrect,
- * if a[great] and pivot are floating-point
- * zeros of different signs. Therefore in float
- * and double sorting methods we have to use
- * more accurate assignment a[k] = a[great].
- */
- a[k] = pivot;
- }
- a[great] = ak;
- --great;
- }
- }
-
- /*
- * Sort left and right parts recursively.
- * All elements from center part are equal
- * and, therefore, already sorted.
- */
- sort(a, left, less - 1, leftmost);
- sort(a, great + 1, right, false);
+ high = lower; // Iterate along the left part
}
}
- /** The number of distinct byte values. */
- private static final int NUM_BYTE_VALUES = 1 << 8;
-
/**
- * Sorts the specified range of the array.
+ * Sorts the specified range of the array using insertion sort.
*
* @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
*/
- static void sort(byte[] a, int left, int right) {
- // Use counting sort on large arrays
- if (right - left > COUNTING_SORT_THRESHOLD_FOR_BYTE) {
- int[] count = new int[NUM_BYTE_VALUES];
-
- for (int i = left - 1; ++i <= right;
- count[a[i] - Byte.MIN_VALUE]++
- );
- for (int i = NUM_BYTE_VALUES, k = right + 1; k > left; ) {
- while (count[--i] == 0);
- byte value = (byte) (i + Byte.MIN_VALUE);
- int s = count[i];
-
- do {
- a[--k] = value;
- } while (--s > 0);
- }
- } else { // Use insertion sort on small arrays
- for (int i = left, j = i; i < right; j = ++i) {
- byte ai = a[i + 1];
- while (ai < a[j]) {
- a[j + 1] = a[j];
- if (j-- == left) {
- break;
- }
+ private static void insertionSort(char[] a, int low, int high) {
+ for (int i, k = low; ++k < high; ) {
+ char ai = a[i = k];
+
+ if (ai < a[i - 1]) {
+ while (--i >= low && ai < a[i]) {
+ a[i + 1] = a[i];
}
- a[j + 1] = ai;
+ a[i + 1] = ai;
}
}
}
/**
- * Sorts the specified range of the array using the given
- * workspace array slice if possible for merging
+ * The number of distinct char values.
+ */
+ private static final int NUM_CHAR_VALUES = 1 << 16;
+
+ /**
+ * Sorts the specified range of the array using counting sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void countingSort(char[] a, int low, int high) {
+ int[] count = new int[NUM_CHAR_VALUES];
+
+ /*
+ * Compute a histogram with the number of each values.
+ */
+ for (int i = high; i > low; ++count[a[--i]]);
+
+ /*
+ * Place values on their final positions.
+ */
+ if (high - low > NUM_CHAR_VALUES) {
+ for (int i = NUM_CHAR_VALUES; i > 0; ) {
+ for (low = high - count[--i]; high > low;
+ a[--high] = (char) i
+ );
+ }
+ } else {
+ for (int i = NUM_CHAR_VALUES; high > low; ) {
+ while (count[--i] == 0);
+ int c = count[i];
+
+ do {
+ a[--high] = (char) i;
+ } while (--c > 0);
+ }
+ }
+ }
+
+// [short]
+
+ /**
+ * Sorts the specified range of the array using
+ * counting sort or Dual-Pivot Quicksort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ static void sort(short[] a, int low, int high) {
+ if (high - low > MIN_SHORT_OR_CHAR_COUNTING_SORT_SIZE) {
+ countingSort(a, low, high);
+ } else {
+ sort(a, 0, low, high);
+ }
+ }
+
+ /**
+ * Sorts the specified array using the Dual-Pivot Quicksort and/or
+ * other sorts in special-cases, possibly with parallel partitions.
*
* @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param work a workspace array (slice)
- * @param workBase origin of usable space in work array
- * @param workLen usable size of work array
+ * @param bits the combination of recursion depth and bit flag, where
+ * the right bit "0" indicates that array is the leftmost part
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
*/
- static void sort(float[] a, int left, int right,
- float[] work, int workBase, int workLen) {
+ static void sort(short[] a, int bits, int low, int high) {
+ while (true) {
+ int end = high - 1, size = high - low;
+
+ /*
+ * Invoke insertion sort on small leftmost part.
+ */
+ if (size < MAX_INSERTION_SORT_SIZE) {
+ insertionSort(a, low, high);
+ return;
+ }
+
+ /*
+ * Switch to counting sort if execution
+ * time is becoming quadratic.
+ */
+ if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
+ countingSort(a, low, high);
+ return;
+ }
+
+ /*
+ * Use an inexpensive approximation of the golden ratio
+ * to select five sample elements and determine pivots.
+ */
+ int step = (size >> 3) * 3 + 3;
+
+ /*
+ * Five elements around (and including) the central element
+ * will be used for pivot selection as described below. The
+ * unequal choice of spacing these elements was empirically
+ * determined to work well on a wide variety of inputs.
+ */
+ int e1 = low + step;
+ int e5 = end - step;
+ int e3 = (e1 + e5) >>> 1;
+ int e2 = (e1 + e3) >>> 1;
+ int e4 = (e3 + e5) >>> 1;
+ short a3 = a[e3];
+
+ /*
+ * Sort these elements in place by the combination
+ * of 4-element sorting network and insertion sort.
+ *
+ * 5 ------o-----------o------------
+ * | |
+ * 4 ------|-----o-----o-----o------
+ * | | |
+ * 2 ------o-----|-----o-----o------
+ * | |
+ * 1 ------------o-----o------------
+ */
+ if (a[e5] < a[e2]) { short t = a[e5]; a[e5] = a[e2]; a[e2] = t; }
+ if (a[e4] < a[e1]) { short t = a[e4]; a[e4] = a[e1]; a[e1] = t; }
+ if (a[e5] < a[e4]) { short t = a[e5]; a[e5] = a[e4]; a[e4] = t; }
+ if (a[e2] < a[e1]) { short t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
+ if (a[e4] < a[e2]) { short t = a[e4]; a[e4] = a[e2]; a[e2] = t; }
+
+ if (a3 < a[e2]) {
+ if (a3 < a[e1]) {
+ a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = a3;
+ } else {
+ a[e3] = a[e2]; a[e2] = a3;
+ }
+ } else if (a3 > a[e4]) {
+ if (a3 > a[e5]) {
+ a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = a3;
+ } else {
+ a[e3] = a[e4]; a[e4] = a3;
+ }
+ }
+
+ // Pointers
+ int lower = low; // The index of the last element of the left part
+ int upper = end; // The index of the first element of the right part
+
+ /*
+ * Partitioning with 2 pivots in case of different elements.
+ */
+ if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
+
+ /*
+ * Use the first and fifth of the five sorted elements as
+ * the pivots. These values are inexpensive approximation
+ * of tertiles. Note, that pivot1 < pivot2.
+ */
+ short pivot1 = a[e1];
+ short pivot2 = a[e5];
+
+ /*
+ * The first and the last elements to be sorted are moved
+ * to the locations formerly occupied by the pivots. When
+ * partitioning is completed, the pivots are swapped back
+ * into their final positions, and excluded from the next
+ * subsequent sorting.
+ */
+ a[e1] = a[lower];
+ a[e5] = a[upper];
+
+ /*
+ * Skip elements, which are less or greater than the pivots.
+ */
+ while (a[++lower] < pivot1);
+ while (a[--upper] > pivot2);
+
+ /*
+ * Backward 3-interval partitioning
+ *
+ * left part central part right part
+ * +------------------------------------------------------------+
+ * | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
+ * +------------------------------------------------------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Invariants:
+ *
+ * all in (low, lower] < pivot1
+ * pivot1 <= all in (k, upper) <= pivot2
+ * all in [upper, end) > pivot2
+ *
+ * Pointer k is the last index of ?-part
+ */
+ for (int unused = --lower, k = ++upper; --k > lower; ) {
+ short ak = a[k];
+
+ if (ak < pivot1) { // Move a[k] to the left side
+ while (lower < k) {
+ if (a[++lower] >= pivot1) {
+ if (a[lower] > pivot2) {
+ a[k] = a[--upper];
+ a[upper] = a[lower];
+ } else {
+ a[k] = a[lower];
+ }
+ a[lower] = ak;
+ break;
+ }
+ }
+ } else if (ak > pivot2) { // Move a[k] to the right side
+ a[k] = a[--upper];
+ a[upper] = ak;
+ }
+ }
+
+ /*
+ * Swap the pivots into their final positions.
+ */
+ a[low] = a[lower]; a[lower] = pivot1;
+ a[end] = a[upper]; a[upper] = pivot2;
+
+ /*
+ * Sort non-left parts recursively,
+ * excluding known pivots.
+ */
+ sort(a, bits | 1, lower + 1, upper);
+ sort(a, bits | 1, upper + 1, high);
+
+ } else { // Use single pivot in case of many equal elements
+
+ /*
+ * Use the third of the five sorted elements as the pivot.
+ * This value is inexpensive approximation of the median.
+ */
+ short pivot = a[e3];
+
+ /*
+ * The first element to be sorted is moved to the
+ * location formerly occupied by the pivot. After
+ * completion of partitioning the pivot is swapped
+ * back into its final position, and excluded from
+ * the next subsequent sorting.
+ */
+ a[e3] = a[lower];
+
+ /*
+ * Traditional 3-way (Dutch National Flag) partitioning
+ *
+ * left part central part right part
+ * +------------------------------------------------------+
+ * | < pivot | ? | == pivot | > pivot |
+ * +------------------------------------------------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Invariants:
+ *
+ * all in (low, lower] < pivot
+ * all in (k, upper) == pivot
+ * all in [upper, end] > pivot
+ *
+ * Pointer k is the last index of ?-part
+ */
+ for (int k = ++upper; --k > lower; ) {
+ short ak = a[k];
+
+ if (ak != pivot) {
+ a[k] = pivot;
+
+ if (ak < pivot) { // Move a[k] to the left side
+ while (a[++lower] < pivot);
+
+ if (a[lower] > pivot) {
+ a[--upper] = a[lower];
+ }
+ a[lower] = ak;
+ } else { // ak > pivot - Move a[k] to the right side
+ a[--upper] = ak;
+ }
+ }
+ }
+
+ /*
+ * Swap the pivot into its final position.
+ */
+ a[low] = a[lower]; a[lower] = pivot;
+
+ /*
+ * Sort the right part, excluding known pivot.
+ * All elements from the central part are
+ * equal and therefore already sorted.
+ */
+ sort(a, bits | 1, upper, high);
+ }
+ high = lower; // Iterate along the left part
+ }
+ }
+
+ /**
+ * Sorts the specified range of the array using insertion sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void insertionSort(short[] a, int low, int high) {
+ for (int i, k = low; ++k < high; ) {
+ short ai = a[i = k];
+
+ if (ai < a[i - 1]) {
+ while (--i >= low && ai < a[i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = ai;
+ }
+ }
+ }
+
+ /**
+ * The number of distinct short values.
+ */
+ private static final int NUM_SHORT_VALUES = 1 << 16;
+
+ /**
+ * Max index of short counter.
+ */
+ private static final int MAX_SHORT_INDEX = Short.MAX_VALUE + NUM_SHORT_VALUES + 1;
+
+ /**
+ * Sorts the specified range of the array using counting sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void countingSort(short[] a, int low, int high) {
+ int[] count = new int[NUM_SHORT_VALUES];
+
/*
- * Phase 1: Move NaNs to the end of the array.
+ * Compute a histogram with the number of each values.
+ */
+ for (int i = high; i > low; ++count[a[--i] & 0xFFFF]);
+
+ /*
+ * Place values on their final positions.
*/
- while (left <= right && Float.isNaN(a[right])) {
- --right;
+ if (high - low > NUM_SHORT_VALUES) {
+ for (int i = MAX_SHORT_INDEX; --i > Short.MAX_VALUE; ) {
+ int value = i & 0xFFFF;
+
+ for (low = high - count[value]; high > low;
+ a[--high] = (short) value
+ );
+ }
+ } else {
+ for (int i = MAX_SHORT_INDEX; high > low; ) {
+ while (count[--i & 0xFFFF] == 0);
+
+ int value = i & 0xFFFF;
+ int c = count[value];
+
+ do {
+ a[--high] = (short) value;
+ } while (--c > 0);
+ }
}
- for (int k = right; --k >= left; ) {
- float ak = a[k];
- if (ak != ak) { // a[k] is NaN
- a[k] = a[right];
- a[right] = ak;
- --right;
+ }
+
+// [float]
+
+ /**
+ * Sorts the specified range of the array using parallel merge
+ * sort and/or Dual-Pivot Quicksort.
+ *
+ * To balance the faster splitting and parallelism of merge sort
+ * with the faster element partitioning of Quicksort, ranges are
+ * subdivided in tiers such that, if there is enough parallelism,
+ * the four-way parallel merge is started, still ensuring enough
+ * parallelism to process the partitions.
+ *
+ * @param a the array to be sorted
+ * @param parallelism the parallelism level
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ static void sort(float[] a, int parallelism, int low, int high) {
+ /*
+ * Phase 1. Count the number of negative zero -0.0f,
+ * turn them into positive zero, and move all NaNs
+ * to the end of the array.
+ */
+ int numNegativeZero = 0;
+
+ for (int k = high; k > low; ) {
+ float ak = a[--k];
+
+ if (ak == 0.0f && Float.floatToRawIntBits(ak) < 0) { // ak is -0.0f
+ numNegativeZero += 1;
+ a[k] = 0.0f;
+ } else if (ak != ak) { // ak is NaN
+ a[k] = a[--high];
+ a[high] = ak;
}
}
/*
- * Phase 2: Sort everything except NaNs (which are already in place).
+ * Phase 2. Sort everything except NaNs,
+ * which are already in place.
*/
- doSort(a, left, right, work, workBase, workLen);
-
- /*
- * Phase 3: Place negative zeros before positive zeros.
- */
- int hi = right;
+ int size = high - low;
+
+ if (parallelism > 1 && size > MIN_PARALLEL_SORT_SIZE) {
+ int depth = getDepth(parallelism, size >> 12);
+ float[] b = depth == 0 ? null : new float[size];
+ new Sorter(null, a, b, low, size, low, depth).invoke();
+ } else {
+ sort(null, a, 0, low, high);
+ }
/*
- * Find the first zero, or first positive, or last negative element.
+ * Phase 3. Turn positive zero 0.0f
+ * back into negative zero -0.0f.
*/
- while (left < hi) {
- int middle = (left + hi) >>> 1;
- float middleValue = a[middle];
-
- if (middleValue < 0.0f) {
- left = middle + 1;
+ if (++numNegativeZero == 1) {
+ return;
+ }
+
+ /*
+ * Find the position one less than
+ * the index of the first zero.
+ */
+ while (low <= high) {
+ int middle = (low + high) >>> 1;
+
+ if (a[middle] < 0) {
+ low = middle + 1;
} else {
- hi = middle;
+ high = middle - 1;
}
}
/*
- * Skip the last negative value (if any) or all leading negative zeros.
+ * Replace the required number of 0.0f by -0.0f.
*/
- while (left <= right && Float.floatToRawIntBits(a[left]) < 0) {
- ++left;
+ while (--numNegativeZero > 0) {
+ a[++high] = -0.0f;
}
-
- /*
- * Move negative zeros to the beginning of the sub-range.
- *
- * Partitioning:
- *
- * +----------------------------------------------------+
- * | < 0.0 | -0.0 | 0.0 | ? ( >= 0.0 ) |
- * +----------------------------------------------------+
- * ^ ^ ^
- * | | |
- * left p k
- *
- * Invariants:
- *
- * all in (*, left) < 0.0
- * all in [left, p) == -0.0
- * all in [p, k) == 0.0
- * all in [k, right] >= 0.0
- *
- * Pointer k is the first index of ?-part.
- */
- for (int k = left, p = left - 1; ++k <= right; ) {
- float ak = a[k];
- if (ak != 0.0f) {
- break;
+ }
+
+ /**
+ * Sorts the specified array using the Dual-Pivot Quicksort and/or
+ * other sorts in special-cases, possibly with parallel partitions.
+ *
+ * @param sorter parallel context
+ * @param a the array to be sorted
+ * @param bits the combination of recursion depth and bit flag, where
+ * the right bit "0" indicates that array is the leftmost part
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ static void sort(Sorter sorter, float[] a, int bits, int low, int high) {
+ while (true) {
+ int end = high - 1, size = high - low;
+
+ /*
+ * Run mixed insertion sort on small non-leftmost parts.
+ */
+ if (size < MAX_MIXED_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
+ mixedInsertionSort(a, low, high - 3 * ((size >> 5) << 3), high);
+ return;
+ }
+
+ /*
+ * Invoke insertion sort on small leftmost part.
+ */
+ if (size < MAX_INSERTION_SORT_SIZE) {
+ insertionSort(a, low, high);
+ return;
+ }
+
+ /*
+ * Check if the whole array or large non-leftmost
+ * parts are nearly sorted and then merge runs.
+ */
+ if ((bits == 0 || size > MIN_TRY_MERGE_SIZE && (bits & 1) > 0)
+ && tryMergeRuns(sorter, a, low, size)) {
+ return;
+ }
+
+ /*
+ * Switch to heap sort if execution
+ * time is becoming quadratic.
+ */
+ if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
+ heapSort(a, low, high);
+ return;
+ }
+
+ /*
+ * Use an inexpensive approximation of the golden ratio
+ * to select five sample elements and determine pivots.
+ */
+ int step = (size >> 3) * 3 + 3;
+
+ /*
+ * Five elements around (and including) the central element
+ * will be used for pivot selection as described below. The
+ * unequal choice of spacing these elements was empirically
+ * determined to work well on a wide variety of inputs.
+ */
+ int e1 = low + step;
+ int e5 = end - step;
+ int e3 = (e1 + e5) >>> 1;
+ int e2 = (e1 + e3) >>> 1;
+ int e4 = (e3 + e5) >>> 1;
+ float a3 = a[e3];
+
+ /*
+ * Sort these elements in place by the combination
+ * of 4-element sorting network and insertion sort.
+ *
+ * 5 ------o-----------o------------
+ * | |
+ * 4 ------|-----o-----o-----o------
+ * | | |
+ * 2 ------o-----|-----o-----o------
+ * | |
+ * 1 ------------o-----o------------
+ */
+ if (a[e5] < a[e2]) { float t = a[e5]; a[e5] = a[e2]; a[e2] = t; }
+ if (a[e4] < a[e1]) { float t = a[e4]; a[e4] = a[e1]; a[e1] = t; }
+ if (a[e5] < a[e4]) { float t = a[e5]; a[e5] = a[e4]; a[e4] = t; }
+ if (a[e2] < a[e1]) { float t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
+ if (a[e4] < a[e2]) { float t = a[e4]; a[e4] = a[e2]; a[e2] = t; }
+
+ if (a3 < a[e2]) {
+ if (a3 < a[e1]) {
+ a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = a3;
+ } else {
+ a[e3] = a[e2]; a[e2] = a3;
+ }
+ } else if (a3 > a[e4]) {
+ if (a3 > a[e5]) {
+ a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = a3;
+ } else {
+ a[e3] = a[e4]; a[e4] = a3;
+ }
}
- if (Float.floatToRawIntBits(ak) < 0) { // ak is -0.0f
- a[k] = 0.0f;
- a[++p] = -0.0f;
+
+ // Pointers
+ int lower = low; // The index of the last element of the left part
+ int upper = end; // The index of the first element of the right part
+
+ /*
+ * Partitioning with 2 pivots in case of different elements.
+ */
+ if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
+
+ /*
+ * Use the first and fifth of the five sorted elements as
+ * the pivots. These values are inexpensive approximation
+ * of tertiles. Note, that pivot1 < pivot2.
+ */
+ float pivot1 = a[e1];
+ float pivot2 = a[e5];
+
+ /*
+ * The first and the last elements to be sorted are moved
+ * to the locations formerly occupied by the pivots. When
+ * partitioning is completed, the pivots are swapped back
+ * into their final positions, and excluded from the next
+ * subsequent sorting.
+ */
+ a[e1] = a[lower];
+ a[e5] = a[upper];
+
+ /*
+ * Skip elements, which are less or greater than the pivots.
+ */
+ while (a[++lower] < pivot1);
+ while (a[--upper] > pivot2);
+
+ /*
+ * Backward 3-interval partitioning
+ *
+ * left part central part right part
+ * +------------------------------------------------------------+
+ * | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
+ * +------------------------------------------------------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Invariants:
+ *
+ * all in (low, lower] < pivot1
+ * pivot1 <= all in (k, upper) <= pivot2
+ * all in [upper, end) > pivot2
+ *
+ * Pointer k is the last index of ?-part
+ */
+ for (int unused = --lower, k = ++upper; --k > lower; ) {
+ float ak = a[k];
+
+ if (ak < pivot1) { // Move a[k] to the left side
+ while (lower < k) {
+ if (a[++lower] >= pivot1) {
+ if (a[lower] > pivot2) {
+ a[k] = a[--upper];
+ a[upper] = a[lower];
+ } else {
+ a[k] = a[lower];
+ }
+ a[lower] = ak;
+ break;
+ }
+ }
+ } else if (ak > pivot2) { // Move a[k] to the right side
+ a[k] = a[--upper];
+ a[upper] = ak;
+ }
+ }
+
+ /*
+ * Swap the pivots into their final positions.
+ */
+ a[low] = a[lower]; a[lower] = pivot1;
+ a[end] = a[upper]; a[upper] = pivot2;
+
+ /*
+ * Sort non-left parts recursively (possibly in parallel),
+ * excluding known pivots.
+ */
+ if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
+ sorter.forkSorter(bits | 1, lower + 1, upper);
+ sorter.forkSorter(bits | 1, upper + 1, high);
+ } else {
+ sort(sorter, a, bits | 1, lower + 1, upper);
+ sort(sorter, a, bits | 1, upper + 1, high);
+ }
+
+ } else { // Use single pivot in case of many equal elements
+
+ /*
+ * Use the third of the five sorted elements as the pivot.
+ * This value is inexpensive approximation of the median.
+ */
+ float pivot = a[e3];
+
+ /*
+ * The first element to be sorted is moved to the
+ * location formerly occupied by the pivot. After
+ * completion of partitioning the pivot is swapped
+ * back into its final position, and excluded from
+ * the next subsequent sorting.
+ */
+ a[e3] = a[lower];
+
+ /*
+ * Traditional 3-way (Dutch National Flag) partitioning
+ *
+ * left part central part right part
+ * +------------------------------------------------------+
+ * | < pivot | ? | == pivot | > pivot |
+ * +------------------------------------------------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Invariants:
+ *
+ * all in (low, lower] < pivot
+ * all in (k, upper) == pivot
+ * all in [upper, end] > pivot
+ *
+ * Pointer k is the last index of ?-part
+ */
+ for (int k = ++upper; --k > lower; ) {
+ float ak = a[k];
+
+ if (ak != pivot) {
+ a[k] = pivot;
+
+ if (ak < pivot) { // Move a[k] to the left side
+ while (a[++lower] < pivot);
+
+ if (a[lower] > pivot) {
+ a[--upper] = a[lower];
+ }
+ a[lower] = ak;
+ } else { // ak > pivot - Move a[k] to the right side
+ a[--upper] = ak;
+ }
+ }
+ }
+
+ /*
+ * Swap the pivot into its final position.
+ */
+ a[low] = a[lower]; a[lower] = pivot;
+
+ /*
+ * Sort the right part (possibly in parallel), excluding
+ * known pivot. All elements from the central part are
+ * equal and therefore already sorted.
+ */
+ if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
+ sorter.forkSorter(bits | 1, upper, high);
+ } else {
+ sort(sorter, a, bits | 1, upper, high);
+ }
+ }
+ high = lower; // Iterate along the left part
+ }
+ }
+
+ /**
+ * Sorts the specified range of the array using mixed insertion sort.
+ *
+ * Mixed insertion sort is combination of simple insertion sort,
+ * pin insertion sort and pair insertion sort.
+ *
+ * In the context of Dual-Pivot Quicksort, the pivot element
+ * from the left part plays the role of sentinel, because it
+ * is less than any elements from the given part. Therefore,
+ * expensive check of the left range can be skipped on each
+ * iteration unless it is the leftmost call.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param end the index of the last element for simple insertion sort
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void mixedInsertionSort(float[] a, int low, int end, int high) {
+ if (end == high) {
+
+ /*
+ * Invoke simple insertion sort on tiny array.
+ */
+ for (int i; ++low < end; ) {
+ float ai = a[i = low];
+
+ while (ai < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = ai;
+ }
+ } else {
+
+ /*
+ * Start with pin insertion sort on small part.
+ *
+ * Pin insertion sort is extended simple insertion sort.
+ * The main idea of this sort is to put elements larger
+ * than an element called pin to the end of array (the
+ * proper area for such elements). It avoids expensive
+ * movements of these elements through the whole array.
+ */
+ float pin = a[end];
+
+ for (int i, p = high; ++low < end; ) {
+ float ai = a[i = low];
+
+ if (ai < a[i - 1]) { // Small element
+
+ /*
+ * Insert small element into sorted part.
+ */
+ a[i] = a[--i];
+
+ while (ai < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = ai;
+
+ } else if (p > i && ai > pin) { // Large element
+
+ /*
+ * Find element smaller than pin.
+ */
+ while (a[--p] > pin);
+
+ /*
+ * Swap it with large element.
+ */
+ if (p > i) {
+ ai = a[p];
+ a[p] = a[i];
+ }
+
+ /*
+ * Insert small element into sorted part.
+ */
+ while (ai < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = ai;
+ }
+ }
+
+ /*
+ * Continue with pair insertion sort on remain part.
+ */
+ for (int i; low < high; ++low) {
+ float a1 = a[i = low], a2 = a[++low];
+
+ /*
+ * Insert two elements per iteration: at first, insert the
+ * larger element and then insert the smaller element, but
+ * from the position where the larger element was inserted.
+ */
+ if (a1 > a2) {
+
+ while (a1 < a[--i]) {
+ a[i + 2] = a[i];
+ }
+ a[++i + 1] = a1;
+
+ while (a2 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a2;
+
+ } else if (a1 < a[i - 1]) {
+
+ while (a2 < a[--i]) {
+ a[i + 2] = a[i];
+ }
+ a[++i + 1] = a2;
+
+ while (a1 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a1;
+ }
+ }
+ }
+ }
+
+ /**
+ * Sorts the specified range of the array using insertion sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void insertionSort(float[] a, int low, int high) {
+ for (int i, k = low; ++k < high; ) {
+ float ai = a[i = k];
+
+ if (ai < a[i - 1]) {
+ while (--i >= low && ai < a[i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = ai;
}
}
}
/**
- * Sorts the specified range of the array.
+ * Sorts the specified range of the array using heap sort.
*
* @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param work a workspace array (slice)
- * @param workBase origin of usable space in work array
- * @param workLen usable size of work array
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
*/
- private static void doSort(float[] a, int left, int right,
- float[] work, int workBase, int workLen) {
- // Use Quicksort on small arrays
- if (right - left < QUICKSORT_THRESHOLD) {
- sort(a, left, right, true);
- return;
- }
-
- /*
- * Index run[i] is the start of i-th run
- * (ascending or descending sequence).
- */
- int[] run = new int[MAX_RUN_COUNT + 1];
- int count = 0; run[0] = left;
-
- // Check if the array is nearly sorted
- for (int k = left; k < right; run[count] = k) {
- // Equal items in the beginning of the sequence
- while (k < right && a[k] == a[k + 1])
- k++;
- if (k == right) break; // Sequence finishes with equal items
- if (a[k] < a[k + 1]) { // ascending
- while (++k <= right && a[k - 1] <= a[k]);
- } else if (a[k] > a[k + 1]) { // descending
- while (++k <= right && a[k - 1] >= a[k]);
- // Transform into an ascending sequence
- for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {
- float t = a[lo]; a[lo] = a[hi]; a[hi] = t;
- }
- }
-
- // Merge a transformed descending sequence followed by an
- // ascending sequence
- if (run[count] > left && a[run[count]] >= a[run[count] - 1]) {
- count--;
- }
-
- /*
- * The array is not highly structured,
- * use Quicksort instead of merge sort.
- */
- if (++count == MAX_RUN_COUNT) {
- sort(a, left, right, true);
- return;
- }
+ private static void heapSort(float[] a, int low, int high) {
+ for (int k = (low + high) >>> 1; k > low; ) {
+ pushDown(a, --k, a[k], low, high);
}
-
- // These invariants should hold true:
- // run[0] = 0
- // run[<last>] = right + 1; (terminator)
-
- if (count == 0) {
- // A single equal run
- return;
- } else if (count == 1 && run[count] > right) {
- // Either a single ascending or a transformed descending run.
- // Always check that a final run is a proper terminator, otherwise
- // we have an unterminated trailing run, to handle downstream.
- return;
- }
- right++;
- if (run[count] < right) {
- // Corner case: the final run is not a terminator. This may happen
- // if a final run is an equals run, or there is a single-element run
- // at the end. Fix up by adding a proper terminator at the end.
- // Note that we terminate with (right + 1), incremented earlier.
- run[++count] = right;
- }
-
- // Determine alternation base for merge
- byte odd = 0;
- for (int n = 1; (n <<= 1) < count; odd ^= 1);
-
- // Use or create temporary array b for merging
- float[] b; // temp array; alternates with a
- int ao, bo; // array offsets from 'left'
- int blen = right - left; // space needed for b
- if (work == null || workLen < blen || workBase + blen > work.length) {
- work = new float[blen];
- workBase = 0;
- }
- if (odd == 0) {
- System.arraycopy(a, left, work, workBase, blen);
- b = a;
- bo = 0;
- a = work;
- ao = workBase - left;
- } else {
- b = work;
- ao = 0;
- bo = workBase - left;
- }
-
- // Merging
- for (int last; count > 1; count = last) {
- for (int k = (last = 0) + 2; k <= count; k += 2) {
- int hi = run[k], mi = run[k - 1];
- for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
- if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {
- b[i + bo] = a[p++ + ao];
- } else {
- b[i + bo] = a[q++ + ao];
- }
- }
- run[++last] = hi;
- }
- if ((count & 1) != 0) {
- for (int i = right, lo = run[count - 1]; --i >= lo;
- b[i + bo] = a[i + ao]
- );
- run[++last] = right;
- }
- float[] t = a; a = b; b = t;
- int o = ao; ao = bo; bo = o;
+ while (--high > low) {
+ float max = a[low];
+ pushDown(a, low, a[high], low, high);
+ a[high] = max;
}
}
/**
- * Sorts the specified range of the array by Dual-Pivot Quicksort.
+ * Pushes specified element down during heap sort.
*
- * @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param leftmost indicates if this part is the leftmost in the range
+ * @param a the given array
+ * @param p the start index
+ * @param value the given element
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
*/
- private static void sort(float[] a, int left, int right, boolean leftmost) {
- int length = right - left + 1;
-
- // Use insertion sort on tiny arrays
- if (length < INSERTION_SORT_THRESHOLD) {
- if (leftmost) {
- /*
- * Traditional (without sentinel) insertion sort,
- * optimized for server VM, is used in case of
- * the leftmost part.
- */
- for (int i = left, j = i; i < right; j = ++i) {
- float ai = a[i + 1];
- while (ai < a[j]) {
- a[j + 1] = a[j];
- if (j-- == left) {
- break;
- }
- }
- a[j + 1] = ai;
- }
- } else {
- /*
- * Skip the longest ascending sequence.
- */
- do {
- if (left >= right) {
- return;
- }
- } while (a[++left] >= a[left - 1]);
-
- /*
- * Every element from adjoining part plays the role
- * of sentinel, therefore this allows us to avoid the
- * left range check on each iteration. Moreover, we use
- * the more optimized algorithm, so called pair insertion
- * sort, which is faster (in the context of Quicksort)
- * than traditional implementation of insertion sort.
- */
- for (int k = left; ++left <= right; k = ++left) {
- float a1 = a[k], a2 = a[left];
-
- if (a1 < a2) {
- a2 = a1; a1 = a[left];
- }
- while (a1 < a[--k]) {
- a[k + 2] = a[k];
- }
- a[++k + 1] = a1;
-
- while (a2 < a[--k]) {
- a[k + 1] = a[k];
- }
- a[k + 1] = a2;
- }
- float last = a[right];
-
- while (last < a[--right]) {
- a[right + 1] = a[right];
- }
- a[right + 1] = last;
+ private static void pushDown(float[] a, int p, float value, int low, int high) {
+ for (int k ;; a[p] = a[p = k]) {
+ k = (p << 1) - low + 2; // Index of the right child
+
+ if (k > high) {
+ break;
+ }
+ if (k == high || a[k] < a[k - 1]) {
+ --k;
+ }
+ if (a[k] <= value) {
+ break;
}
- return;
}
-
- // Inexpensive approximation of length / 7
- int seventh = (length >> 3) + (length >> 6) + 1;
+ a[p] = value;
+ }
+
+ /**
+ * Tries to sort the specified range of the array.
+ *
+ * @param sorter parallel context
+ * @param a the array to be sorted
+ * @param low the index of the first element to be sorted
+ * @param size the array size
+ * @return true if finally sorted, false otherwise
+ */
+ private static boolean tryMergeRuns(Sorter sorter, float[] a, int low, int size) {
/*
- * Sort five evenly spaced elements around (and including) the
- * center element in the range. These elements will be used for
- * pivot selection as described below. The choice for spacing
- * these elements was empirically determined to work well on
- * a wide variety of inputs.
+ * The run array is constructed only if initial runs are
+ * long enough to continue, run[i] then holds start index
+ * of the i-th sequence of elements in non-descending order.
*/
- int e3 = (left + right) >>> 1; // The midpoint
- int e2 = e3 - seventh;
- int e1 = e2 - seventh;
- int e4 = e3 + seventh;
- int e5 = e4 + seventh;
-
- // Sort these elements using insertion sort
- if (a[e2] < a[e1]) { float t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
-
- if (a[e3] < a[e2]) { float t = a[e3]; a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- if (a[e4] < a[e3]) { float t = a[e4]; a[e4] = a[e3]; a[e3] = t;
- if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- }
- if (a[e5] < a[e4]) { float t = a[e5]; a[e5] = a[e4]; a[e4] = t;
- if (t < a[e3]) { a[e4] = a[e3]; a[e3] = t;
- if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- }
- }
-
- // Pointers
- int less = left; // The index of the first element of center part
- int great = right; // The index before the first element of right part
-
- if (a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5]) {
- /*
- * Use the second and fourth of the five sorted elements as pivots.
- * These values are inexpensive approximations of the first and
- * second terciles of the array. Note that pivot1 <= pivot2.
- */
- float pivot1 = a[e2];
- float pivot2 = a[e4];
-
- /*
- * The first and the last elements to be sorted are moved to the
- * locations formerly occupied by the pivots. When partitioning
- * is complete, the pivots are swapped back into their final
- * positions, and excluded from subsequent sorting.
- */
- a[e2] = a[left];
- a[e4] = a[right];
-
- /*
- * Skip elements, which are less or greater than pivot values.
- */
- while (a[++less] < pivot1);
- while (a[--great] > pivot2);
+ int[] run = null;
+ int high = low + size;
+ int count = 1, last = low;
+
+ /*
+ * Identify all possible runs.
+ */
+ for (int k = low + 1; k < high; ) {
/*
- * Partitioning:
- *
- * left part center part right part
- * +--------------------------------------------------------------+
- * | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 |
- * +--------------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (left, less) < pivot1
- * pivot1 <= all in [less, k) <= pivot2
- * all in (great, right) > pivot2
- *
- * Pointer k is the first index of ?-part.
+ * Find the end index of the current run.
*/
- outer:
- for (int k = less - 1; ++k <= great; ) {
- float ak = a[k];
- if (ak < pivot1) { // Move a[k] to left part
- a[k] = a[less];
- /*
- * Here and below we use "a[i] = b; i++;" instead
- * of "a[i++] = b;" due to performance issue.
- */
- a[less] = ak;
- ++less;
- } else if (ak > pivot2) { // Move a[k] to right part
- while (a[great] > pivot2) {
- if (great-- == k) {
- break outer;
- }
- }
- if (a[great] < pivot1) { // a[great] <= pivot2
- a[k] = a[less];
- a[less] = a[great];
- ++less;
- } else { // pivot1 <= a[great] <= pivot2
- a[k] = a[great];
- }
- /*
- * Here and below we use "a[i] = b; i--;" instead
- * of "a[i--] = b;" due to performance issue.
- */
- a[great] = ak;
- --great;
- }
- }
-
- // Swap pivots into their final positions
- a[left] = a[less - 1]; a[less - 1] = pivot1;
- a[right] = a[great + 1]; a[great + 1] = pivot2;
-
- // Sort left and right parts recursively, excluding known pivots
- sort(a, left, less - 2, leftmost);
- sort(a, great + 2, right, false);
-
- /*
- * If center part is too large (comprises > 4/7 of the array),
- * swap internal pivot values to ends.
- */
- if (less < e1 && e5 < great) {
- /*
- * Skip elements, which are equal to pivot values.
- */
- while (a[less] == pivot1) {
- ++less;
- }
-
- while (a[great] == pivot2) {
- --great;
+ if (a[k - 1] < a[k]) {
+
+ // Identify ascending sequence
+ while (++k < high && a[k - 1] <= a[k]);
+
+ } else if (a[k - 1] > a[k]) {
+
+ // Identify descending sequence
+ while (++k < high && a[k - 1] >= a[k]);
+
+ // Reverse into ascending order
+ for (int i = last - 1, j = k; ++i < --j && a[i] > a[j]; ) {
+ float ai = a[i]; a[i] = a[j]; a[j] = ai;
}
-
- /*
- * Partitioning:
- *
- * left part center part right part
- * +----------------------------------------------------------+
- * | == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 |
- * +----------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (*, less) == pivot1
- * pivot1 < all in [less, k) < pivot2
- * all in (great, *) == pivot2
- *
- * Pointer k is the first index of ?-part.
- */
- outer:
- for (int k = less - 1; ++k <= great; ) {
- float ak = a[k];
- if (ak == pivot1) { // Move a[k] to left part
- a[k] = a[less];
- a[less] = ak;
- ++less;
- } else if (ak == pivot2) { // Move a[k] to right part
- while (a[great] == pivot2) {
- if (great-- == k) {
- break outer;
- }
- }
- if (a[great] == pivot1) { // a[great] < pivot2
- a[k] = a[less];
- /*
- * Even though a[great] equals to pivot1, the
- * assignment a[less] = pivot1 may be incorrect,
- * if a[great] and pivot1 are floating-point zeros
- * of different signs. Therefore in float and
- * double sorting methods we have to use more
- * accurate assignment a[less] = a[great].
- */
- a[less] = a[great];
- ++less;
- } else { // pivot1 < a[great] < pivot2
- a[k] = a[great];
- }
- a[great] = ak;
- --great;
- }
- }
- }
-
- // Sort center part recursively
- sort(a, less, great, false);
-
- } else { // Partitioning with one pivot
- /*
- * Use the third of the five sorted elements as pivot.
- * This value is inexpensive approximation of the median.
- */
- float pivot = a[e3];
-
- /*
- * Partitioning degenerates to the traditional 3-way
- * (or "Dutch National Flag") schema:
- *
- * left part center part right part
- * +-------------------------------------------------+
- * | < pivot | == pivot | ? | > pivot |
- * +-------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (left, less) < pivot
- * all in [less, k) == pivot
- * all in (great, right) > pivot
- *
- * Pointer k is the first index of ?-part.
- */
- for (int k = less; k <= great; ++k) {
- if (a[k] == pivot) {
+ } else { // Identify constant sequence
+ for (float ak = a[k]; ++k < high && ak == a[k]; );
+
+ if (k < high) {
continue;
}
- float ak = a[k];
- if (ak < pivot) { // Move a[k] to left part
- a[k] = a[less];
- a[less] = ak;
- ++less;
- } else { // a[k] > pivot - Move a[k] to right part
- while (a[great] > pivot) {
- --great;
- }
- if (a[great] < pivot) { // a[great] <= pivot
- a[k] = a[less];
- a[less] = a[great];
- ++less;
- } else { // a[great] == pivot
- /*
- * Even though a[great] equals to pivot, the
- * assignment a[k] = pivot may be incorrect,
- * if a[great] and pivot are floating-point
- * zeros of different signs. Therefore in float
- * and double sorting methods we have to use
- * more accurate assignment a[k] = a[great].
- */
- a[k] = a[great];
- }
- a[great] = ak;
- --great;
- }
}
/*
- * Sort left and right parts recursively.
- * All elements from center part are equal
- * and, therefore, already sorted.
+ * Check special cases.
*/
- sort(a, left, less - 1, leftmost);
- sort(a, great + 1, right, false);
+ if (run == null) {
+ if (k == high) {
+
+ /*
+ * The array is monotonous sequence,
+ * and therefore already sorted.
+ */
+ return true;
+ }
+
+ if (k - low < MIN_FIRST_RUN_SIZE) {
+
+ /*
+ * The first run is too small
+ * to proceed with scanning.
+ */
+ return false;
+ }
+
+ run = new int[((size >> 10) | 0x7F) & 0x3FF];
+ run[0] = low;
+
+ } else if (a[last - 1] > a[last]) {
+
+ if (count > (k - low) >> MIN_FIRST_RUNS_FACTOR) {
+
+ /*
+ * The first runs are not long
+ * enough to continue scanning.
+ */
+ return false;
+ }
+
+ if (++count == MAX_RUN_CAPACITY) {
+
+ /*
+ * Array is not highly structured.
+ */
+ return false;
+ }
+
+ if (count == run.length) {
+
+ /*
+ * Increase capacity of index array.
+ */
+ run = Arrays.copyOf(run, count << 1);
+ }
+ }
+ run[count] = (last = k);
}
+
+ /*
+ * Merge runs of highly structured array.
+ */
+ if (count > 1) {
+ float[] b; int offset = low;
+
+ if (sorter == null || (b = (float[]) sorter.b) == null) {
+ b = new float[size];
+ } else {
+ offset = sorter.offset;
+ }
+ mergeRuns(a, b, offset, 1, sorter != null, run, 0, count);
+ }
+ return true;
+ }
+
+ /**
+ * Merges the specified runs.
+ *
+ * @param a the source array
+ * @param b the temporary buffer used in merging
+ * @param offset the start index in the source, inclusive
+ * @param aim specifies merging: to source ( > 0), buffer ( < 0) or any ( == 0)
+ * @param parallel indicates whether merging is performed in parallel
+ * @param run the start indexes of the runs, inclusive
+ * @param lo the start index of the first run, inclusive
+ * @param hi the start index of the last run, inclusive
+ * @return the destination where runs are merged
+ */
+ private static float[] mergeRuns(float[] a, float[] b, int offset,
+ int aim, boolean parallel, int[] run, int lo, int hi) {
+
+ if (hi - lo == 1) {
+ if (aim >= 0) {
+ return a;
+ }
+ for (int i = run[hi], j = i - offset, low = run[lo]; i > low;
+ b[--j] = a[--i]
+ );
+ return b;
+ }
+
+ /*
+ * Split into approximately equal parts.
+ */
+ int mi = lo, rmi = (run[lo] + run[hi]) >>> 1;
+ while (run[++mi + 1] <= rmi);
+
+ /*
+ * Merge the left and right parts.
+ */
+ float[] a1, a2;
+
+ if (parallel && hi - lo > MIN_RUN_COUNT) {
+ RunMerger merger = new RunMerger(a, b, offset, 0, run, mi, hi).forkMe();
+ a1 = mergeRuns(a, b, offset, -aim, true, run, lo, mi);
+ a2 = (float[]) merger.getDestination();
+ } else {
+ a1 = mergeRuns(a, b, offset, -aim, false, run, lo, mi);
+ a2 = mergeRuns(a, b, offset, 0, false, run, mi, hi);
+ }
+
+ float[] dst = a1 == a ? b : a;
+
+ int k = a1 == a ? run[lo] - offset : run[lo];
+ int lo1 = a1 == b ? run[lo] - offset : run[lo];
+ int hi1 = a1 == b ? run[mi] - offset : run[mi];
+ int lo2 = a2 == b ? run[mi] - offset : run[mi];
+ int hi2 = a2 == b ? run[hi] - offset : run[hi];
+
+ if (parallel) {
+ new Merger(null, dst, k, a1, lo1, hi1, a2, lo2, hi2).invoke();
+ } else {
+ mergeParts(null, dst, k, a1, lo1, hi1, a2, lo2, hi2);
+ }
+ return dst;
}
/**
- * Sorts the specified range of the array using the given
- * workspace array slice if possible for merging
+ * Merges the sorted parts.
+ *
+ * @param merger parallel context
+ * @param dst the destination where parts are merged
+ * @param k the start index of the destination, inclusive
+ * @param a1 the first part
+ * @param lo1 the start index of the first part, inclusive
+ * @param hi1 the end index of the first part, exclusive
+ * @param a2 the second part
+ * @param lo2 the start index of the second part, inclusive
+ * @param hi2 the end index of the second part, exclusive
+ */
+ private static void mergeParts(Merger merger, float[] dst, int k,
+ float[] a1, int lo1, int hi1, float[] a2, int lo2, int hi2) {
+
+ if (merger != null && a1 == a2) {
+
+ while (true) {
+
+ /*
+ * The first part must be larger.
+ */
+ if (hi1 - lo1 < hi2 - lo2) {
+ int lo = lo1; lo1 = lo2; lo2 = lo;
+ int hi = hi1; hi1 = hi2; hi2 = hi;
+ }
+
+ /*
+ * Small parts will be merged sequentially.
+ */
+ if (hi1 - lo1 < MIN_PARALLEL_MERGE_PARTS_SIZE) {
+ break;
+ }
+
+ /*
+ * Find the median of the larger part.
+ */
+ int mi1 = (lo1 + hi1) >>> 1;
+ float key = a1[mi1];
+ int mi2 = hi2;
+
+ /*
+ * Partition the smaller part.
+ */
+ for (int loo = lo2; loo < mi2; ) {
+ int t = (loo + mi2) >>> 1;
+
+ if (key > a2[t]) {
+ loo = t + 1;
+ } else {
+ mi2 = t;
+ }
+ }
+
+ int d = mi2 - lo2 + mi1 - lo1;
+
+ /*
+ * Merge the right sub-parts in parallel.
+ */
+ merger.forkMerger(dst, k + d, a1, mi1, hi1, a2, mi2, hi2);
+
+ /*
+ * Process the sub-left parts.
+ */
+ hi1 = mi1;
+ hi2 = mi2;
+ }
+ }
+
+ /*
+ * Merge small parts sequentially.
+ */
+ while (lo1 < hi1 && lo2 < hi2) {
+ dst[k++] = a1[lo1] < a2[lo2] ? a1[lo1++] : a2[lo2++];
+ }
+ if (dst != a1 || k < lo1) {
+ while (lo1 < hi1) {
+ dst[k++] = a1[lo1++];
+ }
+ }
+ if (dst != a2 || k < lo2) {
+ while (lo2 < hi2) {
+ dst[k++] = a2[lo2++];
+ }
+ }
+ }
+
+// [double]
+
+ /**
+ * Sorts the specified range of the array using parallel merge
+ * sort and/or Dual-Pivot Quicksort.
+ *
+ * To balance the faster splitting and parallelism of merge sort
+ * with the faster element partitioning of Quicksort, ranges are
+ * subdivided in tiers such that, if there is enough parallelism,
+ * the four-way parallel merge is started, still ensuring enough
+ * parallelism to process the partitions.
*
* @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param work a workspace array (slice)
- * @param workBase origin of usable space in work array
- * @param workLen usable size of work array
+ * @param parallelism the parallelism level
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
*/
- static void sort(double[] a, int left, int right,
- double[] work, int workBase, int workLen) {
+ static void sort(double[] a, int parallelism, int low, int high) {
+ /*
+ * Phase 1. Count the number of negative zero -0.0d,
+ * turn them into positive zero, and move all NaNs
+ * to the end of the array.
+ */
+ int numNegativeZero = 0;
+
+ for (int k = high; k > low; ) {
+ double ak = a[--k];
+
+ if (ak == 0.0d && Double.doubleToRawLongBits(ak) < 0) { // ak is -0.0d
+ numNegativeZero += 1;
+ a[k] = 0.0d;
+ } else if (ak != ak) { // ak is NaN
+ a[k] = a[--high];
+ a[high] = ak;
+ }
+ }
+
/*
- * Phase 1: Move NaNs to the end of the array.
+ * Phase 2. Sort everything except NaNs,
+ * which are already in place.
*/
- while (left <= right && Double.isNaN(a[right])) {
- --right;
+ int size = high - low;
+
+ if (parallelism > 1 && size > MIN_PARALLEL_SORT_SIZE) {
+ int depth = getDepth(parallelism, size >> 12);
+ double[] b = depth == 0 ? null : new double[size];
+ new Sorter(null, a, b, low, size, low, depth).invoke();
+ } else {
+ sort(null, a, 0, low, high);
}
- for (int k = right; --k >= left; ) {
- double ak = a[k];
- if (ak != ak) { // a[k] is NaN
- a[k] = a[right];
- a[right] = ak;
- --right;
+
+ /*
+ * Phase 3. Turn positive zero 0.0d
+ * back into negative zero -0.0d.
+ */
+ if (++numNegativeZero == 1) {
+ return;
+ }
+
+ /*
+ * Find the position one less than
+ * the index of the first zero.
+ */
+ while (low <= high) {
+ int middle = (low + high) >>> 1;
+
+ if (a[middle] < 0) {
+ low = middle + 1;
+ } else {
+ high = middle - 1;
}
}
/*
- * Phase 2: Sort everything except NaNs (which are already in place).
- */
- doSort(a, left, right, work, workBase, workLen);
-
- /*
- * Phase 3: Place negative zeros before positive zeros.
- */
- int hi = right;
-
- /*
- * Find the first zero, or first positive, or last negative element.
+ * Replace the required number of 0.0d by -0.0d.
*/
- while (left < hi) {
- int middle = (left + hi) >>> 1;
- double middleValue = a[middle];
-
- if (middleValue < 0.0d) {
- left = middle + 1;
- } else {
- hi = middle;
- }
- }
-
- /*
- * Skip the last negative value (if any) or all leading negative zeros.
- */
- while (left <= right && Double.doubleToRawLongBits(a[left]) < 0) {
- ++left;
+ while (--numNegativeZero > 0) {
+ a[++high] = -0.0d;
}
-
- /*
- * Move negative zeros to the beginning of the sub-range.
- *
- * Partitioning:
- *
- * +----------------------------------------------------+
- * | < 0.0 | -0.0 | 0.0 | ? ( >= 0.0 ) |
- * +----------------------------------------------------+
- * ^ ^ ^
- * | | |
- * left p k
- *
- * Invariants:
- *
- * all in (*, left) < 0.0
- * all in [left, p) == -0.0
- * all in [p, k) == 0.0
- * all in [k, right] >= 0.0
- *
- * Pointer k is the first index of ?-part.
- */
- for (int k = left, p = left - 1; ++k <= right; ) {
- double ak = a[k];
- if (ak != 0.0d) {
- break;
+ }
+
+ /**
+ * Sorts the specified array using the Dual-Pivot Quicksort and/or
+ * other sorts in special-cases, possibly with parallel partitions.
+ *
+ * @param sorter parallel context
+ * @param a the array to be sorted
+ * @param bits the combination of recursion depth and bit flag, where
+ * the right bit "0" indicates that array is the leftmost part
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ static void sort(Sorter sorter, double[] a, int bits, int low, int high) {
+ while (true) {
+ int end = high - 1, size = high - low;
+
+ /*
+ * Run mixed insertion sort on small non-leftmost parts.
+ */
+ if (size < MAX_MIXED_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
+ mixedInsertionSort(a, low, high - 3 * ((size >> 5) << 3), high);
+ return;
+ }
+
+ /*
+ * Invoke insertion sort on small leftmost part.
+ */
+ if (size < MAX_INSERTION_SORT_SIZE) {
+ insertionSort(a, low, high);
+ return;
+ }
+
+ /*
+ * Check if the whole array or large non-leftmost
+ * parts are nearly sorted and then merge runs.
+ */
+ if ((bits == 0 || size > MIN_TRY_MERGE_SIZE && (bits & 1) > 0)
+ && tryMergeRuns(sorter, a, low, size)) {
+ return;
+ }
+
+ /*
+ * Switch to heap sort if execution
+ * time is becoming quadratic.
+ */
+ if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
+ heapSort(a, low, high);
+ return;
+ }
+
+ /*
+ * Use an inexpensive approximation of the golden ratio
+ * to select five sample elements and determine pivots.
+ */
+ int step = (size >> 3) * 3 + 3;
+
+ /*
+ * Five elements around (and including) the central element
+ * will be used for pivot selection as described below. The
+ * unequal choice of spacing these elements was empirically
+ * determined to work well on a wide variety of inputs.
+ */
+ int e1 = low + step;
+ int e5 = end - step;
+ int e3 = (e1 + e5) >>> 1;
+ int e2 = (e1 + e3) >>> 1;
+ int e4 = (e3 + e5) >>> 1;
+ double a3 = a[e3];
+
+ /*
+ * Sort these elements in place by the combination
+ * of 4-element sorting network and insertion sort.
+ *
+ * 5 ------o-----------o------------
+ * | |
+ * 4 ------|-----o-----o-----o------
+ * | | |
+ * 2 ------o-----|-----o-----o------
+ * | |
+ * 1 ------------o-----o------------
+ */
+ if (a[e5] < a[e2]) { double t = a[e5]; a[e5] = a[e2]; a[e2] = t; }
+ if (a[e4] < a[e1]) { double t = a[e4]; a[e4] = a[e1]; a[e1] = t; }
+ if (a[e5] < a[e4]) { double t = a[e5]; a[e5] = a[e4]; a[e4] = t; }
+ if (a[e2] < a[e1]) { double t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
+ if (a[e4] < a[e2]) { double t = a[e4]; a[e4] = a[e2]; a[e2] = t; }
+
+ if (a3 < a[e2]) {
+ if (a3 < a[e1]) {
+ a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = a3;
+ } else {
+ a[e3] = a[e2]; a[e2] = a3;
+ }
+ } else if (a3 > a[e4]) {
+ if (a3 > a[e5]) {
+ a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = a3;
+ } else {
+ a[e3] = a[e4]; a[e4] = a3;
+ }
}
- if (Double.doubleToRawLongBits(ak) < 0) { // ak is -0.0d
- a[k] = 0.0d;
- a[++p] = -0.0d;
+
+ // Pointers
+ int lower = low; // The index of the last element of the left part
+ int upper = end; // The index of the first element of the right part
+
+ /*
+ * Partitioning with 2 pivots in case of different elements.
+ */
+ if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
+
+ /*
+ * Use the first and fifth of the five sorted elements as
+ * the pivots. These values are inexpensive approximation
+ * of tertiles. Note, that pivot1 < pivot2.
+ */
+ double pivot1 = a[e1];
+ double pivot2 = a[e5];
+
+ /*
+ * The first and the last elements to be sorted are moved
+ * to the locations formerly occupied by the pivots. When
+ * partitioning is completed, the pivots are swapped back
+ * into their final positions, and excluded from the next
+ * subsequent sorting.
+ */
+ a[e1] = a[lower];
+ a[e5] = a[upper];
+
+ /*
+ * Skip elements, which are less or greater than the pivots.
+ */
+ while (a[++lower] < pivot1);
+ while (a[--upper] > pivot2);
+
+ /*
+ * Backward 3-interval partitioning
+ *
+ * left part central part right part
+ * +------------------------------------------------------------+
+ * | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
+ * +------------------------------------------------------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Invariants:
+ *
+ * all in (low, lower] < pivot1
+ * pivot1 <= all in (k, upper) <= pivot2
+ * all in [upper, end) > pivot2
+ *
+ * Pointer k is the last index of ?-part
+ */
+ for (int unused = --lower, k = ++upper; --k > lower; ) {
+ double ak = a[k];
+
+ if (ak < pivot1) { // Move a[k] to the left side
+ while (lower < k) {
+ if (a[++lower] >= pivot1) {
+ if (a[lower] > pivot2) {
+ a[k] = a[--upper];
+ a[upper] = a[lower];
+ } else {
+ a[k] = a[lower];
+ }
+ a[lower] = ak;
+ break;
+ }
+ }
+ } else if (ak > pivot2) { // Move a[k] to the right side
+ a[k] = a[--upper];
+ a[upper] = ak;
+ }
+ }
+
+ /*
+ * Swap the pivots into their final positions.
+ */
+ a[low] = a[lower]; a[lower] = pivot1;
+ a[end] = a[upper]; a[upper] = pivot2;
+
+ /*
+ * Sort non-left parts recursively (possibly in parallel),
+ * excluding known pivots.
+ */
+ if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
+ sorter.forkSorter(bits | 1, lower + 1, upper);
+ sorter.forkSorter(bits | 1, upper + 1, high);
+ } else {
+ sort(sorter, a, bits | 1, lower + 1, upper);
+ sort(sorter, a, bits | 1, upper + 1, high);
+ }
+
+ } else { // Use single pivot in case of many equal elements
+
+ /*
+ * Use the third of the five sorted elements as the pivot.
+ * This value is inexpensive approximation of the median.
+ */
+ double pivot = a[e3];
+
+ /*
+ * The first element to be sorted is moved to the
+ * location formerly occupied by the pivot. After
+ * completion of partitioning the pivot is swapped
+ * back into its final position, and excluded from
+ * the next subsequent sorting.
+ */
+ a[e3] = a[lower];
+
+ /*
+ * Traditional 3-way (Dutch National Flag) partitioning
+ *
+ * left part central part right part
+ * +------------------------------------------------------+
+ * | < pivot | ? | == pivot | > pivot |
+ * +------------------------------------------------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Invariants:
+ *
+ * all in (low, lower] < pivot
+ * all in (k, upper) == pivot
+ * all in [upper, end] > pivot
+ *
+ * Pointer k is the last index of ?-part
+ */
+ for (int k = ++upper; --k > lower; ) {
+ double ak = a[k];
+
+ if (ak != pivot) {
+ a[k] = pivot;
+
+ if (ak < pivot) { // Move a[k] to the left side
+ while (a[++lower] < pivot);
+
+ if (a[lower] > pivot) {
+ a[--upper] = a[lower];
+ }
+ a[lower] = ak;
+ } else { // ak > pivot - Move a[k] to the right side
+ a[--upper] = ak;
+ }
+ }
+ }
+
+ /*
+ * Swap the pivot into its final position.
+ */
+ a[low] = a[lower]; a[lower] = pivot;
+
+ /*
+ * Sort the right part (possibly in parallel), excluding
+ * known pivot. All elements from the central part are
+ * equal and therefore already sorted.
+ */
+ if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
+ sorter.forkSorter(bits | 1, upper, high);
+ } else {
+ sort(sorter, a, bits | 1, upper, high);
+ }
+ }
+ high = lower; // Iterate along the left part
+ }
+ }
+
+ /**
+ * Sorts the specified range of the array using mixed insertion sort.
+ *
+ * Mixed insertion sort is combination of simple insertion sort,
+ * pin insertion sort and pair insertion sort.
+ *
+ * In the context of Dual-Pivot Quicksort, the pivot element
+ * from the left part plays the role of sentinel, because it
+ * is less than any elements from the given part. Therefore,
+ * expensive check of the left range can be skipped on each
+ * iteration unless it is the leftmost call.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param end the index of the last element for simple insertion sort
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void mixedInsertionSort(double[] a, int low, int end, int high) {
+ if (end == high) {
+
+ /*
+ * Invoke simple insertion sort on tiny array.
+ */
+ for (int i; ++low < end; ) {
+ double ai = a[i = low];
+
+ while (ai < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = ai;
+ }
+ } else {
+
+ /*
+ * Start with pin insertion sort on small part.
+ *
+ * Pin insertion sort is extended simple insertion sort.
+ * The main idea of this sort is to put elements larger
+ * than an element called pin to the end of array (the
+ * proper area for such elements). It avoids expensive
+ * movements of these elements through the whole array.
+ */
+ double pin = a[end];
+
+ for (int i, p = high; ++low < end; ) {
+ double ai = a[i = low];
+
+ if (ai < a[i - 1]) { // Small element
+
+ /*
+ * Insert small element into sorted part.
+ */
+ a[i] = a[--i];
+
+ while (ai < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = ai;
+
+ } else if (p > i && ai > pin) { // Large element
+
+ /*
+ * Find element smaller than pin.
+ */
+ while (a[--p] > pin);
+
+ /*
+ * Swap it with large element.
+ */
+ if (p > i) {
+ ai = a[p];
+ a[p] = a[i];
+ }
+
+ /*
+ * Insert small element into sorted part.
+ */
+ while (ai < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = ai;
+ }
+ }
+
+ /*
+ * Continue with pair insertion sort on remain part.
+ */
+ for (int i; low < high; ++low) {
+ double a1 = a[i = low], a2 = a[++low];
+
+ /*
+ * Insert two elements per iteration: at first, insert the
+ * larger element and then insert the smaller element, but
+ * from the position where the larger element was inserted.
+ */
+ if (a1 > a2) {
+
+ while (a1 < a[--i]) {
+ a[i + 2] = a[i];
+ }
+ a[++i + 1] = a1;
+
+ while (a2 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a2;
+
+ } else if (a1 < a[i - 1]) {
+
+ while (a2 < a[--i]) {
+ a[i + 2] = a[i];
+ }
+ a[++i + 1] = a2;
+
+ while (a1 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a1;
+ }
}
}
}
/**
- * Sorts the specified range of the array.
+ * Sorts the specified range of the array using insertion sort.
*
* @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param work a workspace array (slice)
- * @param workBase origin of usable space in work array
- * @param workLen usable size of work array
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
*/
- private static void doSort(double[] a, int left, int right,
- double[] work, int workBase, int workLen) {
- // Use Quicksort on small arrays
- if (right - left < QUICKSORT_THRESHOLD) {
- sort(a, left, right, true);
- return;
- }
-
- /*
- * Index run[i] is the start of i-th run
- * (ascending or descending sequence).
- */
- int[] run = new int[MAX_RUN_COUNT + 1];
- int count = 0; run[0] = left;
-
- // Check if the array is nearly sorted
- for (int k = left; k < right; run[count] = k) {
- // Equal items in the beginning of the sequence
- while (k < right && a[k] == a[k + 1])
- k++;
- if (k == right) break; // Sequence finishes with equal items
- if (a[k] < a[k + 1]) { // ascending
- while (++k <= right && a[k - 1] <= a[k]);
- } else if (a[k] > a[k + 1]) { // descending
- while (++k <= right && a[k - 1] >= a[k]);
- // Transform into an ascending sequence
- for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {
- double t = a[lo]; a[lo] = a[hi]; a[hi] = t;
+ private static void insertionSort(double[] a, int low, int high) {
+ for (int i, k = low; ++k < high; ) {
+ double ai = a[i = k];
+
+ if (ai < a[i - 1]) {
+ while (--i >= low && ai < a[i]) {
+ a[i + 1] = a[i];
}
- }
-
- // Merge a transformed descending sequence followed by an
- // ascending sequence
- if (run[count] > left && a[run[count]] >= a[run[count] - 1]) {
- count--;
- }
-
- /*
- * The array is not highly structured,
- * use Quicksort instead of merge sort.
- */
- if (++count == MAX_RUN_COUNT) {
- sort(a, left, right, true);
- return;
+ a[i + 1] = ai;
}
}
-
- // These invariants should hold true:
- // run[0] = 0
- // run[<last>] = right + 1; (terminator)
-
- if (count == 0) {
- // A single equal run
- return;
- } else if (count == 1 && run[count] > right) {
- // Either a single ascending or a transformed descending run.
- // Always check that a final run is a proper terminator, otherwise
- // we have an unterminated trailing run, to handle downstream.
- return;
- }
- right++;
- if (run[count] < right) {
- // Corner case: the final run is not a terminator. This may happen
- // if a final run is an equals run, or there is a single-element run
- // at the end. Fix up by adding a proper terminator at the end.
- // Note that we terminate with (right + 1), incremented earlier.
- run[++count] = right;
+ }
+
+ /**
+ * Sorts the specified range of the array using heap sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void heapSort(double[] a, int low, int high) {
+ for (int k = (low + high) >>> 1; k > low; ) {
+ pushDown(a, --k, a[k], low, high);
}
-
- // Determine alternation base for merge
- byte odd = 0;
- for (int n = 1; (n <<= 1) < count; odd ^= 1);
-
- // Use or create temporary array b for merging
- double[] b; // temp array; alternates with a
- int ao, bo; // array offsets from 'left'
- int blen = right - left; // space needed for b
- if (work == null || workLen < blen || workBase + blen > work.length) {
- work = new double[blen];
- workBase = 0;
- }
- if (odd == 0) {
- System.arraycopy(a, left, work, workBase, blen);
- b = a;
- bo = 0;
- a = work;
- ao = workBase - left;
- } else {
- b = work;
- ao = 0;
- bo = workBase - left;
- }
-
- // Merging
- for (int last; count > 1; count = last) {
- for (int k = (last = 0) + 2; k <= count; k += 2) {
- int hi = run[k], mi = run[k - 1];
- for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
- if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {
- b[i + bo] = a[p++ + ao];
- } else {
- b[i + bo] = a[q++ + ao];
- }
- }
- run[++last] = hi;
- }
- if ((count & 1) != 0) {
- for (int i = right, lo = run[count - 1]; --i >= lo;
- b[i + bo] = a[i + ao]
- );
- run[++last] = right;
- }
- double[] t = a; a = b; b = t;
- int o = ao; ao = bo; bo = o;
+ while (--high > low) {
+ double max = a[low];
+ pushDown(a, low, a[high], low, high);
+ a[high] = max;
}
}
/**
- * Sorts the specified range of the array by Dual-Pivot Quicksort.
+ * Pushes specified element down during heap sort.
*
- * @param a the array to be sorted
- * @param left the index of the first element, inclusive, to be sorted
- * @param right the index of the last element, inclusive, to be sorted
- * @param leftmost indicates if this part is the leftmost in the range
+ * @param a the given array
+ * @param p the start index
+ * @param value the given element
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
*/
- private static void sort(double[] a, int left, int right, boolean leftmost) {
- int length = right - left + 1;
-
- // Use insertion sort on tiny arrays
- if (length < INSERTION_SORT_THRESHOLD) {
- if (leftmost) {
- /*
- * Traditional (without sentinel) insertion sort,
- * optimized for server VM, is used in case of
- * the leftmost part.
- */
- for (int i = left, j = i; i < right; j = ++i) {
- double ai = a[i + 1];
- while (ai < a[j]) {
- a[j + 1] = a[j];
- if (j-- == left) {
- break;
- }
- }
- a[j + 1] = ai;
- }
- } else {
- /*
- * Skip the longest ascending sequence.
- */
- do {
- if (left >= right) {
- return;
- }
- } while (a[++left] >= a[left - 1]);
-
- /*
- * Every element from adjoining part plays the role
- * of sentinel, therefore this allows us to avoid the
- * left range check on each iteration. Moreover, we use
- * the more optimized algorithm, so called pair insertion
- * sort, which is faster (in the context of Quicksort)
- * than traditional implementation of insertion sort.
- */
- for (int k = left; ++left <= right; k = ++left) {
- double a1 = a[k], a2 = a[left];
-
- if (a1 < a2) {
- a2 = a1; a1 = a[left];
- }
- while (a1 < a[--k]) {
- a[k + 2] = a[k];
- }
- a[++k + 1] = a1;
-
- while (a2 < a[--k]) {
- a[k + 1] = a[k];
- }
- a[k + 1] = a2;
- }
- double last = a[right];
-
- while (last < a[--right]) {
- a[right + 1] = a[right];
- }
- a[right + 1] = last;
+ private static void pushDown(double[] a, int p, double value, int low, int high) {
+ for (int k ;; a[p] = a[p = k]) {
+ k = (p << 1) - low + 2; // Index of the right child
+
+ if (k > high) {
+ break;
+ }
+ if (k == high || a[k] < a[k - 1]) {
+ --k;
+ }
+ if (a[k] <= value) {
+ break;
}
- return;
}
-
- // Inexpensive approximation of length / 7
- int seventh = (length >> 3) + (length >> 6) + 1;
+ a[p] = value;
+ }
+
+ /**
+ * Tries to sort the specified range of the array.
+ *
+ * @param sorter parallel context
+ * @param a the array to be sorted
+ * @param low the index of the first element to be sorted
+ * @param size the array size
+ * @return true if finally sorted, false otherwise
+ */
+ private static boolean tryMergeRuns(Sorter sorter, double[] a, int low, int size) {
/*
- * Sort five evenly spaced elements around (and including) the
- * center element in the range. These elements will be used for
- * pivot selection as described below. The choice for spacing
- * these elements was empirically determined to work well on
- * a wide variety of inputs.
+ * The run array is constructed only if initial runs are
+ * long enough to continue, run[i] then holds start index
+ * of the i-th sequence of elements in non-descending order.
*/
- int e3 = (left + right) >>> 1; // The midpoint
- int e2 = e3 - seventh;
- int e1 = e2 - seventh;
- int e4 = e3 + seventh;
- int e5 = e4 + seventh;
-
- // Sort these elements using insertion sort
- if (a[e2] < a[e1]) { double t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
-
- if (a[e3] < a[e2]) { double t = a[e3]; a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- if (a[e4] < a[e3]) { double t = a[e4]; a[e4] = a[e3]; a[e3] = t;
- if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- }
- if (a[e5] < a[e4]) { double t = a[e5]; a[e5] = a[e4]; a[e4] = t;
- if (t < a[e3]) { a[e4] = a[e3]; a[e3] = t;
- if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
- if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
- }
- }
- }
-
- // Pointers
- int less = left; // The index of the first element of center part
- int great = right; // The index before the first element of right part
-
- if (a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5]) {
- /*
- * Use the second and fourth of the five sorted elements as pivots.
- * These values are inexpensive approximations of the first and
- * second terciles of the array. Note that pivot1 <= pivot2.
- */
- double pivot1 = a[e2];
- double pivot2 = a[e4];
-
- /*
- * The first and the last elements to be sorted are moved to the
- * locations formerly occupied by the pivots. When partitioning
- * is complete, the pivots are swapped back into their final
- * positions, and excluded from subsequent sorting.
- */
- a[e2] = a[left];
- a[e4] = a[right];
-
- /*
- * Skip elements, which are less or greater than pivot values.
- */
- while (a[++less] < pivot1);
- while (a[--great] > pivot2);
+ int[] run = null;
+ int high = low + size;
+ int count = 1, last = low;
+
+ /*
+ * Identify all possible runs.
+ */
+ for (int k = low + 1; k < high; ) {
/*
- * Partitioning:
- *
- * left part center part right part
- * +--------------------------------------------------------------+
- * | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 |
- * +--------------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (left, less) < pivot1
- * pivot1 <= all in [less, k) <= pivot2
- * all in (great, right) > pivot2
- *
- * Pointer k is the first index of ?-part.
+ * Find the end index of the current run.
*/
- outer:
- for (int k = less - 1; ++k <= great; ) {
- double ak = a[k];
- if (ak < pivot1) { // Move a[k] to left part
- a[k] = a[less];
- /*
- * Here and below we use "a[i] = b; i++;" instead
- * of "a[i++] = b;" due to performance issue.
- */
- a[less] = ak;
- ++less;
- } else if (ak > pivot2) { // Move a[k] to right part
- while (a[great] > pivot2) {
- if (great-- == k) {
- break outer;
- }
- }
- if (a[great] < pivot1) { // a[great] <= pivot2
- a[k] = a[less];
- a[less] = a[great];
- ++less;
- } else { // pivot1 <= a[great] <= pivot2
- a[k] = a[great];
- }
- /*
- * Here and below we use "a[i] = b; i--;" instead
- * of "a[i--] = b;" due to performance issue.
- */
- a[great] = ak;
- --great;
- }
- }
-
- // Swap pivots into their final positions
- a[left] = a[less - 1]; a[less - 1] = pivot1;
- a[right] = a[great + 1]; a[great + 1] = pivot2;
-
- // Sort left and right parts recursively, excluding known pivots
- sort(a, left, less - 2, leftmost);
- sort(a, great + 2, right, false);
-
- /*
- * If center part is too large (comprises > 4/7 of the array),
- * swap internal pivot values to ends.
- */
- if (less < e1 && e5 < great) {
- /*
- * Skip elements, which are equal to pivot values.
- */
- while (a[less] == pivot1) {
- ++less;
- }
-
- while (a[great] == pivot2) {
- --great;
+ if (a[k - 1] < a[k]) {
+
+ // Identify ascending sequence
+ while (++k < high && a[k - 1] <= a[k]);
+
+ } else if (a[k - 1] > a[k]) {
+
+ // Identify descending sequence
+ while (++k < high && a[k - 1] >= a[k]);
+
+ // Reverse into ascending order
+ for (int i = last - 1, j = k; ++i < --j && a[i] > a[j]; ) {
+ double ai = a[i]; a[i] = a[j]; a[j] = ai;
}
-
- /*
- * Partitioning:
- *
- * left part center part right part
- * +----------------------------------------------------------+
- * | == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 |
- * +----------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (*, less) == pivot1
- * pivot1 < all in [less, k) < pivot2
- * all in (great, *) == pivot2
- *
- * Pointer k is the first index of ?-part.
- */
- outer:
- for (int k = less - 1; ++k <= great; ) {
- double ak = a[k];
- if (ak == pivot1) { // Move a[k] to left part
- a[k] = a[less];
- a[less] = ak;
- ++less;
- } else if (ak == pivot2) { // Move a[k] to right part
- while (a[great] == pivot2) {
- if (great-- == k) {
- break outer;
- }
- }
- if (a[great] == pivot1) { // a[great] < pivot2
- a[k] = a[less];
- /*
- * Even though a[great] equals to pivot1, the
- * assignment a[less] = pivot1 may be incorrect,
- * if a[great] and pivot1 are floating-point zeros
- * of different signs. Therefore in float and
- * double sorting methods we have to use more
- * accurate assignment a[less] = a[great].
- */
- a[less] = a[great];
- ++less;
- } else { // pivot1 < a[great] < pivot2
- a[k] = a[great];
- }
- a[great] = ak;
- --great;
- }
- }
- }
-
- // Sort center part recursively
- sort(a, less, great, false);
-
- } else { // Partitioning with one pivot
- /*
- * Use the third of the five sorted elements as pivot.
- * This value is inexpensive approximation of the median.
- */
- double pivot = a[e3];
-
- /*
- * Partitioning degenerates to the traditional 3-way
- * (or "Dutch National Flag") schema:
- *
- * left part center part right part
- * +-------------------------------------------------+
- * | < pivot | == pivot | ? | > pivot |
- * +-------------------------------------------------+
- * ^ ^ ^
- * | | |
- * less k great
- *
- * Invariants:
- *
- * all in (left, less) < pivot
- * all in [less, k) == pivot
- * all in (great, right) > pivot
- *
- * Pointer k is the first index of ?-part.
- */
- for (int k = less; k <= great; ++k) {
- if (a[k] == pivot) {
+ } else { // Identify constant sequence
+ for (double ak = a[k]; ++k < high && ak == a[k]; );
+
+ if (k < high) {
continue;
}
- double ak = a[k];
- if (ak < pivot) { // Move a[k] to left part
- a[k] = a[less];
- a[less] = ak;
- ++less;
- } else { // a[k] > pivot - Move a[k] to right part
- while (a[great] > pivot) {
- --great;
- }
- if (a[great] < pivot) { // a[great] <= pivot
- a[k] = a[less];
- a[less] = a[great];
- ++less;
- } else { // a[great] == pivot
- /*
- * Even though a[great] equals to pivot, the
- * assignment a[k] = pivot may be incorrect,
- * if a[great] and pivot are floating-point
- * zeros of different signs. Therefore in float
- * and double sorting methods we have to use
- * more accurate assignment a[k] = a[great].
- */
- a[k] = a[great];
- }
- a[great] = ak;
- --great;
- }
}
/*
- * Sort left and right parts recursively.
- * All elements from center part are equal
- * and, therefore, already sorted.
+ * Check special cases.
*/
- sort(a, left, less - 1, leftmost);
- sort(a, great + 1, right, false);
+ if (run == null) {
+ if (k == high) {
+
+ /*
+ * The array is monotonous sequence,
+ * and therefore already sorted.
+ */
+ return true;
+ }
+
+ if (k - low < MIN_FIRST_RUN_SIZE) {
+
+ /*
+ * The first run is too small
+ * to proceed with scanning.
+ */
+ return false;
+ }
+
+ run = new int[((size >> 10) | 0x7F) & 0x3FF];
+ run[0] = low;
+
+ } else if (a[last - 1] > a[last]) {
+
+ if (count > (k - low) >> MIN_FIRST_RUNS_FACTOR) {
+
+ /*
+ * The first runs are not long
+ * enough to continue scanning.
+ */
+ return false;
+ }
+
+ if (++count == MAX_RUN_CAPACITY) {
+
+ /*
+ * Array is not highly structured.
+ */
+ return false;
+ }
+
+ if (count == run.length) {
+
+ /*
+ * Increase capacity of index array.
+ */
+ run = Arrays.copyOf(run, count << 1);
+ }
+ }
+ run[count] = (last = k);
+ }
+
+ /*
+ * Merge runs of highly structured array.
+ */
+ if (count > 1) {
+ double[] b; int offset = low;
+
+ if (sorter == null || (b = (double[]) sorter.b) == null) {
+ b = new double[size];
+ } else {
+ offset = sorter.offset;
+ }
+ mergeRuns(a, b, offset, 1, sorter != null, run, 0, count);
+ }
+ return true;
+ }
+
+ /**
+ * Merges the specified runs.
+ *
+ * @param a the source array
+ * @param b the temporary buffer used in merging
+ * @param offset the start index in the source, inclusive
+ * @param aim specifies merging: to source ( > 0), buffer ( < 0) or any ( == 0)
+ * @param parallel indicates whether merging is performed in parallel
+ * @param run the start indexes of the runs, inclusive
+ * @param lo the start index of the first run, inclusive
+ * @param hi the start index of the last run, inclusive
+ * @return the destination where runs are merged
+ */
+ private static double[] mergeRuns(double[] a, double[] b, int offset,
+ int aim, boolean parallel, int[] run, int lo, int hi) {
+
+ if (hi - lo == 1) {
+ if (aim >= 0) {
+ return a;
+ }
+ for (int i = run[hi], j = i - offset, low = run[lo]; i > low;
+ b[--j] = a[--i]
+ );
+ return b;
+ }
+
+ /*
+ * Split into approximately equal parts.
+ */
+ int mi = lo, rmi = (run[lo] + run[hi]) >>> 1;
+ while (run[++mi + 1] <= rmi);
+
+ /*
+ * Merge the left and right parts.
+ */
+ double[] a1, a2;
+
+ if (parallel && hi - lo > MIN_RUN_COUNT) {
+ RunMerger merger = new RunMerger(a, b, offset, 0, run, mi, hi).forkMe();
+ a1 = mergeRuns(a, b, offset, -aim, true, run, lo, mi);
+ a2 = (double[]) merger.getDestination();
+ } else {
+ a1 = mergeRuns(a, b, offset, -aim, false, run, lo, mi);
+ a2 = mergeRuns(a, b, offset, 0, false, run, mi, hi);
+ }
+
+ double[] dst = a1 == a ? b : a;
+
+ int k = a1 == a ? run[lo] - offset : run[lo];
+ int lo1 = a1 == b ? run[lo] - offset : run[lo];
+ int hi1 = a1 == b ? run[mi] - offset : run[mi];
+ int lo2 = a2 == b ? run[mi] - offset : run[mi];
+ int hi2 = a2 == b ? run[hi] - offset : run[hi];
+
+ if (parallel) {
+ new Merger(null, dst, k, a1, lo1, hi1, a2, lo2, hi2).invoke();
+ } else {
+ mergeParts(null, dst, k, a1, lo1, hi1, a2, lo2, hi2);
+ }
+ return dst;
+ }
+
+ /**
+ * Merges the sorted parts.
+ *
+ * @param merger parallel context
+ * @param dst the destination where parts are merged
+ * @param k the start index of the destination, inclusive
+ * @param a1 the first part
+ * @param lo1 the start index of the first part, inclusive
+ * @param hi1 the end index of the first part, exclusive
+ * @param a2 the second part
+ * @param lo2 the start index of the second part, inclusive
+ * @param hi2 the end index of the second part, exclusive
+ */
+ private static void mergeParts(Merger merger, double[] dst, int k,
+ double[] a1, int lo1, int hi1, double[] a2, int lo2, int hi2) {
+
+ if (merger != null && a1 == a2) {
+
+ while (true) {
+
+ /*
+ * The first part must be larger.
+ */
+ if (hi1 - lo1 < hi2 - lo2) {
+ int lo = lo1; lo1 = lo2; lo2 = lo;
+ int hi = hi1; hi1 = hi2; hi2 = hi;
+ }
+
+ /*
+ * Small parts will be merged sequentially.
+ */
+ if (hi1 - lo1 < MIN_PARALLEL_MERGE_PARTS_SIZE) {
+ break;
+ }
+
+ /*
+ * Find the median of the larger part.
+ */
+ int mi1 = (lo1 + hi1) >>> 1;
+ double key = a1[mi1];
+ int mi2 = hi2;
+
+ /*
+ * Partition the smaller part.
+ */
+ for (int loo = lo2; loo < mi2; ) {
+ int t = (loo + mi2) >>> 1;
+
+ if (key > a2[t]) {
+ loo = t + 1;
+ } else {
+ mi2 = t;
+ }
+ }
+
+ int d = mi2 - lo2 + mi1 - lo1;
+
+ /*
+ * Merge the right sub-parts in parallel.
+ */
+ merger.forkMerger(dst, k + d, a1, mi1, hi1, a2, mi2, hi2);
+
+ /*
+ * Process the sub-left parts.
+ */
+ hi1 = mi1;
+ hi2 = mi2;
+ }
+ }
+
+ /*
+ * Merge small parts sequentially.
+ */
+ while (lo1 < hi1 && lo2 < hi2) {
+ dst[k++] = a1[lo1] < a2[lo2] ? a1[lo1++] : a2[lo2++];
+ }
+ if (dst != a1 || k < lo1) {
+ while (lo1 < hi1) {
+ dst[k++] = a1[lo1++];
+ }
+ }
+ if (dst != a2 || k < lo2) {
+ while (lo2 < hi2) {
+ dst[k++] = a2[lo2++];
+ }
+ }
+ }
+
+// [class]
+
+ /**
+ * This class implements parallel sorting.
+ */
+ private static final class Sorter extends CountedCompleter<Void> {
+ private static final long serialVersionUID = 20180818L;
+ private final Object a, b;
+ private final int low, size, offset, depth;
+
+ private Sorter(CountedCompleter<?> parent,
+ Object a, Object b, int low, int size, int offset, int depth) {
+ super(parent);
+ this.a = a;
+ this.b = b;
+ this.low = low;
+ this.size = size;
+ this.offset = offset;
+ this.depth = depth;
+ }
+
+ @Override
+ public final void compute() {
+ if (depth < 0) {
+ setPendingCount(2);
+ int half = size >> 1;
+ new Sorter(this, b, a, low, half, offset, depth + 1).fork();
+ new Sorter(this, b, a, low + half, size - half, offset, depth + 1).compute();
+ } else {
+ if (a instanceof int[]) {
+ sort(this, (int[]) a, depth, low, low + size);
+ } else if (a instanceof long[]) {
+ sort(this, (long[]) a, depth, low, low + size);
+ } else if (a instanceof float[]) {
+ sort(this, (float[]) a, depth, low, low + size);
+ } else if (a instanceof double[]) {
+ sort(this, (double[]) a, depth, low, low + size);
+ } else {
+ throw new IllegalArgumentException(
+ "Unknown type of array: " + a.getClass().getName());
+ }
+ }
+ tryComplete();
+ }
+
+ @Override
+ public final void onCompletion(CountedCompleter<?> caller) {
+ if (depth < 0) {
+ int mi = low + (size >> 1);
+ boolean src = (depth & 1) == 0;
+
+ new Merger(null,
+ a,
+ src ? low : low - offset,
+ b,
+ src ? low - offset : low,
+ src ? mi - offset : mi,
+ b,
+ src ? mi - offset : mi,
+ src ? low + size - offset : low + size
+ ).invoke();
+ }
+ }
+
+ private void forkSorter(int depth, int low, int high) {
+ addToPendingCount(1);
+ Object a = this.a; // Use local variable for performance
+ new Sorter(this, a, b, low, high - low, offset, depth).fork();
+ }
+ }
+
+ /**
+ * This class implements parallel merging.
+ */
+ private static final class Merger extends CountedCompleter<Void> {
+ private static final long serialVersionUID = 20180818L;
+ private final Object dst, a1, a2;
+ private final int k, lo1, hi1, lo2, hi2;
+
+ private Merger(CountedCompleter<?> parent, Object dst, int k,
+ Object a1, int lo1, int hi1, Object a2, int lo2, int hi2) {
+ super(parent);
+ this.dst = dst;
+ this.k = k;
+ this.a1 = a1;
+ this.lo1 = lo1;
+ this.hi1 = hi1;
+ this.a2 = a2;
+ this.lo2 = lo2;
+ this.hi2 = hi2;
+ }
+
+ @Override
+ public final void compute() {
+ if (dst instanceof int[]) {
+ mergeParts(this, (int[]) dst, k,
+ (int[]) a1, lo1, hi1, (int[]) a2, lo2, hi2);
+ } else if (dst instanceof long[]) {
+ mergeParts(this, (long[]) dst, k,
+ (long[]) a1, lo1, hi1, (long[]) a2, lo2, hi2);
+ } else if (dst instanceof float[]) {
+ mergeParts(this, (float[]) dst, k,
+ (float[]) a1, lo1, hi1, (float[]) a2, lo2, hi2);
+ } else if (dst instanceof double[]) {
+ mergeParts(this, (double[]) dst, k,
+ (double[]) a1, lo1, hi1, (double[]) a2, lo2, hi2);
+ } else {
+ throw new IllegalArgumentException(
+ "Unknown type of array: " + dst.getClass().getName());
+ }
+ propagateCompletion();
+ }
+
+ private void forkMerger(Object dst, int k,
+ Object a1, int lo1, int hi1, Object a2, int lo2, int hi2) {
+ addToPendingCount(1);
+ new Merger(this, dst, k, a1, lo1, hi1, a2, lo2, hi2).fork();
+ }
+ }
+
+ /**
+ * This class implements parallel merging of runs.
+ */
+ private static final class RunMerger extends RecursiveTask<Object> {
+ private static final long serialVersionUID = 20180818L;
+ private final Object a, b;
+ private final int[] run;
+ private final int offset, aim, lo, hi;
+
+ private RunMerger(Object a, Object b, int offset,
+ int aim, int[] run, int lo, int hi) {
+ this.a = a;
+ this.b = b;
+ this.offset = offset;
+ this.aim = aim;
+ this.run = run;
+ this.lo = lo;
+ this.hi = hi;
+ }
+
+ @Override
+ protected final Object compute() {
+ if (a instanceof int[]) {
+ return mergeRuns((int[]) a, (int[]) b, offset, aim, true, run, lo, hi);
+ }
+ if (a instanceof long[]) {
+ return mergeRuns((long[]) a, (long[]) b, offset, aim, true, run, lo, hi);
+ }
+ if (a instanceof float[]) {
+ return mergeRuns((float[]) a, (float[]) b, offset, aim, true, run, lo, hi);
+ }
+ if (a instanceof double[]) {
+ return mergeRuns((double[]) a, (double[]) b, offset, aim, true, run, lo, hi);
+ }
+ throw new IllegalArgumentException(
+ "Unknown type of array: " + a.getClass().getName());
+ }
+
+ private RunMerger forkMe() {
+ fork();
+ return this;
+ }
+
+ private Object getDestination() {
+ join();
+ return getRawResult();
}
}
}
--- a/src/java.base/share/classes/javax/crypto/Cipher.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/share/classes/javax/crypto/Cipher.java Wed Nov 13 17:21:31 2019 -0500
@@ -147,10 +147,6 @@
* <li>{@code AES/ECB/NoPadding} (128)</li>
* <li>{@code AES/ECB/PKCS5Padding} (128)</li>
* <li>{@code AES/GCM/NoPadding} (128)</li>
- * <li>{@code DES/CBC/NoPadding} (56)</li>
- * <li>{@code DES/CBC/PKCS5Padding} (56)</li>
- * <li>{@code DES/ECB/NoPadding} (56)</li>
- * <li>{@code DES/ECB/PKCS5Padding} (56)</li>
* <li>{@code DESede/CBC/NoPadding} (168)</li>
* <li>{@code DESede/CBC/PKCS5Padding} (168)</li>
* <li>{@code DESede/ECB/NoPadding} (168)</li>
--- a/src/java.base/share/classes/javax/crypto/KeyGenerator.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/share/classes/javax/crypto/KeyGenerator.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -97,7 +97,6 @@
* parentheses:
* <ul>
* <li>{@code AES} (128)</li>
- * <li>{@code DES} (56)</li>
* <li>{@code DESede} (168)</li>
* <li>{@code HmacSHA1}</li>
* <li>{@code HmacSHA256}</li>
--- a/src/java.base/share/classes/javax/crypto/Mac.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/share/classes/javax/crypto/Mac.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -56,7 +56,6 @@
* <p> Every implementation of the Java platform is required to support
* the following standard {@code Mac} algorithms:
* <ul>
- * <li>{@code HmacMD5}</li>
* <li>{@code HmacSHA1}</li>
* <li>{@code HmacSHA256}</li>
* </ul>
--- a/src/java.base/share/classes/javax/crypto/SecretKeyFactory.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/share/classes/javax/crypto/SecretKeyFactory.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -52,16 +52,13 @@
* {@link #generateSecret(java.security.spec.KeySpec) generateSecret} and
* {@link #getKeySpec(javax.crypto.SecretKey, java.lang.Class) getKeySpec}
* methods.
- * For example, the DES secret-key factory supplied by the "SunJCE" provider
- * supports {@code DESKeySpec} as a transparent representation of DES
- * keys, and that provider's secret-key factory for Triple DES keys supports
- * {@code DESedeKeySpec} as a transparent representation of Triple DES
- * keys.
+ * For example, the DESede (Triple DES) secret-key factory supplied by the
+ * "SunJCE" provider supports {@code DESedeKeySpec} as a transparent
+ * representation of Triple DES keys.
*
* <p> Every implementation of the Java platform is required to support the
* following standard {@code SecretKeyFactory} algorithms:
* <ul>
- * <li>{@code DES}</li>
* <li>{@code DESede}</li>
* </ul>
* These algorithms are described in the <a href=
@@ -74,7 +71,6 @@
* @author Jan Luehe
*
* @see SecretKey
- * @see javax.crypto.spec.DESKeySpec
* @see javax.crypto.spec.DESedeKeySpec
* @see javax.crypto.spec.PBEKeySpec
* @since 1.4
--- a/src/java.base/share/classes/sun/net/www/protocol/https/DelegateHttpsURLConnection.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/share/classes/sun/net/www/protocol/https/DelegateHttpsURLConnection.java Wed Nov 13 17:21:31 2019 -0500
@@ -72,13 +72,4 @@
protected javax.net.ssl.HostnameVerifier getHostnameVerifier() {
return httpsURLConnection.getHostnameVerifier();
}
-
- /*
- * Called by layered delegator's finalize() method to handle closing
- * the underlying object.
- */
- @SuppressWarnings("deprecation")
- protected void dispose() throws Throwable {
- super.finalize();
- }
}
--- a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsURLConnectionImpl.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsURLConnectionImpl.java Wed Nov 13 17:21:31 2019 -0500
@@ -461,16 +461,6 @@
delegate.setDefaultUseCaches(defaultusecaches);
}
- /*
- * finalize (dispose) the delegated object. Otherwise
- * sun.net.www.protocol.http.HttpURLConnection's finalize()
- * would have to be made public.
- */
- @SuppressWarnings("deprecation")
- protected void finalize() throws Throwable {
- delegate.dispose();
- }
-
public boolean equals(Object obj) {
return this == obj || ((obj instanceof HttpsURLConnectionImpl) &&
delegate.equals(((HttpsURLConnectionImpl)obj).delegate));
--- a/src/java.base/share/classes/sun/util/locale/provider/HostLocaleProviderAdapter.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/share/classes/sun/util/locale/provider/HostLocaleProviderAdapter.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.text.DecimalFormat;
import java.util.spi.LocaleServiceProvider;
/**
@@ -60,4 +61,19 @@
}
return null;
}
+
+ /**
+ * Utility to make the decimal format specific to integer, called
+ * by the platform dependent adapter implementations.
+ *
+ * @param df A DecimalFormat object
+ * @return The same DecimalFormat object in the argument, modified
+ * to allow integer formatting/parsing only.
+ */
+ static DecimalFormat makeIntegerFormatter(DecimalFormat df) {
+ df.setMaximumFractionDigits(0);
+ df.setDecimalSeparatorAlwaysShown(false);
+ df.setParseIntegerOnly(true);
+ return df;
+ }
}
--- a/src/java.base/share/man/java.1 Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/share/man/java.1 Wed Nov 13 17:21:31 2019 -0500
@@ -2975,70 +2975,6 @@
application uses the heap space.
.RS
.RE
-.TP
-.B \f[CB]\-XX:+CMSClassUnloadingEnabled\f[R]
-Enables class unloading when using the concurrent mark\-sweep (CMS)
-garbage collector.
-This option is enabled by default.
-To disable class unloading for the CMS garbage collector, specify
-\f[CB]\-XX:\-CMSClassUnloadingEnabled\f[R].
-.RS
-.RE
-.TP
-.B \f[CB]\-XX:CMSExpAvgFactor=\f[R]\f[I]percent\f[R]
-Sets the percentage of time (0 to 100) used to weight the current sample
-when computing exponential averages for the concurrent collection
-statistics.
-By default, the exponential averages factor is set to 25%.
-The following example shows how to set the factor to 15%:
-.RS
-.RS
-.PP
-\f[CB]\-XX:CMSExpAvgFactor=15\f[R]
-.RE
-.RE
-.TP
-.B \f[CB]\-XX:CMSInitiatingOccupancyFraction=\f[R]\f[I]percent\f[R]
-Sets the percentage of the old generation occupancy (0 to 100) at which
-to start a CMS collection cycle.
-The default value is set to \-1.
-Any negative value (including the default) implies that the option
-\f[CB]\-XX:CMSTriggerRatio\f[R] is used to define the value of the
-initiating occupancy fraction.
-.RS
-.PP
-The following example shows how to set the factor to 20%:
-.RS
-.PP
-\f[CB]\-XX:CMSInitiatingOccupancyFraction=20\f[R]
-.RE
-.RE
-.TP
-.B \f[CB]\-XX:CMSIncrementalDutySafetyFactor=\f[R]\f[I]percent\f[R]
-Sets the percentage (0 to 100) used to add conservatism when computing
-the duty cycle.
-The default value is 10.
-.RS
-.RE
-.TP
-.B \f[CB]\-XX:+CMSScavengeBeforeRemark\f[R]
-Enables scavenging attempts before the CMS remark step.
-By default, this option is disabled.
-.RS
-.RE
-.TP
-.B \f[CB]\-XX:CMSTriggerRatio=percent\f[R]
-Sets the percentage (0 to 100) of the value specified by the option
-\f[CB]\-XX:MinHeapFreeRatio\f[R] that\[aq]s allocated before a CMS
-collection cycle commences.
-The default value is set to 80%.
-.RS
-.PP
-The following example shows how to set the occupancy fraction to 75%:
-.RS
-.PP
-\f[CB]\-XX:CMSTriggerRatio=75\f[R]
-.RE
.RE
.TP
.B \f[CB]\-XX:ConcGCThreads=\f[R]\f[I]threads\f[R]
@@ -3070,7 +3006,6 @@
Enables invoking of concurrent GC by using the \f[CB]System.gc()\f[R]
request.
This option is disabled by default and can be enabled only with the
-deprecated \f[CB]\-XX:+UseConcMarkSweepGC\f[R] option and the
\f[CB]\-XX:+UseG1GC\f[R] option.
.RS
.RE
@@ -3460,8 +3395,7 @@
.B \f[CB]\-XX:MaxTenuringThreshold=\f[R]\f[I]threshold\f[R]
Sets the maximum tenuring threshold for use in adaptive GC sizing.
The largest value is 15.
-The default value is 15 for the parallel (throughput) collector, and 6
-for the CMS collector.
+The default value is 15 for the parallel (throughput) collector.
.RS
.PP
The following example shows how to set the maximum tenuring threshold to
@@ -3724,13 +3658,6 @@
.RS
.RE
.TP
-.B \f[CB]\-XX:+UseCMSInitiatingOccupancyOnly\f[R]
-Enables the use of the occupancy value as the only criterion for
-initiating the CMS collector.
-By default, this option is disabled and other criteria may be used.
-.RS
-.RE
-.TP
.B \f[CB]\-XX:+UseG1GC\f[R]
Enables the use of the garbage\-first (G1) garbage collector.
It\[aq]s a server\-style garbage collector, targeted for multiprocessor
@@ -3975,21 +3902,6 @@
See \f[B]Enable Logging with the JVM Unified Logging Framework\f[R].
.RE
.TP
-.B \f[CB]\-XX:+UseConcMarkSweepGC\f[R]
-Enables the use of the CMS garbage collector for the old generation.
-CMS is an alternative to the default garbage collector (G1), which also
-focuses on meeting application latency requirements.
-By default, this option is disabled and the collector is selected
-automatically based on the configuration of the machine and type of the
-JVM.
-The CMS garbage collector is deprecated.
-.RS
-.RE
-.SH OBSOLETE JAVA OPTIONS
-.PP
-These \f[CB]java\f[R] options are still accepted but ignored, and a
-warning is issued when they\[aq]re used.
-.TP
.B \f[CB]\-XX:+UseMembar\f[R]
Enabled issuing membars on thread\-state transitions.
This option was disabled by default on all platforms except ARM servers,
--- a/src/java.base/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java Wed Nov 13 17:21:31 2019 -0500
@@ -258,8 +258,9 @@
@Override
public NumberFormat getIntegerInstance(Locale locale) {
AtomicReferenceArray<String> patterns = getNumberPatterns(locale);
- return new DecimalFormat(patterns.get(NF_INTEGER),
+ DecimalFormat format = new DecimalFormat(patterns.get(NF_INTEGER),
DecimalFormatSymbols.getInstance(locale));
+ return HostLocaleProviderAdapter.makeIntegerFormatter(format);
}
@Override
--- a/src/java.base/windows/native/libjava/HostLocaleProviderAdapter_md.c Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.base/windows/native/libjava/HostLocaleProviderAdapter_md.c Wed Nov 13 17:21:31 2019 -0500
@@ -910,7 +910,7 @@
if (digits > 0) {
int i;
for(i = digits; i > 0; i--) {
- fractionPattern[i] = L'0';
+ fractionPattern[i] = L'#';
}
fractionPattern[0] = L'.';
fractionPattern[digits+1] = L'\0';
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosPrincipal.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosPrincipal.java Wed Nov 13 17:21:31 2019 -0500
@@ -82,6 +82,8 @@
/**
* Enterprise name (alias)
+ *
+ * @since 13
*/
public static final int KRB_NT_ENTERPRISE = 10;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java Wed Nov 13 17:21:31 2019 -0500
@@ -785,6 +785,7 @@
unnamedPackage.modle = module;
//we cannot use a method reference below, as initialCompleter might be null now
unnamedPackage.completer = s -> initialCompleter.complete(s);
+ unnamedPackage.flags_field |= EXISTS;
module.unnamedPackage = unnamedPackage;
}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/cms/AdaptiveFreeList.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-/*
- * @(#)AdaptiveFreeList.java
- *
- * Copyright (c) 2000, 2015, 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.cms;
-
-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.types.CIntegerField;
-import sun.jvm.hotspot.types.Type;
-import sun.jvm.hotspot.types.TypeDataBase;
-
-public class AdaptiveFreeList extends VMObject {
- static {
- VM.registerVMInitializedObserver(new Observer() {
- public void update(Observable o, Object data) {
- initialize(VM.getVM().getTypeDataBase());
- }
- });
- }
-
- private static synchronized void initialize(TypeDataBase db) {
- Type type = db.lookupType("AdaptiveFreeList<FreeChunk>");
- sizeField = type.getCIntegerField("_size");
- countField = type.getCIntegerField("_count");
- headerSize = type.getSize();
- }
-
- // Fields
- private static CIntegerField sizeField;
- private static CIntegerField countField;
- private static long headerSize;
-
- //Constructor
- public AdaptiveFreeList(Address address) {
- super(address);
- }
-
- // Accessors
- public long size() {
- return sizeField.getValue(addr);
- }
-
- public long count() {
- return countField.getValue(addr);
- }
-
- public static long sizeOf() {
- return headerSize;
- }
-}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/cms/CMSBitMap.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2007, 2015, 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.cms;
-
-import java.io.*;
-import java.util.*;
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.memory.*;
-import sun.jvm.hotspot.runtime.*;
-import sun.jvm.hotspot.types.*;
-import sun.jvm.hotspot.utilities.*;
-
-public class CMSBitMap extends VMObject {
- private static AddressField bmStartWordField;
- private static CIntegerField bmWordSizeField;
- private static CIntegerField shifterField;
- //private static AddressField bmField;
- private static long virtualSpaceFieldOffset;
-
- public CMSBitMap(Address addr) {
- super(addr);
- }
-
- static {
- VM.registerVMInitializedObserver(new Observer() {
- public void update(Observable o, Object data) {
- initialize(VM.getVM().getTypeDataBase());
- }
- });
- }
-
- private static synchronized void initialize(TypeDataBase db) {
- Type type = db.lookupType("CMSBitMap");
- bmStartWordField = type.getAddressField("_bmStartWord");
- bmWordSizeField = type.getCIntegerField("_bmWordSize");
- shifterField = type.getCIntegerField("_shifter");
- //bmField = type.getAddressField("_bm");
- virtualSpaceFieldOffset = type.getField("_virtual_space").getOffset();
- }
- public void printAll() {
- System.out.println("bmStartWord(): "+bmStartWord());
- System.out.println("bmWordSize(): "+bmWordSize());
- System.out.println("shifter(): "+shifter());
- }
-
- public Address bmStartWord() {
- return bmStartWordField.getValue(addr);
- }
- public long bmWordSize() {
- return bmWordSizeField.getValue(addr);
- }
- public long shifter() {
- return shifterField.getValue(addr);
- }
- public VirtualSpace virtualSpace() {
- return (VirtualSpace) VMObjectFactory.newObject(VirtualSpace.class, addr.addOffsetTo(virtualSpaceFieldOffset));
- }
-
- public BitMap bm() {
- BitMap bitMap = new BitMap((int) (bmWordSize() >> shifter() ));
- VirtualSpace vs = virtualSpace();
- bitMap.set_map(vs.low());
- return bitMap;
- }
-
- public Address getNextMarkedWordAddress(Address addr) {
- Address endWord = bmStartWord().addOffsetTo(bmWordSize());
- int nextOffset = bm().getNextOneOffset(heapWordToOffset(addr), heapWordToOffset(endWord) );
- Address nextAddr = offsetToHeapWord(nextOffset);
- return nextAddr;
- }
-
- int heapWordToOffset(Address addr) {
- int temp = (int)addr.minus(bmStartWord()) / (int) VM.getVM().getAddressSize();
- int ret_val = temp >> shifter();
- return ret_val;
- }
-
- Address offsetToHeapWord(int offset) {
- int temp = offset << shifter();
- return bmStartWord().addOffsetTo(temp*VM.getVM().getAddressSize());
- }
-
- boolean isMarked(Address addr) {
- BitMap bm = bm();
- return bm.at(heapWordToOffset(addr));
- }
-}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/cms/CMSCollector.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2007, 2015, 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.cms;
-
-import java.io.*;
-import java.util.*;
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.runtime.*;
-import sun.jvm.hotspot.types.*;
-
-public class CMSCollector extends VMObject {
- private static long markBitMapFieldOffset;
-
- public CMSCollector(Address addr) {
- super(addr);
- }
-
- static {
- VM.registerVMInitializedObserver(new Observer() {
- public void update(Observable o, Object data) {
- initialize(VM.getVM().getTypeDataBase());
- }
- });
- }
-
- private static synchronized void initialize(TypeDataBase db) {
- Type type = db.lookupType("CMSCollector");
- markBitMapFieldOffset = type.getField("_markBitMap").getOffset();
- }
-
- //Accessing mark bitmap
- public CMSBitMap markBitMap() {
- return (CMSBitMap) VMObjectFactory.newObject(
- CMSBitMap.class,
- addr.addOffsetTo(markBitMapFieldOffset));
- }
-
- public long blockSizeUsingPrintezisBits(Address addr) {
- CMSBitMap markBitMap = markBitMap();
- long addressSize = VM.getVM().getAddressSize();
- if ( markBitMap.isMarked(addr) && markBitMap.isMarked(addr.addOffsetTo(1*addressSize)) ) {
- Address nextOneAddr = markBitMap.getNextMarkedWordAddress(addr.addOffsetTo(2*addressSize));
- //return size in bytes
- long size = (nextOneAddr.addOffsetTo(1*addressSize)).minus(addr);
- return size;
- } else {
- //missing Printezis marks
- return -1;
- }
-
- }
-}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/cms/CMSHeap.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates.
- * 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.cms;
-
-import sun.jvm.hotspot.debugger.Address;
-import sun.jvm.hotspot.gc.shared.GenCollectedHeap;
-import sun.jvm.hotspot.gc.shared.CollectedHeapName;
-
-public class CMSHeap extends GenCollectedHeap {
-
- public CMSHeap(Address addr) {
- super(addr);
- }
-
- public CollectedHeapName kind() {
- return CollectedHeapName.CMS;
- }
-}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/cms/CompactibleFreeListSpace.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,209 +0,0 @@
-/*
- * Copyright (c) 2003, 2019, 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.cms;
-
-import java.io.*;
-import java.util.*;
-
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.gc.shared.*;
-import sun.jvm.hotspot.memory.*;
-import sun.jvm.hotspot.oops.*;
-import sun.jvm.hotspot.runtime.*;
-import sun.jvm.hotspot.types.*;
-import sun.jvm.hotspot.utilities.*;
-
-public class CompactibleFreeListSpace extends CompactibleSpace implements LiveRegionsProvider {
- private static AddressField collectorField;
- private static AddressField indexedFreeListField;
- private static AddressField dictionaryField;
- private static long smallLinearAllocBlockFieldOffset;
-
- private int heapWordSize; // 4 for 32bit, 8 for 64 bits
- private int IndexSetStart; // for small indexed list
- private int IndexSetSize;
- private int IndexSetStride;
- private static long MinChunkSizeInBytes;
-
- static {
- VM.registerVMInitializedObserver(new Observer() {
- public void update(Observable o, Object data) {
- initialize(VM.getVM().getTypeDataBase());
- }
- });
- }
-
- private static synchronized void initialize(TypeDataBase db) {
- long sizeofFreeChunk = db.lookupType("FreeChunk").getSize();
- VM vm = VM.getVM();
-
- Type type = db.lookupType("CompactibleFreeListSpace");
- collectorField = type.getAddressField("_collector");
- collectorField = type.getAddressField("_collector");
- dictionaryField = type.getAddressField("_dictionary");
- indexedFreeListField = type.getAddressField("_indexedFreeList[0]");
- smallLinearAllocBlockFieldOffset = type.getField("_smallLinearAllocBlock").getOffset();
- MinChunkSizeInBytes = (type.getCIntegerField("_min_chunk_size_in_bytes")).getValue();
- }
-
- public CompactibleFreeListSpace(Address addr) {
- super(addr);
- VM vm = VM.getVM();
- heapWordSize = vm.getHeapWordSize();
- IndexSetStart = vm.getMinObjAlignmentInBytes() / heapWordSize;
- IndexSetStride = IndexSetStart;
- IndexSetSize = vm.getIndexSetSize();
- }
-
- // Accessing block offset table
- public CMSCollector collector() {
- return (CMSCollector) VMObjectFactory.newObject(
- CMSCollector.class,
- collectorField.getValue(addr));
- }
-
- public long free0() {
- return capacity() - used0();
- }
-
- public long used() {
- return capacity() - free();
- }
-
- public long used0() {
- List<MemRegion> regions = getLiveRegions();
- long usedSize = 0L;
- for (Iterator<MemRegion> itr = regions.iterator(); itr.hasNext();) {
- MemRegion mr = itr.next();
- usedSize += mr.byteSize();
- }
- return usedSize;
- }
-
- public long free() {
- // small chunks
- long size = 0;
- Address cur = addr.addOffsetTo( indexedFreeListField.getOffset() );
- cur = cur.addOffsetTo(IndexSetStart*AdaptiveFreeList.sizeOf());
- for (int i=IndexSetStart; i<IndexSetSize; i += IndexSetStride) {
- AdaptiveFreeList freeList = (AdaptiveFreeList) VMObjectFactory.newObject(AdaptiveFreeList.class, cur);
- size += i*freeList.count();
- cur= cur.addOffsetTo(IndexSetStride*AdaptiveFreeList.sizeOf());
- }
-
- // large block
- AFLBinaryTreeDictionary aflbd = (AFLBinaryTreeDictionary) VMObjectFactory.newObject(AFLBinaryTreeDictionary.class,
- dictionaryField.getValue(addr));
- size += aflbd.size();
-
-
- // linear block in TLAB
- LinearAllocBlock lab = (LinearAllocBlock) VMObjectFactory.newObject(LinearAllocBlock.class,
- addr.addOffsetTo(smallLinearAllocBlockFieldOffset));
- size += lab.word_size();
-
- return size*heapWordSize;
- }
-
- public void printOn(PrintStream tty) {
- tty.print("free-list-space");
- tty.print("[ " + bottom() + " , " + end() + " ) ");
- long cap = capacity();
- long used_size = used();
- long free_size = free();
- int used_perc = (int)((double)used_size/cap*100);
- tty.print("space capacity = " + cap + " used(" + used_perc + "%)= " + used_size + " ");
- tty.print("free= " + free_size );
- tty.print("\n");
-
- }
-
- public Address skipBlockSizeUsingPrintezisBits(Address pos) {
- CMSCollector collector = collector();
- long size = 0;
- Address addr = null;
-
- if (collector != null) {
- size = collector.blockSizeUsingPrintezisBits(pos);
- if (size >= 3) {
- addr = pos.addOffsetTo(adjustObjectSizeInBytes(size));
- }
- }
- return addr;
- }
-
- @Override
- public List<MemRegion> getLiveRegions() {
- List<MemRegion> res = new ArrayList<>();
- VM vm = VM.getVM();
- Debugger dbg = vm.getDebugger();
- ObjectHeap heap = vm.getObjectHeap();
- Address cur = bottom();
- Address regionStart = cur;
- Address limit = end();
- final long addressSize = vm.getAddressSize();
-
- for (; cur.lessThan(limit);) {
- Address k = cur.getAddressAt(addressSize);
- if (FreeChunk.indicatesFreeChunk(cur)) {
- if (! cur.equals(regionStart)) {
- res.add(new MemRegion(regionStart, cur));
- }
- FreeChunk fc = (FreeChunk) VMObjectFactory.newObject(FreeChunk.class, cur);
- long chunkSize = fc.size();
- if (Assert.ASSERTS_ENABLED) {
- Assert.that(chunkSize > 0, "invalid FreeChunk size");
- }
- // note that fc.size() gives chunk size in heap words
- cur = cur.addOffsetTo(chunkSize * addressSize);
- regionStart = cur;
- } else if (k != null) {
- Oop obj = heap.newOop(cur.addOffsetToAsOopHandle(0));
- long objectSize = obj.getObjectSize();
- cur = cur.addOffsetTo(adjustObjectSizeInBytes(objectSize));
- } else {
- // FIXME: need to do a better job here.
- // can I use bitMap here?
- //Find the object size using Printezis bits and skip over
- long size = collector().blockSizeUsingPrintezisBits(cur);
- if (size == -1) {
- break;
- }
- cur = cur.addOffsetTo(adjustObjectSizeInBytes(size));
- }
- }
- return res;
- }
-
- //-- Internals only below this point
-
- // Unlike corresponding VM code, we operate on byte size rather than
- // HeapWord size for convenience.
-
- public static long adjustObjectSizeInBytes(long sizeInBytes) {
- return Oop.alignObjectSize(Math.max(sizeInBytes, MinChunkSizeInBytes));
- }
-
-}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/cms/ConcurrentMarkSweepGeneration.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2003, 2019, 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.cms;
-
-import java.io.*;
-import java.util.*;
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.gc.shared.*;
-import sun.jvm.hotspot.runtime.*;
-import sun.jvm.hotspot.types.*;
-
-public class ConcurrentMarkSweepGeneration extends CardGeneration {
- private static AddressField cmsSpaceField;
-
- public ConcurrentMarkSweepGeneration(Address addr) {
- super(addr);
- }
-
- static {
- VM.registerVMInitializedObserver(new Observer() {
- public void update(Observable o, Object data) {
- initialize(VM.getVM().getTypeDataBase());
- }
- });
- }
-
- private static synchronized void initialize(TypeDataBase db) {
- Type type = db.lookupType("ConcurrentMarkSweepGeneration");
- cmsSpaceField = type.getAddressField("_cmsSpace");
- }
-
- // Accessing space
- public CompactibleFreeListSpace cmsSpace() {
- return (CompactibleFreeListSpace) VMObjectFactory.newObject(
- CompactibleFreeListSpace.class,
- cmsSpaceField.getValue(addr));
- }
-
- public long capacity() { return cmsSpace().capacity(); }
- public long used() { return cmsSpace().used(); }
- public long free() { return cmsSpace().free(); }
- public long contiguousAvailable() { throw new RuntimeException("not yet implemented"); }
- public boolean contains(Address p) { return cmsSpace().contains(p); }
- public void spaceIterate(SpaceClosure blk, boolean usedOnly) {
- blk.doSpace(cmsSpace());
- }
- public void liveRegionsIterate(LiveRegionsClosure closure) {
- closure.doLiveRegions(cmsSpace());
- }
-
- public Generation.Name kind() {
- return Generation.Name.CONCURRENT_MARK_SWEEP;
- }
-
- public String name() {
- return "concurrent mark-sweep generation";
- }
-
- public void printOn(PrintStream tty) {
- tty.println(name());
- cmsSpace().printOn(tty);
- }
-}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/cms/LinearAllocBlock.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * @(#)BinaryTreeDictionary.java
- * Copyright (c) 2000, 2015, 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.cms;
-
-import java.util.*;
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.types.*;
-import sun.jvm.hotspot.runtime.*;
-
-public class LinearAllocBlock extends VMObject {
- static {
- VM.registerVMInitializedObserver(new Observer() {
- public void update(Observable o, Object data) {
- initialize(VM.getVM().getTypeDataBase());
- }
- });
- }
-
- private static synchronized void initialize(TypeDataBase db) {
- Type type = db.lookupType("LinearAllocBlock");
- word_sizeField= type.getCIntegerField("_word_size");
- }
-
- // Fields
- private static CIntegerField word_sizeField;
-
- // Accessors
- public long word_size() {
- return word_sizeField.getValue(addr);
- }
-
- // Constructor
- public LinearAllocBlock(Address addr) {
- super(addr);
- }
-}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/cms/ParNewGeneration.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2003, 2015, 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.cms;
-
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.gc.serial.*;
-import sun.jvm.hotspot.gc.shared.*;
-
-public class ParNewGeneration extends DefNewGeneration {
- public ParNewGeneration(Address addr) {
- super(addr);
- }
-
- public Generation.Name kind() {
- return Generation.Name.PAR_NEW;
- }
-}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GCName.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GCName.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,9 +32,7 @@
PSMarkSweep ("PSMarkSweep"),
ParallelScavenge ("ParallelScavenge"),
DefNew ("DefNew"),
- ParNew ("ParNew"),
G1New ("G1New"),
- ConcurrentMarkSweep ("ConcurrentMarkSweep"),
G1Old ("G1Old"),
G1Full ("G1Full"),
Z ("Z"),
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/Generation.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/Generation.java Wed Nov 13 17:21:31 2019 -0500
@@ -81,9 +81,7 @@
// constants from Generation::Name
NAME_DEF_NEW = db.lookupIntConstant("Generation::DefNew").intValue();
- NAME_PAR_NEW = db.lookupIntConstant("Generation::ParNew").intValue();
NAME_MARK_SWEEP_COMPACT = db.lookupIntConstant("Generation::MarkSweepCompact").intValue();
- NAME_CONCURRENT_MARK_SWEEP = db.lookupIntConstant("Generation::ConcurrentMarkSweep").intValue();
NAME_OTHER = db.lookupIntConstant("Generation::Other").intValue();
}
@@ -93,9 +91,7 @@
public static class Name {
public static final Name DEF_NEW = new Name("DefNew");
- public static final Name PAR_NEW = new Name("ParNew");
public static final Name MARK_SWEEP_COMPACT = new Name("MarkSweepCompact");
- public static final Name CONCURRENT_MARK_SWEEP = new Name("ConcurrentMarkSweep");
public static final Name OTHER = new Name("Other");
private Name(String value) {
@@ -115,12 +111,8 @@
static Generation.Name nameForEnum(int value) {
if (value == NAME_DEF_NEW) {
return Name.DEF_NEW;
- } else if (value == NAME_PAR_NEW) {
- return Name.PAR_NEW;
} else if (value == NAME_MARK_SWEEP_COMPACT) {
return Name.MARK_SWEEP_COMPACT;
- } else if (value == NAME_CONCURRENT_MARK_SWEEP) {
- return Name.CONCURRENT_MARK_SWEEP;
} else if (value == NAME_OTHER) {
return Name.OTHER;
} else {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GenerationFactory.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GenerationFactory.java Wed Nov 13 17:21:31 2019 -0500
@@ -27,7 +27,6 @@
import java.util.*;
import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.gc.cms.*;
import sun.jvm.hotspot.gc.serial.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.types.*;
@@ -50,9 +49,7 @@
ctor = new VirtualConstructor(db);
ctor.addMapping("DefNewGeneration", DefNewGeneration.class);
- ctor.addMapping("ParNewGeneration", ParNewGeneration.class);
ctor.addMapping("TenuredGeneration", TenuredGeneration.class);
- ctor.addMapping("ConcurrentMarkSweepGeneration", ConcurrentMarkSweepGeneration.class);
}
public static Generation newObject(Address addr) {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/Universe.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/Universe.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, 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
@@ -30,7 +30,6 @@
import sun.jvm.hotspot.debugger.Address;
import sun.jvm.hotspot.debugger.OopHandle;
-import sun.jvm.hotspot.gc.cms.CMSHeap;
import sun.jvm.hotspot.gc.epsilon.EpsilonHeap;
import sun.jvm.hotspot.gc.g1.G1CollectedHeap;
import sun.jvm.hotspot.gc.parallel.ParallelScavengeHeap;
@@ -84,7 +83,6 @@
collectedHeapField = type.getAddressField("_collectedHeap");
heapConstructor = new VirtualConstructor(db);
- addHeapTypeIfInDB(db, CMSHeap.class);
addHeapTypeIfInDB(db, SerialHeap.class);
addHeapTypeIfInDB(db, ParallelScavengeHeap.class);
addHeapTypeIfInDB(db, G1CollectedHeap.class);
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Mark.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Mark.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -73,11 +73,6 @@
noHashInPlace = db.lookupLongConstant("markWord::no_hash_in_place").longValue();
noLockInPlace = db.lookupLongConstant("markWord::no_lock_in_place").longValue();
maxAge = db.lookupLongConstant("markWord::max_age").longValue();
-
- /* Constants in markWord used by CMS. */
- cmsShift = db.lookupLongConstant("markWord::cms_shift").longValue();
- cmsMask = db.lookupLongConstant("markWord::cms_mask").longValue();
- sizeShift = db.lookupLongConstant("markWord::size_shift").longValue();
}
// Field accessors
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java Wed Nov 13 17:21:31 2019 -0500
@@ -32,7 +32,6 @@
import java.util.*;
import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.gc.cms.*;
import sun.jvm.hotspot.gc.shared.*;
import sun.jvm.hotspot.gc.epsilon.*;
import sun.jvm.hotspot.gc.g1.*;
@@ -234,16 +233,11 @@
}
visitor.prologue(totalSize);
- CompactibleFreeListSpace cmsSpaceOld = null;
CollectedHeap heap = VM.getVM().getUniverse().heap();
if (heap instanceof GenCollectedHeap) {
GenCollectedHeap genHeap = (GenCollectedHeap) heap;
Generation genOld = genHeap.getGen(1);
- if (genOld instanceof ConcurrentMarkSweepGeneration) {
- ConcurrentMarkSweepGeneration concGen = (ConcurrentMarkSweepGeneration)genOld;
- cmsSpaceOld = concGen.cmsSpace();
- }
}
for (int i = 0; i < liveRegions.size(); i += 2) {
@@ -265,20 +259,7 @@
}
}
if (obj == null) {
- //Find the object size using Printezis bits and skip over
- long size = 0;
-
- if ( (cmsSpaceOld != null) && cmsSpaceOld.contains(handle) ){
- size = cmsSpaceOld.collector().blockSizeUsingPrintezisBits(handle);
- }
-
- if (size <= 0) {
- //Either Printezis bits not set or handle is not in cms space.
- throw new UnknownOopException();
- }
-
- handle = handle.addOffsetToAsOopHandle(CompactibleFreeListSpace.adjustObjectSizeInBytes(size));
- continue;
+ throw new UnknownOopException();
}
if (of == null || of.canInclude(obj)) {
if (visitor.doObj(obj)) {
@@ -286,11 +267,8 @@
break;
}
}
- if ( (cmsSpaceOld != null) && cmsSpaceOld.contains(handle)) {
- handle = handle.addOffsetToAsOopHandle(CompactibleFreeListSpace.adjustObjectSizeInBytes(obj.getObjectSize()) );
- } else {
- handle = handle.addOffsetToAsOopHandle(obj.getObjectSize());
- }
+
+ handle = handle.addOffsetToAsOopHandle(obj.getObjectSize());
}
}
catch (AddressException e) {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Thread.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Thread.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -118,7 +118,6 @@
public boolean isHiddenFromExternalView() { return false; }
public boolean isJvmtiAgentThread() { return false; }
public boolean isWatcherThread() { return false; }
- public boolean isConcurrentMarkSweepThread() { return false; }
public boolean isServiceThread() { return false; }
/** Memory operations */
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java Wed Nov 13 17:21:31 2019 -0500
@@ -104,7 +104,6 @@
private int heapOopSize;
private int klassPtrSize;
private int oopSize;
- private final int IndexSetSize;
/** -XX flags (value origin) */
public static int Flags_DEFAULT;
public static int Flags_COMMAND_LINE;
@@ -491,7 +490,6 @@
Flags_VALUE_ORIGIN_MASK = db.lookupIntConstant("JVMFlag::VALUE_ORIGIN_MASK").intValue();
Flags_ORIG_COMMAND_LINE = db.lookupIntConstant("JVMFlag::ORIG_COMMAND_LINE").intValue();
oopSize = db.lookupIntConstant("oopSize").intValue();
- IndexSetSize = db.lookupIntConstant("CompactibleFreeListSpace::IndexSetSize").intValue();
intType = db.lookupType("int");
uintType = db.lookupType("uint");
@@ -711,10 +709,6 @@
return heapOopSize;
}
- public int getIndexSetSize() {
- return IndexSetSize;
- }
-
public int getKlassPtrSize() {
return klassPtrSize;
}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64CurrentFrameGuess.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64CurrentFrameGuess.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -137,7 +137,16 @@
setValues(curSP, null, pc);
return true;
}
+ Frame oldFrame = frame;
frame = frame.sender(map);
+ if (frame.getSP().lessThanOrEqual(oldFrame.getSP())) {
+ // Frame points to itself or to a location in the wrong direction.
+ // Break the loop and move on to next offset.
+ if (DEBUG) {
+ System.out.println("AMD64CurrentFrameGuess.run: frame <= oldFrame: " + frame);
+ }
+ break;
+ }
}
} catch (Exception e) {
if (DEBUG) {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/x86/X86CurrentFrameGuess.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/x86/X86CurrentFrameGuess.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -137,7 +137,16 @@
setValues(curSP, null, pc);
return true;
}
+ Frame oldFrame = frame;
frame = frame.sender(map);
+ if (frame.getSP().lessThanOrEqual(oldFrame.getSP())) {
+ // Frame points to itself or to a location in the wrong direction.
+ // Break the loop and move on to next offset.
+ if (DEBUG) {
+ System.out.println("X86CurrentFrameGuess.run: frame <= oldFrame: " + frame);
+ }
+ break;
+ }
}
} catch (Exception e) {
if (DEBUG) {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/HeapSummary.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/HeapSummary.java Wed Nov 13 17:21:31 2019 -0500
@@ -160,12 +160,6 @@
System.out.println("using thread-local object allocation.");
}
- l = getFlagValue("UseConcMarkSweepGC", flagMap);
- if (l == 1L) {
- System.out.println("Concurrent Mark-Sweep GC");
- return;
- }
-
l = getFlagValue("UseParallelGC", flagMap);
if (l == 1L) {
System.out.print("Parallel GC ");
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java Wed Nov 13 17:21:31 2019 -0500
@@ -164,7 +164,6 @@
}
public final boolean useG1GC = getFlag("UseG1GC", Boolean.class);
- public final boolean useCMSGC = getFlag("UseConcMarkSweepGC", Boolean.class);
public final int allocatePrefetchStyle = getFlag("AllocatePrefetchStyle", Integer.class);
public final int allocatePrefetchInstr = getFlag("AllocatePrefetchInstr", Integer.class);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java Tue Nov 12 10:25:55 2019 -0500
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java Wed Nov 13 17:21:31 2019 -0500
@@ -231,8 +231,7 @@
public enum HotSpotGC {
// Supported GCs
Serial(true, "UseSerialGC"),
- Parallel(true, "UseParallelGC", "UseParallelOldGC", "UseParNewGC"),
- CMS(true, "UseConcMarkSweepGC"),
+ Parallel(true, "UseParallelGC", "UseParallelOldGC"),
G1(true, "UseG1GC"),
// Unsupported GCs
--- a/test/hotspot/gtest/gc/z/test_zForwarding.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/gtest/gc/z/test_zForwarding.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -159,7 +159,7 @@
const uint32_t live_objects = size;
const size_t live_bytes = live_objects * object_size;
- page.inc_live_atomic(live_objects, live_bytes);
+ page.inc_live(live_objects, live_bytes);
// Setup forwarding
ZForwarding* const forwarding = ZForwarding::create(&page);
--- a/test/hotspot/gtest/gc/z/test_zLiveMap.cpp Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/gtest/gc/z/test_zLiveMap.cpp Wed Nov 13 17:21:31 2019 -0500
@@ -35,7 +35,7 @@
uintptr_t object = 0u;
// Mark the object strong.
- livemap.set_atomic(object, false /* finalizable */, inc_live);
+ livemap.set(object, false /* finalizable */, inc_live);
// Check that both bits are in the same segment.
ASSERT_EQ(livemap.index_to_segment(0), livemap.index_to_segment(1));
--- a/test/hotspot/jtreg/ProblemList.txt Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/ProblemList.txt Wed Nov 13 17:21:31 2019 -0500
@@ -108,7 +108,7 @@
serviceability/sa/ClhsdbJdis.java 8193639 solaris-all
serviceability/sa/ClhsdbJhisto.java 8193639,8211767 solaris-all,linux-ppc64le,linux-ppc64
serviceability/sa/ClhsdbJstack.java 8193639 solaris-all
-serviceability/sa/ClhsdbJstackXcompStress.java 8193639,8231635 solaris-all,windows-x64
+serviceability/sa/ClhsdbJstackXcompStress.java 8193639 solaris-all
serviceability/sa/ClhsdbLongConstant.java 8193639 solaris-all
serviceability/sa/ClhsdbPmap.java 8193639,8211767 solaris-all,linux-ppc64le,linux-ppc64
serviceability/sa/ClhsdbPrintAll.java 8193639 solaris-all
--- a/test/hotspot/jtreg/TEST.ROOT Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/TEST.ROOT Wed Nov 13 17:21:31 2019 -0500
@@ -47,7 +47,6 @@
vm.gc.G1 \
vm.gc.Serial \
vm.gc.Parallel \
- vm.gc.ConcMarkSweep \
vm.gc.Shenandoah \
vm.gc.Epsilon \
vm.gc.Z \
--- a/test/hotspot/jtreg/TEST.groups Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/TEST.groups Wed Nov 13 17:21:31 2019 -0500
@@ -37,7 +37,7 @@
hotspot_compiler_all_gcs = \
:hotspot_compiler \
- -:tier1_compiler_not_cms
+ -:tier1_compiler_aot_jvmci
hotspot_gc = \
gc \
@@ -152,7 +152,7 @@
compiler/aot \
compiler/profiling
-tier1_compiler_not_cms = \
+tier1_compiler_aot_jvmci = \
compiler/aot \
compiler/jvmci
@@ -198,7 +198,6 @@
-gc/logging/TestUnifiedLoggingSwitchStress.java \
-gc/stress \
-gc/survivorAlignment/TestPromotionFromSurvivorToTenuredAfterMinorGC.java \
- -gc/cms/TestMBeanCMS.java \
-gc/metaspace/CompressedClassSpaceSizeInJmapHeap.java \
-gc/shenandoah \
-gc/nvdimm
@@ -210,13 +209,11 @@
tier1_gc_gcold = \
gc/stress/gcold/TestGCOldWithG1.java \
- gc/stress/gcold/TestGCOldWithCMS.java \
gc/stress/gcold/TestGCOldWithSerial.java \
gc/stress/gcold/TestGCOldWithParallel.java
tier1_gc_gcbasher = \
gc/stress/gcbasher/TestGCBasherWithG1.java \
- gc/stress/gcbasher/TestGCBasherWithCMS.java \
gc/stress/gcbasher/TestGCBasherWithSerial.java \
gc/stress/gcbasher/TestGCBasherWithParallel.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/compiler/c2/TestBitSetAndReset.java Wed Nov 13 17:21:31 2019 -0500
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+/*
+ * @test
+ * @bug 8214239
+ * @summary Missing x86_64.ad patterns for clearing and setting long vector bits
+ *
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
+ * -XX:-TieredCompilation -XX:CompileThreshold=1000
+ * -XX:CompileCommand=print,compiler/c2/TestBitSetAndReset.test*
+ * -XX:CompileCommand=compileonly,compiler/c2/TestBitSetAndReset.test*
+ * -XX:CompileCommand=dontinline,compiler/c2/TestBitSetAndReset.test*
+ * compiler.c2.TestBitSetAndReset
+ */
+
+package compiler.c2;
+
+public class TestBitSetAndReset {
+ private static final int COUNT = 10_000;
+
+ private static final long MASK63 = 0x8000_0000_0000_0000L;
+ private static final long MASK32 = 0x0000_0001_0000_0000L;
+ private static final long MASK31 = 0x0000_0000_8000_0000L;
+ private static final long MASK15 = 0x0000_0000_0000_8000L;
+ private static final long MASK00 = 0x0000_0000_0000_0001L;
+
+ private static long andq, orq;
+
+ public static void main(String... args) {
+ boolean success = true;
+
+ for (int i=0; i<COUNT; i++) {
+ andq = MASK63 | MASK31 | MASK15 | MASK00;
+ orq = 0;
+ test63();
+ test32();
+ test31();
+ test15();
+ test00();
+ success &= andq == 0 && orq == (MASK63 | MASK32 | MASK31 | MASK15 | MASK00);
+ }
+ if (!success)
+ throw new AssertionError("Failure while setting or clearing long vector bits!");
+ }
+
+ private static void test63() {
+ andq &= ~MASK63;
+ orq |= MASK63;
+ }
+ private static void test32() {
+ andq &= ~MASK32;
+ orq |= MASK32;
+ }
+ private static void test31() {
+ andq &= ~MASK31;
+ orq |= MASK31;
+ }
+ private static void test15() {
+ andq &= ~MASK15;
+ orq |= MASK15;
+ }
+ private static void test00() {
+ andq &= ~MASK00;
+ orq |= MASK00;
+ }
+}
--- a/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java Wed Nov 13 17:21:31 2019 -0500
@@ -36,8 +36,6 @@
* TestUnsafeVolatileCAE,
* TestUnsafeVolatileGAS}
* and <testtype> in {G1,
- * CMS,
- * CMSCondMark,
* Serial,
* Parallel,
* Shenandoah,
@@ -90,18 +88,6 @@
procArgs = new String[argcount];
procArgs[argcount - 2] = "-XX:+UseSerialGC";
break;
- case "CMS":
- argcount = 10;
- procArgs = new String[argcount];
- procArgs[argcount - 3] = "-XX:+UseConcMarkSweepGC";
- procArgs[argcount - 2] = "-XX:-UseCondCardMark";
- break;
- case "CMSCondMark":
- argcount = 10;
- procArgs = new String[argcount];
- procArgs[argcount - 3] = "-XX:+UseConcMarkSweepGC";
- procArgs[argcount - 2] = "-XX:+UseCondCardMark";
- break;
case "Shenandoah":
argcount = 10;
procArgs = new String[argcount];
@@ -340,36 +326,6 @@
"ret"
};
break;
- case "CMSCondMark":
- // a card mark volatile barrier should be generated
- // before the card mark strb from the StoreCM and the
- // storestore barrier from the StoreCM should be elided
- matches = new String[] {
- "membar_release \\(elided\\)",
- useCompressedOops ? "stlrw?" : "stlr",
- "membar_volatile",
- "dmb ish",
- "storestore \\(elided\\)",
- "strb",
- "membar_volatile \\(elided\\)",
- "ret"
- };
- break;
- case "CMS":
- // a volatile card mark membar should not be generated
- // before the card mark strb from the StoreCM and the
- // storestore barrier from the StoreCM should be
- // generated as "dmb ishst"
- matches = new String[] {
- "membar_release \\(elided\\)",
- useCompressedOops ? "stlrw?" : "stlr",
- "storestore",
- "dmb ishst",
- "strb",
- "membar_volatile \\(elided\\)",
- "ret"
- };
- break;
case "Shenandoah":
case "ShenandoahTraversal":
// Shenandoah generates normal object graphs for
@@ -531,35 +487,6 @@
"ret"
};
break;
- case "CMSCondMark":
- // a card mark volatile barrier should be generated
- // before the card mark strb from the StoreCM and the
- // storestore barrier from the StoreCM should be elided
- matches = new String[] {
- "membar_release \\(elided\\)",
- useCompressedOops ? "cmpxchgw?_acq" : "cmpxchg_acq",
- "membar_volatile",
- "dmb ish",
- "storestore \\(elided\\)",
- "strb",
- "membar_acquire \\(elided\\)",
- "ret"
- };
- break;
- case "CMS":
- // a volatile card mark membar should not be generated
- // before the card mark strb from the StoreCM and the
- // storestore barrier from the StoreCM should be elided
- matches = new String[] {
- "membar_release \\(elided\\)",
- useCompressedOops ? "cmpxchgw?_acq" : "cmpxchg_acq",
- "storestore",
- "dmb ishst",
- "strb",
- "membar_acquire \\(elided\\)",
- "ret"
- };
- break;
case "Shenandoah":
case "ShenandoahTraversal":
// For volatile CAS, Shenanodoah generates normal
@@ -736,35 +663,6 @@
"ret"
};
break;
- case "CMSCondMark":
- // a card mark volatile barrier should be generated
- // before the card mark strb from the StoreCM and the
- // storestore barrier from the StoreCM should be elided
- matches = new String[] {
- "membar_release \\(elided\\)",
- useCompressedOops ? "cmpxchgw?_acq" : "cmpxchg_acq",
- "membar_volatile",
- "dmb ish",
- "storestore \\(elided\\)",
- "strb",
- "membar_acquire \\(elided\\)",
- "ret"
- };
- break;
- case "CMS":
- // a volatile card mark membar should not be generated
- // before the card mark strb from the StoreCM and the
- // storestore barrier from the StoreCM should be elided
- matches = new String[] {
- "membar_release \\(elided\\)",
- useCompressedOops ? "cmpxchgw?_acq" : "cmpxchg_acq",
- "storestore",
- "dmb ishst",
- "strb",
- "membar_acquire \\(elided\\)",
- "ret"
- };
- break;
case "Shenandoah":
case "ShenandoahTraversal":
// For volatile CAS, Shenanodoah generates normal
@@ -921,35 +819,6 @@
"ret"
};
break;
- case "CMSCondMark":
- // a card mark volatile barrier should be generated
- // before the card mark strb from the StoreCM and the
- // storestore barrier from the StoreCM should be elided
- matches = new String[] {
- "membar_release \\(elided\\)",
- useCompressedOops ? "atomic_xchgw?_acq" : "atomic_xchg_acq",
- "membar_volatile",
- "dmb ish",
- "storestore \\(elided\\)",
- "strb",
- "membar_acquire \\(elided\\)",
- "ret"
- };
- break;
- case "CMS":
- // a volatile card mark membar should not be generated
- // before the card mark strb from the StoreCM and the
- // storestore barrier from the StoreCM should be elided
- matches = new String[] {
- "membar_release \\(elided\\)",
- useCompressedOops ? "atomic_xchgw?_acq" : "atomic_xchg_acq",
- "storestore",
- "dmb ishst",
- "strb",
- "membar_acquire \\(elided\\)",
- "ret"
- };
- break;
case "Shenandoah":
case "ShenandoahTraversal":
matches = new String[] {
--- a/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatilesCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2018, Red Hat, Inc. 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.
- */
-
-/*
- * @test
- * @summary C2 should use ldar, stlr and ldaxr+stlxr insns for volatile operations
- * @library /test/lib /
- *
- * @modules java.base/jdk.internal.misc
- *
- * @requires os.arch=="aarch64" & vm.debug == true &
- * vm.flavor == "server" & !vm.graal.enabled &
- * vm.gc.ConcMarkSweep
- *
- * @build compiler.c2.aarch64.TestVolatiles
- * compiler.c2.aarch64.TestVolatileLoad
- * compiler.c2.aarch64.TestUnsafeVolatileLoad
- * compiler.c2.aarch64.TestVolatileStore
- * compiler.c2.aarch64.TestUnsafeVolatileStore
- * compiler.c2.aarch64.TestUnsafeVolatileCAS
- * compiler.c2.aarch64.TestUnsafeVolatileWeakCAS
- * compiler.c2.aarch64.TestUnsafeVolatileCAE
- * compiler.c2.aarch64.TestUnsafeVolatileGAS
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMS
- * TestVolatileLoad CMS
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMS
- * TestVolatileStore CMS
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMS
- * TestUnsafeVolatileLoad CMS
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMS
- * TestUnsafeVolatileStore CMS
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMS
- * TestUnsafeVolatileCAS CMS
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMS
- * TestUnsafeVolatileWeakCAS CMS
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMS
- * TestUnsafeVolatileCAE CMS
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMS
- * TestUnsafeVolatileGAS CMS
- */
-
-package compiler.c2.aarch64;
-
-public class TestVolatilesCMS {
- public static void main(String args[]) throws Throwable
- {
- // delegate work to shared code
- new TestVolatiles().runtest(args[0], args[1]);
- }
-}
--- a/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatilesCMSCondMark.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2018, Red Hat, Inc. 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.
- */
-
-/*
- * @test
- * @summary C2 should use ldar, stlr and ldaxr+stlxr insns for volatile operations
- * @library /test/lib /
- *
- * @modules java.base/jdk.internal.misc
- *
- * @requires os.arch=="aarch64" & vm.debug == true &
- * vm.flavor == "server" & !vm.graal.enabled &
- * vm.gc.ConcMarkSweep
- *
- * @build compiler.c2.aarch64.TestVolatiles
- * compiler.c2.aarch64.TestVolatileLoad
- * compiler.c2.aarch64.TestUnsafeVolatileLoad
- * compiler.c2.aarch64.TestVolatileStore
- * compiler.c2.aarch64.TestUnsafeVolatileStore
- * compiler.c2.aarch64.TestUnsafeVolatileCAS
- * compiler.c2.aarch64.TestUnsafeVolatileWeakCAS
- * compiler.c2.aarch64.TestUnsafeVolatileCAE
- * compiler.c2.aarch64.TestUnsafeVolatileGAS
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMSCondMark
- * TestVolatileLoad CMSCondMark
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMSCondMark
- * TestVolatileStore CMSCondMark
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMSCondMark
- * TestUnsafeVolatileLoad CMSCondMark
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMSCondMark
- * TestUnsafeVolatileStore CMSCondMark
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMSCondMark
- * TestUnsafeVolatileCAS CMSCondMark
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMSCondMark
- * TestUnsafeVolatileWeakCAS CMSCondMark
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMSCondMark
- * TestUnsafeVolatileCAE CMSCondMark
- *
- * @run driver compiler.c2.aarch64.TestVolatilesCMSCondMark
- * TestUnsafeVolatileGAS CMSCondMark
- */
-
-package compiler.c2.aarch64;
-
-public class TestVolatilesCMSCondMark {
- public static void main(String args[]) throws Throwable
- {
- // delegate work to shared code
- new TestVolatiles().runtest(args[0], args[1]);
- }
-}
--- a/test/hotspot/jtreg/compiler/loopstripmining/CheckLoopStripMining.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/compiler/loopstripmining/CheckLoopStripMining.java Wed Nov 13 17:21:31 2019 -0500
@@ -27,16 +27,38 @@
* @summary C2: LoopStripMining doesn't strip as expected
* @requires vm.compiler2.enabled
*
- * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+SafepointTimeout -XX:+SafepointALot
- * -XX:+AbortVMOnSafepointTimeout -XX:SafepointTimeoutDelay=500 -XX:GuaranteedSafepointInterval=500
- * -XX:-TieredCompilation -XX:+UseCountedLoopSafepoints -XX:LoopStripMiningIter=1000
- * -XX:LoopUnrollLimit=0 -XX:CompileCommand=compileonly,CheckLoopStripMining::test_loop -Xcomp CheckLoopStripMining
- *
+ * @library /test/lib
+ * @run driver compiler.loopstripmining.CheckLoopStripMining
*/
-public class CheckLoopStripMining {
+package compiler.loopstripmining;
+
+import jdk.test.lib.Utils;
+import jdk.test.lib.process.ProcessTools;
- public static int test_loop(int x) {
+public class CheckLoopStripMining {
+ public static void main(String args[]) throws Exception {
+ ProcessTools.executeTestJvm(
+ "-XX:+UnlockDiagnosticVMOptions",
+ // to prevent biased locking handshakes from changing the timing of this test
+ "-XX:-UseBiasedLocking",
+ "-XX:+SafepointTimeout",
+ "-XX:+SafepointALot",
+ "-XX:+AbortVMOnSafepointTimeout",
+ "-XX:SafepointTimeoutDelay=" + Utils.adjustTimeout(500),
+ "-XX:GuaranteedSafepointInterval=" + Utils.adjustTimeout(500),
+ "-XX:-TieredCompilation",
+ "-XX:+UseCountedLoopSafepoints",
+ "-XX:LoopStripMiningIter=1000",
+ "-XX:LoopUnrollLimit=0",
+ "-XX:CompileCommand=compileonly,compiler.loopstripmining.CheckLoopStripMining$Test::test_loop",
+ "-Xcomp",
+ Test.class.getName()).shouldHaveExitValue(0)
+ .stdoutShouldContain("sum: 715827882");
+ }
+
+ public static class Test {
+ public static int test_loop(int x) {
int sum = 0;
if (x != 0) {
for (int y = 1; y < Integer.MAX_VALUE; ++y) {
@@ -44,10 +66,11 @@
}
}
return sum;
- }
+ }
- public static void main(String args[]) {
- int sum = test_loop(3);
- System.out.println("sum: " + sum);
+ public static void main(String args[]) {
+ int sum = test_loop(3);
+ System.out.println("sum: " + sum);
+ }
}
}
--- a/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -25,8 +25,13 @@
* @test
* @bug 8189439
* @summary Parameters type profiling is not performed from aarch64 interpreter
+ *
* @requires os.arch != "arm"
* @requires vm.flavor == "server" & vm.compMode == "Xmixed" & !vm.emulatedClient & !vm.graal.enabled
+ *
+ * @comment the test can't be run w/ TieredStopAtLevel < 4
+ * @requires vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4
+ *
* @library /test/lib /
* @build sun.hotspot.WhiteBox
* @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission
--- a/test/hotspot/jtreg/compiler/tiered/Level2RecompilationTest.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/compiler/tiered/Level2RecompilationTest.java Wed Nov 13 17:21:31 2019 -0500
@@ -28,6 +28,9 @@
* @modules java.base/jdk.internal.misc
* java.management
*
+ * @comment the test can't be run w/ TieredStopAtLevel < 4
+ * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4)
+ *
* @build sun.hotspot.WhiteBox
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
* sun.hotspot.WhiteBox$WhiteBoxPermission
--- a/test/hotspot/jtreg/compiler/whitebox/OSRFailureLevel4Test.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/compiler/whitebox/OSRFailureLevel4Test.java Wed Nov 13 17:21:31 2019 -0500
@@ -27,6 +27,10 @@
* @library /test/lib /
* @modules java.base/jdk.internal.misc
* java.management
+ *
+ * @comment the test can't be run w/ TieredStopAtLevel < 4
+ * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4)
+ *
* @build sun.hotspot.WhiteBox
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
* sun.hotspot.WhiteBox$WhiteBoxPermission
--- a/test/hotspot/jtreg/gc/TestAgeOutput.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/TestAgeOutput.java Wed Nov 13 17:21:31 2019 -0500
@@ -48,19 +48,6 @@
* @run main/othervm -XX:+UseG1GC gc.TestAgeOutput UseG1GC
*/
-/*
- * @test TestAgeOutputCMS
- * @bug 8164936
- * @key gc
- * @comment Graal does not support CMS
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @modules java.base/jdk.internal.misc
- * @library /test/lib
- * @build sun.hotspot.WhiteBox
- * @run driver ClassFileInstaller sun.hotspot.WhiteBox
- * @run main/othervm -XX:+UseConcMarkSweepGC gc.TestAgeOutput UseConcMarkSweepGC
- */
-
import sun.hotspot.WhiteBox;
import java.util.regex.Matcher;
--- a/test/hotspot/jtreg/gc/TestFullGCCount.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/TestFullGCCount.java Wed Nov 13 17:21:31 2019 -0500
@@ -26,8 +26,7 @@
/**
* @test TestFullGCCount.java
* @bug 7072527
- * @summary CMS: JMM GC counters overcount in some cases
- * @requires !(vm.gc == "ConcMarkSweep" & vm.opt.ExplicitGCInvokesConcurrent == true)
+ * @summary JMM GC counters overcount in some cases
* @comment Shenandoah has "ExplicitGCInvokesConcurrent" on by default
* @requires !(vm.gc == "Shenandoah" & vm.opt.ExplicitGCInvokesConcurrent != false)
* @modules java.management
@@ -41,7 +40,7 @@
import java.util.List;
/*
- * Originally for a specific failure in CMS, this test now monitors all
+ * Originally for a specific failure in CMS[[keep]], this test now monitors all
* collectors for double-counting of collections.
*/
public class TestFullGCCount {
--- a/test/hotspot/jtreg/gc/TestGenerationPerfCounter.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/TestGenerationPerfCounter.java Wed Nov 13 17:21:31 2019 -0500
@@ -63,19 +63,6 @@
* @run main/othervm -XX:+UsePerfData -XX:+UseG1GC gc.TestGenerationPerfCounter
*/
-/* @test TestGenerationPerfCounterCMS
- * @bug 8080345
- * @comment Graal does not support CMS
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @library /test/lib /
- * @summary Tests that the sun.gc.policy.generations returns 2 for all GCs.
- * @modules java.base/jdk.internal.misc
- * java.compiler
- * java.management/sun.management
- * jdk.internal.jvmstat/sun.jvmstat.monitor
- * @run main/othervm -XX:+UsePerfData -XX:+UseConcMarkSweepGC gc.TestGenerationPerfCounter
- */
-
public class TestGenerationPerfCounter {
public static void main(String[] args) throws Exception {
long numGenerations =
--- a/test/hotspot/jtreg/gc/TestMemoryInitializationWithCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2002, 2019, 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 gc;
-
-/*
- * @test TestMemoryInitializationWithCMS
- * @key gc
- * @bug 4668531
- * @library /
- * @requires vm.debug & vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @summary Simple test for -XX:+CheckMemoryInitialization doesn't crash VM
- * @run main/othervm -XX:+UseConcMarkSweepGC -XX:+CheckMemoryInitialization gc.TestMemoryInitializationWithCMS
- */
-
-public class TestMemoryInitializationWithCMS {
-
- public static void main(String args[]) {
- TestMemoryInitialization.main(args);
- }
-}
--- a/test/hotspot/jtreg/gc/TestMemoryMXBeansAndPoolsPresence.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/TestMemoryMXBeansAndPoolsPresence.java Wed Nov 13 17:21:31 2019 -0500
@@ -58,16 +58,6 @@
* @run main/othervm -XX:+UseSerialGC gc.TestMemoryMXBeansAndPoolsPresence Serial
*/
-/* @test TestMemoryMXBeansAndPoolsPresenceCMS
- * @bug 8191564
- * @library /test/lib
- * @modules java.base/jdk.internal.misc
- * java.management
- * @comment Graal does not support CMS
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @run main/othervm -XX:+UseConcMarkSweepGC gc.TestMemoryMXBeansAndPoolsPresence CMS
- */
-
class GCBeanDescription {
public String name;
public String[] poolNames;
@@ -108,10 +98,6 @@
test(new GCBeanDescription("G1 Young Generation", new String[] {"G1 Eden Space", "G1 Survivor Space", "G1 Old Gen"}),
new GCBeanDescription("G1 Old Generation", new String[] {"G1 Eden Space", "G1 Survivor Space", "G1 Old Gen"}));
break;
- case "CMS":
- test(new GCBeanDescription("ParNew", new String[] {"Par Eden Space", "Par Survivor Space"}),
- new GCBeanDescription("ConcurrentMarkSweep", new String[] {"Par Eden Space", "Par Survivor Space", "CMS Old Gen"}));
- break;
case "Parallel":
test(new GCBeanDescription("PS Scavenge", new String[] {"PS Eden Space", "PS Survivor Space"}),
new GCBeanDescription("PS MarkSweep", new String[] {"PS Eden Space", "PS Survivor Space", "PS Old Gen"}));
--- a/test/hotspot/jtreg/gc/TestNumWorkerOutput.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/TestNumWorkerOutput.java Wed Nov 13 17:21:31 2019 -0500
@@ -36,19 +36,6 @@
* @run main/othervm -XX:+UseG1GC gc.TestNumWorkerOutput UseG1GC
*/
-/*
- * @test TestNumWorkerOutputCMS
- * @bug 8165292
- * @key gc
- * @comment Graal does not support CMS
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @modules java.base/jdk.internal.misc
- * @library /test/lib
- * @build sun.hotspot.WhiteBox
- * @run driver ClassFileInstaller sun.hotspot.WhiteBox
- * @run main/othervm -XX:+UseConcMarkSweepGC gc.TestNumWorkerOutput UseConcMarkSweepGC
- */
-
import sun.hotspot.WhiteBox;
import java.util.regex.Matcher;
--- a/test/hotspot/jtreg/gc/TestPolicyNamePerfCounter.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/TestPolicyNamePerfCounter.java Wed Nov 13 17:21:31 2019 -0500
@@ -63,19 +63,6 @@
* @run main/othervm -XX:+UsePerfData -XX:+UseG1GC gc.TestPolicyNamePerfCounter GarbageFirst
*/
-/* @test TestPolicyNamePerfCounterCMS
- * @bug 8210192
- * @comment Graal does not support CMS
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @library /test/lib /
- * @summary Tests that sun.gc.policy.name returns expected values for different GCs.
- * @modules java.base/jdk.internal.misc
- * java.compiler
- * java.management/sun.management
- * jdk.internal.jvmstat/sun.jvmstat.monitor
- * @run main/othervm -XX:+UsePerfData -XX:+UseConcMarkSweepGC gc.TestPolicyNamePerfCounter ParNew:CMS
- */
-
public class TestPolicyNamePerfCounter {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
--- a/test/hotspot/jtreg/gc/TestSmallHeap.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/TestSmallHeap.java Wed Nov 13 17:21:31 2019 -0500
@@ -91,12 +91,8 @@
noneGCSupported = false;
verifySmallHeapSize("-XX:+UseG1GC", expectedMaxHeap);
}
- if (GC.ConcMarkSweep.isSupported()) {
- noneGCSupported = false;
- verifySmallHeapSize("-XX:+UseConcMarkSweepGC", expectedMaxHeap);
- }
if (noneGCSupported) {
- throw new SkippedException("Skipping test because none of Parallel/Serial/G1/ConcMarkSweep is supported.");
+ throw new SkippedException("Skipping test because none of Parallel/Serial/G1 is supported.");
}
}
--- a/test/hotspot/jtreg/gc/TestSystemGC.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/TestSystemGC.java Wed Nov 13 17:21:31 2019 -0500
@@ -52,15 +52,6 @@
*/
/*
- * @test TestSystemGCCMS
- * @key gc
- * @comment Graal does not support CMS
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @run main/othervm -XX:+UseConcMarkSweepGC gc.TestSystemGC
- * @run main/othervm -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrent gc.TestSystemGC
- */
-
-/*
* @test TestSystemGCShenandoah
* @key gc
* @requires vm.gc.Shenandoah & !vm.graal.enabled
--- a/test/hotspot/jtreg/gc/arguments/GCTypes.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/arguments/GCTypes.java Wed Nov 13 17:21:31 2019 -0500
@@ -70,7 +70,6 @@
public static enum YoungGCType implements GCType {
DefNew("Copy"),
- ParNew("ParNew"),
PSNew("PS Scavenge"),
G1("G1 Young Generation");
@@ -95,7 +94,6 @@
public static enum OldGCType implements GCType {
Serial("MarkSweepCompact"),
- CMS("ConcurrentMarkSweep"),
PSOld("PS MarkSweep"),
G1("G1 Old Generation");
--- a/test/hotspot/jtreg/gc/arguments/TestAlignmentToUseLargePages.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/arguments/TestAlignmentToUseLargePages.java Wed Nov 13 17:21:31 2019 -0500
@@ -54,16 +54,6 @@
*/
/**
- * @test TestAlignmentToUseLargePagesCMS
- * @key gc regression
- * @bug 8024396
- * @comment Graal does not support CMS
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @run main/othervm -Xms71M -Xmx91M -XX:+UseConcMarkSweepGC -XX:+UseLargePages gc.arguments.TestAlignmentToUseLargePages
- * @run main/othervm -Xms71M -Xmx91M -XX:+UseConcMarkSweepGC -XX:-UseLargePages gc.arguments.TestAlignmentToUseLargePages
- */
-
-/**
* @test TestAlignmentToUseLargePagesShenandoah
* @key gc
* @bug 8024396
--- a/test/hotspot/jtreg/gc/arguments/TestCMSHeapSizeFlags.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2013, 2019, 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 gc.arguments;
-
-/*
- * @test TestCMSHeapSizeFlags
- * @key gc
- * @bug 8006088
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @summary Tests argument processing for initial and maximum heap size for the CMS collector
- * @library /test/lib
- * @library /
- * @modules java.base/jdk.internal.misc
- * java.management
- * @build sun.hotspot.WhiteBox
- * @run driver ClassFileInstaller sun.hotspot.WhiteBox
- * sun.hotspot.WhiteBox$WhiteBoxPermission
- * @run main/othervm gc.arguments.TestCMSHeapSizeFlags
- * @author thomas.schatzl@oracle.com
- */
-
-public class TestCMSHeapSizeFlags {
-
- public static void main(String args[]) throws Exception {
- final String gcName = "-XX:+UseConcMarkSweepGC";
-
- TestMaxHeapSizeTools.checkMinInitialMaxHeapFlags(gcName);
-
- TestMaxHeapSizeTools.checkGenMaxHeapErgo(gcName);
- }
-}
-
--- a/test/hotspot/jtreg/gc/arguments/TestDisableDefaultGC.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/arguments/TestDisableDefaultGC.java Wed Nov 13 17:21:31 2019 -0500
@@ -36,7 +36,6 @@
* @run driver gc.arguments.TestDisableDefaultGC
*/
-import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
public class TestDisableDefaultGC {
@@ -45,7 +44,6 @@
ProcessBuilder pb = GCArguments.createJavaProcessBuilder("-XX:-UseSerialGC",
"-XX:-UseParallelGC",
"-XX:-UseG1GC",
- "-XX:-UseConcMarkSweepGC",
"-XX:+UnlockExperimentalVMOptions",
"-XX:-UseShenandoahGC",
"-XX:-UseZGC",
--- a/test/hotspot/jtreg/gc/arguments/TestMaxNewSize.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/arguments/TestMaxNewSize.java Wed Nov 13 17:21:31 2019 -0500
@@ -68,19 +68,6 @@
* @author thomas.schatzl@oracle.com, jesper.wilhelmsson@oracle.com
*/
-/*
- * @test TestMaxNewSizeCMS
- * @key gc
- * @bug 7057939
- * @comment Graal does not support CMS
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @library /test/lib
- * @library /
- * @modules java.base/jdk.internal.misc
- * java.management
- * @run main gc.arguments.TestMaxNewSize -XX:+UseConcMarkSweepGC
- */
-
import java.util.regex.Matcher;
import java.util.regex.Pattern;
--- a/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.java Wed Nov 13 17:21:31 2019 -0500
@@ -101,7 +101,6 @@
int expectedRatio = Integer.valueOf(args[0]);
switch (GCTypes.YoungGCType.getYoungGCType()) {
case DefNew:
- case ParNew:
verifyDefNewNewRatio(expectedRatio);
break;
case PSNew:
--- a/test/hotspot/jtreg/gc/arguments/TestNewSizeFlags.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/arguments/TestNewSizeFlags.java Wed Nov 13 17:21:31 2019 -0500
@@ -306,7 +306,6 @@
public static long alignGenSize(long value) {
switch (YOUNG_GC_TYPE) {
case DefNew:
- case ParNew:
return HeapRegionUsageTool.alignDown(value, HEAP_SPACE_ALIGNMENT);
case PSNew:
return HeapRegionUsageTool.alignUp(HeapRegionUsageTool.alignDown(value,
--- a/test/hotspot/jtreg/gc/arguments/TestParallelGCThreads.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/arguments/TestParallelGCThreads.java Wed Nov 13 17:21:31 2019 -0500
@@ -80,7 +80,7 @@
}
public static void testFlags() throws Exception {
- // For each parallel collector (G1, Parallel, ParNew/CMS)
+ // For each parallel collector (G1, Parallel)
List<String> supportedGC = new ArrayList<String>();
if (GC.G1.isSupported()) {
@@ -89,12 +89,9 @@
if (GC.Parallel.isSupported()) {
supportedGC.add("Parallel");
}
- if (GC.ConcMarkSweep.isSupported()) {
- supportedGC.add("ConcMarkSweep");
- }
if (supportedGC.isEmpty()) {
- throw new SkippedException("Skipping test because none of G1/Parallel/ConcMarkSweep is supported.");
+ throw new SkippedException("Skipping test because none of G1/Parallel is supported.");
}
for (String gc : supportedGC) {
--- a/test/hotspot/jtreg/gc/arguments/TestParallelRefProc.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/arguments/TestParallelRefProc.java Wed Nov 13 17:21:31 2019 -0500
@@ -51,10 +51,6 @@
noneGCSupported = false;
testFlag(new String[] { "-XX:+UseSerialGC" }, false);
}
- if (GC.ConcMarkSweep.isSupported()) {
- noneGCSupported = false;
- testFlag(new String[] { "-XX:+UseConcMarkSweepGC" }, false);
- }
if (GC.Parallel.isSupported()) {
noneGCSupported = false;
testFlag(new String[] { "-XX:+UseParallelGC" }, false);
@@ -66,7 +62,7 @@
testFlag(new String[] { "-XX:+UseG1GC", "-XX:-ParallelRefProcEnabled", "-XX:ParallelGCThreads=2" }, false);
}
if (noneGCSupported) {
- throw new SkippedException("Skipping test because none of Serial/ConcMarkSweep/Parallel/G1 is supported.");
+ throw new SkippedException("Skipping test because none of Serial/Parallel/G1 is supported.");
}
}
--- a/test/hotspot/jtreg/gc/arguments/TestSelectDefaultGC.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/arguments/TestSelectDefaultGC.java Wed Nov 13 17:21:31 2019 -0500
@@ -64,8 +64,6 @@
assertVMOption(output, "UseG1GC", isServer);
// Serial is default for non-server class machines
assertVMOption(output, "UseSerialGC", !isServer);
- // CMS is never default
- assertVMOption(output, "UseConcMarkSweepGC", false);
}
public static void main(String[] args) throws Exception {
--- a/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.java Wed Nov 13 17:21:31 2019 -0500
@@ -126,7 +126,6 @@
GCTypes.YoungGCType type = GCTypes.YoungGCType.getYoungGCType();
switch (type) {
case DefNew:
- case ParNew:
verifyDefNewSurvivorRatio(expectedRatio);
break;
case PSNew:
--- a/test/hotspot/jtreg/gc/arguments/TestUseCompressedOopsErgo.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/arguments/TestUseCompressedOopsErgo.java Wed Nov 13 17:21:31 2019 -0500
@@ -73,22 +73,6 @@
*/
/*
- * @test TestUseCompressedOopsErgoCMS
- * @key gc
- * @bug 8010722
- * @comment Graal does not support CMS
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @library /test/lib
- * @library /
- * @modules java.base/jdk.internal.misc
- * java.management/sun.management
- * @build sun.hotspot.WhiteBox
- * @run driver ClassFileInstaller sun.hotspot.WhiteBox
- * sun.hotspot.WhiteBox$WhiteBoxPermission
- * @run main/othervm gc.arguments.TestUseCompressedOopsErgo -XX:+UseConcMarkSweepGC
- */
-
-/*
* @test TestUseCompressedOopsErgoShenandoah
* @key gc
* @bug 8010722
--- a/test/hotspot/jtreg/gc/class_unloading/TestCMSClassUnloadingEnabledHWM.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +0,0 @@
-/*
- * Copyright (c) 2014, 2019, 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 gc.class_unloading;
-
-/*
- * @test
- * @key gc
- * @bug 8049831
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @library /test/lib
- * @modules java.base/jdk.internal.misc
- * java.management
- * @build sun.hotspot.WhiteBox
- * @run driver ClassFileInstaller sun.hotspot.WhiteBox
- * sun.hotspot.WhiteBox$WhiteBoxPermission
- * @run driver gc.class_unloading.TestCMSClassUnloadingEnabledHWM
- * @summary Test that -XX:-CMSClassUnloadingEnabled will trigger a Full GC when more than MetaspaceSize metadata is allocated.
- */
-
-import jdk.test.lib.process.OutputAnalyzer;
-import jdk.test.lib.process.ProcessTools;
-import java.lang.management.GarbageCollectorMXBean;
-import java.lang.management.ManagementFactory;
-import sun.hotspot.WhiteBox;
-
-public class TestCMSClassUnloadingEnabledHWM {
- private static long MetaspaceSize = 32 * 1024 * 1024;
- private static long YoungGenSize = 32 * 1024 * 1024;
-
- private static OutputAnalyzer run(boolean enableUnloading) throws Exception {
- ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
- "-Xbootclasspath/a:.",
- "-XX:+UnlockDiagnosticVMOptions",
- "-XX:+WhiteBoxAPI",
- "-Xmx128m",
- "-XX:CMSMaxAbortablePrecleanTime=1",
- "-XX:CMSWaitDuration=50",
- "-XX:MetaspaceSize=" + MetaspaceSize,
- "-Xmn" + YoungGenSize,
- "-XX:+UseConcMarkSweepGC",
- "-XX:" + (enableUnloading ? "+" : "-") + "CMSClassUnloadingEnabled",
- "-Xlog:gc",
- TestCMSClassUnloadingEnabledHWM.AllocateBeyondMetaspaceSize.class.getName(),
- "" + MetaspaceSize);
- return new OutputAnalyzer(pb.start());
- }
-
- public static OutputAnalyzer runWithCMSClassUnloading() throws Exception {
- return run(true);
- }
-
- public static OutputAnalyzer runWithoutCMSClassUnloading() throws Exception {
- return run(false);
- }
-
- public static void testWithoutCMSClassUnloading() throws Exception {
- // -XX:-CMSClassUnloadingEnabled is used, so we expect a full GC instead of a concurrent cycle.
- OutputAnalyzer out = runWithoutCMSClassUnloading();
-
- out.shouldMatch(".*Pause Full.*");
- out.shouldNotMatch(".*Pause Initial Mark.*");
- }
-
- public static void testWithCMSClassUnloading() throws Exception {
- // -XX:+CMSClassUnloadingEnabled is used, so we expect a concurrent cycle instead of a full GC.
- OutputAnalyzer out = runWithCMSClassUnloading();
-
- out.shouldMatch(".*Pause Initial Mark.*");
- out.shouldNotMatch(".*Pause Full.*");
- }
-
- public static void main(String args[]) throws Exception {
- testWithCMSClassUnloading();
- testWithoutCMSClassUnloading();
- }
-
- public static class AllocateBeyondMetaspaceSize {
- public static void main(String [] args) throws Exception {
- if (args.length != 1) {
- throw new IllegalArgumentException("Usage: <MetaspaceSize>");
- }
-
- WhiteBox wb = WhiteBox.getWhiteBox();
-
- // Allocate past the MetaspaceSize limit.
- long metaspaceSize = Long.parseLong(args[0]);
- long allocationBeyondMetaspaceSize = metaspaceSize * 2;
- long metaspace = wb.allocateMetaspace(null, allocationBeyondMetaspaceSize);
-
- // Wait for at least one GC to occur. The caller will parse the log files produced.
- GarbageCollectorMXBean cmsGCBean = getCMSGCBean();
- while (cmsGCBean.getCollectionCount() == 0) {
- Thread.sleep(100);
- }
-
- wb.freeMetaspace(null, metaspace, metaspace);
- }
-
- private static GarbageCollectorMXBean getCMSGCBean() {
- for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) {
- if (gcBean.getObjectName().toString().equals("java.lang:type=GarbageCollector,name=ConcurrentMarkSweep")) {
- return gcBean;
- }
- }
- return null;
- }
- }
-}
-
--- a/test/hotspot/jtreg/gc/class_unloading/TestClassUnloadingDisabled.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/class_unloading/TestClassUnloadingDisabled.java Wed Nov 13 17:21:31 2019 -0500
@@ -81,24 +81,6 @@
*/
/*
- * @test TestClassUnloadingDisabledCMS
- * @key gc
- * @bug 8114823
- * @comment Graal does not support CMS
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @requires vm.opt.ExplicitGCInvokesConcurrent != true
- * @requires vm.opt.ClassUnloading != true
- * @library /test/lib
- * @modules java.base/jdk.internal.misc
- * java.management
- * @build sun.hotspot.WhiteBox
- * @run driver ClassFileInstaller sun.hotspot.WhiteBox
- * sun.hotspot.WhiteBox$WhiteBoxPermission
- * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
- * -XX:-ClassUnloading -XX:+UseConcMarkSweepGC gc.class_unloading.TestClassUnloadingDisabled
- */
-
-/*
* @test TestClassUnloadingDisabledShenandoah
* @key gc
* @bug 8114823
--- a/test/hotspot/jtreg/gc/cms/DisableResizePLAB.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2014, 2019, 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 gc.cms;
-
-import static java.lang.ref.Reference.reachabilityFence;
-
-/*
- * @test DisableResizePLAB
- * @key gc
- * @bug 8060467
- * @author filipp.zhinkin@oracle.com, john.coomes@oracle.com
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @summary Run CMS with PLAB resizing disabled and a small OldPLABSize
- * @run main/othervm -XX:+UseConcMarkSweepGC -XX:-ResizePLAB -XX:OldPLABSize=1k -Xmx256m -Xlog:gc=debug gc.cms.DisableResizePLAB
- */
-
-public class DisableResizePLAB {
- public static void main(String args[]) throws Exception {
- Object garbage[] = new Object[1_000];
- for (int i = 0; i < garbage.length; i++) {
- garbage[i] = new byte[0];
- }
- long startTime = System.currentTimeMillis();
- while (System.currentTimeMillis() - startTime < 10_000) {
- reachabilityFence(new byte[1024]);
- }
- }
-}
--- a/test/hotspot/jtreg/gc/cms/GuardShrinkWarning.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/*
- * Copyright (c) 2013, 2019, 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 gc.cms;
-
-/**
- * @test GuardShrinkWarning
- * @key gc regression
- * @summary Remove warning about CMS generation shrinking.
- * @bug 8012111
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @library /test/lib
- * @modules java.base/jdk.internal.misc
- * java.management
- * @run main/othervm gc.cms.GuardShrinkWarning
- * @author jon.masamitsu@oracle.com
- */
-
-import jdk.test.lib.process.OutputAnalyzer;
-import jdk.test.lib.process.ProcessTools;
-
-public class GuardShrinkWarning {
- public static void main(String args[]) throws Exception {
-
- ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
- "-showversion",
- "-XX:+UseConcMarkSweepGC",
- "-XX:+ExplicitGCInvokesConcurrent",
- SystemGCCaller.class.getName()
- );
-
- OutputAnalyzer output = new OutputAnalyzer(pb.start());
-
- output.shouldNotContain("Shrinking of CMS not yet implemented");
-
- output.shouldNotContain("error");
-
- output.shouldHaveExitValue(0);
- }
- static class SystemGCCaller {
- public static void main(String [] args) {
- System.gc();
- }
- }
-}
--- a/test/hotspot/jtreg/gc/cms/TestBubbleUpRef.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,188 +0,0 @@
-/*
- * Copyright (c) 2004, 2019, 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 gc.cms;
-
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
-import java.util.LinkedList;
-import java.util.ListIterator;
-
-/*
- * @test
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @key cte_test
- * @bug 4950157
- * @summary Stress the behavior of ergonomics when the heap is nearly full and
- * stays nearly full.
- * @run main/othervm
- * -XX:+UseConcMarkSweepGC -XX:-CMSYield -XX:-CMSPrecleanRefLists1
- * -XX:CMSInitiatingOccupancyFraction=0 -Xmx80m gc.cms.TestBubbleUpRef 16000 50 10000
- */
-
-/**
- * Test program to stress the behavior of ergonomics when the
- * heap is nearly full and stays nearly full.
- * This is a test to catch references that have been discovered
- * during concurrent marking and whose referents have been
- * cleared by the mutator.
- * Allocate objects with weak references until the heap is full
- * Free the objects.
- * Do work so that concurrent marking has a chance to work
- * Clear the referents out of the weak references
- * System.gc() in the hopes that it will acquire the collection
- * Free the weak references
- * Do it again.
- *
- * Use the following VM options
- * -Xmx80m -XX:-CMSYield [-XX:+UseConcMarkSweepGC] -XX:-CMSPrecleanRefLists1
- * -XX:CMSInitiatingOccupancyFraction=0
- *
- * Use parameter:
- * args[0] - array size (16000)
- * args[1] - iterations (50)
- * args[2] - work (10000)
- */
-class MyList extends LinkedList {
-
- int[] a;
-
- MyList(int size) {
- a = new int[size];
- }
-}
-
-class MyRefList extends LinkedList {
-
- WeakReference ref;
-
- MyRefList(Object o, ReferenceQueue rq) {
- ref = new WeakReference(o, rq);
- }
-
- void clearReferent() {
- ref.clear();
- }
-}
-
-public class TestBubbleUpRef {
-
- MyList list;
- MyRefList refList;
- ReferenceQueue rq;
- int refListLen;
- int arraySize;
- int iterations;
- int workUnits;
-
- TestBubbleUpRef(int as, int cnt, int wk) {
- arraySize = as;
- iterations = cnt;
- workUnits = wk;
- list = new MyList(arraySize);
- refList = new MyRefList(list, rq);
- }
-
- public void fill() {
- System.out.println("fill() " + iterations + " times");
- int count = 0;
- while (true) {
- try {
- // Allocations
- MyList next = new MyList(arraySize);
- list.add(next);
- MyRefList nextRef = new MyRefList(next, rq);
- refList.add(nextRef);
- } catch (OutOfMemoryError e) {
- // When the heap is full
- try {
- if (count++ > iterations) {
- return;
- }
- System.out.println("Freeing list");
- while (!list.isEmpty()) {
- list.removeFirst();
- }
- System.out.println("Doing work");
- int j = 0;
- for (int i = 1; i < workUnits; i++) {
- j = j + i;
- }
- System.out.println("Clearing refs");
- ListIterator listIt = refList.listIterator();
- while (listIt.hasNext()) {
- MyRefList next = (MyRefList) listIt.next();
- next.clearReferent();
- }
- System.gc();
- System.out.println("Freeing refs");
- while (!refList.isEmpty()) {
- refList.removeFirst();
- }
- } catch (OutOfMemoryError e2) {
- System.err.println("Out of Memory - 2 ");
- continue;
- }
- } catch (Exception e) {
- System.err.println("Unexpected exception: " + e);
- return;
- }
- }
- }
-
- /**
- * Test entry point.
- * args[0] - array size (is the size of the int array in a list item)
- * args[1] - iterations (is the number of out-of-memory exceptions before exit)
- * args[2] - work (is the work done between allocations)
- * @param args
- */
- public static void main(String[] args) {
- // Get the input parameters.
- if (args.length != 3) {
- throw new IllegalArgumentException("Wrong number of input argumets");
- }
-
- int as = Integer.parseInt(args[0]);
- int cnt = Integer.parseInt(args[1]);
- int work = Integer.parseInt(args[2]);
-
- System.out.println("<array size> " + as + "\n"
- + "<OOM's> " + cnt + "\n"
- + "<work units> " + work + "\n");
-
- // Initialization
- TestBubbleUpRef b = new TestBubbleUpRef(as, cnt, work);
-
- // Run the test
- try {
- b.fill();
- } catch (OutOfMemoryError e) {
- b = null; // Free memory before trying to print anything
- System.err.println("Out of Memory - exiting ");
- } catch (Exception e) {
- System.err.println("Exiting ");
- }
- }
-}
-
--- a/test/hotspot/jtreg/gc/cms/TestCMSScavengeBeforeRemark.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2015, 2019, 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 gc.cms;
-
-/*
- * @test TestCMSScavengeBeforeRemark
- * @key gc
- * @bug 8139868
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @summary Run CMS with CMSScavengeBeforeRemark
- * @run main/othervm -XX:+UseConcMarkSweepGC -XX:+CMSScavengeBeforeRemark -XX:+ExplicitGCInvokesConcurrent -Xmx256m -Xlog:gc=debug gc.cms.TestCMSScavengeBeforeRemark
- */
-
-public class TestCMSScavengeBeforeRemark {
- public static void main(String args[]) throws Exception {
- System.gc();
- }
-}
--- a/test/hotspot/jtreg/gc/cms/TestCriticalPriority.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2019, Red Hat, Inc. 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 gc.cms;
-
-/*
- * @test TestCriticalPriority
- * @key gc
- * @bug 8217378
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @summary Test critical priority is accepted
- * @run main/othervm -XX:+UseConcMarkSweepGC -XX:+UnlockExperimentalVMOptions -XX:+UseCriticalCMSThreadPriority gc.cms.TestCriticalPriority
- */
-
-public class TestCriticalPriority {
- public static void main(String args[]) throws Exception {
- // The failure would be detected before entering main().
- }
-}
--- a/test/hotspot/jtreg/gc/cms/TestMBeanCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,157 +0,0 @@
-/*
- * Copyright (c) 2010, 2019, 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 gc.cms;
-
-/*
- * @test TestMBeanCMS.java
- * @bug 6581734
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @summary CMS Old Gen's collection usage is zero after GC which is incorrect
- * @modules java.management
- * @run main/othervm -Xmx512m -verbose:gc -XX:+UseConcMarkSweepGC gc.cms.TestMBeanCMS
- *
- */
-
-import java.lang.management.GarbageCollectorMXBean;
-import java.lang.management.ManagementFactory;
-import java.lang.management.MemoryPoolMXBean;
-import java.util.LinkedList;
-import java.util.List;
-
-// 6581734 states that memory pool usage via the mbean is wrong
-// for CMS (zero, even after a collection).
-//
-// 6580448 states that the collection count similarly is wrong
-// (stays at zero for CMS collections)
-// -- closed as dup of 6581734 as the same fix resolves both.
-
-
-public class TestMBeanCMS {
-
- private String poolName = "CMS";
- private String collectorName = "ConcurrentMarkSweep";
-
- public static void main(String [] args) {
-
- TestMBeanCMS t = null;
- if (args.length==2) {
- t = new TestMBeanCMS(args[0], args[1]);
- } else {
- System.out.println("Defaulting to monitor CMS pool and collector.");
- t = new TestMBeanCMS();
- }
- t.run();
- }
-
- public TestMBeanCMS(String pool, String collector) {
- poolName = pool;
- collectorName = collector;
- }
-
- public TestMBeanCMS() {
- }
-
- public void run() {
- // Use some memory, enough that we expect collections should
- // have happened.
- // Must run with options to ensure no stop the world full GC,
- // but e.g. at least one CMS cycle.
- allocationWork(300*1024*1024);
- System.out.println("Done allocationWork");
-
- // Verify some non-zero results are stored.
- List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
- int poolsFound = 0;
- int poolsWithStats = 0;
- for (int i=0; i<pools.size(); i++) {
- MemoryPoolMXBean pool = pools.get(i);
- String name = pool.getName();
- System.out.println("found pool: " + name);
-
- if (name.contains(poolName)) {
- long usage = pool.getCollectionUsage().getUsed();
- System.out.println(name + ": usage after GC = " + usage);
- poolsFound++;
- if (usage > 0) {
- poolsWithStats++;
- }
- }
- }
- if (poolsFound == 0) {
- throw new RuntimeException("No matching memory pools found: test with -XX:+UseConcMarkSweepGC");
- }
-
- List<GarbageCollectorMXBean> collectors = ManagementFactory.getGarbageCollectorMXBeans();
- int collectorsFound = 0;
- int collectorsWithTime= 0;
- for (int i=0; i<collectors.size(); i++) {
- GarbageCollectorMXBean collector = collectors.get(i);
- String name = collector.getName();
- System.out.println("found collector: " + name);
- if (name.contains(collectorName)) {
- collectorsFound++;
- System.out.println(name + ": collection count = "
- + collector.getCollectionCount());
- System.out.println(name + ": collection time = "
- + collector.getCollectionTime());
- if (collector.getCollectionCount() <= 0) {
- throw new RuntimeException("collection count <= 0");
- }
- if (collector.getCollectionTime() > 0) {
- collectorsWithTime++;
- }
- }
- }
- // verify:
- if (poolsWithStats < poolsFound) {
- throw new RuntimeException("pools found with zero stats");
- }
-
- if (collectorsWithTime<collectorsFound) {
- throw new RuntimeException("collectors found with zero time");
- }
- System.out.println("Test passed.");
- }
-
- public void allocationWork(long target) {
-
- long sizeAllocated = 0;
- List<byte[]> list = new LinkedList<>();
- long delay = 50;
- long count = 0;
-
- while (sizeAllocated < target) {
- int size = 1024*1024;
- byte [] alloc = new byte[size];
- if (count % 2 == 0) {
- list.add(alloc);
- sizeAllocated+=size;
- System.out.print(".");
- }
- try { Thread.sleep(delay); } catch (InterruptedException ie) { }
- count++;
- }
- }
-
-}
--- a/test/hotspot/jtreg/gc/concurrent_phase_control/TestConcurrentPhaseControlCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2017, 2019, 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 gc.concurrent_phase_control;
-
-/*
- * @test TestConcurrentPhaseControlCMS
- * @bug 8169517
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @summary Verify CMS GC doesn't support WhiteBox concurrent phase control.
- * @key gc
- * @modules java.base
- * @library /test/lib /
- * @build sun.hotspot.WhiteBox
- * @run driver ClassFileInstaller sun.hotspot.WhiteBox
- * sun.hotspot.WhiteBox$WhiteBoxPermission
- * @run main/othervm -XX:+UseConcMarkSweepGC
- * -Xbootclasspath/a:.
- * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
- * gc.concurrent_phase_control.TestConcurrentPhaseControlCMS
- */
-
-import gc.concurrent_phase_control.CheckUnsupported;
-
-public class TestConcurrentPhaseControlCMS {
-
- public static void main(String[] args) throws Exception {
- CheckUnsupported.check("CMS");
- }
-}
--- a/test/hotspot/jtreg/gc/ergonomics/TestDynamicNumberOfGCThreads.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/ergonomics/TestDynamicNumberOfGCThreads.java Wed Nov 13 17:21:31 2019 -0500
@@ -44,11 +44,6 @@
public static void main(String[] args) throws Exception {
boolean noneGCSupported = true;
- if (GC.ConcMarkSweep.isSupported()) {
- noneGCSupported = false;
- testDynamicNumberOfGCThreads("UseConcMarkSweepGC");
- }
-
if (GC.G1.isSupported()) {
noneGCSupported = false;
testDynamicNumberOfGCThreads("UseG1GC");
@@ -65,7 +60,7 @@
}
if (noneGCSupported) {
- throw new SkippedException("Skipping test because none of ConcMarkSweep/G1/Parallel/Shenandoah is supported.");
+ throw new SkippedException("Skipping test because none of G1/Parallel/Shenandoah is supported.");
}
}
--- a/test/hotspot/jtreg/gc/ergonomics/TestInitialGCThreadLogging.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/ergonomics/TestInitialGCThreadLogging.java Wed Nov 13 17:21:31 2019 -0500
@@ -44,11 +44,6 @@
public static void main(String[] args) throws Exception {
boolean noneGCSupported = true;
- if (GC.ConcMarkSweep.isSupported()) {
- noneGCSupported = false;
- testInitialGCThreadLogging("UseConcMarkSweepGC", "GC Thread");
- }
-
if (GC.G1.isSupported()) {
noneGCSupported = false;
testInitialGCThreadLogging("UseG1GC", "GC Thread");
@@ -65,7 +60,7 @@
}
if (noneGCSupported) {
- throw new SkippedException("Skipping test because none of ConcMarkSweep/G1/Parallel/Shenandoah is supported.");
+ throw new SkippedException("Skipping test because none of G1/Parallel/Shenandoah is supported.");
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/gc/g1/numa/TestG1NUMATouchRegions.java Wed Nov 13 17:21:31 2019 -0500
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2019, 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 gc.g1;
+
+/**
+ * @test TestG1NUMATouchRegions
+ * @summary Ensure the bottom of the given heap regions are properly touched with requested NUMA id.
+ * @key gc
+ * @requires vm.gc.G1
+ * @requires os.family == "linux"
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ * java.management
+ * @build sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main/othervm -XX:+UseG1GC -Xbootclasspath/a:. -XX:+UseNUMA -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI gc.g1.TestG1NUMATouchRegions
+ */
+
+import java.util.LinkedList;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import sun.hotspot.WhiteBox;
+
+public class TestG1NUMATouchRegions {
+ enum NUMASupportStatus {
+ NOT_CHECKED,
+ SUPPORT,
+ NOT_SUPPORT
+ };
+
+ static int G1HeapRegionSize1MB = 1;
+ static int G1HeapRegionSize8MB = 8;
+
+ static NUMASupportStatus status = NUMASupportStatus.NOT_CHECKED;
+
+ public static void main(String[] args) throws Exception {
+ // 1. Page size < G1HeapRegionSize
+ // Test default page with 1MB heap region size
+ testMemoryTouch("-XX:-UseLargePages", G1HeapRegionSize1MB);
+ // 2. Page size > G1HeapRegionSize
+ // Test large page with 1MB heap region size.
+ testMemoryTouch("-XX:+UseLargePages", G1HeapRegionSize1MB);
+ // 3. Page size < G1HeapRegionSize
+ // Test large page with 8MB heap region size.
+ testMemoryTouch("-XX:+UseLargePages", G1HeapRegionSize8MB);
+ }
+
+ // On Linux, always UseNUMA is enabled if there is multiple active numa nodes.
+ static NUMASupportStatus checkNUMAIsEnabled(OutputAnalyzer output) {
+ boolean supportNUMA = Boolean.parseBoolean(output.firstMatch("\\bUseNUMA\\b.*?=.*?([a-z]+)", 1));
+ System.out.println("supportNUMA=" + supportNUMA);
+ return supportNUMA ? NUMASupportStatus.SUPPORT : NUMASupportStatus.NOT_SUPPORT;
+ }
+
+ static long parseSizeString(String size) {
+ long multiplier = 1;
+
+ if (size.endsWith("B")) {
+ multiplier = 1;
+ } else if (size.endsWith("K")) {
+ multiplier = 1024;
+ } else if (size.endsWith("M")) {
+ multiplier = 1024 * 1024;
+ } else if (size.endsWith("G")) {
+ multiplier = 1024 * 1024 * 1024;
+ } else {
+ throw new IllegalArgumentException("Expected memory string '" + size + "'to end with either of: B, K, M, G");
+ }
+
+ long longSize = Long.parseUnsignedLong(size.substring(0, size.length() - 1));
+
+ return longSize * multiplier;
+ }
+
+ static long heapPageSize(OutputAnalyzer output) {
+ String HeapPageSizePattern = "Heap: .*page_size=([^ ]+)";
+ String str = output.firstMatch(HeapPageSizePattern, 1);
+
+ if (str == null) {
+ output.reportDiagnosticSummary();
+ throw new RuntimeException("Match from '" + HeapPageSizePattern + "' got 'null'");
+ }
+
+ return parseSizeString(str);
+ }
+
+ // 1. -UseLargePages: default page, page size < G1HeapRegionSize
+ // +UseLargePages: large page size <= G1HeapRegionSize
+ //
+ // Each 'int' represents a numa id of single HeapRegion (bottom page).
+ // e.g. 1MB heap region, 2MB page size and 2 NUMA nodes system
+ // Check the first set(2 regions)
+ // 0| ...omitted..| 0
+ // 1| ...omitted..| 1
+ static void checkCase1Pattern(OutputAnalyzer output, int index, long g1HeapRegionSize, long actualPageSize, int[] memoryNodeIds) throws Exception {
+ StringBuilder sb = new StringBuilder();
+
+ // Append index which means heap region index.
+ sb.append(String.format("%6d", index));
+ sb.append("| .* | ");
+
+ // Append page node id.
+ sb.append(memoryNodeIds[index]);
+
+ output.shouldMatch(sb.toString());
+ }
+
+ // 3. +UseLargePages: large page size > G1HeapRegionSize
+ //
+ // As a OS page is consist of multiple heap regions, log also should be
+ // printed multiple times for same numa id.
+ // e.g. 1MB heap region, 2MB page size and 2 NUMA nodes system
+ // Check the first set(4 regions)
+ // 0| ...omitted..| 0
+ // 1| ...omitted..| 0
+ // 2| ...omitted..| 1
+ // 3| ...omitted..| 1
+ static void checkCase2Pattern(OutputAnalyzer output, int index, long g1HeapRegionSize, long actualPageSize, int[] memoryNodeIds) throws Exception {
+ StringBuilder sb = new StringBuilder();
+
+ // Append page range.
+ int lines_to_print = (int)(actualPageSize / g1HeapRegionSize);
+ for (int i = 0; i < lines_to_print; i++) {
+ // Append index which means heap region index.
+ sb.append(String.format("%6d", index * lines_to_print + i));
+ sb.append("| .* | ");
+
+ // Append page node id.
+ sb.append(memoryNodeIds[index]);
+
+ output.shouldMatch(sb.toString());
+ sb.setLength(0);
+ }
+ }
+
+ static void checkNUMALog(OutputAnalyzer output, int regionSizeInMB) throws Exception {
+ WhiteBox wb = WhiteBox.getWhiteBox();
+ long g1HeapRegionSize = regionSizeInMB * 1024 * 1024;
+ long actualPageSize = heapPageSize(output);
+ long defaultPageSize = (long)wb.getVMPageSize();
+ int memoryNodeCount = wb.g1ActiveMemoryNodeCount();
+ int[] memoryNodeIds = wb.g1MemoryNodeIds();
+
+ System.out.println("node count=" + memoryNodeCount + ", actualPageSize=" + actualPageSize);
+ // Check for the first set of active numa nodes.
+ for (int index = 0; index < memoryNodeCount; index++) {
+ if (actualPageSize <= defaultPageSize) {
+ checkCase1Pattern(output, index, g1HeapRegionSize, actualPageSize, memoryNodeIds);
+ } else {
+ checkCase2Pattern(output, index, g1HeapRegionSize, actualPageSize, memoryNodeIds);
+ }
+ }
+ }
+
+ static void testMemoryTouch(String largePagesSetting, int regionSizeInMB) throws Exception {
+ // Skip testing with message.
+ if (status == NUMASupportStatus.NOT_SUPPORT) {
+ System.out.println("NUMA is not supported");
+ return;
+ }
+
+ ProcessBuilder pb_enabled = ProcessTools.createJavaProcessBuilder(
+ "-Xbootclasspath/a:.",
+ "-Xlog:pagesize,gc+heap+region=trace",
+ "-XX:+UseG1GC",
+ "-Xmx128m",
+ "-Xms128m",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI",
+ "-XX:+PrintFlagsFinal",
+ "-XX:+UseNUMA",
+ "-XX:+AlwaysPreTouch",
+ largePagesSetting,
+ "-XX:G1HeapRegionSize=" + regionSizeInMB + "m",
+ GCTest.class.getName());
+ OutputAnalyzer output = new OutputAnalyzer(pb_enabled.start());
+
+ // Check NUMA availability.
+ if (status == NUMASupportStatus.NOT_CHECKED) {
+ status = checkNUMAIsEnabled(output);
+ }
+
+ if (status == NUMASupportStatus.SUPPORT) {
+ checkNUMALog(output, regionSizeInMB);
+ } else {
+ // Exit with message for the first test.
+ System.out.println("NUMA is not supported");
+ }
+ }
+
+ static class GCTest {
+ public static final int M = 1024*1024;
+ public static LinkedList<Object> garbageList = new LinkedList<Object>();
+ // A large object referenced by a static.
+ static int[] filler = new int[10 * M];
+
+ public static void genGarbage() {
+ for (int i = 0; i < 32*1024; i++) {
+ garbageList.add(new int[100]);
+ }
+ garbageList.clear();
+ }
+
+ public static void main(String[] args) {
+
+ int[] large = new int[M];
+ Object ref = large;
+
+ System.out.println("Creating garbage");
+ for (int i = 0; i < 100; i++) {
+ // A large object that will be reclaimed eagerly.
+ large = new int[6*M];
+ genGarbage();
+ // Make sure that the compiler cannot completely remove
+ // the allocation of the large object until here.
+ System.out.println(large);
+ }
+
+ // Keep the reference to the first object alive.
+ System.out.println(ref);
+ System.out.println("Done");
+ }
+ }
+}
--- a/test/hotspot/jtreg/gc/logging/TestGCId.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/logging/TestGCId.java Wed Nov 13 17:21:31 2019 -0500
@@ -53,10 +53,6 @@
noneGCSupported = false;
testGCId("UseG1GC");
}
- if (GC.ConcMarkSweep.isSupported()) {
- noneGCSupported = false;
- testGCId("UseConcMarkSweepGC");
- }
if (GC.Serial.isSupported()) {
noneGCSupported = false;
testGCId("UseSerialGC");
@@ -67,7 +63,7 @@
}
if (noneGCSupported) {
- throw new SkippedException("Skipping test because none of Parallel/G1/ConcMarkSweep/Serial/Shenandoah is supported.");
+ throw new SkippedException("Skipping test because none of Parallel/G1/Serial/Shenandoah is supported.");
}
}
--- a/test/hotspot/jtreg/gc/metaspace/TestMetaspaceCMSCancel.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/*
- * Copyright (c) 2016, 2019, 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 gc.metaspace;
-import jdk.test.lib.process.ProcessTools;
-import jdk.test.lib.process.OutputAnalyzer;
-import jdk.test.lib.Asserts;
-import sun.hotspot.WhiteBox;
-
-/* @test TestMetaspaceCMSCancel
- * @bug 8026752
- * @summary Tests cancel of CMS concurrent cycle for Metaspace after a full GC
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @library /test/lib
- * @modules java.base/jdk.internal.misc
- * @build sun.hotspot.WhiteBox
- * @run driver ClassFileInstaller sun.hotspot.WhiteBox
- * @run main/othervm gc.metaspace.TestMetaspaceCMSCancel
- */
-
-
-public class TestMetaspaceCMSCancel {
-
- public static void main(String[] args) throws Exception {
- // Set a small MetaspaceSize so that a CMS concurrent collection will be
- // scheduled. Set CMSWaitDuration to 5s so that the concurrent collection
- // start may be delayed. It does not guarantee 5s before the start of the
- // concurrent collection but does increase the probability that it will
- // be started later. System.gc() is used to invoke a full collection. Set
- // ExplicitGCInvokesConcurrent to off so it is a STW collection.
- ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-Xbootclasspath/a:.",
- "-XX:+UnlockDiagnosticVMOptions",
- "-XX:+WhiteBoxAPI",
- "-XX:+UseConcMarkSweepGC",
- "-XX:MetaspaceSize=2m",
- "-XX:CMSWaitDuration=5000",
- "-XX:-ExplicitGCInvokesConcurrent",
- "-Xlog:gc*=debug",
- MetaspaceGCTest.class.getName());
-
- OutputAnalyzer output = new OutputAnalyzer(pb.start());
- output.shouldNotContain("Concurrent Reset");
- output.shouldHaveExitValue(0);
- }
-
- static class MetaspaceGCTest {
- public static void main(String [] args) {
- WhiteBox wb = WhiteBox.getWhiteBox();
- System.gc();
- Asserts.assertFalse(wb.metaspaceShouldConcurrentCollect());
- }
- }
-}
--- a/test/hotspot/jtreg/gc/metaspace/TestSizeTransitions.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/metaspace/TestSizeTransitions.java Wed Nov 13 17:21:31 2019 -0500
@@ -58,15 +58,6 @@
* @run driver gc.metaspace.TestSizeTransitions true -XX:+UseG1GC
*/
-/* @test TestSizeTransitionsCMS
- * @key gc
- * @requires vm.gc.ConcMarkSweep
- * @summary Tests that the metaspace size transition logging is done correctly.
- * @library /test/lib
- * @run driver gc.metaspace.TestSizeTransitions false -XX:+UseConcMarkSweepGC
- * @run driver gc.metaspace.TestSizeTransitions true -XX:+UseConcMarkSweepGC
- */
-
public class TestSizeTransitions {
public static class Run {
public static void main(String... args) throws Exception {
--- a/test/hotspot/jtreg/gc/startup_warnings/TestCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2013, 2019, 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 gc.startup_warnings;
-
-/*
- * @test TestCMS
- * @key gc
- * @bug 8006398 8155948 8179013
- * @summary Test that CMS prints a warning message
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @library /test/lib
- * @modules java.base/jdk.internal.misc
- * java.management
- * @run main gc.startup_warnings.TestCMS
- */
-
-import jdk.test.lib.process.ProcessTools;
-import jdk.test.lib.process.OutputAnalyzer;
-
-public class TestCMS {
-
- public static void runTest(String[] args) throws Exception {
- ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(args);
- OutputAnalyzer output = new OutputAnalyzer(pb.start());
- output.shouldContain("deprecated");
- output.shouldNotContain("error");
- output.shouldHaveExitValue(0);
- }
-
- public static void main(String args[]) throws Exception {
- runTest(new String[] {"-XX:+UseConcMarkSweepGC", "-version"});
- runTest(new String[] {"-Xconcgc", "-version"});
- runTest(new String[] {"-Xnoconcgc", "-version"});
- }
-
-}
--- a/test/hotspot/jtreg/gc/stress/TestReclaimStringsLeaksMemory.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/stress/TestReclaimStringsLeaksMemory.java Wed Nov 13 17:21:31 2019 -0500
@@ -35,7 +35,6 @@
* @run main/othervm gc.stress.TestReclaimStringsLeaksMemory -XX:+UseSerialGC
* @run main/othervm gc.stress.TestReclaimStringsLeaksMemory -XX:+UseParallelGC
* @run main/othervm gc.stress.TestReclaimStringsLeaksMemory -XX:+UseParallelGC -XX:-UseParallelOldGC
- * @run main/othervm gc.stress.TestReclaimStringsLeaksMemory -XX:+UseConcMarkSweepGC
* @run main/othervm gc.stress.TestReclaimStringsLeaksMemory -XX:+UseG1GC
*/
--- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2016, 2019, 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 gc.stress.gcbasher;
-
-import java.io.IOException;
-
-/*
- * @test TestGCBasherWithCMS
- * @key gc stress
- * @library /
- * @requires vm.gc.ConcMarkSweep
- * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled
- * @summary Stress the CMS GC by trying to make old objects more likely to be garbage than young objects.
- * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx256m -server -XX:+UseConcMarkSweepGC gc.stress.gcbasher.TestGCBasherWithCMS 120000
- */
-public class TestGCBasherWithCMS {
- public static void main(String[] args) throws IOException {
- TestGCBasher.main(args);
- }
-}
--- a/test/hotspot/jtreg/gc/stress/gclocker/TestGCLockerWithCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2017, 2019, 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 gc.stress.gclocker;
-
-/*
- * @test TestGCLockerWithCMS
- * @key gc
- * @library /
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @summary Stress CMS' GC locker by calling GetPrimitiveArrayCritical while concurrently filling up old gen.
- * @run main/native/othervm/timeout=200 -Xlog:gc*=info -Xms1500m -Xmx1500m -XX:+UseConcMarkSweepGC gc.stress.gclocker.TestGCLockerWithCMS
- */
-public class TestGCLockerWithCMS {
- public static void main(String[] args) {
- String[] testArgs = {"2", "CMS Old Gen"};
- TestGCLocker.main(testArgs);
- }
-}
--- a/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2017, 2019, 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 gc.stress.gcold;
-
-/*
- * @test TestGCOldWithCMS
- * @key gc
- * @library /
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @summary Stress the CMS GC by trying to make old objects more likely to be garbage than young objects.
- * @run main/othervm -Xmx384M -XX:+UseConcMarkSweepGC gc.stress.gcold.TestGCOldWithCMS 50 1 20 10 10000
- */
-public class TestGCOldWithCMS {
- public static void main(String[] args) {
- TestGCOld.main(args);
- }
-}
--- a/test/hotspot/jtreg/gc/stress/jfr/TestStressAllocationGCEventsWithCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.detailed;
-
-/**
- * @test
- * @requires vm.hasJFR
- * @requires (vm.gc == "null" | vm.gc == "ConcMarkSweep") & !vm.graal.enabled
- * @library /test/lib /test/jdk
- * @run main/othervm -XX:+UseConcMarkSweepGC -Xmx64m jdk.jfr.event.gc.detailed.TestStressAllocationGCEventsWithCMS
- */
-public class TestStressAllocationGCEventsWithCMS {
-
- public static void main(String[] args) throws Exception {
- new StressAllocationGCEvents().run(args);
- }
-}
--- a/test/hotspot/jtreg/gc/stress/jfr/TestStressAllocationGCEventsWithParNew.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.detailed;
-
-/**
- * @test
- * @requires vm.hasJFR
- * @requires vm.gc == "null" & !vm.graal.enabled
- * @library /test/lib /test/jdk
- * @run main/othervm -XX:+UseConcMarkSweepGC -Xmx64m jdk.jfr.event.gc.detailed.TestStressAllocationGCEventsWithParNew
- */
-public class TestStressAllocationGCEventsWithParNew {
-
- public static void main(String[] args) throws Exception {
- new StressAllocationGCEvents().run(args);
- }
-}
--- a/test/hotspot/jtreg/gc/stress/jfr/TestStressBigAllocationGCEventsWithCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.detailed;
-
-/**
- * @test
- * @requires vm.hasJFR
- * @requires (vm.gc == "null" | vm.gc == "ConcMarkSweep") & !vm.graal.enabled
- * @library /test/lib /test/jdk
- * @run main/othervm -XX:+UseConcMarkSweepGC -Xmx256m jdk.jfr.event.gc.detailed.TestStressBigAllocationGCEventsWithCMS 1048576
- */
-public class TestStressBigAllocationGCEventsWithCMS {
-
- public static void main(String[] args) throws Exception {
- new StressAllocationGCEvents().run(args);
- }
-}
--- a/test/hotspot/jtreg/gc/stress/jfr/TestStressBigAllocationGCEventsWithParNew.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.detailed;
-
-/**
- * @test
- * @requires vm.hasJFR
- * @requires vm.gc == "null" & !vm.graal.enabled
- * @library /test/lib /test/jdk
- * @run main/othervm -XX:+UseConcMarkSweepGC -Xmx256m jdk.jfr.event.gc.detailed.TestStressBigAllocationGCEventsWithParNew 1048576
- */
-public class TestStressBigAllocationGCEventsWithParNew {
-
- public static void main(String[] args) throws Exception {
- new StressAllocationGCEvents().run(args);
- }
-}
--- a/test/hotspot/jtreg/gc/stress/systemgc/TestSystemGCWithCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2017, 2019, 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 gc.stress.systemgc;
-
-/*
- * @test TestSystemGCWithCMS
- * @key gc stress
- * @bug 8190703
- * @library /
- * @requires vm.gc.ConcMarkSweep & !vm.graal.enabled
- * @summary Stress the CMS GC full GC by allocating objects of different lifetimes concurrently with System.gc().
- * @run main/othervm/timeout=300 -Xlog:gc*=info -Xmx512m -XX:+UseConcMarkSweepGC gc.stress.systemgc.TestSystemGCWithCMS 270
- */
-public class TestSystemGCWithCMS {
- public static void main(String[] args) throws Exception {
- TestSystemGC.main(args);
- }
-}
--- a/test/hotspot/jtreg/gc/survivorAlignment/SurvivorAlignmentTestMain.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/gc/survivorAlignment/SurvivorAlignmentTestMain.java Wed Nov 13 17:21:31 2019 -0500
@@ -71,7 +71,6 @@
private static final String G1_EDEN = "G1 Eden Space";
private static final String G1_SURVIVOR = "G1 Survivor Space";
private static final String SERIAL_TENURED = "Tenured Gen";
- private static final String CMS_TENURED = "CMS Old Gen";
private static final String PS_TENURED = "PS Old Gen";
private static final String G1_TENURED = "G1 Old Gen";
@@ -79,14 +78,6 @@
SurvivorAlignmentTestMain.WHITE_BOX.getUintxVMFlag(
"G1HeapRegionSize")).orElse(-1L);
- /**
- * Min size of free chunk in CMS generation.
- * An object allocated in CMS generation will at least occupy this amount
- * of bytes.
- */
- private static final long CMS_MIN_FREE_CHUNK_SIZE
- = 3L * Unsafe.ADDRESS_SIZE;
-
private static final AlignmentHelper EDEN_SPACE_HELPER;
private static final AlignmentHelper SURVIVOR_SPACE_HELPER;
private static final AlignmentHelper TENURED_SPACE_HELPER;
@@ -125,11 +116,6 @@
* alignment in other spaces is expected to be equal to
* {@code ObjectAlignmentInBytes} value.
*
- * In CMS generation we can't allocate less then {@code MinFreeChunk} value,
- * for other CGs we expect that object of size {@code MIN_OBJECT_SIZE}
- * could be allocated as it is (of course, its size could be aligned
- * according to alignment value used in a particular space).
- *
* For G1 GC MXBeans could report memory usage only with region size
* precision (if an object allocated in some G1 heap region, then all region
* will claimed as used), so for G1's spaces precision is equal to
@@ -187,15 +173,6 @@
AlignmentHelper.OBJECT_ALIGNMENT_IN_BYTES,
AlignmentHelper.MIN_OBJECT_SIZE, pool);
break;
- case SurvivorAlignmentTestMain.CMS_TENURED:
- Asserts.assertNull(tenuredHelper,
- "Only one bean for tenured space is expected.");
- tenuredHelper = new AlignmentHelper(
- AlignmentHelper.OBJECT_ALIGNMENT_IN_BYTES,
- AlignmentHelper.OBJECT_ALIGNMENT_IN_BYTES,
- SurvivorAlignmentTestMain.CMS_MIN_FREE_CHUNK_SIZE,
- pool);
- break;
}
}
EDEN_SPACE_HELPER = Objects.requireNonNull(edenHelper,
--- a/test/hotspot/jtreg/runtime/7167069/PrintAsFlag.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/runtime/7167069/PrintAsFlag.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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,7 +27,7 @@
*
* @test PrintAsFlag
* @summary verify that Flag::print_as_flag() works correctly. This is used by "jinfo -flag" and -XX:+PrintCommandLineFlags.
- * @run main/othervm -XX:+PrintCommandLineFlags -XX:-ShowMessageBoxOnError -XX:BiasedLockingStartupDelay=4000 -XX:ParallelGCThreads=4 -XX:MaxRAM=1G -XX:CMSSmallCoalSurplusPercent=1.05 -XX:ErrorFile="file" PrintAsFlag
+ * @run main/othervm -XX:+PrintCommandLineFlags -XX:-ShowMessageBoxOnError -XX:BiasedLockingStartupDelay=4000 -XX:ParallelGCThreads=4 -XX:MaxRAM=1G -XX:ErrorFile="file" PrintAsFlag
*/
public class PrintAsFlag {
--- a/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java Wed Nov 13 17:21:31 2019 -0500
@@ -219,11 +219,6 @@
excludeTestMinRange("MallocMaxTestWords");
/*
- * Exclude CMSSamplingGrain as it can cause intermittent failures on Windows
- */
- excludeTestRange("CMSSamplingGrain");
-
- /*
* Exclude below options as their maximum value would consume too much memory
* and would affect other tests that run in parallel.
*/
--- a/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java Wed Nov 13 17:21:31 2019 -0500
@@ -400,8 +400,7 @@
}
if (GCType != null &&
- !(prepend.contains("-XX:+UseConcMarkSweepGC") ||
- prepend.contains("-XX:+UseSerialGC") ||
+ !(prepend.contains("-XX:+UseSerialGC") ||
prepend.contains("-XX:+UseParallelGC") ||
prepend.contains("-XX:+UseG1GC"))) {
explicitGC = GCType;
--- a/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOptionsUtils.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOptionsUtils.java Wed Nov 13 17:21:31 2019 -0500
@@ -79,9 +79,6 @@
for (GarbageCollectorMXBean gcMxBean : gcMxBeans) {
switch (gcMxBean.getName()) {
- case "ConcurrentMarkSweep":
- GCType = "-XX:+UseConcMarkSweepGC";
- break;
case "MarkSweepCompact":
GCType = "-XX:+UseSerialGC";
break;
@@ -186,10 +183,6 @@
option.addPrepend("-XX:+UseG1GC");
}
- if (name.startsWith("CMS")) {
- option.addPrepend("-XX:+UseConcMarkSweepGC");
- }
-
if (name.startsWith("NUMA")) {
option.addPrepend("-XX:+UseNUMA");
}
@@ -207,18 +200,6 @@
case "MaxMetaspaceFreeRatio":
option.addPrepend("-XX:MinMetaspaceFreeRatio=0");
break;
- case "CMSOldPLABMin":
- option.addPrepend("-XX:CMSOldPLABMax=" + option.getMax());
- break;
- case "CMSOldPLABMax":
- option.addPrepend("-XX:CMSOldPLABMin=" + option.getMin());
- break;
- case "CMSPrecleanNumerator":
- option.addPrepend("-XX:CMSPrecleanDenominator=" + option.getMax());
- break;
- case "CMSPrecleanDenominator":
- option.addPrepend("-XX:CMSPrecleanNumerator=" + ((new Integer(option.getMin())) - 1));
- break;
case "G1RefProcDrainInterval":
option.addPrepend("-XX:+ExplicitGCInvokesConcurrent");
break;
@@ -228,9 +209,6 @@
case "NUMAInterleaveGranularity":
option.addPrepend("-XX:+UseNUMAInterleaving");
break;
- case "CPUForCMSThread":
- option.addPrepend("-XX:+BindCMSThreadToCPU");
- break;
case "VerifyGCStartAt":
option.addPrepend("-XX:+VerifyBeforeGC");
option.addPrepend("-XX:+VerifyAfterGC");
--- a/test/hotspot/jtreg/runtime/CommandLine/TestNullTerminatedFlags.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/runtime/CommandLine/TestNullTerminatedFlags.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -36,8 +36,6 @@
public class TestNullTerminatedFlags {
public static String[] options = {
"-Xnoclassgc",
- "-Xconcgc",
- "-Xnoconcgc",
"-Xbatch",
"-green",
"-native",
--- a/test/hotspot/jtreg/runtime/CompressedOops/UseCompressedOops.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/runtime/CompressedOops/UseCompressedOops.java Wed Nov 13 17:21:31 2019 -0500
@@ -58,9 +58,6 @@
testCompressedOopsModes(args);
// Test GCs.
testCompressedOopsModes(args, "-XX:+UseG1GC");
- if (!Compiler.isGraalEnabled()) { // Graal does not support CMS
- testCompressedOopsModes(args, "-XX:+UseConcMarkSweepGC");
- }
testCompressedOopsModes(args, "-XX:+UseSerialGC");
testCompressedOopsModes(args, "-XX:+UseParallelGC");
testCompressedOopsModes(args, "-XX:+UseParallelOldGC");
--- a/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java Wed Nov 13 17:21:31 2019 -0500
@@ -50,7 +50,7 @@
// shared base address test table
private static final String[] testTable = {
- "-XX:+UseG1GC", "-XX:+UseSerialGC", "-XX:+UseParallelGC", "-XX:+UseConcMarkSweepGC",
+ "-XX:+UseG1GC", "-XX:+UseSerialGC", "-XX:+UseParallelGC",
"-XX:+FlightRecorder",
"-XX:+UseLargePages", // may only take effect on machines with large-pages
"-XX:+UseCompressedClassPointers",
@@ -123,18 +123,11 @@
}
}
- if (Compiler.isGraalEnabled() && testEntry.equals("-XX:+UseConcMarkSweepGC"))
- {
- System.out.println("Graal does not support CMS");
- return true;
- }
-
if (!WhiteBox.getWhiteBox().isJFRIncludedInVmBuild() && testEntry.equals("-XX:+FlightRecorder"))
{
System.out.println("JFR does not exist");
return true;
}
-
return false;
}
}
--- a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java Wed Nov 13 17:21:31 2019 -0500
@@ -112,9 +112,6 @@
// incompatible GCs
testDump(2, "-XX:+UseParallelGC", "", GC_WARNING, false);
testDump(3, "-XX:+UseSerialGC", "", GC_WARNING, false);
- if (!Compiler.isGraalEnabled()) { // Graal does not support CMS
- testDump(4, "-XX:+UseConcMarkSweepGC", "", GC_WARNING, false);
- }
// ======= archive with compressed oops, run w/o
testDump(5, "-XX:+UseG1GC", "-XX:+UseCompressedOops", null, false);
@@ -125,9 +122,6 @@
// Still run, to ensure no crash or exception
testExec(6, "-XX:+UseParallelGC", "", "", false);
testExec(7, "-XX:+UseSerialGC", "", "", false);
- if (!Compiler.isGraalEnabled()) { // Graal does not support CMS
- testExec(8, "-XX:+UseConcMarkSweepGC", "", "", false);
- }
// Test various oops encodings, by varying ObjectAlignmentInBytes and heap sizes
testDump(9, "-XX:+UseG1GC", "-XX:ObjectAlignmentInBytes=8", null, false);
--- a/test/hotspot/jtreg/runtime/testlibrary/ClassUnloadCommon.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/runtime/testlibrary/ClassUnloadCommon.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -62,7 +62,7 @@
}
public static void triggerUnloading() {
- allocateMemory(16 * 1024); // yg size is 8m with cms, force young collection
+ allocateMemory(16 * 1024); // force young collection
System.gc();
}
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2018, Google 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 MyPackage;
-
-/**
- * @test
- * @summary Verifies the JVMTI Heap Monitor Statistics using CMS GC
- * @build Frame HeapMonitor
- * @requires vm.gc == "ConcMarkSweep" | vm.gc == "null"
- * @requires !vm.graal.enabled
- * @compile HeapMonitorGCCMSTest.java
- * @run main/othervm/native -agentlib:HeapMonitorTest -XX:+UseConcMarkSweepGC MyPackage.HeapMonitorGCTest
- */
--- a/test/hotspot/jtreg/serviceability/sa/TestIntConstant.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/serviceability/sa/TestIntConstant.java Wed Nov 13 17:21:31 2019 -0500
@@ -64,11 +64,10 @@
// with names and the values derived from enums and #define preprocessor
// macros in hotspot.
expStrMap.put("intConstant", List.of(
- "CollectedHeap::G1 4",
+ "CollectedHeap::G1 3",
"RUNNABLE 2",
"Deoptimization::Reason_class_check 4",
"InstanceKlass::_misc_is_unsafe_anonymous 32",
- "Generation::ParNew 1",
"_thread_uninitialized 0"));
expStrMap.put("intConstant _temp_constant", List.of(
"intConstant _temp_constant 45"));
--- a/test/hotspot/jtreg/serviceability/sa/TestUniverse.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/serviceability/sa/TestUniverse.java Wed Nov 13 17:21:31 2019 -0500
@@ -63,10 +63,6 @@
expStrings.add("eden");
break;
- case ConcMarkSweep:
- expStrings.add("Gen 1: concurrent mark-sweep generation");
- break;
-
case G1:
expStrings.add("garbage-first heap");
expStrings.add("region size");
@@ -112,7 +108,7 @@
}
if (Compiler.isGraalEnabled()) {
- if (gc == GC.ConcMarkSweep || gc == GC.Epsilon || gc == GC.Z || gc == GC.Shenandoah) {
+ if (gc == GC.Epsilon || gc == GC.Z || gc == GC.Shenandoah) {
// Not supported
System.out.println ("Skipped testing of " + gc + "GC, not supported by Graal");
return false;
--- a/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_0_1/TestDescription.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_0_1/TestDescription.java Wed Nov 13 17:21:31 2019 -0500
@@ -32,7 +32,6 @@
* @comment Don't run test in configurations where we can't reliably count number of metaspace triggered GCs
* @requires vm.gc != null | !vm.opt.final.ClassUnloadingWithConcurrentMark
* @requires vm.gc != "G1" | !vm.opt.final.ClassUnloadingWithConcurrentMark
- * @requires vm.gc != "ConcMarkSweep"
* @requires vm.gc != "Z"
* @library /vmTestbase /test/lib
* @run driver jdk.test.lib.FileInstaller . .
--- a/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_10_20/TestDescription.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_10_20/TestDescription.java Wed Nov 13 17:21:31 2019 -0500
@@ -32,7 +32,6 @@
* @comment Don't run test in configurations where we can't reliably count number of metaspace triggered GCs
* @requires vm.gc != null | !vm.opt.final.ClassUnloadingWithConcurrentMark
* @requires vm.gc != "G1" | !vm.opt.final.ClassUnloadingWithConcurrentMark
- * @requires vm.gc != "ConcMarkSweep"
* @requires vm.gc != "Z"
* @library /vmTestbase /test/lib
* @run driver jdk.test.lib.FileInstaller . .
--- a/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_70_80/TestDescription.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_70_80/TestDescription.java Wed Nov 13 17:21:31 2019 -0500
@@ -32,7 +32,6 @@
* @comment Don't run test in configurations where we can't reliably count number of metaspace triggered GCs
* @requires vm.gc != null | !vm.opt.final.ClassUnloadingWithConcurrentMark
* @requires vm.gc != "G1" | !vm.opt.final.ClassUnloadingWithConcurrentMark
- * @requires vm.gc != "ConcMarkSweep"
* @requires vm.gc != "Z"
* @library /vmTestbase /test/lib
* @run driver jdk.test.lib.FileInstaller . .
--- a/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_99_100/TestDescription.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_99_100/TestDescription.java Wed Nov 13 17:21:31 2019 -0500
@@ -32,7 +32,6 @@
* @comment Don't run test in configurations where we can't reliably count number of metaspace triggered GCs
* @requires vm.gc != null | !vm.opt.final.ClassUnloadingWithConcurrentMark
* @requires vm.gc != "G1" | !vm.opt.final.ClassUnloadingWithConcurrentMark
- * @requires vm.gc != "ConcMarkSweep"
* @requires vm.gc != "Z"
* @library /vmTestbase /test/lib
* @run driver jdk.test.lib.FileInstaller . .
--- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/general_functions/GF08/gf08t001/TestDriver.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/general_functions/GF08/gf08t001/TestDriver.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -44,9 +44,9 @@
* First time, with "setVerboseMode=yes" agent mode. Second
* time, with "setVerboseMode=no" agent mode and with
* "-verbose:gc" VM option. In both cases the output is
- * searched for 'Full GC' string, unless ExplicitGCInvokesConcurrent
- * is enabled and G1 or CMS GCs are enbled. If ExplicitGCInvokesConcurrent and
- * either G1 or CMS GCs are enbled the test searches for 'GC' string in output.
+ * searched for 'Pause Full' string, unless ExplicitGCInvokesConcurrent
+ * is enabled and G1 is enabled. If ExplicitGCInvokesConcurrent and
+ * G1 is enabled the test searches for 'GC' string in output.
* The test fails if this string is not found in the output.
* COMMENTS
*
@@ -70,18 +70,17 @@
sun.hotspot.WhiteBox wb = sun.hotspot.WhiteBox.getWhiteBox();
Boolean isExplicitGCInvokesConcurrentOn = wb.getBooleanVMFlag("ExplicitGCInvokesConcurrent");
Boolean isUseG1GCon = wb.getBooleanVMFlag("UseG1GC");
- Boolean isUseConcMarkSweepGCon = wb.getBooleanVMFlag("UseConcMarkSweepGC");
Boolean isUseZGCon = wb.getBooleanVMFlag("UseZGC");
Boolean isShenandoahGCon = wb.getBooleanVMFlag("UseShenandoahGC");
Boolean isUseEpsilonGCon = wb.getBooleanVMFlag("UseEpsilonGC");
if (Compiler.isGraalEnabled() &&
- (isUseConcMarkSweepGCon || isUseZGCon || isUseEpsilonGCon || isShenandoahGCon)) {
+ (isUseZGCon || isUseEpsilonGCon || isShenandoahGCon)) {
return; // Graal does not support these GCs
}
String keyPhrase;
- if ((isExplicitGCInvokesConcurrentOn && (isUseG1GCon || isUseConcMarkSweepGCon)) || isUseZGCon || isShenandoahGCon) {
+ if ((isExplicitGCInvokesConcurrentOn && isUseG1GCon) || isUseZGCon || isShenandoahGCon) {
keyPhrase = "GC";
} else {
keyPhrase = "Pause Full";
--- a/test/jdk/ProblemList.txt Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/ProblemList.txt Wed Nov 13 17:21:31 2019 -0500
@@ -837,6 +837,8 @@
tools/pack200/CommandLineTests.java 8059906 generic-all
+tools/jlink/JLinkReproducibleTest.java 8217166 windows-all
+
############################################################################
# jdk_jdi
--- a/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/CheckOrigin.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/CheckOrigin.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -25,8 +25,6 @@
* @test
* @bug 8028994
* @author Staffan Larsen
- * @comment Graal does not support CMS
- * @requires !vm.graal.enabled
* @library /test/lib
* @modules jdk.attach/sun.tools.attach
* jdk.management
@@ -63,7 +61,7 @@
ProcessBuilder pb = ProcessTools.
createJavaProcessBuilder(
"--add-exports", "jdk.attach/sun.tools.attach=ALL-UNNAMED",
- "-XX:+UseConcMarkSweepGC", // this will cause MaxNewSize to be FLAG_SET_ERGO
+ "-XX:+UseG1GC", // this will cause MaxNewSize to be FLAG_SET_ERGO
"-XX:+UseCodeAging",
"-XX:+UseCerealGC", // Should be ignored.
"-XX:Flags=" + flagsFile.getAbsolutePath(),
@@ -73,8 +71,7 @@
"-runtests");
Map<String, String> env = pb.environment();
- // "UseCMSGC" should be ignored.
- env.put("_JAVA_OPTIONS", "-XX:+CheckJNICalls -XX:+UseCMSGC");
+ env.put("_JAVA_OPTIONS", "-XX:+CheckJNICalls");
// "UseGOneGC" should be ignored.
env.put("JAVA_TOOL_OPTIONS", "-XX:+IgnoreUnrecognizedVMOptions "
+ "-XX:+PrintVMOptions -XX:+UseGOneGC");
@@ -110,7 +107,7 @@
checkOrigin("PrintVMQWaitTime", Origin.CONFIG_FILE);
// Set through j.l.m
checkOrigin("HeapDumpOnOutOfMemoryError", Origin.MANAGEMENT);
- // Should be set by the VM, when we set UseConcMarkSweepGC
+ // Should be set by the VM, when we set UseG1GC
checkOrigin("MaxNewSize", Origin.ERGONOMIC);
// Set using attach
checkOrigin("HeapDumpPath", Origin.ATTACH_ON_DEMAND);
--- a/test/jdk/java/lang/String/Formatted.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/java/lang/String/Formatted.java Wed Nov 13 17:21:31 2019 -0500
@@ -25,7 +25,7 @@
* @test
* bug 8203444
* @summary Unit tests for instance versions of String#format
- * @compile --enable-preview -source 14 Formatted.java
+ * @compile --enable-preview -source ${jdk.version} Formatted.java
* @run main/othervm --enable-preview Formatted
*/
--- a/test/jdk/java/lang/String/StripIndent.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/java/lang/String/StripIndent.java Wed Nov 13 17:21:31 2019 -0500
@@ -25,7 +25,7 @@
* @test
* @bug 8223775
* @summary This exercises String#stripIndent patterns and limits.
- * @compile --enable-preview -source 14 StripIndent.java
+ * @compile --enable-preview -source ${jdk.version} StripIndent.java
* @run main/othervm --enable-preview StripIndent
*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/String/TEST.properties Wed Nov 13 17:21:31 2019 -0500
@@ -0,0 +1,1 @@
+allowSmartActionArgs=true
--- a/test/jdk/java/lang/String/TranslateEscapes.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/java/lang/String/TranslateEscapes.java Wed Nov 13 17:21:31 2019 -0500
@@ -25,7 +25,7 @@
* @test
* @bug 8223780
* @summary This exercises String#translateEscapes patterns and limits.
- * @compile --enable-preview -source 14 TranslateEscapes.java
+ * @compile --enable-preview -source ${jdk.version} TranslateEscapes.java
* @run main/othervm --enable-preview TranslateEscapes
*/
--- a/test/jdk/java/lang/management/GarbageCollectorMXBean/GcInfoCompositeType.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/java/lang/management/GarbageCollectorMXBean/GcInfoCompositeType.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2019, 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
@@ -30,7 +30,7 @@
* @run main/othervm -XX:-ExplicitGCInvokesConcurrent GcInfoCompositeType
*/
// Passing "-XX:-ExplicitGCInvokesConcurrent" to force System.gc()
-// run on foreground when CMS is used and prevent situations when "GcInfo"
+// run on foreground when a concurrent collector is used and prevent situations when "GcInfo"
// is missing even though System.gc() was successfuly processed.
import java.util.*;
--- a/test/jdk/java/lang/management/MemoryMXBean/CollectionUsageThreshold.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/java/lang/management/MemoryMXBean/CollectionUsageThreshold.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -76,9 +76,6 @@
RunUtil.runTestClearGcOpts(main, "-XX:+UseSerialGC");
RunUtil.runTestClearGcOpts(main, "-XX:+UseParallelGC");
RunUtil.runTestClearGcOpts(main, "-XX:+UseG1GC");
- if (!Compiler.isGraalEnabled()) { // Graal does not support CMS
- RunUtil.runTestClearGcOpts(main, "-XX:+UseConcMarkSweepGC");
- }
}
static class PoolRecord {
--- a/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -84,9 +84,6 @@
traceTest(classMain + ", -XX:+UseSerialGC", nmFlag, lpFlag, "-XX:+UseSerialGC");
traceTest(classMain + ", -XX:+UseParallelGC", nmFlag, lpFlag, "-XX:+UseParallelGC");
traceTest(classMain + ", -XX:+UseG1GC", nmFlag, lpFlag, "-XX:+UseG1GC", g1Flag);
- if (!Compiler.isGraalEnabled()) { // Graal does not support CMS
- traceTest(classMain + ", -XX:+UseConcMarkSweepGC", nmFlag, lpFlag, "-XX:+UseConcMarkSweepGC");
- }
}
/*
@@ -169,15 +166,10 @@
}
static class TestListener implements NotificationListener {
- private boolean isRelaxed = false;
private int triggers = 0;
private final long[] count = new long[NUM_TRIGGERS * 2];
private final long[] usedMemory = new long[NUM_TRIGGERS * 2];
- public TestListener() {
- isRelaxed = ManagementFactory.getRuntimeMXBean().getInputArguments().contains("-XX:+UseConcMarkSweepGC");
- }
-
@Override
public void handleNotification(Notification notif, Object handback) {
MemoryNotificationInfo minfo = MemoryNotificationInfo.
@@ -212,11 +204,7 @@
}
private boolean checkValue(long value, int target) {
- if (!isRelaxed) {
- return value == target;
- } else {
- return value >= target;
- }
+ return value == target;
}
}
--- a/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest2.sh Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest2.sh Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -59,7 +59,6 @@
go -noclassgc -XX:MaxMetaspaceSize=32m -XX:+UseSerialGC LowMemoryTest2
go -noclassgc -XX:MaxMetaspaceSize=32m -XX:+UseParallelGC LowMemoryTest2
-go -noclassgc -XX:MaxMetaspaceSize=32m -XX:+UseConcMarkSweepGC LowMemoryTest2
# Test class metaspace - might hit MaxMetaspaceSize instead if
# UseCompressedClassPointers is off or if 32 bit.
--- a/test/jdk/java/lang/management/MemoryMXBean/MemoryManagementConcMarkSweepGC.sh Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-#
-# Copyright (c) 2003, 2018, 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.
-#
-
-#
-# @test
-# @bug 4530538
-# @summary Run MemoryManagement test with concurrent mark sweep GC
-# @author Mandy Chung
-#
-# @requires (vm.gc=="ConcMarkSweep" | vm.gc=="null") & !vm.graal.enabled
-#
-# @run build MemoryManagement
-# @run shell/timeout=600 MemoryManagementConcMarkSweepGC.sh
-#
-
-#Set appropriate jdk
-
-if [ ! -z "${TESTJAVA}" ] ; then
- jdk="$TESTJAVA"
-else
- echo "--Error: TESTJAVA must be defined as the pathname of a jdk to test."
- exit 1
-fi
-
-runOne()
-{
- echo "runOne $@"
- $TESTJAVA/bin/java ${TESTVMOPTS} -classpath $TESTCLASSES $@ || exit 2
-}
-
-# Test MemoryManagement with concurrent collector
-runOne -XX:+UseConcMarkSweepGC MemoryManagement
-
-exit 0
--- a/test/jdk/java/lang/management/MemoryMXBean/PendingAllGC.sh Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/java/lang/management/MemoryMXBean/PendingAllGC.sh Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2003, 2019, 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
@@ -54,7 +54,4 @@
# Test Pending with parallel scavenger collector
runOne -XX:+UseParallelGC Pending
-# Test Pending with concurrent collector
-runOne -XX:+UseConcMarkSweepGC Pending
-
exit 0
--- a/test/jdk/java/lang/management/MemoryMXBean/ResetPeakMemoryUsage.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/java/lang/management/MemoryMXBean/ResetPeakMemoryUsage.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -63,9 +63,7 @@
final String main = "ResetPeakMemoryUsage$TestMain";
final String ms = "-Xms256m";
final String mn = "-Xmn8m";
- if (!Compiler.isGraalEnabled()) { // Graal does not support CMS
- RunUtil.runTestClearGcOpts(main, ms, mn, "-XX:+UseConcMarkSweepGC");
- }
+
RunUtil.runTestClearGcOpts(main, ms, mn, "-XX:+UseParallelGC");
RunUtil.runTestClearGcOpts(main, ms, mn, "-XX:+UseG1GC", "-XX:G1HeapRegionSize=1m");
RunUtil.runTestClearGcOpts(main, ms, mn, "-XX:+UseSerialGC",
--- a/test/jdk/java/util/Arrays/ParallelSorting.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2067 +0,0 @@
-/*
- * Copyright (c) 2011, 2013, 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.
- */
-
-/* Adapted from test/java/util/Arrays/Sorting.java
- *
- * Where that test checks Arrays.sort against manual quicksort routines,
- * this test checks parallelSort against either Arrays.sort or manual
- * quicksort routines.
- */
-
-/*
- * @test
- * @bug 8003981
- * @run main ParallelSorting -shortrun
- * @summary Exercise Arrays.parallelSort (adapted from test Sorting)
- *
- * @author Vladimir Yaroslavskiy
- * @author Jon Bentley
- * @author Josh Bloch
- */
-
-import java.util.Arrays;
-import java.util.Random;
-import java.io.PrintStream;
-import java.util.Comparator;
-
-public class ParallelSorting {
- private static final PrintStream out = System.out;
- private static final PrintStream err = System.err;
-
- // Array lengths used in a long run (default)
- private static final int[] LONG_RUN_LENGTHS = {
- 1000, 10000, 100000, 1000000 };
-
- // Array lengths used in a short run
- private static final int[] SHORT_RUN_LENGTHS = {
- 5000, 9000, 10000, 12000 };
-
- // Random initial values used in a long run (default)
- private static final long[] LONG_RUN_RANDOMS = { 666, 0xC0FFEE, 999 };
-
- // Random initial values used in a short run
- private static final long[] SHORT_RUN_RANDOMS = { 666 };
-
- public static void main(String[] args) {
- boolean shortRun = args.length > 0 && args[0].equals("-shortrun");
- long start = System.currentTimeMillis();
-
- if (shortRun) {
- testAndCheck(SHORT_RUN_LENGTHS, SHORT_RUN_RANDOMS);
- } else {
- testAndCheck(LONG_RUN_LENGTHS, LONG_RUN_RANDOMS);
- }
- long end = System.currentTimeMillis();
-
- out.format("PASSED in %d sec.\n", Math.round((end - start) / 1E3));
- }
-
- private static void testAndCheck(int[] lengths, long[] randoms) {
- testEmptyAndNullIntArray();
- testEmptyAndNullLongArray();
- testEmptyAndNullShortArray();
- testEmptyAndNullCharArray();
- testEmptyAndNullByteArray();
- testEmptyAndNullFloatArray();
- testEmptyAndNullDoubleArray();
-
- for (int length : lengths) {
- testMergeSort(length);
- testAndCheckRange(length);
- testAndCheckSubArray(length);
- }
- for (long seed : randoms) {
- for (int length : lengths) {
- testAndCheckWithInsertionSort(length, new MyRandom(seed));
- testAndCheckWithCheckSum(length, new MyRandom(seed));
- testAndCheckWithScrambling(length, new MyRandom(seed));
- testAndCheckFloat(length, new MyRandom(seed));
- testAndCheckDouble(length, new MyRandom(seed));
- testStable(length, new MyRandom(seed));
- }
- }
- }
-
- private static void testEmptyAndNullIntArray() {
- ourDescription = "Check empty and null array";
- Arrays.parallelSort(new int[]{});
- Arrays.parallelSort(new int[]{}, 0, 0);
-
- try {
- Arrays.parallelSort((int[]) null);
- } catch (NullPointerException expected) {
- try {
- Arrays.parallelSort((int[]) null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- failed("Arrays.parallelSort(int[],fromIndex,toIndex) shouldn't " +
- "catch null array");
- }
- failed("Arrays.parallelSort(int[]) shouldn't catch null array");
- }
-
- private static void testEmptyAndNullLongArray() {
- ourDescription = "Check empty and null array";
- Arrays.parallelSort(new long[]{});
- Arrays.parallelSort(new long[]{}, 0, 0);
-
- try {
- Arrays.parallelSort((long[]) null);
- } catch (NullPointerException expected) {
- try {
- Arrays.parallelSort((long[]) null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- failed("Arrays.parallelSort(long[],fromIndex,toIndex) shouldn't " +
- "catch null array");
- }
- failed("Arrays.parallelSort(long[]) shouldn't catch null array");
- }
-
- private static void testEmptyAndNullShortArray() {
- ourDescription = "Check empty and null array";
- Arrays.parallelSort(new short[]{});
- Arrays.parallelSort(new short[]{}, 0, 0);
-
- try {
- Arrays.parallelSort((short[]) null);
- } catch (NullPointerException expected) {
- try {
- Arrays.parallelSort((short[]) null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- failed("Arrays.parallelSort(short[],fromIndex,toIndex) shouldn't " +
- "catch null array");
- }
- failed("Arrays.parallelSort(short[]) shouldn't catch null array");
- }
-
- private static void testEmptyAndNullCharArray() {
- ourDescription = "Check empty and null array";
- Arrays.parallelSort(new char[]{});
- Arrays.parallelSort(new char[]{}, 0, 0);
-
- try {
- Arrays.parallelSort((char[]) null);
- } catch (NullPointerException expected) {
- try {
- Arrays.parallelSort((char[]) null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- failed("Arrays.parallelSort(char[],fromIndex,toIndex) shouldn't " +
- "catch null array");
- }
- failed("Arrays.parallelSort(char[]) shouldn't catch null array");
- }
-
- private static void testEmptyAndNullByteArray() {
- ourDescription = "Check empty and null array";
- Arrays.parallelSort(new byte[]{});
- Arrays.parallelSort(new byte[]{}, 0, 0);
-
- try {
- Arrays.parallelSort((byte[]) null);
- } catch (NullPointerException expected) {
- try {
- Arrays.parallelSort((byte[]) null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- failed("Arrays.parallelSort(byte[],fromIndex,toIndex) shouldn't " +
- "catch null array");
- }
- failed("Arrays.parallelSort(byte[]) shouldn't catch null array");
- }
-
- private static void testEmptyAndNullFloatArray() {
- ourDescription = "Check empty and null array";
- Arrays.parallelSort(new float[]{});
- Arrays.parallelSort(new float[]{}, 0, 0);
-
- try {
- Arrays.parallelSort((float[]) null);
- } catch (NullPointerException expected) {
- try {
- Arrays.parallelSort((float[]) null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- failed("Arrays.parallelSort(float[],fromIndex,toIndex) shouldn't " +
- "catch null array");
- }
- failed("Arrays.parallelSort(float[]) shouldn't catch null array");
- }
-
- private static void testEmptyAndNullDoubleArray() {
- ourDescription = "Check empty and null array";
- Arrays.parallelSort(new double[]{});
- Arrays.parallelSort(new double[]{}, 0, 0);
-
- try {
- Arrays.parallelSort((double[]) null);
- } catch (NullPointerException expected) {
- try {
- Arrays.parallelSort((double[]) null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- failed("Arrays.parallelSort(double[],fromIndex,toIndex) shouldn't " +
- "catch null array");
- }
- failed("Arrays.parallelSort(double[]) shouldn't catch null array");
- }
-
- private static void testAndCheckSubArray(int length) {
- ourDescription = "Check sorting of subarray";
- int[] golden = new int[length];
- boolean newLine = false;
-
- for (int m = 1; m < length / 2; m *= 2) {
- newLine = true;
- int fromIndex = m;
- int toIndex = length - m;
-
- prepareSubArray(golden, fromIndex, toIndex, m);
- int[] test = golden.clone();
-
- for (TypeConverter converter : TypeConverter.values()) {
- out.println("Test 'subarray': " + converter +
- " length = " + length + ", m = " + m);
- Object convertedGolden = converter.convert(golden);
- Object convertedTest = converter.convert(test);
- sortSubArray(convertedTest, fromIndex, toIndex);
- checkSubArray(convertedTest, fromIndex, toIndex, m);
- }
- }
- if (newLine) {
- out.println();
- }
- }
-
- private static void testAndCheckRange(int length) {
- ourDescription = "Check range check";
- int[] golden = new int[length];
-
- for (int m = 1; m < 2 * length; m *= 2) {
- for (int i = 1; i <= length; i++) {
- golden[i - 1] = i % m + m % i;
- }
- for (TypeConverter converter : TypeConverter.values()) {
- out.println("Test 'range': " + converter +
- ", length = " + length + ", m = " + m);
- Object convertedGolden = converter.convert(golden);
- checkRange(convertedGolden, m);
- }
- }
- out.println();
- }
-
- private static void testStable(int length, MyRandom random) {
- ourDescription = "Check if sorting is stable";
- Pair[] a = build(length, random);
-
- out.println("Test 'stable': " + "random = " + random.getSeed() +
- ", length = " + length);
- Arrays.parallelSort(a);
- checkSorted(a);
- checkStable(a);
- out.println();
-
- a = build(length, random);
-
- out.println("Test 'stable' comparator: " + "random = " + random.getSeed() +
- ", length = " + length);
- Arrays.parallelSort(a, pairCmp);
- checkSorted(a);
- checkStable(a);
- out.println();
-
- }
-
- private static void checkSorted(Pair[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i].getKey() > a[i + 1].getKey()) {
- failedSort(i, "" + a[i].getKey(), "" + a[i + 1].getKey());
- }
- }
- }
-
- private static void checkStable(Pair[] a) {
- for (int i = 0; i < a.length / 4; ) {
- int key1 = a[i].getKey();
- int value1 = a[i++].getValue();
- int key2 = a[i].getKey();
- int value2 = a[i++].getValue();
- int key3 = a[i].getKey();
- int value3 = a[i++].getValue();
- int key4 = a[i].getKey();
- int value4 = a[i++].getValue();
-
- if (!(key1 == key2 && key2 == key3 && key3 == key4)) {
- failed("On position " + i + " keys are different " +
- key1 + ", " + key2 + ", " + key3 + ", " + key4);
- }
- if (!(value1 < value2 && value2 < value3 && value3 < value4)) {
- failed("Sorting is not stable at position " + i +
- ". Second values have been changed: " + value1 + ", " +
- value2 + ", " + value3 + ", " + value4);
- }
- }
- }
-
- private static Pair[] build(int length, Random random) {
- Pair[] a = new Pair[length * 4];
-
- for (int i = 0; i < a.length; ) {
- int key = random.nextInt();
- a[i++] = new Pair(key, 1);
- a[i++] = new Pair(key, 2);
- a[i++] = new Pair(key, 3);
- a[i++] = new Pair(key, 4);
- }
- return a;
- }
-
- private static Comparator<Pair> pairCmp = new Comparator<Pair>() {
- public int compare(Pair p1, Pair p2) {
- return p1.compareTo(p2);
- }
- };
-
- private static final class Pair implements Comparable<Pair> {
- Pair(int key, int value) {
- myKey = key;
- myValue = value;
- }
-
- int getKey() {
- return myKey;
- }
-
- int getValue() {
- return myValue;
- }
-
- public int compareTo(Pair pair) {
- if (myKey < pair.myKey) {
- return -1;
- }
- if (myKey > pair.myKey) {
- return 1;
- }
- return 0;
- }
-
- @Override
- public String toString() {
- return "(" + myKey + ", " + myValue + ")";
- }
-
- private int myKey;
- private int myValue;
- }
-
-
- private static void testAndCheckWithInsertionSort(int length, MyRandom random) {
- if (length > 1000) {
- return;
- }
- ourDescription = "Check sorting with insertion sort";
- int[] golden = new int[length];
-
- for (int m = 1; m < 2 * length; m *= 2) {
- for (UnsortedBuilder builder : UnsortedBuilder.values()) {
- builder.build(golden, m, random);
- int[] test = golden.clone();
-
- for (TypeConverter converter : TypeConverter.values()) {
- out.println("Test 'insertion sort': " + converter +
- " " + builder + "random = " + random.getSeed() +
- ", length = " + length + ", m = " + m);
- Object convertedGolden = converter.convert(golden);
- Object convertedTest1 = converter.convert(test);
- Object convertedTest2 = converter.convert(test);
- sort(convertedTest1);
- sortByInsertionSort(convertedTest2);
- compare(convertedTest1, convertedTest2);
- }
- }
- }
- out.println();
- }
-
- private static void testMergeSort(int length) {
- if (length < 1000) {
- return;
- }
- ourDescription = "Check merge sorting";
- int[] golden = new int[length];
- int period = 67; // java.util.DualPivotQuicksort.MAX_RUN_COUNT
-
- for (int m = period - 2; m <= period + 2; m++) {
- for (MergeBuilder builder : MergeBuilder.values()) {
- builder.build(golden, m);
- int[] test = golden.clone();
-
- for (TypeConverter converter : TypeConverter.values()) {
- out.println("Test 'merge sort': " + converter + " " +
- builder + "length = " + length + ", m = " + m);
- Object convertedGolden = converter.convert(golden);
- sort(convertedGolden);
- checkSorted(convertedGolden);
- }
- }
- }
- out.println();
- }
-
- private static void testAndCheckWithCheckSum(int length, MyRandom random) {
- ourDescription = "Check sorting with check sum";
- int[] golden = new int[length];
-
- for (int m = 1; m < 2 * length; m *= 2) {
- for (UnsortedBuilder builder : UnsortedBuilder.values()) {
- builder.build(golden, m, random);
- int[] test = golden.clone();
-
- for (TypeConverter converter : TypeConverter.values()) {
- out.println("Test 'check sum': " + converter +
- " " + builder + "random = " + random.getSeed() +
- ", length = " + length + ", m = " + m);
- Object convertedGolden = converter.convert(golden);
- Object convertedTest = converter.convert(test);
- sort(convertedTest);
- checkWithCheckSum(convertedTest, convertedGolden);
- }
- }
- }
- out.println();
- }
-
- private static void testAndCheckWithScrambling(int length, MyRandom random) {
- ourDescription = "Check sorting with scrambling";
- int[] golden = new int[length];
-
- for (int m = 1; m <= 7; m++) {
- if (m > length) {
- break;
- }
- for (SortedBuilder builder : SortedBuilder.values()) {
- builder.build(golden, m);
- int[] test = golden.clone();
- scramble(test, random);
-
- for (TypeConverter converter : TypeConverter.values()) {
- out.println("Test 'scrambling': " + converter +
- " " + builder + "random = " + random.getSeed() +
- ", length = " + length + ", m = " + m);
- Object convertedGolden = converter.convert(golden);
- Object convertedTest = converter.convert(test);
- sort(convertedTest);
- compare(convertedTest, convertedGolden);
- }
- }
- }
- out.println();
- }
-
- private static void testAndCheckFloat(int length, MyRandom random) {
- ourDescription = "Check float sorting";
- float[] golden = new float[length];
- final int MAX = 10;
- boolean newLine = false;
-
- for (int a = 0; a <= MAX; a++) {
- for (int g = 0; g <= MAX; g++) {
- for (int z = 0; z <= MAX; z++) {
- for (int n = 0; n <= MAX; n++) {
- for (int p = 0; p <= MAX; p++) {
- if (a + g + z + n + p > length) {
- continue;
- }
- if (a + g + z + n + p < length) {
- continue;
- }
- for (FloatBuilder builder : FloatBuilder.values()) {
- out.println("Test 'float': random = " + random.getSeed() +
- ", length = " + length + ", a = " + a + ", g = " +
- g + ", z = " + z + ", n = " + n + ", p = " + p);
- builder.build(golden, a, g, z, n, p, random);
- float[] test = golden.clone();
- scramble(test, random);
- sort(test);
- compare(test, golden, a, n, g);
- }
- newLine = true;
- }
- }
- }
- }
- }
- if (newLine) {
- out.println();
- }
- }
-
- private static void testAndCheckDouble(int length, MyRandom random) {
- ourDescription = "Check double sorting";
- double[] golden = new double[length];
- final int MAX = 10;
- boolean newLine = false;
-
- for (int a = 0; a <= MAX; a++) {
- for (int g = 0; g <= MAX; g++) {
- for (int z = 0; z <= MAX; z++) {
- for (int n = 0; n <= MAX; n++) {
- for (int p = 0; p <= MAX; p++) {
- if (a + g + z + n + p > length) {
- continue;
- }
- if (a + g + z + n + p < length) {
- continue;
- }
- for (DoubleBuilder builder : DoubleBuilder.values()) {
- out.println("Test 'double': random = " + random.getSeed() +
- ", length = " + length + ", a = " + a + ", g = " +
- g + ", z = " + z + ", n = " + n + ", p = " + p);
- builder.build(golden, a, g, z, n, p, random);
- double[] test = golden.clone();
- scramble(test, random);
- sort(test);
- compare(test, golden, a, n, g);
- }
- newLine = true;
- }
- }
- }
- }
- }
- if (newLine) {
- out.println();
- }
- }
-
- private static void prepareSubArray(int[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- a[i] = 0xDEDA;
- }
- int middle = (fromIndex + toIndex) >>> 1;
- int k = 0;
-
- for (int i = fromIndex; i < middle; i++) {
- a[i] = k++;
- }
- for (int i = middle; i < toIndex; i++) {
- a[i] = k--;
- }
- for (int i = toIndex; i < a.length; i++) {
- a[i] = 0xBABA;
- }
- }
-
- private static void scramble(int[] a, Random random) {
- for (int i = 0; i < a.length * 7; i++) {
- swap(a, random.nextInt(a.length), random.nextInt(a.length));
- }
- }
-
- private static void scramble(float[] a, Random random) {
- for (int i = 0; i < a.length * 7; i++) {
- swap(a, random.nextInt(a.length), random.nextInt(a.length));
- }
- }
-
- private static void scramble(double[] a, Random random) {
- for (int i = 0; i < a.length * 7; i++) {
- swap(a, random.nextInt(a.length), random.nextInt(a.length));
- }
- }
-
- private static void swap(int[] a, int i, int j) {
- int t = a[i];
- a[i] = a[j];
- a[j] = t;
- }
-
- private static void swap(float[] a, int i, int j) {
- float t = a[i];
- a[i] = a[j];
- a[j] = t;
- }
-
- private static void swap(double[] a, int i, int j) {
- double t = a[i];
- a[i] = a[j];
- a[j] = t;
- }
-
- private static enum TypeConverter {
- INT {
- Object convert(int[] a) {
- return a.clone();
- }
- },
- LONG {
- Object convert(int[] a) {
- long[] b = new long[a.length];
-
- for (int i = 0; i < a.length; i++) {
- b[i] = (long) a[i];
- }
- return b;
- }
- },
- BYTE {
- Object convert(int[] a) {
- byte[] b = new byte[a.length];
-
- for (int i = 0; i < a.length; i++) {
- b[i] = (byte) a[i];
- }
- return b;
- }
- },
- SHORT {
- Object convert(int[] a) {
- short[] b = new short[a.length];
-
- for (int i = 0; i < a.length; i++) {
- b[i] = (short) a[i];
- }
- return b;
- }
- },
- CHAR {
- Object convert(int[] a) {
- char[] b = new char[a.length];
-
- for (int i = 0; i < a.length; i++) {
- b[i] = (char) a[i];
- }
- return b;
- }
- },
- FLOAT {
- Object convert(int[] a) {
- float[] b = new float[a.length];
-
- for (int i = 0; i < a.length; i++) {
- b[i] = (float) a[i];
- }
- return b;
- }
- },
- DOUBLE {
- Object convert(int[] a) {
- double[] b = new double[a.length];
-
- for (int i = 0; i < a.length; i++) {
- b[i] = (double) a[i];
- }
- return b;
- }
- },
- INTEGER {
- Object convert(int[] a) {
- Integer[] b = new Integer[a.length];
-
- for (int i = 0; i < a.length; i++) {
- b[i] = new Integer(a[i]);
- }
- return b;
- }
- };
-
- abstract Object convert(int[] a);
-
- @Override public String toString() {
- String name = name();
-
- for (int i = name.length(); i < 9; i++) {
- name += " ";
- }
- return name;
- }
- }
-
- private static enum FloatBuilder {
- SIMPLE {
- void build(float[] x, int a, int g, int z, int n, int p, Random random) {
- int fromIndex = 0;
- float negativeValue = -random.nextFloat();
- float positiveValue = random.nextFloat();
-
- writeValue(x, negativeValue, fromIndex, n);
- fromIndex += n;
-
- writeValue(x, -0.0f, fromIndex, g);
- fromIndex += g;
-
- writeValue(x, 0.0f, fromIndex, z);
- fromIndex += z;
-
- writeValue(x, positiveValue, fromIndex, p);
- fromIndex += p;
-
- writeValue(x, Float.NaN, fromIndex, a);
- }
- };
-
- abstract void build(float[] x, int a, int g, int z, int n, int p, Random random);
- }
-
- private static enum DoubleBuilder {
- SIMPLE {
- void build(double[] x, int a, int g, int z, int n, int p, Random random) {
- int fromIndex = 0;
- double negativeValue = -random.nextFloat();
- double positiveValue = random.nextFloat();
-
- writeValue(x, negativeValue, fromIndex, n);
- fromIndex += n;
-
- writeValue(x, -0.0d, fromIndex, g);
- fromIndex += g;
-
- writeValue(x, 0.0d, fromIndex, z);
- fromIndex += z;
-
- writeValue(x, positiveValue, fromIndex, p);
- fromIndex += p;
-
- writeValue(x, Double.NaN, fromIndex, a);
- }
- };
-
- abstract void build(double[] x, int a, int g, int z, int n, int p, Random random);
- }
-
- private static void writeValue(float[] a, float value, int fromIndex, int count) {
- for (int i = fromIndex; i < fromIndex + count; i++) {
- a[i] = value;
- }
- }
-
- private static void compare(float[] a, float[] b, int numNaN, int numNeg, int numNegZero) {
- for (int i = a.length - numNaN; i < a.length; i++) {
- if (a[i] == a[i]) {
- failed("On position " + i + " must be NaN instead of " + a[i]);
- }
- }
- final int NEGATIVE_ZERO = Float.floatToIntBits(-0.0f);
-
- for (int i = numNeg; i < numNeg + numNegZero; i++) {
- if (NEGATIVE_ZERO != Float.floatToIntBits(a[i])) {
- failed("On position " + i + " must be -0.0 instead of " + a[i]);
- }
- }
- for (int i = 0; i < a.length - numNaN; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static void writeValue(double[] a, double value, int fromIndex, int count) {
- for (int i = fromIndex; i < fromIndex + count; i++) {
- a[i] = value;
- }
- }
-
- private static void compare(double[] a, double[] b, int numNaN, int numNeg, int numNegZero) {
- for (int i = a.length - numNaN; i < a.length; i++) {
- if (a[i] == a[i]) {
- failed("On position " + i + " must be NaN instead of " + a[i]);
- }
- }
- final long NEGATIVE_ZERO = Double.doubleToLongBits(-0.0d);
-
- for (int i = numNeg; i < numNeg + numNegZero; i++) {
- if (NEGATIVE_ZERO != Double.doubleToLongBits(a[i])) {
- failed("On position " + i + " must be -0.0 instead of " + a[i]);
- }
- }
- for (int i = 0; i < a.length - numNaN; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static enum SortedBuilder {
- REPEATED {
- void build(int[] a, int m) {
- int period = a.length / m;
- int i = 0;
- int k = 0;
-
- while (true) {
- for (int t = 1; t <= period; t++) {
- if (i >= a.length) {
- return;
- }
- a[i++] = k;
- }
- if (i >= a.length) {
- return;
- }
- k++;
- }
- }
- },
- ORGAN_PIPES {
- void build(int[] a, int m) {
- int i = 0;
- int k = m;
-
- while (true) {
- for (int t = 1; t <= m; t++) {
- if (i >= a.length) {
- return;
- }
- a[i++] = k;
- }
- }
- }
- };
-
- abstract void build(int[] a, int m);
-
- @Override public String toString() {
- String name = name();
-
- for (int i = name.length(); i < 12; i++) {
- name += " ";
- }
- return name;
- }
- }
-
- private static enum MergeBuilder {
- ASCENDING {
- void build(int[] a, int m) {
- int period = a.length / m;
- int v = 1, i = 0;
-
- for (int k = 0; k < m; k++) {
- v = 1;
- for (int p = 0; p < period; p++) {
- a[i++] = v++;
- }
- }
- for (int j = i; j < a.length - 1; j++) {
- a[j] = v++;
- }
- a[a.length - 1] = 0;
- }
- },
- DESCENDING {
- void build(int[] a, int m) {
- int period = a.length / m;
- int v = -1, i = 0;
-
- for (int k = 0; k < m; k++) {
- v = -1;
- for (int p = 0; p < period; p++) {
- a[i++] = v--;
- }
- }
- for (int j = i; j < a.length - 1; j++) {
- a[j] = v--;
- }
- a[a.length - 1] = 0;
- }
- };
-
- abstract void build(int[] a, int m);
-
- @Override public String toString() {
- String name = name();
-
- for (int i = name.length(); i < 12; i++) {
- name += " ";
- }
- return name;
- }
- }
-
- private static enum UnsortedBuilder {
- RANDOM {
- void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = random.nextInt();
- }
- }
- },
- ASCENDING {
- void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = m + i;
- }
- }
- },
- DESCENDING {
- void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = a.length - m - i;
- }
- }
- },
- ALL_EQUAL {
- void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = m;
- }
- }
- },
- SAW {
- void build(int[] a, int m, Random random) {
- int incCount = 1;
- int decCount = a.length;
- int i = 0;
- int period = m--;
-
- while (true) {
- for (int k = 1; k <= period; k++) {
- if (i >= a.length) {
- return;
- }
- a[i++] = incCount++;
- }
- period += m;
-
- for (int k = 1; k <= period; k++) {
- if (i >= a.length) {
- return;
- }
- a[i++] = decCount--;
- }
- period += m;
- }
- }
- },
- REPEATED {
- void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = i % m;
- }
- }
- },
- DUPLICATED {
- void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = random.nextInt(m);
- }
- }
- },
- ORGAN_PIPES {
- void build(int[] a, int m, Random random) {
- int middle = a.length / (m + 1);
-
- for (int i = 0; i < middle; i++) {
- a[i] = i;
- }
- for (int i = middle; i < a.length; i++) {
- a[i] = a.length - i - 1;
- }
- }
- },
- STAGGER {
- void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = (i * m + i) % a.length;
- }
- }
- },
- PLATEAU {
- void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = Math.min(i, m);
- }
- }
- },
- SHUFFLE {
- void build(int[] a, int m, Random random) {
- int x = 0, y = 0;
- for (int i = 0; i < a.length; i++) {
- a[i] = random.nextBoolean() ? (x += 2) : (y += 2);
- }
- }
- };
-
- abstract void build(int[] a, int m, Random random);
-
- @Override public String toString() {
- String name = name();
-
- for (int i = name.length(); i < 12; i++) {
- name += " ";
- }
- return name;
- }
- }
-
- private static void checkWithCheckSum(Object test, Object golden) {
- checkSorted(test);
- checkCheckSum(test, golden);
- }
-
- private static void failed(String message) {
- err.format("\n*** TEST FAILED - %s.\n\n%s.\n\n", ourDescription, message);
- throw new RuntimeException("Test failed - see log file for details");
- }
-
- private static void failedSort(int index, String value1, String value2) {
- failed("Array is not sorted at " + index + "-th position: " +
- value1 + " and " + value2);
- }
-
- private static void failedCompare(int index, String value1, String value2) {
- failed("On position " + index + " must be " + value2 + " instead of " + value1);
- }
-
- private static void compare(Object test, Object golden) {
- if (test instanceof int[]) {
- compare((int[]) test, (int[]) golden);
- } else if (test instanceof long[]) {
- compare((long[]) test, (long[]) golden);
- } else if (test instanceof short[]) {
- compare((short[]) test, (short[]) golden);
- } else if (test instanceof byte[]) {
- compare((byte[]) test, (byte[]) golden);
- } else if (test instanceof char[]) {
- compare((char[]) test, (char[]) golden);
- } else if (test instanceof float[]) {
- compare((float[]) test, (float[]) golden);
- } else if (test instanceof double[]) {
- compare((double[]) test, (double[]) golden);
- } else if (test instanceof Integer[]) {
- compare((Integer[]) test, (Integer[]) golden);
- } else {
- failed("Unknow type of array: " + test + " of class " +
- test.getClass().getName());
- }
- }
-
- private static void compare(int[] a, int[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static void compare(long[] a, long[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static void compare(short[] a, short[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static void compare(byte[] a, byte[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static void compare(char[] a, char[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static void compare(float[] a, float[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static void compare(double[] a, double[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static void compare(Integer[] a, Integer[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i].compareTo(b[i]) != 0) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static void checkSorted(Object object) {
- if (object instanceof int[]) {
- checkSorted((int[]) object);
- } else if (object instanceof long[]) {
- checkSorted((long[]) object);
- } else if (object instanceof short[]) {
- checkSorted((short[]) object);
- } else if (object instanceof byte[]) {
- checkSorted((byte[]) object);
- } else if (object instanceof char[]) {
- checkSorted((char[]) object);
- } else if (object instanceof float[]) {
- checkSorted((float[]) object);
- } else if (object instanceof double[]) {
- checkSorted((double[]) object);
- } else if (object instanceof Integer[]) {
- checkSorted((Integer[]) object);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- }
- }
-
- private static void checkSorted(int[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
- }
-
- private static void checkSorted(long[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
- }
-
- private static void checkSorted(short[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
- }
-
- private static void checkSorted(byte[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
- }
-
- private static void checkSorted(char[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
- }
-
- private static void checkSorted(float[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
- }
-
- private static void checkSorted(double[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
- }
-
- private static void checkSorted(Integer[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i].intValue() > a[i + 1].intValue()) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
- }
-
- private static void checkCheckSum(Object test, Object golden) {
- if (checkSumXor(test) != checkSumXor(golden)) {
- failed("Original and sorted arrays are not identical [xor]");
- }
- if (checkSumPlus(test) != checkSumPlus(golden)) {
- failed("Original and sorted arrays are not identical [plus]");
- }
- }
-
- private static int checkSumXor(Object object) {
- if (object instanceof int[]) {
- return checkSumXor((int[]) object);
- } else if (object instanceof long[]) {
- return checkSumXor((long[]) object);
- } else if (object instanceof short[]) {
- return checkSumXor((short[]) object);
- } else if (object instanceof byte[]) {
- return checkSumXor((byte[]) object);
- } else if (object instanceof char[]) {
- return checkSumXor((char[]) object);
- } else if (object instanceof float[]) {
- return checkSumXor((float[]) object);
- } else if (object instanceof double[]) {
- return checkSumXor((double[]) object);
- } else if (object instanceof Integer[]) {
- return checkSumXor((Integer[]) object);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- return -1;
- }
- }
-
- private static int checkSumXor(Integer[] a) {
- int checkSum = 0;
-
- for (Integer e : a) {
- checkSum ^= e.intValue();
- }
- return checkSum;
- }
-
- private static int checkSumXor(int[] a) {
- int checkSum = 0;
-
- for (int e : a) {
- checkSum ^= e;
- }
- return checkSum;
- }
-
- private static int checkSumXor(long[] a) {
- long checkSum = 0;
-
- for (long e : a) {
- checkSum ^= e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumXor(short[] a) {
- short checkSum = 0;
-
- for (short e : a) {
- checkSum ^= e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumXor(byte[] a) {
- byte checkSum = 0;
-
- for (byte e : a) {
- checkSum ^= e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumXor(char[] a) {
- char checkSum = 0;
-
- for (char e : a) {
- checkSum ^= e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumXor(float[] a) {
- int checkSum = 0;
-
- for (float e : a) {
- checkSum ^= (int) e;
- }
- return checkSum;
- }
-
- private static int checkSumXor(double[] a) {
- int checkSum = 0;
-
- for (double e : a) {
- checkSum ^= (int) e;
- }
- return checkSum;
- }
-
- private static int checkSumPlus(Object object) {
- if (object instanceof int[]) {
- return checkSumPlus((int[]) object);
- } else if (object instanceof long[]) {
- return checkSumPlus((long[]) object);
- } else if (object instanceof short[]) {
- return checkSumPlus((short[]) object);
- } else if (object instanceof byte[]) {
- return checkSumPlus((byte[]) object);
- } else if (object instanceof char[]) {
- return checkSumPlus((char[]) object);
- } else if (object instanceof float[]) {
- return checkSumPlus((float[]) object);
- } else if (object instanceof double[]) {
- return checkSumPlus((double[]) object);
- } else if (object instanceof Integer[]) {
- return checkSumPlus((Integer[]) object);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- return -1;
- }
- }
-
- private static int checkSumPlus(int[] a) {
- int checkSum = 0;
-
- for (int e : a) {
- checkSum += e;
- }
- return checkSum;
- }
-
- private static int checkSumPlus(long[] a) {
- long checkSum = 0;
-
- for (long e : a) {
- checkSum += e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumPlus(short[] a) {
- short checkSum = 0;
-
- for (short e : a) {
- checkSum += e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumPlus(byte[] a) {
- byte checkSum = 0;
-
- for (byte e : a) {
- checkSum += e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumPlus(char[] a) {
- char checkSum = 0;
-
- for (char e : a) {
- checkSum += e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumPlus(float[] a) {
- int checkSum = 0;
-
- for (float e : a) {
- checkSum += (int) e;
- }
- return checkSum;
- }
-
- private static int checkSumPlus(double[] a) {
- int checkSum = 0;
-
- for (double e : a) {
- checkSum += (int) e;
- }
- return checkSum;
- }
-
- private static int checkSumPlus(Integer[] a) {
- int checkSum = 0;
-
- for (Integer e : a) {
- checkSum += e.intValue();
- }
- return checkSum;
- }
-
- private static void sortByInsertionSort(Object object) {
- if (object instanceof int[]) {
- sortByInsertionSort((int[]) object);
- } else if (object instanceof long[]) {
- sortByInsertionSort((long[]) object);
- } else if (object instanceof short[]) {
- sortByInsertionSort((short[]) object);
- } else if (object instanceof byte[]) {
- sortByInsertionSort((byte[]) object);
- } else if (object instanceof char[]) {
- sortByInsertionSort((char[]) object);
- } else if (object instanceof float[]) {
- sortByInsertionSort((float[]) object);
- } else if (object instanceof double[]) {
- sortByInsertionSort((double[]) object);
- } else if (object instanceof Integer[]) {
- sortByInsertionSort((Integer[]) object);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- }
- }
-
- private static void sortByInsertionSort(int[] a) {
- for (int j, i = 1; i < a.length; i++) {
- int ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
-
- private static void sortByInsertionSort(long[] a) {
- for (int j, i = 1; i < a.length; i++) {
- long ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
-
- private static void sortByInsertionSort(short[] a) {
- for (int j, i = 1; i < a.length; i++) {
- short ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
-
- private static void sortByInsertionSort(byte[] a) {
- for (int j, i = 1; i < a.length; i++) {
- byte ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
-
- private static void sortByInsertionSort(char[] a) {
- for (int j, i = 1; i < a.length; i++) {
- char ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
-
- private static void sortByInsertionSort(float[] a) {
- for (int j, i = 1; i < a.length; i++) {
- float ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
-
- private static void sortByInsertionSort(double[] a) {
- for (int j, i = 1; i < a.length; i++) {
- double ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
-
- private static void sortByInsertionSort(Integer[] a) {
- for (int j, i = 1; i < a.length; i++) {
- Integer ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
-
- private static void sort(Object object) {
- if (object instanceof int[]) {
- Arrays.parallelSort((int[]) object);
- } else if (object instanceof long[]) {
- Arrays.parallelSort((long[]) object);
- } else if (object instanceof short[]) {
- Arrays.parallelSort((short[]) object);
- } else if (object instanceof byte[]) {
- Arrays.parallelSort((byte[]) object);
- } else if (object instanceof char[]) {
- Arrays.parallelSort((char[]) object);
- } else if (object instanceof float[]) {
- Arrays.parallelSort((float[]) object);
- } else if (object instanceof double[]) {
- Arrays.parallelSort((double[]) object);
- } else if (object instanceof Integer[]) {
- Arrays.parallelSort((Integer[]) object);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- }
- }
-
- private static void sortSubArray(Object object, int fromIndex, int toIndex) {
- if (object instanceof int[]) {
- Arrays.parallelSort((int[]) object, fromIndex, toIndex);
- } else if (object instanceof long[]) {
- Arrays.parallelSort((long[]) object, fromIndex, toIndex);
- } else if (object instanceof short[]) {
- Arrays.parallelSort((short[]) object, fromIndex, toIndex);
- } else if (object instanceof byte[]) {
- Arrays.parallelSort((byte[]) object, fromIndex, toIndex);
- } else if (object instanceof char[]) {
- Arrays.parallelSort((char[]) object, fromIndex, toIndex);
- } else if (object instanceof float[]) {
- Arrays.parallelSort((float[]) object, fromIndex, toIndex);
- } else if (object instanceof double[]) {
- Arrays.parallelSort((double[]) object, fromIndex, toIndex);
- } else if (object instanceof Integer[]) {
- Arrays.parallelSort((Integer[]) object, fromIndex, toIndex);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- }
- }
-
- private static void checkSubArray(Object object, int fromIndex, int toIndex, int m) {
- if (object instanceof int[]) {
- checkSubArray((int[]) object, fromIndex, toIndex, m);
- } else if (object instanceof long[]) {
- checkSubArray((long[]) object, fromIndex, toIndex, m);
- } else if (object instanceof short[]) {
- checkSubArray((short[]) object, fromIndex, toIndex, m);
- } else if (object instanceof byte[]) {
- checkSubArray((byte[]) object, fromIndex, toIndex, m);
- } else if (object instanceof char[]) {
- checkSubArray((char[]) object, fromIndex, toIndex, m);
- } else if (object instanceof float[]) {
- checkSubArray((float[]) object, fromIndex, toIndex, m);
- } else if (object instanceof double[]) {
- checkSubArray((double[]) object, fromIndex, toIndex, m);
- } else if (object instanceof Integer[]) {
- checkSubArray((Integer[]) object, fromIndex, toIndex, m);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- }
- }
-
- private static void checkSubArray(Integer[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i].intValue() != 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i].intValue() > a[i + 1].intValue()) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i].intValue() != 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
-
- private static void checkSubArray(int[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
-
- private static void checkSubArray(byte[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (byte) 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (byte) 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
-
- private static void checkSubArray(long[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (long) 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (long) 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
-
- private static void checkSubArray(char[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (char) 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (char) 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
-
- private static void checkSubArray(short[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (short) 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (short) 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
-
- private static void checkSubArray(float[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (float) 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (float) 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
-
- private static void checkSubArray(double[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (double) 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (double) 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
-
- private static void checkRange(Object object, int m) {
- if (object instanceof int[]) {
- checkRange((int[]) object, m);
- } else if (object instanceof long[]) {
- checkRange((long[]) object, m);
- } else if (object instanceof short[]) {
- checkRange((short[]) object, m);
- } else if (object instanceof byte[]) {
- checkRange((byte[]) object, m);
- } else if (object instanceof char[]) {
- checkRange((char[]) object, m);
- } else if (object instanceof float[]) {
- checkRange((float[]) object, m);
- } else if (object instanceof double[]) {
- checkRange((double[]) object, m);
- } else if (object instanceof Integer[]) {
- checkRange((Integer[]) object, m);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- }
- }
-
- private static void checkRange(Integer[] a, int m) {
- try {
- Arrays.parallelSort(a, m + 1, m);
-
- failed("ParallelSort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.parallelSort(a, -m, a.length);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.parallelSort(a, 0, a.length + m);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void checkRange(int[] a, int m) {
- try {
- Arrays.parallelSort(a, m + 1, m);
-
- failed("ParallelSort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.parallelSort(a, -m, a.length);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.parallelSort(a, 0, a.length + m);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void checkRange(long[] a, int m) {
- try {
- Arrays.parallelSort(a, m + 1, m);
-
- failed("ParallelSort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.parallelSort(a, -m, a.length);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.parallelSort(a, 0, a.length + m);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void checkRange(byte[] a, int m) {
- try {
- Arrays.parallelSort(a, m + 1, m);
-
- failed("ParallelSort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.parallelSort(a, -m, a.length);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.parallelSort(a, 0, a.length + m);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void checkRange(short[] a, int m) {
- try {
- Arrays.parallelSort(a, m + 1, m);
-
- failed("ParallelSort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.parallelSort(a, -m, a.length);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.parallelSort(a, 0, a.length + m);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void checkRange(char[] a, int m) {
- try {
- Arrays.parallelSort(a, m + 1, m);
-
- failed("ParallelSort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.parallelSort(a, -m, a.length);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.parallelSort(a, 0, a.length + m);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void checkRange(float[] a, int m) {
- try {
- Arrays.parallelSort(a, m + 1, m);
-
- failed("ParallelSort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.parallelSort(a, -m, a.length);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.parallelSort(a, 0, a.length + m);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void checkRange(double[] a, int m) {
- try {
- Arrays.parallelSort(a, m + 1, m);
-
- failed("ParallelSort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.parallelSort(a, -m, a.length);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.parallelSort(a, 0, a.length + m);
-
- failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void outArray(Object[] a) {
- for (int i = 0; i < a.length; i++) {
- out.print(a[i] + " ");
- }
- out.println();
- }
-
- private static void outArray(int[] a) {
- for (int i = 0; i < a.length; i++) {
- out.print(a[i] + " ");
- }
- out.println();
- }
-
- private static void outArray(float[] a) {
- for (int i = 0; i < a.length; i++) {
- out.print(a[i] + " ");
- }
- out.println();
- }
-
- private static void outArray(double[] a) {
- for (int i = 0; i < a.length; i++) {
- out.print(a[i] + " ");
- }
- out.println();
- }
-
- private static class MyRandom extends Random {
- MyRandom(long seed) {
- super(seed);
- mySeed = seed;
- }
-
- long getSeed() {
- return mySeed;
- }
-
- private long mySeed;
- }
-
- private static String ourDescription;
-}
--- a/test/jdk/java/util/Arrays/Sorting.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/java/util/Arrays/Sorting.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2019, 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
@@ -23,277 +23,329 @@
/*
* @test
- * @bug 6880672 6896573 6899694 6976036 7013585 7018258
- * @summary Exercise Arrays.sort
+ * @compile/module=java.base java/util/SortingHelper.java
+ * @bug 6880672 6896573 6899694 6976036 7013585 7018258 8003981 8226297
* @build Sorting
* @run main Sorting -shortrun
+ * @summary Exercise Arrays.sort, Arrays.parallelSort
*
* @author Vladimir Yaroslavskiy
* @author Jon Bentley
* @author Josh Bloch
*/
-import java.util.Arrays;
+import java.io.PrintStream;
+import java.util.Comparator;
import java.util.Random;
-import java.io.PrintStream;
+import java.util.SortingHelper;
public class Sorting {
+
private static final PrintStream out = System.out;
private static final PrintStream err = System.err;
// Array lengths used in a long run (default)
private static final int[] LONG_RUN_LENGTHS = {
- 1, 2, 3, 5, 8, 13, 21, 34, 55, 100, 1000, 10000, 100000, 1000000 };
+ 1, 3, 8, 21, 55, 100, 1_000, 10_000, 100_000 };
// Array lengths used in a short run
private static final int[] SHORT_RUN_LENGTHS = {
- 1, 2, 3, 21, 55, 1000, 10000 };
+ 1, 8, 55, 100, 10_000 };
// Random initial values used in a long run (default)
- private static final long[] LONG_RUN_RANDOMS = { 666, 0xC0FFEE, 999 };
+ private static final TestRandom[] LONG_RUN_RANDOMS = {
+ TestRandom.BABA, TestRandom.DEDA, TestRandom.C0FFEE };
// Random initial values used in a short run
- private static final long[] SHORT_RUN_RANDOMS = { 666 };
+ private static final TestRandom[] SHORT_RUN_RANDOMS = {
+ TestRandom.C0FFEE };
+
+ // Constants used in subarray sorting
+ private static final int A380 = 0xA380;
+ private static final int B747 = 0xB747;
+
+ private final SortingHelper sortingHelper;
+ private final TestRandom[] randoms;
+ private final int[] lengths;
+ private Object[] gold;
+ private Object[] test;
public static void main(String[] args) {
+ long start = System.currentTimeMillis();
boolean shortRun = args.length > 0 && args[0].equals("-shortrun");
- long start = System.currentTimeMillis();
+
+ int[] lengths = shortRun ? SHORT_RUN_LENGTHS : LONG_RUN_LENGTHS;
+ TestRandom[] randoms = shortRun ? SHORT_RUN_RANDOMS : LONG_RUN_RANDOMS;
- if (shortRun) {
- testAndCheck(SHORT_RUN_LENGTHS, SHORT_RUN_RANDOMS);
- } else {
- testAndCheck(LONG_RUN_LENGTHS, LONG_RUN_RANDOMS);
- }
+ new Sorting(SortingHelper.DUAL_PIVOT_QUICKSORT, randoms, lengths).testCore();
+ new Sorting(SortingHelper.PARALLEL_SORT, randoms, lengths).testCore();
+ new Sorting(SortingHelper.HEAP_SORT, randoms, lengths).testBasic();
+ new Sorting(SortingHelper.ARRAYS_SORT, randoms, lengths).testAll();
+ new Sorting(SortingHelper.ARRAYS_PARALLEL_SORT, randoms, lengths).testAll();
+
long end = System.currentTimeMillis();
-
- out.format("PASSED in %d sec.\n", Math.round((end - start) / 1E3));
+ out.format("PASSED in %d sec.\n", (end - start) / 1000);
}
- private static void testAndCheck(int[] lengths, long[] randoms) {
- testEmptyAndNullIntArray();
- testEmptyAndNullLongArray();
- testEmptyAndNullShortArray();
- testEmptyAndNullCharArray();
- testEmptyAndNullByteArray();
- testEmptyAndNullFloatArray();
- testEmptyAndNullDoubleArray();
+ private Sorting(SortingHelper sortingHelper, TestRandom[] randoms, int[] lengths) {
+ this.sortingHelper = sortingHelper;
+ this.randoms = randoms;
+ this.lengths = lengths;
+ }
+
+ private void testBasic() {
+ testEmptyArray();
for (int length : lengths) {
- testMergeSort(length);
- testAndCheckRange(length);
- testAndCheckSubArray(length);
+ createData(length);
+ testBasic(length);
}
- for (long seed : randoms) {
- for (int length : lengths) {
- testAndCheckWithInsertionSort(length, new MyRandom(seed));
- testAndCheckWithCheckSum(length, new MyRandom(seed));
- testAndCheckWithScrambling(length, new MyRandom(seed));
- testAndCheckFloat(length, new MyRandom(seed));
- testAndCheckDouble(length, new MyRandom(seed));
- testStable(length, new MyRandom(seed));
- }
+ }
+
+ private void testBasic(int length) {
+ for (TestRandom random : randoms) {
+ testWithInsertionSort(length, random);
+ testWithCheckSum(length, random);
+ testWithScrambling(length, random);
}
}
- private static void testEmptyAndNullIntArray() {
- ourDescription = "Check empty and null array";
- Arrays.sort(new int[] {});
- Arrays.sort(new int[] {}, 0, 0);
+ private void testCore() {
+ for (int length : lengths) {
+ createData(length);
+ testCore(length);
+ }
+ }
+
+ private void testCore(int length) {
+ testBasic(length);
- try {
- Arrays.sort((int[]) null);
- } catch (NullPointerException expected) {
- try {
- Arrays.sort((int[]) null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- failed("Arrays.sort(int[],fromIndex,toIndex) shouldn't " +
- "catch null array");
+ for (TestRandom random : randoms) {
+ testMergingSort(length, random);
+ testSubArray(length, random);
+ testNegativeZero(length, random);
+ testFloatingPointSorting(length, random);
}
- failed("Arrays.sort(int[]) shouldn't catch null array");
+ }
+
+ private void testAll() {
+ for (int length : lengths) {
+ createData(length);
+ testAll(length);
+ }
+ }
+
+ private void testAll(int length) {
+ testCore(length);
+
+ for (TestRandom random : randoms) {
+ testRange(length, random);
+ testStability(length, random);
+ }
}
- private static void testEmptyAndNullLongArray() {
- ourDescription = "Check empty and null array";
- Arrays.sort(new long[] {});
- Arrays.sort(new long[] {}, 0, 0);
+ private void testEmptyArray() {
+ testEmptyAndNullIntArray();
+ testEmptyAndNullLongArray();
+ testEmptyAndNullByteArray();
+ testEmptyAndNullCharArray();
+ testEmptyAndNullShortArray();
+ testEmptyAndNullFloatArray();
+ testEmptyAndNullDoubleArray();
+ }
+
+ private void testStability(int length, TestRandom random) {
+ printTestName("Test stability", random, length);
+
+ Pair[] a = build(length, random);
+ sortingHelper.sort(a);
+ checkSorted(a);
+ checkStable(a);
+
+ a = build(length, random);
+ sortingHelper.sort(a, pairComparator);
+ checkSorted(a);
+ checkStable(a);
+
+ out.println();
+ }
+
+ private void testEmptyAndNullIntArray() {
+ sortingHelper.sort(new int[] {});
+ sortingHelper.sort(new int[] {}, 0, 0);
try {
- Arrays.sort((long[]) null);
+ sortingHelper.sort(null);
} catch (NullPointerException expected) {
try {
- Arrays.sort((long[]) null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- failed("Arrays.sort(long[],fromIndex,toIndex) shouldn't " +
- "catch null array");
- }
- failed("Arrays.sort(long[]) shouldn't catch null array");
- }
-
- private static void testEmptyAndNullShortArray() {
- ourDescription = "Check empty and null array";
- Arrays.sort(new short[] {});
- Arrays.sort(new short[] {}, 0, 0);
-
- try {
- Arrays.sort((short[]) null);
- } catch (NullPointerException expected) {
- try {
- Arrays.sort((short[]) null, 0, 0);
+ sortingHelper.sort(null, 0, 0);
} catch (NullPointerException expected2) {
return;
}
- failed("Arrays.sort(short[],fromIndex,toIndex) shouldn't " +
+ fail(sortingHelper + "(int[],fromIndex,toIndex) shouldn't " +
"catch null array");
}
- failed("Arrays.sort(short[]) shouldn't catch null array");
+ fail(sortingHelper + "(int[]) shouldn't catch null array");
}
- private static void testEmptyAndNullCharArray() {
- ourDescription = "Check empty and null array";
- Arrays.sort(new char[] {});
- Arrays.sort(new char[] {}, 0, 0);
+ private void testEmptyAndNullLongArray() {
+ sortingHelper.sort(new long[] {});
+ sortingHelper.sort(new long[] {}, 0, 0);
try {
- Arrays.sort((char[]) null);
+ sortingHelper.sort(null);
} catch (NullPointerException expected) {
try {
- Arrays.sort((char[]) null, 0, 0);
+ sortingHelper.sort(null, 0, 0);
} catch (NullPointerException expected2) {
return;
}
- failed("Arrays.sort(char[],fromIndex,toIndex) shouldn't " +
+ fail(sortingHelper + "(long[],fromIndex,toIndex) shouldn't " +
"catch null array");
}
- failed("Arrays.sort(char[]) shouldn't catch null array");
+ fail(sortingHelper + "(long[]) shouldn't catch null array");
}
- private static void testEmptyAndNullByteArray() {
- ourDescription = "Check empty and null array";
- Arrays.sort(new byte[] {});
- Arrays.sort(new byte[] {}, 0, 0);
+ private void testEmptyAndNullByteArray() {
+ sortingHelper.sort(new byte[] {});
+ sortingHelper.sort(new byte[] {}, 0, 0);
try {
- Arrays.sort((byte[]) null);
+ sortingHelper.sort(null);
} catch (NullPointerException expected) {
try {
- Arrays.sort((byte[]) null, 0, 0);
+ sortingHelper.sort(null, 0, 0);
} catch (NullPointerException expected2) {
return;
}
- failed("Arrays.sort(byte[],fromIndex,toIndex) shouldn't " +
+ fail(sortingHelper + "(byte[],fromIndex,toIndex) shouldn't " +
"catch null array");
}
- failed("Arrays.sort(byte[]) shouldn't catch null array");
+ fail(sortingHelper + "(byte[]) shouldn't catch null array");
}
- private static void testEmptyAndNullFloatArray() {
- ourDescription = "Check empty and null array";
- Arrays.sort(new float[] {});
- Arrays.sort(new float[] {}, 0, 0);
+ private void testEmptyAndNullCharArray() {
+ sortingHelper.sort(new char[] {});
+ sortingHelper.sort(new char[] {}, 0, 0);
try {
- Arrays.sort((float[]) null);
+ sortingHelper.sort(null);
} catch (NullPointerException expected) {
try {
- Arrays.sort((float[]) null, 0, 0);
+ sortingHelper.sort(null, 0, 0);
} catch (NullPointerException expected2) {
return;
}
- failed("Arrays.sort(float[],fromIndex,toIndex) shouldn't " +
+ fail(sortingHelper + "(char[],fromIndex,toIndex) shouldn't " +
"catch null array");
}
- failed("Arrays.sort(float[]) shouldn't catch null array");
+ fail(sortingHelper + "(char[]) shouldn't catch null array");
}
- private static void testEmptyAndNullDoubleArray() {
- ourDescription = "Check empty and null array";
- Arrays.sort(new double[] {});
- Arrays.sort(new double[] {}, 0, 0);
+ private void testEmptyAndNullShortArray() {
+ sortingHelper.sort(new short[] {});
+ sortingHelper.sort(new short[] {}, 0, 0);
try {
- Arrays.sort((double[]) null);
+ sortingHelper.sort(null);
} catch (NullPointerException expected) {
try {
- Arrays.sort((double[]) null, 0, 0);
+ sortingHelper.sort(null, 0, 0);
+ } catch (NullPointerException expected2) {
+ return;
+ }
+ fail(sortingHelper + "(short[],fromIndex,toIndex) shouldn't " +
+ "catch null array");
+ }
+ fail(sortingHelper + "(short[]) shouldn't catch null array");
+ }
+
+ private void testEmptyAndNullFloatArray() {
+ sortingHelper.sort(new float[] {});
+ sortingHelper.sort(new float[] {}, 0, 0);
+
+ try {
+ sortingHelper.sort(null);
+ } catch (NullPointerException expected) {
+ try {
+ sortingHelper.sort(null, 0, 0);
} catch (NullPointerException expected2) {
return;
}
- failed("Arrays.sort(double[],fromIndex,toIndex) shouldn't " +
+ fail(sortingHelper + "(float[],fromIndex,toIndex) shouldn't " +
"catch null array");
}
- failed("Arrays.sort(double[]) shouldn't catch null array");
+ fail(sortingHelper + "(float[]) shouldn't catch null array");
}
- private static void testAndCheckSubArray(int length) {
- ourDescription = "Check sorting of subarray";
- int[] golden = new int[length];
- boolean newLine = false;
+ private void testEmptyAndNullDoubleArray() {
+ sortingHelper.sort(new double[] {});
+ sortingHelper.sort(new double[] {}, 0, 0);
- for (int m = 1; m < length / 2; m *= 2) {
- newLine = true;
+ try {
+ sortingHelper.sort(null);
+ } catch (NullPointerException expected) {
+ try {
+ sortingHelper.sort(null, 0, 0);
+ } catch (NullPointerException expected2) {
+ return;
+ }
+ fail(sortingHelper + "(double[],fromIndex,toIndex) shouldn't " +
+ "catch null array");
+ }
+ fail(sortingHelper + "(double[]) shouldn't catch null array");
+ }
+
+ private void testSubArray(int length, TestRandom random) {
+ if (length < 4) {
+ return;
+ }
+ for (int m = 1; m < length / 2; m <<= 1) {
int fromIndex = m;
int toIndex = length - m;
- prepareSubArray(golden, fromIndex, toIndex, m);
- int[] test = golden.clone();
+ prepareSubArray((int[]) gold[0], fromIndex, toIndex);
+ convertData(length);
- for (TypeConverter converter : TypeConverter.values()) {
- out.println("Test 'subarray': " + converter +
- " length = " + length + ", m = " + m);
- Object convertedGolden = converter.convert(golden);
- Object convertedTest = converter.convert(test);
- sortSubArray(convertedTest, fromIndex, toIndex);
- checkSubArray(convertedTest, fromIndex, toIndex, m);
- }
- }
- if (newLine) {
- out.println();
- }
- }
-
- private static void testAndCheckRange(int length) {
- ourDescription = "Check range check";
- int[] golden = new int[length];
-
- for (int m = 1; m < 2 * length; m *= 2) {
- for (int i = 1; i <= length; i++) {
- golden[i - 1] = i % m + m % i;
- }
- for (TypeConverter converter : TypeConverter.values()) {
- out.println("Test 'range': " + converter +
- ", length = " + length + ", m = " + m);
- Object convertedGolden = converter.convert(golden);
- checkRange(convertedGolden, m);
+ for (int i = 0; i < test.length; i++) {
+ printTestName("Test subarray", random, length,
+ ", m = " + m + ", " + getType(i));
+ sortingHelper.sort(test[i], fromIndex, toIndex);
+ checkSubArray(test[i], fromIndex, toIndex);
}
}
out.println();
}
- private static void testStable(int length, MyRandom random) {
- ourDescription = "Check if sorting is stable";
- Pair[] a = build(length, random);
+ private void testRange(int length, TestRandom random) {
+ if (length < 2) {
+ return;
+ }
+ for (int m = 1; m < length; m <<= 1) {
+ for (int i = 1; i <= length; i++) {
+ ((int[]) gold[0]) [i - 1] = i % m + m % i;
+ }
+ convertData(length);
- out.println("Test 'stable': " + "random = " + random.getSeed() +
- ", length = " + length);
- Arrays.sort(a);
- checkSorted(a);
- checkStable(a);
+ for (int i = 0; i < test.length; i++) {
+ printTestName("Test range check", random, length,
+ ", m = " + m + ", " + getType(i));
+ checkRange(test[i], m);
+ }
+ }
out.println();
}
- private static void checkSorted(Pair[] a) {
+ private void checkSorted(Pair[] a) {
for (int i = 0; i < a.length - 1; i++) {
if (a[i].getKey() > a[i + 1].getKey()) {
- failedSort(i, "" + a[i].getKey(), "" + a[i + 1].getKey());
+ fail("Array is not sorted at " + i + "-th position: " +
+ a[i].getKey() + " and " + a[i + 1].getKey());
}
}
}
- private static void checkStable(Pair[] a) {
+ private void checkStable(Pair[] a) {
for (int i = 0; i < a.length / 4; ) {
int key1 = a[i].getKey();
int value1 = a[i++].getValue();
@@ -305,18 +357,18 @@
int value4 = a[i++].getValue();
if (!(key1 == key2 && key2 == key3 && key3 == key4)) {
- failed("On position " + i + " keys are different " +
- key1 + ", " + key2 + ", " + key3 + ", " + key4);
+ fail("Keys are different " + key1 + ", " + key2 + ", " +
+ key3 + ", " + key4 + " at position " + i);
}
if (!(value1 < value2 && value2 < value3 && value3 < value4)) {
- failed("Sorting is not stable at position " + i +
- ". Second values have been changed: " + value1 + ", " +
+ fail("Sorting is not stable at position " + i +
+ ". Second values have been changed: " + value1 + ", " +
value2 + ", " + value3 + ", " + value4);
}
}
}
- private static Pair[] build(int length, Random random) {
+ private Pair[] build(int length, Random random) {
Pair[] a = new Pair[length * 4];
for (int i = 0; i < a.length; ) {
@@ -329,222 +381,151 @@
return a;
}
- private static final class Pair implements Comparable<Pair> {
- Pair(int key, int value) {
- myKey = key;
- myValue = value;
- }
-
- int getKey() {
- return myKey;
- }
-
- int getValue() {
- return myValue;
- }
-
- public int compareTo(Pair pair) {
- if (myKey < pair.myKey) {
- return -1;
- }
- if (myKey > pair.myKey) {
- return 1;
- }
- return 0;
- }
-
- @Override
- public String toString() {
- return "(" + myKey + ", " + myValue + ")";
- }
-
- private int myKey;
- private int myValue;
- }
-
-
- private static void testAndCheckWithInsertionSort(int length, MyRandom random) {
+ private void testWithInsertionSort(int length, TestRandom random) {
if (length > 1000) {
return;
}
- ourDescription = "Check sorting with insertion sort";
- int[] golden = new int[length];
-
- for (int m = 1; m < 2 * length; m *= 2) {
+ for (int m = 1; m <= length; m <<= 1) {
for (UnsortedBuilder builder : UnsortedBuilder.values()) {
- builder.build(golden, m, random);
- int[] test = golden.clone();
+ builder.build((int[]) gold[0], m, random);
+ convertData(length);
- for (TypeConverter converter : TypeConverter.values()) {
- out.println("Test 'insertion sort': " + converter +
- " " + builder + "random = " + random.getSeed() +
- ", length = " + length + ", m = " + m);
- Object convertedGolden = converter.convert(golden);
- Object convertedTest1 = converter.convert(test);
- Object convertedTest2 = converter.convert(test);
- sort(convertedTest1);
- sortByInsertionSort(convertedTest2);
- compare(convertedTest1, convertedTest2);
+ for (int i = 0; i < test.length; i++) {
+ printTestName("Test with insertion sort", random, length,
+ ", m = " + m + ", " + getType(i) + " " + builder);
+ sortingHelper.sort(test[i]);
+ sortByInsertionSort(gold[i]);
+ compare(test[i], gold[i]);
}
}
}
out.println();
}
- private static void testMergeSort(int length) {
- if (length < 1000) {
+ private void testMergingSort(int length, TestRandom random) {
+ if (length < (4 << 10)) { // DualPivotQuicksort.MIN_TRY_MERGE_SIZE
return;
}
- ourDescription = "Check merge sorting";
- int[] golden = new int[length];
- int period = 67; // java.util.DualPivotQuicksort.MAX_RUN_COUNT
+ final int PERIOD = 50;
+
+ for (int m = PERIOD - 2; m <= PERIOD + 2; m++) {
+ for (MergingBuilder builder : MergingBuilder.values()) {
+ builder.build((int[]) gold[0], m);
+ convertData(length);
- for (int m = period - 2; m <= period + 2; m++) {
- for (MergeBuilder builder : MergeBuilder.values()) {
- builder.build(golden, m);
- int[] test = golden.clone();
+ for (int i = 0; i < test.length; i++) {
+ printTestName("Test merging sort", random, length,
+ ", m = " + m + ", " + getType(i) + " " + builder);
+ sortingHelper.sort(test[i]);
+ checkSorted(test[i]);
+ }
+ }
+ }
+ out.println();
+ }
- for (TypeConverter converter : TypeConverter.values()) {
- out.println("Test 'merge sort': " + converter + " " +
- builder + "length = " + length + ", m = " + m);
- Object convertedGolden = converter.convert(golden);
- sort(convertedGolden);
- checkSorted(convertedGolden);
+ private void testWithCheckSum(int length, TestRandom random) {
+ for (int m = 1; m <= length; m <<= 1) {
+ for (UnsortedBuilder builder : UnsortedBuilder.values()) {
+ builder.build((int[]) gold[0], m, random);
+ convertData(length);
+
+ for (int i = 0; i < test.length; i++) {
+ printTestName("Test with check sum", random, length,
+ ", m = " + m + ", " + getType(i) + " " + builder);
+ sortingHelper.sort(test[i]);
+ checkWithCheckSum(test[i], gold[i]);
}
}
}
out.println();
}
- private static void testAndCheckWithCheckSum(int length, MyRandom random) {
- ourDescription = "Check sorting with check sum";
- int[] golden = new int[length];
-
- for (int m = 1; m < 2 * length; m *= 2) {
- for (UnsortedBuilder builder : UnsortedBuilder.values()) {
- builder.build(golden, m, random);
- int[] test = golden.clone();
+ private void testWithScrambling(int length, TestRandom random) {
+ for (int m = 1; m <= length; m <<= 1) {
+ for (SortedBuilder builder : SortedBuilder.values()) {
+ builder.build((int[]) gold[0], m);
+ convertData(length);
- for (TypeConverter converter : TypeConverter.values()) {
- out.println("Test 'check sum': " + converter +
- " " + builder + "random = " + random.getSeed() +
- ", length = " + length + ", m = " + m);
- Object convertedGolden = converter.convert(golden);
- Object convertedTest = converter.convert(test);
- sort(convertedTest);
- checkWithCheckSum(convertedTest, convertedGolden);
- }
- }
- }
- out.println();
- }
-
- private static void testAndCheckWithScrambling(int length, MyRandom random) {
- ourDescription = "Check sorting with scrambling";
- int[] golden = new int[length];
-
- for (int m = 1; m <= 7; m++) {
- if (m > length) {
- break;
- }
- for (SortedBuilder builder : SortedBuilder.values()) {
- builder.build(golden, m);
- int[] test = golden.clone();
- scramble(test, random);
-
- for (TypeConverter converter : TypeConverter.values()) {
- out.println("Test 'scrambling': " + converter +
- " " + builder + "random = " + random.getSeed() +
- ", length = " + length + ", m = " + m);
- Object convertedGolden = converter.convert(golden);
- Object convertedTest = converter.convert(test);
- sort(convertedTest);
- compare(convertedTest, convertedGolden);
+ for (int i = 0; i < test.length; i++) {
+ printTestName("Test with scrambling", random, length,
+ ", m = " + m + ", " + getType(i) + " " + builder);
+ scramble(test[i], random);
+ sortingHelper.sort(test[i]);
+ compare(test[i], gold[i]);
}
}
}
out.println();
}
- private static void testAndCheckFloat(int length, MyRandom random) {
- ourDescription = "Check float sorting";
- float[] golden = new float[length];
- final int MAX = 10;
- boolean newLine = false;
+ private void testNegativeZero(int length, TestRandom random) {
+ for (int i = 5; i < test.length; i++) {
+ printTestName("Test negative zero -0.0", random, length, " " + getType(i));
+
+ NegativeZeroBuilder builder = NegativeZeroBuilder.values() [i - 5];
+ builder.build(test[i], random);
+
+ sortingHelper.sort(test[i]);
+ checkNegativeZero(test[i]);
+ }
+ out.println();
+ }
- for (int a = 0; a <= MAX; a++) {
- for (int g = 0; g <= MAX; g++) {
- for (int z = 0; z <= MAX; z++) {
- for (int n = 0; n <= MAX; n++) {
- for (int p = 0; p <= MAX; p++) {
- if (a + g + z + n + p > length) {
+ private void testFloatingPointSorting(int length, TestRandom random) {
+ if (length < 2) {
+ return;
+ }
+ final int MAX = 13;
+
+ for (int a = 0; a < MAX; a++) {
+ for (int g = 0; g < MAX; g++) {
+ for (int z = 0; z < MAX; z++) {
+ for (int n = 0; n < MAX; n++) {
+ for (int p = 0; p < MAX; p++) {
+ if (a + g + z + n + p != length) {
continue;
}
- if (a + g + z + n + p < length) {
- continue;
+ for (int i = 5; i < test.length; i++) {
+ printTestName("Test float-pointing sorting", random, length,
+ ", a = " + a + ", g = " + g + ", z = " + z +
+ ", n = " + n + ", p = " + p + ", " + getType(i));
+ FloatingPointBuilder builder = FloatingPointBuilder.values()[i - 5];
+ builder.build(gold[i], a, g, z, n, p, random);
+ copy(test[i], gold[i]);
+ scramble(test[i], random);
+ sortingHelper.sort(test[i]);
+ compare(test[i], gold[i], a, n, g);
}
- for (FloatBuilder builder : FloatBuilder.values()) {
- out.println("Test 'float': random = " + random.getSeed() +
- ", length = " + length + ", a = " + a + ", g = " +
- g + ", z = " + z + ", n = " + n + ", p = " + p);
- builder.build(golden, a, g, z, n, p, random);
- float[] test = golden.clone();
- scramble(test, random);
- sort(test);
- compare(test, golden, a, n, g);
- }
- newLine = true;
}
}
}
}
}
- if (newLine) {
- out.println();
+
+ for (int m = 13; m > 4; m--) {
+ int t = length / m;
+ int g = t, z = t, n = t, p = t;
+ int a = length - g - z - n - p;
+
+ for (int i = 5; i < test.length; i++) {
+ printTestName("Test float-pointing sorting", random, length,
+ ", a = " + a + ", g = " + g + ", z = " + z +
+ ", n = " + n + ", p = " + p + ", " + getType(i));
+ FloatingPointBuilder builder = FloatingPointBuilder.values() [i - 5];
+ builder.build(gold[i], a, g, z, n, p, random);
+ copy(test[i], gold[i]);
+ scramble(test[i], random);
+ sortingHelper.sort(test[i]);
+ compare(test[i], gold[i], a, n, g);
+ }
}
+ out.println();
}
- private static void testAndCheckDouble(int length, MyRandom random) {
- ourDescription = "Check double sorting";
- double[] golden = new double[length];
- final int MAX = 10;
- boolean newLine = false;
-
- for (int a = 0; a <= MAX; a++) {
- for (int g = 0; g <= MAX; g++) {
- for (int z = 0; z <= MAX; z++) {
- for (int n = 0; n <= MAX; n++) {
- for (int p = 0; p <= MAX; p++) {
- if (a + g + z + n + p > length) {
- continue;
- }
- if (a + g + z + n + p < length) {
- continue;
- }
- for (DoubleBuilder builder : DoubleBuilder.values()) {
- out.println("Test 'double': random = " + random.getSeed() +
- ", length = " + length + ", a = " + a + ", g = " +
- g + ", z = " + z + ", n = " + n + ", p = " + p);
- builder.build(golden, a, g, z, n, p, random);
- double[] test = golden.clone();
- scramble(test, random);
- sort(test);
- compare(test, golden, a, n, g);
- }
- newLine = true;
- }
- }
- }
- }
- }
- if (newLine) {
- out.println();
- }
- }
-
- private static void prepareSubArray(int[] a, int fromIndex, int toIndex, int m) {
+ private void prepareSubArray(int[] a, int fromIndex, int toIndex) {
for (int i = 0; i < fromIndex; i++) {
- a[i] = 0xDEDA;
+ a[i] = A380;
}
int middle = (fromIndex + toIndex) >>> 1;
int k = 0;
@@ -552,338 +533,1112 @@
for (int i = fromIndex; i < middle; i++) {
a[i] = k++;
}
+
for (int i = middle; i < toIndex; i++) {
a[i] = k--;
}
+
for (int i = toIndex; i < a.length; i++) {
- a[i] = 0xBABA;
+ a[i] = B747;
}
}
- private static void scramble(int[] a, Random random) {
+ private void scramble(Object a, Random random) {
+ if (a instanceof int[]) {
+ scramble((int[]) a, random);
+ } else if (a instanceof long[]) {
+ scramble((long[]) a, random);
+ } else if (a instanceof byte[]) {
+ scramble((byte[]) a, random);
+ } else if (a instanceof char[]) {
+ scramble((char[]) a, random);
+ } else if (a instanceof short[]) {
+ scramble((short[]) a, random);
+ } else if (a instanceof float[]) {
+ scramble((float[]) a, random);
+ } else if (a instanceof double[]) {
+ scramble((double[]) a, random);
+ } else {
+ fail("Unknown type of array: " + a.getClass().getName());
+ }
+ }
+
+ private void scramble(int[] a, Random random) {
for (int i = 0; i < a.length * 7; i++) {
swap(a, random.nextInt(a.length), random.nextInt(a.length));
}
}
- private static void scramble(float[] a, Random random) {
+ private void scramble(long[] a, Random random) {
+ for (int i = 0; i < a.length * 7; i++) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
+ }
+
+ private void scramble(byte[] a, Random random) {
for (int i = 0; i < a.length * 7; i++) {
swap(a, random.nextInt(a.length), random.nextInt(a.length));
}
}
- private static void scramble(double[] a, Random random) {
+ private void scramble(char[] a, Random random) {
+ for (int i = 0; i < a.length * 7; i++) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
+ }
+
+ private void scramble(short[] a, Random random) {
+ for (int i = 0; i < a.length * 7; i++) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
+ }
+
+ private void scramble(float[] a, Random random) {
+ for (int i = 0; i < a.length * 7; i++) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
+ }
+
+ private void scramble(double[] a, Random random) {
for (int i = 0; i < a.length * 7; i++) {
swap(a, random.nextInt(a.length), random.nextInt(a.length));
}
}
- private static void swap(int[] a, int i, int j) {
- int t = a[i];
- a[i] = a[j];
- a[j] = t;
+ private void swap(int[] a, int i, int j) {
+ int t = a[i]; a[i] = a[j]; a[j] = t;
+ }
+
+ private void swap(long[] a, int i, int j) {
+ long t = a[i]; a[i] = a[j]; a[j] = t;
}
- private static void swap(float[] a, int i, int j) {
- float t = a[i];
- a[i] = a[j];
- a[j] = t;
+ private void swap(byte[] a, int i, int j) {
+ byte t = a[i]; a[i] = a[j]; a[j] = t;
}
- private static void swap(double[] a, int i, int j) {
- double t = a[i];
- a[i] = a[j];
- a[j] = t;
+ private void swap(char[] a, int i, int j) {
+ char t = a[i]; a[i] = a[j]; a[j] = t;
+ }
+
+ private void swap(short[] a, int i, int j) {
+ short t = a[i]; a[i] = a[j]; a[j] = t;
}
- private static enum TypeConverter {
- INT {
- Object convert(int[] a) {
- return a.clone();
- }
- },
- LONG {
- Object convert(int[] a) {
- long[] b = new long[a.length];
-
- for (int i = 0; i < a.length; i++) {
- b[i] = (long) a[i];
- }
- return b;
- }
- },
- BYTE {
- Object convert(int[] a) {
- byte[] b = new byte[a.length];
+ private void swap(float[] a, int i, int j) {
+ float t = a[i]; a[i] = a[j]; a[j] = t;
+ }
- for (int i = 0; i < a.length; i++) {
- b[i] = (byte) a[i];
- }
- return b;
- }
- },
- SHORT {
- Object convert(int[] a) {
- short[] b = new short[a.length];
+ private void swap(double[] a, int i, int j) {
+ double t = a[i]; a[i] = a[j]; a[j] = t;
+ }
- for (int i = 0; i < a.length; i++) {
- b[i] = (short) a[i];
- }
- return b;
- }
- },
- CHAR {
- Object convert(int[] a) {
- char[] b = new char[a.length];
+ private void checkWithCheckSum(Object test, Object gold) {
+ checkSorted(test);
+ checkCheckSum(test, gold);
+ }
- for (int i = 0; i < a.length; i++) {
- b[i] = (char) a[i];
- }
- return b;
- }
- },
- FLOAT {
- Object convert(int[] a) {
- float[] b = new float[a.length];
-
- for (int i = 0; i < a.length; i++) {
- b[i] = (float) a[i];
- }
- return b;
- }
- },
- DOUBLE {
- Object convert(int[] a) {
- double[] b = new double[a.length];
+ private void fail(String message) {
+ err.format("\n*** TEST FAILED ***\n\n%s\n\n", message);
+ throw new RuntimeException("Test failed");
+ }
- for (int i = 0; i < a.length; i++) {
- b[i] = (double) a[i];
- }
- return b;
- }
- },
- INTEGER {
- Object convert(int[] a) {
- Integer[] b = new Integer[a.length];
-
- for (int i = 0; i < a.length; i++) {
- b[i] = new Integer(a[i]);
- }
- return b;
- }
- };
-
- abstract Object convert(int[] a);
-
- @Override public String toString() {
- String name = name();
-
- for (int i = name.length(); i < 9; i++) {
- name += " ";
- }
- return name;
+ private void checkNegativeZero(Object a) {
+ if (a instanceof float[]) {
+ checkNegativeZero((float[]) a);
+ } else if (a instanceof double[]) {
+ checkNegativeZero((double[]) a);
+ } else {
+ fail("Unknown type of array: " + a.getClass().getName());
}
}
- private static enum FloatBuilder {
- SIMPLE {
- void build(float[] x, int a, int g, int z, int n, int p, Random random) {
- int fromIndex = 0;
- float negativeValue = -random.nextFloat();
- float positiveValue = random.nextFloat();
-
- writeValue(x, negativeValue, fromIndex, n);
- fromIndex += n;
-
- writeValue(x, -0.0f, fromIndex, g);
- fromIndex += g;
-
- writeValue(x, 0.0f, fromIndex, z);
- fromIndex += z;
-
- writeValue(x, positiveValue, fromIndex, p);
- fromIndex += p;
-
- writeValue(x, Float.NaN, fromIndex, a);
+ private void checkNegativeZero(float[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (Float.floatToRawIntBits(a[i]) == 0 && Float.floatToRawIntBits(a[i + 1]) < 0) {
+ fail(a[i] + " before " + a[i + 1] + " at position " + i);
}
- };
-
- abstract void build(float[] x, int a, int g, int z, int n, int p, Random random);
- }
-
- private static enum DoubleBuilder {
- SIMPLE {
- void build(double[] x, int a, int g, int z, int n, int p, Random random) {
- int fromIndex = 0;
- double negativeValue = -random.nextFloat();
- double positiveValue = random.nextFloat();
-
- writeValue(x, negativeValue, fromIndex, n);
- fromIndex += n;
-
- writeValue(x, -0.0d, fromIndex, g);
- fromIndex += g;
-
- writeValue(x, 0.0d, fromIndex, z);
- fromIndex += z;
-
- writeValue(x, positiveValue, fromIndex, p);
- fromIndex += p;
-
- writeValue(x, Double.NaN, fromIndex, a);
- }
- };
-
- abstract void build(double[] x, int a, int g, int z, int n, int p, Random random);
- }
-
- private static void writeValue(float[] a, float value, int fromIndex, int count) {
- for (int i = fromIndex; i < fromIndex + count; i++) {
- a[i] = value;
}
}
- private static void compare(float[] a, float[] b, int numNaN, int numNeg, int numNegZero) {
+ private void checkNegativeZero(double[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (Double.doubleToRawLongBits(a[i]) == 0 && Double.doubleToRawLongBits(a[i + 1]) < 0) {
+ fail(a[i] + " before " + a[i + 1] + " at position " + i);
+ }
+ }
+ }
+
+ private void compare(Object a, Object b, int numNaN, int numNeg, int numNegZero) {
+ if (a instanceof float[]) {
+ compare((float[]) a, (float[]) b, numNaN, numNeg, numNegZero);
+ } else if (a instanceof double[]) {
+ compare((double[]) a, (double[]) b, numNaN, numNeg, numNegZero);
+ } else {
+ fail("Unknown type of array: " + a.getClass().getName());
+ }
+ }
+
+ private void compare(float[] a, float[] b, int numNaN, int numNeg, int numNegZero) {
for (int i = a.length - numNaN; i < a.length; i++) {
if (a[i] == a[i]) {
- failed("On position " + i + " must be NaN instead of " + a[i]);
+ fail("There must be NaN instead of " + a[i] + " at position " + i);
}
}
final int NEGATIVE_ZERO = Float.floatToIntBits(-0.0f);
for (int i = numNeg; i < numNeg + numNegZero; i++) {
if (NEGATIVE_ZERO != Float.floatToIntBits(a[i])) {
- failed("On position " + i + " must be -0.0 instead of " + a[i]);
+ fail("There must be -0.0 instead of " + a[i] + " at position " + i);
}
}
+
for (int i = 0; i < a.length - numNaN; i++) {
if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
+ fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
}
}
}
- private static void writeValue(double[] a, double value, int fromIndex, int count) {
- for (int i = fromIndex; i < fromIndex + count; i++) {
- a[i] = value;
- }
- }
-
- private static void compare(double[] a, double[] b, int numNaN, int numNeg, int numNegZero) {
+ private void compare(double[] a, double[] b, int numNaN, int numNeg, int numNegZero) {
for (int i = a.length - numNaN; i < a.length; i++) {
if (a[i] == a[i]) {
- failed("On position " + i + " must be NaN instead of " + a[i]);
+ fail("There must be NaN instead of " + a[i] + " at position " + i);
}
}
final long NEGATIVE_ZERO = Double.doubleToLongBits(-0.0d);
for (int i = numNeg; i < numNeg + numNegZero; i++) {
if (NEGATIVE_ZERO != Double.doubleToLongBits(a[i])) {
- failed("On position " + i + " must be -0.0 instead of " + a[i]);
+ fail("There must be -0.0 instead of " + a[i] + " at position " + i);
+ }
+ }
+
+ for (int i = 0; i < a.length - numNaN; i++) {
+ if (a[i] != b[i]) {
+ fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
+ }
+ }
+ }
+
+ private void compare(Object a, Object b) {
+ if (a instanceof int[]) {
+ compare((int[]) a, (int[]) b);
+ } else if (a instanceof long[]) {
+ compare((long[]) a, (long[]) b);
+ } else if (a instanceof byte[]) {
+ compare((byte[]) a, (byte[]) b);
+ } else if (a instanceof char[]) {
+ compare((char[]) a, (char[]) b);
+ } else if (a instanceof short[]) {
+ compare((short[]) a, (short[]) b);
+ } else if (a instanceof float[]) {
+ compare((float[]) a, (float[]) b);
+ } else if (a instanceof double[]) {
+ compare((double[]) a, (double[]) b);
+ } else {
+ fail("Unknown type of array: " + a.getClass().getName());
+ }
+ }
+
+ private void compare(int[] a, int[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
+ }
+ }
+ }
+
+ private void compare(long[] a, long[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
+ }
+ }
+ }
+
+ private void compare(byte[] a, byte[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
+ }
+ }
+ }
+
+ private void compare(char[] a, char[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
+ }
+ }
+ }
+
+ private void compare(short[] a, short[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
+ }
+ }
+ }
+
+ private void compare(float[] a, float[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
+ }
+ }
+ }
+
+ private void compare(double[] a, double[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
}
}
- for (int i = 0; i < a.length - numNaN; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
+ }
+
+ private String getType(int i) {
+ Object a = test[i];
+
+ if (a instanceof int[]) {
+ return "INT ";
+ }
+ if (a instanceof long[]) {
+ return "LONG ";
+ }
+ if (a instanceof byte[]) {
+ return "BYTE ";
+ }
+ if (a instanceof char[]) {
+ return "CHAR ";
+ }
+ if (a instanceof short[]) {
+ return "SHORT ";
+ }
+ if (a instanceof float[]) {
+ return "FLOAT ";
+ }
+ if (a instanceof double[]) {
+ return "DOUBLE";
+ }
+ fail("Unknown type of array: " + a.getClass().getName());
+ return null;
+ }
+
+ private void checkSorted(Object a) {
+ if (a instanceof int[]) {
+ checkSorted((int[]) a);
+ } else if (a instanceof long[]) {
+ checkSorted((long[]) a);
+ } else if (a instanceof byte[]) {
+ checkSorted((byte[]) a);
+ } else if (a instanceof char[]) {
+ checkSorted((char[]) a);
+ } else if (a instanceof short[]) {
+ checkSorted((short[]) a);
+ } else if (a instanceof float[]) {
+ checkSorted((float[]) a);
+ } else if (a instanceof double[]) {
+ checkSorted((double[]) a);
+ } else {
+ fail("Unknown type of array: " + a.getClass().getName());
+ }
+ }
+
+ private void checkSorted(int[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
+ }
+ }
+ }
+
+ private void checkSorted(long[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
+ }
+ }
+ }
+
+ private void checkSorted(byte[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
+ }
+ }
+ }
+
+ private void checkSorted(char[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
+ }
+ }
+ }
+
+ private void checkSorted(short[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
+ }
+ }
+ }
+
+ private void checkSorted(float[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
+ }
+ }
+ }
+
+ private void checkSorted(double[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
}
}
}
- private static enum SortedBuilder {
- REPEATED {
- void build(int[] a, int m) {
- int period = a.length / m;
- int i = 0;
- int k = 0;
+ private void checkCheckSum(Object test, Object gold) {
+ if (checkSumXor(test) != checkSumXor(gold)) {
+ fail("Original and sorted arrays are not identical [^]");
+ }
+ if (checkSumPlus(test) != checkSumPlus(gold)) {
+ fail("Original and sorted arrays are not identical [+]");
+ }
+ }
+
+ private int checkSumXor(Object a) {
+ if (a instanceof int[]) {
+ return checkSumXor((int[]) a);
+ }
+ if (a instanceof long[]) {
+ return checkSumXor((long[]) a);
+ }
+ if (a instanceof byte[]) {
+ return checkSumXor((byte[]) a);
+ }
+ if (a instanceof char[]) {
+ return checkSumXor((char[]) a);
+ }
+ if (a instanceof short[]) {
+ return checkSumXor((short[]) a);
+ }
+ if (a instanceof float[]) {
+ return checkSumXor((float[]) a);
+ }
+ if (a instanceof double[]) {
+ return checkSumXor((double[]) a);
+ }
+ fail("Unknown type of array: " + a.getClass().getName());
+ return -1;
+ }
+
+ private int checkSumXor(int[] a) {
+ int checkSum = 0;
+
+ for (int e : a) {
+ checkSum ^= e;
+ }
+ return checkSum;
+ }
+
+ private int checkSumXor(long[] a) {
+ long checkSum = 0;
+
+ for (long e : a) {
+ checkSum ^= e;
+ }
+ return (int) checkSum;
+ }
+
+ private int checkSumXor(byte[] a) {
+ byte checkSum = 0;
+
+ for (byte e : a) {
+ checkSum ^= e;
+ }
+ return (int) checkSum;
+ }
+
+ private int checkSumXor(char[] a) {
+ char checkSum = 0;
+
+ for (char e : a) {
+ checkSum ^= e;
+ }
+ return (int) checkSum;
+ }
+
+ private int checkSumXor(short[] a) {
+ short checkSum = 0;
+
+ for (short e : a) {
+ checkSum ^= e;
+ }
+ return (int) checkSum;
+ }
+
+ private int checkSumXor(float[] a) {
+ int checkSum = 0;
+
+ for (float e : a) {
+ checkSum ^= (int) e;
+ }
+ return checkSum;
+ }
+
+ private int checkSumXor(double[] a) {
+ int checkSum = 0;
+
+ for (double e : a) {
+ checkSum ^= (int) e;
+ }
+ return checkSum;
+ }
+
+ private int checkSumPlus(Object a) {
+ if (a instanceof int[]) {
+ return checkSumPlus((int[]) a);
+ }
+ if (a instanceof long[]) {
+ return checkSumPlus((long[]) a);
+ }
+ if (a instanceof byte[]) {
+ return checkSumPlus((byte[]) a);
+ }
+ if (a instanceof char[]) {
+ return checkSumPlus((char[]) a);
+ }
+ if (a instanceof short[]) {
+ return checkSumPlus((short[]) a);
+ }
+ if (a instanceof float[]) {
+ return checkSumPlus((float[]) a);
+ }
+ if (a instanceof double[]) {
+ return checkSumPlus((double[]) a);
+ }
+ fail("Unknown type of array: " + a.getClass().getName());
+ return -1;
+ }
+
+ private int checkSumPlus(int[] a) {
+ int checkSum = 0;
+
+ for (int e : a) {
+ checkSum += e;
+ }
+ return checkSum;
+ }
+
+ private int checkSumPlus(long[] a) {
+ long checkSum = 0;
+
+ for (long e : a) {
+ checkSum += e;
+ }
+ return (int) checkSum;
+ }
+
+ private int checkSumPlus(byte[] a) {
+ byte checkSum = 0;
+
+ for (byte e : a) {
+ checkSum += e;
+ }
+ return (int) checkSum;
+ }
+
+ private int checkSumPlus(char[] a) {
+ char checkSum = 0;
+
+ for (char e : a) {
+ checkSum += e;
+ }
+ return (int) checkSum;
+ }
+
+ private int checkSumPlus(short[] a) {
+ short checkSum = 0;
+
+ for (short e : a) {
+ checkSum += e;
+ }
+ return (int) checkSum;
+ }
+
+ private int checkSumPlus(float[] a) {
+ int checkSum = 0;
+
+ for (float e : a) {
+ checkSum += (int) e;
+ }
+ return checkSum;
+ }
+
+ private int checkSumPlus(double[] a) {
+ int checkSum = 0;
+
+ for (double e : a) {
+ checkSum += (int) e;
+ }
+ return checkSum;
+ }
+
+ private void sortByInsertionSort(Object a) {
+ if (a instanceof int[]) {
+ sortByInsertionSort((int[]) a);
+ } else if (a instanceof long[]) {
+ sortByInsertionSort((long[]) a);
+ } else if (a instanceof byte[]) {
+ sortByInsertionSort((byte[]) a);
+ } else if (a instanceof char[]) {
+ sortByInsertionSort((char[]) a);
+ } else if (a instanceof short[]) {
+ sortByInsertionSort((short[]) a);
+ } else if (a instanceof float[]) {
+ sortByInsertionSort((float[]) a);
+ } else if (a instanceof double[]) {
+ sortByInsertionSort((double[]) a);
+ } else {
+ fail("Unknown type of array: " + a.getClass().getName());
+ }
+ }
+
+ private void sortByInsertionSort(int[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ int ai = a[i];
+
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private void sortByInsertionSort(long[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ long ai = a[i];
+
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private void sortByInsertionSort(byte[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ byte ai = a[i];
+
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private void sortByInsertionSort(char[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ char ai = a[i];
+
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private void sortByInsertionSort(short[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ short ai = a[i];
+
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private void sortByInsertionSort(float[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ float ai = a[i];
+
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private void sortByInsertionSort(double[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ double ai = a[i];
+
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private void checkSubArray(Object a, int fromIndex, int toIndex) {
+ if (a instanceof int[]) {
+ checkSubArray((int[]) a, fromIndex, toIndex);
+ } else if (a instanceof long[]) {
+ checkSubArray((long[]) a, fromIndex, toIndex);
+ } else if (a instanceof byte[]) {
+ checkSubArray((byte[]) a, fromIndex, toIndex);
+ } else if (a instanceof char[]) {
+ checkSubArray((char[]) a, fromIndex, toIndex);
+ } else if (a instanceof short[]) {
+ checkSubArray((short[]) a, fromIndex, toIndex);
+ } else if (a instanceof float[]) {
+ checkSubArray((float[]) a, fromIndex, toIndex);
+ } else if (a instanceof double[]) {
+ checkSubArray((double[]) a, fromIndex, toIndex);
+ } else {
+ fail("Unknown type of array: " + a.getClass().getName());
+ }
+ }
+
+ private void checkSubArray(int[] a, int fromIndex, int toIndex) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != A380) {
+ fail("Range sort changes left element at position " + i + hex(a[i], A380));
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != B747) {
+ fail("Range sort changes right element at position " + i + hex(a[i], B747));
+ }
+ }
+ }
+
+ private void checkSubArray(long[] a, int fromIndex, int toIndex) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != (long) A380) {
+ fail("Range sort changes left element at position " + i + hex(a[i], A380));
+ }
+ }
- while (true) {
- for (int t = 1; t <= period; t++) {
- if (i >= a.length) {
- return;
- }
- a[i++] = k;
- }
- if (i >= a.length) {
- return;
- }
- k++;
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != (long) B747) {
+ fail("Range sort changes right element at position " + i + hex(a[i], B747));
+ }
+ }
+ }
+
+ private void checkSubArray(byte[] a, int fromIndex, int toIndex) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != (byte) A380) {
+ fail("Range sort changes left element at position " + i + hex(a[i], A380));
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != (byte) B747) {
+ fail("Range sort changes right element at position " + i + hex(a[i], B747));
+ }
+ }
+ }
+
+ private void checkSubArray(char[] a, int fromIndex, int toIndex) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != (char) A380) {
+ fail("Range sort changes left element at position " + i + hex(a[i], A380));
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != (char) B747) {
+ fail("Range sort changes right element at position " + i + hex(a[i], B747));
+ }
+ }
+ }
+
+ private void checkSubArray(short[] a, int fromIndex, int toIndex) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != (short) A380) {
+ fail("Range sort changes left element at position " + i + hex(a[i], A380));
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != (short) B747) {
+ fail("Range sort changes right element at position " + i + hex(a[i], B747));
+ }
+ }
+ }
+
+ private void checkSubArray(float[] a, int fromIndex, int toIndex) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != (float) A380) {
+ fail("Range sort changes left element at position " + i + hex((long) a[i], A380));
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != (float) B747) {
+ fail("Range sort changes right element at position " + i + hex((long) a[i], B747));
+ }
+ }
+ }
+
+ private void checkSubArray(double[] a, int fromIndex, int toIndex) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != (double) A380) {
+ fail("Range sort changes left element at position " + i + hex((long) a[i], A380));
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != (double) B747) {
+ fail("Range sort changes right element at position " + i + hex((long) a[i], B747));
+ }
+ }
+ }
+
+ private void checkRange(Object a, int m) {
+ if (a instanceof int[]) {
+ checkRange((int[]) a, m);
+ } else if (a instanceof long[]) {
+ checkRange((long[]) a, m);
+ } else if (a instanceof byte[]) {
+ checkRange((byte[]) a, m);
+ } else if (a instanceof char[]) {
+ checkRange((char[]) a, m);
+ } else if (a instanceof short[]) {
+ checkRange((short[]) a, m);
+ } else if (a instanceof float[]) {
+ checkRange((float[]) a, m);
+ } else if (a instanceof double[]) {
+ checkRange((double[]) a, m);
+ } else {
+ fail("Unknown type of array: " + a.getClass().getName());
+ }
+ }
+
+ private void checkRange(int[] a, int m) {
+ try {
+ sortingHelper.sort(a, m + 1, m);
+ fail(sortingHelper + " does not throw IllegalArgumentException " +
+ "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
+ } catch (IllegalArgumentException iae) {
+ try {
+ sortingHelper.sort(a, -m, a.length);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: fromIndex = " + (-m));
+ } catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ sortingHelper.sort(a, 0, a.length + m);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {}
+ }
+ }
+ }
+
+ private void checkRange(long[] a, int m) {
+ try {
+ sortingHelper.sort(a, m + 1, m);
+ fail(sortingHelper + " does not throw IllegalArgumentException " +
+ "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
+ } catch (IllegalArgumentException iae) {
+ try {
+ sortingHelper.sort(a, -m, a.length);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: fromIndex = " + (-m));
+ } catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ sortingHelper.sort(a, 0, a.length + m);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {}
+ }
+ }
+ }
+
+ private void checkRange(byte[] a, int m) {
+ try {
+ sortingHelper.sort(a, m + 1, m);
+ fail(sortingHelper + " does not throw IllegalArgumentException " +
+ "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
+ } catch (IllegalArgumentException iae) {
+ try {
+ sortingHelper.sort(a, -m, a.length);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: fromIndex = " + (-m));
+ } catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ sortingHelper.sort(a, 0, a.length + m);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {}
+ }
+ }
+ }
+
+ private void checkRange(char[] a, int m) {
+ try {
+ sortingHelper.sort(a, m + 1, m);
+ fail(sortingHelper + " does not throw IllegalArgumentException " +
+ "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
+ } catch (IllegalArgumentException iae) {
+ try {
+ sortingHelper.sort(a, -m, a.length);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: fromIndex = " + (-m));
+ } catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ sortingHelper.sort(a, 0, a.length + m);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {}
+ }
+ }
+ }
+
+ private void checkRange(short[] a, int m) {
+ try {
+ sortingHelper.sort(a, m + 1, m);
+ fail(sortingHelper + " does not throw IllegalArgumentException " +
+ "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
+ } catch (IllegalArgumentException iae) {
+ try {
+ sortingHelper.sort(a, -m, a.length);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: fromIndex = " + (-m));
+ } catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ sortingHelper.sort(a, 0, a.length + m);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {}
+ }
+ }
+ }
+
+ private void checkRange(float[] a, int m) {
+ try {
+ sortingHelper.sort(a, m + 1, m);
+ fail(sortingHelper + " does not throw IllegalArgumentException " +
+ "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
+ } catch (IllegalArgumentException iae) {
+ try {
+ sortingHelper.sort(a, -m, a.length);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: fromIndex = " + (-m));
+ } catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ sortingHelper.sort(a, 0, a.length + m);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {}
+ }
+ }
+ }
+
+ private void checkRange(double[] a, int m) {
+ try {
+ sortingHelper.sort(a, m + 1, m);
+ fail(sortingHelper + " does not throw IllegalArgumentException " +
+ "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
+ } catch (IllegalArgumentException iae) {
+ try {
+ sortingHelper.sort(a, -m, a.length);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: fromIndex = " + (-m));
+ } catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ sortingHelper.sort(a, 0, a.length + m);
+ fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
+ "as expected: toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {}
+ }
+ }
+ }
+
+ private void copy(Object dst, Object src) {
+ if (src instanceof float[]) {
+ copy((float[]) dst, (float[]) src);
+ } else if (src instanceof double[]) {
+ copy((double[]) dst, (double[]) src);
+ } else {
+ fail("Unknown type of array: " + src.getClass().getName());
+ }
+ }
+
+ private void copy(float[] dst, float[] src) {
+ System.arraycopy(src, 0, dst, 0, src.length);
+ }
+
+ private void copy(double[] dst, double[] src) {
+ System.arraycopy(src, 0, dst, 0, src.length);
+ }
+
+ private void printTestName(String test, TestRandom random, int length) {
+ printTestName(test, random, length, "");
+ }
+
+ private void createData(int length) {
+ gold = new Object[] {
+ new int[length], new long[length],
+ new byte[length], new char[length], new short[length],
+ new float[length], new double[length]
+ };
+
+ test = new Object[] {
+ new int[length], new long[length],
+ new byte[length], new char[length], new short[length],
+ new float[length], new double[length]
+ };
+ }
+
+ private void convertData(int length) {
+ for (int i = 1; i < gold.length; i++) {
+ TypeConverter converter = TypeConverter.values()[i - 1];
+ converter.convert((int[])gold[0], gold[i]);
+ }
+
+ for (int i = 0; i < gold.length; i++) {
+ System.arraycopy(gold[i], 0, test[i], 0, length);
+ }
+ }
+
+ private String hex(long a, int b) {
+ return ": " + Long.toHexString(a) + ", must be " + Integer.toHexString(b);
+ }
+
+ private void printTestName(String test, TestRandom random, int length, String message) {
+ out.println( "[" + sortingHelper + "] '" + test +
+ "' length = " + length + ", random = " + random + message);
+ }
+
+ private static enum TypeConverter {
+ LONG {
+ void convert(int[] src, Object dst) {
+ long[] b = (long[]) dst;
+
+ for (int i = 0; i < src.length; i++) {
+ b[i] = (long) src[i];
}
}
},
- ORGAN_PIPES {
- void build(int[] a, int m) {
- int i = 0;
- int k = m;
+
+ BYTE {
+ void convert(int[] src, Object dst) {
+ byte[] b = (byte[]) dst;
+
+ for (int i = 0; i < src.length; i++) {
+ b[i] = (byte) src[i];
+ }
+ }
+ },
+
+ CHAR {
+ void convert(int[] src, Object dst) {
+ char[] b = (char[]) dst;
+
+ for (int i = 0; i < src.length; i++) {
+ b[i] = (char) src[i];
+ }
+ }
+ },
+
+ SHORT {
+ void convert(int[] src, Object dst) {
+ short[] b = (short[]) dst;
+
+ for (int i = 0; i < src.length; i++) {
+ b[i] = (short) src[i];
+ }
+ }
+ },
- while (true) {
- for (int t = 1; t <= m; t++) {
- if (i >= a.length) {
- return;
- }
- a[i++] = k;
- }
+ FLOAT {
+ void convert(int[] src, Object dst) {
+ float[] b = (float[]) dst;
+
+ for (int i = 0; i < src.length; i++) {
+ b[i] = (float) src[i];
+ }
+ }
+ },
+
+ DOUBLE {
+ void convert(int[] src, Object dst) {
+ double[] b = (double[]) dst;
+
+ for (int i = 0; i < src.length; i++) {
+ b[i] = (double) src[i];
+ }
+ }
+ };
+
+ abstract void convert(int[] src, Object dst);
+ }
+
+ private static enum SortedBuilder {
+ STEPS {
+ void build(int[] a, int m) {
+ for (int i = 0; i < m; i++) {
+ a[i] = 0;
+ }
+
+ for (int i = m; i < a.length; i++) {
+ a[i] = 1;
}
}
};
abstract void build(int[] a, int m);
-
- @Override public String toString() {
- String name = name();
-
- for (int i = name.length(); i < 12; i++) {
- name += " ";
- }
- return name;
- }
- }
-
- private static enum MergeBuilder {
- ASCENDING {
- void build(int[] a, int m) {
- int period = a.length / m;
- int v = 1, i = 0;
-
- for (int k = 0; k < m; k++) {
- v = 1;
- for (int p = 0; p < period; p++) {
- a[i++] = v++;
- }
- }
- for (int j = i; j < a.length - 1; j++) {
- a[j] = v++;
- }
- a[a.length - 1] = 0;
- }
- },
- DESCENDING {
- void build(int[] a, int m) {
- int period = a.length / m;
- int v = -1, i = 0;
-
- for (int k = 0; k < m; k++) {
- v = -1;
- for (int p = 0; p < period; p++) {
- a[i++] = v--;
- }
- }
- for (int j = i; j < a.length - 1; j++) {
- a[j] = v--;
- }
- a[a.length - 1] = 0;
- }
- };
-
- abstract void build(int[] a, int m);
-
- @Override public String toString() {
- String name = name();
-
- for (int i = name.length(); i < 12; i++) {
- name += " ";
- }
- return name;
- }
}
private static enum UnsortedBuilder {
@@ -894,6 +1649,7 @@
}
}
},
+
ASCENDING {
void build(int[] a, int m, Random random) {
for (int i = 0; i < a.length; i++) {
@@ -901,6 +1657,7 @@
}
}
},
+
DESCENDING {
void build(int[] a, int m, Random random) {
for (int i = 0; i < a.length; i++) {
@@ -908,13 +1665,15 @@
}
}
},
- ALL_EQUAL {
+
+ EQUAL {
void build(int[] a, int m, Random random) {
for (int i = 0; i < a.length; i++) {
a[i] = m;
}
}
},
+
SAW {
void build(int[] a, int m, Random random) {
int incCount = 1;
@@ -941,6 +1700,7 @@
}
}
},
+
REPEATED {
void build(int[] a, int m, Random random) {
for (int i = 0; i < a.length; i++) {
@@ -948,6 +1708,7 @@
}
}
},
+
DUPLICATED {
void build(int[] a, int m, Random random) {
for (int i = 0; i < a.length; i++) {
@@ -955,6 +1716,7 @@
}
}
},
+
ORGAN_PIPES {
void build(int[] a, int m, Random random) {
int middle = a.length / (m + 1);
@@ -962,11 +1724,13 @@
for (int i = 0; i < middle; i++) {
a[i] = i;
}
+
for (int i = middle; i < a.length; i++) {
a[i] = a.length - i - 1;
}
}
},
+
STAGGER {
void build(int[] a, int m, Random random) {
for (int i = 0; i < a.length; i++) {
@@ -974,6 +1738,7 @@
}
}
},
+
PLATEAU {
void build(int[] a, int m, Random random) {
for (int i = 0; i < a.length; i++) {
@@ -981,1064 +1746,271 @@
}
}
},
+
SHUFFLE {
void build(int[] a, int m, Random random) {
int x = 0, y = 0;
+
for (int i = 0; i < a.length; i++) {
a[i] = random.nextBoolean() ? (x += 2) : (y += 2);
}
}
+ },
+
+ LATCH {
+ void build(int[] a, int m, Random random) {
+ int max = a.length / m;
+ max = max < 2 ? 2 : max;
+
+ for (int i = 0; i < a.length; i++) {
+ a[i] = i % max;
+ }
+ }
};
abstract void build(int[] a, int m, Random random);
-
- @Override public String toString() {
- String name = name();
-
- for (int i = name.length(); i < 12; i++) {
- name += " ";
- }
- return name;
- }
- }
-
- private static void checkWithCheckSum(Object test, Object golden) {
- checkSorted(test);
- checkCheckSum(test, golden);
- }
-
- private static void failed(String message) {
- err.format("\n*** TEST FAILED - %s.\n\n%s.\n\n", ourDescription, message);
- throw new RuntimeException("Test failed - see log file for details");
- }
-
- private static void failedSort(int index, String value1, String value2) {
- failed("Array is not sorted at " + index + "-th position: " +
- value1 + " and " + value2);
- }
-
- private static void failedCompare(int index, String value1, String value2) {
- failed("On position " + index + " must be " + value2 + " instead of " + value1);
- }
-
- private static void compare(Object test, Object golden) {
- if (test instanceof int[]) {
- compare((int[]) test, (int[]) golden);
- } else if (test instanceof long[]) {
- compare((long[]) test, (long[]) golden);
- } else if (test instanceof short[]) {
- compare((short[]) test, (short[]) golden);
- } else if (test instanceof byte[]) {
- compare((byte[]) test, (byte[]) golden);
- } else if (test instanceof char[]) {
- compare((char[]) test, (char[]) golden);
- } else if (test instanceof float[]) {
- compare((float[]) test, (float[]) golden);
- } else if (test instanceof double[]) {
- compare((double[]) test, (double[]) golden);
- } else if (test instanceof Integer[]) {
- compare((Integer[]) test, (Integer[]) golden);
- } else {
- failed("Unknow type of array: " + test + " of class " +
- test.getClass().getName());
- }
- }
-
- private static void compare(int[] a, int[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static void compare(long[] a, long[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static void compare(short[] a, short[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static void compare(byte[] a, byte[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
-
- private static void compare(char[] a, char[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
}
- private static void compare(float[] a, float[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
+ private static enum MergingBuilder {
+ ASCENDING {
+ void build(int[] a, int m) {
+ int period = a.length / m;
+ int v = 1, i = 0;
+
+ for (int k = 0; k < m; k++) {
+ v = 1;
- private static void compare(double[] a, double[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i] != b[i]) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
+ for (int p = 0; p < period; p++) {
+ a[i++] = v++;
+ }
+ }
- private static void compare(Integer[] a, Integer[] b) {
- for (int i = 0; i < a.length; i++) {
- if (a[i].compareTo(b[i]) != 0) {
- failedCompare(i, "" + a[i], "" + b[i]);
- }
- }
- }
+ for (int j = i; j < a.length - 1; j++) {
+ a[j] = v++;
+ }
- private static void checkSorted(Object object) {
- if (object instanceof int[]) {
- checkSorted((int[]) object);
- } else if (object instanceof long[]) {
- checkSorted((long[]) object);
- } else if (object instanceof short[]) {
- checkSorted((short[]) object);
- } else if (object instanceof byte[]) {
- checkSorted((byte[]) object);
- } else if (object instanceof char[]) {
- checkSorted((char[]) object);
- } else if (object instanceof float[]) {
- checkSorted((float[]) object);
- } else if (object instanceof double[]) {
- checkSorted((double[]) object);
- } else if (object instanceof Integer[]) {
- checkSorted((Integer[]) object);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- }
- }
+ a[a.length - 1] = 0;
+ }
+ },
+
+ DESCENDING {
+ void build(int[] a, int m) {
+ int period = a.length / m;
+ int v = -1, i = 0;
- private static void checkSorted(int[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
+ for (int k = 0; k < m; k++) {
+ v = -1;
+
+ for (int p = 0; p < period; p++) {
+ a[i++] = v--;
+ }
+ }
+
+ for (int j = i; j < a.length - 1; j++) {
+ a[j] = v--;
+ }
+
+ a[a.length - 1] = 0;
}
- }
- }
-
- private static void checkSorted(long[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
- }
+ },
- private static void checkSorted(short[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
+ POINT {
+ void build(int[] a, int m) {
+ for (int i = 0; i < a.length; i++) {
+ a[i] = 0;
+ }
+ a[a.length / 2] = m;
}
- }
- }
+ },
- private static void checkSorted(byte[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
+ LINE {
+ void build(int[] a, int m) {
+ for (int i = 0; i < a.length; i++) {
+ a[i] = i;
+ }
+ reverse(a, 0, a.length - 1);
}
- }
- }
+ },
- private static void checkSorted(char[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
- }
-
- private static void checkSorted(float[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
+ PEARL {
+ void build(int[] a, int m) {
+ for (int i = 0; i < a.length; i++) {
+ a[i] = i;
+ }
+ reverse(a, 0, 2);
}
- }
- }
+ },
+
+ RING {
+ void build(int[] a, int m) {
+ int k1 = a.length / 3;
+ int k2 = a.length / 3 * 2;
+ int level = a.length / 3;
- private static void checkSorted(double[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
+ for (int i = 0, k = level; i < k1; i++) {
+ a[i] = k--;
+ }
+
+ for (int i = k1; i < k2; i++) {
+ a[i] = 0;
+ }
+
+ for (int i = k2, k = level; i < a.length; i++) {
+ a[i] = k--;
+ }
}
- }
- }
+ };
+
+ abstract void build(int[] a, int m);
- private static void checkSorted(Integer[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i].intValue() > a[i + 1].intValue()) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
+ private static void reverse(int[] a, int lo, int hi) {
+ for (--hi; lo < hi; ) {
+ int tmp = a[lo];
+ a[lo++] = a[hi];
+ a[hi--] = tmp;
}
}
}
- private static void checkCheckSum(Object test, Object golden) {
- if (checkSumXor(test) != checkSumXor(golden)) {
- failed("Original and sorted arrays are not identical [xor]");
- }
- if (checkSumPlus(test) != checkSumPlus(golden)) {
- failed("Original and sorted arrays are not identical [plus]");
- }
- }
-
- private static int checkSumXor(Object object) {
- if (object instanceof int[]) {
- return checkSumXor((int[]) object);
- } else if (object instanceof long[]) {
- return checkSumXor((long[]) object);
- } else if (object instanceof short[]) {
- return checkSumXor((short[]) object);
- } else if (object instanceof byte[]) {
- return checkSumXor((byte[]) object);
- } else if (object instanceof char[]) {
- return checkSumXor((char[]) object);
- } else if (object instanceof float[]) {
- return checkSumXor((float[]) object);
- } else if (object instanceof double[]) {
- return checkSumXor((double[]) object);
- } else if (object instanceof Integer[]) {
- return checkSumXor((Integer[]) object);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- return -1;
- }
- }
-
- private static int checkSumXor(Integer[] a) {
- int checkSum = 0;
-
- for (Integer e : a) {
- checkSum ^= e.intValue();
- }
- return checkSum;
- }
-
- private static int checkSumXor(int[] a) {
- int checkSum = 0;
+ private static enum NegativeZeroBuilder {
+ FLOAT {
+ void build(Object o, Random random) {
+ float[] a = (float[]) o;
- for (int e : a) {
- checkSum ^= e;
- }
- return checkSum;
- }
-
- private static int checkSumXor(long[] a) {
- long checkSum = 0;
-
- for (long e : a) {
- checkSum ^= e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumXor(short[] a) {
- short checkSum = 0;
-
- for (short e : a) {
- checkSum ^= e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumXor(byte[] a) {
- byte checkSum = 0;
-
- for (byte e : a) {
- checkSum ^= e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumXor(char[] a) {
- char checkSum = 0;
-
- for (char e : a) {
- checkSum ^= e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumXor(float[] a) {
- int checkSum = 0;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = random.nextBoolean() ? -0.0f : 0.0f;
+ }
+ }
+ },
- for (float e : a) {
- checkSum ^= (int) e;
- }
- return checkSum;
- }
-
- private static int checkSumXor(double[] a) {
- int checkSum = 0;
-
- for (double e : a) {
- checkSum ^= (int) e;
- }
- return checkSum;
- }
-
- private static int checkSumPlus(Object object) {
- if (object instanceof int[]) {
- return checkSumPlus((int[]) object);
- } else if (object instanceof long[]) {
- return checkSumPlus((long[]) object);
- } else if (object instanceof short[]) {
- return checkSumPlus((short[]) object);
- } else if (object instanceof byte[]) {
- return checkSumPlus((byte[]) object);
- } else if (object instanceof char[]) {
- return checkSumPlus((char[]) object);
- } else if (object instanceof float[]) {
- return checkSumPlus((float[]) object);
- } else if (object instanceof double[]) {
- return checkSumPlus((double[]) object);
- } else if (object instanceof Integer[]) {
- return checkSumPlus((Integer[]) object);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- return -1;
- }
- }
-
- private static int checkSumPlus(int[] a) {
- int checkSum = 0;
+ DOUBLE {
+ void build(Object o, Random random) {
+ double[] a = (double[]) o;
- for (int e : a) {
- checkSum += e;
- }
- return checkSum;
- }
-
- private static int checkSumPlus(long[] a) {
- long checkSum = 0;
-
- for (long e : a) {
- checkSum += e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumPlus(short[] a) {
- short checkSum = 0;
-
- for (short e : a) {
- checkSum += e;
- }
- return (int) checkSum;
- }
+ for (int i = 0; i < a.length; i++) {
+ a[i] = random.nextBoolean() ? -0.0d : 0.0d;
+ }
+ }
+ };
- private static int checkSumPlus(byte[] a) {
- byte checkSum = 0;
-
- for (byte e : a) {
- checkSum += e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumPlus(char[] a) {
- char checkSum = 0;
-
- for (char e : a) {
- checkSum += e;
- }
- return (int) checkSum;
- }
-
- private static int checkSumPlus(float[] a) {
- int checkSum = 0;
-
- for (float e : a) {
- checkSum += (int) e;
- }
- return checkSum;
+ abstract void build(Object o, Random random);
}
- private static int checkSumPlus(double[] a) {
- int checkSum = 0;
-
- for (double e : a) {
- checkSum += (int) e;
- }
- return checkSum;
- }
-
- private static int checkSumPlus(Integer[] a) {
- int checkSum = 0;
-
- for (Integer e : a) {
- checkSum += e.intValue();
- }
- return checkSum;
- }
+ private static enum FloatingPointBuilder {
+ FLOAT {
+ void build(Object o, int a, int g, int z, int n, int p, Random random) {
+ float negativeValue = -random.nextFloat();
+ float positiveValue = random.nextFloat();
+ float[] x = (float[]) o;
+ int fromIndex = 0;
- private static void sortByInsertionSort(Object object) {
- if (object instanceof int[]) {
- sortByInsertionSort((int[]) object);
- } else if (object instanceof long[]) {
- sortByInsertionSort((long[]) object);
- } else if (object instanceof short[]) {
- sortByInsertionSort((short[]) object);
- } else if (object instanceof byte[]) {
- sortByInsertionSort((byte[]) object);
- } else if (object instanceof char[]) {
- sortByInsertionSort((char[]) object);
- } else if (object instanceof float[]) {
- sortByInsertionSort((float[]) object);
- } else if (object instanceof double[]) {
- sortByInsertionSort((double[]) object);
- } else if (object instanceof Integer[]) {
- sortByInsertionSort((Integer[]) object);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- }
- }
+ writeValue(x, negativeValue, fromIndex, n);
+ fromIndex += n;
- private static void sortByInsertionSort(int[] a) {
- for (int j, i = 1; i < a.length; i++) {
- int ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
-
- private static void sortByInsertionSort(long[] a) {
- for (int j, i = 1; i < a.length; i++) {
- long ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
+ writeValue(x, -0.0f, fromIndex, g);
+ fromIndex += g;
- private static void sortByInsertionSort(short[] a) {
- for (int j, i = 1; i < a.length; i++) {
- short ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
+ writeValue(x, 0.0f, fromIndex, z);
+ fromIndex += z;
- private static void sortByInsertionSort(byte[] a) {
- for (int j, i = 1; i < a.length; i++) {
- byte ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
+ writeValue(x, positiveValue, fromIndex, p);
+ fromIndex += p;
- private static void sortByInsertionSort(char[] a) {
- for (int j, i = 1; i < a.length; i++) {
- char ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
-
- private static void sortByInsertionSort(float[] a) {
- for (int j, i = 1; i < a.length; i++) {
- float ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
+ writeValue(x, Float.NaN, fromIndex, a);
}
- a[j + 1] = ai;
- }
- }
-
- private static void sortByInsertionSort(double[] a) {
- for (int j, i = 1; i < a.length; i++) {
- double ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
+ },
- private static void sortByInsertionSort(Integer[] a) {
- for (int j, i = 1; i < a.length; i++) {
- Integer ai = a[i];
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
+ DOUBLE {
+ void build(Object o, int a, int g, int z, int n, int p, Random random) {
+ double negativeValue = -random.nextFloat();
+ double positiveValue = random.nextFloat();
+ double[] x = (double[]) o;
+ int fromIndex = 0;
- private static void sort(Object object) {
- if (object instanceof int[]) {
- Arrays.sort((int[]) object);
- } else if (object instanceof long[]) {
- Arrays.sort((long[]) object);
- } else if (object instanceof short[]) {
- Arrays.sort((short[]) object);
- } else if (object instanceof byte[]) {
- Arrays.sort((byte[]) object);
- } else if (object instanceof char[]) {
- Arrays.sort((char[]) object);
- } else if (object instanceof float[]) {
- Arrays.sort((float[]) object);
- } else if (object instanceof double[]) {
- Arrays.sort((double[]) object);
- } else if (object instanceof Integer[]) {
- Arrays.sort((Integer[]) object);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- }
- }
+ writeValue(x, negativeValue, fromIndex, n);
+ fromIndex += n;
+
+ writeValue(x, -0.0d, fromIndex, g);
+ fromIndex += g;
- private static void sortSubArray(Object object, int fromIndex, int toIndex) {
- if (object instanceof int[]) {
- Arrays.sort((int[]) object, fromIndex, toIndex);
- } else if (object instanceof long[]) {
- Arrays.sort((long[]) object, fromIndex, toIndex);
- } else if (object instanceof short[]) {
- Arrays.sort((short[]) object, fromIndex, toIndex);
- } else if (object instanceof byte[]) {
- Arrays.sort((byte[]) object, fromIndex, toIndex);
- } else if (object instanceof char[]) {
- Arrays.sort((char[]) object, fromIndex, toIndex);
- } else if (object instanceof float[]) {
- Arrays.sort((float[]) object, fromIndex, toIndex);
- } else if (object instanceof double[]) {
- Arrays.sort((double[]) object, fromIndex, toIndex);
- } else if (object instanceof Integer[]) {
- Arrays.sort((Integer[]) object, fromIndex, toIndex);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- }
- }
+ writeValue(x, 0.0d, fromIndex, z);
+ fromIndex += z;
+
+ writeValue(x, positiveValue, fromIndex, p);
+ fromIndex += p;
- private static void checkSubArray(Object object, int fromIndex, int toIndex, int m) {
- if (object instanceof int[]) {
- checkSubArray((int[]) object, fromIndex, toIndex, m);
- } else if (object instanceof long[]) {
- checkSubArray((long[]) object, fromIndex, toIndex, m);
- } else if (object instanceof short[]) {
- checkSubArray((short[]) object, fromIndex, toIndex, m);
- } else if (object instanceof byte[]) {
- checkSubArray((byte[]) object, fromIndex, toIndex, m);
- } else if (object instanceof char[]) {
- checkSubArray((char[]) object, fromIndex, toIndex, m);
- } else if (object instanceof float[]) {
- checkSubArray((float[]) object, fromIndex, toIndex, m);
- } else if (object instanceof double[]) {
- checkSubArray((double[]) object, fromIndex, toIndex, m);
- } else if (object instanceof Integer[]) {
- checkSubArray((Integer[]) object, fromIndex, toIndex, m);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- }
- }
+ writeValue(x, Double.NaN, fromIndex, a);
+ }
+ };
- private static void checkSubArray(Integer[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i].intValue() != 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
+ abstract void build(Object o, int a, int g, int z, int n, int p, Random random);
+
+ private static void writeValue(float[] a, float value, int fromIndex, int count) {
+ for (int i = fromIndex; i < fromIndex + count; i++) {
+ a[i] = value;
}
}
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i].intValue() > a[i + 1].intValue()) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i].intValue() != 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
-
- private static void checkSubArray(int[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
+ private static void writeValue(double[] a, double value, int fromIndex, int count) {
+ for (int i = fromIndex; i < fromIndex + count; i++) {
+ a[i] = value;
}
}
}
- private static void checkSubArray(byte[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (byte) 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (byte) 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
-
- private static void checkSubArray(long[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (long) 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (long) 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
+ private static Comparator<Pair> pairComparator = new Comparator<Pair>() {
- private static void checkSubArray(char[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (char) 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
+ @Override
+ public int compare(Pair p1, Pair p2) {
+ return p1.compareTo(p2);
}
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (char) 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
+ };
- private static void checkSubArray(short[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (short) 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
- }
+ private static class Pair implements Comparable<Pair> {
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (short) 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
-
- private static void checkSubArray(float[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (float) 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
+ private Pair(int key, int value) {
+ this.key = key;
+ this.value = value;
}
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (float) 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
- }
- }
-
- private static void checkSubArray(double[] a, int fromIndex, int toIndex, int m) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (double) 0xDEDA) {
- failed("Range sort changes left element on position " + i +
- ": " + a[i] + ", must be " + 0xDEDA);
- }
+ int getKey() {
+ return key;
}
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- failedSort(i, "" + a[i], "" + a[i + 1]);
- }
+ int getValue() {
+ return value;
}
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (double) 0xBABA) {
- failed("Range sort changes right element on position " + i +
- ": " + a[i] + ", must be " + 0xBABA);
- }
+ @Override
+ public int compareTo(Pair pair) {
+ return Integer.compare(key, pair.key);
}
- }
-
- private static void checkRange(Object object, int m) {
- if (object instanceof int[]) {
- checkRange((int[]) object, m);
- } else if (object instanceof long[]) {
- checkRange((long[]) object, m);
- } else if (object instanceof short[]) {
- checkRange((short[]) object, m);
- } else if (object instanceof byte[]) {
- checkRange((byte[]) object, m);
- } else if (object instanceof char[]) {
- checkRange((char[]) object, m);
- } else if (object instanceof float[]) {
- checkRange((float[]) object, m);
- } else if (object instanceof double[]) {
- checkRange((double[]) object, m);
- } else if (object instanceof Integer[]) {
- checkRange((Integer[]) object, m);
- } else {
- failed("Unknow type of array: " + object + " of class " +
- object.getClass().getName());
- }
- }
-
- private static void checkRange(Integer[] a, int m) {
- try {
- Arrays.sort(a, m + 1, m);
-
- failed("Sort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.sort(a, -m, a.length);
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.sort(a, 0, a.length + m);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
+ @Override
+ public String toString() {
+ return "(" + key + ", " + value + ")";
}
- }
-
- private static void checkRange(int[] a, int m) {
- try {
- Arrays.sort(a, m + 1, m);
- failed("Sort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.sort(a, -m, a.length);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.sort(a, 0, a.length + m);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
+ private int key;
+ private int value;
}
- private static void checkRange(long[] a, int m) {
- try {
- Arrays.sort(a, m + 1, m);
-
- failed("Sort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.sort(a, -m, a.length);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.sort(a, 0, a.length + m);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void checkRange(byte[] a, int m) {
- try {
- Arrays.sort(a, m + 1, m);
-
- failed("Sort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.sort(a, -m, a.length);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.sort(a, 0, a.length + m);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void checkRange(short[] a, int m) {
- try {
- Arrays.sort(a, m + 1, m);
-
- failed("Sort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.sort(a, -m, a.length);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.sort(a, 0, a.length + m);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void checkRange(char[] a, int m) {
- try {
- Arrays.sort(a, m + 1, m);
-
- failed("Sort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.sort(a, -m, a.length);
+ private static class TestRandom extends Random {
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.sort(a, 0, a.length + m);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void checkRange(float[] a, int m) {
- try {
- Arrays.sort(a, m + 1, m);
-
- failed("Sort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.sort(a, -m, a.length);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.sort(a, 0, a.length + m);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void checkRange(double[] a, int m) {
- try {
- Arrays.sort(a, m + 1, m);
+ private static final TestRandom BABA = new TestRandom(0xBABA);
+ private static final TestRandom DEDA = new TestRandom(0xDEDA);
+ private static final TestRandom C0FFEE = new TestRandom(0xC0FFEE);
- failed("Sort does not throw IllegalArgumentException " +
- " as expected: fromIndex = " + (m + 1) +
- " toIndex = " + m);
- }
- catch (IllegalArgumentException iae) {
- try {
- Arrays.sort(a, -m, a.length);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: fromIndex = " + (-m));
- }
- catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- Arrays.sort(a, 0, a.length + m);
-
- failed("Sort does not throw ArrayIndexOutOfBoundsException " +
- " as expected: toIndex = " + (a.length + m));
- }
- catch (ArrayIndexOutOfBoundsException aie) {
- return;
- }
- }
- }
- }
-
- private static void outArray(Object[] a) {
- for (int i = 0; i < a.length; i++) {
- out.print(a[i] + " ");
- }
- out.println();
- }
-
- private static void outArray(int[] a) {
- for (int i = 0; i < a.length; i++) {
- out.print(a[i] + " ");
- }
- out.println();
- }
-
- private static void outArray(float[] a) {
- for (int i = 0; i < a.length; i++) {
- out.print(a[i] + " ");
- }
- out.println();
- }
-
- private static void outArray(double[] a) {
- for (int i = 0; i < a.length; i++) {
- out.print(a[i] + " ");
- }
- out.println();
- }
-
- private static class MyRandom extends Random {
- MyRandom(long seed) {
+ private TestRandom(long seed) {
super(seed);
- mySeed = seed;
+ this.seed = Long.toHexString(seed).toUpperCase();
}
- long getSeed() {
- return mySeed;
+ @Override
+ public String toString() {
+ return seed;
}
- private long mySeed;
+ private String seed;
}
-
- private static String ourDescription;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/util/Arrays/java.base/java/util/SortingHelper.java Wed Nov 13 17:21:31 2019 -0500
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2019, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 java.util;
+
+/**
+ * This class provides access to package-private
+ * methods of DualPivotQuicksort class.
+ *
+ * @author Vladimir Yaroslavskiy
+ *
+ * @version 2019.09.19
+ *
+ * @since 14
+ */
+public enum SortingHelper {
+
+ DUAL_PIVOT_QUICKSORT("Dual-Pivot Quicksort") {
+
+ @Override
+ public void sort(Object a) {
+ if (a instanceof int[]) {
+ DualPivotQuicksort.sort((int[]) a, SEQUENTIAL, 0, ((int[]) a).length);
+ } else if (a instanceof long[]) {
+ DualPivotQuicksort.sort((long[]) a, SEQUENTIAL, 0, ((long[]) a).length);
+ } else if (a instanceof byte[]) {
+ DualPivotQuicksort.sort((byte[]) a, 0, ((byte[]) a).length);
+ } else if (a instanceof char[]) {
+ DualPivotQuicksort.sort((char[]) a, SEQUENTIAL, 0, ((char[]) a).length);
+ } else if (a instanceof short[]) {
+ DualPivotQuicksort.sort((short[]) a, SEQUENTIAL, 0, ((short[]) a).length);
+ } else if (a instanceof float[]) {
+ DualPivotQuicksort.sort((float[]) a, SEQUENTIAL, 0, ((float[]) a).length);
+ } else if (a instanceof double[]) {
+ DualPivotQuicksort.sort((double[]) a, SEQUENTIAL, 0, ((double[]) a).length);
+ } else {
+ fail(a);
+ }
+ }
+
+ @Override
+ public void sort(Object a, int low, int high) {
+ if (a instanceof int[]) {
+ DualPivotQuicksort.sort((int[]) a, SEQUENTIAL, low, high);
+ } else if (a instanceof long[]) {
+ DualPivotQuicksort.sort((long[]) a, SEQUENTIAL, low, high);
+ } else if (a instanceof byte[]) {
+ DualPivotQuicksort.sort((byte[]) a, low, high);
+ } else if (a instanceof char[]) {
+ DualPivotQuicksort.sort((char[]) a, SEQUENTIAL, low, high);
+ } else if (a instanceof short[]) {
+ DualPivotQuicksort.sort((short[]) a, SEQUENTIAL, low, high);
+ } else if (a instanceof float[]) {
+ DualPivotQuicksort.sort((float[]) a, SEQUENTIAL, low, high);
+ } else if (a instanceof double[]) {
+ DualPivotQuicksort.sort((double[]) a, SEQUENTIAL, low, high);
+ } else {
+ fail(a);
+ }
+ }
+
+ @Override
+ public void sort(Object[] a) {
+ fail(a);
+ }
+
+ @Override
+ public void sort(Object[] a, Comparator comparator) {
+ fail(a);
+ }
+ },
+
+ PARALLEL_SORT("Parallel sort") {
+
+ @Override
+ public void sort(Object a) {
+ if (a instanceof int[]) {
+ DualPivotQuicksort.sort((int[]) a, PARALLEL, 0, ((int[]) a).length);
+ } else if (a instanceof long[]) {
+ DualPivotQuicksort.sort((long[]) a, PARALLEL, 0, ((long[]) a).length);
+ } else if (a instanceof byte[]) {
+ DualPivotQuicksort.sort((byte[]) a, 0, ((byte[]) a).length);
+ } else if (a instanceof char[]) {
+ DualPivotQuicksort.sort((char[]) a, PARALLEL, 0, ((char[]) a).length);
+ } else if (a instanceof short[]) {
+ DualPivotQuicksort.sort((short[]) a, PARALLEL, 0, ((short[]) a).length);
+ } else if (a instanceof float[]) {
+ DualPivotQuicksort.sort((float[]) a, PARALLEL, 0, ((float[]) a).length);
+ } else if (a instanceof double[]) {
+ DualPivotQuicksort.sort((double[]) a, PARALLEL, 0, ((double[]) a).length);
+ } else {
+ fail(a);
+ }
+ }
+
+ @Override
+ public void sort(Object a, int low, int high) {
+ if (a instanceof int[]) {
+ DualPivotQuicksort.sort((int[]) a, PARALLEL, low, high);
+ } else if (a instanceof long[]) {
+ DualPivotQuicksort.sort((long[]) a, PARALLEL, low, high);
+ } else if (a instanceof byte[]) {
+ DualPivotQuicksort.sort((byte[]) a, low, high);
+ } else if (a instanceof char[]) {
+ DualPivotQuicksort.sort((char[]) a, PARALLEL, low, high);
+ } else if (a instanceof short[]) {
+ DualPivotQuicksort.sort((short[]) a, PARALLEL, low, high);
+ } else if (a instanceof float[]) {
+ DualPivotQuicksort.sort((float[]) a, PARALLEL, low, high);
+ } else if (a instanceof double[]) {
+ DualPivotQuicksort.sort((double[]) a, PARALLEL, low, high);
+ } else {
+ fail(a);
+ }
+ }
+
+ @Override
+ public void sort(Object[] a) {
+ fail(a);
+ }
+
+ @Override
+ public void sort(Object[] a, Comparator comparator) {
+ fail(a);
+ }
+ },
+
+ HEAP_SORT("Heap sort") {
+
+ @Override
+ public void sort(Object a) {
+ if (a instanceof int[]) {
+ DualPivotQuicksort.sort(null, (int[]) a, BIG_DEPTH, 0, ((int[]) a).length);
+ } else if (a instanceof long[]) {
+ DualPivotQuicksort.sort(null, (long[]) a, BIG_DEPTH, 0, ((long[]) a).length);
+ } else if (a instanceof byte[]) {
+ DualPivotQuicksort.sort((byte[]) a, 0, ((byte[]) a).length);
+ } else if (a instanceof char[]) {
+ DualPivotQuicksort.sort((char[]) a, BIG_DEPTH, 0, ((char[]) a).length);
+ } else if (a instanceof short[]) {
+ DualPivotQuicksort.sort((short[]) a, BIG_DEPTH, 0, ((short[]) a).length);
+ } else if (a instanceof float[]) {
+ DualPivotQuicksort.sort(null, (float[]) a, BIG_DEPTH, 0, ((float[]) a).length);
+ } else if (a instanceof double[]) {
+ DualPivotQuicksort.sort(null, (double[]) a, BIG_DEPTH, 0, ((double[]) a).length);
+ } else {
+ fail(a);
+ }
+ }
+
+ @Override
+ public void sort(Object a, int low, int high) {
+ if (a instanceof int[]) {
+ DualPivotQuicksort.sort(null, (int[]) a, BIG_DEPTH, low, high);
+ } else if (a instanceof long[]) {
+ DualPivotQuicksort.sort(null, (long[]) a, BIG_DEPTH, low, high);
+ } else if (a instanceof byte[]) {
+ DualPivotQuicksort.sort((byte[]) a, low, high);
+ } else if (a instanceof char[]) {
+ DualPivotQuicksort.sort((char[]) a, BIG_DEPTH, low, high);
+ } else if (a instanceof short[]) {
+ DualPivotQuicksort.sort((short[]) a, BIG_DEPTH, low, high);
+ } else if (a instanceof float[]) {
+ DualPivotQuicksort.sort(null, (float[]) a, BIG_DEPTH, low, high);
+ } else if (a instanceof double[]) {
+ DualPivotQuicksort.sort(null, (double[]) a, BIG_DEPTH, low, high);
+ } else {
+ fail(a);
+ }
+ }
+
+ @Override
+ public void sort(Object[] a) {
+ fail(a);
+ }
+
+ @Override
+ public void sort(Object[] a, Comparator comparator) {
+ fail(a);
+ }
+ },
+
+ ARRAYS_SORT("Arrays.sort") {
+
+ @Override
+ public void sort(Object a) {
+ if (a instanceof int[]) {
+ Arrays.sort((int[]) a);
+ } else if (a instanceof long[]) {
+ Arrays.sort((long[]) a);
+ } else if (a instanceof byte[]) {
+ Arrays.sort((byte[]) a);
+ } else if (a instanceof char[]) {
+ Arrays.sort((char[]) a);
+ } else if (a instanceof short[]) {
+ Arrays.sort((short[]) a);
+ } else if (a instanceof float[]) {
+ Arrays.sort((float[]) a);
+ } else if (a instanceof double[]) {
+ Arrays.sort((double[]) a);
+ } else {
+ fail(a);
+ }
+ }
+
+ @Override
+ public void sort(Object a, int low, int high) {
+ if (a instanceof int[]) {
+ Arrays.sort((int[]) a, low, high);
+ } else if (a instanceof long[]) {
+ Arrays.sort((long[]) a, low, high);
+ } else if (a instanceof byte[]) {
+ Arrays.sort((byte[]) a, low, high);
+ } else if (a instanceof char[]) {
+ Arrays.sort((char[]) a, low, high);
+ } else if (a instanceof short[]) {
+ Arrays.sort((short[]) a, low, high);
+ } else if (a instanceof float[]) {
+ Arrays.sort((float[]) a, low, high);
+ } else if (a instanceof double[]) {
+ Arrays.sort((double[]) a, low, high);
+ } else {
+ fail(a);
+ }
+ }
+
+ @Override
+ public void sort(Object[] a) {
+ Arrays.sort(a);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void sort(Object[] a, Comparator comparator) {
+ Arrays.sort(a, comparator);
+ }
+ },
+
+ ARRAYS_PARALLEL_SORT("Arrays.parallelSort") {
+
+ @Override
+ public void sort(Object a) {
+ if (a instanceof int[]) {
+ Arrays.parallelSort((int[]) a);
+ } else if (a instanceof long[]) {
+ Arrays.parallelSort((long[]) a);
+ } else if (a instanceof byte[]) {
+ Arrays.parallelSort((byte[]) a);
+ } else if (a instanceof char[]) {
+ Arrays.parallelSort((char[]) a);
+ } else if (a instanceof short[]) {
+ Arrays.parallelSort((short[]) a);
+ } else if (a instanceof float[]) {
+ Arrays.parallelSort((float[]) a);
+ } else if (a instanceof double[]) {
+ Arrays.parallelSort((double[]) a);
+ } else {
+ fail(a);
+ }
+ }
+
+ @Override
+ public void sort(Object a, int low, int high) {
+ if (a instanceof int[]) {
+ Arrays.parallelSort((int[]) a, low, high);
+ } else if (a instanceof long[]) {
+ Arrays.parallelSort((long[]) a, low, high);
+ } else if (a instanceof byte[]) {
+ Arrays.parallelSort((byte[]) a, low, high);
+ } else if (a instanceof char[]) {
+ Arrays.parallelSort((char[]) a, low, high);
+ } else if (a instanceof short[]) {
+ Arrays.parallelSort((short[]) a, low, high);
+ } else if (a instanceof float[]) {
+ Arrays.parallelSort((float[]) a, low, high);
+ } else if (a instanceof double[]) {
+ Arrays.parallelSort((double[]) a, low, high);
+ } else {
+ fail(a);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void sort(Object[] a) {
+ Arrays.parallelSort((Comparable[]) a);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void sort(Object[] a, Comparator comparator) {
+ Arrays.parallelSort(a, comparator);
+ }
+ };
+
+ abstract public void sort(Object a);
+
+ abstract public void sort(Object a, int low, int high);
+
+ abstract public void sort(Object[] a);
+
+ abstract public void sort(Object[] a, Comparator comparator);
+
+ private SortingHelper(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ private static void fail(Object a) {
+ throw new RuntimeException("Unexpected type of array: " + a.getClass().getName());
+ }
+
+ private String name;
+
+ /**
+ * Parallelism level for sequential and parallel sorting.
+ */
+ private static final int SEQUENTIAL = 0;
+ private static final int PARALLEL = 87;
+
+ /**
+ * Heap sort will be invoked, if recursion depth is too big.
+ * Value is taken from DualPivotQuicksort.MAX_RECURSION_DEPTH.
+ */
+ private static final int BIG_DEPTH = 64 * (3 << 1);
+}
--- a/test/jdk/java/util/Locale/LocaleProviders.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/java/util/Locale/LocaleProviders.java Wed Nov 13 17:21:31 2019 -0500
@@ -24,6 +24,7 @@
import java.text.spi.*;
import java.util.*;
import java.util.spi.*;
+import java.util.stream.IntStream;
import sun.util.locale.provider.LocaleProviderAdapter;
public class LocaleProviders {
@@ -87,6 +88,10 @@
bug8232871Test();
break;
+ case "bug8232860Test":
+ bug8232860Test();
+ break;
+
default:
throw new RuntimeException("Test method '"+methodName+"' not found.");
}
@@ -327,4 +332,42 @@
"native calendar is not JapaneseCalendar: " + calType);
}
}
+
+ static void bug8232860Test() {
+ var inputList = List.of(123, 123.4);
+ var nfExpectedList = List.of("123", "123.4");
+ var ifExpectedList = List.of("123", "123");
+
+ var type = LocaleProviderAdapter.getAdapter(CalendarNameProvider.class, Locale.US)
+ .getAdapterType();
+ if (type == LocaleProviderAdapter.Type.HOST && (IS_WINDOWS || IS_MAC)) {
+ final var numf = NumberFormat.getNumberInstance(Locale.US);
+ final var intf = NumberFormat.getIntegerInstance(Locale.US);
+
+ IntStream.range(0, inputList.size())
+ .forEach(i -> {
+ var input = inputList.get(i);
+ var nfExpected = nfExpectedList.get(i);
+ var result = numf.format(input);
+ if (!result.equals(nfExpected)) {
+ throw new RuntimeException("Incorrect number format. " +
+ "input: " + input + ", expected: " +
+ nfExpected + ", result: " + result);
+ }
+
+ var ifExpected = ifExpectedList.get(i);
+ result = intf.format(input);
+ if (!result.equals(ifExpected)) {
+ throw new RuntimeException("Incorrect integer format. " +
+ "input: " + input + ", expected: " +
+ ifExpected + ", result: " + result);
+ }
+ });
+ System.out.println("bug8232860Test succeeded.");
+ } else {
+ System.out.println("Test ignored. Either :-\n" +
+ "OS is neither macOS/Windows, or\n" +
+ "provider is not HOST: " + type);
+ }
+ }
}
--- a/test/jdk/java/util/Locale/LocaleProvidersRun.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/java/util/Locale/LocaleProvidersRun.java Wed Nov 13 17:21:31 2019 -0500
@@ -25,7 +25,7 @@
* @test
* @bug 6336885 7196799 7197573 7198834 8000245 8000615 8001440 8008577
* 8010666 8013086 8013233 8013903 8015960 8028771 8054482 8062006
- * 8150432 8215913 8220227 8228465 8232871
+ * 8150432 8215913 8220227 8228465 8232871 8232860
* @summary tests for "java.locale.providers" system property
* @library /test/lib
* @build LocaleProviders
@@ -159,6 +159,9 @@
//testing 8232871 fix. (macOS only)
testRun("HOST", "bug8232871Test", "", "", "");
+
+ //testing 8232860 fix. (macOS/Windows only)
+ testRun("HOST", "bug8232860Test", "", "", "");
}
private static void testRun(String prefList, String methodName,
--- a/test/jdk/java/util/zip/ZipFile/ZipFileInputStreamSkipTest.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/java/util/zip/ZipFile/ZipFileInputStreamSkipTest.java Wed Nov 13 17:21:31 2019 -0500
@@ -105,7 +105,7 @@
* @throws Exception If an error occurs during the test
*/
@Test
- private void testStoredSkip() throws Exception {
+ public void testStoredSkip() throws Exception {
try (ZipFile zf = new ZipFile(STORED_ZIPFILE.toFile())) {
var entries = zf.entries();
@@ -153,7 +153,7 @@
* @throws Exception If an error occurs during the test
*/
@Test
- private void testStoredNegativeSkip() throws Exception {
+ public void testStoredNegativeSkip() throws Exception {
try (ZipFile zf = new ZipFile(STORED_ZIPFILE.toFile())) {
var entries = zf.entries();
@@ -198,7 +198,7 @@
* @throws Exception If an error occurs during the test
*/
@Test
- private void testDeflatedSkip() throws Exception {
+ public void testDeflatedSkip() throws Exception {
try (ZipFile zf = new ZipFile(DEFLATED_ZIPFILE.toFile())) {
var toSkip = 5; // Bytes to Skip
var entries = zf.entries();
@@ -225,7 +225,7 @@
* @throws Exception If an unexpected error occurs during the test
*/
@Test
- private void testDeflatedIOException() throws Exception {
+ public void testDeflatedIOException() throws Exception {
try (ZipFile zf = new ZipFile(DEFLATED_ZIPFILE.toFile())) {
var entries = zf.entries();
while (entries.hasMoreElements()) {
--- a/test/jdk/jdk/jfr/event/gc/collection/GCEventAll.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/jdk/jfr/event/gc/collection/GCEventAll.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -125,7 +125,7 @@
// recording.stop and getBeanCollectionCount().
doSystemGc();
// Add an extra System.gc() to make sure we get at least one full garbage_collection batch at
- // the end of the test. This extra System.gc() is only necessary when using "UseConcMarkSweepGC" and "+ExplicitGCInvokesConcurrent".
+ // the end of the test. This extra System.gc() is only necessary when using "+ExplicitGCInvokesConcurrent".
doSystemGc();
recording.stop();
@@ -170,7 +170,6 @@
// For some GC configurations, the JFR recording may have stopped before we received the last gc event.
try {
- events = filterIncompleteGcBatch(events);
gcBatches = GCHelper.GcBatch.createFromEvents(events);
eventCounts = GCHelper.CollectionSummary.createFromEvents(gcBatches);
@@ -191,41 +190,6 @@
}
}
- /**
- * When using collector ConcurrentMarkSweep with -XX:+ExplicitGCInvokesConcurrent, the JFR recording may
- * stop before we have received the last garbage_collection event.
- *
- * This function does 3 things:
- * 1. Check if the last batch is incomplete.
- * 2. If it is incomplete, then asserts that incomplete batches are allowed for this configuration.
- * 3. If incomplete batches are allowed, then the incomplete batch is removed.
- *
- * @param events All events
- * @return All events with any incomplete batch removed.
- * @throws Throwable
- */
- private List<RecordedEvent> filterIncompleteGcBatch(List<RecordedEvent> events) throws Throwable {
- List<RecordedEvent> returnEvents = new ArrayList<RecordedEvent>(events);
- int lastGcId = getLastGcId(events);
- List<RecordedEvent> lastBatchEvents = getEventsWithGcId(events, lastGcId);
- String[] endEvents = {GCHelper.event_garbage_collection, GCHelper.event_old_garbage_collection, GCHelper.event_young_garbage_collection};
- boolean isComplete = containsAnyPath(lastBatchEvents, endEvents);
- if (!isComplete) {
- // The last GC batch does not contain an end event. The batch is incomplete.
- // This is only allowed if we are using old_collector="ConcurrentMarkSweep" and "-XX:+ExplicitGCInvokesConcurrent"
- boolean isExplicitGCInvokesConcurrent = hasInputArgument("-XX:+ExplicitGCInvokesConcurrent");
- boolean isConcurrentMarkSweep = GCHelper.gcConcurrentMarkSweep.equals(oldCollector);
- String msg = String.format(
- "Incomplete batch only allowed for '%s' with -XX:+ExplicitGCInvokesConcurrent",
- GCHelper.gcConcurrentMarkSweep);
- Asserts.assertTrue(isConcurrentMarkSweep && isExplicitGCInvokesConcurrent, msg);
-
- // Incomplete batch is allowed with the current settings. Remove incomplete batch.
- returnEvents.removeAll(lastBatchEvents);
- }
- return returnEvents;
- }
-
private boolean hasInputArgument(String arg) {
return ManagementFactory.getRuntimeMXBean().getInputArguments().contains(arg);
}
@@ -276,8 +240,7 @@
}
private void verifyCollectionCount(String collector, long eventCounts, long beanCounts) {
- if (GCHelper.gcConcurrentMarkSweep.equals(collector) || GCHelper.gcG1Old.equals(oldCollector)) {
- // ConcurrentMarkSweep mixes old and new collections. Not same values as in MXBean.
+ if (GCHelper.gcG1Old.equals(oldCollector)) {
// MXBean does not report old collections for G1Old, so we have nothing to compare with.
return;
}
@@ -338,11 +301,8 @@
}
}
}
- if (!GCHelper.gcConcurrentMarkSweep.equals(batch.getName())) {
- // We do not get heap_summary events for ConcurrentMarkSweep
- Asserts.assertEquals(1, countBeforeGc, "Unexpected number of heap_summary.before_gc");
- Asserts.assertEquals(1, countAfterGc, "Unexpected number of heap_summary.after_gc");
- }
+ Asserts.assertEquals(1, countBeforeGc, "Unexpected number of heap_summary.before_gc");
+ Asserts.assertEquals(1, countAfterGc, "Unexpected number of heap_summary.after_gc");
} catch (Throwable e) {
GCHelper.log("verifySingleGcBatch failed for gcEvent:");
GCHelper.log(batch.getLog());
--- a/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java Wed Nov 13 17:21:31 2019 -0500
@@ -176,6 +176,9 @@
public static void provokeMixedGC(int g1HeapRegionSize) {
final var arraySize = 20_000;
var liveOldObjects = new ArrayList<byte[]>();
+
+ // Make sure the heap is in a known state.
+ getWhiteBox().fullGC();
allocateOldObjects(liveOldObjects, g1HeapRegionSize, arraySize);
waitTillCMCFinished(10);
getWhiteBox().g1StartConcMarkCycle();
--- a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithCMSConcurrent.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.collection;
-import jdk.test.lib.jfr.GCHelper;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * @requires vm.opt.ExplicitGCInvokesConcurrent != false
- * @library /test/lib /test/jdk
- * @run driver jdk.jfr.event.gc.collection.TestGCCauseWithCMSConcurrent
- */
-public class TestGCCauseWithCMSConcurrent {
- public static void main(String[] args) throws Exception {
- String testID = "CMSConcurrent";
- String[] vmFlags = {"-XX:+UseConcMarkSweepGC", "-XX:+ExplicitGCInvokesConcurrent"};
- String[] gcNames = {GCHelper.gcConcurrentMarkSweep, GCHelper.gcParNew, GCHelper.gcSerialOld};
- String[] gcCauses = {"CMS Concurrent Mark", "Allocation Failure", "System.gc()"};
- GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses);
- }
-}
-
--- a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithCMSMarkSweep.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.collection;
-import jdk.test.lib.jfr.GCHelper;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- *
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * @requires vm.opt.ExplicitGCInvokesConcurrent != true
- * @library /test/lib /test/jdk
- * @run driver jdk.jfr.event.gc.collection.TestGCCauseWithCMSMarkSweep
- */
-public class TestGCCauseWithCMSMarkSweep {
- public static void main(String[] args) throws Exception {
- String testID = "CMSMarkSweep";
- String[] vmFlags = {"-XX:+UseConcMarkSweepGC"};
- String[] gcNames = {GCHelper.gcConcurrentMarkSweep, GCHelper.gcParNew, GCHelper.gcSerialOld};
- String[] gcCauses = {"CMS Concurrent Mark", "Allocation Failure", "System.gc()"};
- GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses);
- }
-}
-
--- a/test/jdk/jdk/jfr/event/gc/collection/TestGCEventMixedWithCMSConcurrent.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.collection;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- *
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * & vm.opt.ExplicitGCInvokesConcurrent != false
- * @library /test/lib /test/jdk
- *
- * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xmx32m -Xmn8m -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrent jdk.jfr.event.gc.collection.TestGCEventMixedWithCMSConcurrent
- * good debug flags: -Xlog:gc+heap=trace,gc*=debug
- */
-public class TestGCEventMixedWithCMSConcurrent {
- public static void main(String[] args) throws Throwable {
- GCEventAll.doTest();
- }
-}
--- a/test/jdk/jdk/jfr/event/gc/collection/TestGCEventMixedWithCMSMarkSweep.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.collection;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- *
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * & vm.opt.ExplicitGCInvokesConcurrent != true
- * @library /test/lib /test/jdk
- *
- * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xmx32m -Xmn8m -XX:+UseConcMarkSweepGC -XX:-ExplicitGCInvokesConcurrent jdk.jfr.event.gc.collection.TestGCEventMixedWithCMSMarkSweep
- * good debug flags: -Xlog:gc+heap=trace,gc*=debug
- */
-public class TestGCEventMixedWithCMSMarkSweep {
- public static void main(String[] args) throws Throwable {
- GCEventAll.doTest();
- }
-}
--- a/test/jdk/jdk/jfr/event/gc/collection/TestGCEventMixedWithParNew.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.collection;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- *
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * @library /test/lib /test/jdk
- * @run main/othervm -Xmx32m -Xmn8m -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseConcMarkSweepGC jdk.jfr.event.gc.collection.TestGCEventMixedWithParNew
- * good debug flags: -Xlog:gc*=debug
- */
-
-public class TestGCEventMixedWithParNew {
- public static void main(String[] args) throws Throwable {
- GCEventAll.doTest();
- }
-}
--- a/test/jdk/jdk/jfr/event/gc/collection/TestYoungGarbageCollectionEventWithParNew.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.collection;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * @library /test/lib /test/jdk
- * @run main/othervm -Xmx50m -Xmn2m -XX:+UseConcMarkSweepGC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xlog:gc+heap=trace,gc*=debug jdk.jfr.event.gc.collection.TestYoungGarbageCollectionEventWithParNew
- */
-public class TestYoungGarbageCollectionEventWithParNew {
-
- public static void main(String[] args) throws Exception {
- YoungGarbageCollectionEvent.test();
- }
-
-}
--- a/test/jdk/jdk/jfr/event/gc/detailed/PromotionEvent.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/jdk/jfr/event/gc/detailed/PromotionEvent.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -69,8 +69,7 @@
List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcBean : gcBeans) {
if ("PS Scavenge".equals(gcBean.getName())
- || "G1 Young Generation".equals(gcBean.getName())
- || ("ParNew".equals(gcBean.getName()))) {
+ || "G1 Young Generation".equals(gcBean.getName())) {
ycBean = gcBean;
}
--- a/test/jdk/jdk/jfr/event/gc/detailed/TestCMSConcurrentModeFailureEvent.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.detailed;
-
-import java.io.IOException;
-import java.io.File;
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Optional;
-
-import jdk.jfr.consumer.RecordedEvent;
-import jdk.jfr.consumer.RecordingFile;
-import jdk.test.lib.Asserts;
-import jdk.test.lib.jfr.EventNames;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- *
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * @library /test/lib /test/jdk
- *
- * @run main jdk.jfr.event.gc.detailed.TestCMSConcurrentModeFailureEvent
- */
-public class TestCMSConcurrentModeFailureEvent {
-
- private final static String EVENT_NAME = EventNames.ConcurrentModeFailure;
- private final static String EVENT_SETTINGS_FILE = System.getProperty("test.src", ".") + File.separator + "concurrentmodefailure-testsettings.jfc";
- private final static String JFR_FILE = "TestCMSConcurrentModeFailureEvent.jfr";
- private final static int BYTES_TO_ALLOCATE = 1024 * 512;
-
- public static void main(String[] args) throws Exception {
- String[] vmFlags = {"-Xmx128m", "-XX:MaxTenuringThreshold=0", "-Xlog:gc*=debug:testCMSGC.log",
- "-XX:+UseConcMarkSweepGC", "-XX:+UnlockExperimentalVMOptions", "-XX:-UseFastUnorderedTimeStamps"};
-
- if (!ExecuteOOMApp.execute(EVENT_SETTINGS_FILE, JFR_FILE, vmFlags, BYTES_TO_ALLOCATE)) {
- System.out.println("OOM happened in the other thread(not test thread). Skip test.");
- // Skip test, process terminates due to the OOME error in the different thread
- return;
- }
-
- Optional<RecordedEvent> event = RecordingFile.readAllEvents(Paths.get(JFR_FILE)).stream().findFirst();
- if (event.isPresent()) {
- Asserts.assertEquals(EVENT_NAME, event.get().getEventType().getName(), "Wrong event type");
- } else {
- // No event received. Check if test did trigger the event.
- boolean isEventTriggered = fileContainsString("testCMSGC.log", "concurrent mode failure");
- System.out.println("isEventTriggered=" +isEventTriggered);
- Asserts.assertFalse(isEventTriggered, "Event found in log, but not in JFR");
- }
- }
-
- private static boolean fileContainsString(String filename, String text) throws IOException {
- Path p = Paths.get(filename);
- for (String line : Files.readAllLines(p, Charset.defaultCharset())) {
- if (line.contains(text)) {
- return true;
- }
- }
- return false;
- }
-}
--- a/test/jdk/jdk/jfr/event/gc/detailed/TestPromotionFailedEventWithParNew.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.detailed;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * @library /test/lib /test/jdk
- * @run main/othervm jdk.jfr.event.gc.detailed.TestPromotionFailedEventWithParNew
- */
-public class TestPromotionFailedEventWithParNew {
-
- public static void main(String[] args) throws Throwable {
- PromotionFailedEvent.test("TestPromotionFailedEventWithParNew",
- new String[]{"-Xmx32m", "-Xmn30m", "-XX:-UseDynamicNumberOfGCThreads",
- "-XX:ParallelGCThreads=3", "-XX:MaxTenuringThreshold=0",
- "-Xlog:gc*=debug", "-XX:+UseConcMarkSweepGC",
- "-XX:+UnlockExperimentalVMOptions", "-XX:-UseFastUnorderedTimeStamps"});
- }
-
-}
--- a/test/jdk/jdk/jfr/event/gc/heapsummary/TestHeapSummaryEventConcurrentCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.heapsummary;
-
-import java.time.Duration;
-import java.util.List;
-
-import jdk.jfr.Recording;
-import jdk.jfr.consumer.RecordedEvent;
-import jdk.test.lib.Asserts;
-import jdk.test.lib.jfr.EventNames;
-import jdk.test.lib.jfr.Events;
-import jdk.test.lib.jfr.GCHelper;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * & vm.opt.ExplicitGCInvokesConcurrent != false
- * @library /test/lib /test/jdk
- * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrent jdk.jfr.event.gc.heapsummary.TestHeapSummaryEventConcurrentCMS
- */
-public class TestHeapSummaryEventConcurrentCMS {
-
- public static void main(String[] args) throws Exception {
- Recording recording = new Recording();
- recording.enable(EventNames.GarbageCollection).withThreshold(Duration.ofMillis(0));
- recording.enable(EventNames.GCHeapSummary).withThreshold(Duration.ofMillis(0));
-
- recording.start();
- // Need several GCs to ensure at least one heap summary event from concurrent CMS
- GCHelper.callSystemGc(6, true);
- recording.stop();
-
- // Remove first and last GCs which can be incomplete
- List<RecordedEvent> events = GCHelper.removeFirstAndLastGC(Events.fromRecording(recording));
- Asserts.assertFalse(events.isEmpty(), "No events found");
- for (RecordedEvent event : events) {
- System.out.println("Event: " + event);
- if (!isCmsGcEvent(event)) {
- continue;
- }
- int gcId = Events.assertField(event, "gcId").getValue();
- verifyHeapSummary(events, gcId, "Before GC");
- verifyHeapSummary(events, gcId, "After GC");
- }
- }
-
- private static boolean isCmsGcEvent(RecordedEvent event) {
- if (!Events.isEventType(event, EventNames.GarbageCollection)) {
- return false;
- }
- final String gcName = Events.assertField(event, "name").notEmpty().getValue();
- return "ConcurrentMarkSweep".equals(gcName);
- }
-
- private static void verifyHeapSummary(List<RecordedEvent> events, int gcId, String when) {
- for (RecordedEvent event : events) {
- if (!Events.isEventType(event, EventNames.GCHeapSummary)) {
- continue;
- }
- if (gcId == (int)Events.assertField(event, "gcId").getValue() &&
- when.equals(Events.assertField(event, "when").getValue())) {
- System.out.printf("Found " + EventNames.GCHeapSummary + " for id=%d, when=%s%n", gcId, when);
- return;
- }
- }
- Asserts.fail(String.format("No " + EventNames.GCHeapSummary + " for id=%d, when=%s", gcId, when));
- }
-
-}
--- a/test/jdk/jdk/jfr/event/gc/heapsummary/TestHeapSummaryEventParNewCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.heapsummary;
-import jdk.test.lib.jfr.GCHelper;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * & vm.opt.ExplicitGCInvokesConcurrent != true
- * @library /test/lib /test/jdk
- * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseConcMarkSweepGC jdk.jfr.event.gc.heapsummary.TestHeapSummaryEventParNewCMS
- */
-public class TestHeapSummaryEventParNewCMS {
- public static void main(String[] args) throws Exception {
- HeapSummaryEventAllGcs.test(GCHelper.gcParNew, GCHelper.gcConcurrentMarkSweep);
- }
-}
--- a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithCMSConcurrent.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.objectcount;
-import jdk.test.lib.jfr.GCHelper;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * & vm.opt.ExplicitGCInvokesConcurrent != false
- * @library /test/lib /test/jdk
- * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrent -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithCMSConcurrent
- */
-public class TestObjectCountAfterGCEventWithCMSConcurrent {
- public static void main(String[] args) throws Exception {
- ObjectCountAfterGCEvent.test(GCHelper.gcConcurrentMarkSweep);
- }
-}
--- a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithCMSMarkSweep.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.objectcount;
-import jdk.test.lib.jfr.GCHelper;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * & vm.opt.ExplicitGCInvokesConcurrent != true
- * @library /test/lib /test/jdk
- * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseConcMarkSweepGC -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithCMSMarkSweep
- */
-public class TestObjectCountAfterGCEventWithCMSMarkSweep {
- public static void main(String[] args) throws Exception {
- ObjectCountAfterGCEvent.test(GCHelper.gcSerialOld);
- }
-}
--- a/test/jdk/jdk/jfr/event/gc/refstat/TestRefStatEventWithCMSConcurrent.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.refstat;
-import jdk.test.lib.jfr.GCHelper;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * & vm.opt.ExplicitGCInvokesConcurrent != false
- * @library /test/lib /test/jdk
- * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xlog:gc+heap=trace,gc*=debug -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrent jdk.jfr.event.gc.refstat.TestRefStatEventWithCMSConcurrent
- */
-public class TestRefStatEventWithCMSConcurrent {
- public static void main(String[] args) throws Exception {
- RefStatEvent.test(GCHelper.gcConcurrentMarkSweep);
- }
-}
--- a/test/jdk/jdk/jfr/event/gc/refstat/TestRefStatEventWithCMSMarkSweep.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.refstat;
-import jdk.test.lib.jfr.GCHelper;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- * @requires (vm.gc == "ConcMarkSweep" | vm.gc == null) & !vm.graal.enabled
- * & vm.opt.ExplicitGCInvokesConcurrent != true
- * @library /test/lib /test/jdk
- * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xlog:gc+heap=trace,gc*=debug -XX:+UseConcMarkSweepGC jdk.jfr.event.gc.refstat.TestRefStatEventWithCMSMarkSweep
- */
-public class TestRefStatEventWithCMSMarkSweep {
- public static void main(String[] args) throws Exception {
- RefStatEvent.test(GCHelper.gcSerialOld);
- }
-}
--- a/test/jdk/jdk/jfr/event/gc/stacktrace/AllocationStackTrace.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/jdk/jfr/event/gc/stacktrace/AllocationStackTrace.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -210,52 +210,6 @@
}
/**
- * Tests event stacktrace for young GC if -XX:+UseConcMarkSweepGC is used
- */
- public static void testParNewAllocEvent() throws Exception {
- GarbageCollectorMXBean bean = garbageCollectorMXBean("ParNew");
- MemoryAllocator memory = new EdenMemoryAllocator();
-
- String[] expectedStack = new String[]{
- "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
- "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testParNewAllocEvent"
- };
-
- testAllocEvent(bean, memory, expectedStack);
- }
-
- /**
- * Tests event stacktrace for old GC if -XX:+UseConcMarkSweepGC is used
- */
- public static void testConcMarkSweepAllocEvent() throws Exception {
- GarbageCollectorMXBean bean = garbageCollectorMXBean("ConcurrentMarkSweep");
- MemoryAllocator memory = new OldGenMemoryAllocator();
-
- String[] expectedStack = new String[]{
- "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
- "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testConcMarkSweepAllocEvent"
- };
-
- testAllocEvent(bean, memory, expectedStack);
- }
-
- /**
- * Tests event stacktrace during metaspace GC threshold if
- * -XX:+UseConcMarkSweepGC is used
- */
- public static void testMetaspaceConcMarkSweepGCAllocEvent() throws Exception {
- GarbageCollectorMXBean bean = garbageCollectorMXBean("ConcurrentMarkSweep");
- MemoryAllocator memory = new MetaspaceMemoryAllocator();
-
- String[] expectedStack = new String[]{
- "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
- "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testMetaspaceConcMarkSweepGCAllocEvent"
- };
-
- testAllocEvent(bean, memory, expectedStack);
- }
-
- /**
* Tests event stacktrace for young GC if -XX:+UseParallelGC is used
*/
public static void testParallelScavengeAllocEvent() throws Exception {
--- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestConcMarkSweepAllocationPendingStackTrace.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.stacktrace;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- *
- * @requires (vm.gc == "null" | vm.gc == "ConcMarkSweep") & !vm.graal.enabled
- * @library /test/lib /test/jdk
- * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseConcMarkSweepGC -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestConcMarkSweepAllocationPendingStackTrace
- */
-public class TestConcMarkSweepAllocationPendingStackTrace {
-
- public static void main(String[] args) throws Exception {
- AllocationStackTrace.testConcMarkSweepAllocEvent();
- }
-}
--- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceConcMarkSweepGCAllocationPendingStackTrace.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.stacktrace;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- *
- * @requires (vm.gc == "null" | vm.gc == "ConcMarkSweep") & !vm.graal.enabled
- * @requires !(vm.compMode == "Xcomp" & os.arch == "aarch64")
- * @library /test/lib /test/jdk
- * @run main/othervm -XX:+UseConcMarkSweepGC -XX:MaxMetaspaceSize=64M -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestMetaspaceConcMarkSweepGCAllocationPendingStackTrace
- */
-public class TestMetaspaceConcMarkSweepGCAllocationPendingStackTrace {
-
- public static void main(String[] args) throws Exception {
- AllocationStackTrace.testMetaspaceConcMarkSweepGCAllocEvent();
- }
-}
--- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestParNewAllocationPendingStackTrace.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.gc.stacktrace;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- *
- * @requires (vm.gc == "null" | vm.gc == "ConcMarkSweep") & !vm.graal.enabled
- * @library /test/lib /test/jdk
- * @run main/othervm -XX:+UseConcMarkSweepGC -Xlog:gc* -XX:+FlightRecorder jdk.jfr.event.gc.stacktrace.TestParNewAllocationPendingStackTrace
- */
-public class TestParNewAllocationPendingStackTrace {
-
- public static void main(String[] args) throws Exception {
- AllocationStackTrace.testParNewAllocEvent();
- }
-}
--- a/test/jdk/jdk/jfr/event/oldobject/TestCMS.java Tue Nov 12 10:25:55 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2018, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.event.oldobject;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import jdk.jfr.Recording;
-import jdk.jfr.consumer.RecordedEvent;
-import jdk.jfr.internal.test.WhiteBox;
-import jdk.test.lib.jfr.EventNames;
-import jdk.test.lib.jfr.Events;
-
-/**
- * @test
- * @key jfr
- * @requires vm.hasJFR
- * @requires vm.gc == "null" & !vm.graal.enabled
- * @summary Test leak profiler with CMS GC
- * @library /test/lib /test/jdk
- * @modules jdk.jfr/jdk.jfr.internal.test
- * @run main/othervm -XX:TLABSize=2k -XX:+UseConcMarkSweepGC jdk.jfr.event.oldobject.TestCMS
- */
-public class TestCMS {
-
- static private class FindMe {
- }
-
- public static List<FindMe[]> list = new ArrayList<>(OldObjects.MIN_SIZE);
-
- public static void main(String[] args) throws Exception {
- WhiteBox.setWriteAllObjectSamples(true);
-
- try (Recording r = new Recording()) {
- r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity");
- r.start();
- allocateFindMe();
- System.gc();
- r.stop();
- List<RecordedEvent> events = Events.fromRecording(r);
- System.out.println(events);
- if (OldObjects.countMatchingEvents(events, FindMe[].class, null, null, -1, "allocateFindMe") == 0) {
- throw new Exception("Could not find leak with " + FindMe[].class);
- }
- }
- }
-
- public static void allocateFindMe() {
- for (int i = 0; i < OldObjects.MIN_SIZE; i++) {
- // Allocate array to trigger sampling code path for interpreter / c1
- list.add(new FindMe[0]);
- }
- }
-
-}
--- a/test/jdk/jdk/jfr/event/oldobject/TestMetadataRetention.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/jdk/jfr/event/oldobject/TestMetadataRetention.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -88,9 +88,8 @@
allocatorThread = null;
// System.gc() will trigger class unloading if -XX:+ExplicitGCInvokesConcurrent
- // is NOT set. If this flag is set G1 will never unload classes on System.gc()
- // and CMS will not guarantee that all semantically dead classes will be
- // unloaded. As far as the "jfr" key guarantees no VM flags are set from the
+ // is NOT set. If this flag is set G1 will never unload classes on System.gc().
+ // As far as the "jfr" key guarantees no VM flags are set from the
// outside it should be enough with System.gc().
System.gc();
--- a/test/jdk/jdk/jfr/event/runtime/TestClassLoadingStatisticsEvent.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/jdk/jfr/event/runtime/TestClassLoadingStatisticsEvent.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -47,9 +47,8 @@
* the loadedClassCount and unloadedClassCount attributes are correct.
*
* System.gc() will trigger class unloading if -XX:+ExplicitGCInvokesConcurrent
- * is NOT set. If this flag is set G1 will never unload classes on System.gc()
- * and CMS will not guarantee that all semantically dead classes will be
- * unloaded. As far as the "jfr" key guarantees no VM flags are set from the
+ * is NOT set. If this flag is set G1 will never unload classes on System.gc().
+ * As far as the "jfr" key guarantees no VM flags are set from the
* outside it should be enough with System.gc().
*/
public class TestClassLoadingStatisticsEvent {
--- a/test/jdk/jdk/jfr/event/runtime/TestClassUnloadEvent.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/jdk/jfr/event/runtime/TestClassUnloadEvent.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -49,8 +49,7 @@
/**
* System.gc() will trigger class unloading if -XX:+ExplicitGCInvokesConcurrent is NOT set.
- * If this flag is set G1 will never unload classes on System.gc() and
- * CMS will not guarantee that all semantically dead classes will be unloaded.
+ * If this flag is set G1 will never unload classes on System.gc().
* As far as the "jfr" key guarantees no VM flags are set from the outside
* it should be enough with System.gc().
*/
--- a/test/jdk/jdk/jfr/event/runtime/TestVmFlagChangedEvent.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/jdk/jdk/jfr/event/runtime/TestVmFlagChangedEvent.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -51,7 +51,6 @@
public static void main(String[] args) throws Throwable {
EventFlag[] eventFlags = {
- new EventFlag(EventNames.LongFlagChanged, "CMSWaitDuration", "2500"),
new EventFlag(EventNames.StringFlagChanged, "HeapDumpPath", "/a/sample/path"),
new EventFlag(EventNames.BooleanFlagChanged, "HeapDumpOnOutOfMemoryError", "true")
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/api/TestModuleUnnamedPackage.java Wed Nov 13 17:21:31 2019 -0500
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @bug 8234025
+ * @summary Elements.getPackageElement(ModuleElement,CharSequence) returns null for unnamed package
+ * @modules jdk.compiler
+ * @library /tools/lib /tools/javac/lib
+ * @build toolbox.ModuleBuilder toolbox.ToolBox
+ * @run main TestModuleUnnamedPackage
+ */
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.ModuleElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+
+import com.sun.source.util.JavacTask;
+import toolbox.Assert;
+import toolbox.ToolBox;
+
+public class TestModuleUnnamedPackage extends JavacTestingAbstractProcessor {
+ public static void main(String... args) throws IOException {
+ TestModuleUnnamedPackage t = new TestModuleUnnamedPackage();
+ t.run();
+ }
+
+ void run() throws IOException {
+ ToolBox tb = new ToolBox();
+ Path src = Path.of("src");
+
+ tb.writeJavaFiles(src, "module m { exports p; }",
+ "package p; public class C { }");
+
+ JavaCompiler c = ToolProvider.getSystemJavaCompiler();
+ StandardJavaFileManager fm = c.getStandardFileManager(null, null, null);
+ fm.setLocationFromPaths(StandardLocation.SOURCE_PATH, List.of(src));
+ List<String> options = List.of("-proc:only");
+ Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(tb.findJavaFiles(src));
+ JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null, files);
+ t.setProcessors(List.of(this));
+ t.call();
+ }
+
+ public boolean process(Set<? extends TypeElement> elems, RoundEnvironment rEnv) {
+ if (rEnv.processingOver()) {
+ Elements elements = processingEnv.getElementUtils();
+ ModuleElement m = elements.getModuleElement("m");
+ PackageElement unnamed = elements.getPackageElement(m, "");
+ System.out.println("module m: " + m);
+ System.out.println("module m, unnamed package: " + unnamed);
+ Assert.checkNonNull(unnamed, "unnamed package in module m");
+ }
+ return true;
+ }
+}
+
--- a/test/lib/jdk/test/lib/Utils.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/lib/jdk/test/lib/Utils.java Wed Nov 13 17:21:31 2019 -0500
@@ -198,8 +198,7 @@
* @return A copy of given opts with all GC options removed.
*/
private static final Pattern useGcPattern = Pattern.compile(
- "(?:\\-XX\\:[\\+\\-]Use.+GC)"
- + "|(?:\\-Xconcgc)");
+ "(?:\\-XX\\:[\\+\\-]Use.+GC)");
public static List<String> removeGcOpts(List<String> opts) {
List<String> optsWithoutGC = new ArrayList<String>();
for (String opt : opts) {
--- a/test/lib/jdk/test/lib/jfr/GCHelper.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/lib/jdk/test/lib/jfr/GCHelper.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -68,12 +68,10 @@
public static final String event_phases_level_3 = EventNames.GCPhasePauseLevel3;
public static final String gcG1New = "G1New";
- public static final String gcParNew = "ParNew";
public static final String gcDefNew = "DefNew";
public static final String gcParallelScavenge = "ParallelScavenge";
public static final String gcG1Old = "G1Old";
public static final String gcG1Full = "G1Full";
- public static final String gcConcurrentMarkSweep = "ConcurrentMarkSweep";
public static final String gcSerialOld = "SerialOld";
public static final String gcPSMarkSweep = "PSMarkSweep";
public static final String gcParallelOld = "ParallelOld";
@@ -174,26 +172,21 @@
beanCollectorTypes.put("G1 Young Generation", true);
beanCollectorTypes.put("Copy", true);
beanCollectorTypes.put("PS Scavenge", true);
- beanCollectorTypes.put("ParNew", true);
// old GarbageCollectionMXBeans.
beanCollectorTypes.put("G1 Old Generation", false);
- beanCollectorTypes.put("ConcurrentMarkSweep", false);
beanCollectorTypes.put("PS MarkSweep", false);
beanCollectorTypes.put("MarkSweepCompact", false);
// List of expected collector overrides. "A.B" means that collector A may use collector B.
collectorOverrides.add("G1Old.G1Full");
- collectorOverrides.add("ConcurrentMarkSweep.SerialOld");
collectorOverrides.add("SerialOld.PSMarkSweep");
requiredEvents.put(gcG1New, new String[] {event_heap_summary, event_young_garbage_collection});
- requiredEvents.put(gcParNew, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_young_garbage_collection});
requiredEvents.put(gcDefNew, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_young_garbage_collection});
requiredEvents.put(gcParallelScavenge, new String[] {event_heap_summary, event_heap_ps_summary, event_heap_metaspace_summary, event_reference_statistics, event_phases_pause, event_phases_level_1, event_young_garbage_collection});
requiredEvents.put(gcG1Old, new String[] {event_heap_summary, event_old_garbage_collection});
requiredEvents.put(gcG1Full, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_old_garbage_collection});
- requiredEvents.put(gcConcurrentMarkSweep, new String[] {event_phases_pause, event_phases_level_1, event_old_garbage_collection});
requiredEvents.put(gcSerialOld, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_old_garbage_collection});
requiredEvents.put(gcParallelOld, new String[] {event_heap_summary, event_heap_ps_summary, event_heap_metaspace_summary, event_reference_statistics, event_phases_pause, event_phases_level_1, event_old_garbage_collection, event_parold_garbage_collection});
--- a/test/lib/sun/hotspot/WhiteBox.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/lib/sun/hotspot/WhiteBox.java Wed Nov 13 17:21:31 2019 -0500
@@ -193,6 +193,9 @@
return parseCommandLine0(commandline, delim, args);
}
+ public native int g1ActiveMemoryNodeCount();
+ public native int[] g1MemoryNodeIds();
+
// Parallel GC
public native long psVirtualSpaceAlignment();
public native long psHeapGenerationAlignment();
@@ -384,7 +387,6 @@
public native void freeMetaspace(ClassLoader classLoader, long addr, long size);
public native long incMetaspaceCapacityUntilGC(long increment);
public native long metaspaceCapacityUntilGC();
- public native boolean metaspaceShouldConcurrentCollect();
public native long metaspaceReserveAlignment();
// Don't use these methods directly
--- a/test/lib/sun/hotspot/gc/GC.java Tue Nov 12 10:25:55 2019 -0500
+++ b/test/lib/sun/hotspot/gc/GC.java Wed Nov 13 17:21:31 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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,11 +35,10 @@
*/
Serial(1),
Parallel(2),
- ConcMarkSweep(3),
- G1(4),
- Epsilon(5),
- Z(6),
- Shenandoah(7);
+ G1(3),
+ Epsilon(4),
+ Z(5),
+ Shenandoah(6);
private static final WhiteBox WB = WhiteBox.getWhiteBox();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/micro/org/openjdk/bench/vm/compiler/BitSetAndReset.java Wed Nov 13 17:21:31 2019 -0500
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2019 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 org.openjdk.bench.vm.compiler;
+
+import org.openjdk.jmh.annotations.*;
+import org.openjdk.jmh.infra.*;
+
+import java.util.concurrent.TimeUnit;
+
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@State(Scope.Thread)
+public class BitSetAndReset {
+ private static final int COUNT = 10_000;
+
+ private static final long MASK63 = 0x8000_0000_0000_0000L;
+ private static final long MASK31 = 0x0000_0000_8000_0000L;
+ private static final long MASK15 = 0x0000_0000_0000_8000L;
+ private static final long MASK00 = 0x0000_0000_0000_0001L;
+
+ private long andq, orq;
+ private boolean success = true;
+
+ @TearDown(Level.Iteration)
+ public void finish() {
+ if (!success)
+ throw new AssertionError("Failure while setting or clearing long vector bits!");
+ }
+
+ @Benchmark
+ public void bitSet(Blackhole bh) {
+ for (int i=0; i<COUNT; i++) {
+ andq = MASK63 | MASK31 | MASK15 | MASK00;
+ orq = 0;
+ bh.consume(test63());
+ bh.consume(test31());
+ bh.consume(test15());
+ bh.consume(test00());
+ success &= andq == 0 && orq == (MASK63 | MASK31 | MASK15 | MASK00);
+ }
+ }
+
+ private long test63() {
+ andq &= ~MASK63;
+ orq |= MASK63;
+ return 0L;
+ }
+ private long test31() {
+ andq &= ~MASK31;
+ orq |= MASK31;
+ return 0L;
+ }
+ private long test15() {
+ andq &= ~MASK15;
+ orq |= MASK15;
+ return 0L;
+ }
+ private long test00() {
+ andq &= ~MASK00;
+ orq |= MASK00;
+ return 0L;
+ }
+
+ private static final long MASK62 = 0x4000_0000_0000_0000L;
+ private static final long MASK61 = 0x2000_0000_0000_0000L;
+ private static final long MASK60 = 0x1000_0000_0000_0000L;
+
+ private long orq63, orq62, orq61, orq60;
+
+ @Benchmark
+ public void throughput(Blackhole bh) {
+ for (int i=0; i<COUNT; i++) {
+ orq63 = orq62 = orq61 = orq60 = 0;
+ bh.consume(testTp());
+ }
+ }
+
+ private long testTp() {
+ orq63 |= MASK63;
+ orq62 |= MASK62;
+ orq61 |= MASK61;
+ orq60 |= MASK60;
+ return 0L;
+ }
+}