Bug 49326 - OutOfMemoryError: Map failed
Summary: OutOfMemoryError: Map failed
Status: RESOLVED FIXED
Alias: None
Product: Ant
Classification: Unclassified
Component: Core tasks (show other bugs)
Version: 1.8.1
Hardware: PC Windows XP
: P2 normal with 1 vote (vote)
Target Milestone: 1.8.2
Assignee: Ant Notifications List
URL:
Keywords:
: 49341 (view as bug list)
Depends on:
Blocks:
 
Reported: 2010-05-21 08:42 UTC by Mike Murray
Modified: 2014-02-17 13:55 UTC (History)
3 users (show)



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Mike Murray 2010-05-21 08:42:47 UTC
Revision 922179 of ResourceUtils.java, to resolve https://issues.apache.org/bugzilla/show_bug.cgi?id=48894, is causing copies of large files to fail when max heap is set to a “high” value.  I got the error below with –Xmx800M.  I lowered it to 750 and it worked.  The file being copied is 840M.  A file of 670M failed with –Xmx900M, but succeeded with 800.

It runs for 2 seconds and fails as soon as this task is called.  Java VisualVM shows the process uses less than 20M.  It’s using Java 1.6 on 32-bit Windows XP.

   <copy file="my.zip” tofile="my.01.zip" />

BUILD FAILED
Failed to copy my.zip to my.01.zip due to Map failed
        at org.apache.tools.ant.taskdefs.Copy.doFileOperations(Copy.java:853)
        at org.apache.tools.ant.taskdefs.Copy.execute(Copy.java:523)
        at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
        at sun.reflect.GeneratedMethodAccessor45.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
        at org.apache.tools.ant.Task.perform(Task.java:348)
        at org.apache.tools.ant.Target.execute(Target.java:390)
        at org.apache.tools.ant.Target.performTasks(Target.java:411)
        at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1397)
        at org.apache.tools.ant.Project.executeTarget(Project.java:1366)
        at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
        at org.apache.tools.ant.Project.executeTargets(Project.java:1249)
        at org.apache.tools.ant.Main.runBuild(Main.java:801)
        at org.apache.tools.ant.Main.startAnt(Main.java:218)
        at org.apache.tools.ant.launch.Launcher.run(Launcher.java:280)
        at org.apache.tools.ant.launch.Launcher.main(Launcher.java:109)
Caused by: java.io.IOException: Map failed
        at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:761)
        at sun.nio.ch.FileChannelImpl.transferToTrustedChannel(FileChannelImpl.java:450)
        at sun.nio.ch.FileChannelImpl.transferTo(FileChannelImpl.java:523)
        at org.apache.tools.ant.util.ResourceUtils.copyResource(ResourceUtils.java:475)
        at org.apache.tools.ant.util.FileUtils.copyFile(FileUtils.java:519)
        at org.apache.tools.ant.util.FileUtils.copyFile(FileUtils.java:481)
        at org.apache.tools.ant.util.FileUtils.copyFile(FileUtils.java:310)
        at org.apache.tools.ant.taskdefs.Copy.doFileOperations(Copy.java:841)
        ... 17 more
Caused by: java.lang.OutOfMemoryError: Map failed
        at sun.nio.ch.FileChannelImpl.map0(Native Method)
        at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:758)
        ... 24 more
Comment 1 J.M. (Martijn) Kruithof 2010-05-22 15:50:15 UTC
Seems we are now hitting a bug in sun's version of the jre.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6417205
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5092131

could you please indicate what exact version of java you are using?
Comment 2 Peter Reilly 2010-05-22 15:57:26 UTC
I think that we should be conservative here, and do something
like:

 srcChannel.transferTo(position,
   Math.min(FileUtils.BUF_SIZE, count - position),
   destChannel);

Peter
Comment 3 J.M. (Martijn) Kruithof 2010-05-22 16:02:19 UTC
Well when reading the bug reports, it seems this would not be enough.

Currently I wrote down this:
with MAX_IO_CHUNCK_SIZE = 10MiB

                long count = srcChannel.size();
                while (position < count) {
                    long chunck = Math.min(MAX_IO_CHUNCK_SIZE, count - position);
                    position +=
                        srcChannel.transferTo(position, chunck,
                                              destChannel);
                    System.gc();
                    System.runFinalization();
                }
Comment 4 Peter Reilly 2010-05-22 16:12:56 UTC
{{{shudder}}}
I do not think that we need to do the gc stuff.
The code worked (using BUF_SIZE) in ant 1.8 - except for gcj.
which had a problem with the last block.

