Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/hotspot/share/memory/filemap.cpp
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -1364,10 +1364,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 @@ -1417,6 +1420,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 @@ -1472,6 +1476,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 @@ -1484,7 +1489,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 @@ -1533,6 +1538,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 @@ -1547,6 +1556,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 @@ -1612,34 +1622,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 @@ -1658,15 +1687,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 @@ -1710,6 +1745,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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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");
Comment on lines +56 to +61
Copy link
Member

Choose a reason for hiding this comment

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

Instead of TestCommon.exec, you could use TestCommon.execAuto and no need to pass the -Xshare:auto argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed to execAuto, remove "-Xshare:auto", it failed on "Error: Could not find or load main class non-exist.jar" so i will keep original version.

Copy link
Member

@calvinccheung calvinccheung Dec 14, 2020

Choose a reason for hiding this comment

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

I think you'll need to add "-cp" before "non-exist.jar" for execAuto to work.
I'm fine to leave it as is.

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
}
}