I think I've managed to reasonably firmly establish that atomically remapping memory is impossible on Windows based on various anecdotal evidence found from searching around (particularly on Cygwin mailing list archives).

Another possible option is to use other Windows memory management APIs to prevent a region of memory from being used by the automatic address selection mechanism - perhaps a VirtualAlloc with MEM_RESERVE would still allow a file mapping to be loaded there later via MapViewOfFileEx? In this way, you could non-atomically unmap and remap the pages with some degree of assurance that they won't be reused.

Assuming that it is impossible to delete the mapping in any of these ways though, an unmap worst case implementation on Windows is not worse than the current status quo: it could just VirtualProtect the memory mapping to PAGE_NOACCESS and wait for GC to do its thing. Perhaps such an implementation could suffice until such time that a specialized HotSpot-based solution (like what Andrew described) or other better option could be achieved.

On 09/09/2015 07:41 AM, David M. Lloyd wrote:
If you have access to a Windows development environment, it seems to me
that you could help directly with the testing required to determine a
solution.

I think my proposed remapping approach will work, but it is contingent
on testing the following on Windows:

* Determine if it is possible to remap the pages previously mapped to a
file view without first unmapping them.  Preferably, the pages would be
remapped as inaccessible (equivalent to POSIX PROT_NONE).

* Determine if such remapping releases the file to be deleted, or, if
not, determine if the previously mapped file view can be unmapped
without affecting the remapped pages.

If the answer to both of these can be shown to be affirmative, then I
think there is a real viable solution which allows immediate release of
backing resources, with the address space being reclaimed by GC.

On 09/09/2015 05:51 AM, Uwe Schindler wrote:
Hi,

Dawid Weiss and I are both involved in the Apache Lucene project and
we know the problems with MappedByteBuffer and unmapping. Dawid
already responded with a source code link to our impl (which needs to
use the hacky cleaner() approach; also look at the heavy documentation
in this class):
https://github.com/apache/lucene-solr/blob/trunk/lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java


So we would be very happy to get this issue resolved! The cleaner()
hack is enabled by default in Lucene if the JVM supports it (so we
won't break if JIGSAW prevents this, but our *large* users would
heavily complain).

This is fundamentally about *integrity* of the runtime. It follows
there
are security implications, but it’s still fundamentally an integrity
issue
and guarding an unsafe operation with a Security Manager is
unfortunately an insufficient solution.

Right, and just to add that there has been many attempts over the years
to find solutions to this issue. I think the closest was atomimcally
remapping but that wasn't feasible on all platforms and also didn't free
up the address space in a timely manner.

So we should really find a solution here. I was talking with several
people on various conferences (Rory O'Donnel or Mark Reinhold) and we
had some ideas how to solve this. My idea how to solve this is
explained below (I am not a JVM internals or Hotspot guy, so excuse
some obviously "wrong" assumptions):

Actually there are 2 issues, not only one. The first issue is, as
mentioned before: you cannot unmap via API. This is needed for many
apps, including Apache Lucene, for a reason which comes more from
"another" bug, and this is my issue #2 (see below).

First, unmapping for Lucene is very important at the moment, because
we operate on the Lucene indexes purely using mmap (see [1]), which
may be several hundreds of Gigabytes easily. On highly dynamic
systems, Lucene often maps new files (also very largeones ) and relies
on the fact, that older, deleted files are unmapped in time (this does
not need to be ASAP, just "in time"). So we have those 2 "bugs", which
force us to unmap:

(1) disk space issues / delete after last close (POSIX) vs. No delete
at all (Windows)

- disk space: we have seen customers running out of disk space on
Lucene, because unmapping wasn’t done in time and therefore POSIX with
delete on last close cannot free the disk space, although the file was
already deleted. The problem you are seeing on Windows that you cannot
delete, is therefore worse on Linux, because it is hidden to the user
- you cannot free the disk space of the deleted file! Lucene creates
and deletes files all the time while indexing realtime data (e.g.
think of Github's very dynamic code search index, which is backed by
Lucene/Elasticsearch).
- virtual memory: If you map huge files (several hundreds of
Gigabytes) and they are not unmapped in time, you may run out of
virtual address space. This especially affects Windows, because it
does not use the full 46 bits (or like that) of addresses. So
effectively you can only map like 4 Terabytes on Windows. If you have
fragmentation of address space this gets worse (In Lucene, we map in
chunks of 1 GiB because of the signed 32 bit integer limit of
ByteBuffer, so fragmentation is not our biggest issue).

(2) It takes veeeeeeeeeeeeeeeery long time until the unmapping
actually occurs!

This is the real bug! If the garbage collector would clean up the
buffers asap, we would not need to unmap from user code. In Lucene we
just delay the file delete on Windows, so we are not really affected
by the file deletion inability (but that would be nice if it could be
fixed).

If you look at the usage pattern of those huge, mapped files, you will
see why they are in most cases *never ever* unmapped automatically:
Lucene maps very large files and uses them for longer time. So the
MappedByteBuffer object gets migrated to older generations on the
heap. Garbage collection there happens, of course, very delayed. That
would not be the most problematic part, but there is a second issue:
The MappedByteBuffer object is just a very small object (in heap size
measurement: just an object header and a few pointers), so the garbage
collector does not see it as heavy! It's just a very small like 30
bytes object instance. Why should the Garbage collector clean it up?
And in fact it will almost never do this! The garbage collector cannot
see that our 30 bytes object instance "sits" on something like 300
Gigabytes of virtual memory and disk space!

One proposal to fix this would be to add something like an internal
OpenJDK Java Annotation or similar where you can "mark" heavy objects,
so Garbage collector could free them by preference (similar to
sun.misc.Contended).

For the Apache Lucene team,
Uwe

[1]
http://blog.thetaphi.de/2012/07/use-lucenes-mmapdirectory-on-64bit.html

-----
Uwe Schindler
uschind...@apache.org
ASF Member, Apache Lucene PMC / Committer
Bremen, Germany
http://lucene.apache.org/




--
- DML

Reply via email to