设为首页 加入收藏

TOP

Java中Runtime.getRuntime().exec()错误:Cannot allocate memory!
2014-11-24 08:10:22 来源: 作者: 【 】 浏览:1
Tags:Java Runtime.getRuntime .exec 错误 Cannot allocate memory

用java的Runtime.getRuntime().exec(cmd)方式,执行aapt命令行解包apk文件时,遇到“Cannot allocate memory”的错误。


但是直接在linux上使用aapt命令可以正常使用。


网上查询资料整理如下:


Cannot allocate memory


在Linux上调试一个比较复杂的Java程序,称为JavaA吧,JavaA会频繁的通过Process proc = Runtime.getRuntime().exec(cmd);调用一些外部程序。在系统负载和该程序占用内存都比较大的情况下,会出现调用失败的情况,错误信息是:"Cannot allocate memory"。


overcommit_memory


通过top发现,JavaA大部分时间占用的内存实际并不多,但是占用的虚拟内存很大。马上修改该程序启动时的JVM参数,将最大内存调的小一些,果然就不出错了。由于JavaA必须在内存中处理大量的数据,内存太小了就可能处理不了,因此这么改是不可行的。


上网查的过程中,发现一些有趣的东西。Linux内核中可以设置内存的overcommit_memory属性,意思是Linux内核认为有些程序很保守,总是申请较多的内存,但实际并不使用,因此在设置overcommit后,内核将不检查剩余内存是否够用,直接允许所有的内存分配。可能大部分情况下没问题,但是仔细想想,还是有很大的问题,最严重的是改变了malloc的语义,调用者不能通过返回值来判断内存是否分配成功了。另外一个问题是,万一内存真的不够了怎么办?Linux中有个特殊的进程,OOM(out-of-memory)进程终止者,其功能就是在内存真的不够时,随机或者根据某些原则杀掉一些进程。选择进程的原则好像不能精确控制,那将是一件很恐怖的事情。。。


顺道小小八卦一下,当时还有不少人研究如何选择要杀掉的进程,提出了不少的改进方法,于是有人看不下去了,说了一个很有意思的故事,某航空公司为了节省油钱,每次飞行并不加满油,飞行途中要是超员了就挑一些乘客扔下去,于是大家兴高采烈的讨论应该扔谁下去。。。


Runtime.getRuntime().exec(cmd)的执行流程分析


继续上网查,大概意思是Java程序调用外部程序时可能需要分配跟父进程同等大小的内存。这就奇怪了,比如说,我随便调用一下ls命令,也需要很多内存吗?肯定是Java调用外部程序的接口里处理比较特殊。嗯,刚好JDK也开源,看看源码去。


分析SUN JDK 1.5 SRC,找到Runtime.getRuntime().exec(cmd)的执行流程:


java.lang.Runtime.exec(cmd);


--java.lang.ProcessBuilder.start();


----java.lang.ProcessImpl.start();


------Java_java_lang_UNIXProcess_forkAndExec() in j2se/src/solaris/native/java/lang


/UNIXProcess_md.c


--------1). fork(); 2). execvp();



man fork知道,fork产生的子进程需要复制父进程在内存中的所有数据内容(代码段、数据段、堆栈段),由于全部复制开销较大,因此Linux已经采用copy-on-write机制,即只是复制页表,共享内容,在有改变的时候再去申请内存和复制数据。



因此我分析,问题的原因可能是这样的,虽然Linux早已在fork()中采用copy-on-write机制,但是JVM调用fork()后,Java进程里的其它线程往往会被调度回来继续执行,修改了自己的内存,而这个时候execvp()还没有执行,于是悲剧就发生了,内存都要重新复制一遍。


最后说说解决办法,既然问题出在可能会申请分配跟父进程同等大小的内存,那么我限制父进程使用的内存就可以了。前面说了我们正在开发的JavaA必须使用比较大的内存,可是JavaA不一定是父进程呀,我可以单独运行一个Java程序,称为JavaB吧,由它负责调用外部程序,JavaA调用我们封装后的接口与之通信,等待外部程序结束,从而与Runtime.getRuntime().exec(cmd)的语义保持一致。这个单独运行的JavaB只需要很小很小的内存,因此不太可能出现无法分配内存,进而无法执行外部程序的问题了。


】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇自定义实现Hadoop Key-Value 下一篇C语言中的无符号数与有符号数混合..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·C语言中,“指针”用 (2025-12-26 15:20:18)
·在c语言的指针运算中 (2025-12-26 15:20:15)
·C语言-函数指针与函 (2025-12-26 15:20:12)
·求navicat for mysql (2025-12-26 13:21:33)
·有哪位大哥推荐一下m (2025-12-26 13:21:30)