2

I've a small and simple storage system which is accessible through memory mapped files. As I need to address more than 2GB space I need a list of MappedByteBuffer with a fixed size like 2GB (I'm using less for different reasons). Then all is relativly simple: a buffer maps to a certain space say to 1GB and when I need more I map a new MappedByteBuffer (the file automatically increases), then when I need more a third buffer is mapped etc. This just worked.

But then I've read in the Java NIO book that when I change the file length problems could occur:

A MappedByteBuffer directly reflects the disc file with which it is associated. If the file is structurally modified while the mapping is in effect, strange behaviour can result (exact behaviour are OS and file system dependent) A MappedByteBuffer has a fixed size, but file it's mapped to is elastic. Specifically if a file's size changes while the mapping is in effect, some or all of the buffer may become inaccessible, undefined data could be returned, or unchecked exceptions could be thrown. Be careful about how files are manipulated by other threads or external processes when they are memory-mapped.

I think the problems could occur as the OS could move the file when it is increasing and the MappedByteBuffers then could point to an invalid space (or am I misinterpreting this?)

So, instead of adding a new MappedByteBuffer to the list I am now doing the following

  1. increasing the file length
  2. clearing the buffer list (throwing away the old buffers and hoping that the buffers where released via garbage collector. hmmh, probably I should clean all the buffers explicitly via cleaner.clean()?)
  3. re-mapping (filling the list with fresh buffers)

BUT THIS procedure has the disadvantage of failing sometimes while mapping with

IOException: Operation not permitted
    at sun.nio.ch.FileChannelImpl.map0(Native Method)
    at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:734)

Why that? Because clearing the buffer list did not properly release and clean the buffers and multiple mappings are not allowed? Should I just stick to the old working method and ignoring the comment in the book?

Update

  • splitting the mapping on a 32bit OS has the advantage of better finding free space and less likely make an error (ref)
  • splitting the mapping into smaller parts is an advantage as setting up the mmap could be costly (ref)
  • both approaches are not clean while my second approach should work but will need an unmap (will try to force the release with the normal cleaner.clean hack). The first approach should work on systems (like ibm) where I could increase the file size but in general it won't work although I couldn't find the exact reason yet...
  • cleanest way would be to use multiple files I fear (one file per MappedByteBuffer)
Community
  • 1
  • 1
Karussell
  • 17,085
  • 16
  • 97
  • 197
  • Can you clarify "when the file gets too large I map a new MappedByteBuffer (the file automatically increases) ". Why don't you use multi file approach to get over 2G limit? Btw your previous method was OK, however with the new one you exactly do what book warns you about - changing file size. – auselen Dec 23 '12 at 15:13
  • I clarified that part you mentioned a bit. To get over the 2GB limit it is not necessary to use multiple files. The Buffer API with get/set(int) is that what is limiting us. Why is the new approach exactly that what the book warns me about? I'm throwing away the old buffers ... – Karussell Dec 23 '12 at 15:37
  • Do you increase the file size before clearing the buffer list? That can be a problem. Also may be if you are trying to exclusively map the same area, that could cause "Operation not permitted" – auselen Dec 23 '12 at 16:18
  • Yes, I increased the size before clearing. But where do I get some definitive explanations? I don't like programming by trial and error :) – Karussell Dec 23 '12 at 16:54
  • As you imagine this is quite an OS centric mechanism. So it might be better for example study mmap on Linux or its equivalent on Windows. – auselen Dec 23 '12 at 17:16
  • Why don't you try it and find out? – Bohemian Dec 23 '12 at 21:33

1 Answers1

1

The root cause was my fault: accidentally I remapped the underlying file too often (the capacity increased only via mini steps).

But even in this extreme situation I was able to finally fix the IOException (Operation not permitted) when I was retrying the failing mapping operation (+ System.gc + a 5ms sleep -> which should give the jvm the opportunity to unmap the buffer). Now I only saw massive numbers of remappings which leads to the final conclusion.

At least I learned a bit more about mmap: it is very OS+filesystem dependent - thanks also to auselen! If you like a clean solution you should use one MappedByteBuffer per file as initially suggested from him. But this could be also problematic if you need large space and your OS file descriptor limit is too low.

Last but not least I would strongly recommend against my first solution as I couldn't find a guarantee (only in the IBM OS ;)) which leaves the mapped buffer intact after a file size increase.

Community
  • 1
  • 1
Karussell
  • 17,085
  • 16
  • 97
  • 197