Skip to content

Commit f626ed6

Browse files
committed
8255978: [windows] os::release_memory may not release the full range
Reviewed-by: iklam, minqi
1 parent 6702910 commit f626ed6

File tree

7 files changed

+491
-1
lines changed

7 files changed

+491
-1
lines changed

src/hotspot/os/aix/os_aix.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3290,3 +3290,6 @@ int os::compare_file_modified_times(const char* file1, const char* file2) {
32903290
bool os::supports_map_sync() {
32913291
return false;
32923292
}
3293+
3294+
void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) {}
3295+

src/hotspot/os/bsd/os_bsd.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2786,3 +2786,6 @@ bool os::start_debugging(char *buf, int buflen) {
27862786
}
27872787
return yes;
27882788
}
2789+
2790+
void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) {}
2791+

src/hotspot/os/linux/os_linux.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5485,6 +5485,35 @@ bool os::supports_map_sync() {
54855485
return true;
54865486
}
54875487

5488+
void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) {
5489+
unsigned long long start = (unsigned long long)addr;
5490+
unsigned long long end = start + bytes;
5491+
FILE* f = ::fopen("/proc/self/maps", "r");
5492+
int num_found = 0;
5493+
if (f != NULL) {
5494+
st->print("Range [%llx-%llx) contains: ", start, end);
5495+
char line[512];
5496+
while(fgets(line, sizeof(line), f) == line) {
5497+
unsigned long long a1 = 0;
5498+
unsigned long long a2 = 0;
5499+
if (::sscanf(line, "%llx-%llx", &a1, &a2) == 2) {
5500+
// Lets print out every range which touches ours.
5501+
if ((a1 >= start && a1 < end) || // left leg in
5502+
(a2 >= start && a2 < end) || // right leg in
5503+
(a1 < start && a2 >= end)) { // superimposition
5504+
num_found ++;
5505+
st->print("%s", line); // line includes \n
5506+
}
5507+
}
5508+
}
5509+
::fclose(f);
5510+
if (num_found == 0) {
5511+
st->print("nothing.");
5512+
}
5513+
st->cr();
5514+
}
5515+
}
5516+
54885517
/////////////// Unit tests ///////////////
54895518

54905519
#ifndef PRODUCT

src/hotspot/os/windows/os_windows.cpp

Lines changed: 199 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3487,7 +3487,57 @@ bool os::pd_uncommit_memory(char* addr, size_t bytes) {
34873487
}
34883488

34893489
bool os::pd_release_memory(char* addr, size_t bytes) {
3490-
return virtualFree(addr, 0, MEM_RELEASE) != 0;
3490+
// Given a range we are to release, we require a mapping to start at the beginning of that range;
3491+
// if NUMA or LP we allow the range to contain multiple mappings, which have to cover the range
3492+
// completely; otherwise the range must match an OS mapping exactly.
3493+
address start = (address)addr;
3494+
address end = start + bytes;
3495+
os::win32::mapping_info_t mi;
3496+
const bool multiple_mappings_allowed = UseLargePagesIndividualAllocation || UseNUMAInterleaving;
3497+
address p = start;
3498+
bool first_mapping = true;
3499+
3500+
do {
3501+
// Find mapping and check it
3502+
const char* err = NULL;
3503+
if (!os::win32::find_mapping(p, &mi)) {
3504+
err = "no mapping found";
3505+
} else {
3506+
if (first_mapping) {
3507+
if (mi.base != start) {
3508+
err = "base address mismatch";
3509+
}
3510+
if (multiple_mappings_allowed ? (mi.size > bytes) : (mi.size != bytes)) {
3511+
err = "size mismatch";
3512+
}
3513+
} else {
3514+
assert(p == mi.base && mi.size > 0, "Sanity");
3515+
if (mi.base + mi.size > end) {
3516+
err = "mapping overlaps end";
3517+
}
3518+
if (mi.size == 0) {
3519+
err = "zero length mapping?"; // Should never happen; just to prevent endlessly looping in release.
3520+
}
3521+
}
3522+
}
3523+
// Handle mapping error. We assert in debug, unconditionally print a warning in release.
3524+
if (err != NULL) {
3525+
log_warning(os)("bad release: [" PTR_FORMAT "-" PTR_FORMAT "): %s", p2i(start), p2i(end), err);
3526+
#ifdef ASSERT
3527+
os::print_memory_mappings((char*)start, bytes, tty);
3528+
assert(false, "bad release: [" PTR_FORMAT "-" PTR_FORMAT "): %s", p2i(start), p2i(end), err);
3529+
#endif
3530+
return false;
3531+
}
3532+
// Free this range
3533+
if (virtualFree(p, 0, MEM_RELEASE) == FALSE) {
3534+
return false;
3535+
}
3536+
first_mapping = false;
3537+
p = mi.base + mi.size;
3538+
} while (p < end);
3539+
3540+
return true;
34913541
}
34923542

