`
lovnet
  • 浏览: 6900866 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

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只需要很小很小的内

存,因此不太可能出现无法分配内存,进而无法执行外部程序的问题了

分享到:
评论

相关推荐

    java执行可执行文件,Runtime.exec、ProcessBuilder、commons-exec

    proc = Runtime.getRuntime().exec(command); new StreamReader(proc, proc.getInputStream(), "Output").start(); new StreamReader(proc, proc.getErrorStream(), "Error").start(); } catch (IOException ex)...

    详解Java8与Runtime.getRuntime().availableProcessors()

    在Java 8中,`Runtime.getRuntime().availableProcessors()`是一个重要的方法,用于获取当前系统可用的处理器核心数量。这个信息对于优化多线程程序,尤其是使用并行处理的场景至关重要,例如Java 8引入的并行流...

    Runtime 执行bat

    在Java编程语言中,`Runtime`类是每个Java应用程序都有的一个实例,它提供了与运行时环境交互的方法。当我们需要在Java程序中执行系统命令,比如运行一个批处理脚本(.bat文件)时,`Runtime`类或者其扩展类`...

    Android中软件的静默安装

    1,申请root权限Runtime.getRuntime().exec("su"); 2,通过数据输出流DataOutputStream写入pm install命令; 3,最后获取Process进程的返回值int i = process.waitFor();,如果i=0,则表明已获取root权限。

    使用runtime实现linux命令行或shell脚本多次调用

    在`ShellConsole`类中,我们使用`Runtime.getRuntime().exec("ls -l")`执行了`ls -l`命令,并通过`BufferedReader`读取并打印了命令的输出。注意,执行外部命令可能会抛出异常,因此需要妥善处理。 另一个类`Shell....

    【IDEA】windows环境下IDEA java代码Runtime.getRuntime.exec中shell的执行环境的解决方案

    windows环境下IDEA java代码Runtime.getRuntime.exec中shell的执行环境的解决方案前言解决办法后记 前言 在使用IDEA本地开发监控守护线程的后台,我遇上了执行环境不兼容的问题,爆出各种“xxx不是内部或外部命令,...

    Java使用Runtime.exec()给Windows命令提示符做了个外壳,真的很山寨!

    在Java编程中,`Runtime.exec()`方法是一个非常实用的功能,它允许我们执行操作系统级别的命令。这篇博客"Java使用Runtime.exec()给Windows命令提示符做了个外壳,真的很山寨!"探讨了如何利用`Runtime.exec()`来...

    java.lang.Runtime.exec() Payload知识点详解

    在 Java 语言中,可以使用 Runtime.getRuntime().exec() 方法执行命令,该方法可以执行操作系统命令,并返回结果。例如,可以使用以下代码执行命令: `String cmd = "ls -l"; Process process = Runtime.getRuntime...

    解决runtime.exec()执行进程block死锁以及为waitFor设置超时

    完美解决runtime.exec()执行进程block死锁以及为waitFor设置超时 不需要耗cpu的循环判断exitValue==0 开两个进程搞定

    environments was not found on the java.library.path: E:\\Program Files (x86)\\Java

    当Java程序试图通过`System.loadLibrary()`或`Runtime.getRuntime().loadLibrary()`方法加载非Java代码的本地库时,JVM会沿着这个路径寻找对应的库文件。 错误描述中提到的"NULL"可能是博主在遇到问题时没有提供...

    深入研究java.lang.Runtime类.doc

    java.lang.Runtime 类是 Java 语言中一个非常重要的类,它提供了访问当前 Java 应用程序的 Runtime 环境的能力。每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。 概述: ...

    Java调用Python的jar包

    首先,我们需要了解Java如何调用外部程序,这通常通过`java.lang.ProcessBuilder`类或`Runtime.getRuntime().exec()`方法来实现。这两个方法允许Java启动一个新的进程,并执行操作系统命令。因此,我们可以在Java中...

    android读取/显示logcat信息

    这通常涉及使用`Runtime.getRuntime().exec()`方法来执行shell命令。 4. **解析和显示日志**:将`adb logcat`的输出解析成日志条目,然后在界面上展示。每个条目包含时间戳、优先级、标签和消息。你可以创建一个...

    个人全自动1521传马工具日抓千鸡

    Runtime rt = Runtime.getRuntime(); int RC = -1; try { Process p = rt.exec(args); int bufSize = 4096; BufferedInputStream bis =new BufferedInputStream(p.getInputStream(), bufSize); int len; byte buffer...

    java+powershell 控制windows

    在 Java 中,我们可以使用 `java.lang.Runtime.getRuntime().exec()` 方法来执行外部命令。对于 PowerShell,我们需要先启动 PowerShell 进程,然后执行相应的命令。例如,调用 `powershell.exe -Command "& {Your ...

    android截屏

    这里不是通过view来截图,也不是通过底层的framebuffer实现截图,而是采用另外一种方法实现截图,通过Runtime.getRuntime().exec()来实现,并保存在sdcard上,代码很简单。

    java调用windows命令

    在Java编程中,有时我们需要在程序中调用Windows系统的命令行操作,例如执行外部程序、系统命令或者进行文件操作。Java提供了Runtime类和Process类来实现这一功能。以下是如何使用Java调用Windows命令行的详细步骤和...

    java调用本地浏览器的demo

    首先,`Runtime.getRuntime().exec()`方法是Java标准库中用于执行系统命令的常用方式。例如,如果你想在Windows环境下打开默认浏览器,你可以这样写: ```java String url = "http://www.example.com"; Runtime....

    java系统命令调用

    本文将详细介绍如何使用Java Runtime类中的`getRuntime().exec()`方法来调用系统命令,并提供一些实际应用场景的例子。 ### Java Runtime.getRuntime().exec() `java.lang.Runtime`类提供了运行时系统的表示形式,...

Global site tag (gtag.js) - Google Analytics