Java 进程

转自http://jiangshuiy.iteye.com/blog/1674235

PS:今天做android助手项目的时候,发现adb
push命令执行会卡死,最后发现不能用waitfor阻塞等待执行结果,而且cmd上print的内容是打在进程的error流而不是input流,所以搞了好一会儿,最后这篇文章的提醒下解决了问题,故此转载学习一下。

0 预备知识

1 不正确的调用exitValue

2不正确的调用waitFor

3 一种可接受的调用方式

4 调用认为是可执行程序的时候容易发生的错误

5 window执行的良好示例

6 不良好的重定向命令输出

7 良好的重定向输出示例

8 总结

9 问答

预备知识

Runtime类是一个与JVM运行时环境有关的Singleton类,有以下几个值得注意的地方:

0.1 Runtime.getRuntime()可以取得当前JVM的运行时环境,这也是在Java中唯一得到运行时环境的方法。

0.2 Runtime上其他大部分的方法都是实例方法,也就是说每次进行运行时调用时都要用到getRuntime方法。

0.3
Runtime中的exit方法是退出当前JVM的方法,估计也是唯一的。System类中的exit实际上也是通过调用Runtime.exit()来退出JVM的。

Java对Runtime返回值的一般规则,0代表正常退出,非0代表异常中止,这只是Java的规则,在各个操作系统中总会发生一些小的混淆。

0.4
Runtime.addShutdownHook()方法可以注册一个hook在JVM执行shutdown的过程中,方法的参数只要是一个初始化过但是没有执行的Thread实例就可以。(注意,Java中的Thread都是执行过了就不值钱的哦)

0.5说到addShutdownHook这个方法就要说一下JVM运行环境是在什么情况下shutdown或者abort的。

Shutdown:当最后一个非精灵进程退出或者收到了一个用户中断信号、用户登出、系统shutdown、Runtime的exit方法被调用时JVM会启动shutdown的过程,在这个过程开始后,他会并行启动所有登记的shutdown
hook(注意是并行启动,这就需要线程安全和防止死锁)。当shutdown过程启动后,只有通过调用halt方法才能中止shutdown的过程并退出JVM。

Abort: abort退出时JVM就是停止运行但并不一定进行shutdown。这只有JVM在遇到SIGKILL信号或者windows中止进程的信号、本地方法发生类似于访问非法地址一类的内部错误时会出现。这种情况下并不能保证shutdown
hook是否被执行。

0.6 Runtime.exec()方法的所有重载。这里要注意的是:

public Process exec(String[] cmdarray, String[] envp, File dir);

这个方法中cmdArray是一个执行的命令和参数的字符串数组,数组的第一个元素是要执行的命令往后依次都是命令的参数,envp中是name=value形式的环境变量设置,如果子进程要继承当前进程的环境时是null。

不正确的调用exitValue

Java代码

  1. public class BadExecJavac {

  2. public static void main(String args[]) {

  3. try {

  4. Runtime rt = Runtime.getRuntime();

  5. Process proc = rt.exec("java");

  6. int exitVal = proc.exitValue();

  7. System.out.println("Process exitValue: " + exitVal);

  8. } catch (Throwable t) {

  9. t.printStackTrace();

  10. }

  11. }

  12. }

输出

Java代码

  1. java.lang.IllegalThreadStateException: process has not exited

  2. at java.lang.ProcessImpl.exitValue(Native Method)

  3. at BadExecJavac.main(BadExecJavac.java:26)

错误分析:

主要问题就是错误的调用了exitValue来取得外部命令的返回值。因为exitValue方法是非阻塞的,在调用这个方法时外部命令并没有返回所以引起异常。阻塞形式的方法是waitFor,它会一直等待外部命令执行完毕,然后返回执行的结果。

当你在一个Process上调用waitFor方法时,当前线程是阻塞的,如果外部命令无法执行结束,那么你的线程就会一直阻塞下去,这种意外会影响我们程序的执行。所以在我们不能判断外部命令什么时候执行完毕而我们的程序还需要继续执行的情况下,我们就应该循环的使用exitValue来取得外部命令的返回状态,并在外部命令返回时作出相应的处理。