The problem with using the full file size is that
we are basically asking the OS to map in memory that corresponds
to the file size and that
can be very taxing and one cannot expect Windows to
do that.


If we have to use gc, then we may as well stop using
NIO, and go back using Classic IO.

Peter
Comment 5 J.M. (Martijn) Kruithof 2010-05-22 16:38:16 UTC
Not entirely, we are asking the standard java libraries to copy at most count characters from one channel to the other. The amount of memory mapped memory in that operation is entirely up to the implementation.

If we must conclude that the implementation of nio in most jvm's is not according the specs provided, maybe we should revert. The gc kludge is just the one provided by Sun engineering in both http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5092131 and http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6417205
Comment 6 Bruce Atherton 2010-05-22 17:28:44 UTC
(In reply to comment #3)
> Currently I wrote down this:
> with MAX_IO_CHUNCK_SIZE = 10MiB
> 
>                 long count = srcChannel.size();
>                 while (position < count) {
>                     long chunck = Math.min(MAX_IO_CHUNCK_SIZE, count -
> position);
>                     position +=
>                         srcChannel.transferTo(position, chunck,
>                                               destChannel);
>                     System.gc();
>                     System.runFinalization();
>                 }

Why not use the recommended workaround from Bug# 6417205, something like:

    // MAX_IO_CHUNK_SIZE = 10MiB
    long count = srcChannel.size();
    while (position < count) {
        long chunk = Math.min(MAX_IO_CHUNK_SIZE, count - position);
        try {
            long readBytes = srcChannel.transferTo(position, 
                    chunk, destChannel);
        } catch (java.io.IOException e) {
            System.gc();
            System.runFinalization();
            long readBytes = srcChannel.transferTo(position, 
                    chunk, destChannel);
        }
        position += readBytes;
    }

That way, the garbage collection only happens when it is needed, and will never effect a JVM that has this bug fixed.
Comment 7 Bruce Atherton 2010-05-22 17:31:31 UTC
Sorry, I should have had the long declared outside the try. But you get the idea.
Comment 8 J.M. (Martijn) Kruithof 2010-05-22 18:03:13 UTC
Hmm giving this another thought.
If the leakage problem is solved in the JVM, still the memory-mapped size allocated together with the VM size could go over the limit, and this problem may still be present in the standard SUN libraries.
I have performed some tests with java 1.6.0_20, and it seems that if the transfer size requested, combined with the JVM size remains below a certain limit, no problems occur.

In order to allow more efficient transfer, while avoiding to hit the limit I propose 16 MB chunk sizes instead of 8192 byte chunck sizes. The optimal chunk size varies with machines, and seems to be dependent on the amount of data the IO controller / disk cache can process at once.

This after testing the effect of different chunk sizes on my machine. Too small a size apparently causes too many operations to allow efficient copying a 
Time to copy a single file of 1.8 gig from one drive to the other with a limiting size of:
64 MiB -> 1:08
32 MiB -> 1:08
16 MiB -> 1:18
8 Mib -> 1:12
4 Mib -> 1:55
1 MiB -> 3:31
8 KiB -> 3:31

Committing a version limiting the maximal request size to 16 MiB, trying to strike balance between the needed memory space and limiting the number of operations needed.

Fix applied in
http://svn.apache.org/viewvc?rev=947339&view=rev
Comment 9 Stefan Bodewig 2010-05-26 08:45:20 UTC
*** Bug 49341 has been marked as a duplicate of this bug. ***
Comment 10 Stefan Bodewig 2011-02-24 05:00:53 UTC
*** Bug 49341 has been marked as a duplicate of this bug. ***
Comment 11 Stefan Bodewig 2011-02-28 06:04:55 UTC
*** Bug 49341 has been marked as a duplicate of this bug. ***
Comment 12 Krishnan Ganapathy 2012-08-21 06:32:59 UTC
We are still able to reproduce this issue in our app deployed on JBoss server (5.1.0). We are using ANT 1.8.2 version which from what i understand contains this fix. 

Interestingly as mentioned in one of the comments, we are getting this error only on 32 bit m/c with Xmx setting as 1024M. We then lowered it to 800M and this error did not occur. 

From our analysis, its seems that whatever be the block size (16M in the patch applied), there is always a chance that this error might occur as there might be stray cases where the native map0 call in the FileChannel class might fail.

Hence, the code should fallback on using the read/write methods on FileChannel or use the classic IO to accomplish this copy instead of throwing an IOException. 

We would like to go ahead, make changes and submit the patch but wanted to know if there are any other suggestions that we can try out.

-
Krishnan