Merge jdk7u80-b00 into jdk7u60-b05
diff --git a/.hgtags-top-repo b/.hgtags-top-repo
index f1b7534..2b78338 100644
--- a/.hgtags-top-repo
+++ b/.hgtags-top-repo
@@ -401,3 +401,4 @@
88113cabda386320a087b288d43e792f523cc0ba jdk7u60-b01
6bdacebbc97f0a03be45be48a6d5b5cf2f7fe77d jdk7u60-b02
87f2193da40d3a2eedca95108ae78403c7bdcd49 jdk7u60-b03
+d4397128f8b65eb96287128575dd1a3da6a7825b jdk7u60-b04
diff --git a/corba/.hgtags b/corba/.hgtags
index ed5ed5b..814b5fa 100644
--- a/corba/.hgtags
+++ b/corba/.hgtags
@@ -403,3 +403,4 @@
a531112cc6d0b0a1e7d4ffdaa3ba53addcd25cf4 jdk7u60-b01
d81370c5b863acc19e8fb07315b1ec687ac1136a jdk7u60-b02
d7e98ed925a3885380226f8375fe109a9a25397f jdk7u60-b03
+1a3aa4637b80fabbd069ae88c241efcb3520fc49 jdk7u60-b04
diff --git a/hotspot/.hgtags b/hotspot/.hgtags
index fa946c7..cf47c2e 100644
--- a/hotspot/.hgtags
+++ b/hotspot/.hgtags
@@ -620,3 +620,5 @@
a59134ccb1b704b2cd05e157970d425af43e5437 hs24.60-b06
2c971ed884cec0a9293ccff3def696da81823225 jdk7u60-b03
1afbeb8cb558429156d432f35e7582716053a9cb hs24.60-b07
+05fe7a87d14908eb3f21a0d29fc72cee2f996b7f jdk7u60-b04
+f52b5452d424545e3b101d808e6d7da763d6f0f3 hs24.60-b08
diff --git a/hotspot/agent/src/os/linux/ps_core.c b/hotspot/agent/src/os/linux/ps_core.c
index 092ab7b..c330431 100644
--- a/hotspot/agent/src/os/linux/ps_core.c
+++ b/hotspot/agent/src/os/linux/ps_core.c
@@ -700,55 +700,61 @@
// read segments of a shared object
static bool read_lib_segments(struct ps_prochandle* ph, int lib_fd, ELF_EHDR* lib_ehdr, uintptr_t lib_base) {
- int i = 0;
- ELF_PHDR* phbuf;
- ELF_PHDR* lib_php = NULL;
+ int i = 0;
+ ELF_PHDR* phbuf;
+ ELF_PHDR* lib_php = NULL;
- int page_size=sysconf(_SC_PAGE_SIZE);
+ int page_size = sysconf(_SC_PAGE_SIZE);
- if ((phbuf = read_program_header_table(lib_fd, lib_ehdr)) == NULL)
- return false;
+ if ((phbuf = read_program_header_table(lib_fd, lib_ehdr)) == NULL) {
+ return false;
+ }
- // we want to process only PT_LOAD segments that are not writable.
- // i.e., text segments. The read/write/exec (data) segments would
- // have been already added from core file segments.
- for (lib_php = phbuf, i = 0; i < lib_ehdr->e_phnum; i++) {
- if ((lib_php->p_type == PT_LOAD) && !(lib_php->p_flags & PF_W) && (lib_php->p_filesz != 0)) {
- uintptr_t target_vaddr = lib_php->p_vaddr + lib_base;
- map_info *existing_map = core_lookup(ph, target_vaddr);
+ // we want to process only PT_LOAD segments that are not writable.
+ // i.e., text segments. The read/write/exec (data) segments would
+ // have been already added from core file segments.
+ for (lib_php = phbuf, i = 0; i < lib_ehdr->e_phnum; i++) {
+ if ((lib_php->p_type == PT_LOAD) && !(lib_php->p_flags & PF_W) && (lib_php->p_filesz != 0)) {
- if (existing_map == NULL) {
- if (add_map_info(ph, lib_fd, lib_php->p_offset,
- target_vaddr, lib_php->p_filesz) == NULL) {
- goto err;
- }
- } else {
- if ((existing_map->memsz != page_size) &&
- (existing_map->fd != lib_fd) &&
- (existing_map->memsz != lib_php->p_filesz)) {
+ uintptr_t target_vaddr = lib_php->p_vaddr + lib_base;
+ map_info *existing_map = core_lookup(ph, target_vaddr);
- print_debug("address conflict @ 0x%lx (size = %ld, flags = %d\n)",
- target_vaddr, lib_php->p_filesz, lib_php->p_flags);
- goto err;
- }
+ if (existing_map == NULL){
+ if (add_map_info(ph, lib_fd, lib_php->p_offset,
+ target_vaddr, lib_php->p_memsz) == NULL) {
+ goto err;
+ }
+ } else {
+ // Coredump stores value of p_memsz elf field
+ // rounded up to page boundary.
- /* replace PT_LOAD segment with library segment */
- print_debug("overwrote with new address mapping (memsz %ld -> %ld)\n",
- existing_map->memsz, lib_php->p_filesz);
+ if ((existing_map->memsz != page_size) &&
+ (existing_map->fd != lib_fd) &&
+ (ROUNDUP(existing_map->memsz, page_size) != ROUNDUP(lib_php->p_memsz, page_size))) {
- existing_map->fd = lib_fd;
- existing_map->offset = lib_php->p_offset;
- existing_map->memsz = lib_php->p_filesz;
- }
+ print_debug("address conflict @ 0x%lx (existing map size = %ld, size = %ld, flags = %d)\n",
+ target_vaddr, existing_map->memsz, lib_php->p_memsz, lib_php->p_flags);
+ goto err;
+ }
+
+ /* replace PT_LOAD segment with library segment */
+ print_debug("overwrote with new address mapping (memsz %ld -> %ld)\n",
+ existing_map->memsz, ROUNDUP(lib_php->p_memsz, page_size));
+
+ existing_map->fd = lib_fd;
+ existing_map->offset = lib_php->p_offset;
+ existing_map->memsz = ROUNDUP(lib_php->p_memsz, page_size);
}
- lib_php++;
- }
+ }
- free(phbuf);
- return true;
+ lib_php++;
+ }
+
+ free(phbuf);
+ return true;
err:
- free(phbuf);
- return false;
+ free(phbuf);
+ return false;
}
// process segments from interpreter (ld.so or ld-linux.so)
diff --git a/hotspot/make/hotspot_version b/hotspot/make/hotspot_version
index 690bda7..88bda18 100644
--- a/hotspot/make/hotspot_version
+++ b/hotspot/make/hotspot_version
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -31,11 +31,11 @@
#
# Don't put quotes (fail windows build).
-HOTSPOT_VM_COPYRIGHT=Copyright 2013
+HOTSPOT_VM_COPYRIGHT=Copyright 2014
HS_MAJOR_VER=24
HS_MINOR_VER=60
-HS_BUILD_NUMBER=07
+HS_BUILD_NUMBER=08
JDK_MAJOR_VER=1
JDK_MINOR_VER=7
diff --git a/hotspot/src/os/bsd/vm/os_bsd.cpp b/hotspot/src/os/bsd/vm/os_bsd.cpp
index e19f013..fdc44d4 100644
--- a/hotspot/src/os/bsd/vm/os_bsd.cpp
+++ b/hotspot/src/os/bsd/vm/os_bsd.cpp
@@ -1582,10 +1582,10 @@
return (1000 * 1000);
}
-// XXX: For now, code this as if BSD does not support vtime.
-bool os::supports_vtime() { return false; }
+bool os::supports_vtime() { return true; }
bool os::enable_vtime() { return false; }
bool os::vtime_enabled() { return false; }
+
double os::elapsedVTime() {
// better than nothing, but not much
return elapsedTime();
diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp
index 9a90a3c..1c89d5e 100644
--- a/hotspot/src/os/linux/vm/os_linux.cpp
+++ b/hotspot/src/os/linux/vm/os_linux.cpp
@@ -120,6 +120,12 @@
# include <inttypes.h>
# include <sys/ioctl.h>
+// if RUSAGE_THREAD for getrusage() has not been defined, do it here. The code calling
+// getrusage() is prepared to handle the associated failure.
+#ifndef RUSAGE_THREAD
+#define RUSAGE_THREAD (1) /* only the calling thread */
+#endif
+
#define MAX_PATH (2 * K)
// for timer info max values which include all bits
@@ -1378,15 +1384,19 @@
return (1000 * 1000);
}
-// For now, we say that linux does not support vtime. I have no idea
-// whether it can actually be made to (DLD, 9/13/05).
-
-bool os::supports_vtime() { return false; }
+bool os::supports_vtime() { return true; }
bool os::enable_vtime() { return false; }
bool os::vtime_enabled() { return false; }
+
double os::elapsedVTime() {
- // better than nothing, but not much
- return elapsedTime();
+ struct rusage usage;
+ int retval = getrusage(RUSAGE_THREAD, &usage);
+ if (retval == 0) {
+ return (double) (usage.ru_utime.tv_sec + usage.ru_stime.tv_sec) + (double) (usage.ru_utime.tv_usec + usage.ru_stime.tv_usec) / (1000 * 1000);
+ } else {
+ // better than nothing, but not much
+ return elapsedTime();
+ }
}
jlong os::javaTimeMillis() {
diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp
index ce59493..b81dea6 100644
--- a/hotspot/src/os/windows/vm/os_windows.cpp
+++ b/hotspot/src/os/windows/vm/os_windows.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -800,15 +800,21 @@
return result;
}
-// For now, we say that Windows does not support vtime. I have no idea
-// whether it can actually be made to (DLD, 9/13/05).
-
-bool os::supports_vtime() { return false; }
+bool os::supports_vtime() { return true; }
bool os::enable_vtime() { return false; }
bool os::vtime_enabled() { return false; }
+
double os::elapsedVTime() {
- // better than nothing, but not much
- return elapsedTime();
+ FILETIME created;
+ FILETIME exited;
+ FILETIME kernel;
+ FILETIME user;
+ if (GetThreadTimes(GetCurrentThread(), &created, &exited, &kernel, &user) != 0) {
+ // the resolution of windows_to_java_time() should be sufficient (ms)
+ return (double) (windows_to_java_time(kernel) + windows_to_java_time(user)) / MILLIUNITS;
+ } else {
+ return elapsedTime();
+ }
}
jlong os::javaTimeMillis() {
@@ -1591,6 +1597,7 @@
void os::win32::print_windows_version(outputStream* st) {
OSVERSIONINFOEX osvi;
+ SYSTEM_INFO si;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
@@ -1600,6 +1607,18 @@
}
int os_vers = osvi.dwMajorVersion * 1000 + osvi.dwMinorVersion;
+
+ ZeroMemory(&si, sizeof(SYSTEM_INFO));
+ if (os_vers >= 5002) {
+ // Retrieve SYSTEM_INFO from GetNativeSystemInfo call so that we could
+ // find out whether we are running on 64 bit processor or not.
+ if (os::Kernel32Dll::GetNativeSystemInfoAvailable()) {
+ os::Kernel32Dll::GetNativeSystemInfo(&si);
+ } else {
+ GetSystemInfo(&si);
+ }
+ }
+
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
switch (os_vers) {
case 3051: st->print(" Windows NT 3.51"); break;
@@ -1607,57 +1626,48 @@
case 5000: st->print(" Windows 2000"); break;
case 5001: st->print(" Windows XP"); break;
case 5002:
- case 6000:
- case 6001:
- case 6002: {
- // Retrieve SYSTEM_INFO from GetNativeSystemInfo call so that we could
- // find out whether we are running on 64 bit processor or not.
- SYSTEM_INFO si;
- ZeroMemory(&si, sizeof(SYSTEM_INFO));
- if (!os::Kernel32Dll::GetNativeSystemInfoAvailable()){
- GetSystemInfo(&si);
- } else {
- os::Kernel32Dll::GetNativeSystemInfo(&si);
- }
- if (os_vers == 5002) {
- if (osvi.wProductType == VER_NT_WORKSTATION &&
- si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
+ if (osvi.wProductType == VER_NT_WORKSTATION &&
+ si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
st->print(" Windows XP x64 Edition");
- else
- st->print(" Windows Server 2003 family");
- } else if (os_vers == 6000) {
- if (osvi.wProductType == VER_NT_WORKSTATION)
- st->print(" Windows Vista");
- else
- st->print(" Windows Server 2008");
- if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
- st->print(" , 64 bit");
- } else if (os_vers == 6001) {
- if (osvi.wProductType == VER_NT_WORKSTATION) {
- st->print(" Windows 7");
- } else {
- // Unrecognized windows, print out its major and minor versions
- st->print(" Windows NT %d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion);
- }
- if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
- st->print(" , 64 bit");
- } else if (os_vers == 6002) {
- if (osvi.wProductType == VER_NT_WORKSTATION) {
- st->print(" Windows 8");
- } else {
- st->print(" Windows Server 2012");
- }
- if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
- st->print(" , 64 bit");
- } else { // future os
- // Unrecognized windows, print out its major and minor versions
- st->print(" Windows NT %d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion);
- if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
- st->print(" , 64 bit");
+ } else {
+ st->print(" Windows Server 2003 family");
}
break;
- }
- default: // future windows, print out its major and minor versions
+
+ case 6000:
+ if (osvi.wProductType == VER_NT_WORKSTATION) {
+ st->print(" Windows Vista");
+ } else {
+ st->print(" Windows Server 2008");
+ }
+ break;
+
+ case 6001:
+ if (osvi.wProductType == VER_NT_WORKSTATION) {
+ st->print(" Windows 7");
+ } else {
+ st->print(" Windows Server 2008 R2");
+ }
+ break;
+
+ case 6002:
+ if (osvi.wProductType == VER_NT_WORKSTATION) {
+ st->print(" Windows 8");
+ } else {
+ st->print(" Windows Server 2012");
+ }
+ break;
+
+ case 6003:
+ if (osvi.wProductType == VER_NT_WORKSTATION) {
+ st->print(" Windows 8.1");
+ } else {
+ st->print(" Windows Server 2012 R2");
+ }
+ break;
+
+ default: // future os
+ // Unrecognized windows, print out its major and minor versions
st->print(" Windows NT %d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion);
}
} else {
@@ -1669,6 +1679,11 @@
st->print(" Windows %d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion);
}
}
+
+ if (os_vers >= 6000 && si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
+ st->print(" , 64 bit");
+ }
+
st->print(" Build %d", osvi.dwBuildNumber);
st->print(" %s", osvi.szCSDVersion); // service pack
st->cr();
diff --git a/hotspot/src/share/vm/c1/c1_Runtime1.cpp b/hotspot/src/share/vm/c1/c1_Runtime1.cpp
index d0b6abe..bff3c3f 100644
--- a/hotspot/src/share/vm/c1/c1_Runtime1.cpp
+++ b/hotspot/src/share/vm/c1/c1_Runtime1.cpp
@@ -911,16 +911,6 @@
// Return to the now deoptimized frame.
}
- // If we are patching in a non-perm oop, make sure the nmethod
- // is on the right list.
- if (ScavengeRootsInCode && load_klass.not_null() && load_klass->is_scavengable()) {
- MutexLockerEx ml_code (CodeCache_lock, Mutex::_no_safepoint_check_flag);
- nmethod* nm = CodeCache::find_nmethod(caller_frame.pc());
- guarantee(nm != NULL, "only nmethods can contain non-perm oops");
- if (!nm->on_scavenge_root_list())
- CodeCache::add_scavenge_root_nmethod(nm);
- }
-
// Now copy code back
{
@@ -1096,6 +1086,22 @@
}
}
}
+
+
+ // If we are patching in a non-perm oop, make sure the nmethod
+ // is on the right list.
+ if (ScavengeRootsInCode && load_klass.not_null() && load_klass->is_scavengable()) {
+ MutexLockerEx ml_code (CodeCache_lock, Mutex::_no_safepoint_check_flag);
+ nmethod* nm = CodeCache::find_nmethod(caller_frame.pc());
+ guarantee(nm != NULL, "only nmethods can contain non-perm oops");
+ if (!nm->on_scavenge_root_list()) {
+ CodeCache::add_scavenge_root_nmethod(nm);
+ }
+
+ // Since we've patched some oops in the nmethod,
+ // (re)register it with the heap.
+ Universe::heap()->register_nmethod(nm);
+ }
JRT_END
//
diff --git a/hotspot/src/share/vm/classfile/symbolTable.cpp b/hotspot/src/share/vm/classfile/symbolTable.cpp
index af13b3f..d5a100f 100644
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp
@@ -39,6 +39,9 @@
// --------------------------------------------------------------------------
+// the number of buckets a thread claims
+const int ClaimChunkSize = 32;
+
SymbolTable* SymbolTable::_the_table = NULL;
// Static arena for symbols that are not deallocated
Arena* SymbolTable::_arena = NULL;
@@ -81,16 +84,12 @@
}
}
-int SymbolTable::symbols_removed = 0;
-int SymbolTable::symbols_counted = 0;
+int SymbolTable::_symbols_removed = 0;
+int SymbolTable::_symbols_counted = 0;
+volatile int SymbolTable::_parallel_claimed_idx = 0;
-// Remove unreferenced symbols from the symbol table
-// This is done late during GC.
-void SymbolTable::unlink() {
- int removed = 0;
- int total = 0;
- size_t memory_total = 0;
- for (int i = 0; i < the_table()->table_size(); ++i) {
+void SymbolTable::buckets_unlink(int start_idx, int end_idx, int* processed, int* removed, size_t* memory_total) {
+ for (int i = start_idx; i < end_idx; ++i) {
HashtableEntry<Symbol*, mtSymbol>** p = the_table()->bucket_addr(i);
HashtableEntry<Symbol*, mtSymbol>* entry = the_table()->bucket(i);
while (entry != NULL) {
@@ -102,14 +101,14 @@
break;
}
Symbol* s = entry->literal();
- memory_total += s->object_size();
- total++;
+ (*memory_total) += s->object_size();
+ (*processed)++;
assert(s != NULL, "just checking");
// If reference count is zero, remove.
if (s->refcount() == 0) {
assert(!entry->is_shared(), "shared entries should be kept live");
delete s;
- removed++;
+ (*removed)++;
*p = entry->next();
the_table()->free_entry(entry);
} else {
@@ -119,12 +118,45 @@
entry = (HashtableEntry<Symbol*, mtSymbol>*)HashtableEntry<Symbol*, mtSymbol>::make_ptr(*p);
}
}
- symbols_removed += removed;
- symbols_counted += total;
+}
+
+// Remove unreferenced symbols from the symbol table
+// This is done late during GC.
+void SymbolTable::unlink(int* processed, int* removed) {
+ size_t memory_total = 0;
+ buckets_unlink(0, the_table()->table_size(), processed, removed, &memory_total);
+ _symbols_removed += *removed;
+ _symbols_counted += *processed;
// Exclude printing for normal PrintGCDetails because people parse
// this output.
if (PrintGCDetails && Verbose && WizardMode) {
- gclog_or_tty->print(" [Symbols=%d size=" SIZE_FORMAT "K] ", total,
+ gclog_or_tty->print(" [Symbols=%d size=" SIZE_FORMAT "K] ", *processed,
+ (memory_total*HeapWordSize)/1024);
+ }
+}
+
+void SymbolTable::possibly_parallel_unlink(int* processed, int* removed) {
+ const int limit = the_table()->table_size();
+
+ size_t memory_total = 0;
+
+ for (;;) {
+ // Grab next set of buckets to scan
+ int start_idx = Atomic::add(ClaimChunkSize, &_parallel_claimed_idx) - ClaimChunkSize;
+ if (start_idx >= limit) {
+ // End of table
+ break;
+ }
+
+ int end_idx = MIN2(limit, start_idx + ClaimChunkSize);
+ buckets_unlink(start_idx, end_idx, processed, removed, &memory_total);
+ }
+ Atomic::add(*processed, &_symbols_counted);
+ Atomic::add(*removed, &_symbols_removed);
+ // Exclude printing for normal PrintGCDetails because people parse
+ // this output.
+ if (PrintGCDetails && Verbose && WizardMode) {
+ gclog_or_tty->print(" [Symbols: scanned=%d removed=%d size=" SIZE_FORMAT "K] ", *processed, *removed,
(memory_total*HeapWordSize)/1024);
}
}
@@ -503,21 +535,21 @@
}
}
tty->print_cr("Symbol Table:");
- tty->print_cr("Total number of symbols %5d", count);
- tty->print_cr("Total size in memory %5dK",
+ tty->print_cr("Total number of symbols "INT32_FORMAT, count);
+ tty->print_cr("Total size in memory "INT32_FORMAT"K",
(memory_total*HeapWordSize)/1024);
- tty->print_cr("Total counted %5d", symbols_counted);
- tty->print_cr("Total removed %5d", symbols_removed);
- if (symbols_counted > 0) {
+ tty->print_cr("Total counted "INT32_FORMAT, _symbols_counted);
+ tty->print_cr("Total removed "INT32_FORMAT, _symbols_removed);
+ if (_symbols_counted > 0) {
tty->print_cr("Percent removed %3.2f",
- ((float)symbols_removed/(float)symbols_counted)* 100);
+ ((float)_symbols_removed/(float)_symbols_counted)* 100);
}
- tty->print_cr("Reference counts %5d", Symbol::_total_count);
- tty->print_cr("Symbol arena size %5d used %5d",
+ tty->print_cr("Reference counts "INT32_FORMAT, Symbol::_total_count);
+ tty->print_cr("Symbol arena size "SIZE_FORMAT" used "SIZE_FORMAT,
arena()->size_in_bytes(), arena()->used());
tty->print_cr("Histogram of symbol length:");
- tty->print_cr("%8s %5d", "Total ", total);
- tty->print_cr("%8s %5d", "Maximum", max_symbols);
+ tty->print_cr("%8s "INT32_FORMAT, "Total ", total);
+ tty->print_cr("%8s "INT32_FORMAT, "Maximum", max_symbols);
tty->print_cr("%8s %3.2f", "Average",
((float) total / (float) the_table()->table_size()));
tty->print_cr("%s", "Histogram:");
@@ -746,11 +778,41 @@
return result;
}
-void StringTable::unlink(BoolObjectClosure* is_alive) {
+void StringTable::unlink(BoolObjectClosure* is_alive, int* processed, int* removed) {
+ buckets_unlink(is_alive, 0, the_table()->table_size(), processed, removed);
+}
+
+void StringTable::possibly_parallel_unlink(BoolObjectClosure* is_alive, int* processed, int* removed) {
// Readers of the table are unlocked, so we should only be removing
// entries at a safepoint.
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
- for (int i = 0; i < the_table()->table_size(); ++i) {
+ const int limit = the_table()->table_size();
+
+ for (;;) {
+ // Grab next set of buckets to scan
+ int start_idx = Atomic::add(ClaimChunkSize, &_parallel_claimed_idx) - ClaimChunkSize;
+ if (start_idx >= limit) {
+ // End of table
+ break;
+ }
+
+ int end_idx = MIN2(limit, start_idx + ClaimChunkSize);
+ buckets_unlink(is_alive, start_idx, end_idx, processed, removed);
+ }
+}
+
+void StringTable::buckets_unlink(BoolObjectClosure* is_alive, int start_idx, int end_idx, int* processed, int* removed) {
+ const int limit = the_table()->table_size();
+
+ assert(0 <= start_idx && start_idx <= limit,
+ err_msg("start_idx (" INT32_FORMAT ") is out of bounds", start_idx));
+ assert(0 <= end_idx && end_idx <= limit,
+ err_msg("end_idx (" INT32_FORMAT ") is out of bounds", end_idx));
+ assert(start_idx <= end_idx,
+ err_msg("Index ordering: start_idx=" INT32_FORMAT", end_idx=" INT32_FORMAT,
+ start_idx, end_idx));
+
+ for (int i = start_idx; i < end_idx; ++i) {
HashtableEntry<oop, mtSymbol>** p = the_table()->bucket_addr(i);
HashtableEntry<oop, mtSymbol>* entry = the_table()->bucket(i);
while (entry != NULL) {
@@ -767,24 +829,26 @@
} else {
*p = entry->next();
the_table()->free_entry(entry);
+ (*removed)++;
}
+ (*processed)++;
entry = (HashtableEntry<oop, mtSymbol>*)HashtableEntry<oop, mtSymbol>::make_ptr(*p);
}
}
}
-void StringTable::buckets_do(OopClosure* f, int start_idx, int end_idx) {
+void StringTable::buckets_oops_do(OopClosure* f, int start_idx, int end_idx) {
const int limit = the_table()->table_size();
assert(0 <= start_idx && start_idx <= limit,
- err_msg("start_idx (" INT32_FORMAT ") oob?", start_idx));
+ err_msg("start_idx (" INT32_FORMAT ") is out of bounds", start_idx));
assert(0 <= end_idx && end_idx <= limit,
- err_msg("end_idx (" INT32_FORMAT ") oob?", end_idx));
+ err_msg("end_idx (" INT32_FORMAT ") is out of bounds", end_idx));
assert(start_idx <= end_idx,
- err_msg("Ordering: start_idx=" INT32_FORMAT", end_idx=" INT32_FORMAT,
+ err_msg("Index ordering: start_idx=" INT32_FORMAT", end_idx=" INT32_FORMAT,
start_idx, end_idx));
- for (int i = start_idx; i < end_idx; i += 1) {
+ for (int i = start_idx; i < end_idx; i++) {
HashtableEntry<oop, mtSymbol>** p = the_table()->bucket_addr(i);
HashtableEntry<oop, mtSymbol>* entry = the_table()->bucket(i);
while (entry != NULL) {
@@ -804,11 +868,10 @@
}
void StringTable::oops_do(OopClosure* f) {
- buckets_do(f, 0, the_table()->table_size());
+ buckets_oops_do(f, 0, the_table()->table_size());
}
void StringTable::possibly_parallel_oops_do(OopClosure* f) {
- const int ClaimChunkSize = 32;
const int limit = the_table()->table_size();
for (;;) {
@@ -820,7 +883,7 @@
}
int end_idx = MIN2(limit, start_idx + ClaimChunkSize);
- buckets_do(f, start_idx, end_idx);
+ buckets_oops_do(f, start_idx, end_idx);
}
}
diff --git a/hotspot/src/share/vm/classfile/symbolTable.hpp b/hotspot/src/share/vm/classfile/symbolTable.hpp
index 9ed7d72..afdad5b 100644
--- a/hotspot/src/share/vm/classfile/symbolTable.hpp
+++ b/hotspot/src/share/vm/classfile/symbolTable.hpp
@@ -86,8 +86,8 @@
static bool _needs_rehashing;
// For statistics
- static int symbols_removed;
- static int symbols_counted;
+ static int _symbols_removed;
+ static int _symbols_counted;
Symbol* allocate_symbol(const u1* name, int len, bool c_heap, TRAPS); // Assumes no characters larger than 0x7F
@@ -126,6 +126,11 @@
static Arena* arena() { return _arena; } // called for statistics
static void initialize_symbols(int arena_alloc_size = 0);
+
+ static volatile int _parallel_claimed_idx;
+
+ // Release any dead symbols
+ static void buckets_unlink(int start_idx, int end_idx, int* processed, int* removed, size_t* memory_total);
public:
enum {
symbol_alloc_batch_size = 8,
@@ -175,7 +180,19 @@
unsigned int* hashValues, TRAPS);
// Release any dead symbols
- static void unlink();
+ static void unlink() {
+ int processed = 0;
+ int removed = 0;
+ unlink(&processed, &removed);
+ }
+ static void unlink(int* processed, int* removed);
+ // Release any dead symbols, possibly parallel version
+ static void possibly_parallel_unlink() {
+ int processed = 0;
+ int removed = 0;
+ possibly_parallel_unlink(&processed, &removed);
+ }
+ static void possibly_parallel_unlink(int* processed, int* removed);
// iterate over symbols
static void symbols_do(SymbolClosure *cl);
@@ -233,6 +250,9 @@
// Rehash the symbol table if it gets out of balance
static void rehash_table();
static bool needs_rehashing() { return _needs_rehashing; }
+ // Parallel chunked scanning
+ static void clear_parallel_claimed_index() { _parallel_claimed_idx = 0; }
+ static int parallel_claimed_index() { return _parallel_claimed_idx; }
};
class StringTable : public Hashtable<oop, mtSymbol> {
@@ -256,7 +276,9 @@
// Apply the give oop closure to the entries to the buckets
// in the range [start_idx, end_idx).
- static void buckets_do(OopClosure* f, int start_idx, int end_idx);
+ static void buckets_oops_do(OopClosure* f, int start_idx, int end_idx);
+ // Unlink the entries to the buckets in the range [start_idx, end_idx).
+ static void buckets_unlink(BoolObjectClosure* is_alive, int start_idx, int end_idx, int* processed, int* removed);
StringTable() : Hashtable<oop, mtSymbol>((int)StringTableSize,
sizeof (HashtableEntry<oop, mtSymbol>)) {}
@@ -283,7 +305,13 @@
// GC support
// Delete pointers to otherwise-unreachable objects.
- static void unlink(BoolObjectClosure* cl);
+ static void unlink(BoolObjectClosure* cl) {
+ int processed = 0;
+ int removed = 0;
+ unlink(cl, &processed, &removed);
+ }
+
+ static void unlink(BoolObjectClosure* cl, int* processed, int* removed);
// Serially invoke "f->do_oop" on the locations of all oops in the table.
static void oops_do(OopClosure* f);
@@ -291,6 +319,8 @@
// Possibly parallel version of the above
static void possibly_parallel_oops_do(OopClosure* f);
+ static void possibly_parallel_unlink(BoolObjectClosure* cl, int* processed, int* removed);
+
// Hashing algorithm, used as the hash value used by the
// StringTable for bucket selection and comparison (stored in the
// HashtableEntry structures). This is used in the String.intern() method.
@@ -328,5 +358,6 @@
// Parallel chunked scanning
static void clear_parallel_claimed_index() { _parallel_claimed_idx = 0; }
+ static int parallel_claimed_index() { return _parallel_claimed_idx; }
};
#endif // SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP
diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp
index 92af961..4899d32 100644
--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp
+++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp
@@ -1572,9 +1572,10 @@
// Used for assertions and verification only
klassOop SystemDictionary::find_class(Symbol* class_name, Handle class_loader) {
#ifndef ASSERT
- guarantee(VerifyBeforeGC ||
- VerifyDuringGC ||
- VerifyBeforeExit ||
+ guarantee(VerifyBeforeGC ||
+ VerifyDuringGC ||
+ VerifyBeforeExit ||
+ VerifyDuringStartup ||
VerifyAfterGC, "too expensive");
#endif
assert_locked_or_safepoint(SystemDictionary_lock);
diff --git a/hotspot/src/share/vm/code/nmethod.cpp b/hotspot/src/share/vm/code/nmethod.cpp
index 21c9413..dddbea2 100644
--- a/hotspot/src/share/vm/code/nmethod.cpp
+++ b/hotspot/src/share/vm/code/nmethod.cpp
@@ -676,6 +676,7 @@
code_buffer->copy_oops_to(this);
if (ScavengeRootsInCode && detect_scavenge_root_oops()) {
CodeCache::add_scavenge_root_nmethod(this);
+ Universe::heap()->register_nmethod(this);
}
debug_only(verify_scavenge_root_oops());
CodeCache::commit(this);
@@ -869,6 +870,7 @@
dependencies->copy_to(this);
if (ScavengeRootsInCode && detect_scavenge_root_oops()) {
CodeCache::add_scavenge_root_nmethod(this);
+ Universe::heap()->register_nmethod(this);
}
debug_only(verify_scavenge_root_oops());
@@ -1282,6 +1284,13 @@
methodHandle the_method(method());
No_Safepoint_Verifier nsv;
+ // during patching, depending on the nmethod state we must notify the GC that
+ // code has been unloaded, unregistering it. We cannot do this right while
+ // holding the Patching_lock because we need to use the CodeCache_lock. This
+ // would be prone to deadlocks.
+ // This flag is used to remember whether we need to later lock and unregister.
+ bool nmethod_needs_unregister = false;
+
{
// invalidate osr nmethod before acquiring the patching lock since
// they both acquire leaf locks and we don't want a deadlock.
@@ -1314,6 +1323,13 @@
inc_decompile_count();
}
+ // If the state is becoming a zombie, signal to unregister the nmethod with
+ // the heap.
+ // This nmethod may have already been unloaded during a full GC.
+ if ((state == zombie) && !is_unloaded()) {
+ nmethod_needs_unregister = true;
+ }
+
// Change state
_state = state;
@@ -1349,6 +1365,9 @@
// safepoint can sneak in, otherwise the oops used by the
// dependency logic could have become stale.
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+ if (nmethod_needs_unregister) {
+ Universe::heap()->unregister_nmethod(this);
+ }
flush_dependencies(NULL);
}
@@ -1696,20 +1715,10 @@
#endif // !PRODUCT
}
-// This method is called twice during GC -- once while
-// tracing the "active" nmethods on thread stacks during
-// the (strong) marking phase, and then again when walking
-// the code cache contents during the weak roots processing
-// phase. The two uses are distinguished by means of the
-// 'do_strong_roots_only' flag, which is true in the first
-// case. We want to walk the weak roots in the nmethod
-// only in the second case. The weak roots in the nmethod
-// are the oops in the ExceptionCache and the InlineCache
-// oops.
-void nmethod::oops_do(OopClosure* f, bool do_strong_roots_only) {
- // make sure the oops ready to receive visitors
- assert(!is_zombie() && !is_unloaded(),
- "should not call follow on zombie or unloaded nmethod");
+void nmethod::oops_do(OopClosure* f, bool do_strong_roots_only, bool allow_zombie) {
+ // make sure the oops ready to receive visitors
+ assert(allow_zombie || !is_zombie(), "should not call follow on zombie nmethod");
+ assert(!is_unloaded(), "should not call follow on unloaded nmethod");
// If the method is not entrant or zombie then a JMP is plastered over the
// first few bytes. If an oop in the old code was there, that oop
diff --git a/hotspot/src/share/vm/code/nmethod.hpp b/hotspot/src/share/vm/code/nmethod.hpp
index 8d76afb..1a4ad9e 100644
--- a/hotspot/src/share/vm/code/nmethod.hpp
+++ b/hotspot/src/share/vm/code/nmethod.hpp
@@ -548,8 +548,8 @@
void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map,
OopClosure* f);
- void oops_do(OopClosure* f) { oops_do(f, false); }
- void oops_do(OopClosure* f, bool do_strong_roots_only);
+ void oops_do(OopClosure* f) { oops_do(f, false, false); }
+ void oops_do(OopClosure* f, bool do_strong_roots_only, bool allow_zombie);
bool detect_scavenge_root_oops();
void verify_scavenge_root_oops() PRODUCT_RETURN;
diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp
index 481a387..84199b2 100644
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp
@@ -2510,8 +2510,7 @@
// initial marking in checkpointRootsInitialWork has been completed
if (VerifyDuringGC &&
GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) {
- gclog_or_tty->print("Verify before initial mark: ");
- Universe::verify();
+ Universe::verify("Verify before initial mark: ");
}
{
bool res = markFromRoots(false);
@@ -2522,8 +2521,7 @@
case FinalMarking:
if (VerifyDuringGC &&
GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) {
- gclog_or_tty->print("Verify before re-mark: ");
- Universe::verify();
+ Universe::verify("Verify before re-mark: ");
}
checkpointRootsFinal(false, clear_all_soft_refs,
init_mark_was_synchronous);
@@ -2534,8 +2532,7 @@
// final marking in checkpointRootsFinal has been completed
if (VerifyDuringGC &&
GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) {
- gclog_or_tty->print("Verify before sweep: ");
- Universe::verify();
+ Universe::verify("Verify before sweep: ");
}
sweep(false);
assert(_collectorState == Resizing, "Incorrect state");
@@ -2550,8 +2547,7 @@
// The heap has been resized.
if (VerifyDuringGC &&
GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) {
- gclog_or_tty->print("Verify before reset: ");
- Universe::verify();
+ Universe::verify("Verify before reset: ");
}
save_heap_summary();
reset(false);
@@ -2890,8 +2886,8 @@
bool failed() { return _failed; }
};
-bool CMSCollector::verify_after_remark() {
- gclog_or_tty->print(" [Verifying CMS Marking... ");
+bool CMSCollector::verify_after_remark(bool silent) {
+ if (!silent) gclog_or_tty->print(" [Verifying CMS Marking... ");
MutexLockerEx ml(verification_mark_bm()->lock(), Mutex::_no_safepoint_check_flag);
static bool init = false;
@@ -2952,7 +2948,7 @@
warning("Unrecognized value %d for CMSRemarkVerifyVariant",
CMSRemarkVerifyVariant);
}
- gclog_or_tty->print(" done] ");
+ if (!silent) gclog_or_tty->print(" done] ");
return true;
}
diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp
index 6d53a77..3d73540 100644
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp
@@ -1013,7 +1013,7 @@
// debugging
void verify();
- bool verify_after_remark();
+ bool verify_after_remark(bool silent = VerifySilently);
void verify_ok_to_terminate() const PRODUCT_RETURN;
void verify_work_stacks_empty() const PRODUCT_RETURN;
void verify_overflow_empty() const PRODUCT_RETURN;
diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp
index 5f04920..f0b4da8 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp
@@ -114,6 +114,14 @@
}
}
+void ConcurrentG1Refine::worker_threads_do(ThreadClosure * tc) {
+ if (_threads != NULL) {
+ for (int i = 0; i < worker_thread_num(); i++) {
+ tc->do_thread(_threads[i]);
+ }
+ }
+}
+
int ConcurrentG1Refine::thread_num() {
int n_threads = (G1ConcRefinementThreads > 0) ? G1ConcRefinementThreads
: ParallelGCThreads;
@@ -126,3 +134,7 @@
st->cr();
}
}
+
+ConcurrentG1RefineThread * ConcurrentG1Refine::sampling_thread() const {
+ return _threads[worker_thread_num()];
+}
diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp
index 46e6622..3dc7c62 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp
@@ -35,6 +35,7 @@
class G1CollectedHeap;
class G1HotCardCache;
class G1RemSet;
+class DirtyCardQueue;
class ConcurrentG1Refine: public CHeapObj<mtGC> {
ConcurrentG1RefineThread** _threads;
@@ -78,9 +79,15 @@
void reinitialize_threads();
- // Iterate over the conc refine threads
+ // Iterate over all concurrent refinement threads
void threads_do(ThreadClosure *tc);
+ // Iterate over all worker refinement threads
+ void worker_threads_do(ThreadClosure * tc);
+
+ // The RS sampling thread
+ ConcurrentG1RefineThread * sampling_thread() const;
+
static int thread_num();
void print_worker_threads_on(outputStream* st) const;
diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp
index 2c1c366..d320cca 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp
@@ -1154,10 +1154,9 @@
if (VerifyDuringGC) {
HandleMark hm; // handle scope
- gclog_or_tty->print(" VerifyDuringGC:(before)");
Universe::heap()->prepare_for_verify();
- Universe::verify(/* silent */ false,
- /* option */ VerifyOption_G1UsePrevMarking);
+ Universe::verify(VerifyOption_G1UsePrevMarking,
+ " VerifyDuringGC:(before)");
}
G1CollectorPolicy* g1p = g1h->g1_policy();
@@ -1181,10 +1180,9 @@
// Verify the heap w.r.t. the previous marking bitmap.
if (VerifyDuringGC) {
HandleMark hm; // handle scope
- gclog_or_tty->print(" VerifyDuringGC:(overflow)");
Universe::heap()->prepare_for_verify();
- Universe::verify(/* silent */ false,
- /* option */ VerifyOption_G1UsePrevMarking);
+ Universe::verify(VerifyOption_G1UsePrevMarking,
+ " VerifyDuringGC:(overflow)");
}
// Clear the marking state because we will be restarting
@@ -1204,10 +1202,9 @@
if (VerifyDuringGC) {
HandleMark hm; // handle scope
- gclog_or_tty->print(" VerifyDuringGC:(after)");
Universe::heap()->prepare_for_verify();
- Universe::verify(/* silent */ false,
- /* option */ VerifyOption_G1UseNextMarking);
+ Universe::verify(VerifyOption_G1UseNextMarking,
+ " VerifyDuringGC:(after)");
}
assert(!restart_for_overflow(), "sanity");
// Completely reset the marking state since marking completed
@@ -1498,7 +1495,6 @@
}
};
-
class G1ParVerifyFinalCountTask: public AbstractGangTask {
protected:
G1CollectedHeap* _g1h;
@@ -1856,10 +1852,9 @@
if (VerifyDuringGC) {
HandleMark hm; // handle scope
- gclog_or_tty->print(" VerifyDuringGC:(before)");
Universe::heap()->prepare_for_verify();
- Universe::verify(/* silent */ false,
- /* option */ VerifyOption_G1UsePrevMarking);
+ Universe::verify(VerifyOption_G1UsePrevMarking,
+ " VerifyDuringGC:(before)");
}
G1CollectorPolicy* g1p = G1CollectedHeap::heap()->g1_policy();
@@ -2011,10 +2006,9 @@
if (VerifyDuringGC) {
HandleMark hm; // handle scope
- gclog_or_tty->print(" VerifyDuringGC:(after)");
Universe::heap()->prepare_for_verify();
- Universe::verify(/* silent */ false,
- /* option */ VerifyOption_G1UsePrevMarking);
+ Universe::verify(VerifyOption_G1UsePrevMarking,
+ " VerifyDuringGC:(after)");
}
g1h->verify_region_sets_optional();
@@ -2412,10 +2406,9 @@
assert(!rp->discovery_enabled(), "Post condition");
}
- // Now clean up stale oops in StringTable
- StringTable::unlink(&g1_is_alive);
- // Clean up unreferenced symbols in symbol table.
- SymbolTable::unlink();
+ g1h->unlink_string_and_symbol_table(&g1_is_alive,
+ /* process_strings */ false, // currently strings are always roots
+ /* process_symbols */ true);
}
void ConcurrentMark::swapMarkBitMaps() {
@@ -4396,7 +4389,8 @@
_total_used_bytes(0), _total_capacity_bytes(0),
_total_prev_live_bytes(0), _total_next_live_bytes(0),
_hum_used_bytes(0), _hum_capacity_bytes(0),
- _hum_prev_live_bytes(0), _hum_next_live_bytes(0) {
+ _hum_prev_live_bytes(0), _hum_next_live_bytes(0),
+ _total_remset_bytes(0), _total_strong_code_roots_bytes(0) {
G1CollectedHeap* g1h = G1CollectedHeap::heap();
MemRegion g1_committed = g1h->g1_committed();
MemRegion g1_reserved = g1h->g1_reserved();
@@ -4414,23 +4408,29 @@
HeapRegion::GrainBytes);
_out->print_cr(G1PPRL_LINE_PREFIX);
_out->print_cr(G1PPRL_LINE_PREFIX
- G1PPRL_TYPE_H_FORMAT
- G1PPRL_ADDR_BASE_H_FORMAT
- G1PPRL_BYTE_H_FORMAT
- G1PPRL_BYTE_H_FORMAT
- G1PPRL_BYTE_H_FORMAT
- G1PPRL_DOUBLE_H_FORMAT,
- "type", "address-range",
- "used", "prev-live", "next-live", "gc-eff");
+ G1PPRL_TYPE_H_FORMAT
+ G1PPRL_ADDR_BASE_H_FORMAT
+ G1PPRL_BYTE_H_FORMAT
+ G1PPRL_BYTE_H_FORMAT
+ G1PPRL_BYTE_H_FORMAT
+ G1PPRL_DOUBLE_H_FORMAT
+ G1PPRL_BYTE_H_FORMAT
+ G1PPRL_BYTE_H_FORMAT,
+ "type", "address-range",
+ "used", "prev-live", "next-live", "gc-eff",
+ "remset", "code-roots");
_out->print_cr(G1PPRL_LINE_PREFIX
- G1PPRL_TYPE_H_FORMAT
- G1PPRL_ADDR_BASE_H_FORMAT
- G1PPRL_BYTE_H_FORMAT
- G1PPRL_BYTE_H_FORMAT
- G1PPRL_BYTE_H_FORMAT
- G1PPRL_DOUBLE_H_FORMAT,
- "", "",
- "(bytes)", "(bytes)", "(bytes)", "(bytes/ms)");
+ G1PPRL_TYPE_H_FORMAT
+ G1PPRL_ADDR_BASE_H_FORMAT
+ G1PPRL_BYTE_H_FORMAT
+ G1PPRL_BYTE_H_FORMAT
+ G1PPRL_BYTE_H_FORMAT
+ G1PPRL_DOUBLE_H_FORMAT
+ G1PPRL_BYTE_H_FORMAT
+ G1PPRL_BYTE_H_FORMAT,
+ "", "",
+ "(bytes)", "(bytes)", "(bytes)", "(bytes/ms)",
+ "(bytes)", "(bytes)");
}
// It takes as a parameter a reference to one of the _hum_* fields, it
@@ -4472,6 +4472,9 @@
size_t prev_live_bytes = r->live_bytes();
size_t next_live_bytes = r->next_live_bytes();
double gc_eff = r->gc_efficiency();
+ size_t remset_bytes = r->rem_set()->mem_size();
+ size_t strong_code_roots_bytes = r->rem_set()->strong_code_roots_mem_size();
+
if (r->used() == 0) {
type = "FREE";
} else if (r->is_survivor()) {
@@ -4505,6 +4508,8 @@
_total_capacity_bytes += capacity_bytes;
_total_prev_live_bytes += prev_live_bytes;
_total_next_live_bytes += next_live_bytes;
+ _total_remset_bytes += remset_bytes;
+ _total_strong_code_roots_bytes += strong_code_roots_bytes;
// Print a line for this particular region.
_out->print_cr(G1PPRL_LINE_PREFIX
@@ -4513,14 +4518,19 @@
G1PPRL_BYTE_FORMAT
G1PPRL_BYTE_FORMAT
G1PPRL_BYTE_FORMAT
- G1PPRL_DOUBLE_FORMAT,
+ G1PPRL_DOUBLE_FORMAT
+ G1PPRL_BYTE_FORMAT
+ G1PPRL_BYTE_FORMAT,
type, bottom, end,
- used_bytes, prev_live_bytes, next_live_bytes, gc_eff);
+ used_bytes, prev_live_bytes, next_live_bytes, gc_eff,
+ remset_bytes, strong_code_roots_bytes);
return false;
}
G1PrintRegionLivenessInfoClosure::~G1PrintRegionLivenessInfoClosure() {
+ // add static memory usages to remembered set sizes
+ _total_remset_bytes += HeapRegionRemSet::fl_mem_size() + HeapRegionRemSet::static_mem_size();
// Print the footer of the output.
_out->print_cr(G1PPRL_LINE_PREFIX);
_out->print_cr(G1PPRL_LINE_PREFIX
@@ -4528,13 +4538,17 @@
G1PPRL_SUM_MB_FORMAT("capacity")
G1PPRL_SUM_MB_PERC_FORMAT("used")
G1PPRL_SUM_MB_PERC_FORMAT("prev-live")
- G1PPRL_SUM_MB_PERC_FORMAT("next-live"),
+ G1PPRL_SUM_MB_PERC_FORMAT("next-live")
+ G1PPRL_SUM_MB_FORMAT("remset")
+ G1PPRL_SUM_MB_FORMAT("code-roots"),
bytes_to_mb(_total_capacity_bytes),
bytes_to_mb(_total_used_bytes),
perc(_total_used_bytes, _total_capacity_bytes),
bytes_to_mb(_total_prev_live_bytes),
perc(_total_prev_live_bytes, _total_capacity_bytes),
bytes_to_mb(_total_next_live_bytes),
- perc(_total_next_live_bytes, _total_capacity_bytes));
+ perc(_total_next_live_bytes, _total_capacity_bytes),
+ bytes_to_mb(_total_remset_bytes),
+ bytes_to_mb(_total_strong_code_roots_bytes));
_out->cr();
}
diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp
index c2acd84..f6b90e7 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp
@@ -1224,6 +1224,12 @@
size_t _hum_prev_live_bytes;
size_t _hum_next_live_bytes;
+ // Accumulator for the remembered set size
+ size_t _total_remset_bytes;
+
+ // Accumulator for strong code roots memory size
+ size_t _total_strong_code_roots_bytes;
+
static double perc(size_t val, size_t total) {
if (total == 0) {
return 0.0;
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
index f40c4c1..c0d58b9 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
@@ -23,6 +23,7 @@
*/
#include "precompiled.hpp"
+#include "code/codeCache.hpp"
#include "code/icBuffer.hpp"
#include "gc_implementation/g1/bufferingOopClosure.hpp"
#include "gc_implementation/g1/concurrentG1Refine.hpp"
@@ -1159,20 +1160,27 @@
ModRefBarrierSet* _mr_bs;
public:
PostMCRemSetClearClosure(G1CollectedHeap* g1h, ModRefBarrierSet* mr_bs) :
- _g1h(g1h), _mr_bs(mr_bs) { }
+ _g1h(g1h), _mr_bs(mr_bs) {}
+
bool doHeapRegion(HeapRegion* r) {
+ HeapRegionRemSet* hrrs = r->rem_set();
+
if (r->continuesHumongous()) {
+ // We'll assert that the strong code root list and RSet is empty
+ assert(hrrs->strong_code_roots_list_length() == 0, "sanity");
+ assert(hrrs->occupied() == 0, "RSet should be empty");
return false;
}
+
_g1h->reset_gc_time_stamps(r);
- HeapRegionRemSet* hrrs = r->rem_set();
- if (hrrs != NULL) hrrs->clear();
+ hrrs->clear();
// You might think here that we could clear just the cards
// corresponding to the used region. But no: if we leave a dirty card
// in a region we might allocate into, then it would prevent that card
// from being enqueued, and cause it to be missed.
// Re: the performance cost: we shouldn't be doing full GC anyway!
_mr_bs->clear(MemRegion(r->bottom(), r->end()));
+
return false;
}
};
@@ -1252,31 +1260,6 @@
heap_region_iterate(&cl);
}
-double G1CollectedHeap::verify(bool guard, const char* msg) {
- double verify_time_ms = 0.0;
-
- if (guard && total_collections() >= VerifyGCStartAt) {
- double verify_start = os::elapsedTime();
- HandleMark hm; // Discard invalid handles created during verification
- gclog_or_tty->print(msg);
- prepare_for_verify();
- Universe::verify(false /* silent */, VerifyOption_G1UsePrevMarking);
- verify_time_ms = (os::elapsedTime() - verify_start) * 1000;
- }
-
- return verify_time_ms;
-}
-
-void G1CollectedHeap::verify_before_gc() {
- double verify_time_ms = verify(VerifyBeforeGC, " VerifyBeforeGC:");
- g1_policy()->phase_times()->record_verify_before_time_ms(verify_time_ms);
-}
-
-void G1CollectedHeap::verify_after_gc() {
- double verify_time_ms = verify(VerifyAfterGC, " VerifyAfterGC:");
- g1_policy()->phase_times()->record_verify_after_time_ms(verify_time_ms);
-}
-
bool G1CollectedHeap::do_collection(bool explicit_gc,
bool clear_all_soft_refs,
size_t word_size) {
@@ -1409,8 +1392,6 @@
MemoryService::track_memory_usage();
- verify_after_gc();
-
assert(!ref_processor_stw()->discovery_enabled(), "Postcondition");
ref_processor_stw()->verify_no_references_recorded();
@@ -1483,6 +1464,9 @@
heap_region_iterate(&rebuild_rs);
}
+ // Rebuild the strong code root lists for each region
+ rebuild_strong_code_roots();
+
if (true) { // FIXME
// Ask the permanent generation to adjust size for full collections
perm()->compute_new_size();
@@ -1510,6 +1494,8 @@
_hrs.verify_optional();
verify_region_sets_optional();
+ verify_after_gc();
+
// Start a new incremental collection set for the next pause
assert(g1_policy()->collection_set() == NULL, "must be");
g1_policy()->start_incremental_cset_building();
@@ -3163,6 +3149,119 @@
return NULL; // keep some compilers happy
}
+// TODO: VerifyRootsClosure extends OopsInGenClosure so that we can
+// pass it as the perm_blk to SharedHeap::process_strong_roots.
+// When process_strong_roots stop calling perm_blk->younger_refs_iterate
+// we can change this closure to extend the simpler OopClosure.
+class VerifyRootsClosure: public OopsInGenClosure {
+private:
+ G1CollectedHeap* _g1h;
+ VerifyOption _vo;
+ bool _failures;
+public:
+ // _vo == UsePrevMarking -> use "prev" marking information,
+ // _vo == UseNextMarking -> use "next" marking information,
+ // _vo == UseMarkWord -> use mark word from object header.
+ VerifyRootsClosure(VerifyOption vo) :
+ _g1h(G1CollectedHeap::heap()),
+ _vo(vo),
+ _failures(false) { }
+
+ bool failures() { return _failures; }
+
+ template <class T> void do_oop_nv(T* p) {
+ T heap_oop = oopDesc::load_heap_oop(p);
+ if (!oopDesc::is_null(heap_oop)) {
+ oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
+ if (_g1h->is_obj_dead_cond(obj, _vo)) {
+ gclog_or_tty->print_cr("Root location "PTR_FORMAT" "
+ "points to dead obj "PTR_FORMAT, p, (void*) obj);
+ if (_vo == VerifyOption_G1UseMarkWord) {
+ gclog_or_tty->print_cr(" Mark word: "PTR_FORMAT, (void*)(obj->mark()));
+ }
+ obj->print_on(gclog_or_tty);
+ _failures = true;
+ }
+ }
+ }
+
+ void do_oop(oop* p) { do_oop_nv(p); }
+ void do_oop(narrowOop* p) { do_oop_nv(p); }
+};
+
+class G1VerifyCodeRootOopClosure: public OopsInGenClosure {
+ G1CollectedHeap* _g1h;
+ OopClosure* _root_cl;
+ nmethod* _nm;
+ VerifyOption _vo;
+ bool _failures;
+
+ template <class T> void do_oop_work(T* p) {
+ // First verify that this root is live
+ _root_cl->do_oop(p);
+
+ if (!G1VerifyHeapRegionCodeRoots) {
+ // We're not verifying the code roots attached to heap region.
+ return;
+ }
+
+ // Don't check the code roots during marking verification in a full GC
+ if (_vo == VerifyOption_G1UseMarkWord) {
+ return;
+ }
+
+ // Now verify that the current nmethod (which contains p) is
+ // in the code root list of the heap region containing the
+ // object referenced by p.
+
+ T heap_oop = oopDesc::load_heap_oop(p);
+ if (!oopDesc::is_null(heap_oop)) {
+ oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
+
+ if (_g1h->is_in_g1_reserved(obj)) {
+ // Now fetch the region containing the object
+ HeapRegion* hr = _g1h->heap_region_containing(obj);
+ HeapRegionRemSet* hrrs = hr->rem_set();
+ // Verify that the strong code root list for this region
+ // contains the nmethod
+ if (!hrrs->strong_code_roots_list_contains(_nm)) {
+ gclog_or_tty->print_cr("Code root location "PTR_FORMAT" "
+ "from nmethod "PTR_FORMAT" not in strong "
+ "code roots for region ["PTR_FORMAT","PTR_FORMAT")",
+ p, _nm, hr->bottom(), hr->end());
+ _failures = true;
+ }
+ }
+ }
+ }
+
+public:
+ G1VerifyCodeRootOopClosure(G1CollectedHeap* g1h, OopClosure* root_cl, VerifyOption vo):
+ _g1h(g1h), _root_cl(root_cl), _vo(vo), _nm(NULL), _failures(false) {}
+
+ void do_oop(oop* p) { do_oop_work(p); }
+ void do_oop(narrowOop* p) { do_oop_work(p); }
+
+ void set_nmethod(nmethod* nm) { _nm = nm; }
+ bool failures() { return _failures; }
+};
+
+class G1VerifyCodeRootBlobClosure: public CodeBlobClosure {
+ G1VerifyCodeRootOopClosure* _oop_cl;
+
+public:
+ G1VerifyCodeRootBlobClosure(G1VerifyCodeRootOopClosure* oop_cl):
+ _oop_cl(oop_cl) {}
+
+ void do_code_blob(CodeBlob* cb) {
+ nmethod* nm = cb->as_nmethod_or_null();
+ if (nm != NULL) {
+ _oop_cl->set_nmethod(nm);
+ nm->oops_do(_oop_cl);
+ }
+ }
+};
+
class VerifyLivenessOopClosure: public OopClosure {
G1CollectedHeap* _g1h;
VerifyOption _vo;
@@ -3296,42 +3395,6 @@
}
};
-class VerifyRootsClosure: public OopsInGenClosure {
-private:
- G1CollectedHeap* _g1h;
- VerifyOption _vo;
- bool _failures;
-public:
- // _vo == UsePrevMarking -> use "prev" marking information,
- // _vo == UseNextMarking -> use "next" marking information,
- // _vo == UseMarkWord -> use mark word from object header.
- VerifyRootsClosure(VerifyOption vo) :
- _g1h(G1CollectedHeap::heap()),
- _vo(vo),
- _failures(false) { }
-
- bool failures() { return _failures; }
-
- template <class T> void do_oop_nv(T* p) {
- T heap_oop = oopDesc::load_heap_oop(p);
- if (!oopDesc::is_null(heap_oop)) {
- oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
- if (_g1h->is_obj_dead_cond(obj, _vo)) {
- gclog_or_tty->print_cr("Root location "PTR_FORMAT" "
- "points to dead obj "PTR_FORMAT, p, (void*) obj);
- if (_vo == VerifyOption_G1UseMarkWord) {
- gclog_or_tty->print_cr(" Mark word: "PTR_FORMAT, (void*)(obj->mark()));
- }
- obj->print_on(gclog_or_tty);
- _failures = true;
- }
- }
- }
-
- void do_oop(oop* p) { do_oop_nv(p); }
- void do_oop(narrowOop* p) { do_oop_nv(p); }
-};
-
// This is the task used for parallel heap verification.
class G1ParVerifyTask: public AbstractGangTask {
@@ -3366,21 +3429,16 @@
}
};
-void G1CollectedHeap::verify(bool silent) {
- verify(silent, VerifyOption_G1UsePrevMarking);
-}
-
-void G1CollectedHeap::verify(bool silent,
- VerifyOption vo) {
+void G1CollectedHeap::verify(bool silent, VerifyOption vo) {
if (SafepointSynchronize::is_at_safepoint()) {
- if (!silent) { gclog_or_tty->print("Roots (excluding permgen) "); }
- VerifyRootsClosure rootsCl(vo);
-
assert(Thread::current()->is_VM_thread(),
"Expected to be executed serially by the VM thread at this point");
- CodeBlobToOopClosure blobsCl(&rootsCl, /*do_marking=*/ false);
+ if (!silent) { gclog_or_tty->print("Roots (excluding permgen) "); }
+ VerifyRootsClosure rootsCl(vo);
+ G1VerifyCodeRootOopClosure codeRootsCl(this, &rootsCl, vo);
+ G1VerifyCodeRootBlobClosure blobsCl(&codeRootsCl);
// We apply the relevant closures to all the oops in the
// system dictionary, the string table and the code cache.
const int so = SO_AllClasses | SO_Strings | SO_CodeCache;
@@ -3475,6 +3533,34 @@
}
}
+void G1CollectedHeap::verify(bool silent) {
+ verify(silent, VerifyOption_G1UsePrevMarking);
+}
+
+double G1CollectedHeap::verify(bool guard, const char* msg) {
+ double verify_time_ms = 0.0;
+
+ if (guard && total_collections() >= VerifyGCStartAt) {
+ double verify_start = os::elapsedTime();
+ HandleMark hm; // Discard invalid handles created during verification
+ prepare_for_verify();
+ Universe::verify(VerifyOption_G1UsePrevMarking, msg);
+ verify_time_ms = (os::elapsedTime() - verify_start) * 1000;
+ }
+
+ return verify_time_ms;
+}
+
+void G1CollectedHeap::verify_before_gc() {
+ double verify_time_ms = verify(VerifyBeforeGC, " VerifyBeforeGC:");
+ g1_policy()->phase_times()->record_verify_before_time_ms(verify_time_ms);
+}
+
+void G1CollectedHeap::verify_after_gc() {
+ double verify_time_ms = verify(VerifyAfterGC, " VerifyAfterGC:");
+ g1_policy()->phase_times()->record_verify_after_time_ms(verify_time_ms);
+}
+
class PrintRegionClosure: public HeapRegionClosure {
outputStream* _st;
public:
@@ -3618,9 +3704,22 @@
AllocationProfiler::iterate_since_last_gc();
// Fill TLAB's and such
ensure_parsability(true);
+
+ if (G1SummarizeRSetStats && (G1SummarizeRSetStatsPeriod > 0) &&
+ (total_collections() % G1SummarizeRSetStatsPeriod == 0)) {
+ g1_rem_set()->print_periodic_summary_info("Before GC RS summary");
+ }
}
void G1CollectedHeap::gc_epilogue(bool full /* Ignored */) {
+
+ if (G1SummarizeRSetStats &&
+ (G1SummarizeRSetStatsPeriod > 0) &&
+ // we are at the end of the GC. Total collections has already been increased.
+ ((total_collections() - 1) % G1SummarizeRSetStatsPeriod == 0)) {
+ g1_rem_set()->print_periodic_summary_info("After GC RS summary");
+ }
+
// FIXME: what is this about?
// I'm ignoring the "fill_newgen()" call if "alloc_event_enabled"
// is set.
@@ -3882,8 +3981,9 @@
append_secondary_free_list_if_not_empty_with_lock();
}
- assert(check_young_list_well_formed(),
- "young list should be well formed");
+ assert(check_young_list_well_formed(), "young list should be well formed");
+ assert(check_heap_region_claim_values(HeapRegion::InitialClaimValue),
+ "sanity check");
// Don't dynamically change the number of GC threads this early. A value of
// 0 is used to indicate serial work. When parallel work is done,
@@ -4199,11 +4299,6 @@
_gc_tracer_stw->report_gc_end(_gc_timer_stw->gc_end(), _gc_timer_stw->time_partitions());
}
- if (G1SummarizeRSetStats &&
- (G1SummarizeRSetStatsPeriod > 0) &&
- (total_collections() % G1SummarizeRSetStatsPeriod == 0)) {
- g1_rem_set()->print_summary_info();
- }
// It should now be safe to tell the concurrent mark thread to start
// without its logging output interfering with the logging output
// that came from the pause.
@@ -4977,7 +5072,8 @@
scan_root_cl,
&push_heap_rs_cl,
scan_perm_cl,
- worker_id);
+ worker_id,
+ /* manages_code_roots */ true);
pss.end_strong_roots();
{
@@ -5011,67 +5107,6 @@
// *** Common G1 Evacuation Stuff
-// Closures that support the filtering of CodeBlobs scanned during
-// external root scanning.
-
-// Closure applied to reference fields in code blobs (specifically nmethods)
-// to determine whether an nmethod contains references that point into
-// the collection set. Used as a predicate when walking code roots so
-// that only nmethods that point into the collection set are added to the
-// 'marked' list.
-
-class G1FilteredCodeBlobToOopClosure : public CodeBlobToOopClosure {
-
- class G1PointsIntoCSOopClosure : public OopClosure {
- G1CollectedHeap* _g1;
- bool _points_into_cs;
- public:
- G1PointsIntoCSOopClosure(G1CollectedHeap* g1) :
- _g1(g1), _points_into_cs(false) { }
-
- bool points_into_cs() const { return _points_into_cs; }
-
- template <class T>
- void do_oop_nv(T* p) {
- if (!_points_into_cs) {
- T heap_oop = oopDesc::load_heap_oop(p);
- if (!oopDesc::is_null(heap_oop) &&
- _g1->in_cset_fast_test(oopDesc::decode_heap_oop_not_null(heap_oop))) {
- _points_into_cs = true;
- }
- }
- }
-
- virtual void do_oop(oop* p) { do_oop_nv(p); }
- virtual void do_oop(narrowOop* p) { do_oop_nv(p); }
- };
-
- G1CollectedHeap* _g1;
-
-public:
- G1FilteredCodeBlobToOopClosure(G1CollectedHeap* g1, OopClosure* cl) :
- CodeBlobToOopClosure(cl, true), _g1(g1) { }
-
- virtual void do_code_blob(CodeBlob* cb) {
- nmethod* nm = cb->as_nmethod_or_null();
- if (nm != NULL && !(nm->test_oops_do_mark())) {
- G1PointsIntoCSOopClosure predicate_cl(_g1);
- nm->oops_do(&predicate_cl);
-
- if (predicate_cl.points_into_cs()) {
- // At least one of the reference fields or the oop relocations
- // in the nmethod points into the collection set. We have to
- // 'mark' this nmethod.
- // Note: Revisit the following if CodeBlobToOopClosure::do_code_blob()
- // or MarkingCodeBlobClosure::do_code_blob() change.
- if (!nm->test_set_oops_do_mark()) {
- do_newly_marked_nmethod(nm);
- }
- }
- }
- }
-};
-
// This method is run in a GC worker.
void
@@ -5081,7 +5116,8 @@
OopClosure* scan_non_heap_roots,
OopsInHeapRegionClosure* scan_rs,
OopsInGenClosure* scan_perm,
- int worker_i) {
+ int worker_i,
+ bool manages_code_roots) {
// First scan the strong roots, including the perm gen.
double ext_roots_start = os::elapsedTime();
@@ -5091,15 +5127,17 @@
BufferingOopsInGenClosure buf_scan_perm(scan_perm);
buf_scan_perm.set_generation(perm_gen());
- // Walk the code cache w/o buffering, because StarTask cannot handle
- // unaligned oop locations.
- G1FilteredCodeBlobToOopClosure eager_scan_code_roots(this, scan_non_heap_roots);
+ assert(so & SO_CodeCache || scan_rs != NULL, "must scan code roots somehow");
+ // Walk the code cache/strong code roots w/o buffering, because StarTask
+ // cannot handle unaligned oop locations.
+ CodeBlobToOopClosure eager_scan_code_roots(scan_non_heap_roots, true /* do_marking */);
process_strong_roots(false, // no scoping; this is parallel code
collecting_perm_gen, so,
&buf_scan_non_heap_roots,
&eager_scan_code_roots,
- &buf_scan_perm);
+ &buf_scan_perm,
+ manages_code_roots);
// Now the CM ref_processor roots.
if (!_process_strong_tasks->is_task_claimed(G1H_PS_refProcessor_oops_do)) {
@@ -5138,9 +5176,22 @@
}
g1_policy()->phase_times()->record_satb_filtering_time(worker_i, satb_filtering_ms);
+ // If this is an initial mark pause, and we're not scanning
+ // the entire code cache, we need to mark the oops in the
+ // strong code root lists for the regions that are not in
+ // the collection set.
+ // Note all threads participate in this set of root tasks.
+ double mark_strong_code_roots_ms = 0.0;
+ if (g1_policy()->during_initial_mark_pause() && !(so & SO_CodeCache)) {
+ double mark_strong_roots_start = os::elapsedTime();
+ mark_strong_code_roots(worker_i);
+ mark_strong_code_roots_ms = (os::elapsedTime() - mark_strong_roots_start) * 1000.0;
+ }
+ g1_policy()->phase_times()->record_strong_code_root_mark_time(worker_i, mark_strong_code_roots_ms);
+
// Now scan the complement of the collection set.
if (scan_rs != NULL) {
- g1_rem_set()->oops_into_collection_set_do(scan_rs, worker_i);
+ g1_rem_set()->oops_into_collection_set_do(scan_rs, &eager_scan_code_roots, worker_i);
}
_process_strong_tasks->all_tasks_completed();
@@ -5153,6 +5204,100 @@
SharedHeap::process_weak_roots(root_closure, &roots_in_blobs, non_root_closure);
}
+class G1StringSymbolTableUnlinkTask : public AbstractGangTask {
+private:
+ BoolObjectClosure* _is_alive;
+ int _initial_string_table_size;
+ int _initial_symbol_table_size;
+
+ bool _process_strings;
+ int _strings_processed;
+ int _strings_removed;
+
+ bool _process_symbols;
+ int _symbols_processed;
+ int _symbols_removed;
+public:
+ G1StringSymbolTableUnlinkTask(BoolObjectClosure* is_alive, bool process_strings, bool process_symbols) :
+ AbstractGangTask("Par String/Symbol table unlink"), _is_alive(is_alive),
+ _process_strings(process_strings), _strings_processed(0), _strings_removed(0),
+ _process_symbols(process_symbols), _symbols_processed(0), _symbols_removed(0) {
+
+ _initial_string_table_size = StringTable::the_table()->table_size();
+ _initial_symbol_table_size = SymbolTable::the_table()->table_size();
+ if (process_strings) {
+ StringTable::clear_parallel_claimed_index();
+ }
+ if (process_symbols) {
+ SymbolTable::clear_parallel_claimed_index();
+ }
+ }
+
+ ~G1StringSymbolTableUnlinkTask() {
+ guarantee(!_process_strings || StringTable::parallel_claimed_index() >= _initial_string_table_size,
+ err_msg("claim value "INT32_FORMAT" after unlink less than initial string table size "INT32_FORMAT,
+ StringTable::parallel_claimed_index(), _initial_string_table_size));
+ guarantee(!_process_symbols || SymbolTable::parallel_claimed_index() >= _initial_symbol_table_size,
+ err_msg("claim value "INT32_FORMAT" after unlink less than initial symbol table size "INT32_FORMAT,
+ SymbolTable::parallel_claimed_index(), _initial_symbol_table_size));
+
+ }
+
+ void work(uint worker_id) {
+ if (G1CollectedHeap::use_parallel_gc_threads()) {
+ int strings_processed = 0;
+ int strings_removed = 0;
+ int symbols_processed = 0;
+ int symbols_removed = 0;
+ if (_process_strings) {
+ StringTable::possibly_parallel_unlink(_is_alive, &strings_processed, &strings_removed);
+ Atomic::add(strings_processed, &_strings_processed);
+ Atomic::add(strings_removed, &_strings_removed);
+ }
+ if (_process_symbols) {
+ SymbolTable::possibly_parallel_unlink(&symbols_processed, &symbols_removed);
+ Atomic::add(symbols_processed, &_symbols_processed);
+ Atomic::add(symbols_removed, &_symbols_removed);
+ }
+ } else {
+ if (_process_strings) {
+ StringTable::unlink(_is_alive, &_strings_processed, &_strings_removed);
+ }
+ if (_process_symbols) {
+ SymbolTable::unlink(&_symbols_processed, &_symbols_removed);
+ }
+ }
+ }
+
+ size_t strings_processed() const { return (size_t)_strings_processed; }
+ size_t strings_removed() const { return (size_t)_strings_removed; }
+
+ size_t symbols_processed() const { return (size_t)_symbols_processed; }
+ size_t symbols_removed() const { return (size_t)_symbols_removed; }
+};
+
+void G1CollectedHeap::unlink_string_and_symbol_table(BoolObjectClosure* is_alive,
+ bool process_strings, bool process_symbols) {
+ uint n_workers = (G1CollectedHeap::use_parallel_gc_threads() ?
+ _g1h->workers()->active_workers() : 1);
+
+ G1StringSymbolTableUnlinkTask g1_unlink_task(is_alive, process_strings, process_symbols);
+ if (G1CollectedHeap::use_parallel_gc_threads()) {
+ set_par_threads(n_workers);
+ workers()->run_task(&g1_unlink_task);
+ set_par_threads(0);
+ } else {
+ g1_unlink_task.work(0);
+ }
+ if (G1TraceStringSymbolTableScrubbing) {
+ gclog_or_tty->print_cr("Cleaned string and symbol table, "
+ "strings: "SIZE_FORMAT" processed, "SIZE_FORMAT" removed, "
+ "symbols: "SIZE_FORMAT" processed, "SIZE_FORMAT" removed",
+ g1_unlink_task.strings_processed(), g1_unlink_task.strings_removed(),
+ g1_unlink_task.symbols_processed(), g1_unlink_task.symbols_removed());
+ }
+}
+
// Weak Reference Processing support
// An always "is_alive" closure that is used to preserve referents.
@@ -5763,9 +5908,6 @@
process_discovered_references(n_workers);
// Weak root processing.
- // Note: when JSR 292 is enabled and code blobs can contain
- // non-perm oops then we will need to process the code blobs
- // here too.
{
G1STWIsAliveClosure is_alive(this);
G1KeepAliveClosure keep_alive(this);
@@ -5781,6 +5923,17 @@
hot_card_cache->reset_hot_cache();
hot_card_cache->set_use_cache(true);
+ // Migrate the strong code roots attached to each region in
+ // the collection set. Ideally we would like to do this
+ // after we have finished the scanning/evacuation of the
+ // strong code roots for a particular heap region.
+ migrate_strong_code_roots();
+
+ if (g1_policy()->during_initial_mark_pause()) {
+ // Reset the claim values set during marking the strong code roots
+ reset_heap_region_claim_values();
+ }
+
finalize_for_evac_failure();
if (evacuation_failed()) {
@@ -6577,3 +6730,234 @@
_humongous_set.verify_end();
_free_list.verify_end();
}
+
+// Optimized nmethod scanning
+
+class RegisterNMethodOopClosure: public OopClosure {
+ G1CollectedHeap* _g1h;
+ nmethod* _nm;
+
+ template <class T> void do_oop_work(T* p) {
+ T heap_oop = oopDesc::load_heap_oop(p);
+ if (!oopDesc::is_null(heap_oop)) {
+ oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
+ HeapRegion* hr = _g1h->heap_region_containing(obj);
+ if (hr == NULL) {
+ // reference into perm gen - ignore.
+ assert(_g1h->is_in_permanent(obj), "must be a reference into perm gen");
+ return;
+ }
+ assert(!hr->continuesHumongous(),
+ err_msg("trying to add code root "PTR_FORMAT" in continuation of humongous region "HR_FORMAT
+ " starting at "HR_FORMAT,
+ _nm, HR_FORMAT_PARAMS(hr), HR_FORMAT_PARAMS(hr->humongous_start_region())));
+
+ // HeapRegion::add_strong_code_root() avoids adding duplicate
+ // entries but having duplicates is OK since we "mark" nmethods
+ // as visited when we scan the strong code root lists during the GC.
+ hr->add_strong_code_root(_nm);
+ assert(hr->rem_set()->strong_code_roots_list_contains(_nm),
+ err_msg("failed to add code root "PTR_FORMAT" to remembered set of region "HR_FORMAT,
+ _nm, HR_FORMAT_PARAMS(hr)));
+ }
+ }
+
+public:
+ RegisterNMethodOopClosure(G1CollectedHeap* g1h, nmethod* nm) :
+ _g1h(g1h), _nm(nm) {}
+
+ void do_oop(oop* p) { do_oop_work(p); }
+ void do_oop(narrowOop* p) { do_oop_work(p); }
+};
+
+class UnregisterNMethodOopClosure: public OopClosure {
+ G1CollectedHeap* _g1h;
+ nmethod* _nm;
+
+ template <class T> void do_oop_work(T* p) {
+ T heap_oop = oopDesc::load_heap_oop(p);
+ if (!oopDesc::is_null(heap_oop)) {
+ oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
+ HeapRegion* hr = _g1h->heap_region_containing(obj);
+ if (hr == NULL) {
+ // reference into perm gen - ignore.
+ assert(_g1h->is_in_permanent(obj), "must be a reference into perm gen");
+ return;
+ }
+ assert(!hr->continuesHumongous(),
+ err_msg("trying to remove code root "PTR_FORMAT" in continuation of humongous region "HR_FORMAT
+ " starting at "HR_FORMAT,
+ _nm, HR_FORMAT_PARAMS(hr), HR_FORMAT_PARAMS(hr->humongous_start_region())));
+
+ hr->remove_strong_code_root(_nm);
+ assert(!hr->rem_set()->strong_code_roots_list_contains(_nm),
+ err_msg("failed to remove code root "PTR_FORMAT" of region "HR_FORMAT,
+ _nm, HR_FORMAT_PARAMS(hr))); }
+ }
+
+public:
+ UnregisterNMethodOopClosure(G1CollectedHeap* g1h, nmethod* nm) :
+ _g1h(g1h), _nm(nm) {}
+
+ void do_oop(oop* p) { do_oop_work(p); }
+ void do_oop(narrowOop* p) { do_oop_work(p); }
+};
+
+void G1CollectedHeap::register_nmethod(nmethod* nm) {
+ CollectedHeap::register_nmethod(nm);
+
+ guarantee(nm != NULL, "sanity");
+ RegisterNMethodOopClosure reg_cl(this, nm);
+ nm->oops_do(®_cl);
+}
+
+void G1CollectedHeap::unregister_nmethod(nmethod* nm) {
+ CollectedHeap::unregister_nmethod(nm);
+
+ guarantee(nm != NULL, "sanity");
+ UnregisterNMethodOopClosure reg_cl(this, nm);
+ nm->oops_do(®_cl, false, true);
+}
+
+class MigrateCodeRootsHeapRegionClosure: public HeapRegionClosure {
+public:
+ bool doHeapRegion(HeapRegion *hr) {
+ assert(!hr->isHumongous(),
+ err_msg("humongous region "HR_FORMAT" should not have been added to collection set",
+ HR_FORMAT_PARAMS(hr)));
+ hr->migrate_strong_code_roots();
+ return false;
+ }
+};
+
+void G1CollectedHeap::migrate_strong_code_roots() {
+ MigrateCodeRootsHeapRegionClosure cl;
+ double migrate_start = os::elapsedTime();
+ collection_set_iterate(&cl);
+ double migration_time_ms = (os::elapsedTime() - migrate_start) * 1000.0;
+ g1_policy()->phase_times()->record_strong_code_root_migration_time(migration_time_ms);
+}
+
+// Mark all the code roots that point into regions *not* in the
+// collection set.
+//
+// Note we do not want to use a "marking" CodeBlobToOopClosure while
+// walking the the code roots lists of regions not in the collection
+// set. Suppose we have an nmethod (M) that points to objects in two
+// separate regions - one in the collection set (R1) and one not (R2).
+// Using a "marking" CodeBlobToOopClosure here would result in "marking"
+// nmethod M when walking the code roots for R1. When we come to scan
+// the code roots for R2, we would see that M is already marked and it
+// would be skipped and the objects in R2 that are referenced from M
+// would not be evacuated.
+
+class MarkStrongCodeRootCodeBlobClosure: public CodeBlobClosure {
+
+ class MarkStrongCodeRootOopClosure: public OopClosure {
+ ConcurrentMark* _cm;
+ HeapRegion* _hr;
+ uint _worker_id;
+
+ template <class T> void do_oop_work(T* p) {
+ T heap_oop = oopDesc::load_heap_oop(p);
+ if (!oopDesc::is_null(heap_oop)) {
+ oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
+ // Only mark objects in the region (which is assumed
+ // to be not in the collection set).
+ if (_hr->is_in(obj)) {
+ _cm->grayRoot(obj, (size_t) obj->size(), _worker_id);
+ }
+ }
+ }
+
+ public:
+ MarkStrongCodeRootOopClosure(ConcurrentMark* cm, HeapRegion* hr, uint worker_id) :
+ _cm(cm), _hr(hr), _worker_id(worker_id) {
+ assert(!_hr->in_collection_set(), "sanity");
+ }
+
+ void do_oop(narrowOop* p) { do_oop_work(p); }
+ void do_oop(oop* p) { do_oop_work(p); }
+ };
+
+ MarkStrongCodeRootOopClosure _oop_cl;
+
+public:
+ MarkStrongCodeRootCodeBlobClosure(ConcurrentMark* cm, HeapRegion* hr, uint worker_id):
+ _oop_cl(cm, hr, worker_id) {}
+
+ void do_code_blob(CodeBlob* cb) {
+ nmethod* nm = (cb == NULL) ? NULL : cb->as_nmethod_or_null();
+ if (nm != NULL) {
+ nm->oops_do(&_oop_cl);
+ }
+ }
+};
+
+class MarkStrongCodeRootsHRClosure: public HeapRegionClosure {
+ G1CollectedHeap* _g1h;
+ uint _worker_id;
+
+public:
+ MarkStrongCodeRootsHRClosure(G1CollectedHeap* g1h, uint worker_id) :
+ _g1h(g1h), _worker_id(worker_id) {}
+
+ bool doHeapRegion(HeapRegion *hr) {
+ HeapRegionRemSet* hrrs = hr->rem_set();
+ if (hr->continuesHumongous()) {
+ // Code roots should never be attached to a continuation of a humongous region
+ assert(hrrs->strong_code_roots_list_length() == 0,
+ err_msg("code roots should never be attached to continuations of humongous region "HR_FORMAT
+ " starting at "HR_FORMAT", but has "INT32_FORMAT,
+ HR_FORMAT_PARAMS(hr), HR_FORMAT_PARAMS(hr->humongous_start_region()),
+ hrrs->strong_code_roots_list_length()));
+ return false;
+ }
+
+ if (hr->in_collection_set()) {
+ // Don't mark code roots into regions in the collection set here.
+ // They will be marked when we scan them.
+ return false;
+ }
+
+ MarkStrongCodeRootCodeBlobClosure cb_cl(_g1h->concurrent_mark(), hr, _worker_id);
+ hr->strong_code_roots_do(&cb_cl);
+ return false;
+ }
+};
+
+void G1CollectedHeap::mark_strong_code_roots(uint worker_id) {
+ MarkStrongCodeRootsHRClosure cl(this, worker_id);
+ if (G1CollectedHeap::use_parallel_gc_threads()) {
+ heap_region_par_iterate_chunked(&cl,
+ worker_id,
+ workers()->active_workers(),
+ HeapRegion::ParMarkRootClaimValue);
+ } else {
+ heap_region_iterate(&cl);
+ }
+}
+
+class RebuildStrongCodeRootClosure: public CodeBlobClosure {
+ G1CollectedHeap* _g1h;
+
+public:
+ RebuildStrongCodeRootClosure(G1CollectedHeap* g1h) :
+ _g1h(g1h) {}
+
+ void do_code_blob(CodeBlob* cb) {
+ nmethod* nm = (cb != NULL) ? cb->as_nmethod_or_null() : NULL;
+ if (nm == NULL) {
+ return;
+ }
+
+ if (ScavengeRootsInCode && nm->detect_scavenge_root_oops()) {
+ _g1h->register_nmethod(nm);
+ }
+ }
+};
+
+void G1CollectedHeap::rebuild_strong_code_roots() {
+ RebuildStrongCodeRootClosure blob_cl(this);
+ CodeCache::blobs_do(&blob_cl);
+}
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
index a42e84a..361b717 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
@@ -46,6 +46,7 @@
// may combine concurrent marking with parallel, incremental compaction of
// heap subsets that will yield large amounts of garbage.
+// Forward declarations
class HeapRegion;
class HRRSCleanupTask;
class PermanentGenerationSpec;
@@ -69,6 +70,7 @@
class G1NewTracer;
class G1OldTracer;
class EvacuationFailedInfo;
+class nmethod;
class Ticks;
typedef OverflowTaskQueue<StarTask, mtGC> RefToScanQueue;
@@ -164,19 +166,6 @@
: G1AllocRegion("Mutator Alloc Region", false /* bot_updates */) { }
};
-// The G1 STW is alive closure.
-// An instance is embedded into the G1CH and used as the
-// (optional) _is_alive_non_header closure in the STW
-// reference processor. It is also extensively used during
-// reference processing during STW evacuation pauses.
-class G1STWIsAliveClosure: public BoolObjectClosure {
- G1CollectedHeap* _g1;
-public:
- G1STWIsAliveClosure(G1CollectedHeap* g1) : _g1(g1) {}
- void do_object(oop p) { assert(false, "Do not call."); }
- bool do_object_b(oop p);
-};
-
class SurvivorGCAllocRegion : public G1AllocRegion {
protected:
virtual HeapRegion* allocate_new_region(size_t word_size, bool force);
@@ -195,6 +184,19 @@
: G1AllocRegion("Old GC Alloc Region", true /* bot_updates */) { }
};
+// The G1 STW is alive closure.
+// An instance is embedded into the G1CH and used as the
+// (optional) _is_alive_non_header closure in the STW
+// reference processor. It is also extensively used during
+// reference processing during STW evacuation pauses.
+class G1STWIsAliveClosure: public BoolObjectClosure {
+ G1CollectedHeap* _g1;
+public:
+ G1STWIsAliveClosure(G1CollectedHeap* g1) : _g1(g1) {}
+ void do_object(oop p) { assert(false, "Do not call."); }
+ bool do_object_b(oop p);
+};
+
class RefineCardTableEntryClosure;
class G1CollectedHeap : public SharedHeap {
@@ -836,7 +838,8 @@
OopClosure* scan_non_heap_roots,
OopsInHeapRegionClosure* scan_rs,
OopsInGenClosure* scan_perm,
- int worker_i);
+ int worker_i,
+ bool manages_code_roots = false);
// Apply "blk" to all the weak roots of the system. These include
// JNI weak roots, the code cache, system dictionary, symbol table,
@@ -1593,41 +1596,6 @@
virtual jlong millis_since_last_gc();
- // Perform any cleanup actions necessary before allowing a verification.
- virtual void prepare_for_verify();
-
- // Perform verification.
-
- // vo == UsePrevMarking -> use "prev" marking information,
- // vo == UseNextMarking -> use "next" marking information
- // vo == UseMarkWord -> use the mark word in the object header
- //
- // NOTE: Only the "prev" marking information is guaranteed to be
- // consistent most of the time, so most calls to this should use
- // vo == UsePrevMarking.
- // Currently, there is only one case where this is called with
- // vo == UseNextMarking, which is to verify the "next" marking
- // information at the end of remark.
- // Currently there is only one place where this is called with
- // vo == UseMarkWord, which is to verify the marking during a
- // full GC.
- void verify(bool silent, VerifyOption vo);
-
- // Override; it uses the "prev" marking information
- virtual void verify(bool silent);
-
- virtual void print_on(outputStream* st) const;
- virtual void print_extended_on(outputStream* st) const;
-
- virtual void print_gc_threads_on(outputStream* st) const;
- virtual void gc_threads_do(ThreadClosure* tc) const;
-
- // Override
- void print_tracing_info() const;
-
- // The following two methods are helpful for debugging RSet issues.
- void print_cset_rsets() PRODUCT_RETURN;
- void print_all_rsets() PRODUCT_RETURN;
// Convenience function to be used in situations where the heap type can be
// asserted to be this type.
@@ -1685,7 +1653,6 @@
// then call the region version of the same function.
// Added if it is in permanent gen it isn't dead.
- // Added if it is NULL it isn't dead.
bool is_obj_dead(const oop obj) const {
const HeapRegion* hr = heap_region_containing(obj);
@@ -1709,13 +1676,90 @@
else return is_obj_ill(obj, hr);
}
+ bool allocated_since_marking(oop obj, HeapRegion* hr, VerifyOption vo);
+ HeapWord* top_at_mark_start(HeapRegion* hr, VerifyOption vo);
+ bool is_marked(oop obj, VerifyOption vo);
+ const char* top_at_mark_start_str(VerifyOption vo);
+
+ ConcurrentMark* concurrent_mark() const { return _cm; }
+
+ // Refinement
+
+ ConcurrentG1Refine* concurrent_g1_refine() const { return _cg1r; }
+
+ // The dirty cards region list is used to record a subset of regions
+ // whose cards need clearing. The list if populated during the
+ // remembered set scanning and drained during the card table
+ // cleanup. Although the methods are reentrant, population/draining
+ // phases must not overlap. For synchronization purposes the last
+ // element on the list points to itself.
+ HeapRegion* _dirty_cards_region_list;
+ void push_dirty_cards_region(HeapRegion* hr);
+ HeapRegion* pop_dirty_cards_region();
+
+ // Optimized nmethod scanning support routines
+
+ // Register the given nmethod with the G1 heap
+ virtual void register_nmethod(nmethod* nm);
+
+ // Unregister the given nmethod from the G1 heap
+ virtual void unregister_nmethod(nmethod* nm);
+
+ // Migrate the nmethods in the code root lists of the regions
+ // in the collection set to regions in to-space. In the event
+ // of an evacuation failure, nmethods that reference objects
+ // that were not successfullly evacuated are not migrated.
+ void migrate_strong_code_roots();
+
+ // During an initial mark pause, mark all the code roots that
+ // point into regions *not* in the collection set.
+ void mark_strong_code_roots(uint worker_id);
+
+ // Rebuild the stong code root lists for each region
+ // after a full GC
+ void rebuild_strong_code_roots();
+
+ // Delete entries for dead interned string and clean up unreferenced symbols
+ // in symbol table, possibly in parallel.
+ void unlink_string_and_symbol_table(BoolObjectClosure* is_alive, bool unlink_strings = true, bool unlink_symbols = true);
+
+ // Verification
+
+ // The following is just to alert the verification code
+ // that a full collection has occurred and that the
+ // remembered sets are no longer up to date.
+ bool _full_collection;
+ void set_full_collection() { _full_collection = true;}
+ void clear_full_collection() {_full_collection = false;}
+ bool full_collection() {return _full_collection;}
+
+ // Perform any cleanup actions necessary before allowing a verification.
+ virtual void prepare_for_verify();
+
+ // Perform verification.
+
+ // vo == UsePrevMarking -> use "prev" marking information,
+ // vo == UseNextMarking -> use "next" marking information
+ // vo == UseMarkWord -> use the mark word in the object header
+ //
+ // NOTE: Only the "prev" marking information is guaranteed to be
+ // consistent most of the time, so most calls to this should use
+ // vo == UsePrevMarking.
+ // Currently, there is only one case where this is called with
+ // vo == UseNextMarking, which is to verify the "next" marking
+ // information at the end of remark.
+ // Currently there is only one place where this is called with
+ // vo == UseMarkWord, which is to verify the marking during a
+ // full GC.
+ void verify(bool silent, VerifyOption vo);
+
+ // Override; it uses the "prev" marking information
+ virtual void verify(bool silent);
+
// The methods below are here for convenience and dispatch the
// appropriate method depending on value of the given VerifyOption
- // parameter. The options for that parameter are:
- //
- // vo == UsePrevMarking -> use "prev" marking information,
- // vo == UseNextMarking -> use "next" marking information,
- // vo == UseMarkWord -> use mark word from object header
+ // parameter. The values for that parameter, and their meanings,
+ // are the same as those above.
bool is_obj_dead_cond(const oop obj,
const HeapRegion* hr,
@@ -1740,31 +1784,20 @@
return false; // keep some compilers happy
}
- bool allocated_since_marking(oop obj, HeapRegion* hr, VerifyOption vo);
- HeapWord* top_at_mark_start(HeapRegion* hr, VerifyOption vo);
- bool is_marked(oop obj, VerifyOption vo);
- const char* top_at_mark_start_str(VerifyOption vo);
+ // Printing
- // The following is just to alert the verification code
- // that a full collection has occurred and that the
- // remembered sets are no longer up to date.
- bool _full_collection;
- void set_full_collection() { _full_collection = true;}
- void clear_full_collection() {_full_collection = false;}
- bool full_collection() {return _full_collection;}
+ virtual void print_on(outputStream* st) const;
+ virtual void print_extended_on(outputStream* st) const;
- ConcurrentMark* concurrent_mark() const { return _cm; }
- ConcurrentG1Refine* concurrent_g1_refine() const { return _cg1r; }
+ virtual void print_gc_threads_on(outputStream* st) const;
+ virtual void gc_threads_do(ThreadClosure* tc) const;
- // The dirty cards region list is used to record a subset of regions
- // whose cards need clearing. The list if populated during the
- // remembered set scanning and drained during the card table
- // cleanup. Although the methods are reentrant, population/draining
- // phases must not overlap. For synchronization purposes the last
- // element on the list points to itself.
- HeapRegion* _dirty_cards_region_list;
- void push_dirty_cards_region(HeapRegion* hr);
- HeapRegion* pop_dirty_cards_region();
+ // Override
+ void print_tracing_info() const;
+
+ // The following two methods are helpful for debugging RSet issues.
+ void print_cset_rsets() PRODUCT_RETURN;
+ void print_all_rsets() PRODUCT_RETURN;
public:
void stop_conc_gc_threads();
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp
index 1559de6..8f36451 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp
@@ -161,6 +161,8 @@
_last_update_rs_times_ms(_max_gc_threads, "%.1lf"),
_last_update_rs_processed_buffers(_max_gc_threads, "%d"),
_last_scan_rs_times_ms(_max_gc_threads, "%.1lf"),
+ _last_strong_code_root_scan_times_ms(_max_gc_threads, "%.1lf"),
+ _last_strong_code_root_mark_times_ms(_max_gc_threads, "%.1lf"),
_last_obj_copy_times_ms(_max_gc_threads, "%.1lf"),
_last_termination_times_ms(_max_gc_threads, "%.1lf"),
_last_termination_attempts(_max_gc_threads, SIZE_FORMAT),
@@ -182,6 +184,8 @@
_last_update_rs_times_ms.reset();
_last_update_rs_processed_buffers.reset();
_last_scan_rs_times_ms.reset();
+ _last_strong_code_root_scan_times_ms.reset();
+ _last_strong_code_root_mark_times_ms.reset();
_last_obj_copy_times_ms.reset();
_last_termination_times_ms.reset();
_last_termination_attempts.reset();
@@ -197,6 +201,8 @@
_last_update_rs_times_ms.verify();
_last_update_rs_processed_buffers.verify();
_last_scan_rs_times_ms.verify();
+ _last_strong_code_root_scan_times_ms.verify();
+ _last_strong_code_root_mark_times_ms.verify();
_last_obj_copy_times_ms.verify();
_last_termination_times_ms.verify();
_last_termination_attempts.verify();
@@ -210,6 +216,8 @@
_last_satb_filtering_times_ms.get(i) +
_last_update_rs_times_ms.get(i) +
_last_scan_rs_times_ms.get(i) +
+ _last_strong_code_root_scan_times_ms.get(i) +
+ _last_strong_code_root_mark_times_ms.get(i) +
_last_obj_copy_times_ms.get(i) +
_last_termination_times_ms.get(i);
@@ -239,6 +247,9 @@
// Now subtract the time taken to fix up roots in generated code
misc_time_ms += _cur_collection_code_root_fixup_time_ms;
+ // Strong code root migration time
+ misc_time_ms += _cur_strong_code_root_migration_time_ms;
+
// Subtract the time taken to clean the card table from the
// current value of "other time"
misc_time_ms += _cur_clear_ct_time_ms;
@@ -257,9 +268,13 @@
if (_last_satb_filtering_times_ms.sum() > 0.0) {
_last_satb_filtering_times_ms.print(2, "SATB Filtering (ms)");
}
+ if (_last_strong_code_root_mark_times_ms.sum() > 0.0) {
+ _last_strong_code_root_mark_times_ms.print(2, "Code Root Marking (ms)");
+ }
_last_update_rs_times_ms.print(2, "Update RS (ms)");
_last_update_rs_processed_buffers.print(3, "Processed Buffers");
_last_scan_rs_times_ms.print(2, "Scan RS (ms)");
+ _last_strong_code_root_scan_times_ms.print(2, "Code Root Scanning (ms)");
_last_obj_copy_times_ms.print(2, "Object Copy (ms)");
_last_termination_times_ms.print(2, "Termination (ms)");
if (G1Log::finest()) {
@@ -273,12 +288,17 @@
if (_last_satb_filtering_times_ms.sum() > 0.0) {
_last_satb_filtering_times_ms.print(1, "SATB Filtering (ms)");
}
+ if (_last_strong_code_root_mark_times_ms.sum() > 0.0) {
+ _last_strong_code_root_mark_times_ms.print(1, "Code Root Marking (ms)");
+ }
_last_update_rs_times_ms.print(1, "Update RS (ms)");
_last_update_rs_processed_buffers.print(2, "Processed Buffers");
_last_scan_rs_times_ms.print(1, "Scan RS (ms)");
+ _last_strong_code_root_scan_times_ms.print(1, "Code Root Scanning (ms)");
_last_obj_copy_times_ms.print(1, "Object Copy (ms)");
}
print_stats(1, "Code Root Fixup", _cur_collection_code_root_fixup_time_ms);
+ print_stats(1, "Code Root Migration", _cur_strong_code_root_migration_time_ms);
print_stats(1, "Clear CT", _cur_clear_ct_time_ms);
double misc_time_ms = pause_time_sec * MILLIUNITS - accounted_time_ms();
print_stats(1, "Other", misc_time_ms);
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp
index a1c5739..b2de97d 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp
@@ -119,6 +119,8 @@
WorkerDataArray<double> _last_update_rs_times_ms;
WorkerDataArray<int> _last_update_rs_processed_buffers;
WorkerDataArray<double> _last_scan_rs_times_ms;
+ WorkerDataArray<double> _last_strong_code_root_scan_times_ms;
+ WorkerDataArray<double> _last_strong_code_root_mark_times_ms;
WorkerDataArray<double> _last_obj_copy_times_ms;
WorkerDataArray<double> _last_termination_times_ms;
WorkerDataArray<size_t> _last_termination_attempts;
@@ -128,6 +130,7 @@
double _cur_collection_par_time_ms;
double _cur_collection_code_root_fixup_time_ms;
+ double _cur_strong_code_root_migration_time_ms;
double _cur_clear_ct_time_ms;
double _cur_ref_proc_time_ms;
@@ -179,6 +182,14 @@
_last_scan_rs_times_ms.set(worker_i, ms);
}
+ void record_strong_code_root_scan_time(uint worker_i, double ms) {
+ _last_strong_code_root_scan_times_ms.set(worker_i, ms);
+ }
+
+ void record_strong_code_root_mark_time(uint worker_i, double ms) {
+ _last_strong_code_root_mark_times_ms.set(worker_i, ms);
+ }
+
void record_obj_copy_time(uint worker_i, double ms) {
_last_obj_copy_times_ms.set(worker_i, ms);
}
@@ -208,6 +219,10 @@
_cur_collection_code_root_fixup_time_ms = ms;
}
+ void record_strong_code_root_migration_time(double ms) {
+ _cur_strong_code_root_migration_time_ms = ms;
+ }
+
void record_ref_proc_time(double ms) {
_cur_ref_proc_time_ms = ms;
}
@@ -294,6 +309,14 @@
return _last_scan_rs_times_ms.average();
}
+ double average_last_strong_code_root_scan_time(){
+ return _last_strong_code_root_scan_times_ms.average();
+ }
+
+ double average_last_strong_code_root_mark_time(){
+ return _last_strong_code_root_mark_times_ms.average();
+ }
+
double average_last_obj_copy_time() {
return _last_obj_copy_times_ms.average();
}
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp
index 3e7825d..fa37b9a 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp
@@ -177,10 +177,8 @@
GenMarkSweep::follow_mdo_weak_refs();
assert(GenMarkSweep::_marking_stack.is_empty(), "just drained");
- // Visit interned string tables and delete unmarked oops
- StringTable::unlink(&GenMarkSweep::is_alive);
- // Clean up unreferenced symbols in symbol table.
- SymbolTable::unlink();
+ // Delete entries for dead interned string and clean up unreferenced symbols in symbol table.
+ G1CollectedHeap::heap()->unlink_string_and_symbol_table(&GenMarkSweep::is_alive);
assert(GenMarkSweep::_marking_stack.is_empty(),
"stack should be empty by now");
@@ -188,7 +186,6 @@
if (VerifyDuringGC) {
HandleMark hm; // handle scope
COMPILER2_PRESENT(DerivedPointerTableDeactivate dpt_deact);
- gclog_or_tty->print(" VerifyDuringGC:(full)[Verifying ");
Universe::heap()->prepare_for_verify();
// Note: we can verify only the heap here. When an object is
// marked, the previous value of the mark word (including
@@ -200,11 +197,13 @@
// fail. At the end of the GC, the orginal mark word values
// (including hash values) are restored to the appropriate
// objects.
- Universe::heap()->verify(/* silent */ false,
- /* option */ VerifyOption_G1UseMarkWord);
-
- G1CollectedHeap* g1h = G1CollectedHeap::heap();
- gclog_or_tty->print_cr("]");
+ if (!VerifySilently) {
+ gclog_or_tty->print(" VerifyDuringGC:(full)[Verifying ");
+ }
+ Universe::heap()->verify(VerifySilently, VerifyOption_G1UseMarkWord);
+ if (!VerifySilently) {
+ gclog_or_tty->print_cr("]");
+ }
}
gc_tracer()->report_object_count_after_gc(&GenMarkSweep::is_alive);
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp
index 9c57ec2..b1b5ee9 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp
@@ -34,6 +34,7 @@
#include "gc_implementation/g1/g1OopClosures.inline.hpp"
#include "gc_implementation/g1/g1RemSet.inline.hpp"
#include "gc_implementation/g1/heapRegionSeq.inline.hpp"
+#include "gc_implementation/g1/heapRegionRemSet.hpp"
#include "memory/iterator.hpp"
#include "oops/oop.inline.hpp"
#include "utilities/intHisto.hpp"
@@ -73,7 +74,8 @@
_ct_bs(ct_bs), _g1p(_g1->g1_policy()),
_cg1r(g1->concurrent_g1_refine()),
_cset_rs_update_cl(NULL),
- _cards_scanned(NULL), _total_cards_scanned(0)
+ _cards_scanned(NULL), _total_cards_scanned(0),
+ _prev_period_summary()
{
_seq_task = new SubTasksDone(NumSeqTasks);
guarantee(n_workers() > 0, "There should be some workers");
@@ -81,6 +83,9 @@
for (uint i = 0; i < n_workers(); i++) {
_cset_rs_update_cl[i] = NULL;
}
+ if (G1SummarizeRSetStats) {
+ _prev_period_summary.initialize(this);
+ }
}
G1RemSet::~G1RemSet() {
@@ -101,15 +106,25 @@
class ScanRSClosure : public HeapRegionClosure {
size_t _cards_done, _cards;
G1CollectedHeap* _g1h;
+
OopsInHeapRegionClosure* _oc;
+ CodeBlobToOopClosure* _code_root_cl;
+
G1BlockOffsetSharedArray* _bot_shared;
CardTableModRefBS *_ct_bs;
- int _worker_i;
- int _block_size;
- bool _try_claimed;
+
+ double _strong_code_root_scan_time_sec;
+ int _worker_i;
+ int _block_size;
+ bool _try_claimed;
+
public:
- ScanRSClosure(OopsInHeapRegionClosure* oc, int worker_i) :
+ ScanRSClosure(OopsInHeapRegionClosure* oc,
+ CodeBlobToOopClosure* code_root_cl,
+ int worker_i) :
_oc(oc),
+ _code_root_cl(code_root_cl),
+ _strong_code_root_scan_time_sec(0.0),
_cards(0),
_cards_done(0),
_worker_i(worker_i),
@@ -157,6 +172,12 @@
card_start, card_start + G1BlockOffsetSharedArray::N_words);
}
+ void scan_strong_code_roots(HeapRegion* r) {
+ double scan_start = os::elapsedTime();
+ r->strong_code_roots_do(_code_root_cl);
+ _strong_code_root_scan_time_sec += (os::elapsedTime() - scan_start);
+ }
+
bool doHeapRegion(HeapRegion* r) {
assert(r->in_collection_set(), "should only be called on elements of CS.");
HeapRegionRemSet* hrrs = r->rem_set();
@@ -170,6 +191,7 @@
// _try_claimed || r->claim_iter()
// is true: either we're supposed to work on claimed-but-not-complete
// regions, or we successfully claimed the region.
+
HeapRegionRemSetIterator* iter = _g1h->rem_set_iterator(_worker_i);
hrrs->init_iterator(iter);
size_t card_index;
@@ -203,30 +225,43 @@
}
}
if (!_try_claimed) {
+ // Scan the strong code root list attached to the current region
+ scan_strong_code_roots(r);
+
hrrs->set_iter_complete();
}
return false;
}
+
+ double strong_code_root_scan_time_sec() {
+ return _strong_code_root_scan_time_sec;
+ }
+
size_t cards_done() { return _cards_done;}
size_t cards_looked_up() { return _cards;}
};
-void G1RemSet::scanRS(OopsInHeapRegionClosure* oc, int worker_i) {
+void G1RemSet::scanRS(OopsInHeapRegionClosure* oc,
+ CodeBlobToOopClosure* code_root_cl,
+ int worker_i) {
double rs_time_start = os::elapsedTime();
HeapRegion *startRegion = _g1->start_cset_region_for_worker(worker_i);
- ScanRSClosure scanRScl(oc, worker_i);
+ ScanRSClosure scanRScl(oc, code_root_cl, worker_i);
_g1->collection_set_iterate_from(startRegion, &scanRScl);
scanRScl.set_try_claimed();
_g1->collection_set_iterate_from(startRegion, &scanRScl);
- double scan_rs_time_sec = os::elapsedTime() - rs_time_start;
+ double scan_rs_time_sec = (os::elapsedTime() - rs_time_start)
+ - scanRScl.strong_code_root_scan_time_sec();
- assert( _cards_scanned != NULL, "invariant" );
+ assert(_cards_scanned != NULL, "invariant");
_cards_scanned[worker_i] = scanRScl.cards_done();
_g1p->phase_times()->record_scan_rs_time(worker_i, scan_rs_time_sec * 1000.0);
+ _g1p->phase_times()->record_strong_code_root_scan_time(worker_i,
+ scanRScl.strong_code_root_scan_time_sec() * 1000.0);
}
// Closure used for updating RSets and recording references that
@@ -286,7 +321,8 @@
}
void G1RemSet::oops_into_collection_set_do(OopsInHeapRegionClosure* oc,
- int worker_i) {
+ CodeBlobToOopClosure* code_root_cl,
+ int worker_i) {
#if CARD_REPEAT_HISTO
ct_freq_update_histo_and_reset();
#endif
@@ -326,7 +362,7 @@
_g1p->phase_times()->record_update_rs_time(worker_i, 0.0);
}
if (G1UseParallelRSetScanning || (worker_i == 0)) {
- scanRS(oc, worker_i);
+ scanRS(oc, code_root_cl, worker_i);
} else {
_g1p->phase_times()->record_scan_rs_time(worker_i, 0.0);
}
@@ -700,47 +736,29 @@
return has_refs_into_cset;
}
-class HRRSStatsIter: public HeapRegionClosure {
- size_t _occupied;
- size_t _total_mem_sz;
- size_t _max_mem_sz;
- HeapRegion* _max_mem_sz_region;
-public:
- HRRSStatsIter() :
- _occupied(0),
- _total_mem_sz(0),
- _max_mem_sz(0),
- _max_mem_sz_region(NULL)
- {}
+void G1RemSet::print_periodic_summary_info(const char* header) {
+ G1RemSetSummary current;
+ current.initialize(this);
- bool doHeapRegion(HeapRegion* r) {
- if (r->continuesHumongous()) return false;
- size_t mem_sz = r->rem_set()->mem_size();
- if (mem_sz > _max_mem_sz) {
- _max_mem_sz = mem_sz;
- _max_mem_sz_region = r;
- }
- _total_mem_sz += mem_sz;
- size_t occ = r->rem_set()->occupied();
- _occupied += occ;
- return false;
- }
- size_t total_mem_sz() { return _total_mem_sz; }
- size_t max_mem_sz() { return _max_mem_sz; }
- size_t occupied() { return _occupied; }
- HeapRegion* max_mem_sz_region() { return _max_mem_sz_region; }
-};
+ _prev_period_summary.subtract_from(¤t);
+ print_summary_info(&_prev_period_summary, header);
-class PrintRSThreadVTimeClosure : public ThreadClosure {
-public:
- virtual void do_thread(Thread *t) {
- ConcurrentG1RefineThread* crt = (ConcurrentG1RefineThread*) t;
- gclog_or_tty->print(" %5.2f", crt->vtime_accum());
- }
-};
+ _prev_period_summary.set(¤t);
+}
void G1RemSet::print_summary_info() {
- G1CollectedHeap* g1 = G1CollectedHeap::heap();
+ G1RemSetSummary current;
+ current.initialize(this);
+
+ print_summary_info(¤t, " Cumulative RS summary");
+}
+
+void G1RemSet::print_summary_info(G1RemSetSummary * summary, const char * header) {
+ assert(summary != NULL, "just checking");
+
+ if (header != NULL) {
+ gclog_or_tty->print_cr("%s", header);
+ }
#if CARD_REPEAT_HISTO
gclog_or_tty->print_cr("\nG1 card_repeat count histogram: ");
@@ -748,52 +766,13 @@
card_repeat_count.print_on(gclog_or_tty);
#endif
- gclog_or_tty->print_cr("\n Concurrent RS processed %d cards",
- _conc_refine_cards);
- DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
- jint tot_processed_buffers =
- dcqs.processed_buffers_mut() + dcqs.processed_buffers_rs_thread();
- gclog_or_tty->print_cr(" Of %d completed buffers:", tot_processed_buffers);
- gclog_or_tty->print_cr(" %8d (%5.1f%%) by conc RS threads.",
- dcqs.processed_buffers_rs_thread(),
- 100.0*(float)dcqs.processed_buffers_rs_thread()/
- (float)tot_processed_buffers);
- gclog_or_tty->print_cr(" %8d (%5.1f%%) by mutator threads.",
- dcqs.processed_buffers_mut(),
- 100.0*(float)dcqs.processed_buffers_mut()/
- (float)tot_processed_buffers);
- gclog_or_tty->print_cr(" Conc RS threads times(s)");
- PrintRSThreadVTimeClosure p;
- gclog_or_tty->print(" ");
- g1->concurrent_g1_refine()->threads_do(&p);
- gclog_or_tty->print_cr("");
-
- HRRSStatsIter blk;
- g1->heap_region_iterate(&blk);
- gclog_or_tty->print_cr(" Total heap region rem set sizes = "SIZE_FORMAT"K."
- " Max = "SIZE_FORMAT"K.",
- blk.total_mem_sz()/K, blk.max_mem_sz()/K);
- gclog_or_tty->print_cr(" Static structures = "SIZE_FORMAT"K,"
- " free_lists = "SIZE_FORMAT"K.",
- HeapRegionRemSet::static_mem_size() / K,
- HeapRegionRemSet::fl_mem_size() / K);
- gclog_or_tty->print_cr(" "SIZE_FORMAT" occupied cards represented.",
- blk.occupied());
- HeapRegion* max_mem_sz_region = blk.max_mem_sz_region();
- HeapRegionRemSet* rem_set = max_mem_sz_region->rem_set();
- gclog_or_tty->print_cr(" Max size region = "HR_FORMAT", "
- "size = "SIZE_FORMAT "K, occupied = "SIZE_FORMAT"K.",
- HR_FORMAT_PARAMS(max_mem_sz_region),
- (rem_set->mem_size() + K - 1)/K,
- (rem_set->occupied() + K - 1)/K);
- gclog_or_tty->print_cr(" Did %d coarsenings.",
- HeapRegionRemSet::n_coarsenings());
+ summary->print_on(gclog_or_tty);
}
void G1RemSet::prepare_for_verify() {
if (G1HRRSFlushLogBuffersOnVerify &&
(VerifyBeforeGC || VerifyAfterGC)
- && !_g1->full_collection()) {
+ && (!_g1->full_collection() || G1VerifyRSetsDuringFullGC)) {
cleanupHRRS();
_g1->set_refine_cte_cl_concurrency(false);
if (SafepointSynchronize::is_at_safepoint()) {
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp
index 8bcd966..2853a67 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp
@@ -25,6 +25,8 @@
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1REMSET_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_G1REMSET_HPP
+#include "gc_implementation/g1/g1RemSetSummary.hpp"
+
// A G1RemSet provides ways of iterating over pointers into a selected
// collection set.
@@ -37,9 +39,11 @@
// so that they can be used to update the individual region remsets.
class G1RemSet: public CHeapObj<mtGC> {
+private:
+ G1RemSetSummary _prev_period_summary;
protected:
G1CollectedHeap* _g1;
- unsigned _conc_refine_cards;
+ size_t _conc_refine_cards;
uint n_workers();
protected:
@@ -66,6 +70,8 @@
// references into the collection set.
OopsInHeapRegionClosure** _cset_rs_update_cl;
+ // Print the given summary info
+ virtual void print_summary_info(G1RemSetSummary * summary, const char * header = NULL);
public:
// This is called to reset dual hash tables after the gc pause
// is finished and the initial hash table is no longer being
@@ -75,14 +81,23 @@
G1RemSet(G1CollectedHeap* g1, CardTableModRefBS* ct_bs);
~G1RemSet();
- // Invoke "blk->do_oop" on all pointers into the CS in objects in regions
- // outside the CS (having invoked "blk->set_region" to set the "from"
- // region correctly beforehand.) The "worker_i" param is for the
- // parallel case where the number of the worker thread calling this
- // function can be helpful in partitioning the work to be done. It
- // should be the same as the "i" passed to the calling thread's
- // work(i) function. In the sequential case this param will be ingored.
- void oops_into_collection_set_do(OopsInHeapRegionClosure* blk, int worker_i);
+ // Invoke "blk->do_oop" on all pointers into the collection set
+ // from objects in regions outside the collection set (having
+ // invoked "blk->set_region" to set the "from" region correctly
+ // beforehand.)
+ //
+ // Invoke code_root_cl->do_code_blob on the unmarked nmethods
+ // on the strong code roots list for each region in the
+ // collection set.
+ //
+ // The "worker_i" param is for the parallel case where the id
+ // of the worker thread calling this function can be helpful in
+ // partitioning the work to be done. It should be the same as
+ // the "i" passed to the calling thread's work(i) function.
+ // In the sequential case this param will be ignored.
+ void oops_into_collection_set_do(OopsInHeapRegionClosure* blk,
+ CodeBlobToOopClosure* code_root_cl,
+ int worker_i);
// Prepare for and cleanup after an oops_into_collection_set_do
// call. Must call each of these once before and after (in sequential
@@ -92,7 +107,10 @@
void prepare_for_oops_into_collection_set_do();
void cleanup_after_oops_into_collection_set_do();
- void scanRS(OopsInHeapRegionClosure* oc, int worker_i);
+ void scanRS(OopsInHeapRegionClosure* oc,
+ CodeBlobToOopClosure* code_root_cl,
+ int worker_i);
+
void updateRS(DirtyCardQueue* into_cset_dcq, int worker_i);
CardTableModRefBS* ct_bs() { return _ct_bs; }
@@ -123,11 +141,18 @@
int worker_i,
bool check_for_refs_into_cset);
- // Print any relevant summary info.
+ // Print accumulated summary info from the start of the VM.
virtual void print_summary_info();
+ // Print accumulated summary info from the last time called.
+ virtual void print_periodic_summary_info(const char* header);
+
// Prepare remembered set for verification.
virtual void prepare_for_verify();
+
+ size_t conc_refine_cards() const {
+ return _conc_refine_cards;
+ }
};
class CountNonCleanMemRegionClosure: public MemRegionClosure {
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSetSummary.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSetSummary.cpp
new file mode 100644
index 0000000..a0cdab5
--- /dev/null
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSetSummary.cpp
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc_implementation/g1/concurrentG1Refine.hpp"
+#include "gc_implementation/g1/concurrentG1RefineThread.hpp"
+#include "gc_implementation/g1/heapRegion.hpp"
+#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
+#include "gc_implementation/g1/g1RemSet.inline.hpp"
+#include "gc_implementation/g1/g1RemSetSummary.hpp"
+#include "gc_implementation/g1/heapRegionRemSet.hpp"
+#include "runtime/thread.hpp"
+
+class GetRSThreadVTimeClosure : public ThreadClosure {
+private:
+ G1RemSetSummary* _summary;
+ uint _counter;
+
+public:
+ GetRSThreadVTimeClosure(G1RemSetSummary * summary) : ThreadClosure(), _summary(summary), _counter(0) {
+ assert(_summary != NULL, "just checking");
+ }
+
+ virtual void do_thread(Thread* t) {
+ ConcurrentG1RefineThread* crt = (ConcurrentG1RefineThread*) t;
+ _summary->set_rs_thread_vtime(_counter, crt->vtime_accum());
+ _counter++;
+ }
+};
+
+void G1RemSetSummary::update() {
+ _num_refined_cards = remset()->conc_refine_cards();
+ DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
+ _num_processed_buf_mutator = dcqs.processed_buffers_mut();
+ _num_processed_buf_rs_threads = dcqs.processed_buffers_rs_thread();
+
+ _num_coarsenings = HeapRegionRemSet::n_coarsenings();
+
+ ConcurrentG1Refine * cg1r = G1CollectedHeap::heap()->concurrent_g1_refine();
+ if (_rs_threads_vtimes != NULL) {
+ GetRSThreadVTimeClosure p(this);
+ cg1r->worker_threads_do(&p);
+ }
+ set_sampling_thread_vtime(cg1r->sampling_thread()->vtime_accum());
+}
+
+void G1RemSetSummary::set_rs_thread_vtime(uint thread, double value) {
+ assert(_rs_threads_vtimes != NULL, "just checking");
+ assert(thread < _num_vtimes, "just checking");
+ _rs_threads_vtimes[thread] = value;
+}
+
+double G1RemSetSummary::rs_thread_vtime(uint thread) const {
+ assert(_rs_threads_vtimes != NULL, "just checking");
+ assert(thread < _num_vtimes, "just checking");
+ return _rs_threads_vtimes[thread];
+}
+
+void G1RemSetSummary::initialize(G1RemSet* remset) {
+ assert(_rs_threads_vtimes == NULL, "just checking");
+ assert(remset != NULL, "just checking");
+
+ _remset = remset;
+ _num_vtimes = ConcurrentG1Refine::thread_num();
+ _rs_threads_vtimes = NEW_C_HEAP_ARRAY(double, _num_vtimes, mtGC);
+ memset(_rs_threads_vtimes, 0, sizeof(double) * _num_vtimes);
+
+ update();
+}
+
+void G1RemSetSummary::set(G1RemSetSummary* other) {
+ assert(other != NULL, "just checking");
+ assert(remset() == other->remset(), "just checking");
+ assert(_num_vtimes == other->_num_vtimes, "just checking");
+
+ _num_refined_cards = other->num_concurrent_refined_cards();
+
+ _num_processed_buf_mutator = other->num_processed_buf_mutator();
+ _num_processed_buf_rs_threads = other->num_processed_buf_rs_threads();
+
+ _num_coarsenings = other->_num_coarsenings;
+
+ memcpy(_rs_threads_vtimes, other->_rs_threads_vtimes, sizeof(double) * _num_vtimes);
+
+ set_sampling_thread_vtime(other->sampling_thread_vtime());
+}
+
+void G1RemSetSummary::subtract_from(G1RemSetSummary* other) {
+ assert(other != NULL, "just checking");
+ assert(remset() == other->remset(), "just checking");
+ assert(_num_vtimes == other->_num_vtimes, "just checking");
+
+ _num_refined_cards = other->num_concurrent_refined_cards() - _num_refined_cards;
+
+ _num_processed_buf_mutator = other->num_processed_buf_mutator() - _num_processed_buf_mutator;
+ _num_processed_buf_rs_threads = other->num_processed_buf_rs_threads() - _num_processed_buf_rs_threads;
+
+ _num_coarsenings = other->num_coarsenings() - _num_coarsenings;
+
+ for (uint i = 0; i < _num_vtimes; i++) {
+ set_rs_thread_vtime(i, other->rs_thread_vtime(i) - rs_thread_vtime(i));
+ }
+
+ _sampling_thread_vtime = other->sampling_thread_vtime() - _sampling_thread_vtime;
+}
+
+static double percent_of(size_t numerator, size_t denominator) {
+ if (denominator != 0) {
+ return (double)numerator / denominator * 100.0f;
+ } else {
+ return 0.0f;
+ }
+}
+
+static size_t round_to_K(size_t value) {
+ return value / K;
+}
+
+class RegionTypeCounter VALUE_OBJ_CLASS_SPEC {
+private:
+ const char* _name;
+
+ size_t _rs_mem_size;
+ size_t _cards_occupied;
+ size_t _amount;
+
+ size_t _code_root_mem_size;
+ size_t _code_root_elems;
+
+ double rs_mem_size_percent_of(size_t total) {
+ return percent_of(_rs_mem_size, total);
+ }
+
+ double cards_occupied_percent_of(size_t total) {
+ return percent_of(_cards_occupied, total);
+ }
+
+ double code_root_mem_size_percent_of(size_t total) {
+ return percent_of(_code_root_mem_size, total);
+ }
+
+ double code_root_elems_percent_of(size_t total) {
+ return percent_of(_code_root_elems, total);
+ }
+
+ size_t amount() const { return _amount; }
+
+public:
+
+ RegionTypeCounter(const char* name) : _name(name), _rs_mem_size(0), _cards_occupied(0),
+ _amount(0), _code_root_mem_size(0), _code_root_elems(0) { }
+
+ void add(size_t rs_mem_size, size_t cards_occupied, size_t code_root_mem_size,
+ size_t code_root_elems) {
+ _rs_mem_size += rs_mem_size;
+ _cards_occupied += cards_occupied;
+ _code_root_mem_size += code_root_mem_size;
+ _code_root_elems += code_root_elems;
+ _amount++;
+ }
+
+ size_t rs_mem_size() const { return _rs_mem_size; }
+ size_t cards_occupied() const { return _cards_occupied; }
+
+ size_t code_root_mem_size() const { return _code_root_mem_size; }
+ size_t code_root_elems() const { return _code_root_elems; }
+
+ void print_rs_mem_info_on(outputStream * out, size_t total) {
+ out->print_cr(" "SIZE_FORMAT_W(8)"K (%5.1f%%) by "SIZE_FORMAT" %s regions",
+ round_to_K(rs_mem_size()), rs_mem_size_percent_of(total), amount(), _name);
+ }
+
+ void print_cards_occupied_info_on(outputStream * out, size_t total) {
+ out->print_cr(" "SIZE_FORMAT_W(8)" (%5.1f%%) entries by "SIZE_FORMAT" %s regions",
+ cards_occupied(), cards_occupied_percent_of(total), amount(), _name);
+ }
+
+ void print_code_root_mem_info_on(outputStream * out, size_t total) {
+ out->print_cr(" "SIZE_FORMAT_W(8)"K (%5.1f%%) by "SIZE_FORMAT" %s regions",
+ round_to_K(code_root_mem_size()), code_root_mem_size_percent_of(total), amount(), _name);
+ }
+
+ void print_code_root_elems_info_on(outputStream * out, size_t total) {
+ out->print_cr(" "SIZE_FORMAT_W(8)" (%5.1f%%) elements by "SIZE_FORMAT" %s regions",
+ code_root_elems(), code_root_elems_percent_of(total), amount(), _name);
+ }
+};
+
+
+class HRRSStatsIter: public HeapRegionClosure {
+private:
+ RegionTypeCounter _young;
+ RegionTypeCounter _humonguous;
+ RegionTypeCounter _free;
+ RegionTypeCounter _old;
+ RegionTypeCounter _all;
+
+ size_t _max_rs_mem_sz;
+ HeapRegion* _max_rs_mem_sz_region;
+
+ size_t total_rs_mem_sz() const { return _all.rs_mem_size(); }
+ size_t total_cards_occupied() const { return _all.cards_occupied(); }
+
+ size_t max_rs_mem_sz() const { return _max_rs_mem_sz; }
+ HeapRegion* max_rs_mem_sz_region() const { return _max_rs_mem_sz_region; }
+
+ size_t _max_code_root_mem_sz;
+ HeapRegion* _max_code_root_mem_sz_region;
+
+ size_t total_code_root_mem_sz() const { return _all.code_root_mem_size(); }
+ size_t total_code_root_elems() const { return _all.code_root_elems(); }
+
+ size_t max_code_root_mem_sz() const { return _max_code_root_mem_sz; }
+ HeapRegion* max_code_root_mem_sz_region() const { return _max_code_root_mem_sz_region; }
+
+public:
+ HRRSStatsIter() : _all("All"), _young("Young"), _humonguous("Humonguous"),
+ _free("Free"), _old("Old"), _max_code_root_mem_sz_region(NULL), _max_rs_mem_sz_region(NULL),
+ _max_rs_mem_sz(0), _max_code_root_mem_sz(0)
+ {}
+
+ bool doHeapRegion(HeapRegion* r) {
+ HeapRegionRemSet* hrrs = r->rem_set();
+
+ // HeapRegionRemSet::mem_size() includes the
+ // size of the strong code roots
+ size_t rs_mem_sz = hrrs->mem_size();
+ if (rs_mem_sz > _max_rs_mem_sz) {
+ _max_rs_mem_sz = rs_mem_sz;
+ _max_rs_mem_sz_region = r;
+ }
+ size_t occupied_cards = hrrs->occupied();
+ size_t code_root_mem_sz = hrrs->strong_code_roots_mem_size();
+ if (code_root_mem_sz > max_code_root_mem_sz()) {
+ _max_code_root_mem_sz_region = r;
+ }
+ size_t code_root_elems = hrrs->strong_code_roots_list_length();
+
+ RegionTypeCounter* current = NULL;
+ if (r->is_young()) {
+ current = &_young;
+ } else if (r->isHumongous()) {
+ current = &_humonguous;
+ } else if (r->is_empty()) {
+ current = &_free;
+ } else {
+ current = &_old;
+ }
+ current->add(rs_mem_sz, occupied_cards, code_root_mem_sz, code_root_elems);
+ _all.add(rs_mem_sz, occupied_cards, code_root_mem_sz, code_root_elems);
+
+ return false;
+ }
+
+ void print_summary_on(outputStream* out) {
+ RegionTypeCounter* counters[] = { &_young, &_humonguous, &_free, &_old, NULL };
+
+ out->print_cr("\n Current rem set statistics");
+ out->print_cr(" Total per region rem sets sizes = "SIZE_FORMAT"K."
+ " Max = "SIZE_FORMAT"K.",
+ round_to_K(total_rs_mem_sz()), round_to_K(max_rs_mem_sz()));
+ for (RegionTypeCounter** current = &counters[0]; *current != NULL; current++) {
+ (*current)->print_rs_mem_info_on(out, total_rs_mem_sz());
+ }
+
+ out->print_cr(" Static structures = "SIZE_FORMAT"K,"
+ " free_lists = "SIZE_FORMAT"K.",
+ round_to_K(HeapRegionRemSet::static_mem_size()),
+ round_to_K(HeapRegionRemSet::fl_mem_size()));
+
+ out->print_cr(" "SIZE_FORMAT" occupied cards represented.",
+ total_cards_occupied());
+ for (RegionTypeCounter** current = &counters[0]; *current != NULL; current++) {
+ (*current)->print_cards_occupied_info_on(out, total_cards_occupied());
+ }
+
+ // Largest sized rem set region statistics
+ HeapRegionRemSet* rem_set = max_rs_mem_sz_region()->rem_set();
+ out->print_cr(" Region with largest rem set = "HR_FORMAT", "
+ "size = "SIZE_FORMAT "K, occupied = "SIZE_FORMAT"K.",
+ HR_FORMAT_PARAMS(max_rs_mem_sz_region()),
+ round_to_K(rem_set->mem_size()),
+ round_to_K(rem_set->occupied()));
+
+ // Strong code root statistics
+ HeapRegionRemSet* max_code_root_rem_set = max_code_root_mem_sz_region()->rem_set();
+ out->print_cr(" Total heap region code root sets sizes = "SIZE_FORMAT"K."
+ " Max = "SIZE_FORMAT"K.",
+ round_to_K(total_code_root_mem_sz()),
+ round_to_K(max_code_root_rem_set->strong_code_roots_mem_size()));
+ for (RegionTypeCounter** current = &counters[0]; *current != NULL; current++) {
+ (*current)->print_code_root_mem_info_on(out, total_code_root_mem_sz());
+ }
+
+ out->print_cr(" "SIZE_FORMAT" code roots represented.",
+ total_code_root_elems());
+ for (RegionTypeCounter** current = &counters[0]; *current != NULL; current++) {
+ (*current)->print_code_root_elems_info_on(out, total_code_root_elems());
+ }
+
+ out->print_cr(" Region with largest amount of code roots = "HR_FORMAT", "
+ "size = "SIZE_FORMAT "K, num_elems = "SIZE_FORMAT".",
+ HR_FORMAT_PARAMS(max_code_root_mem_sz_region()),
+ round_to_K(max_code_root_rem_set->strong_code_roots_mem_size()),
+ round_to_K(max_code_root_rem_set->strong_code_roots_list_length()));
+ }
+};
+
+void G1RemSetSummary::print_on(outputStream* out) {
+ out->print_cr("\n Recent concurrent refinement statistics");
+ out->print_cr(" Processed "SIZE_FORMAT" cards",
+ num_concurrent_refined_cards());
+ out->print_cr(" Of "SIZE_FORMAT" completed buffers:", num_processed_buf_total());
+ out->print_cr(" "SIZE_FORMAT_W(8)" (%5.1f%%) by concurrent RS threads.",
+ num_processed_buf_total(),
+ percent_of(num_processed_buf_rs_threads(), num_processed_buf_total()));
+ out->print_cr(" "SIZE_FORMAT_W(8)" (%5.1f%%) by mutator threads.",
+ num_processed_buf_mutator(),
+ percent_of(num_processed_buf_mutator(), num_processed_buf_total()));
+ out->print_cr(" Did "SIZE_FORMAT" coarsenings.", num_coarsenings());
+ out->print_cr(" Concurrent RS threads times (s)");
+ out->print(" ");
+ for (uint i = 0; i < _num_vtimes; i++) {
+ out->print(" %5.2f", rs_thread_vtime(i));
+ }
+ out->cr();
+ out->print_cr(" Concurrent sampling threads times (s)");
+ out->print_cr(" %5.2f", sampling_thread_vtime());
+
+ HRRSStatsIter blk;
+ G1CollectedHeap::heap()->heap_region_iterate(&blk);
+ blk.print_summary_on(out);
+}
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSetSummary.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSetSummary.hpp
new file mode 100644
index 0000000..9c019d9
--- /dev/null
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSetSummary.hpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1REMSETSUMMARY_HPP
+#define SHARE_VM_GC_IMPLEMENTATION_G1_G1REMSETSUMMARY_HPP
+
+#include "utilities/ostream.hpp"
+
+class G1RemSet;
+
+// A G1RemSetSummary manages statistical information about the G1RemSet
+
+class G1RemSetSummary VALUE_OBJ_CLASS_SPEC {
+private:
+ friend class GetRSThreadVTimeClosure;
+
+ G1RemSet* _remset;
+
+ G1RemSet* remset() const {
+ return _remset;
+ }
+
+ size_t _num_refined_cards;
+ size_t _num_processed_buf_mutator;
+ size_t _num_processed_buf_rs_threads;
+
+ size_t _num_coarsenings;
+
+ double* _rs_threads_vtimes;
+ size_t _num_vtimes;
+
+ double _sampling_thread_vtime;
+
+ void set_rs_thread_vtime(uint thread, double value);
+ void set_sampling_thread_vtime(double value) {
+ _sampling_thread_vtime = value;
+ }
+
+ void free_and_null() {
+ if (_rs_threads_vtimes) {
+ FREE_C_HEAP_ARRAY(double, _rs_threads_vtimes, mtGC);
+ _rs_threads_vtimes = NULL;
+ _num_vtimes = 0;
+ }
+ }
+
+ // update this summary with current data from various places
+ void update();
+
+public:
+ G1RemSetSummary() : _remset(NULL), _num_refined_cards(0),
+ _num_processed_buf_mutator(0), _num_processed_buf_rs_threads(0), _num_coarsenings(0),
+ _rs_threads_vtimes(NULL), _num_vtimes(0), _sampling_thread_vtime(0.0f) {
+ }
+
+ ~G1RemSetSummary() {
+ free_and_null();
+ }
+
+ // set the counters in this summary to the values of the others
+ void set(G1RemSetSummary* other);
+ // subtract all counters from the other summary, and set them in the current
+ void subtract_from(G1RemSetSummary* other);
+
+ // initialize and get the first sampling
+ void initialize(G1RemSet* remset);
+
+ void print_on(outputStream* out);
+
+ double rs_thread_vtime(uint thread) const;
+
+ double sampling_thread_vtime() const {
+ return _sampling_thread_vtime;
+ }
+
+ size_t num_concurrent_refined_cards() const {
+ return _num_refined_cards;
+ }
+
+ size_t num_processed_buf_mutator() const {
+ return _num_processed_buf_mutator;
+ }
+
+ size_t num_processed_buf_rs_threads() const {
+ return _num_processed_buf_rs_threads;
+ }
+
+ size_t num_processed_buf_total() const {
+ return num_processed_buf_mutator() + num_processed_buf_rs_threads();
+ }
+
+ size_t num_coarsenings() const {
+ return _num_coarsenings;
+ }
+};
+
+#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1REMSETSUMMARY_HPP
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp
index 190b627..efc1229 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp
@@ -71,6 +71,9 @@
diagnostic(bool, G1TraceConcRefinement, false, \
"Trace G1 concurrent refinement") \
\
+ experimental(bool, G1TraceStringSymbolTableScrubbing, false, \
+ "Trace information string and symbol table scrubbing.") \
+ \
product(double, G1ConcMarkStepDurationMillis, 10.0, \
"Target duration of individual concurrent marking steps " \
"in milliseconds.") \
@@ -332,7 +335,14 @@
\
develop(bool, G1EvacuationFailureALotDuringMixedGC, true, \
"Force use of evacuation failure handling during mixed " \
- "evacuation pauses")
+ "evacuation pauses") \
+ \
+ diagnostic(bool, G1VerifyRSetsDuringFullGC, false, \
+ "If true, perform verification of each heap region's " \
+ "remembered set when verifying the heap during a full GC.") \
+ \
+ diagnostic(bool, G1VerifyHeapRegionCodeRoots, false, \
+ "Verify the code root lists attached to each heap region.")
G1_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_EXPERIMENTAL_FLAG, DECLARE_NOTPRODUCT_FLAG, DECLARE_MANAGEABLE_FLAG, DECLARE_PRODUCT_RW_FLAG)
diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp
index a6046f1..7546c42 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp
@@ -23,6 +23,7 @@
*/
#include "precompiled.hpp"
+#include "code/nmethod.hpp"
#include "gc_implementation/g1/g1BlockOffsetTable.inline.hpp"
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
#include "gc_implementation/g1/g1OopClosures.inline.hpp"
@@ -50,144 +51,6 @@
OopClosure* oc) :
_r_bottom(r->bottom()), _r_end(r->end()), _oc(oc) { }
-class VerifyLiveClosure: public OopClosure {
-private:
- G1CollectedHeap* _g1h;
- CardTableModRefBS* _bs;
- oop _containing_obj;
- bool _failures;
- int _n_failures;
- VerifyOption _vo;
-public:
- // _vo == UsePrevMarking -> use "prev" marking information,
- // _vo == UseNextMarking -> use "next" marking information,
- // _vo == UseMarkWord -> use mark word from object header.
- VerifyLiveClosure(G1CollectedHeap* g1h, VerifyOption vo) :
- _g1h(g1h), _bs(NULL), _containing_obj(NULL),
- _failures(false), _n_failures(0), _vo(vo)
- {
- BarrierSet* bs = _g1h->barrier_set();
- if (bs->is_a(BarrierSet::CardTableModRef))
- _bs = (CardTableModRefBS*)bs;
- }
-
- void set_containing_obj(oop obj) {
- _containing_obj = obj;
- }
-
- bool failures() { return _failures; }
- int n_failures() { return _n_failures; }
-
- virtual void do_oop(narrowOop* p) { do_oop_work(p); }
- virtual void do_oop( oop* p) { do_oop_work(p); }
-
- void print_object(outputStream* out, oop obj) {
-#ifdef PRODUCT
- klassOop k = obj->klass();
- const char* class_name = instanceKlass::cast(k)->external_name();
- out->print_cr("class name %s", class_name);
-#else // PRODUCT
- obj->print_on(out);
-#endif // PRODUCT
- }
-
- template <class T>
- void do_oop_work(T* p) {
- assert(_containing_obj != NULL, "Precondition");
- assert(!_g1h->is_obj_dead_cond(_containing_obj, _vo),
- "Precondition");
- T heap_oop = oopDesc::load_heap_oop(p);
- if (!oopDesc::is_null(heap_oop)) {
- oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
- bool failed = false;
- if (!_g1h->is_in_closed_subset(obj) || _g1h->is_obj_dead_cond(obj, _vo)) {
- MutexLockerEx x(ParGCRareEvent_lock,
- Mutex::_no_safepoint_check_flag);
-
- if (!_failures) {
- gclog_or_tty->print_cr("");
- gclog_or_tty->print_cr("----------");
- }
- if (!_g1h->is_in_closed_subset(obj)) {
- HeapRegion* from = _g1h->heap_region_containing((HeapWord*)p);
- gclog_or_tty->print_cr("Field "PTR_FORMAT
- " of live obj "PTR_FORMAT" in region "
- "["PTR_FORMAT", "PTR_FORMAT")",
- p, (void*) _containing_obj,
- from->bottom(), from->end());
- print_object(gclog_or_tty, _containing_obj);
- gclog_or_tty->print_cr("points to obj "PTR_FORMAT" not in the heap",
- (void*) obj);
- } else {
- HeapRegion* from = _g1h->heap_region_containing((HeapWord*)p);
- HeapRegion* to = _g1h->heap_region_containing((HeapWord*)obj);
- gclog_or_tty->print_cr("Field "PTR_FORMAT
- " of live obj "PTR_FORMAT" in region "
- "["PTR_FORMAT", "PTR_FORMAT")",
- p, (void*) _containing_obj,
- from->bottom(), from->end());
- print_object(gclog_or_tty, _containing_obj);
- gclog_or_tty->print_cr("points to dead obj "PTR_FORMAT" in region "
- "["PTR_FORMAT", "PTR_FORMAT")",
- (void*) obj, to->bottom(), to->end());
- print_object(gclog_or_tty, obj);
- }
- gclog_or_tty->print_cr("----------");
- gclog_or_tty->flush();
- _failures = true;
- failed = true;
- _n_failures++;
- }
-
- if (!_g1h->full_collection()) {
- HeapRegion* from = _g1h->heap_region_containing((HeapWord*)p);
- HeapRegion* to = _g1h->heap_region_containing(obj);
- if (from != NULL && to != NULL &&
- from != to &&
- !to->isHumongous()) {
- jbyte cv_obj = *_bs->byte_for_const(_containing_obj);
- jbyte cv_field = *_bs->byte_for_const(p);
- const jbyte dirty = CardTableModRefBS::dirty_card_val();
-
- bool is_bad = !(from->is_young()
- || to->rem_set()->contains_reference(p)
- || !G1HRRSFlushLogBuffersOnVerify && // buffers were not flushed
- (_containing_obj->is_objArray() ?
- cv_field == dirty
- : cv_obj == dirty || cv_field == dirty));
- if (is_bad) {
- MutexLockerEx x(ParGCRareEvent_lock,
- Mutex::_no_safepoint_check_flag);
-
- if (!_failures) {
- gclog_or_tty->print_cr("");
- gclog_or_tty->print_cr("----------");
- }
- gclog_or_tty->print_cr("Missing rem set entry:");
- gclog_or_tty->print_cr("Field "PTR_FORMAT" "
- "of obj "PTR_FORMAT", "
- "in region "HR_FORMAT,
- p, (void*) _containing_obj,
- HR_FORMAT_PARAMS(from));
- _containing_obj->print_on(gclog_or_tty);
- gclog_or_tty->print_cr("points to obj "PTR_FORMAT" "
- "in region "HR_FORMAT,
- (void*) obj,
- HR_FORMAT_PARAMS(to));
- obj->print_on(gclog_or_tty);
- gclog_or_tty->print_cr("Obj head CTE = %d, field CTE = %d.",
- cv_obj, cv_field);
- gclog_or_tty->print_cr("----------");
- gclog_or_tty->flush();
- _failures = true;
- if (!failed) _n_failures++;
- }
- }
- }
- }
- }
-};
-
template<class ClosureType>
HeapWord* walk_mem_region_loop(ClosureType* cl, G1CollectedHeap* g1h,
HeapRegion* hr,
@@ -314,6 +177,11 @@
region_size = MAX_REGION_SIZE;
}
+ if (region_size != G1HeapRegionSize) {
+ // Update the flag to make sure that PrintFlagsFinal logs the correct value
+ FLAG_SET_ERGO(uintx, G1HeapRegionSize, region_size);
+ }
+
// And recalculate the log.
region_size_log = log2_long((jlong) region_size);
@@ -363,7 +231,7 @@
if (!par) {
// If this is parallel, this will be done later.
HeapRegionRemSet* hrrs = rem_set();
- if (hrrs != NULL) hrrs->clear();
+ hrrs->clear();
_claimed = InitialClaimValue;
}
zero_marked_bytes();
@@ -504,6 +372,7 @@
_rem_set(NULL), _recorded_rs_length(0), _predicted_elapsed_time_ms(0),
_predicted_bytes_to_copy(0)
{
+ _rem_set = new HeapRegionRemSet(sharedOffsetArray, this);
_orig_end = mr.end();
// Note that initialize() will set the start of the unmarked area of the
// region.
@@ -511,8 +380,6 @@
set_top(bottom());
set_saved_mark();
- _rem_set = new HeapRegionRemSet(sharedOffsetArray, this);
-
assert(HeapRegionRemSet::num_par_rem_sets() > 0, "Invariant.");
}
@@ -732,6 +599,161 @@
return NULL;
}
+// Code roots support
+
+void HeapRegion::add_strong_code_root(nmethod* nm) {
+ HeapRegionRemSet* hrrs = rem_set();
+ hrrs->add_strong_code_root(nm);
+}
+
+void HeapRegion::remove_strong_code_root(nmethod* nm) {
+ HeapRegionRemSet* hrrs = rem_set();
+ hrrs->remove_strong_code_root(nm);
+}
+
+void HeapRegion::migrate_strong_code_roots() {
+ assert(in_collection_set(), "only collection set regions");
+ assert(!isHumongous(),
+ err_msg("humongous region "HR_FORMAT" should not have been added to collection set",
+ HR_FORMAT_PARAMS(this)));
+
+ HeapRegionRemSet* hrrs = rem_set();
+ hrrs->migrate_strong_code_roots();
+}
+
+void HeapRegion::strong_code_roots_do(CodeBlobClosure* blk) const {
+ HeapRegionRemSet* hrrs = rem_set();
+ hrrs->strong_code_roots_do(blk);
+}
+
+class VerifyStrongCodeRootOopClosure: public OopClosure {
+ const HeapRegion* _hr;
+ nmethod* _nm;
+ bool _failures;
+ bool _has_oops_in_region;
+
+ template <class T> void do_oop_work(T* p) {
+ T heap_oop = oopDesc::load_heap_oop(p);
+ if (!oopDesc::is_null(heap_oop)) {
+ oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
+
+ // Note: not all the oops embedded in the nmethod are in the
+ // current region. We only look at those which are.
+ if (_hr->is_in(obj)) {
+ // Object is in the region. Check that its less than top
+ if (_hr->top() <= (HeapWord*)obj) {
+ // Object is above top
+ gclog_or_tty->print_cr("Object "PTR_FORMAT" in region "
+ "["PTR_FORMAT", "PTR_FORMAT") is above "
+ "top "PTR_FORMAT,
+ obj, _hr->bottom(), _hr->end(), _hr->top());
+ _failures = true;
+ return;
+ }
+ // Nmethod has at least one oop in the current region
+ _has_oops_in_region = true;
+ }
+ }
+ }
+
+public:
+ VerifyStrongCodeRootOopClosure(const HeapRegion* hr, nmethod* nm):
+ _hr(hr), _failures(false), _has_oops_in_region(false) {}
+
+ void do_oop(narrowOop* p) { do_oop_work(p); }
+ void do_oop(oop* p) { do_oop_work(p); }
+
+ bool failures() { return _failures; }
+ bool has_oops_in_region() { return _has_oops_in_region; }
+};
+
+class VerifyStrongCodeRootCodeBlobClosure: public CodeBlobClosure {
+ const HeapRegion* _hr;
+ bool _failures;
+public:
+ VerifyStrongCodeRootCodeBlobClosure(const HeapRegion* hr) :
+ _hr(hr), _failures(false) {}
+
+ void do_code_blob(CodeBlob* cb) {
+ nmethod* nm = (cb == NULL) ? NULL : cb->as_nmethod_or_null();
+ if (nm != NULL) {
+ // Verify that the nemthod is live
+ if (!nm->is_alive()) {
+ gclog_or_tty->print_cr("region ["PTR_FORMAT","PTR_FORMAT"] has dead nmethod "
+ PTR_FORMAT" in its strong code roots",
+ _hr->bottom(), _hr->end(), nm);
+ _failures = true;
+ } else {
+ VerifyStrongCodeRootOopClosure oop_cl(_hr, nm);
+ nm->oops_do(&oop_cl);
+ if (!oop_cl.has_oops_in_region()) {
+ gclog_or_tty->print_cr("region ["PTR_FORMAT","PTR_FORMAT"] has nmethod "
+ PTR_FORMAT" in its strong code roots "
+ "with no pointers into region",
+ _hr->bottom(), _hr->end(), nm);
+ _failures = true;
+ } else if (oop_cl.failures()) {
+ gclog_or_tty->print_cr("region ["PTR_FORMAT","PTR_FORMAT"] has other "
+ "failures for nmethod "PTR_FORMAT,
+ _hr->bottom(), _hr->end(), nm);
+ _failures = true;
+ }
+ }
+ }
+ }
+
+ bool failures() { return _failures; }
+};
+
+void HeapRegion::verify_strong_code_roots(VerifyOption vo, bool* failures) const {
+ if (!G1VerifyHeapRegionCodeRoots) {
+ // We're not verifying code roots.
+ return;
+ }
+ if (vo == VerifyOption_G1UseMarkWord) {
+ // Marking verification during a full GC is performed after class
+ // unloading, code cache unloading, etc so the strong code roots
+ // attached to each heap region are in an inconsistent state. They won't
+ // be consistent until the strong code roots are rebuilt after the
+ // actual GC. Skip verifying the strong code roots in this particular
+ // time.
+ assert(VerifyDuringGC, "only way to get here");
+ return;
+ }
+
+ HeapRegionRemSet* hrrs = rem_set();
+ int strong_code_roots_length = hrrs->strong_code_roots_list_length();
+
+ // if this region is empty then there should be no entries
+ // on its strong code root list
+ if (is_empty()) {
+ if (strong_code_roots_length > 0) {
+ gclog_or_tty->print_cr("region ["PTR_FORMAT","PTR_FORMAT"] is empty "
+ "but has "INT32_FORMAT" code root entries",
+ bottom(), end(), strong_code_roots_length);
+ *failures = true;
+ }
+ return;
+ }
+
+ if (continuesHumongous()) {
+ if (strong_code_roots_length > 0) {
+ gclog_or_tty->print_cr("region "HR_FORMAT" is a continuation of a humongous "
+ "region but has "INT32_FORMAT" code root entries",
+ HR_FORMAT_PARAMS(this), strong_code_roots_length);
+ *failures = true;
+ }
+ return;
+ }
+
+ VerifyStrongCodeRootCodeBlobClosure cb_cl(this);
+ strong_code_roots_do(&cb_cl);
+
+ if (cb_cl.failures()) {
+ *failures = true;
+ }
+}
+
void HeapRegion::print() const { print_on(gclog_or_tty); }
void HeapRegion::print_on(outputStream* st) const {
if (isHumongous()) {
@@ -760,10 +782,143 @@
G1OffsetTableContigSpace::print_on(st);
}
-void HeapRegion::verify() const {
- bool dummy = false;
- verify(VerifyOption_G1UsePrevMarking, /* failures */ &dummy);
-}
+class VerifyLiveClosure: public OopClosure {
+private:
+ G1CollectedHeap* _g1h;
+ CardTableModRefBS* _bs;
+ oop _containing_obj;
+ bool _failures;
+ int _n_failures;
+ VerifyOption _vo;
+public:
+ // _vo == UsePrevMarking -> use "prev" marking information,
+ // _vo == UseNextMarking -> use "next" marking information,
+ // _vo == UseMarkWord -> use mark word from object header.
+ VerifyLiveClosure(G1CollectedHeap* g1h, VerifyOption vo) :
+ _g1h(g1h), _bs(NULL), _containing_obj(NULL),
+ _failures(false), _n_failures(0), _vo(vo)
+ {
+ BarrierSet* bs = _g1h->barrier_set();
+ if (bs->is_a(BarrierSet::CardTableModRef))
+ _bs = (CardTableModRefBS*)bs;
+ }
+
+ void set_containing_obj(oop obj) {
+ _containing_obj = obj;
+ }
+
+ bool failures() { return _failures; }
+ int n_failures() { return _n_failures; }
+
+ virtual void do_oop(narrowOop* p) { do_oop_work(p); }
+ virtual void do_oop( oop* p) { do_oop_work(p); }
+
+ void print_object(outputStream* out, oop obj) {
+#ifdef PRODUCT
+ klassOop k = obj->klass();
+ const char* class_name = instanceKlass::cast(k)->external_name();
+ out->print_cr("class name %s", class_name);
+#else // PRODUCT
+ obj->print_on(out);
+#endif // PRODUCT
+ }
+
+ template <class T>
+ void do_oop_work(T* p) {
+ assert(_containing_obj != NULL, "Precondition");
+ assert(!_g1h->is_obj_dead_cond(_containing_obj, _vo),
+ "Precondition");
+ T heap_oop = oopDesc::load_heap_oop(p);
+ if (!oopDesc::is_null(heap_oop)) {
+ oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
+ bool failed = false;
+ if (!_g1h->is_in_closed_subset(obj) || _g1h->is_obj_dead_cond(obj, _vo)) {
+ MutexLockerEx x(ParGCRareEvent_lock,
+ Mutex::_no_safepoint_check_flag);
+
+ if (!_failures) {
+ gclog_or_tty->print_cr("");
+ gclog_or_tty->print_cr("----------");
+ }
+ if (!_g1h->is_in_closed_subset(obj)) {
+ HeapRegion* from = _g1h->heap_region_containing((HeapWord*)p);
+ gclog_or_tty->print_cr("Field "PTR_FORMAT
+ " of live obj "PTR_FORMAT" in region "
+ "["PTR_FORMAT", "PTR_FORMAT")",
+ p, (void*) _containing_obj,
+ from->bottom(), from->end());
+ print_object(gclog_or_tty, _containing_obj);
+ gclog_or_tty->print_cr("points to obj "PTR_FORMAT" not in the heap",
+ (void*) obj);
+ } else {
+ HeapRegion* from = _g1h->heap_region_containing((HeapWord*)p);
+ HeapRegion* to = _g1h->heap_region_containing((HeapWord*)obj);
+ gclog_or_tty->print_cr("Field "PTR_FORMAT
+ " of live obj "PTR_FORMAT" in region "
+ "["PTR_FORMAT", "PTR_FORMAT")",
+ p, (void*) _containing_obj,
+ from->bottom(), from->end());
+ print_object(gclog_or_tty, _containing_obj);
+ gclog_or_tty->print_cr("points to dead obj "PTR_FORMAT" in region "
+ "["PTR_FORMAT", "PTR_FORMAT")",
+ (void*) obj, to->bottom(), to->end());
+ print_object(gclog_or_tty, obj);
+ }
+ gclog_or_tty->print_cr("----------");
+ gclog_or_tty->flush();
+ _failures = true;
+ failed = true;
+ _n_failures++;
+ }
+
+ if (!_g1h->full_collection() || G1VerifyRSetsDuringFullGC) {
+ HeapRegion* from = _g1h->heap_region_containing((HeapWord*)p);
+ HeapRegion* to = _g1h->heap_region_containing(obj);
+ if (from != NULL && to != NULL &&
+ from != to &&
+ !to->isHumongous()) {
+ jbyte cv_obj = *_bs->byte_for_const(_containing_obj);
+ jbyte cv_field = *_bs->byte_for_const(p);
+ const jbyte dirty = CardTableModRefBS::dirty_card_val();
+
+ bool is_bad = !(from->is_young()
+ || to->rem_set()->contains_reference(p)
+ || !G1HRRSFlushLogBuffersOnVerify && // buffers were not flushed
+ (_containing_obj->is_objArray() ?
+ cv_field == dirty
+ : cv_obj == dirty || cv_field == dirty));
+ if (is_bad) {
+ MutexLockerEx x(ParGCRareEvent_lock,
+ Mutex::_no_safepoint_check_flag);
+
+ if (!_failures) {
+ gclog_or_tty->print_cr("");
+ gclog_or_tty->print_cr("----------");
+ }
+ gclog_or_tty->print_cr("Missing rem set entry:");
+ gclog_or_tty->print_cr("Field "PTR_FORMAT" "
+ "of obj "PTR_FORMAT", "
+ "in region "HR_FORMAT,
+ p, (void*) _containing_obj,
+ HR_FORMAT_PARAMS(from));
+ _containing_obj->print_on(gclog_or_tty);
+ gclog_or_tty->print_cr("points to obj "PTR_FORMAT" "
+ "in region "HR_FORMAT,
+ (void*) obj,
+ HR_FORMAT_PARAMS(to));
+ obj->print_on(gclog_or_tty);
+ gclog_or_tty->print_cr("Obj head CTE = %d, field CTE = %d.",
+ cv_obj, cv_field);
+ gclog_or_tty->print_cr("----------");
+ gclog_or_tty->flush();
+ _failures = true;
+ if (!failed) _n_failures++;
+ }
+ }
+ }
+ }
+ }
+};
// This really ought to be commoned up into OffsetTableContigSpace somehow.
// We would need a mechanism to make that code skip dead objects.
@@ -903,6 +1058,13 @@
*failures = true;
return;
}
+
+ verify_strong_code_roots(vo, failures);
+}
+
+void HeapRegion::verify() const {
+ bool dummy = false;
+ verify(VerifyOption_G1UsePrevMarking, /* failures */ &dummy);
}
// G1OffsetTableContigSpace code; copied from space.cpp. Hope this can go
diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp
index 8ab893f..b15dc59 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp
@@ -51,6 +51,7 @@
class HeapRegionRemSetIterator;
class HeapRegion;
class HeapRegionSetBase;
+class nmethod;
#define HR_FORMAT "%u:(%s)["PTR_FORMAT","PTR_FORMAT","PTR_FORMAT"]"
#define HR_FORMAT_PARAMS(_hr_) \
@@ -374,7 +375,8 @@
RebuildRSClaimValue = 5,
ParEvacFailureClaimValue = 6,
AggregateCountClaimValue = 7,
- VerifyCountClaimValue = 8
+ VerifyCountClaimValue = 8,
+ ParMarkRootClaimValue = 9
};
inline HeapWord* par_allocate_no_bot_updates(size_t word_size) {
@@ -801,6 +803,25 @@
virtual void reset_after_compaction();
+ // Routines for managing a list of code roots (attached to the
+ // this region's RSet) that point into this heap region.
+ void add_strong_code_root(nmethod* nm);
+ void remove_strong_code_root(nmethod* nm);
+
+ // During a collection, migrate the successfully evacuated
+ // strong code roots that referenced into this region to the
+ // new regions that they now point into. Unsuccessfully
+ // evacuated code roots are not migrated.
+ void migrate_strong_code_roots();
+
+ // Applies blk->do_code_blob() to each of the entries in
+ // the strong code roots list for this region
+ void strong_code_roots_do(CodeBlobClosure* blk) const;
+
+ // 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;
+
void print() const;
void print_on(outputStream* st) const;
diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp
index 56e9405..4f7af43 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp
@@ -33,6 +33,7 @@
#include "oops/oop.inline.hpp"
#include "utilities/bitMap.inline.hpp"
#include "utilities/globalDefinitions.hpp"
+#include "utilities/growableArray.hpp"
class PerRegionTable: public CHeapObj<mtGC> {
friend class OtherRegionsTable;
@@ -706,10 +707,11 @@
// Cast away const in this case.
MutexLockerEx x((Mutex*)&_m, Mutex::_no_safepoint_check_flag);
size_t sum = 0;
- PerRegionTable * cur = _first_all_fine_prts;
- while (cur != NULL) {
- sum += cur->mem_size();
- cur = cur->next();
+ // all PRTs are of the same size so it is sufficient to query only one of them.
+ if (_first_all_fine_prts != NULL) {
+ assert(_last_all_fine_prts != NULL &&
+ _first_all_fine_prts->mem_size() == _last_all_fine_prts->mem_size(), "check that mem_size() is constant");
+ sum += _first_all_fine_prts->mem_size() * _n_fine_entries;
}
sum += (sizeof(PerRegionTable*) * _max_fine_entries);
sum += (_coarse_map.size_in_words() * HeapWordSize);
@@ -845,7 +847,7 @@
HeapRegionRemSet::HeapRegionRemSet(G1BlockOffsetSharedArray* bosa,
HeapRegion* hr)
- : _bosa(bosa), _other_regions(hr) {
+ : _bosa(bosa), _strong_code_roots_list(NULL), _other_regions(hr) {
reset_for_par_iteration();
}
@@ -909,6 +911,12 @@
}
void HeapRegionRemSet::clear() {
+ if (_strong_code_roots_list != NULL) {
+ delete _strong_code_roots_list;
+ }
+ _strong_code_roots_list = new (ResourceObj::C_HEAP, mtGC)
+ GrowableArray<nmethod*>(10, 0, NULL, true);
+
_other_regions.clear();
assert(occupied() == 0, "Should be clear.");
reset_for_par_iteration();
@@ -926,6 +934,126 @@
_other_regions.scrub(ctbs, region_bm, card_bm);
}
+
+// Code roots support
+
+void HeapRegionRemSet::add_strong_code_root(nmethod* nm) {
+ assert(nm != NULL, "sanity");
+ // Search for the code blob from the RHS to avoid
+ // duplicate entries as much as possible
+ if (_strong_code_roots_list->find_from_end(nm) < 0) {
+ // Code blob isn't already in the list
+ _strong_code_roots_list->push(nm);
+ }
+}
+
+void HeapRegionRemSet::remove_strong_code_root(nmethod* nm) {
+ assert(nm != NULL, "sanity");
+ int idx = _strong_code_roots_list->find(nm);
+ if (idx >= 0) {
+ _strong_code_roots_list->remove_at(idx);
+ }
+ // Check that there were no duplicates
+ guarantee(_strong_code_roots_list->find(nm) < 0, "duplicate entry found");
+}
+
+class NMethodMigrationOopClosure : public OopClosure {
+ G1CollectedHeap* _g1h;
+ HeapRegion* _from;
+ nmethod* _nm;
+
+ uint _num_self_forwarded;
+
+ template <class T> void do_oop_work(T* p) {
+ T heap_oop = oopDesc::load_heap_oop(p);
+ if (!oopDesc::is_null(heap_oop)) {
+ oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
+ if (obj->is_perm()) {
+ // reference into perm gen - ignore.
+ return;
+ } else if (_from->is_in(obj)) {
+ // Reference still points into the source region.
+ // Since roots are immediately evacuated this means that
+ // we must have self forwarded the object
+ assert(obj->is_forwarded(),
+ err_msg("code roots should be immediately evacuated. "
+ "Ref: "PTR_FORMAT", "
+ "Obj: "PTR_FORMAT", "
+ "Region: "HR_FORMAT,
+ p, (void*) obj, HR_FORMAT_PARAMS(_from)));
+ assert(obj->forwardee() == obj,
+ err_msg("not self forwarded? obj = "PTR_FORMAT, (void*)obj));
+
+ // The object has been self forwarded.
+ // Note, if we're during an initial mark pause, there is
+ // no need to explicitly mark object. It will be marked
+ // during the regular evacuation failure handling code.
+ _num_self_forwarded++;
+ } else {
+ // The reference points into a promotion or to-space region
+ HeapRegion* to = _g1h->heap_region_containing(obj);
+ to->rem_set()->add_strong_code_root(_nm);
+ }
+ }
+ }
+
+public:
+ NMethodMigrationOopClosure(G1CollectedHeap* g1h, HeapRegion* from, nmethod* nm):
+ _g1h(g1h), _from(from), _nm(nm), _num_self_forwarded(0) {}
+
+ void do_oop(narrowOop* p) { do_oop_work(p); }
+ void do_oop(oop* p) { do_oop_work(p); }
+
+ uint retain() { return _num_self_forwarded > 0; }
+};
+
+void HeapRegionRemSet::migrate_strong_code_roots() {
+ assert(hr()->in_collection_set(), "only collection set regions");
+ assert(!hr()->isHumongous(),
+ err_msg("humongous region "HR_FORMAT" should not have been added to the collection set",
+ HR_FORMAT_PARAMS(hr())));
+
+ ResourceMark rm;
+
+ // List of code blobs to retain for this region
+ GrowableArray<nmethod*> to_be_retained(10);
+ G1CollectedHeap* g1h = G1CollectedHeap::heap();
+
+ while (_strong_code_roots_list->is_nonempty()) {
+ nmethod *nm = _strong_code_roots_list->pop();
+ if (nm != NULL) {
+ NMethodMigrationOopClosure oop_cl(g1h, hr(), nm);
+ nm->oops_do(&oop_cl);
+ if (oop_cl.retain()) {
+ to_be_retained.push(nm);
+ }
+ }
+ }
+
+ // Now push any code roots we need to retain
+ assert(to_be_retained.is_empty() || hr()->evacuation_failed(),
+ "Retained nmethod list must be empty or "
+ "evacuation of this region failed");
+
+ while (to_be_retained.is_nonempty()) {
+ nmethod* nm = to_be_retained.pop();
+ assert(nm != NULL, "sanity");
+ add_strong_code_root(nm);
+ }
+}
+
+void HeapRegionRemSet::strong_code_roots_do(CodeBlobClosure* blk) const {
+ for (int i = 0; i < _strong_code_roots_list->length(); i += 1) {
+ nmethod* nm = _strong_code_roots_list->at(i);
+ blk->do_code_blob(nm);
+ }
+}
+
+size_t HeapRegionRemSet::strong_code_roots_mem_size() {
+ return sizeof(GrowableArray<nmethod*>) +
+ _strong_code_roots_list->max_length() * sizeof(nmethod*);
+}
+
//-------------------- Iteration --------------------
HeapRegionRemSetIterator::
diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp
index 1b1d42d..0063404 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp
@@ -37,6 +37,7 @@
class HeapRegionRemSetIterator;
class PerRegionTable;
class SparsePRT;
+class nmethod;
// Essentially a wrapper around SparsePRTCleanupTask. See
// sparsePRT.hpp for more details.
@@ -191,6 +192,10 @@
G1BlockOffsetSharedArray* _bosa;
G1BlockOffsetSharedArray* bosa() const { return _bosa; }
+ // A list of code blobs (nmethods) whose code contains pointers into
+ // the region that owns this RSet.
+ GrowableArray<nmethod*>* _strong_code_roots_list;
+
OtherRegionsTable _other_regions;
enum ParIterState { Unclaimed, Claimed, Complete };
@@ -285,11 +290,13 @@
void init_iterator(HeapRegionRemSetIterator* iter) const;
// The actual # of bytes this hr_remset takes up.
+ // Note also includes the strong code root set.
size_t mem_size() {
return _other_regions.mem_size()
// This correction is necessary because the above includes the second
// part.
- + sizeof(this) - sizeof(OtherRegionsTable);
+ + (sizeof(this) - sizeof(OtherRegionsTable))
+ + strong_code_roots_mem_size();
}
// Returns the memory occupancy of all static data structures associated
@@ -307,6 +314,37 @@
bool contains_reference(OopOrNarrowOopStar from) const {
return _other_regions.contains_reference(from);
}
+
+ // Routines for managing the list of code roots that point into
+ // the heap region that owns this RSet.
+ void add_strong_code_root(nmethod* nm);
+ void remove_strong_code_root(nmethod* nm);
+
+ // During a collection, migrate the successfully evacuated strong
+ // code roots that referenced into the region that owns this RSet
+ // to the RSets of the new regions that they now point into.
+ // Unsuccessfully evacuated code roots are not migrated.
+ void migrate_strong_code_roots();
+
+ // Applies blk->do_code_blob() to each of the entries in
+ // the strong code roots list
+ void strong_code_roots_do(CodeBlobClosure* blk) const;
+
+ // Returns the number of elements in the strong code roots list
+ int strong_code_roots_list_length() {
+ return _strong_code_roots_list->length();
+ }
+
+ // Returns true if the strong code roots contains the given
+ // nmethod.
+ bool strong_code_roots_list_contains(nmethod* nm) {
+ return _strong_code_roots_list->contains(nm);
+ }
+
+ // Returns the amount of memory, in bytes, currently
+ // consumed by the strong code roots.
+ size_t strong_code_roots_mem_size();
+
void print() const;
// Called during a stop-world phase to perform any deferred cleanups.
diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp
index 95c5641..7e3c690 100644
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp
@@ -149,8 +149,7 @@
if (VerifyBeforeGC && heap->total_collections() >= VerifyGCStartAt) {
HandleMark hm; // Discard invalid handles created during verification
- gclog_or_tty->print(" VerifyBeforeGC:");
- Universe::verify();
+ Universe::verify(" VerifyBeforeGC:");
}
// Verify object start arrays
@@ -359,8 +358,7 @@
if (VerifyAfterGC && heap->total_collections() >= VerifyGCStartAt) {
HandleMark hm; // Discard invalid handles created during verification
- gclog_or_tty->print(" VerifyAfterGC:");
- Universe::verify();
+ Universe::verify(" VerifyAfterGC:");
}
// Re-verify object start arrays
diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp
index 90e1c3d..7568855 100644
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp
@@ -1034,8 +1034,7 @@
if (VerifyBeforeGC && heap->total_collections() >= VerifyGCStartAt) {
HandleMark hm; // Discard invalid handles created during verification
- gclog_or_tty->print(" VerifyBeforeGC:");
- Universe::verify();
+ Universe::verify(" VerifyBeforeGC:");
}
// Verify object start arrays
@@ -2248,8 +2247,7 @@
if (VerifyAfterGC && heap->total_collections() >= VerifyGCStartAt) {
HandleMark hm; // Discard invalid handles created during verification
- gclog_or_tty->print(" VerifyAfterGC:");
- Universe::verify();
+ Universe::verify(" VerifyAfterGC:");
}
// Re-verify object start arrays
diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp
index d50c87d..4d698ca 100644
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp
@@ -325,8 +325,7 @@
if (VerifyBeforeGC && heap->total_collections() >= VerifyGCStartAt) {
HandleMark hm; // Discard invalid handles created during verification
- gclog_or_tty->print(" VerifyBeforeGC:");
- Universe::verify();
+ Universe::verify(" VerifyBeforeGC:");
}
{
@@ -661,8 +660,7 @@
if (VerifyAfterGC && heap->total_collections() >= VerifyGCStartAt) {
HandleMark hm; // Discard invalid handles created during verification
- gclog_or_tty->print(" VerifyAfterGC:");
- Universe::verify();
+ Universe::verify(" VerifyAfterGC:");
}
heap->print_heap_after_gc();
diff --git a/hotspot/src/share/vm/gc_interface/collectedHeap.cpp b/hotspot/src/share/vm/gc_interface/collectedHeap.cpp
index ba7116e..3dd44a7 100644
--- a/hotspot/src/share/vm/gc_interface/collectedHeap.cpp
+++ b/hotspot/src/share/vm/gc_interface/collectedHeap.cpp
@@ -121,6 +121,14 @@
}
}
+void CollectedHeap::register_nmethod(nmethod* nm) {
+ assert_locked_or_safepoint(CodeCache_lock);
+}
+
+void CollectedHeap::unregister_nmethod(nmethod* nm) {
+ assert_locked_or_safepoint(CodeCache_lock);
+}
+
void CollectedHeap::trace_heap(GCWhen::Type when, GCTracer* gc_tracer) {
const GCHeapSummary& heap_summary = create_heap_summary();
const PermGenSummary& perm_summary = create_perm_gen_summary();
diff --git a/hotspot/src/share/vm/gc_interface/collectedHeap.hpp b/hotspot/src/share/vm/gc_interface/collectedHeap.hpp
index 49803b9..0850c13 100644
--- a/hotspot/src/share/vm/gc_interface/collectedHeap.hpp
+++ b/hotspot/src/share/vm/gc_interface/collectedHeap.hpp
@@ -49,6 +49,7 @@
class Thread;
class ThreadClosure;
class VirtualSpaceSummary;
+class nmethod;
class GCMessage : public FormatBuffer<1024> {
public:
@@ -667,6 +668,11 @@
void print_heap_before_gc();
void print_heap_after_gc();
+ // Registering and unregistering an nmethod (compiled code) with the heap.
+ // Override with specific mechanism for each specialized heap type.
+ virtual void register_nmethod(nmethod* nm);
+ virtual void unregister_nmethod(nmethod* nm);
+
void trace_heap_before_gc(GCTracer* gc_tracer);
void trace_heap_after_gc(GCTracer* gc_tracer);
diff --git a/hotspot/src/share/vm/memory/genCollectedHeap.cpp b/hotspot/src/share/vm/memory/genCollectedHeap.cpp
index 1d9f3f5..85d5ec2 100644
--- a/hotspot/src/share/vm/memory/genCollectedHeap.cpp
+++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp
@@ -544,8 +544,7 @@
prepare_for_verify();
prepared_for_verification = true;
}
- gclog_or_tty->print(" VerifyBeforeGC:");
- Universe::verify();
+ Universe::verify(" VerifyBeforeGC:");
}
COMPILER2_PRESENT(DerivedPointerTable::clear());
@@ -616,8 +615,7 @@
if (VerifyAfterGC && i >= VerifyGCLevel &&
total_collections() >= VerifyGCStartAt) {
HandleMark hm; // Discard invalid handles created during verification
- gclog_or_tty->print(" VerifyAfterGC:");
- Universe::verify();
+ Universe::verify(" VerifyAfterGC:");
}
if (PrintGCDetails) {
@@ -938,12 +936,13 @@
// Returns "TRUE" iff "p" points into the committed areas of the heap.
bool GenCollectedHeap::is_in(const void* p) const {
#ifndef ASSERT
- guarantee(VerifyBeforeGC ||
- VerifyDuringGC ||
- VerifyBeforeExit ||
- PrintAssembly ||
- tty->count() != 0 || // already printing
- VerifyAfterGC ||
+ guarantee(VerifyBeforeGC ||
+ VerifyDuringGC ||
+ VerifyBeforeExit ||
+ VerifyDuringStartup ||
+ PrintAssembly ||
+ tty->count() != 0 || // already printing
+ VerifyAfterGC ||
VMError::fatal_error_in_progress(), "too expensive");
#endif
diff --git a/hotspot/src/share/vm/memory/iterator.cpp b/hotspot/src/share/vm/memory/iterator.cpp
index de0958c..62c5892 100644
--- a/hotspot/src/share/vm/memory/iterator.cpp
+++ b/hotspot/src/share/vm/memory/iterator.cpp
@@ -70,7 +70,7 @@
}
void CodeBlobToOopClosure::do_newly_marked_nmethod(nmethod* nm) {
- nm->oops_do(_cl, /*do_strong_roots_only=*/ true);
+ nm->oops_do(_cl, /*do_strong_roots_only=*/ true, /*allow_zombie=*/ false);
}
void CodeBlobToOopClosure::do_code_blob(CodeBlob* cb) {
diff --git a/hotspot/src/share/vm/memory/sharedHeap.cpp b/hotspot/src/share/vm/memory/sharedHeap.cpp
index fc374f1..e8b0146 100644
--- a/hotspot/src/share/vm/memory/sharedHeap.cpp
+++ b/hotspot/src/share/vm/memory/sharedHeap.cpp
@@ -148,7 +148,8 @@
ScanningOption so,
OopClosure* roots,
CodeBlobClosure* code_roots,
- OopsInGenClosure* perm_blk) {
+ OopsInGenClosure* perm_blk,
+ bool manages_code_roots) {
StrongRootsScope srs(this, activate_scope);
// General strong roots.
assert(_strong_roots_parity != 0, "must have called prologue code");
@@ -216,7 +217,7 @@
CodeCache::blobs_do(code_roots);
}
} else if (so & (SO_SystemClasses|SO_AllClasses)) {
- if (!collecting_perm_gen) {
+ if (!manages_code_roots && !collecting_perm_gen) {
// If we are collecting from class statics, but we are not going to
// visit all of the CodeCache, collect from the non-perm roots if any.
// This makes the code cache function temporarily as a source of strong
diff --git a/hotspot/src/share/vm/memory/sharedHeap.hpp b/hotspot/src/share/vm/memory/sharedHeap.hpp
index c97ff66..d09848b 100644
--- a/hotspot/src/share/vm/memory/sharedHeap.hpp
+++ b/hotspot/src/share/vm/memory/sharedHeap.hpp
@@ -267,7 +267,8 @@
ScanningOption so,
OopClosure* roots,
CodeBlobClosure* code_roots,
- OopsInGenClosure* perm_blk);
+ OopsInGenClosure* perm_blk,
+ bool manages_code_roots = false);
// Apply "blk" to all the weak roots of the system. These include
// JNI weak roots, the code cache, system dictionary, symbol table,
diff --git a/hotspot/src/share/vm/memory/universe.cpp b/hotspot/src/share/vm/memory/universe.cpp
index 7b11d0b..4030d9d 100644
--- a/hotspot/src/share/vm/memory/universe.cpp
+++ b/hotspot/src/share/vm/memory/universe.cpp
@@ -1374,7 +1374,7 @@
st->print_cr("}");
}
-void Universe::verify(bool silent, VerifyOption option) {
+void Universe::verify(VerifyOption option, const char* prefix, bool silent) {
if (SharedSkipVerify) {
return;
}
@@ -1395,11 +1395,12 @@
HandleMark hm; // Handles created during verification can be zapped
_verify_count++;
+ if (!silent) gclog_or_tty->print(prefix);
if (!silent) gclog_or_tty->print("[Verifying ");
if (!silent) gclog_or_tty->print("threads ");
Threads::verify();
+ if (!silent) gclog_or_tty->print("heap ");
heap()->verify(silent, option);
-
if (!silent) gclog_or_tty->print("syms ");
SymbolTable::verify();
if (!silent) gclog_or_tty->print("strs ");
diff --git a/hotspot/src/share/vm/memory/universe.hpp b/hotspot/src/share/vm/memory/universe.hpp
index 91441a7..50fcb62a 100644
--- a/hotspot/src/share/vm/memory/universe.hpp
+++ b/hotspot/src/share/vm/memory/universe.hpp
@@ -419,12 +419,12 @@
// Debugging
static bool verify_in_progress() { return _verify_in_progress; }
- static void verify(bool silent, VerifyOption option);
- static void verify(bool silent) {
- verify(silent, VerifyOption_Default /* option */);
+ static void verify(VerifyOption option, const char* prefix, bool silent = VerifySilently);
+ static void verify(const char* prefix, bool silent = VerifySilently) {
+ verify(VerifyOption_Default, prefix, silent);
}
- static void verify() {
- verify(false /* silent */);
+ static void verify(bool silent = VerifySilently) {
+ verify("", silent);
}
static int verify_count() { return _verify_count; }
diff --git a/hotspot/src/share/vm/oops/constantPoolOop.cpp b/hotspot/src/share/vm/oops/constantPoolOop.cpp
index 25dde7a..659fc89 100644
--- a/hotspot/src/share/vm/oops/constantPoolOop.cpp
+++ b/hotspot/src/share/vm/oops/constantPoolOop.cpp
@@ -1078,12 +1078,67 @@
} // end compare_entry_to()
+void constantPoolOopDesc::copy_operands(constantPoolHandle from_cp,
+ constantPoolHandle to_cp,
+ TRAPS) {
+
+ int from_oplen = operand_array_length(from_cp->operands());
+ int old_oplen = operand_array_length(to_cp->operands());
+ if (from_oplen != 0) {
+ // append my operands to the target's operands array
+ if (old_oplen == 0) {
+ to_cp->set_operands(from_cp->operands()); // reuse; do not merge
+ } else {
+ int old_len = to_cp->operands()->length();
+ int from_len = from_cp->operands()->length();
+ int old_off = old_oplen * sizeof(u2);
+ int from_off = from_oplen * sizeof(u2);
+ typeArrayHandle new_operands = oopFactory::new_permanent_shortArray(old_len + from_len, CHECK);
+ int fillp = 0, len = 0;
+ // first part of dest
+ Copy::conjoint_memory_atomic(to_cp->operands()->short_at_addr(0),
+ new_operands->short_at_addr(fillp),
+ (len = old_off) * sizeof(u2));
+ fillp += len;
+ // first part of src
+ Copy::conjoint_memory_atomic(from_cp->operands()->short_at_addr(0),
+ new_operands->short_at_addr(fillp),
+ (len = from_off) * sizeof(u2));
+ fillp += len;
+ // second part of dest
+ Copy::conjoint_memory_atomic(to_cp->operands()->short_at_addr(old_off),
+ new_operands->short_at_addr(fillp),
+ (len = old_len - old_off) * sizeof(u2));
+ fillp += len;
+ // second part of src
+ Copy::conjoint_memory_atomic(from_cp->operands()->short_at_addr(from_off),
+ new_operands->short_at_addr(fillp),
+ (len = from_len - from_off) * sizeof(u2));
+ fillp += len;
+ assert(fillp == new_operands->length(), "");
+
+ // Adjust indexes in the first part of the copied operands array.
+ for (int j = 0; j < from_oplen; j++) {
+ int offset = operand_offset_at(new_operands(), old_oplen + j);
+ assert(offset == operand_offset_at(from_cp->operands(), j), "correct copy");
+ offset += old_len; // every new tuple is preceded by old_len extra u2's
+ operand_offset_at_put(new_operands(), old_oplen + j, offset);
+ }
+
+ // replace target operands array with combined array
+ to_cp->set_operands(new_operands());
+ }
+ }
+} // end copy_operands()
+
+
// Copy this constant pool's entries at start_i to end_i (inclusive)
// to the constant pool to_cp's entries starting at to_i. A total of
// (end_i - start_i) + 1 entries are copied.
void constantPoolOopDesc::copy_cp_to_impl(constantPoolHandle from_cp, int start_i, int end_i,
constantPoolHandle to_cp, int to_i, TRAPS) {
+
int dest_i = to_i; // leave original alone for debug purposes
for (int src_i = start_i; src_i <= end_i; /* see loop bottom */ ) {
@@ -1104,56 +1159,9 @@
break;
}
}
+ copy_operands(from_cp, to_cp, CHECK);
- int from_oplen = operand_array_length(from_cp->operands());
- int old_oplen = operand_array_length(to_cp->operands());
- if (from_oplen != 0) {
- // append my operands to the target's operands array
- if (old_oplen == 0) {
- to_cp->set_operands(from_cp->operands()); // reuse; do not merge
- } else {
- int old_len = to_cp->operands()->length();
- int from_len = from_cp->operands()->length();
- int old_off = old_oplen * sizeof(u2);
- int from_off = from_oplen * sizeof(u2);
- typeArrayHandle new_operands = oopFactory::new_permanent_shortArray(old_len + from_len, CHECK);
- int fillp = 0, len = 0;
- // first part of dest
- Copy::conjoint_memory_atomic(to_cp->operands()->short_at_addr(0),
- new_operands->short_at_addr(fillp),
- (len = old_off) * sizeof(u2));
- fillp += len;
- // first part of src
- Copy::conjoint_memory_atomic(to_cp->operands()->short_at_addr(0),
- new_operands->short_at_addr(fillp),
- (len = from_off) * sizeof(u2));
- fillp += len;
- // second part of dest
- Copy::conjoint_memory_atomic(to_cp->operands()->short_at_addr(old_off),
- new_operands->short_at_addr(fillp),
- (len = old_len - old_off) * sizeof(u2));
- fillp += len;
- // second part of src
- Copy::conjoint_memory_atomic(to_cp->operands()->short_at_addr(from_off),
- new_operands->short_at_addr(fillp),
- (len = from_len - from_off) * sizeof(u2));
- fillp += len;
- assert(fillp == new_operands->length(), "");
-
- // Adjust indexes in the first part of the copied operands array.
- for (int j = 0; j < from_oplen; j++) {
- int offset = operand_offset_at(new_operands(), old_oplen + j);
- assert(offset == operand_offset_at(from_cp->operands(), j), "correct copy");
- offset += old_len; // every new tuple is preceded by old_len extra u2's
- operand_offset_at_put(new_operands(), old_oplen + j, offset);
- }
-
- // replace target operands array with combined array
- to_cp->set_operands(new_operands());
- }
- }
-
-} // end copy_cp_to()
+} // end copy_cp_to_impl()
// Copy this constant pool's entry at from_i to the constant pool
diff --git a/hotspot/src/share/vm/oops/constantPoolOop.hpp b/hotspot/src/share/vm/oops/constantPoolOop.hpp
index ad72e2b..68e532a 100644
--- a/hotspot/src/share/vm/oops/constantPoolOop.hpp
+++ b/hotspot/src/share/vm/oops/constantPoolOop.hpp
@@ -755,6 +755,7 @@
copy_cp_to_impl(h_this, start_i, end_i, to_cp, to_i, THREAD);
}
static void copy_cp_to_impl(constantPoolHandle from_cp, int start_i, int end_i, constantPoolHandle to_cp, int to_i, TRAPS);
+ static void copy_operands(constantPoolHandle from_cp, constantPoolHandle to_cp, TRAPS);
static void copy_entry_to(constantPoolHandle from_cp, int from_i, constantPoolHandle to_cp, int to_i, TRAPS);
int find_matching_entry(int pattern_i, constantPoolHandle search_cp, TRAPS);
int orig_length() const { return _orig_length; }
diff --git a/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp
index 8265569..0a5a2c5 100644
--- a/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp
+++ b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp
@@ -204,7 +204,7 @@
write_attribute_name_index("Code");
write_u4(size);
- write_u2(method->max_stack());
+ write_u2(method->verifier_max_stack());
write_u2(method->max_locals());
write_u4(code_size);
copy_bytecodes(method, (unsigned char*)writeable_address(code_size));
diff --git a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp
index eb52388..bcf37e4 100644
--- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp
+++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp
@@ -262,76 +262,23 @@
case JVM_CONSTANT_NameAndType:
{
int name_ref_i = scratch_cp->name_ref_index_at(scratch_i);
- int new_name_ref_i = 0;
- bool match = (name_ref_i < *merge_cp_length_p) &&
- scratch_cp->compare_entry_to(name_ref_i, *merge_cp_p, name_ref_i,
- THREAD);
- if (!match) {
- // forward reference in *merge_cp_p or not a direct match
-
- int found_i = scratch_cp->find_matching_entry(name_ref_i, *merge_cp_p,
- THREAD);
- if (found_i != 0) {
- guarantee(found_i != name_ref_i,
- "compare_entry_to() and find_matching_entry() do not agree");
-
- // Found a matching entry somewhere else in *merge_cp_p so
- // just need a mapping entry.
- new_name_ref_i = found_i;
- map_index(scratch_cp, name_ref_i, found_i);
- } else {
- // no match found so we have to append this entry to *merge_cp_p
- append_entry(scratch_cp, name_ref_i, merge_cp_p, merge_cp_length_p,
- THREAD);
- // The above call to append_entry() can only append one entry
- // so the post call query of *merge_cp_length_p is only for
- // the sake of consistency.
- new_name_ref_i = *merge_cp_length_p - 1;
- }
- }
+ int new_name_ref_i = find_or_append_indirect_entry(scratch_cp, name_ref_i, merge_cp_p,
+ merge_cp_length_p, THREAD);
int signature_ref_i = scratch_cp->signature_ref_index_at(scratch_i);
- int new_signature_ref_i = 0;
- match = (signature_ref_i < *merge_cp_length_p) &&
- scratch_cp->compare_entry_to(signature_ref_i, *merge_cp_p,
- signature_ref_i, THREAD);
- if (!match) {
- // forward reference in *merge_cp_p or not a direct match
-
- int found_i = scratch_cp->find_matching_entry(signature_ref_i,
- *merge_cp_p, THREAD);
- if (found_i != 0) {
- guarantee(found_i != signature_ref_i,
- "compare_entry_to() and find_matching_entry() do not agree");
-
- // Found a matching entry somewhere else in *merge_cp_p so
- // just need a mapping entry.
- new_signature_ref_i = found_i;
- map_index(scratch_cp, signature_ref_i, found_i);
- } else {
- // no match found so we have to append this entry to *merge_cp_p
- append_entry(scratch_cp, signature_ref_i, merge_cp_p,
- merge_cp_length_p, THREAD);
- // The above call to append_entry() can only append one entry
- // so the post call query of *merge_cp_length_p is only for
- // the sake of consistency.
- new_signature_ref_i = *merge_cp_length_p - 1;
- }
- }
+ int new_signature_ref_i = find_or_append_indirect_entry(scratch_cp, signature_ref_i,
+ merge_cp_p, merge_cp_length_p,
+ THREAD);
// If the referenced entries already exist in *merge_cp_p, then
// both new_name_ref_i and new_signature_ref_i will both be 0.
// In that case, all we are appending is the current entry.
- if (new_name_ref_i == 0) {
- new_name_ref_i = name_ref_i;
- } else {
+ if (new_name_ref_i != name_ref_i) {
RC_TRACE(0x00080000,
("NameAndType entry@%d name_ref_index change: %d to %d",
*merge_cp_length_p, name_ref_i, new_name_ref_i));
}
- if (new_signature_ref_i == 0) {
- new_signature_ref_i = signature_ref_i;
- } else {
+ if (new_signature_ref_i != signature_ref_i) {
RC_TRACE(0x00080000,
("NameAndType entry@%d signature_ref_index change: %d to %d",
*merge_cp_length_p, signature_ref_i, new_signature_ref_i));
@@ -353,76 +300,11 @@
case JVM_CONSTANT_Methodref:
{
int klass_ref_i = scratch_cp->uncached_klass_ref_index_at(scratch_i);
- int new_klass_ref_i = 0;
- bool match = (klass_ref_i < *merge_cp_length_p) &&
- scratch_cp->compare_entry_to(klass_ref_i, *merge_cp_p, klass_ref_i,
- THREAD);
- if (!match) {
- // forward reference in *merge_cp_p or not a direct match
-
- int found_i = scratch_cp->find_matching_entry(klass_ref_i, *merge_cp_p,
- THREAD);
- if (found_i != 0) {
- guarantee(found_i != klass_ref_i,
- "compare_entry_to() and find_matching_entry() do not agree");
-
- // Found a matching entry somewhere else in *merge_cp_p so
- // just need a mapping entry.
- new_klass_ref_i = found_i;
- map_index(scratch_cp, klass_ref_i, found_i);
- } else {
- // no match found so we have to append this entry to *merge_cp_p
- append_entry(scratch_cp, klass_ref_i, merge_cp_p, merge_cp_length_p,
- THREAD);
- // The above call to append_entry() can only append one entry
- // so the post call query of *merge_cp_length_p is only for
- // the sake of consistency. Without the optimization where we
- // use JVM_CONSTANT_UnresolvedClass, then up to two entries
- // could be appended.
- new_klass_ref_i = *merge_cp_length_p - 1;
- }
- }
-
- int name_and_type_ref_i =
- scratch_cp->uncached_name_and_type_ref_index_at(scratch_i);
- int new_name_and_type_ref_i = 0;
- match = (name_and_type_ref_i < *merge_cp_length_p) &&
- scratch_cp->compare_entry_to(name_and_type_ref_i, *merge_cp_p,
- name_and_type_ref_i, THREAD);
- if (!match) {
- // forward reference in *merge_cp_p or not a direct match
-
- int found_i = scratch_cp->find_matching_entry(name_and_type_ref_i,
- *merge_cp_p, THREAD);
- if (found_i != 0) {
- guarantee(found_i != name_and_type_ref_i,
- "compare_entry_to() and find_matching_entry() do not agree");
-
- // Found a matching entry somewhere else in *merge_cp_p so
- // just need a mapping entry.
- new_name_and_type_ref_i = found_i;
- map_index(scratch_cp, name_and_type_ref_i, found_i);
- } else {
- // no match found so we have to append this entry to *merge_cp_p
- append_entry(scratch_cp, name_and_type_ref_i, merge_cp_p,
- merge_cp_length_p, THREAD);
- // The above call to append_entry() can append more than
- // one entry so the post call query of *merge_cp_length_p
- // is required in order to get the right index for the
- // JVM_CONSTANT_NameAndType entry.
- new_name_and_type_ref_i = *merge_cp_length_p - 1;
- }
- }
-
- // If the referenced entries already exist in *merge_cp_p, then
- // both new_klass_ref_i and new_name_and_type_ref_i will both be
- // 0. In that case, all we are appending is the current entry.
- if (new_klass_ref_i == 0) {
- new_klass_ref_i = klass_ref_i;
- }
- if (new_name_and_type_ref_i == 0) {
- new_name_and_type_ref_i = name_and_type_ref_i;
- }
+ int new_klass_ref_i = find_or_append_indirect_entry(scratch_cp, klass_ref_i,
+ merge_cp_p, merge_cp_length_p, THREAD);
+ int name_and_type_ref_i = scratch_cp->uncached_name_and_type_ref_index_at(scratch_i);
+ int new_name_and_type_ref_i = find_or_append_indirect_entry(scratch_cp, name_and_type_ref_i,
+ merge_cp_p, merge_cp_length_p, THREAD);
const char *entry_name;
switch (scratch_cp->tag_at(scratch_i).value()) {
@@ -465,7 +347,73 @@
(*merge_cp_length_p)++;
} break;
- // At this stage, Class or UnresolvedClass could be here, but not
+ // this is an indirect CP entry so it needs special handling
+ case JVM_CONSTANT_MethodType:
+ {
+ int ref_i = scratch_cp->method_type_index_at(scratch_i);
+ int new_ref_i = find_or_append_indirect_entry(scratch_cp, ref_i, merge_cp_p,
+ merge_cp_length_p, THREAD);
+ if (new_ref_i != ref_i) {
+ RC_TRACE(0x00080000,
+ ("MethodType entry@%d ref_index change: %d to %d",
+ *merge_cp_length_p, ref_i, new_ref_i));
+ }
+ (*merge_cp_p)->method_type_index_at_put(*merge_cp_length_p, new_ref_i);
+ if (scratch_i != *merge_cp_length_p) {
+ // The new entry in *merge_cp_p is at a different index than
+ // the new entry in scratch_cp so we need to map the index values.
+ map_index(scratch_cp, scratch_i, *merge_cp_length_p);
+ }
+ (*merge_cp_length_p)++;
+ } break;
+
+ // this is an indirect CP entry so it needs special handling
+ case JVM_CONSTANT_MethodHandle:
+ {
+ int ref_kind = scratch_cp->method_handle_ref_kind_at(scratch_i);
+ int ref_i = scratch_cp->method_handle_index_at(scratch_i);
+ int new_ref_i = find_or_append_indirect_entry(scratch_cp, ref_i, merge_cp_p,
+ merge_cp_length_p, THREAD);
+ if (new_ref_i != ref_i) {
+ RC_TRACE(0x00080000,
+ ("MethodHandle entry@%d ref_index change: %d to %d",
+ *merge_cp_length_p, ref_i, new_ref_i));
+ }
+ (*merge_cp_p)->method_handle_index_at_put(*merge_cp_length_p, ref_kind, new_ref_i);
+ if (scratch_i != *merge_cp_length_p) {
+ // The new entry in *merge_cp_p is at a different index than
+ // the new entry in scratch_cp so we need to map the index values.
+ map_index(scratch_cp, scratch_i, *merge_cp_length_p);
+ }
+ (*merge_cp_length_p)++;
+ } break;
+
+ // this is an indirect CP entry so it needs special handling
+ case JVM_CONSTANT_InvokeDynamic:
+ {
+ // TBD: cross-checks and possible extra appends into CP and bsm operands
+ // are needed as well. This issue is tracked by a separate bug 8007037.
+ int bss_idx = scratch_cp->invoke_dynamic_bootstrap_specifier_index(scratch_i);
+
+ int ref_i = scratch_cp->invoke_dynamic_name_and_type_ref_index_at(scratch_i);
+ int new_ref_i = find_or_append_indirect_entry(scratch_cp, ref_i, merge_cp_p,
+ merge_cp_length_p, THREAD);
+ if (new_ref_i != ref_i) {
+ RC_TRACE(0x00080000,
+ ("InvokeDynamic entry@%d name_and_type ref_index change: %d to %d",
+ *merge_cp_length_p, ref_i, new_ref_i));
+ }
+
+ (*merge_cp_p)->invoke_dynamic_at_put(*merge_cp_length_p, bss_idx, new_ref_i);
+ if (scratch_i != *merge_cp_length_p) {
+ // The new entry in *merge_cp_p is at a different index than
+ // the new entry in scratch_cp so we need to map the index values.
+ map_index(scratch_cp, scratch_i, *merge_cp_length_p);
+ }
+ (*merge_cp_length_p)++;
+ } break;
+
+ // At this stage, Class or UnresolvedClass could be here, but not
// ClassIndex
case JVM_CONSTANT_ClassIndex: // fall through
@@ -492,6 +440,35 @@
} // end append_entry()
+int VM_RedefineClasses::find_or_append_indirect_entry(constantPoolHandle scratch_cp,
+ int ref_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS) {
+
+ int new_ref_i = ref_i;
+ bool match = (ref_i < *merge_cp_length_p) &&
+ scratch_cp->compare_entry_to(ref_i, *merge_cp_p, ref_i, THREAD);
+
+ if (!match) {
+ // forward reference in *merge_cp_p or not a direct match
+ int found_i = scratch_cp->find_matching_entry(ref_i, *merge_cp_p, THREAD);
+ if (found_i != 0) {
+ guarantee(found_i != ref_i, "compare_entry_to() and find_matching_entry() do not agree");
+ // Found a matching entry somewhere else in *merge_cp_p so just need a mapping entry.
+ new_ref_i = found_i;
+ map_index(scratch_cp, ref_i, found_i);
+ } else {
+ // no match found so we have to append this entry to *merge_cp_p
+ append_entry(scratch_cp, ref_i, merge_cp_p, merge_cp_length_p, THREAD);
+ // The above call to append_entry() can only append one entry
+ // so the post call query of *merge_cp_length_p is only for
+ // the sake of consistency.
+ new_ref_i = *merge_cp_length_p - 1;
+ }
+ }
+
+ return new_ref_i;
+} // end find_or_append_indirect_entry()
+
+
void VM_RedefineClasses::swap_all_method_annotations(int i, int j, instanceKlassHandle scratch_class) {
typeArrayOop save;
@@ -1113,6 +1090,8 @@
}
} // end for each old_cp entry
+ constantPoolOopDesc::copy_operands(old_cp, *merge_cp_p, CHECK_0);
+
// We don't need to sanity check that *merge_cp_length_p is within
// *merge_cp_p bounds since we have the minimum on-entry check above.
(*merge_cp_length_p) = old_i;
@@ -1282,8 +1261,12 @@
_index_map_count = 0;
_index_map_p = new intArray(scratch_cp->length(), -1);
+ // reference to the cp holder is needed for copy_operands()
+ merge_cp->set_pool_holder(scratch_class());
bool result = merge_constant_pools(old_cp, scratch_cp, &merge_cp,
&merge_cp_length, THREAD);
+ merge_cp->set_pool_holder(NULL);
+
if (!result) {
// The merge can fail due to memory allocation failure or due
// to robustness checks.
@@ -1326,7 +1309,7 @@
// Replace the new constant pool with a shrunken copy of the
// merged constant pool; the previous new constant pool will
// get GCed.
- set_new_constant_pool(scratch_class, merge_cp, merge_cp_length, true,
+ set_new_constant_pool(scratch_class, merge_cp, merge_cp_length,
THREAD);
// drop local ref to the merged constant pool
merge_cp()->set_is_conc_safe(true);
@@ -1357,7 +1340,7 @@
// merged constant pool so now the rewritten bytecodes have
// valid references; the previous new constant pool will get
// GCed.
- set_new_constant_pool(scratch_class, merge_cp, merge_cp_length, true,
+ set_new_constant_pool(scratch_class, merge_cp, merge_cp_length,
THREAD);
merge_cp()->set_is_conc_safe(true);
}
@@ -1540,6 +1523,7 @@
case Bytecodes::_getfield : // fall through
case Bytecodes::_getstatic : // fall through
case Bytecodes::_instanceof : // fall through
+ case Bytecodes::_invokedynamic : // fall through
case Bytecodes::_invokeinterface: // fall through
case Bytecodes::_invokespecial : // fall through
case Bytecodes::_invokestatic : // fall through
@@ -2343,30 +2327,30 @@
// smaller constant pool is associated with scratch_class.
void VM_RedefineClasses::set_new_constant_pool(
instanceKlassHandle scratch_class, constantPoolHandle scratch_cp,
- int scratch_cp_length, bool shrink, TRAPS) {
- assert(!shrink || scratch_cp->length() >= scratch_cp_length, "sanity check");
+ int scratch_cp_length, TRAPS) {
+ assert(scratch_cp->length() >= scratch_cp_length, "sanity check");
- if (shrink) {
- // scratch_cp is a merged constant pool and has enough space for a
- // worst case merge situation. We want to associate the minimum
- // sized constant pool with the klass to save space.
- constantPoolHandle smaller_cp(THREAD,
- oopFactory::new_constantPool(scratch_cp_length,
- oopDesc::IsUnsafeConc,
- THREAD));
- // preserve orig_length() value in the smaller copy
- int orig_length = scratch_cp->orig_length();
- assert(orig_length != 0, "sanity check");
- smaller_cp->set_orig_length(orig_length);
- scratch_cp->copy_cp_to(1, scratch_cp_length - 1, smaller_cp, 1, THREAD);
- scratch_cp = smaller_cp;
- smaller_cp()->set_is_conc_safe(true);
- }
-
- // attach new constant pool to klass
- scratch_cp->set_pool_holder(scratch_class());
+ // scratch_cp is a merged constant pool and has enough space for a
+ // worst case merge situation. We want to associate the minimum
+ // sized constant pool with the klass to save space.
+ constantPoolHandle smaller_cp(THREAD,
+ oopFactory::new_constantPool(scratch_cp_length,
+ oopDesc::IsUnsafeConc,
+ THREAD));
+ // preserve orig_length() value in the smaller copy
+ int orig_length = scratch_cp->orig_length();
+ assert(orig_length != 0, "sanity check");
+ smaller_cp->set_orig_length(orig_length);
// attach klass to new constant pool
+ // reference to the cp holder is needed for copy_operands()
+ smaller_cp->set_pool_holder(scratch_class());
+
+ scratch_cp->copy_cp_to(1, scratch_cp_length - 1, smaller_cp, 1, THREAD);
+ scratch_cp = smaller_cp;
+ smaller_cp()->set_is_conc_safe(true);
+
+ // attach new constant pool to klass
scratch_class->set_constants(scratch_cp());
int i; // for portability
diff --git a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp
index 671f2ae..82c3228 100644
--- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp
+++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp
@@ -431,10 +431,11 @@
// and in all direct and indirect subclasses.
void increment_class_counter(instanceKlass *ik, TRAPS);
- // Support for constant pool merging (these routines are in alpha
- // order):
+ // Support for constant pool merging (these routines are in alpha order):
void append_entry(constantPoolHandle scratch_cp, int scratch_i,
constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS);
+ int find_or_append_indirect_entry(constantPoolHandle scratch_cp, int scratch_i,
+ constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS);
int find_new_index(int old_index);
bool is_unresolved_class_mismatch(constantPoolHandle cp1, int index1,
constantPoolHandle cp2, int index2);
@@ -474,7 +475,7 @@
address& stackmap_addr_ref, address stackmap_end, u2 frame_i,
u1 frame_size, TRAPS);
void set_new_constant_pool(instanceKlassHandle scratch_class,
- constantPoolHandle scratch_cp, int scratch_cp_length, bool shrink, TRAPS);
+ constantPoolHandle scratch_cp, int scratch_cp_length, TRAPS);
void flush_dependent_code(instanceKlassHandle k_h, TRAPS);
diff --git a/hotspot/src/share/vm/prims/whitebox.cpp b/hotspot/src/share/vm/prims/whitebox.cpp
index ddb35cb..3b56191 100644
--- a/hotspot/src/share/vm/prims/whitebox.cpp
+++ b/hotspot/src/share/vm/prims/whitebox.cpp
@@ -27,6 +27,8 @@
#include "memory/universe.hpp"
#include "oops/oop.inline.hpp"
+#include "code/codeCache.hpp"
+
#include "classfile/symbolTable.hpp"
#include "prims/whitebox.hpp"
@@ -131,6 +133,13 @@
return MemTracker::wbtest_wait_for_data_merge();
WB_END
+WB_ENTRY(void, WB_DeoptimizeAll(JNIEnv* env, jobject o))
+ MutexLockerEx mu(Compile_lock);
+ CodeCache::mark_all_nmethods_for_deoptimization();
+ VM_Deoptimize op;
+ VMThread::execute(&op);
+WB_END
+
//Some convenience methods to deal with objects from java
int WhiteBox::offset_for_field(const char* field_name, oop object,
Symbol* signature_symbol) {
@@ -204,6 +213,7 @@
{CC"NMTUncommitMemory", CC"(JJ)V", (void*)&WB_NMTUncommitMemory },
{CC"NMTReleaseMemory", CC"(JJ)V", (void*)&WB_NMTReleaseMemory },
{CC"NMTWaitForDataMerge", CC"()Z", (void*)&WB_NMTWaitForDataMerge},
+ {CC"deoptimizeAll", CC"()V", (void*)&WB_DeoptimizeAll },
};
#undef CC
diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp
index 22d6fe2..142708d 100644
--- a/hotspot/src/share/vm/runtime/arguments.cpp
+++ b/hotspot/src/share/vm/runtime/arguments.cpp
@@ -1943,11 +1943,12 @@
// than just disable the lock verification. This will be fixed under
// bug 4788986.
if (UseConcMarkSweepGC && FLSVerifyAllHeapReferences) {
- if (VerifyGCStartAt == 0) {
+ if (VerifyDuringStartup) {
warning("Heap verification at start-up disabled "
"(due to current incompatibility with FLSVerifyAllHeapReferences)");
- VerifyGCStartAt = 1; // Disable verification at start-up
+ VerifyDuringStartup = false; // Disable verification at start-up
}
+
if (VerifyBeforeExit) {
warning("Heap verification at shutdown disabled "
"(due to current incompatibility with FLSVerifyAllHeapReferences)");
diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp
index 973b9cc..a7469a7 100644
--- a/hotspot/src/share/vm/runtime/globals.hpp
+++ b/hotspot/src/share/vm/runtime/globals.hpp
@@ -2129,6 +2129,13 @@
product(intx, PrefetchFieldsAhead, -1, \
"How many fields ahead to prefetch in oop scan (<= 0 means off)") \
\
+ diagnostic(bool, VerifySilently, false, \
+ "Don't print print the verification progress") \
+ \
+ diagnostic(bool, VerifyDuringStartup, false, \
+ "Verify memory system before executing any Java code " \
+ "during VM initialization") \
+ \
diagnostic(bool, VerifyBeforeExit, trueInDebug, \
"Verify system before exiting") \
\
diff --git a/hotspot/src/share/vm/runtime/sweeper.hpp b/hotspot/src/share/vm/runtime/sweeper.hpp
index 5ccd228..fffdc5b 100644
--- a/hotspot/src/share/vm/runtime/sweeper.hpp
+++ b/hotspot/src/share/vm/runtime/sweeper.hpp
@@ -79,6 +79,7 @@
static const Tickspan peak_disconnect_time() { return _peak_disconnect_time; }
#ifdef ASSERT
+ static bool is_sweeping(nmethod* which) { return _current == which; }
// Keep track of sweeper activity in the ring buffer
static void record_sweep(nmethod* nm, int line);
static void report_events(int id, address entry);
diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp
index ae28b65..cfd4f16 100644
--- a/hotspot/src/share/vm/runtime/thread.cpp
+++ b/hotspot/src/share/vm/runtime/thread.cpp
@@ -3416,9 +3416,10 @@
}
assert (Universe::is_fully_initialized(), "not initialized");
- if (VerifyBeforeGC && VerifyGCStartAt == 0) {
- Universe::heap()->prepare_for_verify();
- Universe::verify(); // make sure we're starting with a clean slate
+ if (VerifyDuringStartup) {
+ // Make sure we're starting with a clean slate.
+ VM_Verify verify_op;
+ VMThread::execute(&verify_op);
}
EXCEPTION_MARK;
diff --git a/hotspot/src/share/vm/runtime/vmThread.cpp b/hotspot/src/share/vm/runtime/vmThread.cpp
index 7643670..5ddcf26 100644
--- a/hotspot/src/share/vm/runtime/vmThread.cpp
+++ b/hotspot/src/share/vm/runtime/vmThread.cpp
@@ -305,7 +305,7 @@
os::check_heap();
// Silent verification so as not to pollute normal output,
// unless we really asked for it.
- Universe::verify(!(PrintGCDetails || Verbose));
+ Universe::verify(!(PrintGCDetails || Verbose) || VerifySilently);
}
CompileBroker::set_should_block();
diff --git a/hotspot/src/share/vm/runtime/vm_operations.cpp b/hotspot/src/share/vm/runtime/vm_operations.cpp
index 63e5620..95cbd6a 100644
--- a/hotspot/src/share/vm/runtime/vm_operations.cpp
+++ b/hotspot/src/share/vm/runtime/vm_operations.cpp
@@ -189,7 +189,8 @@
}
void VM_Verify::doit() {
- Universe::verify();
+ Universe::heap()->prepare_for_verify();
+ Universe::verify(_silent);
}
bool VM_PrintThreads::doit_prologue() {
diff --git a/hotspot/src/share/vm/runtime/vm_operations.hpp b/hotspot/src/share/vm/runtime/vm_operations.hpp
index 285aa34..10b2b34 100644
--- a/hotspot/src/share/vm/runtime/vm_operations.hpp
+++ b/hotspot/src/share/vm/runtime/vm_operations.hpp
@@ -303,9 +303,9 @@
class VM_Verify: public VM_Operation {
private:
- KlassHandle _dependee;
+ bool _silent;
public:
- VM_Verify() {}
+ VM_Verify(bool silent = VerifySilently) : _silent(silent) {}
VMOp_Type type() const { return VMOp_Verify; }
void doit();
};
diff --git a/hotspot/src/share/vm/services/management.cpp b/hotspot/src/share/vm/services/management.cpp
index 6c5a325..81de4c0 100644
--- a/hotspot/src/share/vm/services/management.cpp
+++ b/hotspot/src/share/vm/services/management.cpp
@@ -851,8 +851,6 @@
total_used += u.used();
total_committed += u.committed();
- // if any one of the memory pool has undefined init_size or max_size,
- // set it to -1
if (u.init_size() == (size_t)-1) {
has_undefined_init_size = true;
}
@@ -869,6 +867,15 @@
}
}
+ // if any one of the memory pool has undefined init_size or max_size,
+ // set it to -1
+ if (has_undefined_init_size) {
+ total_init = (size_t)-1;
+ }
+ if (has_undefined_max_size) {
+ total_max = (size_t)-1;
+ }
+
// In our current implementation, we make sure that all non-heap
// pools have defined init and max sizes. Heap pools do not matter,
// as we never use total_init and total_max for them.
diff --git a/hotspot/src/share/vm/utilities/growableArray.hpp b/hotspot/src/share/vm/utilities/growableArray.hpp
index 2a6d6b8..ea92a80 100644
--- a/hotspot/src/share/vm/utilities/growableArray.hpp
+++ b/hotspot/src/share/vm/utilities/growableArray.hpp
@@ -194,6 +194,7 @@
void clear() { _len = 0; }
int length() const { return _len; }
+ int max_length() const { return _max; }
void trunc_to(int l) { assert(l <= _len,"cannot increase length"); _len = l; }
bool is_empty() const { return _len == 0; }
bool is_nonempty() const { return _len != 0; }
@@ -281,6 +282,13 @@
return -1;
}
+ int find_from_end(const E& elem) const {
+ for (int i = _len-1; i >= 0; i--) {
+ if (_data[i] == elem) return i;
+ }
+ return -1;
+ }
+
int find(void* token, bool f(void*, E)) const {
for (int i = 0; i < _len; i++) {
if (f(token, _data[i])) return i;
diff --git a/hotspot/test/gc/TestVerifyBeforeGCDuringStartup.java b/hotspot/test/gc/TestVerifyDuringStartup.java
similarity index 86%
rename from hotspot/test/gc/TestVerifyBeforeGCDuringStartup.java
rename to hotspot/test/gc/TestVerifyDuringStartup.java
index 109e45e..f0796f3 100644
--- a/hotspot/test/gc/TestVerifyBeforeGCDuringStartup.java
+++ b/hotspot/test/gc/TestVerifyDuringStartup.java
@@ -21,23 +21,23 @@
* questions.
*/
-/* @test TestVerifyBeforeGCDuringStartup.java
+/* @test TestVerifyDuringStartup.java
* @key gc
* @bug 8010463
- * @summary Simple test run with -XX:+VerifyBeforeGC -XX:-UseTLAB to verify 8010463
+ * @summary Simple test run with -XX:+VerifyDuringStartup -XX:-UseTLAB to verify 8010463
* @library /testlibrary
*/
import com.oracle.java.testlibrary.OutputAnalyzer;
import com.oracle.java.testlibrary.ProcessTools;
-public class TestVerifyBeforeGCDuringStartup {
+public class TestVerifyDuringStartup {
public static void main(String args[]) throws Exception {
ProcessBuilder pb =
ProcessTools.createJavaProcessBuilder(System.getProperty("test.vm.opts"),
"-XX:-UseTLAB",
"-XX:+UnlockDiagnosticVMOptions",
- "-XX:+VerifyBeforeGC", "-version");
+ "-XX:+VerifyDuringStartup", "-version");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldContain("[Verifying");
output.shouldHaveExitValue(0);
diff --git a/hotspot/test/gc/TestVerifySilently.java b/hotspot/test/gc/TestVerifySilently.java
new file mode 100644
index 0000000..deefd48
--- /dev/null
+++ b/hotspot/test/gc/TestVerifySilently.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test TestVerifySilently.java
+ * @key gc
+ * @bug 8032771
+ * @summary Test silent verification.
+ * @library /testlibrary
+ */
+
+import com.oracle.java.testlibrary.OutputAnalyzer;
+import com.oracle.java.testlibrary.ProcessTools;
+import java.util.ArrayList;
+import java.util.Collections;
+
+class RunSystemGC {
+ public static void main(String args[]) throws Exception {
+ System.gc();
+ }
+}
+
+
+public class TestVerifySilently {
+ private static String[] getTestJavaOpts() {
+ String testVmOptsStr = System.getProperty("test.java.opts");
+ if (!testVmOptsStr.isEmpty()) {
+ return testVmOptsStr.split(" ");
+ } else {
+ return new String[] {};
+ }
+ }
+
+ private static OutputAnalyzer runTest(boolean verifySilently) throws Exception {
+ ArrayList<String> vmOpts = new ArrayList();
+
+ Collections.addAll(vmOpts, getTestJavaOpts());
+ Collections.addAll(vmOpts, new String[] {"-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+VerifyDuringStartup",
+ "-XX:+VerifyBeforeGC",
+ "-XX:+VerifyAfterGC",
+ "-XX:" + (verifySilently ? "+":"-") + "VerifySilently",
+ RunSystemGC.class.getName()});
+ ProcessBuilder pb =
+ ProcessTools.createJavaProcessBuilder(vmOpts.toArray(new String[vmOpts.size()]));
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+ System.out.println("Output:\n" + output.getOutput());
+ return output;
+ }
+
+
+ public static void main(String args[]) throws Exception {
+
+ OutputAnalyzer output;
+
+ output = runTest(false);
+ output.shouldContain("[Verifying");
+ output.shouldHaveExitValue(0);
+
+ output = runTest(true);
+ output.shouldNotContain("[Verifying");
+ output.shouldHaveExitValue(0);
+ }
+}
diff --git a/hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java b/hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java
new file mode 100644
index 0000000..0d3ad50
--- /dev/null
+++ b/hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 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.
+ */
+
+/*
+ * @test
+ * @key regression
+ * @key gc
+ * @bug 8027756
+ * @library /testlibrary /testlibrary/whitebox
+ * @build TestHumongousCodeCacheRoots
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @summary Humongous objects may have references from the code cache
+ * @run main TestHumongousCodeCacheRoots
+*/
+
+import com.oracle.java.testlibrary.*;
+import sun.hotspot.WhiteBox;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+class TestHumongousCodeCacheRootsHelper {
+
+ static final int n = 1000000;
+ static final int[] AA = new int[n];
+ static final int[] BB = new int[n];
+
+ public static void main(String args[]) throws Exception {
+ // do some work so that the compiler compiles this method, inlining the
+ // reference to the integer array (which is a humonguous object) into
+ // the code cache.
+ for(int i = 0; i < n; i++) {
+ AA[i] = 0;
+ BB[i] = 0;
+ }
+ // trigger a GC that checks that the verification code allows humongous
+ // objects with code cache roots; objects should be all live here.
+ System.gc();
+
+ // deoptimize everyhing: this should make all compiled code zombies.
+ WhiteBox wb = WhiteBox.getWhiteBox();
+ wb.deoptimizeAll();
+
+ // trigger a GC that checks that the verification code allows humongous
+ // objects with code cache roots; objects should be all live here.
+ System.gc();
+
+ // wait a little for the code cache sweeper to try to clean up zombie nmethods
+ // and unregister the code roots.
+ try { Thread.sleep(5000); } catch (InterruptedException ex) { }
+
+ // do some work on the arrays to make sure that they need to be live after the GCs
+ for(int i = 0; i < n; i++) {
+ AA[i] = 1;
+ BB[i] = 10;
+ }
+
+ System.out.println();
+ }
+}
+
+public class TestHumongousCodeCacheRoots {
+
+ /**
+ * Executes a class in a new VM process with the given parameters.
+ * @param vmargs Arguments to the VM to run
+ * @param classname Name of the class to run
+ * @param arguments Arguments to the class
+ * @param useTestDotJavaDotOpts Use test.java.opts as part of the VM argument string
+ * @return The OutputAnalyzer with the results for the invocation.
+ */
+ public static OutputAnalyzer runWhiteBoxTest(String[] vmargs, String classname, String[] arguments, boolean useTestDotJavaDotOpts) throws Exception {
+ ArrayList<String> finalargs = new ArrayList<String>();
+
+ String[] whiteboxOpts = new String[] {
+ "-Xbootclasspath/a:.",
+ "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI",
+ "-cp", System.getProperty("java.class.path"),
+ };
+
+ if (useTestDotJavaDotOpts) {
+ // System.getProperty("test.java.opts") is '' if no options is set,
+ // we need to skip such a result
+ String[] externalVMOpts = new String[0];
+ if (System.getProperty("test.java.opts") != null && System.getProperty("test.java.opts").length() != 0) {
+ externalVMOpts = System.getProperty("test.java.opts").split(" ");
+ }
+ finalargs.addAll(Arrays.asList(externalVMOpts));
+ }
+
+ finalargs.addAll(Arrays.asList(vmargs));
+ finalargs.addAll(Arrays.asList(whiteboxOpts));
+ finalargs.add(classname);
+ finalargs.addAll(Arrays.asList(arguments));
+
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(finalargs.toArray(new String[0]));
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+ output.shouldHaveExitValue(0);
+
+ return output;
+ }
+
+ public static void runTest(String compiler, String[] other) throws Exception {
+ ArrayList<String> joined = new ArrayList<String>();
+ joined.add(compiler);
+ joined.addAll(Arrays.asList(other));
+ runWhiteBoxTest(joined.toArray(new String[0]), TestHumongousCodeCacheRootsHelper.class.getName(),
+ new String[] {}, false);
+ }
+
+ public static void main(String[] args) throws Exception {
+ final String[] baseArguments = new String[] {
+ "-XX:+UseG1GC", "-XX:G1HeapRegionSize=1M", "-Xmx100M", // make sure we get a humongous region
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:InitiatingHeapOccupancyPercent=1", // strong code root marking
+ "-XX:+G1VerifyHeapRegionCodeRoots", "-XX:+VerifyAfterGC", // make sure that verification is run
+ "-XX:NmethodSweepFraction=1", "-XX:NmethodSweepCheckInterval=1", // make the code cache sweep more predictable
+ };
+ runTest("-client", baseArguments);
+ runTest("-server", baseArguments);
+ }
+}
+
diff --git a/hotspot/test/gc/g1/TestPrintRegionRememberedSetInfo.java b/hotspot/test/gc/g1/TestPrintRegionRememberedSetInfo.java
new file mode 100644
index 0000000..6bf4139
--- /dev/null
+++ b/hotspot/test/gc/g1/TestPrintRegionRememberedSetInfo.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 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.
+ */
+
+/**
+ * @test TestPrintRegionRememberedSetInfo
+ * @key gc
+ * @bug 8014240
+ * @summary Test output of G1PrintRegionRememberedSetInfo
+ * @library /testlibrary
+ * @run main TestPrintRegionRememberedSetInfo
+ * @author thomas.schatzl@oracle.com
+ */
+
+import com.oracle.java.testlibrary.*;
+import java.lang.Thread;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+class RunAndWaitForMarking {
+ public static void main(String[] args) {
+ System.gc();
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ }
+ }
+}
+
+public class TestPrintRegionRememberedSetInfo {
+
+ public static String runTest(String arg) throws Exception {
+ ArrayList<String> finalargs = new ArrayList<String>();
+ String[] defaultArgs = new String[] {
+ "-XX:+UseG1GC",
+ "-Xmx10m",
+ "-XX:+ExplicitGCInvokesConcurrent",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+G1PrintRegionLivenessInfo",
+ "-XX:G1HeapRegionSize=1M",
+ "-XX:InitiatingHeapOccupancyPercent=0",
+ };
+
+ finalargs.addAll(Arrays.asList(defaultArgs));
+ finalargs.add(arg);
+
+ finalargs.add(RunAndWaitForMarking.class.getName());
+
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ finalargs.toArray(new String[0]));
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+ output.shouldHaveExitValue(0);
+
+ String result = output.getStdout();
+ return result;
+ }
+
+ public static void main(String[] args) throws Exception {
+ String result;
+
+ result = runTest("-XX:+G1PrintRegionLivenessInfo");
+ // check that we got region statistics output
+ if (result.indexOf("PHASE") == -1) {
+ throw new RuntimeException("Unexpected output from -XX:+PrintRegionLivenessInfo found.");
+ }
+
+ result = runTest("-XX:-G1PrintRegionLivenessInfo");
+ if (result.indexOf("remset") != -1) {
+ throw new RuntimeException("Should find remembered set information in output.");
+ }
+ }
+}
+
diff --git a/hotspot/test/gc/g1/TestSummarizeRSetStats.java b/hotspot/test/gc/g1/TestSummarizeRSetStats.java
new file mode 100644
index 0000000..e78e2df
--- /dev/null
+++ b/hotspot/test/gc/g1/TestSummarizeRSetStats.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 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.
+ */
+
+/*
+ * @test TestSummarizeRSetStats.java
+ * @bug 8013895
+ * @library /testlibrary
+ * @build TestSummarizeRSetStatsTools TestSummarizeRSetStats
+ * @summary Verify output of -XX:+G1SummarizeRSetStats
+ * @run main TestSummarizeRSetStats
+ *
+ * Test the output of G1SummarizeRSetStats in conjunction with G1SummarizeRSetStatsPeriod.
+ */
+
+public class TestSummarizeRSetStats {
+
+ public static void main(String[] args) throws Exception {
+ String result;
+
+ if (!TestSummarizeRSetStatsTools.testingG1GC()) {
+ return;
+ }
+
+ // no remembered set summary output
+ result = TestSummarizeRSetStatsTools.runTest(null, 0);
+ TestSummarizeRSetStatsTools.expectRSetSummaries(result, 0, 0);
+
+ // no remembered set summary output
+ result = TestSummarizeRSetStatsTools.runTest(null, 2);
+ TestSummarizeRSetStatsTools.expectRSetSummaries(result, 0, 0);
+
+ // no remembered set summary output
+ result = TestSummarizeRSetStatsTools.runTest(new String[] { "-XX:G1SummarizeRSetStatsPeriod=1" }, 3);
+ TestSummarizeRSetStatsTools.expectRSetSummaries(result, 0, 0);
+
+ // single remembered set summary output at the end
+ result = TestSummarizeRSetStatsTools.runTest(new String[] { "-XX:+G1SummarizeRSetStats" }, 0);
+ TestSummarizeRSetStatsTools.expectRSetSummaries(result, 1, 0);
+
+ // single remembered set summary output at the end
+ result = TestSummarizeRSetStatsTools.runTest(new String[] { "-XX:+G1SummarizeRSetStats" }, 2);
+ TestSummarizeRSetStatsTools.expectRSetSummaries(result, 1, 0);
+
+ // single remembered set summary output
+ result = TestSummarizeRSetStatsTools.runTest(new String[] { "-XX:+G1SummarizeRSetStats", "-XX:G1SummarizeRSetStatsPeriod=1" }, 0);
+ TestSummarizeRSetStatsTools.expectRSetSummaries(result, 1, 0);
+
+ // two times remembered set summary output
+ result = TestSummarizeRSetStatsTools.runTest(new String[] { "-XX:+G1SummarizeRSetStats", "-XX:G1SummarizeRSetStatsPeriod=1" }, 1);
+ TestSummarizeRSetStatsTools.expectRSetSummaries(result, 1, 2);
+
+ // four times remembered set summary output
+ result = TestSummarizeRSetStatsTools.runTest(new String[] { "-XX:+G1SummarizeRSetStats", "-XX:G1SummarizeRSetStatsPeriod=1" }, 3);
+ TestSummarizeRSetStatsTools.expectRSetSummaries(result, 1, 6);
+
+ // three times remembered set summary output
+ result = TestSummarizeRSetStatsTools.runTest(new String[] { "-XX:+G1SummarizeRSetStats", "-XX:G1SummarizeRSetStatsPeriod=2" }, 3);
+ TestSummarizeRSetStatsTools.expectRSetSummaries(result, 1, 4);
+
+ // single remembered set summary output
+ result = TestSummarizeRSetStatsTools.runTest(new String[] { "-XX:+G1SummarizeRSetStats", "-XX:G1SummarizeRSetStatsPeriod=100" }, 3);
+ TestSummarizeRSetStatsTools.expectRSetSummaries(result, 1, 2);
+ }
+}
+
diff --git a/hotspot/test/gc/g1/TestSummarizeRSetStatsPerRegion.java b/hotspot/test/gc/g1/TestSummarizeRSetStatsPerRegion.java
new file mode 100644
index 0000000..437cbc2
--- /dev/null
+++ b/hotspot/test/gc/g1/TestSummarizeRSetStatsPerRegion.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 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.
+ */
+
+/*
+ * @test TestSummarizeRSetStatsPerRegion.java
+ * @bug 8014078
+ * @library /testlibrary
+ * @build TestSummarizeRSetStatsTools TestSummarizeRSetStatsPerRegion
+ * @summary Verify output of -XX:+G1SummarizeRSetStats in regards to per-region type output
+ * @run main TestSummarizeRSetStatsPerRegion
+ */
+
+import com.oracle.java.testlibrary.*;
+import java.lang.Thread;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class TestSummarizeRSetStatsPerRegion {
+
+ public static void main(String[] args) throws Exception {
+ String result;
+
+ if (!TestSummarizeRSetStatsTools.testingG1GC()) {
+ return;
+ }
+
+ // single remembered set summary output at the end
+ result = TestSummarizeRSetStatsTools.runTest(new String[] { "-XX:+G1SummarizeRSetStats" }, 0);
+ TestSummarizeRSetStatsTools.expectPerRegionRSetSummaries(result, 1, 0);
+
+ // two times remembered set summary output
+ result = TestSummarizeRSetStatsTools.runTest(new String[] { "-XX:+G1SummarizeRSetStats", "-XX:G1SummarizeRSetStatsPeriod=1" }, 1);
+ TestSummarizeRSetStatsTools.expectPerRegionRSetSummaries(result, 1, 2);
+ }
+}
diff --git a/hotspot/test/gc/g1/TestSummarizeRSetStatsThreads.java b/hotspot/test/gc/g1/TestSummarizeRSetStatsThreads.java
new file mode 100644
index 0000000..99014e0
--- /dev/null
+++ b/hotspot/test/gc/g1/TestSummarizeRSetStatsThreads.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 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.
+ */
+
+/*
+ * @test TestSummarizeRSetStatsThreads
+ * @bug 8025441
+ * @summary Ensure that various values of worker threads/concurrent
+ * refinement threads do not crash the VM.
+ * @key gc
+ * @library /testlibrary
+ */
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.oracle.java.testlibrary.ProcessTools;
+import com.oracle.java.testlibrary.OutputAnalyzer;
+
+public class TestSummarizeRSetStatsThreads {
+
+ private static void runTest(int refinementThreads, int workerThreads) throws Exception {
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+G1SummarizeRSetStats",
+ "-XX:G1ConcRefinementThreads=" + refinementThreads,
+ "-XX:ParallelGCThreads=" + workerThreads,
+ "-version");
+
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+ // check output to contain the string "Concurrent RS threads times (s)" followed by
+ // the correct number of values in the next line.
+
+ // a zero in refinement thread numbers indicates that the value in ParallelGCThreads should be used.
+ // Additionally use at least one thread.
+ int expectedNumRefinementThreads = refinementThreads == 0 ? workerThreads : refinementThreads;
+ expectedNumRefinementThreads = Math.max(1, expectedNumRefinementThreads);
+ // create the pattern made up of n copies of a floating point number pattern
+ String numberPattern = String.format("%0" + expectedNumRefinementThreads + "d", 0)
+ .replace("0", "\\s+\\d+\\.\\d+");
+ String pattern = "Concurrent RS threads times \\(s\\)$" + numberPattern + "$";
+ Matcher m = Pattern.compile(pattern, Pattern.MULTILINE).matcher(output.getStdout());
+
+ if (!m.find()) {
+ throw new Exception("Could not find correct output for concurrent RS threads times in stdout," +
+ " should match the pattern \"" + pattern + "\", but stdout is \n" + output.getStdout());
+ }
+ output.shouldHaveExitValue(0);
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (!TestSummarizeRSetStatsTools.testingG1GC()) {
+ return;
+ }
+ // different valid combinations of number of refinement and gc worker threads
+ runTest(0, 0);
+ runTest(0, 5);
+ runTest(5, 0);
+ runTest(10, 10);
+ runTest(1, 2);
+ runTest(4, 3);
+ }
+}
diff --git a/hotspot/test/gc/g1/TestSummarizeRSetStatsTools.java b/hotspot/test/gc/g1/TestSummarizeRSetStatsTools.java
new file mode 100644
index 0000000..096a7c6
--- /dev/null
+++ b/hotspot/test/gc/g1/TestSummarizeRSetStatsTools.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 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.
+ */
+
+/*
+ * Common helpers for TestSummarizeRSetStats* tests
+ */
+
+import sun.management.ManagementFactoryHelper;
+import com.sun.management.HotSpotDiagnosticMXBean;
+import com.sun.management.VMOption;
+
+import com.oracle.java.testlibrary.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.lang.Thread;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+class VerifySummaryOutput {
+ // 4M size, both are directly allocated into the old gen
+ static Object[] largeObject1 = new Object[1024 * 1024];
+ static Object[] largeObject2 = new Object[1024 * 1024];
+
+ static int[] temp;
+
+ public static void main(String[] args) {
+ // create some cross-references between these objects
+ for (int i = 0; i < largeObject1.length; i++) {
+ largeObject1[i] = largeObject2;
+ }
+
+ for (int i = 0; i < largeObject2.length; i++) {
+ largeObject2[i] = largeObject1;
+ }
+
+ int numGCs = Integer.parseInt(args[0]);
+
+ if (numGCs > 0) {
+ // try to force a minor collection: the young gen is 4M, the
+ // amount of data allocated below is roughly that (4*1024*1024 +
+ // some header data)
+ for (int i = 0; i < 1024 ; i++) {
+ temp = new int[1024];
+ }
+ }
+
+ for (int i = 0; i < numGCs - 1; i++) {
+ System.gc();
+ }
+ }
+}
+
+public class TestSummarizeRSetStatsTools {
+
+ // the VM is currently run using G1GC, i.e. trying to test G1 functionality.
+ public static boolean testingG1GC() {
+ HotSpotDiagnosticMXBean diagnostic = ManagementFactoryHelper.getDiagnosticMXBean();
+
+ VMOption option = diagnostic.getVMOption("UseG1GC");
+ if (option.getValue().equals("false")) {
+ System.out.println("Skipping this test. It is only a G1 test.");
+ return false;
+ }
+ return true;
+ }
+
+ public static String runTest(String[] additionalArgs, int numGCs) throws Exception {
+ ArrayList<String> finalargs = new ArrayList<String>();
+ String[] defaultArgs = new String[] {
+ "-XX:+UseG1GC",
+ "-XX:+UseCompressedOops",
+ "-Xmn4m",
+ "-Xmx20m",
+ "-XX:InitiatingHeapOccupancyPercent=100", // we don't want the additional GCs due to initial marking
+ "-XX:+PrintGC",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:G1HeapRegionSize=1M",
+ };
+
+ finalargs.addAll(Arrays.asList(defaultArgs));
+
+ if (additionalArgs != null) {
+ finalargs.addAll(Arrays.asList(additionalArgs));
+ }
+
+ finalargs.add(VerifySummaryOutput.class.getName());
+ finalargs.add(String.valueOf(numGCs));
+
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ finalargs.toArray(new String[0]));
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+ output.shouldHaveExitValue(0);
+
+ String result = output.getStdout();
+ return result;
+ }
+
+ private static void checkCounts(int expected, int actual, String which) throws Exception {
+ if (expected != actual) {
+ throw new Exception("RSet summaries mention " + which + " regions an incorrect number of times. Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void expectPerRegionRSetSummaries(String result, int expectedCumulative, int expectedPeriodic) throws Exception {
+ expectRSetSummaries(result, expectedCumulative, expectedPeriodic);
+ int actualYoung = result.split("Young regions").length - 1;
+ int actualHumonguous = result.split("Humonguous regions").length - 1;
+ int actualFree = result.split("Free regions").length - 1;
+ int actualOther = result.split("Old regions").length - 1;
+
+ // the strings we check for above are printed four times per summary
+ int expectedPerRegionTypeInfo = (expectedCumulative + expectedPeriodic) * 4;
+
+ checkCounts(expectedPerRegionTypeInfo, actualYoung, "Young");
+ checkCounts(expectedPerRegionTypeInfo, actualHumonguous, "Humonguous");
+ checkCounts(expectedPerRegionTypeInfo, actualFree, "Free");
+ checkCounts(expectedPerRegionTypeInfo, actualOther, "Old");
+ }
+
+ public static void expectRSetSummaries(String result, int expectedCumulative, int expectedPeriodic) throws Exception {
+ int actualTotal = result.split("concurrent refinement").length - 1;
+ int actualCumulative = result.split("Cumulative RS summary").length - 1;
+
+ if (expectedCumulative != actualCumulative) {
+ throw new Exception("Incorrect amount of RSet summaries at the end. Expected " + expectedCumulative + ", got " + actualCumulative);
+ }
+
+ if (expectedPeriodic != (actualTotal - actualCumulative)) {
+ throw new Exception("Incorrect amount of per-period RSet summaries at the end. Expected " + expectedPeriodic + ", got " + (actualTotal - actualCumulative));
+ }
+ }
+}
+
diff --git a/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java b/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java
index 0fd398a..d27d356 100644
--- a/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java
+++ b/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java
@@ -78,4 +78,7 @@
public native void NMTUncommitMemory(long addr, long size);
public native void NMTReleaseMemory(long addr, long size);
public native boolean NMTWaitForDataMerge();
+
+ // Compiler
+ public native void deoptimizeAll();
}
diff --git a/jaxp/.hgtags b/jaxp/.hgtags
index a1b40a7..dc43f96 100644
--- a/jaxp/.hgtags
+++ b/jaxp/.hgtags
@@ -404,3 +404,4 @@
ad39e88c503948fc4fc01e97c75b6e3c24599d23 jdk7u60-b01
050986fd54e3ec4515032ee938bc59e86772b6c0 jdk7u60-b02
359b79d99538d17eeb90927a1e4883fcec31661f jdk7u60-b03
+7215972c2c30d0fa469a459a3e4fcee6bc93991d jdk7u60-b04
diff --git a/jaxws/.hgtags b/jaxws/.hgtags
index fc29514..d10e4f0 100644
--- a/jaxws/.hgtags
+++ b/jaxws/.hgtags
@@ -403,3 +403,4 @@
f675dfce1e61a6ed01732ae7cfbae941791cba74 jdk7u60-b01
8a3b9e8492a5ac4e2e0c166dbfc5d058be244377 jdk7u60-b02
d4ba4e1ed3ecdef1ef7c3b7aaf62ff69fc105cb2 jdk7u60-b03
+bef313c7ff7a7a829f8f6a305bf0c3738ad99795 jdk7u60-b04
diff --git a/jdk/.hgtags b/jdk/.hgtags
index 97464a6..597b23f 100644
--- a/jdk/.hgtags
+++ b/jdk/.hgtags
@@ -387,3 +387,4 @@
def34c4a798678c424786a8f0d0508e90185958d jdk7u60-b01
ff67c89658525e8903fb870861ed3645befd6bc5 jdk7u60-b02
b1bcc999a8f1b4b4452b59c6636153bb0154cf5a jdk7u60-b03
+efc8886310cbccb941f826acfad2ad51a2891be5 jdk7u60-b04
diff --git a/jdk/src/share/classes/java/util/jar/JarFile.java b/jdk/src/share/classes/java/util/jar/JarFile.java
index e975aaa..4dc9bae 100644
--- a/jdk/src/share/classes/java/util/jar/JarFile.java
+++ b/jdk/src/share/classes/java/util/jar/JarFile.java
@@ -38,6 +38,7 @@
import sun.security.action.GetPropertyAction;
import sun.security.util.ManifestEntryVerifier;
import sun.misc.SharedSecrets;
+import sun.security.util.SignatureFileVerifier;
/**
* The <code>JarFile</code> class is used to read the contents of a jar file
@@ -329,11 +330,13 @@
String[] names = getMetaInfEntryNames();
if (names != null) {
for (int i = 0; i < names.length; i++) {
- JarEntry e = getJarEntry(names[i]);
- if (e == null) {
- throw new JarException("corrupted jar file");
- }
- if (!e.isDirectory()) {
+ String uname = names[i].toUpperCase(Locale.ENGLISH);
+ if (MANIFEST_NAME.equals(uname)
+ || SignatureFileVerifier.isBlockOrSF(uname)) {
+ JarEntry e = getJarEntry(names[i]);
+ if (e == null) {
+ throw new JarException("corrupted jar file");
+ }
if (mev == null) {
mev = new ManifestEntryVerifier
(getManifestFromReference());
diff --git a/jdk/src/share/classes/java/util/logging/LogManager.java b/jdk/src/share/classes/java/util/logging/LogManager.java
index c684562..f3ab5d0 100644
--- a/jdk/src/share/classes/java/util/logging/LogManager.java
+++ b/jdk/src/share/classes/java/util/logging/LogManager.java
@@ -149,7 +149,15 @@
// The global LogManager object
private static LogManager manager;
- private Properties props = new Properties();
+ // 'props' is assigned within a lock but accessed without it.
+ // Declaring it volatile makes sure that another thread will not
+ // be able to see a partially constructed 'props' object.
+ // (seeing a partially constructed 'props' object can result in
+ // NPE being thrown in Hashtable.get(), because it leaves the door
+ // open for props.getProperties() to be called before the construcor
+ // of Hashtable is actually completed).
+ private volatile Properties props = new Properties();
+
private PropertyChangeSupport changes
= new PropertyChangeSupport(LogManager.class);
private final static Level defaultLevel = Level.INFO;
@@ -540,7 +548,7 @@
if (logger == null) {
// Hashtable holds stale weak reference
// to a logger which has been GC-ed.
- removeLogger(name);
+ ref.dispose();
}
return logger;
}
@@ -627,7 +635,7 @@
// It's possible that the Logger was GC'ed after a
// drainLoggerRefQueueBounded() call so allow
// a new one to be registered.
- removeLogger(name);
+ ref.dispose();
} else {
// We already have a registered logger with the given name.
return false;
@@ -673,10 +681,10 @@
return true;
}
- // note: all calls to removeLogger are synchronized on LogManager's
- // intrinsic lock
- void removeLogger(String name) {
- namedLoggers.remove(name);
+ synchronized void removeLoggerRef(String name, LoggerWeakRef ref) {
+ if (namedLoggers.get(name) == ref) {
+ namedLoggers.remove(name);
+ }
}
synchronized Enumeration<String> getLoggerNames() {
@@ -854,6 +862,7 @@
private String name; // for namedLoggers cleanup
private LogNode node; // for loggerRef cleanup
private WeakReference<Logger> parentRef; // for kids cleanup
+ private boolean disposed = false; // avoid calling dispose twice
LoggerWeakRef(Logger logger) {
super(logger, loggerRefQueue);
@@ -863,14 +872,45 @@
// dispose of this LoggerWeakRef object
void dispose() {
- if (node != null) {
- // if we have a LogNode, then we were a named Logger
- // so clear namedLoggers weak ref to us
- node.context.removeLogger(name);
- name = null; // clear our ref to the Logger's name
+ // Avoid calling dispose twice. When a Logger is gc'ed, its
+ // LoggerWeakRef will be enqueued.
+ // However, a new logger of the same name may be added (or looked
+ // up) before the queue is drained. When that happens, dispose()
+ // will be called by addLocalLogger() or findLogger().
+ // Later when the queue is drained, dispose() will be called again
+ // for the same LoggerWeakRef. Marking LoggerWeakRef as disposed
+ // avoids processing the data twice (even though the code should
+ // now be reentrant).
+ synchronized(this) {
+ // Note to maintainers:
+ // Be careful not to call any method that tries to acquire
+ // another lock from within this block - as this would surely
+ // lead to deadlocks, given that dispose() can be called by
+ // multiple threads, and from within different synchronized
+ // methods/blocks.
+ if (disposed) return;
+ disposed = true;
+ }
- node.loggerRef = null; // clear LogNode's weak ref to us
- node = null; // clear our ref to LogNode
+ final LogNode n = node;
+ if (n != null) {
+ // n.loggerRef can only be safely modified from within
+ // a lock on LoggerContext. removeLoggerRef is already
+ // synchronized on LoggerContext so calling
+ // n.context.removeLoggerRef from within this lock is safe.
+ synchronized (n.context) {
+ // if we have a LogNode, then we were a named Logger
+ // so clear namedLoggers weak ref to us
+ n.context.removeLoggerRef(name, this);
+ name = null; // clear our ref to the Logger's name
+
+ // LogNode may have been reused - so only clear
+ // LogNode.loggerRef if LogNode.loggerRef == this
+ if (n.loggerRef == this) {
+ n.loggerRef = null; // clear LogNode's weak ref to us
+ }
+ node = null; // clear our ref to LogNode
+ }
}
if (parentRef != null) {
@@ -923,7 +963,7 @@
// - maximum: 10.9 ms
//
private final static int MAX_ITERATIONS = 400;
- final synchronized void drainLoggerRefQueueBounded() {
+ final void drainLoggerRefQueueBounded() {
for (int i = 0; i < MAX_ITERATIONS; i++) {
if (loggerRefQueue == null) {
// haven't finished loading LogManager yet
diff --git a/jdk/src/share/classes/java/util/logging/Logger.java b/jdk/src/share/classes/java/util/logging/Logger.java
index d892482..8b087a0 100644
--- a/jdk/src/share/classes/java/util/logging/Logger.java
+++ b/jdk/src/share/classes/java/util/logging/Logger.java
@@ -174,11 +174,11 @@
public class Logger {
private static final Handler emptyHandlers[] = new Handler[0];
private static final int offValue = Level.OFF.intValue();
- private LogManager manager;
+ private volatile LogManager manager;
private String name;
private final CopyOnWriteArrayList<Handler> handlers =
new CopyOnWriteArrayList<>();
- private String resourceBundleName;
+ private volatile String resourceBundleName;
private volatile boolean useParentHandlers = true;
private volatile Filter filter;
private boolean anonymous;
diff --git a/jdk/src/windows/classes/java/net/DualStackPlainSocketImpl.java b/jdk/src/windows/classes/java/net/DualStackPlainSocketImpl.java
index 4f1aae0..12d701f 100644
--- a/jdk/src/windows/classes/java/net/DualStackPlainSocketImpl.java
+++ b/jdk/src/windows/classes/java/net/DualStackPlainSocketImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -162,8 +162,9 @@
if (!fd.valid())
return;
- close0(fdAccess.get(fd));
+ final int nativefd = fdAccess.get(fd);
fdAccess.set(fd, -1);
+ close0(nativefd);
}
void socketShutdown(int howto) throws IOException {
diff --git a/jdk/src/windows/native/java/lang/java_props_md.c b/jdk/src/windows/native/java/lang/java_props_md.c
index 6380351..858b1f3 100644
--- a/jdk/src/windows/native/java/lang/java_props_md.c
+++ b/jdk/src/windows/native/java/lang/java_props_md.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -448,6 +448,7 @@
case 0: sprops.os_name = "Windows Vista"; break;
case 1: sprops.os_name = "Windows 7"; break;
case 2: sprops.os_name = "Windows 8"; break;
+ case 3: sprops.os_name = "Windows 8.1"; break;
default: sprops.os_name = "Windows NT (unknown)";
}
} else {
@@ -455,6 +456,7 @@
case 0: sprops.os_name = "Windows Server 2008"; break;
case 1: sprops.os_name = "Windows Server 2008 R2"; break;
case 2: sprops.os_name = "Windows Server 2012"; break;
+ case 3: sprops.os_name = "Windows Server 2012 R2"; break;
default: sprops.os_name = "Windows NT (unknown)";
}
}
diff --git a/jdk/src/windows/native/java/net/SocketInputStream.c b/jdk/src/windows/native/java/net/SocketInputStream.c
index e7bb043..105f218 100644
--- a/jdk/src/windows/native/java/net/SocketInputStream.c
+++ b/jdk/src/windows/native/java/net/SocketInputStream.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -134,32 +134,34 @@
(*env)->SetByteArrayRegion(env, data, off, nread, (jbyte *)bufP);
} else {
if (nread < 0) {
- /*
- * Recv failed.
- */
- switch (WSAGetLastError()) {
- case WSAEINTR:
- JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
- "socket closed");
- break;
+ // Check if the socket has been closed since we last checked.
+ // This could be a reason for recv failing.
+ if ((*env)->GetIntField(env, fdObj, IO_fd_fdID) == -1) {
+ NET_ThrowSocketException(env, "Socket closed");
+ } else {
+ switch (WSAGetLastError()) {
+ case WSAEINTR:
+ JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
+ "socket closed");
+ break;
+ case WSAECONNRESET:
+ case WSAESHUTDOWN:
+ /*
+ * Connection has been reset - Windows sometimes reports
+ * the reset as a shutdown error.
+ */
+ JNU_ThrowByName(env, "sun/net/ConnectionResetException",
+ "");
+ break;
- case WSAECONNRESET:
- case WSAESHUTDOWN:
- /*
- * Connection has been reset - Windows sometimes reports
- * the reset as a shutdown error.
- */
- JNU_ThrowByName(env, "sun/net/ConnectionResetException",
- "");
- break;
+ case WSAETIMEDOUT :
+ JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
+ "Read timed out");
+ break;
- case WSAETIMEDOUT :
- JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
- "Read timed out");
- break;
-
- default:
- NET_ThrowCurrent(env, "recv failed");
+ default:
+ NET_ThrowCurrent(env, "recv failed");
+ }
}
}
}
diff --git a/jdk/src/windows/resource/java.manifest b/jdk/src/windows/resource/java.manifest
index 24b19c0..34024eb 100644
--- a/jdk/src/windows/resource/java.manifest
+++ b/jdk/src/windows/resource/java.manifest
@@ -44,9 +44,15 @@
<!-- Indicate this JDK version is Windows 7 compatible -->
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
- <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
</application>
- </compatibility>
+ </compatibility>
</assembly>
diff --git a/jdk/test/java/lang/ProcessBuilder/Basic.java b/jdk/test/java/lang/ProcessBuilder/Basic.java
index a8efdd1..e54545f 100644
--- a/jdk/test/java/lang/ProcessBuilder/Basic.java
+++ b/jdk/test/java/lang/ProcessBuilder/Basic.java
@@ -559,9 +559,10 @@
System.getProperty("java.class.path");
private static final List<String> javaChildArgs =
- Arrays.asList(new String[]
- { javaExe, "-classpath", absolutifyPath(classpath),
- "Basic$JavaChild"});
+ Arrays.asList(javaExe,
+ "-XX:+DisplayVMOutputToStderr",
+ "-classpath", absolutifyPath(classpath),
+ "Basic$JavaChild");
private static void testEncoding(String encoding, String tested) {
try {
@@ -1597,8 +1598,8 @@
javaExe));
list.add("ArrayOOME");
ProcessResults r = run(new ProcessBuilder(list));
- check(r.out().contains("java.lang.OutOfMemoryError:"));
- check(r.out().contains(javaExe));
+ check(r.err().contains("java.lang.OutOfMemoryError:"));
+ check(r.err().contains(javaExe));
check(r.err().contains(System.getProperty("java.version")));
equal(r.exitValue(), 1);
} catch (Throwable t) { unexpected(t); }
diff --git a/jdk/test/java/net/Authenticator/B4769350.java b/jdk/test/java/net/Authenticator/B4769350.java
index 86bff12..97c6752 100644
--- a/jdk/test/java/net/Authenticator/B4769350.java
+++ b/jdk/test/java/net/Authenticator/B4769350.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,9 +23,7 @@
/**
* @test
- * @bug 4769350
- * @library ../../../sun/net/www/httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction AbstractCallback
+ * @bug 4769350 8017779
* @run main/othervm B4769350 server
* @run main/othervm B4769350 proxy
* @summary proxy authentication username and password caching only works in serial case
@@ -34,8 +32,17 @@
* tests may already have invoked the HTTP handler.
*/
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
import java.io.*;
import java.net.*;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
public class B4769350 {
@@ -43,13 +50,12 @@
static boolean error = false;
static void read (InputStream is) throws IOException {
- int c;
- while ((c=is.read()) != -1) {
+ while (is.read() != -1) {
//System.out.write (c);
}
}
- static class Client extends Thread {
+ static class Client extends Thread {
String authority, path;
boolean allowerror;
@@ -64,8 +70,8 @@
try {
URI u = new URI ("http", authority, path, null, null);
URL url = u.toURL();
- URLConnection urlc = url.openConnection ();
- InputStream is = urlc.getInputStream ();
+ URLConnection urlc = url.openConnection();
+ InputStream is = urlc.getInputStream();
read (is);
is.close();
} catch (URISyntaxException e) {
@@ -73,7 +79,8 @@
error = true;
} catch (IOException e) {
if (!allowerror) {
- System.out.println (Thread.currentThread().getName() + " " + e);
+ System.out.println (Thread.currentThread().getName()
+ + " " + e);
e.printStackTrace();
error = true;
}
@@ -81,55 +88,58 @@
}
}
- static class CallBack extends AbstractCallback {
+ class Server implements AutoCloseable {
+ HttpServer server;
+ Executor executor;
+ CyclicBarrier t1Cond1;
+ CyclicBarrier t1Cond2;
- void errorReply (HttpTransaction req, String reply) throws IOException {
- req.addResponseHeader ("Connection", "close");
- req.addResponseHeader ("WWW-Authenticate", reply);
- req.sendResponse (401, "Unauthorized");
- req.orderlyClose();
+ public String getAddress() {
+ return server.getAddress().getHostName();
}
- void proxyReply (HttpTransaction req, String reply) throws IOException {
- req.addResponseHeader ("Proxy-Authenticate", reply);
- req.sendResponse (407, "Proxy Authentication Required");
- }
+ public void startServer() {
+ InetSocketAddress addr = new InetSocketAddress(0);
- void okReply (HttpTransaction req) throws IOException {
- req.addResponseHeader ("Connection", "close");
- req.setResponseEntityBody ("Hello .");
- req.sendResponse (200, "Ok");
- req.orderlyClose();
- }
-
- public void request (HttpTransaction req, int count) {
try {
- URI uri = req.getRequestURI();
- String path = uri.getPath();
- if (path.endsWith ("/t1a")) {
- doT1a (req, count);
- } else if (path.endsWith ("/t1b")) {
- doT1b (req, count);
- } else if (path.endsWith ("/t1c")) {
- doT1c (req, count);
- } else if (path.endsWith ("/t1d")) {
- doT1d (req, count);
- } else if (path.endsWith ("/t2a")) {
- doT2a (req, count);
- } else if (path.endsWith ("/t2b")) {
- doT2b (req, count);
- } else if (path.endsWith ("/t3a")) {
- doT3a (req, count);
- } else if (path.endsWith ("/t3b")) {
- doT3bc (req, count);
- } else if (path.endsWith ("/t3c")) {
- doT3bc (req, count);
- } else {
- System.out.println ("unexpected request URI");
- }
- } catch (IOException e) {
- e.printStackTrace();
+ server = HttpServer.create(addr, 0);
+ } catch (IOException ioe) {
+ throw new RuntimeException("Server could not be created");
}
+ executor = Executors.newFixedThreadPool(10);
+ server.setExecutor(executor);
+ server.createContext("/test/realm1/t1a",
+ new AuthenticationHandlerT1a() );
+ server.createContext("/test/realm2/t1b",
+ new AuthenticationHandlerT1b());
+ server.createContext("/test/realm1/t1c",
+ new AuthenticationHandlerT1c());
+ server.createContext("/test/realm2/t1d",
+ new AuthenticationHandlerT1d());
+ server.createContext("/test/realm3/t2a",
+ new AuthenticationHandlerT2a());
+ server.createContext("/test/realm3/t2b",
+ new AuthenticationHandlerT2b());
+ server.createContext("/test/realm4/t3a",
+ new AuthenticationHandlerT3a());
+ server.createContext("/test/realm4/t3b",
+ new AuthenticationHandlerT3bc());
+ server.createContext("/test/realm4/t3c",
+ new AuthenticationHandlerT3bc());
+ t1Cond1 = new CyclicBarrier(2);
+ t1Cond2 = new CyclicBarrier(2);
+ server.start();
+ }
+
+ public int getPort() {
+ return server.getAddress().getPort();
+ }
+
+ public void close() {
+ if (executor != null)
+ ((ExecutorService)executor).shutdownNow();
+ if (server != null)
+ server.stop(0);
}
/* T1 tests the client by sending 4 requests to 2 different realms
@@ -138,90 +148,158 @@
* the second requests should be executed without calling the authenticator.
* The test succeeds if the authenticator was only called twice.
*/
- void doT1a (HttpTransaction req, int count) throws IOException {
- switch (count) {
- case 0:
- errorReply (req, "Basic realm=\"realm1\"");
- HttpServer.rendezvous ("one", 2);
- break;
- case 1:
- HttpServer.waitForCondition ("cond2");
- okReply (req);
- break;
- default:
- System.out.println ("Unexpected request");
+ class AuthenticationHandlerT1a implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ try {
+ switch(count) {
+ case 0:
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm1\"");
+ break;
+ case 1:
+ t1Cond1.await();
+ t1cond2latch.await();
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
+ } catch (InterruptedException |
+ BrokenBarrierException e)
+ {
+ throw new RuntimeException(e);
+ }
}
}
+ class AuthenticationHandlerT1b implements HttpHandler
+ {
+ volatile int count = -1;
- void doT1b (HttpTransaction req, int count) throws IOException {
- switch (count) {
- case 0:
- errorReply (req, "Basic realm=\"realm2\"");
- HttpServer.rendezvous ("one", 2);
- HttpServer.setCondition ("cond1");
- break;
- case 1:
- HttpServer.waitForCondition ("cond2");
- okReply (req);
- break;
- default:
- System.out.println ("Unexpected request");
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ try {
+ switch(count) {
+ case 0:
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm2\"");
+ break;
+ case 1:
+ t1Cond1.await();
+ t1cond1latch.countDown();
+ t1cond2latch.await();
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
+ } catch (InterruptedException | BrokenBarrierException e) {
+ throw new RuntimeException(e);
+ }
}
}
- void doT1c (HttpTransaction req, int count) throws IOException {
- switch (count) {
- case 0:
- errorReply (req, "Basic realm=\"realm1\"");
- HttpServer.rendezvous ("two", 2);
- break;
- case 1:
- okReply (req);
- break;
- default:
- System.out.println ("Unexpected request");
+ class AuthenticationHandlerT1c implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ switch(count) {
+ case 0:
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm1\"");
+ try {
+ t1Cond2.await();
+ } catch (InterruptedException |
+ BrokenBarrierException e)
+ {
+ throw new RuntimeException(e);
+ }
+ break;
+ case 1:
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
}
}
- void doT1d (HttpTransaction req, int count) throws IOException {
- switch (count) {
- case 0:
- errorReply (req, "Basic realm=\"realm2\"");
- HttpServer.rendezvous ("two", 2);
- HttpServer.setCondition ("cond2");
- break;
- case 1:
- okReply (req);
- break;
- default:
- System.out.println ("Unexpected request");
+ class AuthenticationHandlerT1d implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ switch(count) {
+ case 0:
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm2\"");
+ try {
+ t1Cond2.await();
+ } catch (InterruptedException |
+ BrokenBarrierException e)
+ {
+ throw new RuntimeException(e);
+ }
+ t1cond2latch.countDown();
+ break;
+ case 1:
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
}
}
-
/* T2 tests to check that if initial authentication fails, the second will
* succeed, and the authenticator is called twice
*/
- void doT2a (HttpTransaction req, int count) throws IOException {
- /* This will be called several times */
- if (count == 1) {
- HttpServer.setCondition ("T2cond1");
+ class AuthenticationHandlerT2a implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ if (count == 1) {
+ t2condlatch.countDown();
+ }
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm3\"");
+
}
- errorReply (req, "Basic realm=\"realm3\"");
}
- void doT2b (HttpTransaction req, int count) throws IOException {
- switch (count) {
- case 0:
- errorReply (req, "Basic realm=\"realm3\"");
- break;
- case 1:
- okReply (req);
- break;
- default:
- System.out.println ("Unexpected request");
+ class AuthenticationHandlerT2b implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ switch(count) {
+ case 0:
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm3\"");
+ break;
+ case 1:
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
}
}
@@ -229,36 +307,82 @@
* resource at same time. Authenticator should be called once for server
* and once for proxy
*/
- void doT3a (HttpTransaction req, int count) throws IOException {
- switch (count) {
- case 0:
- proxyReply (req, "Basic realm=\"proxy\"");
- HttpServer.setCondition ("T3cond1");
- break;
- case 1:
- errorReply (req, "Basic realm=\"realm4\"");
- break;
- case 2:
- okReply (req);
- break;
- default:
- System.out.println ("Unexpected request");
+
+ class AuthenticationHandlerT3a implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ switch(count) {
+ case 0:
+ AuthenticationHandler.proxyReply(exchange,
+ "Basic realm=\"proxy\"");
+ break;
+ case 1:
+ t3cond1.countDown();
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm4\"");
+ break;
+ case 2:
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
}
}
- void doT3bc (HttpTransaction req, int count) throws IOException {
- switch (count) {
- case 0:
- proxyReply (req, "Basic realm=\"proxy\"");
- break;
- case 1:
- okReply (req);
- break;
- default:
- System.out.println ("Unexpected request");
+ class AuthenticationHandlerT3bc implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ switch(count) {
+ case 0:
+ AuthenticationHandler.proxyReply(exchange,
+ "Basic realm=\"proxy\"");
+ break;
+ case 1:
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
}
}
- };
+ }
+
+ static class AuthenticationHandler {
+ static void errorReply(HttpExchange exchange, String reply)
+ throws IOException
+ {
+ exchange.getResponseHeaders().add("Connection", "close");
+ exchange.getResponseHeaders().add("WWW-Authenticate", reply);
+ exchange.sendResponseHeaders(401, 0);
+ exchange.close();
+ }
+
+ static void proxyReply (HttpExchange exchange, String reply)
+ throws IOException
+ {
+ exchange.getResponseHeaders().add("Proxy-Authenticate", reply);
+ exchange.sendResponseHeaders(407, 0);
+ }
+
+ static void okReply (HttpExchange exchange) throws IOException {
+ exchange.getResponseHeaders().add("Connection", "close");
+ String response = "Hello .";
+ exchange.sendResponseHeaders(200, response.getBytes().length);
+ OutputStream os = exchange.getResponseBody();
+ os.write(response.getBytes());
+ os.close();
+ exchange.close();
+ }
+ }
static HttpServer server;
static MyAuthenticator auth = new MyAuthenticator ();
@@ -267,7 +391,14 @@
static Client c1,c2,c3,c4,c5,c6,c7,c8,c9;
- static void doServerTests (String authority) throws Exception {
+ static CountDownLatch t1cond1latch;
+ static CountDownLatch t1cond2latch;
+ static CountDownLatch t2condlatch;
+ static CountDownLatch t3cond1;
+
+ static void doServerTests (String authority, Server server) throws Exception
+ {
+
System.out.println ("Doing Server tests");
System.out.println ("T1");
c1 = new Client (authority, "/test/realm1/t1a", false);
@@ -275,17 +406,20 @@
c3 = new Client (authority, "/test/realm1/t1c", false);
c4 = new Client (authority, "/test/realm2/t1d", false);
+ t1cond1latch = new CountDownLatch(1);
+ t1cond2latch = new CountDownLatch(1);
c1.start(); c2.start();
- HttpServer.waitForCondition ("cond1");
+ t1cond1latch.await();
c3.start(); c4.start();
c1.join(); c2.join(); c3.join(); c4.join();
int f = auth.getCount();
if (f != 2) {
- except ("Authenticator was called "+f+" times. Should be 2");
+ except ("Authenticator was called "+f+" times. Should be 2",
+ server);
}
if (error) {
- except ("error occurred");
+ except ("error occurred", server);
}
auth.resetCount();
@@ -293,73 +427,71 @@
c5 = new Client (authority, "/test/realm3/t2a", true);
c6 = new Client (authority, "/test/realm3/t2b", false);
+ t2condlatch = new CountDownLatch(1);
c5.start ();
- HttpServer.waitForCondition ("T2cond1");
+ t2condlatch.await();
c6.start ();
c5.join(); c6.join();
f = auth.getCount();
if (f != redirects+1) {
- except ("Authenticator was called "+f+" times. Should be: " + redirects+1);
+ except ("Authenticator was called "+f+" times. Should be: "
+ + redirects+1, server);
}
if (error) {
- except ("error occurred");
+ except ("error occurred", server);
}
}
- static void doProxyTests (String authority) throws Exception {
+ static void doProxyTests (String authority, Server server) throws Exception
+ {
System.out.println ("Doing Proxy tests");
c7 = new Client (authority, "/test/realm4/t3a", false);
c8 = new Client (authority, "/test/realm4/t3b", false);
c9 = new Client (authority, "/test/realm4/t3c", false);
+ t3cond1 = new CountDownLatch(1);
c7.start ();
- HttpServer.waitForCondition ("T3cond1");
+ t3cond1.await();
c8.start ();
c9.start ();
c7.join(); c8.join(); c9.join();
int f = auth.getCount();
if (f != 2) {
- except ("Authenticator was called "+f+" times. Should be: " + 2);
+ except ("Authenticator was called "+f+" times. Should be: " + 2,
+ server);
}
if (error) {
- except ("error occurred");
+ except ("error occurred", server);
}
}
public static void main (String[] args) throws Exception {
+ new B4769350().runTest(args[0].equals ("proxy"));
+ }
+
+ public void runTest(boolean proxy) throws Exception {
System.setProperty ("http.maxRedirects", Integer.toString (redirects));
System.setProperty ("http.auth.serializeRequests", "true");
Authenticator.setDefault (auth);
- boolean proxy = args[0].equals ("proxy");
- try {
- server = new HttpServer (new CallBack(), 10, 1, 0);
- System.out.println ("Server: listening on port: " + server.getLocalPort());
+ try (Server server = new Server()) {
+ server.startServer();
+ System.out.println ("Server: listening on port: "
+ + server.getPort());
if (proxy) {
System.setProperty ("http.proxyHost", "localhost");
- System.setProperty ("http.proxyPort",Integer.toString(server.getLocalPort()));
- doProxyTests ("www.foo.com");
+ System.setProperty ("http.proxyPort",
+ Integer.toString(server.getPort()));
+ doProxyTests ("www.foo.com", server);
} else {
- doServerTests ("localhost:"+server.getLocalPort());
+ doServerTests ("localhost:"+server.getPort(), server);
}
- server.terminate();
-
- } catch (Exception e) {
- if (server != null) {
- server.terminate();
- }
- throw e;
}
+
}
- static void pause (int millis) {
- try {
- Thread.sleep (millis);
- } catch (InterruptedException e) {}
- }
-
- public static void except (String s) {
- server.terminate();
+ public static void except (String s, Server server) {
+ server.close();
throw new RuntimeException (s);
}
@@ -368,13 +500,10 @@
super ();
}
- int count = 0;
+ volatile int count = 0;
+ @Override
public PasswordAuthentication getPasswordAuthentication () {
- //System.out.println ("Authenticator called: " + getRequestingPrompt());
- //try {
- //Thread.sleep (1000);
- //} catch (InterruptedException e) {}
PasswordAuthentication pw;
pw = new PasswordAuthentication ("user", "pass1".toCharArray());
count ++;
@@ -386,7 +515,7 @@
}
public int getCount () {
- return (count);
+ return count;
}
}
}
diff --git a/jdk/test/java/net/Socket/asyncClose/Race.java b/jdk/test/java/net/Socket/asyncClose/Race.java
index f886939..8279d33 100644
--- a/jdk/test/java/net/Socket/asyncClose/Race.java
+++ b/jdk/test/java/net/Socket/asyncClose/Race.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,8 +23,8 @@
/*
* @test
- * @bug 8006395
- * @summary Race in async socket close on Linux
+ * @bug 8006395 8012244
+ * @summary Tests racing code that reads and closes a Socket
*/
import java.io.InputStream;
@@ -58,7 +58,7 @@
Thread.sleep(50);
} catch (Exception x) {
if (!(x instanceof SocketException
- && x.getMessage().equals("Socket closed")))
+ && x.getMessage().equalsIgnoreCase("socket closed")))
x.printStackTrace();
// ok, expect Socket closed
}
diff --git a/jdk/test/java/nio/channels/AsynchronousChannelGroup/Unbounded.java b/jdk/test/java/nio/channels/AsynchronousChannelGroup/Unbounded.java
index 0ca96fb..001a65b 100644
--- a/jdk/test/java/nio/channels/AsynchronousChannelGroup/Unbounded.java
+++ b/jdk/test/java/nio/channels/AsynchronousChannelGroup/Unbounded.java
@@ -36,44 +36,31 @@
// number of concurrent completion handlers
static final int CONCURRENCY_COUNT = 256;
- public static void main(String[] args) throws Exception {
- // all accepted connections are added to a queue
- final ArrayBlockingQueue<AsynchronousSocketChannel> queue =
- new ArrayBlockingQueue<AsynchronousSocketChannel>(CONCURRENCY_COUNT);
+ // set to true if an I/O operation fails
+ static volatile boolean failed;
+ // set to true when the test is done
+ static volatile boolean finished;
+
+ public static void main(String[] args) throws Exception {
// create listener to accept connections
- final AsynchronousServerSocketChannel listener =
+ AsynchronousServerSocketChannel listener =
AsynchronousServerSocketChannel.open()
.bind(new InetSocketAddress(0));
- listener.accept((Void)null, new CompletionHandler<AsynchronousSocketChannel,Void>() {
- public void completed(AsynchronousSocketChannel ch, Void att) {
- queue.add(ch);
- listener.accept((Void)null, this);
- }
- public void failed(Throwable exc, Void att) {
- }
- });
- System.out.println("Listener created.");
- // establish lots of connections
+ // establish connections
+
+ AsynchronousSocketChannel[] clients = new AsynchronousSocketChannel[CONCURRENCY_COUNT];
+ AsynchronousSocketChannel[] peers = new AsynchronousSocketChannel[CONCURRENCY_COUNT];
+
int port = ((InetSocketAddress)(listener.getLocalAddress())).getPort();
SocketAddress sa = new InetSocketAddress(InetAddress.getLocalHost(), port);
- AsynchronousSocketChannel[] channels =
- new AsynchronousSocketChannel[CONCURRENCY_COUNT];
+
for (int i=0; i<CONCURRENCY_COUNT; i++) {
- int attempts = 0;
- for (;;) {
- try {
- channels[i] = AsynchronousSocketChannel.open();
- channels[i].connect(sa).get();
- break;
- } catch (IOException x) {
- // probably resource issue so back off and retry
- if (++attempts >= 3)
- throw x;
- Thread.sleep(50);
- }
- }
+ clients[i] = AsynchronousSocketChannel.open();
+ Future<Void> result = clients[i].connect(sa);
+ peers[i] = listener.accept().get();
+ result.get();
}
System.out.println("All connection established.");
@@ -81,9 +68,9 @@
final CyclicBarrier barrier = new CyclicBarrier(CONCURRENCY_COUNT+1);
// initiate a read operation on each channel.
- for (int i=0; i<CONCURRENCY_COUNT; i++) {
+ for (AsynchronousSocketChannel client: clients) {
ByteBuffer buf = ByteBuffer.allocateDirect(100);
- channels[i].read( buf, channels[i],
+ client.read(buf, client,
new CompletionHandler<Integer,AsynchronousSocketChannel>() {
public void completed(Integer bytesRead, AsynchronousSocketChannel ch) {
try {
@@ -94,23 +81,29 @@
}
}
public void failed(Throwable exc, AsynchronousSocketChannel ch) {
+ failed = true;
+ System.err.println("read failed: " + exc);
+ completed(0, ch);
}
});
}
System.out.println("All read operations outstanding.");
// write data to each of the accepted connections
- int remaining = CONCURRENCY_COUNT;
- while (remaining > 0) {
- AsynchronousSocketChannel ch = queue.take();
- ch.write(ByteBuffer.wrap("welcome".getBytes())).get();
- ch.close();
- remaining--;
+ for (AsynchronousSocketChannel peer: peers) {
+ peer.write(ByteBuffer.wrap("welcome".getBytes())).get();
+ peer.shutdownOutput();
+ peer.close();
}
// wait for all threads to reach the barrier
System.out.println("Waiting for all threads to reach barrier");
barrier.await();
+
+ // finish up
+ finished = true;
listener.close();
+ if (failed)
+ throw new RuntimeException("I/O operation failed, see log for details");
}
}
diff --git a/jdk/test/java/util/logging/TestLogConfigurationDeadLock.java b/jdk/test/java/util/logging/TestLogConfigurationDeadLock.java
new file mode 100644
index 0000000..70badde1
--- /dev/null
+++ b/jdk/test/java/util/logging/TestLogConfigurationDeadLock.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 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.
+ */
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.security.Permission;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+
+/**
+ * @test
+ * @bug 8029281 8027670
+ * @summary Synchronization issues in Logger and LogManager. This test
+ * focusses more particularly on potential deadlock in
+ * drainLoggerRefQueueBounded / readConfiguration
+ * @run main/othervm TestLogConfigurationDeadLock
+ * @author danielfuchs
+ */
+// This test is a best effort to try & detect issues. The test itself will run
+// for 8secs. This is usually unsufficient to detect issues.
+// To get a greater confidence it is recommended to run this test in a loop:
+// e.g. use something like:
+// $ while jtreg -jdk:$JDK -verbose:all \
+// test/java/util/logging/TestLogConfigurationDeadLock.java ; \
+// do echo Running test again ; done
+// and let it run for a few hours...
+//
+public class TestLogConfigurationDeadLock {
+
+ static volatile Exception thrown = null;
+ static volatile boolean goOn = true;
+
+ static final int READERS = 2;
+ static final int LOGGERS = 2;
+ static final long TIME = 4 * 1000; // 4 sec.
+ static final long STEP = 1 * 1000; // message every 1 sec.
+ static final int LCOUNT = 50; // 50 loggers created in a row...
+ static final AtomicLong nextLogger = new AtomicLong(0);
+ static final AtomicLong readCount = new AtomicLong(0);
+ static final AtomicLong checkCount = new AtomicLong(0);
+
+ /**
+ * This test will run both with and without a security manager.
+ *
+ * The test starts a number of threads that will call
+ * LogManager.readConfiguration() concurrently (ReadConf), then starts
+ * a number of threads that will create new loggers concurrently
+ * (AddLogger), and then two additional threads: one (Stopper) that
+ * will stop the test after 4secs (TIME ms), and one DeadlockDetector
+ * that will attempt to detect deadlocks.
+ * If after 4secs no deadlock was detected and no exception was thrown
+ * then the test is considered a success and passes.
+ *
+ * This procedure is done twice: once without a security manager and once
+ * again with a security manager - which means the test takes ~8secs to
+ * run.
+ *
+ * Note that 8sec may not be enough to detect issues if there are some.
+ * This is a best effort test.
+ *
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) throws Exception {
+
+ // test without security
+ System.out.println("No security");
+ test();
+
+ // test with security
+ System.out.println("\nWith security");
+ Policy.setPolicy(new Policy() {
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ if (super.implies(domain, permission)) return true;
+ // System.out.println("Granting " + permission);
+ return true; // all permissions
+ }
+ });
+ System.setSecurityManager(new SecurityManager());
+ test();
+ }
+
+ /**
+ * Starts all threads, wait 4secs, then stops all threads.
+ * @throws Exception if a deadlock was detected or an error occurred.
+ */
+ public static void test() throws Exception {
+ goOn = true;
+ thrown = null;
+ long sNextLogger = nextLogger.get();
+ long sReadCount = readCount.get();
+ long sCheckCount = checkCount.get();
+ List<Thread> threads = new ArrayList<>();
+ for (int i = 0; i<READERS; i++) {
+ threads.add(new ReadConf());
+ }
+ for (int i = 0; i<LOGGERS; i++) {
+ threads.add(new AddLogger());
+ }
+ threads.add(new DeadlockDetector());
+ threads.add(0, new Stopper(TIME));
+ for (Thread t : threads) {
+ t.start();
+ }
+ for (Thread t : threads) {
+ try {
+ t.join();
+ } catch (Exception x) {
+ fail(x);
+ }
+ }
+ if (thrown != null) {
+ throw thrown;
+ }
+ System.out.println("Passed: " + (nextLogger.get() - sNextLogger)
+ + " loggers created by " + LOGGERS + " Thread(s),");
+ System.out.println("\t LogManager.readConfiguration() called "
+ + (readCount.get() - sReadCount) + " times by " + READERS
+ + " Thread(s).");
+ System.out.println("\t ThreadMXBean.findDeadlockedThreads called "
+ + (checkCount.get() -sCheckCount) + " times by 1 Thread.");
+
+ }
+
+
+ final static class ReadConf extends Thread {
+ @Override
+ public void run() {
+ while (goOn) {
+ try {
+ LogManager.getLogManager().readConfiguration();
+ readCount.incrementAndGet();
+ Thread.sleep(1);
+ } catch (Exception x) {
+ fail(x);
+ }
+ }
+ }
+ }
+
+ final static class AddLogger extends Thread {
+ @Override
+ public void run() {
+ try {
+ while (goOn) {
+ Logger l;
+ Logger foo = Logger.getLogger("foo");
+ Logger bar = Logger.getLogger("foo.bar");
+ for (int i=0; i < LCOUNT ; i++) {
+ l = Logger.getLogger("foo.bar.l"+nextLogger.incrementAndGet());
+ l.fine("I'm fine");
+ if (!goOn) break;
+ Thread.sleep(1);
+ }
+ }
+ } catch (InterruptedException | RuntimeException x ) {
+ fail(x);
+ }
+ }
+ }
+
+ final static class DeadlockDetector extends Thread {
+
+ @Override
+ public void run() {
+ while(goOn) {
+ try {
+ long[] ids = ManagementFactory.getThreadMXBean().findDeadlockedThreads();
+ checkCount.incrementAndGet();
+ ids = ids == null ? new long[0] : ids;
+ if (ids.length == 1) {
+ throw new RuntimeException("Found 1 deadlocked thread: "+ids[0]);
+ } else if (ids.length > 0) {
+ ThreadInfo[] infos = ManagementFactory.getThreadMXBean()
+ .getThreadInfo(ids, Integer.MAX_VALUE);
+ System.err.println("Found "+ids.length+" deadlocked threads: ");
+ for (ThreadInfo inf : infos) {
+ System.err.println(inf.toString());
+ }
+ throw new RuntimeException("Found "+ids.length+" deadlocked threads");
+ }
+ Thread.sleep(100);
+ } catch(InterruptedException | RuntimeException x) {
+ fail(x);
+ }
+ }
+ }
+
+ }
+
+ static final class Stopper extends Thread {
+ long start;
+ long time;
+
+ Stopper(long time) {
+ start = System.currentTimeMillis();
+ this.time = time;
+ }
+
+ @Override
+ public void run() {
+ try {
+ long rest, previous;
+ previous = time;
+ while (goOn && (rest = start - System.currentTimeMillis() + time) > 0) {
+ if (previous == time || previous - rest >= STEP) {
+ Logger.getLogger("remaining").info(String.valueOf(rest)+"ms remaining...");
+ previous = rest == time ? rest -1 : rest;
+ System.gc();
+ }
+ if (goOn == false) break;
+ Thread.sleep(Math.min(rest, 100));
+ }
+ System.out.println(System.currentTimeMillis() - start
+ + " ms elapsed ("+time+ " requested)");
+ goOn = false;
+ } catch(InterruptedException | RuntimeException x) {
+ fail(x);
+ }
+ }
+
+ }
+
+ static void fail(Exception x) {
+ x.printStackTrace();
+ if (thrown == null) {
+ thrown = x;
+ }
+ goOn = false;
+ }
+}
diff --git a/jdk/test/java/util/logging/TestLogConfigurationDeadLockWithConf.java b/jdk/test/java/util/logging/TestLogConfigurationDeadLockWithConf.java
new file mode 100644
index 0000000..9bde8d5
--- /dev/null
+++ b/jdk/test/java/util/logging/TestLogConfigurationDeadLockWithConf.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.File;
+import java.io.PrintStream;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.security.Permission;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+
+/**
+ * @test
+ * @bug 8027670 8029281
+ * @summary Deadlock in drainLoggerRefQueueBounded / readConfiguration
+ * caused by synchronization issues in Logger and LogManager.
+ * @run main/othervm TestLogConfigurationDeadLockWithConf
+ * @author danielfuchs
+ */
+// This test is a best effort to try & detect issues. The test itself will run
+// for 8secs. This is usually sufficient to detect issues.
+// However to get a greater confidence it is recommended to run this test in a loop:
+// e.g. use something like:
+// $ while jtreg -jdk:$JDK -verbose:all \
+// test/java/util/logging/TestLogConfigurationDeadLockWithConf.java ; \
+// do echo Running test again ; done
+// and let it run for a few hours...
+//
+public class TestLogConfigurationDeadLockWithConf {
+
+ static volatile Exception thrown = null;
+ static volatile boolean goOn = true;
+
+ static final int READERS = 2;
+ static final int LOGGERS = 2;
+ static final long TIME = 4 * 1000; // 4 sec.
+ static final long STEP = 1 * 1000; // message every 1 sec.
+ static final int LCOUNT = 50; // 50 loggers created in a row...
+ static final AtomicLong nextLogger = new AtomicLong(0);
+ static final AtomicLong readCount = new AtomicLong(0);
+ static final AtomicLong checkCount = new AtomicLong(0);
+
+ /**
+ * This test will run both with and without a security manager.
+ *
+ * The test starts a number of threads that will call
+ * LogManager.readConfiguration() concurrently (ReadConf), then starts
+ * a number of threads that will create new loggers concurrently
+ * (AddLogger), and then two additional threads: one (Stopper) that
+ * will stop the test after 4secs (TIME ms), and one DeadlockDetector
+ * that will attempt to detect deadlocks.
+ * If after 4secs no deadlock was detected and no exception was thrown
+ * then the test is considered a success and passes.
+ *
+ * This procedure is done twice: once without a security manager and once
+ * again with a security manager - which means the test takes ~8secs to
+ * run.
+ *
+ * Note that 8sec may not be enough to detect issues if there are some.
+ * This is a best effort test.
+ *
+ * @param args the command line arguments
+ * @throws java.lang.Exception if the test fails.
+ */
+ public static void main(String[] args) throws Exception {
+ File config = new File(System.getProperty("test.src", "."),
+ "deadlockconf.properties");
+ if (!config.canRead()) {
+ System.err.println("Can't read config file: test cannot execute.");
+ System.err.println("Please check your test environment: ");
+ System.err.println("\t -Dtest.src=" + System.getProperty("test.src", "."));
+ System.err.println("\t config file is: " + config.getAbsolutePath());
+ throw new RuntimeException("Can't read config file: "
+ + config.getAbsolutePath());
+ }
+
+ System.setProperty("java.util.logging.config.file",
+ config.getAbsolutePath());
+
+ // test without security
+ System.out.println("No security");
+ test();
+
+ // test with security
+ System.out.println("\nWith security");
+ Policy.setPolicy(new Policy() {
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ if (super.implies(domain, permission)) return true;
+ // System.out.println("Granting " + permission);
+ return true; // all permissions
+ }
+ });
+ System.setSecurityManager(new SecurityManager());
+ test();
+ }
+
+ static Random rand = new Random(System.currentTimeMillis());
+ private static int getBarCount() {
+ return rand.nextInt(10);
+ }
+
+ /**
+ * Starts all threads, wait 4secs, then stops all threads.
+ * @throws Exception if a deadlock was detected or an error occurred.
+ */
+ public static void test() throws Exception {
+ goOn = true;
+ thrown = null;
+ long sNextLogger = nextLogger.get();
+ long sReadCount = readCount.get();
+ long sCheckCount = checkCount.get();
+ List<Thread> threads = new ArrayList<>();
+ for (int i = 0; i<READERS; i++) {
+ threads.add(new ReadConf());
+ }
+ for (int i = 0; i<LOGGERS; i++) {
+ threads.add(new AddLogger());
+ }
+ DeadlockDetector detector = new DeadlockDetector();
+ threads.add(detector);
+ threads.add(0, new Stopper(TIME));
+ for (Thread t : threads) {
+ t.start();
+ }
+
+ // wait for the detector to finish.
+ detector.join();
+
+ final PrintStream out = thrown == null ? System.out : System.err;
+
+ // Try to wait for all threads to finish.
+ // This is a best effort: if some threads are in deadlock we can't
+ // obviously wait for them, and other threads may have joined in
+ // the deadlock since we last checked.
+ // However, all threads which are succeptible of deadlocking
+ // extend DeamonThread.
+ for (Thread t : threads) {
+ if (t == detector) {
+ continue;
+ }
+ if (detector.deadlocked.contains(t.getId())) {
+ out.println("Skipping deadlocked thread "
+ + t.getClass().getSimpleName() + ": " + t);
+ continue; // don't wait for deadlocked thread: they won't terminate
+ }
+ try {
+ if (detector.deadlocked.isEmpty()) {
+ t.join();
+ } else {
+ if (t instanceof DaemonThread) {
+ // Some other threads may have join the deadlock.
+ // don't wait forever.
+ t.join(100);
+ } else {
+ // Those threads that don't extend DaemonThread
+ // should be safe from deadlock.
+ out.println("Waiting for "
+ + t.getClass().getSimpleName() + ": " + t);
+ t.join();
+ }
+ }
+ } catch (Exception x) {
+ fail(x);
+ }
+ }
+ out.println("All threads joined.");
+
+ final String status = thrown == null ? "Passed" : "FAILED";
+
+ out.println(status + ": " + (nextLogger.get() - sNextLogger)
+ + " loggers created by " + LOGGERS + " Thread(s),");
+ out.println("\t LogManager.readConfiguration() called "
+ + (readCount.get() - sReadCount) + " times by " + READERS
+ + " Thread(s).");
+ out.println("\t ThreadMXBean.findDeadlockedThreads called "
+ + (checkCount.get() -sCheckCount) + " times by 1 Thread.");
+
+ if (thrown != null) {
+ out.println("\t Error is: "+thrown.getMessage());
+ throw thrown;
+ }
+ }
+
+ static class DaemonThread extends Thread {
+ public DaemonThread() {
+ this.setDaemon(true);
+ }
+ }
+
+ final static class ReadConf extends DaemonThread {
+ @Override
+ public void run() {
+ while (goOn) {
+ try {
+ LogManager.getLogManager().readConfiguration();
+ readCount.incrementAndGet();
+ Thread.sleep(1);
+ } catch (Exception x) {
+ fail(x);
+ }
+ }
+ }
+ }
+
+ final static class AddLogger extends DaemonThread {
+ @Override
+ public void run() {
+ try {
+ while (goOn) {
+ Logger l;
+ int barcount = getBarCount();
+ for (int i=0; i < LCOUNT ; i++) {
+ l = Logger.getLogger("foo.bar"+barcount+".l"+nextLogger.incrementAndGet());
+ l.fine("I'm fine");
+ if (!goOn) break;
+ Thread.sleep(1);
+ }
+ }
+ } catch (InterruptedException | RuntimeException x ) {
+ fail(x);
+ }
+ }
+ }
+
+ final static class DeadlockDetector extends Thread {
+
+ final Set<Long> deadlocked = Collections.synchronizedSet(new HashSet<Long>());
+
+ static List<Long> asList(long... ids) {
+ final List<Long> list = new ArrayList<>(ids.length);
+ for (long id : ids) {
+ list.add(id);
+ }
+ return list;
+ }
+
+ @Override
+ public void run() {
+ while(goOn) {
+ try {
+ long[] ids = ManagementFactory.getThreadMXBean().findDeadlockedThreads();
+ checkCount.incrementAndGet();
+ ids = ids == null ? new long[0] : ids;
+ if (ids.length > 0) {
+ deadlocked.addAll(asList(ids));
+ }
+ if (ids.length == 1) {
+ throw new RuntimeException("Found 1 deadlocked thread: "+ids[0]);
+ } else if (ids.length > 0) {
+ ThreadInfo[] infos = ManagementFactory.getThreadMXBean().getThreadInfo(ids, Integer.MAX_VALUE);
+ System.err.println("Found "+ids.length+" deadlocked threads: ");
+ for (ThreadInfo inf : infos) {
+ System.err.println(inf.toString());
+ }
+ throw new RuntimeException("Found "+ids.length+" deadlocked threads");
+ }
+ Thread.sleep(100);
+ } catch(InterruptedException | RuntimeException x) {
+ fail(x);
+ }
+ }
+ }
+
+ }
+
+ static final class Stopper extends Thread {
+ long start;
+ long time;
+
+ static final Logger logger = Logger.getLogger("remaining");
+
+ Stopper(long time) {
+ start = System.currentTimeMillis();
+ this.time = time;
+ }
+
+ @Override
+ public void run() {
+ try {
+ long rest, previous;
+ previous = time;
+ while (goOn && (rest = start - System.currentTimeMillis() + time) > 0) {
+ if (previous == time || previous - rest >= STEP) {
+ logger.log(Level.INFO,
+ "{0}ms remaining...", String.valueOf(rest));
+ previous = rest == time ? rest -1 : rest;
+ System.gc();
+ }
+ if (goOn == false) break;
+ Thread.sleep(Math.min(rest, 100));
+ }
+ System.out.println(System.currentTimeMillis() - start
+ + " ms elapsed ("+time+ " requested)");
+ goOn = false;
+ } catch(InterruptedException | RuntimeException x) {
+ fail(x);
+ }
+ }
+
+ }
+
+ static void fail(Exception x) {
+ x.printStackTrace();
+ if (thrown == null) {
+ thrown = x;
+ }
+ goOn = false;
+ }
+}
diff --git a/jdk/test/java/util/logging/TestLoggerBundleSync.java b/jdk/test/java/util/logging/TestLoggerBundleSync.java
new file mode 100644
index 0000000..0f2ea83
--- /dev/null
+++ b/jdk/test/java/util/logging/TestLoggerBundleSync.java
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 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.
+ */
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.security.Permission;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListResourceBundle;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+/**
+ * @test
+ * @bug 8029281 8028763
+ * @summary Attempts to detect synchronization issues with getResourceBundle()
+ * and getResourceBundleName(). It might also detect issues in the way
+ * that the logger tree is cleaned up after a logger has been garbage
+ * collected. This test helped find the root cause of 8029092, so if
+ * this test fails one might also expect failures in
+ * java/util/logging/Logger/logrb/TestLogrbResourceBundle.java and
+ * java/util/logging/Logger/setResourceBundle/TestSetResourceBundle.java.
+ * Note that this is a best effort test. Running it in a loop to
+ * reproduce intermittent issues can be a good idea.
+ * @run main/othervm TestLoggerBundleSync
+ * @author danielfuchs
+ */
+public class TestLoggerBundleSync {
+
+ static volatile Exception thrown = null;
+ static volatile boolean goOn = true;
+
+ static final int READERS = 3;
+ static final long TIME = 4 * 1000; // 4 sec.
+ static final long STEP = 1 * 1000; // message every 1 sec.
+ static final int LCOUNT = 50; // change bundle 50 times...
+ static final AtomicLong setRBcount = new AtomicLong(0);
+ static final AtomicLong setRBNameCount = new AtomicLong(0);
+ static final AtomicLong getRBcount = new AtomicLong(0);
+ static final AtomicLong checkCount = new AtomicLong(0);
+ static final AtomicLong nextLong = new AtomicLong(0);
+
+ public static class MyBundle extends ListResourceBundle {
+ @Override
+ protected Object[][] getContents() {
+ return new Object[][] {
+ {"dummy", "foo"}
+ };
+ }
+ }
+
+ public static final class MyBundle1 extends MyBundle { };
+ public static final class MyBundle2 extends MyBundle { };
+ public static final class MyBundle3 extends MyBundle { };
+
+
+ public static final class LoggerRB {
+ public final String resourceBundleName;
+ public final ResourceBundle userBundle;
+ public LoggerRB(String name, ResourceBundle bundle) {
+ resourceBundleName = name;
+ userBundle = bundle;
+ }
+ }
+
+ static final List<Class<? extends ResourceBundle>> classes = new ArrayList<>();
+ static {
+ classes.add(MyBundle1.class);
+ classes.add(MyBundle2.class);
+ classes.add(MyBundle3.class);
+ }
+
+
+ /**
+ * This test will run both with and without a security manager.
+ *
+ * The test starts a number of threads that will attempt to concurrently
+ * set resource bundles on Logger, and verifies the consistency of the
+ * obtained results.
+ *
+ * This is a best effort test.
+ *
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) throws Exception {
+
+ try {
+ // test without security
+ System.out.println("No security");
+ test();
+
+ // test with security
+ System.out.println("\nWith security");
+ Policy.setPolicy(new Policy() {
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ if (super.implies(domain, permission)) return true;
+ // System.out.println("Granting " + permission);
+ return true; // all permissions
+ }
+ });
+ System.setSecurityManager(new SecurityManager());
+ test();
+ } finally {
+ SetRB.executor.shutdownNow();
+ SetRBName.executor.shutdownNow();
+ }
+ }
+
+ /**
+ * Starts all threads, wait 15secs, then stops all threads.
+ * @throws Exception if a deadlock was detected or an error occurred.
+ */
+ public static void test() throws Exception {
+ goOn = true;
+ thrown = null;
+ long sGetRBCount = getRBcount.get();
+ long sSetRBCount = setRBcount.get();
+ long sSetRBNameCount = setRBNameCount.get();
+ long sCheckCount = checkCount.get();
+ long sNextLong = nextLong.get();
+ List<Thread> threads = new ArrayList<>();
+ for (Class<? extends ResourceBundle> type : classes) {
+ threads.add(new SetRB(type));
+ threads.add(new SetRBName(type));
+ }
+ for (int i =0 ; i < READERS ; i++) {
+ threads.add(new GetRB());
+ }
+ threads.add(new DeadlockDetector());
+ threads.add(0, new Stopper(TIME));
+ for (Thread t : threads) {
+ t.start();
+ }
+ for (Thread t : threads) {
+ try {
+ t.join();
+ } catch (Exception x) {
+ fail(x);
+ }
+ }
+ if (thrown != null) {
+ throw thrown;
+ }
+ System.out.println("Passed: " + (nextLong.longValue() - sNextLong)
+ + " unique loggers created");
+ System.out.println("\t " +(getRBcount.get() - sGetRBCount)
+ + " loggers tested by " + READERS + " Thread(s),");
+ System.out.println("\t " + (setRBcount.get() - sSetRBCount)
+ + " resource bundles set by " + classes.size() + " Thread(s),");
+ System.out.println("\t " + (setRBNameCount.get() - sSetRBNameCount)
+ + " resource bundle names set by " + classes.size() + " Thread(s),");
+ System.out.println("\t ThreadMXBean.findDeadlockedThreads called "
+ + (checkCount.get() -sCheckCount) + " times by 1 Thread.");
+
+ }
+
+ final static class GetRB extends Thread {
+ final static class MyHandler extends Handler {
+ volatile ResourceBundle rb;
+ volatile String rbName;
+ @Override
+ public synchronized void publish(LogRecord record) {
+ rb = record.getResourceBundle();
+ rbName = record.getResourceBundleName();
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void close() throws SecurityException {
+ }
+ };
+ final MyHandler handler = new MyHandler();
+ @Override
+ public void run() {
+ try {
+ handler.setLevel(Level.FINEST);
+ while (goOn) {
+ Logger l;
+ Logger foo = Logger.getLogger("foo");
+ Logger bar = Logger.getLogger("foo.bar");
+ for (long i=0; i < nextLong.longValue() + 100 ; i++) {
+ if (!goOn) break;
+ l = Logger.getLogger("foo.bar.l"+i);
+ final ResourceBundle b = l.getResourceBundle();
+ final String name = l.getResourceBundleName();
+ if (b != null) {
+ if (!name.equals(b.getClass().getName())) {
+ throw new RuntimeException("Unexpected bundle name: "
+ +b.getClass().getName());
+ }
+ }
+ Logger ll = Logger.getLogger(l.getName()+".bie.bye");
+ ResourceBundle hrb;
+ String hrbName;
+ ll.setLevel(Level.FINEST);
+ ll.addHandler(handler);
+ ll.fine("dummy");
+ ll.removeHandler(handler);
+ hrb = handler.rb;
+ hrbName = handler.rbName;
+ if (name != null) {
+ if (!name.equals(hrbName)) {
+ throw new RuntimeException("Unexpected bundle name: "
+ +hrb.getClass().getName());
+ }
+ if (!name.equals(hrb.getClass().getName())) {
+ throw new RuntimeException("Unexpected bundle name: "
+ +hrb.getClass().getName());
+ }
+ }
+
+ getRBcount.incrementAndGet();
+ if (!goOn) break;
+ Thread.sleep(1);
+ }
+ }
+ } catch (Exception x) {
+ fail(x);
+ }
+ }
+ }
+
+ final static class SetRB extends Thread {
+ final Class<? extends ResourceBundle> type;
+ final static ExecutorService executor = Executors.newSingleThreadExecutor();
+ final static class CheckRBTask implements Callable<Exception> {
+ final Logger logger;
+ volatile String rbName;
+ volatile ResourceBundle rb;
+
+ public CheckRBTask(Logger logger) {
+ this.logger = logger;
+ }
+
+ @Override
+ public Exception call() throws Exception {
+ try {
+ final String name = logger.getResourceBundleName();
+ if (!Objects.equals(name, rbName)) {
+ throw new RuntimeException("Unexpected rbname for "
+ + logger.getName() + ": " + name);
+ }
+ final ResourceBundle b = logger.getResourceBundle();
+ if (b != rb) {
+ throw new RuntimeException("Unexpected rb for "
+ + logger.getName() + ": " + b);
+ }
+ } catch(Exception x) {
+ return x;
+ }
+ return null;
+ }
+
+ public void check() throws Exception {
+ final FutureTask<Exception> futureTask = new FutureTask<>(this);
+ executor.submit(futureTask);
+ Exception x = futureTask.get();
+ if ( x != null) {
+ throw new RuntimeException("Check failed: "+x,x);
+ }
+ }
+ }
+ SetRB(Class<? extends ResourceBundle> type) {
+ super("SetRB["+type.getSimpleName()+"]");
+ this.type = type;
+ }
+ @Override
+ public void run() {
+ try {
+ while (goOn) {
+ Logger l;
+ Logger foo = Logger.getLogger("foo");
+ Logger bar = Logger.getLogger("foo.bar");
+ l = Logger.getLogger("foo.bar.l"+nextLong.incrementAndGet());
+ final CheckRBTask checkTask = new CheckRBTask(l);
+ checkTask.check();
+ Logger l1 = l;
+
+ for (int i=0; i < LCOUNT ; i++) {
+ if (!goOn) break;
+
+ ResourceBundle b = ResourceBundle.getBundle(type.getName());
+ try {
+ l = Logger.getLogger(l1.getName(), type.getName());
+ checkTask.rb = b;
+ checkTask.rbName = type.getName();
+ checkTask.check();
+ if (!goOn) break;
+
+ String name = l.getResourceBundleName();
+ ResourceBundle bb = l.getResourceBundle();
+ if (!type.getName().equals(name)) {
+ throw new RuntimeException(this.getName()
+ + ": Unexpected name: "+name);
+ }
+ if (!b.getClass().getName().equals(name)) {
+ throw new RuntimeException(this.getName()
+ + ": Unexpected base name: " +
+ b.getClass().getName());
+ }
+ if (b != bb) {
+ throw new RuntimeException(this.getName()
+ + ": Unexpected bundle: "+bb);
+ }
+ setRBcount.incrementAndGet();
+ } catch (IllegalArgumentException x) {
+ final String name = l.getResourceBundleName();
+ if (!name.startsWith(MyBundle.class.getName())) {
+ throw new RuntimeException(this.getName()
+ + ": Unexpected name: "+name, x);
+ } else if (type.getName().equals(name)) {
+ throw new RuntimeException(this.getName()
+ + ": Unexpected exception for "+name, x);
+ }
+ throw x;
+ }
+ l.fine("I'm fine");
+ if (!goOn) break;
+ Thread.sleep(1);
+ }
+ }
+ } catch (Exception x) {
+ fail(x);
+ }
+ }
+ }
+
+ final static class SetRBName extends Thread {
+ int nexti = 0;
+ final Class<? extends ResourceBundle> type;
+ final static ExecutorService executor = Executors.newSingleThreadExecutor();
+ final static class CheckRBNameTask implements Callable<Exception> {
+ final Logger logger;
+ volatile String rbName;
+
+ public CheckRBNameTask(Logger logger) {
+ this.logger = logger;
+ }
+
+ @Override
+ public Exception call() throws Exception {
+ try {
+ final String name = logger.getResourceBundleName();
+ if (!Objects.equals(name, rbName)) {
+ throw new RuntimeException("Unexpected rbname for "
+ + logger.getName() + ": " + name);
+ }
+ final ResourceBundle b = logger.getResourceBundle();
+ if (!Objects.equals(b == null ? null : b.getClass().getName(), rbName)) {
+ throw new RuntimeException("Unexpected base name for "
+ + logger.getName() + ": " + b.getClass().getName());
+ }
+ } catch(Exception x) {
+ return x;
+ }
+ return null;
+ }
+
+ public void check() throws Exception {
+ final FutureTask<Exception> futureTask = new FutureTask<>(this);
+ executor.submit(futureTask);
+ Exception x = futureTask.get();
+ if ( x != null) {
+ throw new RuntimeException("Check failed: "+x,x);
+ }
+ }
+
+ }
+ SetRBName(Class<? extends ResourceBundle> type) {
+ super("SetRB["+type.getSimpleName()+"]");
+ this.type = type;
+ }
+ @Override
+ public void run() {
+ try {
+ while (goOn) {
+ Logger foo = Logger.getLogger("foo");
+ Logger bar = Logger.getLogger("foo.bar");
+ Logger l = Logger.getLogger("foo.bar.l"+nextLong.incrementAndGet());
+ final CheckRBNameTask checkTask = new CheckRBNameTask(l);
+ checkTask.check();
+
+ for (int i=0; i < LCOUNT ; i++) {
+ if (!goOn) break;
+
+ try {
+ Logger l2 = Logger.getLogger(l.getName(), type.getName());
+ if (l2 != l) {
+ System.err.println("**** ERROR WITH "+l.getName());
+ throw new RuntimeException("l2 != l ["
+ + l2 + "(" + l2.getName() + ") != "
+ + l + "(" + l.getName() + ")]");
+ }
+ checkTask.rbName = type.getName();
+ checkTask.check();
+ if (!goOn) break;
+
+ String name = l.getResourceBundleName();
+ ResourceBundle bb = l.getResourceBundle();
+ if (!type.getName().equals(name)) {
+ throw new RuntimeException(this.getName()
+ + ": Unexpected name: "+name);
+ }
+ if (!bb.getClass().getName().equals(name)) {
+ throw new RuntimeException(this.getName()
+ + ": Unexpected base name: "
+ + bb.getClass().getName());
+ }
+ setRBNameCount.incrementAndGet();
+ } catch (IllegalArgumentException x) {
+ final String name = l.getResourceBundleName();
+ if (!name.startsWith(MyBundle.class.getName())) {
+ throw new RuntimeException(this.getName()
+ + ": Unexpected name: "+name, x);
+ } else if (type.getName().equals(name)) {
+ throw new RuntimeException(this.getName()
+ + ": Unexpected exception for "+name, x);
+ }
+ throw x;
+ }
+ l.fine("I'm fine");
+ if (!goOn) break;
+ Thread.sleep(1);
+ }
+ }
+ } catch (Exception x) {
+ fail(x);
+ }
+ }
+ }
+
+ final static class DeadlockDetector extends Thread {
+
+ @Override
+ public void run() {
+ while(goOn) {
+ try {
+ long[] ids = ManagementFactory.getThreadMXBean().findDeadlockedThreads();
+ checkCount.incrementAndGet();
+ ids = ids == null ? new long[0] : ids;
+ if (ids.length == 1) {
+ throw new RuntimeException("Found 1 deadlocked thread: "+ids[0]);
+ } else if (ids.length > 0) {
+ ThreadInfo[] infos = ManagementFactory.getThreadMXBean().getThreadInfo(ids);
+ System.err.println("Found "+ids.length+" deadlocked threads: ");
+ for (ThreadInfo inf : infos) {
+ System.err.println(inf.toString());
+ }
+ throw new RuntimeException("Found "+ids.length+" deadlocked threads");
+ }
+ Thread.sleep(100);
+ } catch(InterruptedException | RuntimeException x) {
+ fail(x);
+ }
+ }
+ }
+
+ }
+
+ static final class Stopper extends Thread {
+ long start;
+ long time;
+
+ Stopper(long time) {
+ start = System.currentTimeMillis();
+ this.time = time;
+ }
+
+ @Override
+ public void run() {
+ try {
+ long rest, previous;
+ previous = time;
+ while (goOn && (rest = start - System.currentTimeMillis() + time) > 0) {
+ if (previous == time || previous - rest >= STEP) {
+ Logger.getLogger("remaining").info(String.valueOf(rest)+"ms remaining...");
+ previous = rest == time ? rest -1 : rest;
+ System.gc();
+ }
+ if (goOn == false) break;
+ Thread.sleep(Math.min(rest, 100));
+ }
+ System.out.println(System.currentTimeMillis() - start
+ + " ms elapsed ("+time+ " requested)");
+ goOn = false;
+ } catch(InterruptedException | RuntimeException x) {
+ fail(x);
+ }
+ }
+
+ }
+
+ static void fail(Exception x) {
+ x.printStackTrace();
+ if (thrown == null) {
+ thrown = x;
+ }
+ goOn = false;
+ }
+}
diff --git a/jdk/test/java/util/logging/deadlockconf.properties b/jdk/test/java/util/logging/deadlockconf.properties
new file mode 100644
index 0000000..e81742fa
--- /dev/null
+++ b/jdk/test/java/util/logging/deadlockconf.properties
@@ -0,0 +1,22 @@
+# This file is used by TestLogConfigurationDeadLockWithConf
+handlers= java.util.logging.ConsoleHandler
+.level= INFO
+java.util.logging.ConsoleHandler.level = INFO
+java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
+
+
+foo.bar0.level = INFO
+foo.bar1.level = INFO
+foo.bar2.level = INFO
+foo.bar3.level = INFO
+foo.bar4.level = INFO
+
+# We leave foo.bar5 out so that we have at least
+# one logger whose parent won't be in the configuration
+# file
+#foo.bar5.level = INFO
+
+foo.bar6.level = INFO
+foo.bar7.level = INFO
+foo.bar8.level = INFO
+foo.bar9.level = INFO
diff --git a/jdk/test/sun/security/tools/jarsigner/EntriesOrder.java b/jdk/test/sun/security/tools/jarsigner/EntriesOrder.java
new file mode 100644
index 0000000..3fa7648
--- /dev/null
+++ b/jdk/test/sun/security/tools/jarsigner/EntriesOrder.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8031572
+ * @summary jarsigner -verify exits with 0 when a jar file is not properly signed
+ */
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.cert.Certificate;
+import java.util.*;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+public class EntriesOrder {
+
+ public static void main(String[] args) throws Exception {
+
+ String[] entries = {
+ "META-INF/",
+ "META-INF/MANIFEST.MF",
+ "META-INF/A.RSA",
+ "META-INF/A.SF",
+ "META-INF/inf",
+ "a"};
+
+ Map<String,byte[]> content = new HashMap<>();
+
+ // We will create a jar containing entries above. Try all permutations
+ // and confirm 1) When opened as a JarFile, we can always get 3 signed
+ // ones (MANIFEST, inf, a), and 2) When opened as a JarInputStream,
+ // when the order is correct (MANIFEST at beginning, followed by RSA/SF,
+ // directory ignored), we can get 2 signed ones (inf, a).
+
+ // Prepares raw files
+ Files.write(Paths.get("a"), "a".getBytes());
+ Files.createDirectory(Paths.get("META-INF/"));
+ Files.write(Paths.get("META-INF/inf"), "inf".getBytes());
+
+ // Pack, sign, and extract to get all files
+ sun.tools.jar.Main m =
+ new sun.tools.jar.Main(System.out, System.err, "jar");
+ if (!m.run("cvf a.jar a META-INF/inf".split(" "))) {
+ throw new Exception("jar creation failed");
+ }
+ sun.security.tools.KeyTool.main(
+ ("-keystore jks -storepass changeit -keypass changeit -dname" +
+ " CN=A -alias a -genkeypair -keyalg rsa").split(" "));
+ sun.security.tools.JarSigner.main(
+ "-keystore jks -storepass changeit a.jar a".split(" "));
+ m = new sun.tools.jar.Main(System.out, System.err, "jar");
+ if (!m.run("xvf a.jar".split(" "))) {
+ throw new Exception("jar extraction failed");
+ }
+
+ // Data
+ for (String s: entries) {
+ if (!s.endsWith("/")) {
+ content.put(s, Files.readAllBytes(Paths.get(s)));
+ }
+ }
+
+ // Test
+ for (List<String> perm: Permute(entries)) {
+
+ // Recreate a jar
+ try (ZipOutputStream zos
+ = new ZipOutputStream(new FileOutputStream("x.jar"))) {
+ for (String e: perm) {
+ zos.putNextEntry(new ZipEntry(e));
+ if (Paths.get(e).toFile().isDirectory()) continue;
+ zos.write(content.get(e));
+ }
+ }
+
+ // Open with JarFile, number of signed entries should be 3.
+ int cc = 0;
+ try (JarFile jf = new JarFile("x.jar")) {
+ Enumeration<JarEntry> jes = jf.entries();
+ while (jes.hasMoreElements()) {
+ JarEntry je = jes.nextElement();
+ sun.misc.IOUtils.readFully(jf.getInputStream(je), -1, true);
+ Certificate[] certs = je.getCertificates();
+ if (certs != null && certs.length > 0) {
+ cc++;
+ }
+ }
+ }
+
+ if (cc != 3) {
+ System.out.println(perm + " - jf - " + cc);
+ throw new Exception();
+ }
+
+ // Open with JarInputStream
+ int signed;
+
+ perm.remove("META-INF/");
+ if (perm.get(0).equals("META-INF/MANIFEST.MF") &&
+ perm.get(1).contains("/A.") &&
+ perm.get(2).contains("/A.")) {
+ signed = 2; // Good order
+ } else {
+ signed = 0; // Bad order. In this case, the number of signed
+ // entries is not documented. Just test impl.
+ }
+
+ cc = 0;
+ try (JarInputStream jis
+ = new JarInputStream(new FileInputStream("x.jar"))) {
+ while (true) {
+ JarEntry je = jis.getNextJarEntry();
+ if (je == null) break;
+ sun.misc.IOUtils.readFully(jis, -1, true);
+ Certificate[] certs = je.getCertificates();
+ if (certs != null && certs.length > 0) {
+ cc++;
+ }
+ }
+ }
+
+ if (cc != signed) {
+ System.out.println(perm + " - jis - " + cc + " " + signed);
+ throw new Exception();
+ }
+ }
+ }
+
+ // Helper method to return all permutations of an array. Each output can
+ // be altered without damaging the iteration process.
+ static Iterable<List<String>> Permute(final String[] entries) {
+ return new Iterable<List<String>>() {
+
+ int s = entries.length;
+ long c = factorial(s) - 1; // number of permutations
+
+ private long factorial(int n) {
+ return (n == 1) ? 1: (n * factorial(n-1));
+ }
+
+ @Override
+ public Iterator<List<String>> iterator() {
+ return new Iterator<List<String>>() {
+ @Override
+ public boolean hasNext() {
+ return c >= 0;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+
+ @Override
+ public List<String> next() {
+ if (c < 0) return null;
+ List<String> result = new ArrayList<>(s);
+ LinkedList<String> source = new LinkedList<>(
+ Arrays.asList(entries));
+ // Treat c as a integer with different radixes at
+ // different digits, i.e. at digit 0, radix is s;
+ // at digit 1, radix is s-1. Thus a s-digit number
+ // is able to represent s! different values.
+ long n = c;
+ for (int i=s; i>=1; i--) {
+ int x = (int)(n % i);
+ result.add(source.remove(x));
+ n = n / i;
+ }
+ c--;
+ return result;
+ }
+ };
+ }
+ };
+ }
+}
diff --git a/langtools/.hgtags b/langtools/.hgtags
index 8769029..e71af9a 100644
--- a/langtools/.hgtags
+++ b/langtools/.hgtags
@@ -404,3 +404,4 @@
b19e375d9829daf207b1bdc7f908a3e1d548462c jdk7u60-b01
954e1616449af74f68aed57261cbeb62403377f1 jdk7u60-b02
4170784840d510b4e8ae7ae250b92279aaf5eb25 jdk7u60-b03
+772aad4e9681828b8ee193b9ed971cbfe6c7f347 jdk7u60-b04