2不正确的调用waitFor

Java代码

  1. public class BadExecJavac2 {

  2. public static void main(String args[]) {

  3. try {

  4. Runtime rt = Runtime.getRuntime();

  5. Process proc = rt.exec("javac");

  6. int exitVal = proc.waitFor();

  7. System.out.println("Process exitValue: " + exitVal);

  8. } catch (Throwable t) {

  9. t.printStackTrace();

  10. }

  11. }

  12. }

不幸的是,这个程序也无法执行完成,它没有输出但却一直悬在那里!这是为什么那?

JDK文档中的解释:因为本地的系统对标准输入和输出所提供的缓冲池有效,所以错误的对标准输出快速的写入和从标准输入快速的读入都有可能造成子进程的锁,甚至死锁。

JDK仅仅说明为什么问题会发生,却并没有说明这个问题怎么解决。

解决方法就是:执行完外部命令后我们要控制好Process的所有输入和输出(视情况而定),//在这个例子里边因为调用的是Javac,而他在没有参数的情况下会将提示信息输出到标准出错,所以在下面的程序中我们要对此进行处理。

一种可接受的调用方式

Java代码

  1. public class MediocreExecJavac {

  2. public static void main(String args[]) {

  3. try {

  4. Runtime rt = Runtime.getRuntime();

  5. Process proc = rt.exec("javac");

  6. InputStream stderr = proc.getErrorStream();

  7. InputStreamReader isr = new InputStreamReader(stderr);

  8. BufferedReader br = new BufferedReader(isr);

  9. String line = null;

  10. System.out.println("<error></error>");

  11. while ((line = br.readLine()) != null)

  12. System.out.println(line);

  13. System.out.println("");

  14. int exitVal = proc.waitFor();

  15. System.out.println("Process exitValue: " + exitVal);

  16. } catch (Throwable t) {

  17. t.printStackTrace();

  18. }

  19. }

  20. }

输出:

Java代码

  1. <error></error>

  2. Usage: javac <options></options> <source files=""></source>
  3. ...
  4. Process exitValue: 2

结果分析:

出来了结果。

为了处理好外部命令大量输出的情况,你要确保你的程序处理好外部命令所需要的输入或者输出。

调用认为是可执行程序的时候容易发生的错误

Java代码

  1. public class BadExecWinDir {

  2. public static void main(String args[]) {

  3. try {

  4. Runtime rt = Runtime.getRuntime();

  5. Process proc = rt.exec("dir");

  6. InputStream stdin = proc.getInputStream();

  7. InputStreamReader isr = new InputStreamReader(stdin);

  8. BufferedReader br = new BufferedReader(isr);

  9. String line = null;

  10. System.out.println("<output></output>");

  11. while ((line = br.readLine()) != null)

  12. System.out.println(line);

  13. System.out.println("");

  14. int exitVal = proc.waitFor();

  15. System.out.println("Process exitValue: " + exitVal);

  16. } catch (Throwable t) {

  17. t.printStackTrace();

  18. }

  19. }

  20. }

输出:

Java代码

  1. java.io.IOException: Cannot run program "dir": CreateProcess error=2, ...

  2. at java.lang.ProcessBuilder.start(ProcessBuilder.java:460)

  3. at java.lang.Runtime.exec(Runtime.java:593)

  4. at java.lang.Runtime.exec(Runtime.java:431)

  5. at java.lang.Runtime.exec(Runtime.java:328)

  6. at BadExecWinDir.main(BadExecWinDir.java:29)

原因分析:

因为dir命令是由windows中的解释器解释的,直接执行dir时无法找到dir.exe这个命令,所以会出现文件未找到这个2的错误。如果我们要执行这样的命令,就要先根据操作系统的不同执行不同的解释程序command.com 或者cmd.exe。

5 window执行的良好示例

