Skip to content

Commit

Permalink
8255917: runtime/cds/SharedBaseAddress.java failed "assert(reserved_r…
Browse files Browse the repository at this point in the history
…gn != 0LL) failed: No reserved region"

Reviewed-by: ccheung, iklam, stuefe
  • Loading branch information
yminqi committed Dec 15, 2020
1 parent d53ee62 commit 36e2097
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 47 deletions.
4 changes: 4 additions & 0 deletions src/hotspot/share/memory/filemap.cpp
Expand Up @@ -1680,6 +1680,10 @@ char* FileMapInfo::map_bitmap_region() {

si->set_mapped_base(bitmap_base);
si->set_mapped_from_file(true);
log_info(cds)("Mapped %s region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)",
is_static() ? "static " : "dynamic",
MetaspaceShared::bm, p2i(si->mapped_base()), p2i(si->mapped_end()),
shared_region_name[MetaspaceShared::bm]);
return bitmap_base;
}

Expand Down
110 changes: 73 additions & 37 deletions src/hotspot/share/memory/metaspaceShared.cpp
Expand Up @@ -1369,10 +1369,13 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
assert(static_mapinfo->mapping_end_offset() == dynamic_mapinfo->mapping_base_offset(), "no gap");
}

ReservedSpace archive_space_rs, class_space_rs;
ReservedSpace total_space_rs, archive_space_rs, class_space_rs;
MapArchiveResult result = MAP_ARCHIVE_OTHER_FAILURE;
char* mapped_base_address = reserve_address_space_for_archives(static_mapinfo, dynamic_mapinfo,
use_requested_addr, archive_space_rs,
char* mapped_base_address = reserve_address_space_for_archives(static_mapinfo,
dynamic_mapinfo,
use_requested_addr,
total_space_rs,
archive_space_rs,
class_space_rs);
if (mapped_base_address == NULL) {
result = MAP_ARCHIVE_MMAP_FAILURE;
Expand Down Expand Up @@ -1422,6 +1425,7 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
// this with use_requested_addr, since we're going to patch all the
// pointers anyway so there's no benefit to mmap.
if (use_requested_addr) {
assert(!total_space_rs.is_reserved(), "Should not be reserved for Windows");
log_info(cds)("Windows mmap workaround: releasing archive space.");
archive_space_rs.release();
}
Expand Down Expand Up @@ -1477,6 +1481,7 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
// cover both archive and class space.
address cds_base = (address)static_mapinfo->mapped_base();
address ccs_end = (address)class_space_rs.end();
assert(ccs_end > cds_base, "Sanity check");
CompressedKlassPointers::initialize(cds_base, ccs_end - cds_base);

// map_heap_regions() compares the current narrow oop and klass encodings
Expand All @@ -1489,7 +1494,7 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
} else {
unmap_archive(static_mapinfo);
unmap_archive(dynamic_mapinfo);
release_reserved_spaces(archive_space_rs, class_space_rs);
release_reserved_spaces(total_space_rs, archive_space_rs, class_space_rs);
}

return result;
Expand Down Expand Up @@ -1538,6 +1543,10 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
// Return:
//
// - On success:
// - total_space_rs will be reserved as whole for archive_space_rs and
// class_space_rs if UseCompressedClassPointers is true.
// On Windows, try reserve archive_space_rs and class_space_rs
// separately first if use_archive_base_addr is true.
// - archive_space_rs will be reserved and large enough to host static and
// if needed dynamic archive: [Base, A).
// archive_space_rs.base and size will be aligned to CDS reserve
Expand All @@ -1552,6 +1561,7 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
char* MetaspaceShared::reserve_address_space_for_archives(FileMapInfo* static_mapinfo,
FileMapInfo* dynamic_mapinfo,
bool use_archive_base_addr,
ReservedSpace& total_space_rs,
ReservedSpace& archive_space_rs,
ReservedSpace& class_space_rs) {

Expand Down Expand Up @@ -1617,34 +1627,53 @@ char* MetaspaceShared::reserve_address_space_for_archives(FileMapInfo* static_ma
align_up(archive_space_size + gap_size + class_space_size,
os::vm_allocation_granularity());

ReservedSpace total_rs;
if (base_address != NULL) {
// Reserve at the given archive base address, or not at all.
total_rs = ReservedSpace(total_range_size, archive_space_alignment,
false /* bool large */, (char*) base_address);
assert(total_range_size > ccs_begin_offset, "must be");
if (use_windows_memory_mapping() && use_archive_base_addr) {
if (base_address != nullptr) {
// On Windows, we cannot safely split a reserved memory space into two (see JDK-8255917).
// Hence, we optimistically reserve archive space and class space side-by-side. We only
// do this for use_archive_base_addr=true since for use_archive_base_addr=false case
// caller will not split the combined space for mapping, instead read the archive data
// via sequential file IO.
address ccs_base = base_address + archive_space_size + gap_size;
archive_space_rs = ReservedSpace(archive_space_size, archive_space_alignment,
false /* large */, (char*)base_address);
class_space_rs = ReservedSpace(class_space_size, class_space_alignment,
false /* large */, (char*)ccs_base);
}
if (!archive_space_rs.is_reserved() || !class_space_rs.is_reserved()) {
release_reserved_spaces(total_space_rs, archive_space_rs, class_space_rs);
return NULL;
}
} else {
// Reserve at any address, but leave it up to the platform to choose a good one.
total_rs = Metaspace::reserve_address_space_for_compressed_classes(total_range_size);
}

if (!total_rs.is_reserved()) {
return NULL;
}

// Paranoid checks:
assert(base_address == NULL || (address)total_rs.base() == base_address,
"Sanity (" PTR_FORMAT " vs " PTR_FORMAT ")", p2i(base_address), p2i(total_rs.base()));
assert(is_aligned(total_rs.base(), archive_space_alignment), "Sanity");
assert(total_rs.size() == total_range_size, "Sanity");
assert(CompressedKlassPointers::is_valid_base((address)total_rs.base()), "Sanity");
if (use_archive_base_addr && base_address != nullptr) {
total_space_rs = ReservedSpace(total_range_size, archive_space_alignment,
false /* bool large */, (char*) base_address);
} else {
// Reserve at any address, but leave it up to the platform to choose a good one.
total_space_rs = Metaspace::reserve_address_space_for_compressed_classes(total_range_size);
}

// Now split up the space into ccs and cds archive. For simplicity, just leave
// the gap reserved at the end of the archive space.
archive_space_rs = total_rs.first_part(ccs_begin_offset,
(size_t)os::vm_allocation_granularity(),
/*split=*/true);
class_space_rs = total_rs.last_part(ccs_begin_offset);
if (!total_space_rs.is_reserved()) {
return NULL;
}

// Paranoid checks:
assert(base_address == NULL || (address)total_space_rs.base() == base_address,
"Sanity (" PTR_FORMAT " vs " PTR_FORMAT ")", p2i(base_address), p2i(total_space_rs.base()));
assert(is_aligned(total_space_rs.base(), archive_space_alignment), "Sanity");
assert(total_space_rs.size() == total_range_size, "Sanity");
assert(CompressedKlassPointers::is_valid_base((address)total_space_rs.base()), "Sanity");

// Now split up the space into ccs and cds archive. For simplicity, just leave
// the gap reserved at the end of the archive space. Do not do real splitting.
archive_space_rs = total_space_rs.first_part(ccs_begin_offset,
(size_t)os::vm_allocation_granularity(),
/*split=*/false);
class_space_rs = total_space_rs.last_part(ccs_begin_offset);
MemTracker::record_virtual_memory_split_reserved(total_space_rs.base(), total_space_rs.size(),
ccs_begin_offset);
}
assert(is_aligned(archive_space_rs.base(), archive_space_alignment), "Sanity");
assert(is_aligned(archive_space_rs.size(), archive_space_alignment), "Sanity");
assert(is_aligned(class_space_rs.base(), class_space_alignment), "Sanity");
Expand All @@ -1663,15 +1692,21 @@ char* MetaspaceShared::reserve_address_space_for_archives(FileMapInfo* static_ma

}

void MetaspaceShared::release_reserved_spaces(ReservedSpace& archive_space_rs,
void MetaspaceShared::release_reserved_spaces(ReservedSpace& total_space_rs,
ReservedSpace& archive_space_rs,
ReservedSpace& class_space_rs) {
if (archive_space_rs.is_reserved()) {
log_debug(cds)("Released shared space (archive) " INTPTR_FORMAT, p2i(archive_space_rs.base()));
archive_space_rs.release();
}
if (class_space_rs.is_reserved()) {
log_debug(cds)("Released shared space (classes) " INTPTR_FORMAT, p2i(class_space_rs.base()));
class_space_rs.release();
if (total_space_rs.is_reserved()) {
log_debug(cds)("Released shared space (archive + class) " INTPTR_FORMAT, p2i(total_space_rs.base()));
total_space_rs.release();
} else {
if (archive_space_rs.is_reserved()) {
log_debug(cds)("Released shared space (archive) " INTPTR_FORMAT, p2i(archive_space_rs.base()));
archive_space_rs.release();
}
if (class_space_rs.is_reserved()) {
log_debug(cds)("Released shared space (classes) " INTPTR_FORMAT, p2i(class_space_rs.base()));
class_space_rs.release();
}
}
}

Expand Down Expand Up @@ -1715,6 +1750,7 @@ void MetaspaceShared::unmap_archive(FileMapInfo* mapinfo) {
assert(UseSharedSpaces, "must be runtime");
if (mapinfo != NULL) {
mapinfo->unmap_regions(archive_regions, archive_regions_count);
mapinfo->unmap_region(MetaspaceShared::bm);
mapinfo->set_is_mapped(false);
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/hotspot/share/memory/metaspaceShared.hpp
Expand Up @@ -288,10 +288,12 @@ class MetaspaceShared : AllStatic {
static char* reserve_address_space_for_archives(FileMapInfo* static_mapinfo,
FileMapInfo* dynamic_mapinfo,
bool use_archive_base_addr,
ReservedSpace& total_space_rs,
ReservedSpace& archive_space_rs,
ReservedSpace& class_space_rs);
static void release_reserved_spaces(ReservedSpace& archive_space_rs,
ReservedSpace& class_space_rs);
static void release_reserved_spaces(ReservedSpace& total_space_rs,
ReservedSpace& archive_space_rs,
ReservedSpace& class_space_rs);
static MapArchiveResult map_archive(FileMapInfo* mapinfo, char* mapped_base_address, ReservedSpace rs);
static void unmap_archive(FileMapInfo* mapinfo);
};
Expand Down
3 changes: 3 additions & 0 deletions src/hotspot/share/runtime/os.cpp
Expand Up @@ -1723,6 +1723,9 @@ bool os::release_memory(char* addr, size_t bytes) {
} else {
res = pd_release_memory(addr, bytes);
}
if (!res) {
log_info(os)("os::release_memory failed (" PTR_FORMAT ", " SIZE_FORMAT ")", p2i(addr), bytes);
}
return res;
}

Expand Down
28 changes: 21 additions & 7 deletions src/hotspot/share/services/virtualMemoryTracker.cpp
Expand Up @@ -444,7 +444,7 @@ bool VirtualMemoryTracker::add_committed_region(address addr, size_t size,
assert(reserved_rgn->contain_region(addr, size), "Not completely contained");
bool result = reserved_rgn->add_committed_region(addr, size, stack);
log_debug(nmt)("Add committed region \'%s\'(" INTPTR_FORMAT ", " SIZE_FORMAT ") %s",
rgn.flag_name(), p2i(rgn.base()), rgn.size(), (result ? "Succeeded" : "Failed"));
reserved_rgn->flag_name(), p2i(rgn.base()), rgn.size(), (result ? "Succeeded" : "Failed"));
return result;
}

Expand Down Expand Up @@ -506,12 +506,26 @@ bool VirtualMemoryTracker::remove_released_region(address addr, size_t size) {
return false;
}

if (reserved_rgn->flag() == mtClassShared &&
reserved_rgn->contain_region(addr, size)) {
// This is an unmapped CDS region, which is part of the reserved shared
// memory region.
// See special handling in VirtualMemoryTracker::add_reserved_region also.
return true;
if (reserved_rgn->flag() == mtClassShared) {
if (reserved_rgn->contain_region(addr, size)) {
// This is an unmapped CDS region, which is part of the reserved shared
// memory region.
// See special handling in VirtualMemoryTracker::add_reserved_region also.
return true;
}

if (size > reserved_rgn->size()) {
// This is from release the whole region spanning from archive space to class space,
// so we release them altogether.
ReservedMemoryRegion class_rgn(addr + reserved_rgn->size(),
(size - reserved_rgn->size()));
ReservedMemoryRegion* cls_rgn = _reserved_regions->find(class_rgn);
assert(cls_rgn != NULL, "Class space region not recorded?");
assert(cls_rgn->flag() == mtClass, "Must be class type");
remove_released_region(reserved_rgn);
remove_released_region(cls_rgn);
return true;
}
}

VirtualMemorySummary::record_released_memory(size, reserved_rgn->flag());
Expand Down
1 change: 1 addition & 0 deletions test/hotspot/jtreg/TEST.groups
Expand Up @@ -338,6 +338,7 @@ hotspot_appcds_dynamic = \
-runtime/cds/appcds/LambdaProxyClasslist.java \
-runtime/cds/appcds/LongClassListPath.java \
-runtime/cds/appcds/LotsOfClasses.java \
-runtime/cds/appcds/MismatchedPathTriggerMemoryRelease.java \
-runtime/cds/appcds/NonExistClasspath.java \
-runtime/cds/appcds/RelativePath.java \
-runtime/cds/appcds/SharedArchiveConsistency.java \
Expand Down
6 changes: 5 additions & 1 deletion test/hotspot/jtreg/runtime/cds/SharedBaseAddress.java
Expand Up @@ -50,6 +50,9 @@ public class SharedBaseAddress {
"0", // always let OS pick the base address at runtime (ASLR for CDS archive)
};

// failed pattern
private static String failedPattern = "os::release_memory\\(0x[0-9a-fA-F]*,\\s[0-9]*\\)\\sfailed";

public static void main(String[] args) throws Exception {

for (String testEntry : testTable) {
Expand All @@ -68,7 +71,8 @@ public static void main(String[] args) throws Exception {
OutputAnalyzer out = CDSTestUtils.runWithArchiveAndCheck(opts);
if (testEntry.equals("0")) {
out.shouldContain("Archive(s) were created with -XX:SharedBaseAddress=0. Always map at os-selected address.")
.shouldContain("Try to map archive(s) at an alternative address");
.shouldContain("Try to map archive(s) at an alternative address")
.shouldNotMatch(failedPattern);
}
}
}
Expand Down
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2020, 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 MismatchedPathTriggerMemoryRelease
* @summary Mismatched path at runtime will cause reserved memory released
* @requires vm.cds
* @library /test/lib
* @compile test-classes/Hello.java
* @run main MismatchedPathTriggerMemoryRelease
*/

import jdk.test.lib.process.OutputAnalyzer;

public class MismatchedPathTriggerMemoryRelease {
private static String ERR_MSGS[] = {
"UseSharedSpaces: shared class paths mismatch (hint: enable -Xlog:class+path=info to diagnose the failure)",
"UseSharedSpaces: Unable to map shared spaces"};
private static String RELEASE_SPACE_MATCH =
"Released shared space\\s(\\(archive\\s*\\+\\s*class\\) | ?)0(x|X)[0-9a-fA-F]+$";
private static String OS_RELEASE_MSG = "os::release_memory failed";

public static void main(String[] args) throws Exception {
String appJar = JarBuilder.getOrCreateHelloJar();

OutputAnalyzer dumpOutput = TestCommon.dump(
appJar, new String[] {"Hello"}, "-XX:SharedBaseAddress=0");
TestCommon.checkDump(dumpOutput, "Loading classes to share");

// Normal exit
OutputAnalyzer execOutput = TestCommon.exec(appJar, "Hello");
TestCommon.checkExec(execOutput, "Hello World");

// mismatched jar
execOutput = TestCommon.exec("non-exist.jar",
"-Xshare:auto",
"-Xlog:os,cds=debug",
"-XX:NativeMemoryTracking=detail",
"-XX:SharedBaseAddress=0",
"Hello");
execOutput.shouldHaveExitValue(1);
for (String err : ERR_MSGS) {
execOutput.shouldContain(err);
}
execOutput.shouldMatch(RELEASE_SPACE_MATCH);
execOutput.shouldNotContain(OS_RELEASE_MSG); // os::release only log release failed message
}
}

1 comment on commit 36e2097

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.