34933543
bool os::pd_create_stack_guard_pages(char* addr, size_t size) {
@@ -5873,3 +5923,151 @@ void os::win32::initialize_thread_ptr_offset() {
58735923
bool os::supports_map_sync() {
58745924
return false;
58755925
}
5926+
5927+
#ifdef ASSERT
5928+
static void check_meminfo(MEMORY_BASIC_INFORMATION* minfo) {
5929+
assert(minfo->State == MEM_FREE || minfo->State == MEM_COMMIT || minfo->State == MEM_RESERVE, "Invalid state");
5930+
if (minfo->State != MEM_FREE) {
5931+
assert(minfo->AllocationBase != NULL && minfo->BaseAddress >= minfo->AllocationBase, "Invalid pointers");
5932+
assert(minfo->RegionSize > 0, "Invalid region size");
5933+
}
5934+
}
5935+
#endif
5936+
5937+
5938+
static bool checkedVirtualQuery(address addr, MEMORY_BASIC_INFORMATION* minfo) {
5939+
ZeroMemory(minfo, sizeof(MEMORY_BASIC_INFORMATION));
5940+
if (::VirtualQuery(addr, minfo, sizeof(MEMORY_BASIC_INFORMATION)) == sizeof(MEMORY_BASIC_INFORMATION)) {
5941+
DEBUG_ONLY(check_meminfo(minfo);)
5942+
return true;
5943+
}
5944+
return false;
5945+
}
5946+
5947+
// Given a pointer pointing into an allocation (an area allocated with VirtualAlloc),
5948+
// return information about that allocation.
5949+
bool os::win32::find_mapping(address addr, mapping_info_t* mi) {
5950+
// Query at addr to find allocation base; then, starting at allocation base,
5951+
// query all regions, until we either find the next allocation or a free area.
5952+
ZeroMemory(mi, sizeof(mapping_info_t));
5953+
MEMORY_BASIC_INFORMATION minfo;
5954+
address allocation_base = NULL;
5955+
address allocation_end = NULL;
5956+
bool rc = false;
5957+
if (checkedVirtualQuery(addr, &minfo)) {
5958+
if (minfo.State != MEM_FREE) {
5959+
allocation_base = (address)minfo.AllocationBase;
5960+
allocation_end = allocation_base;
5961+
// Iterate through all regions in this allocation to find its end. While we are here, also count things.
5962+
for (;;) {
5963+
bool rc = checkedVirtualQuery(allocation_end, &minfo);
5964+
if (rc == false || // VirtualQuery error, end of allocation?
5965+
minfo.State == MEM_FREE || // end of allocation, free memory follows
5966+
(address)minfo.AllocationBase != allocation_base) // end of allocation, a new one starts
5967+
{
5968+
break;
5969+
}
5970+
const size_t region_size = minfo.RegionSize;
5971+
mi->regions ++;
5972+
if (minfo.State == MEM_COMMIT) {
5973+
mi->committed_size += minfo.RegionSize;
5974+
}
5975+
allocation_end += region_size;
5976+
}
5977+
if (allocation_base != NULL && allocation_end > allocation_base) {
5978+
mi->base = allocation_base;
5979+
mi->size = allocation_end - allocation_base;
5980+
rc = true;
5981+
}
5982+
}
5983+
}
5984+
#ifdef ASSERT
5985+
if (rc) {
5986+
assert(mi->size > 0 && mi->size >= mi->committed_size, "Sanity");
5987+
assert(addr >= mi->base && addr < mi->base + mi->size, "Sanity");
5988+
assert(mi->regions > 0, "Sanity");
5989+
}
5990+
#endif
5991+
return rc;
5992+
}
5993+
5994+
// Helper function for print_memory_mappings:
5995+
// Given a MEMORY_BASIC_INFORMATION, containing information about a non-free region:
5996+
// print out all regions in that allocation. If any of those regions
5997+
// fall outside the given range [start, end), indicate that in the output.
5998+
// Return the pointer to the end of the allocation.
5999+
static address print_one_mapping(MEMORY_BASIC_INFORMATION* minfo, address start, address end, outputStream* st) {
6000+
assert(start != NULL && end != NULL && end > start, "Sanity");
6001+
assert(minfo->State != MEM_FREE, "Not inside an allocation.");
6002+
address allocation_base = (address)minfo->AllocationBase;
6003+
address last_region_end = NULL;
6004+
st->print_cr("AllocationBase: " PTR_FORMAT ":", allocation_base);
6005+
#define IS_IN(p) (p >= start && p < end)
6006+
for(;;) {
6007+
address region_start = (address)minfo->BaseAddress;
6008+
address region_end = region_start + minfo->RegionSize;
6009+
assert(region_end > region_start, "Sanity");
6010+
if (region_end <= start) {
6011+
st->print("<outside range> ");
6012+
} else if (region_start >= end) {
6013+
st->print("<outside range> ");
6014+
} else if (!IS_IN(region_start) || !IS_IN(region_end - 1)) {
6015+
st->print("<partly outside range> ");
6016+
}
6017+
st->print("[" PTR_FORMAT "-" PTR_FORMAT "), state=", p2i(region_start), p2i(region_end));
6018+
switch (minfo->State) {
6019+
case MEM_COMMIT: st->print("MEM_COMMIT"); break;
6020+
case MEM_FREE: st->print("MEM_FREE"); break;
6021+
case MEM_RESERVE: st->print("MEM_RESERVE"); break;
6022+
default: st->print("%x?", (unsigned)minfo->State);
6023+
}
6024+
st->print(", prot=%x, type=", (unsigned)minfo->AllocationProtect);
6025+
switch (minfo->Type) {
6026+
case MEM_IMAGE: st->print("MEM_IMAGE"); break;
6027+
case MEM_MAPPED: st->print("MEM_MAPPED"); break;
6028+
case MEM_PRIVATE: st->print("MEM_PRIVATE"); break;
6029+
default: st->print("%x?", (unsigned)minfo->State);
6030+
}
6031+
st->cr();
6032+
bool rc = checkedVirtualQuery(region_end, minfo);
6033+
if (rc == false || // VirtualQuery error, end of allocation?
6034+
(minfo->State == MEM_FREE) || // end of allocation, free memory follows
6035+
((address)minfo->AllocationBase != allocation_base) || // end of allocation, a new one starts
6036+
(region_end > end)) // end of range to print.
6037+
{
6038+
return region_end;
6039+
}
6040+
}
6041+
#undef IS_IN
6042+
ShouldNotReachHere();
6043+
return NULL;
6044+
}
6045+
6046+
void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) {
6047+
MEMORY_BASIC_INFORMATION minfo;
6048+
address start = (address)addr;
6049+
address end = start + bytes;
6050+
address p = start;
6051+
while (p < end) {
6052+
// Probe for the next mapping.
6053+
if (checkedVirtualQuery(p, &minfo)) {
6054+
if (minfo.State != MEM_FREE) {
6055+
// Found one. Print it out.
6056+
address p2 = print_one_mapping(&minfo, start, end, st);
6057+
assert(p2 > p, "Sanity");
6058+
p = p2;
6059+
} else {
6060+
// Note: for free regions, most of MEMORY_BASIC_INFORMATION is undefined.
6061+
// Only region dimensions are not: use those to jump to the end of
6062+
// the free range.
6063+
address region_start = (address)minfo.BaseAddress;
6064+
address region_end = region_start + minfo.RegionSize;
6065+
assert(p >= region_start && p < region_end, "Sanity");
6066+
p = region_end;
6067+
}
6068+
} else {
6069+
// advance probe pointer.
6070+
p += os::vm_allocation_granularity();
6071+
}
6072+
}
6073+
}

src/hotspot/os/windows/os_windows.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,20 @@ class win32 {
110110
struct _EXCEPTION_POINTERS* exceptionInfo,
111111
address pc, frame* fr);
112112

113+
struct mapping_info_t {
114+
// Start of allocation (AllocationBase)
115+
address base;
116+
// Total size of allocation over all regions
117+
size_t size;
118+
// Total committed size
119+
size_t committed_size;
120+
// Number of regions
121+
int regions;
122+
};
123+
// Given an address p which points into an area allocated with VirtualAlloc(),
124+
// return information about that area.
125+
static bool find_mapping(address p, mapping_info_t* mapping_info);
126+
113127
#ifndef _WIN64
114128
// A wrapper to install a structured exception handler for fast JNI accesors.
115129
static address fast_jni_accessor_wrapper(BasicType);

src/hotspot/share/runtime/os.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,9 @@ class os: AllStatic {
347347
static bool uncommit_memory(char* addr, size_t bytes);
348348
static bool release_memory(char* addr, size_t bytes);
349349

350+
// A diagnostic function to print memory mappings in the given range.
351+
static void print_memory_mappings(char* addr, size_t bytes, outputStream* st);
352+
350353
// Touch memory pages that cover the memory range from start to end (exclusive)
351354
// to make the OS back the memory range with actual memory.
352355
// Current implementation may not touch the last page if unaligned addresses

0 commit comments

Comments
 (0)