Java代码

  1. public class StreamGobbler extends Thread {

  2. InputStream is;

  3. String      type;
  4. StreamGobbler(InputStream is, String type) {

  5. this.is = is;

  6. this.type = type;

  7. }
  8. public void run() {

  9. try {

  10. InputStreamReader isr = new InputStreamReader(is);

  11. BufferedReader br = new BufferedReader(isr);

  12. String line = null;

  13. while ((line = br.readLine()) != null)

  14. System.out.println(type + ">" + line);

  15. } catch (IOException ioe) {

  16. ioe.printStackTrace();

  17. }

  18. }

  19. }

Java代码

  1. public class GoodWindowsExec {

  2. public static void main(String args[]) {

  3. if (args.length < 1) {

  4. System.out.println("USAGE: java GoodWindowsExec <cmd></cmd>");

  5. System.exit(1);

  6. }

  7. try {

  8. String osName = System.getProperty("os.name");

  9. String[] cmd = new String[3];

  10. if (osName.equals("Windows NT")) {

  11. cmd[0] = "cmd.exe";

  12. cmd[1] = "/C";

  13. cmd[2] = args[0];

  14. } else if (osName.equals("Windows 95")) {

  15. cmd[0] = "command.com";

  16. cmd[1] = "/C";

  17. cmd[2] = args[0];

  18. }

  19. Runtime rt = Runtime.getRuntime();

  20. System.out.println("Execing " + cmd[0] + " " + cmd[1] + " " + cmd[2]);

  21. Process proc = rt.exec(cmd);

  22. // any error message?

  23. StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");

  24. // any output?

  25. StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");

  26. // kick them off

  27. errorGobbler.start();

  28. outputGobbler.start();

  29. // any error???

  30. int exitVal = proc.waitFor();

  31. System.out.println("ExitValue: " + exitVal);

  32. } catch (Throwable t) {

  33. t.printStackTrace();

  34. }

  35. }

  36. }

输出:

ExitValue: 0

原因分析:

就是cmd.exe /C +一个windows中注册了后缀的文档名,windows会自动地调用相关的程序来打开这个文档。

不要假设你执行的程序是可执行的程序,要清楚自己的程序是单独可执行的还是被解释的, 这里还有一点,就是得到process的输出的方式是getInputStream,这是因为我们要从Java 程序的角度来看,外部程序的输出对于Java来说就是输入,反之亦然。

不良好的重定向命令输出

错误的认为exec方法会接受所有你在命令行或者Shell中输入并接受的字符串。这些错误主要出现在命令作为参数的情况下,程序员错误的将所有命令行中可以输入的参数命令加入到exec中。下面的例子中就是一个程序员想重定向一个命令的输出。

Java代码

  1. public class BadWinRedirect {

  2. public static void main(String args[]) {

  3. try {

  4. Runtime rt = Runtime.getRuntime();

  5. Process proc = rt.exec("java jecho ‘Hello World‘ > test.txt");

  6. // any error message?

  7. StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");

  8. // any output?

  9. StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");

  10. // kick them off

  11. errorGobbler.start();

  12. outputGobbler.start();

  13. // any error???

  14. int exitVal = proc.waitFor();

  15. System.out.println("ExitValue: " + exitVal);

  16. } catch (Throwable t) {

  17. t.printStackTrace();

  18. }

  19. }

  20. }

程序员的本意是将Hello
World这个输入重订向到一个文本文件中,但是这个文件并没有生成,jecho仅仅是将命令行中的参数输出到标准输出中,用户觉得可以像dos中重定向一样将输出重定向到一个文件中,但这并不能实现,用户错误的将exec认为是一个shell解释器,但它并不是,如果你想将一个程序的输出重定向到其他的程序中,你必须用程序来实现他。可用java.io中的包。

良好的重定向输出示例

Java代码

  1. public class StreamGobbler extends Thread {

  2. InputStream is;

  3. String      type;

  4. OutputStream os;

  5. StreamGobbler(InputStream is, String type) {

  6. this(is, type, null);

  7. }

  8. StreamGobbler(InputStream is, String type, OutputStream redirect) {

  9. this.is = is;

  10. this.type = type;

  11. this.os = redirect;

  12. }

  13. public void run() {

  14. try {

  15. PrintWriter pw = null;

  16. if (os != null)

  17. pw = new PrintWriter(os);

  18. InputStreamReader isr = new InputStreamReader(is);

  19. BufferedReader br = new BufferedReader(isr);

  20. String line = null;

  21. while ((line = br.readLine()) != null) {

  22. if (pw != null)

  23. pw.println(line);

  24. System.out.println(type + ">" + line);

  25. }

  26. if (pw != null)

  27. pw.flush();

  28. } catch (IOException ioe) {

  29. ioe.printStackTrace();

  30. }

  31. }

  32. }

