Runtime.exec导致重复的JVM无限期挂起,直到被杀死(Solaris 10)

4 solaris runtime.exec hung

所有,

我们在WebLogic Server 9.2 MP2上运行J2EE应用程序,在Solaris 10上使用jrockit 64位JVM(27.3.1).

我们调用use runtime.exec来调用名为jfmerge的可执行文件来创建PDF文档.

我们发现在Solaris中,当调用runtime.exec时,会临时生成一个重复的JVM以启动jfmerge进程.虽然这是低效的(我们的JVM是5 GB,因此重复的shell JVM也是5 GB),但主要的问题在于,当我们的应用程序中的此功能(PDF生成)负载很重时,有时候重复的JVM永远不会退出.

当JVM挂起时,服务器会产生大问题(极端的应用程序缓慢和终止的用户会话),因为整个重复的JVM将所有5 GB的进程大小写入磁盘交换.

我们注意到以下挂起的线程与挂起的JVM进程相关,直到该进程被手动终止:

"[STUCK] ExecuteThread:'17'表示队列:'weblogic.kernel.Default(自我调整)'"id = 3463 idx = 0x158 tid = 3460 prio = 1 alive,原生,daemon at jrockit/io/FileNativeIO. readBytesPinned(Ljava/io/FileDescriptor; [BII)I(本机方法)在jrockit/io/FileNativeIO.readBytes(FileNativeIO.java:30)at java/io/FileInputStream.readBytes([BII] I(FileInputStream.java)at at Java/io/java/lang/UNIXProcess中的java/io/FileInputStream.read(FileInputStream.java:194)java/io上的java/io/BufferedInputStream.fill(BufferedInputStream.java:218)中的$ DeferredCloseInputStream.read(UNIXProcess.java:227) /BufferedInputStream.read(BufferedInputStream.java:235)^ - 在gov/v3/common/formgeneration/sessionbean/FormsBean.getProcessStatus(FormsBean.java:809)中持有锁:java/io/BufferedInputStream @ 0xfffffffec6510470 [thin lock] Gov/v3/common/formgeneration/sessionbean/FormsBean.createPDF(FormsBean.java:750)gov/v3/common/formgeneration/sessionbean/FormsBean.getTemplateDetails(FormsBean.java:450)at gov/v3/common/formgeneration/SES sionbean/FormsBean.generateSinglePDF(FormsBean.java:1371)位于gov/v3/common/formgeneration/sessionbean/FormsBean.generatePDF(FormsBean.java:263)的gov/v3/common/formgeneration/sessionbean/FormsBean.endorseDocument(FormsBean. java:2377)在政府/ v3/actions的gov/v3/delegates/common/FormsAndNoticesDelegate.endorseDocument(FormsAndNoticesDelegate.java:128)gov/v3/common/formgeneration/sessionbean/Forms_qaco28_EOImpl.endorseDocument(Forms_qaco28_EOImpl.java:214) /common/EndorseDocumentAction.executeRequest(EndorseDocumentAction.java:68)gov/v3/fwk/controller/struts/action/V3CommonDispatchAction.dispatchToExecuteMethod(V3CommonDispatchAction.java:532)at gov/v3/fwk/controller/struts/action/V3CommonDispatchAction .executeBaseAction(V3CommonDispatchAction.java:336)gov/v3/fwk/controller/struts/action/V3BaseDispatchAction.execute(V3BaseDispatchAction.java:69)org/apache/struts/action/RequestProcessor.processActionPerform(RequestProcessor.java:484) )在gov/v3/fwk/controller/struts/reques org/apache/struts/action/RequestProcessor.process(RequestProcessor.java:274)上的tprocessor/V3TilesRequestProcessor.processActionPerform(V3TilesRequestProcessor.java:384)org/apache/struts/action/ActionServlet.process(ActionServlet.java:1482) at org/apache/struts/action/ActionServlet.doGet(ActionServlet.java:507)at gov/v3/fwk/controller/struts/servlet/V3ControllerServlet.doGet(V3ControllerServlet.java:110)at javax/servlet/http/HttpServlet .service(HttpServlet.java:743)位于weblogic/servlet /的weblogic/servlet/internal/StubSecurityHelper $ ServletServiceAction.run(StubSecurityHelper.java:227)的javax/servlet/http/HttpServlet.service(HttpServlet.java:856) weblogic/servlet上的weblogic/servlet/internal/ServletStubImpl.exe(ServletStubImpl.java:283)中的内部/ StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)weblogic/servlet上的weblogic/servlet/internal/ServletStubImpl.execute(ServletStubImpl.java:175) /internal/WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3231 at weblogic/security/acl/internal/AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)at weblogic/security/service/SecurityManager.runAs(SecurityManager.java:121)at weblogic/servlet/internal/WebAppServletContext.securedExecute(WebAppServletContext. java:2002)at weblogic/servlet/internal/WebAppServletContext.execute(WebAppServletContext.java:1908)at weblogic/servlet/internal/ServletRequestImpl.run(ServletRequestImpl.java:1362)at weblogic/work/ExecuteThread.execute(ExecuteThread.java) :209)at weblogic/work/ExecuteThread.run(ExecuteThread.java:181)at jrockit/vm/RNI.c2java(JJJJJ)V(Native Method) - 追踪结束

