NanoHttpd / nanohttpd

Tiny, easily embeddable HTTP server in Java.
http://nanohttpd.org
BSD 3-Clause "New" or "Revised" License
6.93k stars 1.69k forks source link

randomAccessFile.getChannel().map() java.io.IOException: mmap failed: ENOMEM (Out of memory) #457

Open mozikun opened 6 years ago

mozikun commented 6 years ago

When I upload 1GB file to android devices by nanohttpd, android devices will OOM.

It maybe that call FileChannel.map() load the 1GB file to ByteBuffer cause out of memory.

How can I fix the problem? Thank you!

ByteBuffer fbuf = null; if (baos != null) { fbuf = ByteBuffer.wrap(baos.toByteArray(), 0, baos.size()); Log.d(TAG, "fbuf1 -- baos != null"); } else {//READ_ONLY fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, randomAccessFile.length()); randomAccessFile.seek(0); Log.d(TAG, "fbuf2 -- baos == null"); // executed }

09-19 14:10:05.056 W/System.err( 1576): java.io.IOException: mmap failed: ENOMEM (Out of memory)

09-19 14:10:05.071 W/System.err( 1576): at java.nio.MemoryBlock.mmap(MemoryBlock.java:125)

09-19 14:10:05.071 W/System.err( 1576): at java.nio.FileChannelImpl.map(FileChannelImpl.java:257)

09-19 14:10:05.071 W/System.err( 1576): at httpserver.NanoHTTPD$HTTPSession.parseBody(NanoHTTPD.java:1080)

LordFokas commented 6 years ago

We probably have to "buffer" files to disk...

ngochai commented 6 years ago

I got the error too, Ubuntu Linux, OpenJDK 32bit 1.7, file uploaded is about 500Mb. System mem is 4G

kaiv587hh commented 5 years ago

I have the same problem too,hope soon...

GggggitHub commented 2 years ago

I have the same problem too.

origin code:

  1. fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, randomAccessFile.length());

  2. private String saveTmpFile(ByteBuffer b, int offset, int len, String filename_hint) {

        String path = "";
        if (len > 0) {
            FileOutputStream fileOutputStream = null;
    
            try {
                TempFile tempFile = this.tempFileManager.createTempFile(filename_hint);
                ByteBuffer src = b.duplicate();
                fileOutputStream = new FileOutputStream(tempFile.getName());
                FileChannel dest = fileOutputStream.getChannel();
                src.position(offset).limit(offset + len);
                dest.write(src.slice());
                path = tempFile.getName();
            } catch (Exception var13) {
                throw new Error(var13);
            } finally {
                NanoHTTPD.safeClose(fileOutputStream);
            }
        }
    
        return path;
    }

origin code 2 can optimization ?? or origin code 1 can optimization ??

GggggitHub commented 2 years ago

I solve this problem ,modify origin code.

    //success  随机读写 + buffer. 成功。1G file memory just used 50mb.
    public void onCopyOTAFile(RandomAccessFile rafi,String destPath,int offset,int len) throws IOException {
        // 2、自己处理Buffer(RandomAccessFile): 0.13s

// String srcFile = "/Users/macpro/Downloads/00111-BXMJ_2G_DVT_2.6.2.63.d.s.d_06240942-ota.zip"; // String destFile = "/Users/macpro/Downloads/copy-BXMJ_2G_DVT_2.6.2.63.d.s.d_06240942-ota.zip"; File file = new File(destPath); if (file.exists())file.delete(); // RandomAccessFile rafi = new RandomAccessFile(srcFile, "r"); RandomAccessFile rafo = new RandomAccessFile(destPath, "rw"); // int len = 1024 1024 1024;//总大小。需要读取的大小 // int offset = 2;//读取的偏移量

        int bufferSize = 1024 * 1024;//
        byte[] buf = new byte[bufferSize];

        long start = System.currentTimeMillis();
        try {
            rafi.seek(0);
            rafi.skipBytes(offset);

            int c = 0;
            //第一次读取,
            if (len <= bufferSize){//读取的总量 < filesize,所以做截取
                c = rafi.read(buf,0,len);
            }else {
                c = rafi.read(buf);
            }

            int count = c;

            while (c > 0) {
                //写入
                if (c == buf.length) {
                    rafo.write(buf);
                } else {
                    rafo.write(buf, 0, c);
                }

                if (count >= len){ //读取,写入完成。
                    break;
                }

                //读取
                if (count + bufferSize >=len){//读取的总量 < filesize,所以做截取
                    int currentlen =  len - count;
                    c = rafi.read(buf,0,currentlen);
                }else {
                    c  = rafi.read(buf);
                }
                count = count+c;
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            rafi.close();
            rafo.close();
        }
        System.out.println("Spend: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");
    }
GggggitHub commented 2 years ago

1个 G file oom

image

GggggitHub commented 2 years ago

channel().map .this method is for get buffer .this buffer for get boundary and real file position. so ,you gan open little buffer.for get real file position.