Java代码

  1. public class GoodWinRedirect {

  2. public static void main(String args[]) {

  3. args = new String[1];

  4. args[0]="g:\\out.txt";

  5. if (args.length < 1) {

  6. System.out.println("USAGE java GoodWinRedirect <outputfile></outputfile>");

  7. System.exit(1);

  8. }

  9. try {

  10. FileOutputStream fos = new FileOutputStream(args[0]);

  11. Runtime rt = Runtime.getRuntime();

  12. Process proc = rt.exec("java jecho ‘Hello World‘");

  13. // any error message?

  14. StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");

  15. // any output?

  16. StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT", fos);

  17. // kick them off

  18. errorGobbler.start();

  19. outputGobbler.start();

  20. // any error???

  21. int exitVal = proc.waitFor();

  22. System.out.println("ExitValue: " + exitVal);

  23. fos.flush();

  24. fos.close();

  25. } catch (Throwable t) {

  26. t.printStackTrace();

  27. }

  28. }

  29. }

总结

总结了几条规则,防止我们在进行Runtime.exec()调用时出现错误。

  • 在一个外部进程执行完之前你不能得到他的退出状态

  • 在你的外部程序开始执行的时候你必须马上控制输入、输出、出错这些流。

  • 你必须用Runtime.exec()去执行程序

  • 你不能象命令行一样使用Runtime.exec()。

问答

问:为什么Runtime.exec("ls")没有任何输出?

答:调用Runtime.exec方法将产生一个本地的进程,并返回一个Process子类的实例,该实例可用于控制进程或取得进程的相关信息。

由于调用Runtime.exec方法所创建的子进程没有自己的终端或控制台,因此该子进程的标准IO(如stdin,stdou,stderr)都通过Process.getOutputStream(),Process.getInputStream(),Process.getErrorStream()方法重定向给它的父进程了。用户需要用这些stream来向子进程输入数据或获取子进程的输出。所以正确执行Runtime.exec("ls")的例程如下:

Java代码

  1. try {

  2. Process process = Runtime.getRuntime().exec(command);

  3. InputStreamReader ir = new InputStreamReader(process.getInputStream());

  4. LineNumberReader input = new LineNumberReader(ir);

  5. String line;

  6. while ((line = input.readLine()) != null)

  7. System.out.println(line);

  8. } catch (java.io.IOException e) {

  9. System.err.println("IOException " + e.getMessage());

  10. }

时间: 2024-08-10 19:00:58

Java 进程的相关文章

jps无法获取到tomcat(java)进程

一.问题描述: Ubuntu 10.10版本,同个JDK 1.6版本,启动tomcat后,使用jps无法获取本机Java进程. 二.jps工作原理: 1. java进程启动时,若没有指定 -Djava.io.tmpdir参数,则会默认新建 /tmp/hsperfdata_${user_name} 文件 2. jps查询java进程时会去扫描 /tmp/hsperfdata_${user_name} 文件 三.jps不能正常工作,可能存在的原因: 1.  启动tomcat的java与jps不同版本

死磕内存篇2 --JAVA进程是怎么突然挂掉的

JVM内存不足导致进程死掉. Native memory allocation (mmap) failed to map 一台服务器上部署很多JAVA进程已经是微服务的常态,但也有些坑. 背景,测试服务器上的一些JAVA进程突然挂掉,查看call back的日志发现如下: # There is insufficient memory for the Java Runtime Environment to continue. # Native memory allocation (mmap) fa

java 进程/线程

