Mercurial > hg > openjdk > bsd-port > hotspot
changeset 5182:f52b5452d424 hs24.60-b08
Merge
author | amurillo |
---|---|
date | Fri, 31 Jan 2014 10:53:59 -0800 |
parents | cff7d6378568 (current diff) b6d3ebe42303 (diff) |
children | 462db155547e |
files | test/gc/TestVerifyBeforeGCDuringStartup.java |
diffstat | 67 files changed, 3230 insertions(+), 1021 deletions(-) [+] |
line wrap: on
line diff
--- a/agent/src/os/linux/ps_core.c Wed Jan 22 12:33:48 2014 -0800 +++ b/agent/src/os/linux/ps_core.c Fri Jan 31 10:53:59 2014 -0800 @@ -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)) { - uintptr_t target_vaddr = lib_php->p_vaddr + lib_base; - map_info *existing_map = core_lookup(ph, target_vaddr); + 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. - 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)) { + if ((existing_map->memsz != page_size) && + (existing_map->fd != lib_fd) && + (ROUNDUP(existing_map->memsz, page_size) != ROUNDUP(lib_php->p_memsz, page_size))) { - print_debug("address conflict @ 0x%lx (size = %ld, flags = %d\n)", - target_vaddr, lib_php->p_filesz, lib_php->p_flags); - goto err; - } + 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)); - /* 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); - - existing_map->fd = lib_fd; - existing_map->offset = lib_php->p_offset; - existing_map->memsz = lib_php->p_filesz; - } + existing_map->fd = lib_fd; + existing_map->offset = lib_php->p_offset; + existing_map->memsz = ROUNDUP(lib_php->p_memsz, page_size); } - lib_php++; - } + } + + lib_php++; + } - free(phbuf); - return true; + free(phbuf); + return true; err: - free(phbuf); - return false; + free(phbuf); + return false; } // process segments from interpreter (ld.so or ld-linux.so)
--- a/make/hotspot_version Wed Jan 22 12:33:48 2014 -0800 +++ b/make/hotspot_version Fri Jan 31 10:53:59 2014 -0800 @@ -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
--- a/src/os/bsd/vm/os_bsd.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/os/bsd/vm/os_bsd.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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();
--- a/src/os/linux/vm/os_linux.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/os/linux/vm/os_linux.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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() {
--- a/src/os/windows/vm/os_windows.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/os/windows/vm/os_windows.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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; + + case 6000: + if (osvi.wProductType == VER_NT_WORKSTATION) { + st->print(" Windows Vista"); + } else { + st->print(" Windows Server 2008"); } break; - } - default: // future windows, print out its major and minor versions + + 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();
--- a/src/share/vm/c1/c1_Runtime1.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/c1/c1_Runtime1.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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 //
--- a/src/share/vm/classfile/symbolTable.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/classfile/symbolTable.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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); } }
--- a/src/share/vm/classfile/symbolTable.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/classfile/symbolTable.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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
--- a/src/share/vm/classfile/systemDictionary.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/classfile/systemDictionary.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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);
--- a/src/share/vm/code/nmethod.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/code/nmethod.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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
--- a/src/share/vm/code/nmethod.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/code/nmethod.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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;
--- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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; }
--- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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;
--- a/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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()]; +}
--- a/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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;
--- a/src/share/vm/gc_implementation/g1/concurrentMark.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/concurrentMark.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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(); }
--- a/src/share/vm/gc_implementation/g1/concurrentMark.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/concurrentMark.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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;
--- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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()) { + assert(Thread::current()->is_VM_thread(), + "Expected to be executed serially by the VM thread at this point"); + 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); - + 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); +}
--- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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();
--- a/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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);
--- a/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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(); }
--- a/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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);
--- a/src/share/vm/gc_implementation/g1/g1RemSet.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/g1RemSet.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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()) {
--- a/src/share/vm/gc_implementation/g1/g1RemSet.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/g1RemSet.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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 {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1RemSetSummary.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1RemSetSummary.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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
--- a/src/share/vm/gc_implementation/g1/g1_globals.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/g1_globals.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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)
--- a/src/share/vm/gc_implementation/g1/heapRegion.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/heapRegion.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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
--- a/src/share/vm/gc_implementation/g1/heapRegion.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/heapRegion.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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;
--- a/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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::
--- a/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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.
--- a/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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
--- a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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
--- a/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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();
--- a/src/share/vm/gc_interface/collectedHeap.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_interface/collectedHeap.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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();
--- a/src/share/vm/gc_interface/collectedHeap.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/gc_interface/collectedHeap.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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);
--- a/src/share/vm/memory/genCollectedHeap.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/memory/genCollectedHeap.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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
--- a/src/share/vm/memory/iterator.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/memory/iterator.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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) {
--- a/src/share/vm/memory/sharedHeap.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/memory/sharedHeap.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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
--- a/src/share/vm/memory/sharedHeap.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/memory/sharedHeap.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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,
--- a/src/share/vm/memory/universe.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/memory/universe.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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 ");
--- a/src/share/vm/memory/universe.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/memory/universe.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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; }
--- a/src/share/vm/oops/constantPoolOop.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/oops/constantPoolOop.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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
--- a/src/share/vm/oops/constantPoolOop.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/oops/constantPoolOop.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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; }
--- a/src/share/vm/prims/jvmtiClassFileReconstituter.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/prims/jvmtiClassFileReconstituter.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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));
--- a/src/share/vm/prims/jvmtiRedefineClasses.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/prims/jvmtiRedefineClasses.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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"); - - 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); - } + int scratch_cp_length, TRAPS) { + assert(scratch_cp->length() >= scratch_cp_length, "sanity check"); + + // 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_cp->set_pool_holder(scratch_class()); - - // attach klass to new constant pool scratch_class->set_constants(scratch_cp()); int i; // for portability
--- a/src/share/vm/prims/jvmtiRedefineClasses.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/prims/jvmtiRedefineClasses.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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);
--- a/src/share/vm/prims/whitebox.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/prims/whitebox.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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
--- a/src/share/vm/runtime/arguments.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/runtime/arguments.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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)");
--- a/src/share/vm/runtime/globals.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/runtime/globals.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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") \ \
--- a/src/share/vm/runtime/sweeper.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/runtime/sweeper.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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);
--- a/src/share/vm/runtime/thread.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/runtime/thread.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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;
--- a/src/share/vm/runtime/vmThread.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/runtime/vmThread.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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();
--- a/src/share/vm/runtime/vm_operations.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/runtime/vm_operations.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -189,7 +189,8 @@ } void VM_Verify::doit() { - Universe::verify(); + Universe::heap()->prepare_for_verify(); + Universe::verify(_silent); } bool VM_PrintThreads::doit_prologue() {
--- a/src/share/vm/runtime/vm_operations.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/runtime/vm_operations.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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(); };
--- a/src/share/vm/services/management.cpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/services/management.cpp Fri Jan 31 10:53:59 2014 -0800 @@ -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.
--- a/src/share/vm/utilities/growableArray.hpp Wed Jan 22 12:33:48 2014 -0800 +++ b/src/share/vm/utilities/growableArray.hpp Fri Jan 31 10:53:59 2014 -0800 @@ -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;
--- a/test/gc/TestVerifyBeforeGCDuringStartup.java Wed Jan 22 12:33:48 2014 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/* - * 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 TestVerifyBeforeGCDuringStartup.java - * @key gc - * @bug 8010463 - * @summary Simple test run with -XX:+VerifyBeforeGC -XX:-UseTLAB to verify 8010463 - * @library /testlibrary - */ - -import com.oracle.java.testlibrary.OutputAnalyzer; -import com.oracle.java.testlibrary.ProcessTools; - -public class TestVerifyBeforeGCDuringStartup { - public static void main(String args[]) throws Exception { - ProcessBuilder pb = - ProcessTools.createJavaProcessBuilder(System.getProperty("test.vm.opts"), - "-XX:-UseTLAB", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+VerifyBeforeGC", "-version"); - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldContain("[Verifying"); - output.shouldHaveExitValue(0); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/TestVerifyDuringStartup.java Fri Jan 31 10:53:59 2014 -0800 @@ -0,0 +1,45 @@ +/* + * 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 TestVerifyDuringStartup.java + * @key gc + * @bug 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 TestVerifyDuringStartup { + public static void main(String args[]) throws Exception { + ProcessBuilder pb = + ProcessTools.createJavaProcessBuilder(System.getProperty("test.vm.opts"), + "-XX:-UseTLAB", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+VerifyDuringStartup", "-version"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("[Verifying"); + output.shouldHaveExitValue(0); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/TestVerifySilently.java Fri Jan 31 10:53:59 2014 -0800 @@ -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); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestHumongousCodeCacheRoots.java Fri Jan 31 10:53:59 2014 -0800 @@ -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); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestPrintRegionRememberedSetInfo.java Fri Jan 31 10:53:59 2014 -0800 @@ -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."); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestSummarizeRSetStats.java Fri Jan 31 10:53:59 2014 -0800 @@ -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); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestSummarizeRSetStatsPerRegion.java Fri Jan 31 10:53:59 2014 -0800 @@ -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); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestSummarizeRSetStatsThreads.java Fri Jan 31 10:53:59 2014 -0800 @@ -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); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestSummarizeRSetStatsTools.java Fri Jan 31 10:53:59 2014 -0800 @@ -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)); + } + } +} +
--- a/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Wed Jan 22 12:33:48 2014 -0800 +++ b/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Fri Jan 31 10:53:59 2014 -0800 @@ -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(); }