Fork and Exec Using on JAVA

本文探讨了在Java应用程序中直接使用Runtime.exec执行Shell脚本的潜在风险,特别是内存消耗问题,并提出了两种替代方案:一是使用开源API来代替Shell脚本的功能;二是通过REST API调用小型独立应用来执行命令。

Fork and Exec Using on JAVA

Recently I am reading the codes which write by my others. I think it will cause us problem in the future. It is like a small bomb on our server.

The system is using scala codes as follow to execute shell script
          
          Logger.debug(String.format("Executing script: %s %s %s", "/bin/sh", shellScript, date))
          val process = Runtime.getRuntime().exec(Array("/bin/sh", shellScript, date))
          process.waitFor()

          Logger.debug("Script execution finished, process output.")

          val buf = new StringBuffer
          val infos = Source.fromInputStream(process.getInputStream())(Codec.UTF8).getLines
          val errors = Source.fromInputStream(process.getErrorStream())(Codec.UTF8).getLines

          if (!infos.isEmpty) {
            buf.append("Info:<br/>")
            infos.foreach(buf.append("&nbsp;&nbsp;&nbsp;&nbsp;").append(_).append("<br/>"))
          }
          if (!errors.isEmpty) {
            buf.append("Error:<br/>")
            errors.foreach(buf.append("&nbsp;&nbsp;&nbsp;&nbsp;").append(_).append("<br/>"))
          }

          buf.toString()

Then it will call java.lang.Runtime.java, I am reading the codes from oracle jdk1.7 from my understanding.
    public Process exec(String[] cmdarray, String[] envp, File dir)
        throws IOException {
        return new ProcessBuilder(cmdarray)
            .environment(envp)
            .directory(dir)
            .start();
    }

It will call the method java.lang.ProcessBuilder.java
            return ProcessImpl.start(cmdarray,
                                     environment,
                                     dir,
                                     redirects,
                                     redirectErrorStream);

Open the file java.lang.ProcessImpl.java
        return new UNIXProcess
            (toCString(cmdarray[0]),
             argBlock, args.length,
             envBlock, envc[0],
             toCString(dir),
                 std_fds,
             redirectErrorStream);

Then Open the file is java.lang.UNIXProcess.java, it is the native codes.
    private native int forkAndExec(int mode, byte[] helperpath,
                                   byte[] prog,
                                   byte[] argBlock, int argc,
                                   byte[] envBlock, int envc,
                                   byte[] dir,
                                   int[] fds,
                                   boolean redirectErrorStream)
        throws IOException;

Then I check the man book on linux system from here
http://linux.die.net/man/2/fork
From the document, it is saying that 
Under Linux, fork() is implemented using copy-on-write pages, so the only penalty that it incurs is the time and memory required to duplicate the parent's page tables, and to create a unique task structure for the child.

That means if the parent application is running in 4G memory, this sub process will use 4G too.

Actually our situation is worse
-Xmx6144m -Xmn2048m -XX:PermSize=192m -XX:MaxPermSize=192m

How to check the free memory on my machine
http://blog.scoutapp.com/articles/2010/10/06/determining-free-memory-on-linux

free -m             total       used       free     shared    buffers     cached Mem:          8008       7327        680          0        153        690 -/+ buffers/cache:       6483       1524 Swap:            0          0          0

The actually free memory will be Free(680 MB) + Buffers ( 153 MB) + cached (690 MB) = 1523 MB

On that shell script, we are using shell script only doing the curl(actually it is wget), zip (compress file), ftp related operations. So we have 2 options.

1. Do not use Shell Script

Using Open Source FTP Client http://commons.apache.org/proper/commons-net/
I read the source codes, it is using socket to deal with ftp protocol, there is no shell script there.
http://svn.apache.org/repos/asf/commons/proper/net/trunk/src/main/java/org/apache/commons/net/ftp/FTPClient.java

Using Open Source API to compress the file
http://commons.apache.org/proper/commons-compress/

2. Using a small operation provide restAPI to execute shell command
A standalone app with little memory.


References:
http://blog.youkuaiyun.com/vernonzheng/article/details/8644936
http://linux.die.net/man/2/fork

ftp client
http://commons.apache.org/proper/commons-net/

compress
http://commons.apache.org/proper/commons-compress/

#!/bin/bash # ============================================= # OpenFire 启动脚本 - Java 17 兼容版 (修复 ext.dirs 和变量顺序) # 作者: 编程专家 # 用途: 安全启动 OpenFire 服务 # ============================================= # === 强制设置 Java 环境(防止被用户环境覆盖)=== unset JAVA_HOME # 清除继承的 JAVA_HOME export JAVA_HOME="/usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64" export PATH="$JAVA_HOME/bin:$PATH" # 将 Java 17 放到 PATH 前面 # === 设置 OpenFire 主目录(必须放在使用前!)=== export OPENFIRE_HOME="/opt/openfire" # === 关键路径定义 === JAVA_EXEC="$JAVA_HOME/bin/java" LOG_DIR="$OPENFIRE_HOME/logs" LOG_FILE="$LOG_DIR/nohup.out" PID_FILE="$LOG_DIR/openfire.pid" # === 切换到 OpenFire 主目录 === cd "$OPENFIRE_HOME" || { echo "ERROR: Cannot access OpenFire home directory: $OPENFIRE_HOME" >&2 exit 1 } # === 创建日志目录(如不存在)=== if [ ! -d "$LOG_DIR" ]; then mkdir -p "$LOG_DIR" || { echo "ERROR: Cannot create log directory: $LOG_DIR" >&2 exit 1 } fi # === 检查 Java 是否存在且可执行 === if [ ! -x "$JAVA_EXEC" ]; then echo "ERROR: Java executable not found or not executable: $JAVA_EXEC" >&2 ls -la "$JAVA_HOME/bin/" 2>/dev/null || echo "Cannot list contents of $JAVA_HOME/bin" >&2 exit 1 fi # === 构建 Classpath(Java 17 推荐方式)=== CLASSPATH="$OPENFIRE_HOME/lib/*:$OPENFIRE_HOME/resources" # === JVM 参数(不再使用已废弃的 java.ext.dirs)=== JAVA_OPTS="-server -Xms64m -Xmx512m -DopenfireHome=$OPENFIRE_HOME" # === 检查是否有旧进程在运行 === if [ -f "$PID_FILE" ]; then OLD_PID=$(cat "$PID_FILE" 2>/dev/null) if [ -n "$OLD_PID" ] && kill -0 "$OLD_PID" 2>/dev/null; then echo "ERROR: OpenFire already running as PID $OLD_PID" >&2 exit 1 else rm -f "$PID_FILE" echo "Removed stale PID file." fi fi # === 启动 OpenFire 主进程 === echo "Starting OpenFire server using Java: $JAVA_EXEC" nohup "$JAVA_EXEC" $JAVA_OPTS -cp "$CLASSPATH" org.jivesoftware.openfire.starter.ServerStarter >"$LOG_FILE" 2>&1 & # 获取后台进程 PID OPENFIRE_PID=$! # === 写入 PID 文件 === echo $OPENFIRE_PID > "$PID_FILE" if [ $? -ne 0 ]; then echo "ERROR: Failed to write PID file: $PID_FILE" >&2 kill "$OPENFIRE_PID" 2>/dev/null || true exit 1 fi chmod 644 "$PID_FILE" # === 输出成功信息 === echo "OpenFire started successfully (PID $OPENFIRE_PID)" echo "Log output: tail -f $LOG_FILE" echo "Web Console: http://$(hostname -I | xargs):9090" exit 0
11-16
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值