进程 OS并发的一个任务 CPU分时间片交替执行 宏观并行 微观串行 线程:在一个进程中并发的一个顺序执行流程 线程的组成: 1.数据 堆空间共享 栈空间独立 2.CPU 3.代码 Thread 线程对象 继承Thread类型 覆盖run方法 直接创建Thread对象 实现Runnable接口 实现run方法 先创建Runnable对象,再创建Thread对象 多线程共同访问同一个对象(临界资源),如果破坏了不可分割的操作(原子操作),就会发生数据不一致的情况 synchronized 同步 s

Java进程CPU使用率高排查

近期java应用,CPU使用率一直很高,经常达到100%,通过以下步骤完美解决,分享一下. 1.jps 获取Java进程的PID. 2.jstack pid >> java.txt 导出CPU占用高进程的线程栈. 3.top -H -p PID 查看对应进程的哪个线程占用CPU过高. 4.echo "obase=16; PID" | bc 将线程的PID转换为16进制. 5.在第二步导出的Java.txt中查找转换成为16进制的线程PID.找到对应的线程栈. 6.分析负载高

jps查看java进程中哪个线程在消耗系统资源

jps或ps -ef|grep java可以看到有哪些java进程,这个不用说了.但值得一提的是jps命令是依赖于/tmp下的某些文件 的. 而某些操作系统,定期会清理掉/tmp下的文件,导致jps无法查看到实际存在的java进程.不过jstat, jstack等命令也同样如此,所以当jps列不出进程的时候,这些命令也都不能用了.不在我们此次讨论范围之内. top -p $pid -H  加上-H这个参数后,会列出有哪些线程.这样就可以看到哪个线程id最消耗系统资源了.看到的线程id是10进制的

Java进程CPU高

Java进程CPU高1-1) 查看$PID进程里面的线程CPU占用状况    top -H -p $PID1-2) 显示线程列表,并按照CPU占用高的线程排序     ps -mp $PID -o THREAD,tid,time | sort -rn2) 找出CPU消耗较多的线程id, 并转换成16进制     printf "%x\n" $TID3) 查询出具体的线程状态     jstack $PID | grep -A 10 0x$TID

Linux下java进程CPU占用率高-分析方法

今天登陆同事的一台gateway 开始以为hive环境登陆不了了,仔细一看看了下是因为机器很卡,我每次等几秒没登陆就ctrl+c了,看了下是有个java进程cpu:340.4%  mem:14.6% 一般解决方法是通过top命令找出消耗资源高的线程id,利用strace命令查看该线程所有系统调用 1. 通过top命令找到可疑进程PID top 一下 可以看出java进程CPU利用率一直保持100%,稳居不下,找到PID 24138 2. 找出消耗资源最高的线程 top -H -p  29580 

java进程卡死问题

原文地址:http://stackoverflow.com/questions/28739600/jvm-hang-and-kill-3-jmap-failed tomcat进程出现了如下异常,并且卡在这个状态几天时间了: 所有线程都停止, 有一些连接停留在 SYNC_RECV 状态 kill -3 没有输出线程栈 jmap 和 jstack 无法正常工作 sudo -u tomcat /usr/java/jdk1.6.0_45/bin/jmap -heap 19938 Attaching to

java进程性能分析步骤-超越昨天的自己系列(11)

java进程load过高分析步骤: top 查看java进程情况 top -Hp 32323 查看某个进程的具体线程情况 printf 0x%x 31973 确认哪一个线程占用cpu比较多,拿出来转成16进制 jstack 32323> jstack.log 注:jstack命令必须由当前运行的java进程用户执行,否则会出现“12377: Operation not permitted”错误 最后: 在jstack.log  中查询相关线程的信息

Jps介绍以及解决jps无法查看某个已经启动的java进程问题【盗用】

1.jps的作用 jps类似linux的ps命令,不同的是ps是用来显示进程,而jps只显示java进程,准确的说是当前用户已启动的部分java进程信息,信息包括进程号和简短的进程command. 2.某个java进程已经启动,用jps却显示不了该进程进程号 这个问题已经碰到过两次了,所以在这里总结下. 现象: 用ps -ef|grep java能看到启动的java进程,但是用jps查看却不存在该进程的id.待会儿解释过之后就能知道在该情况下,jconsole.jvisualvm可能无法监控该进