# HG changeset patch # User jiangli # Date 1440537776 14400 # Node ID 522260f7f54cf98ca33d3aa05d3f410dc57f071b # Parent 09094287d06fa9cc9d80afe40eb5f78259a98adb 8131734: assert(!is_null(v)) failed: narrow klass value can never be zero with -Xshared:auto Summary: Handle shared string mapping failures. Reviewed-by: tschatzl, kbarrett, ddmitriev, hseigel Contributed-by: tom.benson@oracle.com, jiangli.zhou@oracle.com diff -r 09094287d06f -r 522260f7f54c src/share/vm/gc/g1/g1Allocator.cpp --- a/src/share/vm/gc/g1/g1Allocator.cpp Tue Aug 25 21:05:26 2015 +0200 +++ b/src/share/vm/gc/g1/g1Allocator.cpp Tue Aug 25 17:22:56 2015 -0400 @@ -367,7 +367,7 @@ _max = _bottom + HeapRegion::min_region_size_in_words(); // Tell mark-sweep that objects in this region are not to be marked. - G1MarkSweep::mark_range_archive(MemRegion(_bottom, HeapRegion::GrainWords)); + G1MarkSweep::set_range_archive(MemRegion(_bottom, HeapRegion::GrainWords), true); // Since we've modified the old set, call update_sizes. _g1h->g1mm()->update_sizes(); diff -r 09094287d06f -r 522260f7f54c src/share/vm/gc/g1/g1CollectedHeap.cpp --- a/src/share/vm/gc/g1/g1CollectedHeap.cpp Tue Aug 25 21:05:26 2015 +0200 +++ b/src/share/vm/gc/g1/g1CollectedHeap.cpp Tue Aug 25 17:22:56 2015 -0400 @@ -65,6 +65,7 @@ #include "memory/iterator.hpp" #include "oops/oop.inline.hpp" #include "runtime/atomic.inline.hpp" +#include "runtime/init.hpp" #include "runtime/orderAccess.inline.hpp" #include "runtime/vmThread.hpp" #include "utilities/globalDefinitions.hpp" @@ -949,6 +950,7 @@ } bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, size_t count) { + assert(!is_init_completed(), "Expect to be called at JVM init time"); assert(ranges != NULL, "MemRegion array NULL"); assert(count != 0, "No MemRegions provided"); MutexLockerEx x(Heap_lock); @@ -1037,12 +1039,13 @@ } // Notify mark-sweep of the archive range. - G1MarkSweep::mark_range_archive(curr_range); + G1MarkSweep::set_range_archive(curr_range, true); } return true; } void G1CollectedHeap::fill_archive_regions(MemRegion* ranges, size_t count) { + assert(!is_init_completed(), "Expect to be called at JVM init time"); assert(ranges != NULL, "MemRegion array NULL"); assert(count != 0, "No MemRegions provided"); MemRegion reserved = _hrm.reserved(); @@ -1125,6 +1128,81 @@ return result; } +void G1CollectedHeap::dealloc_archive_regions(MemRegion* ranges, size_t count) { + assert(!is_init_completed(), "Expect to be called at JVM init time"); + assert(ranges != NULL, "MemRegion array NULL"); + assert(count != 0, "No MemRegions provided"); + MemRegion reserved = _hrm.reserved(); + HeapWord* prev_last_addr = NULL; + HeapRegion* prev_last_region = NULL; + size_t size_used = 0; + size_t uncommitted_regions = 0; + + // For each Memregion, free the G1 regions that constitute it, and + // notify mark-sweep that the range is no longer to be considered 'archive.' + MutexLockerEx x(Heap_lock); + for (size_t i = 0; i < count; i++) { + HeapWord* start_address = ranges[i].start(); + HeapWord* last_address = ranges[i].last(); + + assert(reserved.contains(start_address) && reserved.contains(last_address), + err_msg("MemRegion outside of heap [" PTR_FORMAT ", " PTR_FORMAT "]", + p2i(start_address), p2i(last_address))); + assert(start_address > prev_last_addr, + err_msg("Ranges not in ascending order: " PTR_FORMAT " <= " PTR_FORMAT , + p2i(start_address), p2i(prev_last_addr))); + size_used += ranges[i].byte_size(); + prev_last_addr = last_address; + + HeapRegion* start_region = _hrm.addr_to_region(start_address); + HeapRegion* last_region = _hrm.addr_to_region(last_address); + + // Check for ranges that start in the same G1 region in which the previous + // range ended, and adjust the start address so we don't try to free + // the same region again. If the current range is entirely within that + // region, skip it. + if (start_region == prev_last_region) { + start_address = start_region->end(); + if (start_address > last_address) { + continue; + } + start_region = _hrm.addr_to_region(start_address); + } + prev_last_region = last_region; + + // After verifying that each region was marked as an archive region by + // alloc_archive_regions, set it free and empty and uncommit it. + HeapRegion* curr_region = start_region; + while (curr_region != NULL) { + guarantee(curr_region->is_archive(), + err_msg("Expected archive region at index %u", curr_region->hrm_index())); + uint curr_index = curr_region->hrm_index(); + _old_set.remove(curr_region); + curr_region->set_free(); + curr_region->set_top(curr_region->bottom()); + if (curr_region != last_region) { + curr_region = _hrm.next_region_in_heap(curr_region); + } else { + curr_region = NULL; + } + _hrm.shrink_at(curr_index, 1); + uncommitted_regions++; + } + + // Notify mark-sweep that this is no longer an archive range. + G1MarkSweep::set_range_archive(ranges[i], false); + } + + if (uncommitted_regions != 0) { + ergo_verbose1(ErgoHeapSizing, + "attempt heap shrinking", + ergo_format_reason("uncommitted archive regions") + ergo_format_byte("total size"), + HeapRegion::GrainWords * HeapWordSize * uncommitted_regions); + } + decrease_used(size_used); +} + HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size, uint* gc_count_before_ret, uint* gclocker_retry_count_ret) { diff -r 09094287d06f -r 522260f7f54c src/share/vm/gc/g1/g1CollectedHeap.hpp --- a/src/share/vm/gc/g1/g1CollectedHeap.hpp Tue Aug 25 21:05:26 2015 +0200 +++ b/src/share/vm/gc/g1/g1CollectedHeap.hpp Tue Aug 25 17:22:56 2015 -0400 @@ -757,6 +757,12 @@ // alloc_archive_regions, and after class loading has occurred. void fill_archive_regions(MemRegion* range, size_t count); + // For each of the specified MemRegions, uncommit the containing G1 regions + // which had been allocated by alloc_archive_regions. This should be called + // rather than fill_archive_regions at JVM init time if the archive file + // mapping failed, with the same non-overlapping and sorted MemRegion array. + void dealloc_archive_regions(MemRegion* range, size_t count); + protected: // Shrink the garbage-first heap by at most the given size (in bytes!). diff -r 09094287d06f -r 522260f7f54c src/share/vm/gc/g1/g1MarkSweep.cpp --- a/src/share/vm/gc/g1/g1MarkSweep.cpp Tue Aug 25 21:05:26 2015 +0200 +++ b/src/share/vm/gc/g1/g1MarkSweep.cpp Tue Aug 25 17:22:56 2015 -0400 @@ -310,9 +310,9 @@ HeapRegion::GrainBytes); } -void G1MarkSweep::mark_range_archive(MemRegion range) { +void G1MarkSweep::set_range_archive(MemRegion range, bool is_archive) { assert(_archive_check_enabled, "archive range check not enabled"); - _archive_region_map.set_by_address(range, true); + _archive_region_map.set_by_address(range, is_archive); } bool G1MarkSweep::in_archive_range(oop object) { diff -r 09094287d06f -r 522260f7f54c src/share/vm/gc/g1/g1MarkSweep.hpp --- a/src/share/vm/gc/g1/g1MarkSweep.hpp Tue Aug 25 21:05:26 2015 +0200 +++ b/src/share/vm/gc/g1/g1MarkSweep.hpp Tue Aug 25 17:22:56 2015 -0400 @@ -58,8 +58,8 @@ // Create the _archive_region_map which is used to identify archive objects. static void enable_archive_object_check(); - // Mark the regions containing the specified address range as archive regions. - static void mark_range_archive(MemRegion range); + // Set the regions containing the specified address range as archive/non-archive. + static void set_range_archive(MemRegion range, bool is_archive); // Check if an object is in an archive region using the _archive_region_map. static bool in_archive_range(oop object); diff -r 09094287d06f -r 522260f7f54c src/share/vm/gc/g1/heapRegionManager.cpp --- a/src/share/vm/gc/g1/heapRegionManager.cpp Tue Aug 25 21:05:26 2015 +0200 +++ b/src/share/vm/gc/g1/heapRegionManager.cpp Tue Aug 25 17:22:56 2015 -0400 @@ -426,7 +426,7 @@ (num_last_found = find_empty_from_idx_reverse(cur, &idx_last_found)) > 0) { uint to_remove = MIN2(num_regions_to_remove - removed, num_last_found); - uncommit_regions(idx_last_found + num_last_found - to_remove, to_remove); + shrink_at(idx_last_found + num_last_found - to_remove, to_remove); cur = idx_last_found; removed += to_remove; @@ -437,6 +437,17 @@ return removed; } +void HeapRegionManager::shrink_at(uint index, size_t num_regions) { +#ifdef ASSERT + for (uint i = index; i < (index + num_regions); i++) { + assert(is_available(i), err_msg("Expected available region at index %u", i)); + assert(at(i)->is_empty(), err_msg("Expected empty region at index %u", i)); + assert(at(i)->is_free(), err_msg("Expected free region at index %u", i)); + } +#endif + uncommit_regions(index, num_regions); +} + uint HeapRegionManager::find_empty_from_idx_reverse(uint start_idx, uint* res_idx) const { guarantee(start_idx < _allocated_heapregions_length, "checking"); guarantee(res_idx != NULL, "checking"); diff -r 09094287d06f -r 522260f7f54c src/share/vm/gc/g1/heapRegionManager.hpp --- a/src/share/vm/gc/g1/heapRegionManager.hpp Tue Aug 25 21:05:26 2015 +0200 +++ b/src/share/vm/gc/g1/heapRegionManager.hpp Tue Aug 25 17:22:56 2015 -0400 @@ -241,6 +241,10 @@ // Return the actual number of uncommitted regions. uint shrink_by(uint num_regions_to_remove); + // Uncommit a number of regions starting at the specified index, which must be available, + // empty, and free. + void shrink_at(uint index, size_t num_regions); + void verify(); // Do some sanity checking. diff -r 09094287d06f -r 522260f7f54c src/share/vm/memory/filemap.cpp --- a/src/share/vm/memory/filemap.cpp Tue Aug 25 21:05:26 2015 +0200 +++ b/src/share/vm/memory/filemap.cpp Tue Aug 25 17:22:56 2015 -0400 @@ -707,12 +707,16 @@ addr, string_ranges[i].byte_size(), si->_read_only, si->_allow_exec); if (base == NULL || base != addr) { + // dealloc the string regions from java heap + dealloc_string_regions(); fail_continue("Unable to map shared string space at required address."); return false; } } if (!verify_string_regions()) { + // dealloc the string regions from java heap + dealloc_string_regions(); fail_continue("Shared string regions are corrupt"); return false; } @@ -745,12 +749,14 @@ } void FileMapInfo::fixup_string_regions() { +#if INCLUDE_ALL_GCS // If any string regions were found, call the fill routine to make them parseable. // Note that string_ranges may be non-NULL even if no ranges were found. if (num_ranges != 0) { assert(string_ranges != NULL, "Null string_ranges array with non-zero count"); G1CollectedHeap::heap()->fill_archive_regions(string_ranges, num_ranges); } +#endif } bool FileMapInfo::verify_region_checksum(int i) { @@ -793,20 +799,14 @@ } } -void FileMapInfo::unmap_string_regions() { - for (int i = MetaspaceShared::first_string; - i < MetaspaceShared::first_string + MetaspaceShared::max_strings; i++) { - struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; - size_t used = si->_used; - if (used > 0) { - size_t size = align_size_up(used, os::vm_allocation_granularity()); - char* addr = (char*)((void*)oopDesc::decode_heap_oop_not_null( - (narrowOop)si->_addr._offset)); - if (!os::unmap_memory(addr, size)) { - fail_stop("Unable to unmap shared space."); - } - } +// dealloc the archived string region from java heap +void FileMapInfo::dealloc_string_regions() { +#if INCLUDE_ALL_GCS + if (num_ranges > 0) { + assert(string_ranges != NULL, "Null string_ranges array with non-zero count"); + G1CollectedHeap::heap()->dealloc_archive_regions(string_ranges, num_ranges); } +#endif } void FileMapInfo::assert_mark(bool check) { @@ -967,7 +967,9 @@ map_info->_header->_space[i]._addr._base = NULL; } } - map_info->unmap_string_regions(); + // Dealloc the string regions only without unmapping. The string regions are part + // of the java heap. Unmapping of the heap regions are managed by GC. + map_info->dealloc_string_regions(); } else if (DumpSharedSpaces) { fail_stop("%s", msg); } diff -r 09094287d06f -r 522260f7f54c src/share/vm/memory/filemap.hpp --- a/src/share/vm/memory/filemap.hpp Tue Aug 25 21:05:26 2015 +0200 +++ b/src/share/vm/memory/filemap.hpp Tue Aug 25 17:22:56 2015 -0400 @@ -208,7 +208,7 @@ bool verify_string_regions(); void fixup_string_regions(); void unmap_region(int i); - void unmap_string_regions(); + void dealloc_string_regions(); bool verify_region_checksum(int i); void close(); bool is_open() { return _file_open; } diff -r 09094287d06f -r 522260f7f54c test/runtime/SharedArchiveFile/SharedStringsRunAuto.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/SharedArchiveFile/SharedStringsRunAuto.java Tue Aug 25 17:22:56 2015 -0400 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test SharedStringsAuto + * @summary Test -Xshare:auto with shared strings. + * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (vm.gc=="G1" | vm.gc=="null") + * @library /testlibrary + * @modules java.base/sun.misc + * java.management + * @run main SharedStringsRunAuto + */ + +import jdk.test.lib.*; +import java.io.File; + +public class SharedStringsRunAuto { + public static void main(String[] args) throws Exception { + // Dump + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:SharedArchiveFile=./SharedStringsRunAuto.jsa", + "-XX:+UseCompressedOops", "-XX:+UseG1GC", + "-XX:+PrintSharedSpaces", + "-Xshare:dump"); + + new OutputAnalyzer(pb.start()) + .shouldContain("Loading classes to share") + .shouldContain("Shared string table stats") + .shouldHaveExitValue(0); + + // Run with -Xshare:auto + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:SharedArchiveFile=./SharedStringsRunAuto.jsa", + "-XX:+UseCompressedOops", "-XX:+UseG1GC", + "-Xshare:auto", + "-version"); + + new OutputAnalyzer(pb.start()) + .shouldMatch("(java|openjdk) version") + .shouldHaveExitValue(0); + } +}