我们想做几件事:

1.)防止产生重复的JVM,因为在执行简单的jfmerge可执行文件时我们不需要它的任何功能,并且它会产生巨大的开销.

2.)在短期内至少可以防止这个重复的JVM无限期地处理.

Abe*_*ayo 6

这个答案很晚,但我们遇到了同样的问题,而我们面临的问题是Solaris如何管理内存.

问题是当我们有一个应用服务器,在我的情况下使用大量内存10GB,并且我们想运行一个简单的"ls"时,新进程需要10GB才能运行.

Solaris需要在我们的服务器中提供10GB额外的功能,Linux使用称为"写时复制"的功能此功能可减少分支新进程的开销

http://developers.sun.com/solaris/articles/subprocess/subprocess.html

历史背景和问题描述

传统上,Unix只有一种方法可以创建一个新进程:使用fork()系统调用,通常后跟exec()系统调用.fork()调用生成整个父进程的地址空间的副本,exec()将该副本转换为新进程.

(注意:在Solaris OS中,术语交换空间用于描述为系统配置的物理内存和磁盘交换空间的组合.但是,对于其他Unix系统,此术语可能表示磁盘上的交换空间,也称为后备存储为了避免混淆,我将使用术语虚拟内存(VM)来表示物理内存和磁盘交换空间.)

通常,fork/exec方法运行得很好.但是,它在某些情况下有缺点,例如没有充分理由耗尽内存和叉子性能差.

内存不足:对于大内存进程,fork()系统调用可能由于VM数量不足而失败,因为fork()需要两倍的父内存量.即使fork()紧跟一个exec()调用会释放大部分额外内存,也会发生这种情况.发生这种情况时,应用程序通常会终止.

例如,假设64位应用程序此刻正在消耗6千兆字节(GB),并且需要创建一个子进程来运行ls(1)命令.父进程发出fork()调用,只有当前还有另外6 GB的VM可用时才会成功.如果系统没有那么多VM可用(这是一种常见的情况),fork()将失败并使用ENOMEM.显然,ls(1)命令不需要任何接近6 GB的内存来运行,但fork()不知道这一点.

不仅应用程序,而且Sun自己的工具也可能遇到同样的问题.例如,已经为dbx提交了以下Sun RFE(增强请求):"4748951 dbx shell应该将posix_spawn()用于非内置命令而不是fork(2)".

RFE 4748951是在客户的实用程序调用dbx以使用脚本读取庞大的核心文件时发生的,该脚本还需要在dbx中运行cut(1)命令.他们得到了一个无法分叉 - 再次尝试错误消息导致dbx中止.调查显示dbx使用fork/exec执行那个tiny cut(1)命令,并在fork()调用期间用尽VM.

Solaris Java虚拟机(JVM)目前也遇到了同样的问题,如本Sun RFE所述:"5049299使用posix_spawn,而不是fork,在S10上避免交换耗尽".


所以你有3个选择.

1.-先执行Runtime.exec函数.

2.-与其他java服务器创建进程间通信,并在那里执行Runtime.exec指令.

3.-创建一个JNI类来调用系统C函数.我选择了这个选项,它完美无缺.

我把示例代码放在这里.

Java代码.

public class CallOS {
    static {
            System.loadLibrary("CallOS");
    }

    public native int exec(java.lang.String cmd);

    public static void main(String[] args) {
            int returnValue = 0;
            returnValue = new CallOS().exec("ls -la");
            System.out.println("- " + returnValue);
    }
}
Run Code Online (Sandbox Code Playgroud)

C头代码.这是使用javah -jni CallOS生成的

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class CallOS */

#ifndef _Included_CallOS
#define _Included_CallOS
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     CallOS
 * Method:    exec
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_CallOS_exec
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif
Run Code Online (Sandbox Code Playgroud)

C代码.

#include "CallOS.h"
#include <stdlib.h>

JNIEXPORT jint JNICALL Java_CallOS_exec
  (JNIEnv *env, jobject obj, jstring cmd)
{
   jint  retval;
   jbyte *str;

   str = (*env)->GetStringUTFChars(env, cmd, NULL);
   if(str == NULL) return NULL;

   retval = system(str);

   (*env)->ReleaseStringUTFChars(env, cmd, str);
   return retval;
};
Run Code Online (Sandbox Code Playgroud)

我希望这对你有所帮助.