防止java程序打开线程

时间:2016-06-08 11:25:37

标签: java multithreading jvm execve setrlimit

我正在尝试使用c中的execve()来运行java应用程序,更具体地说是一个jar编译的应用程序 类似的东西:

char *cmd[] = {"a.jar"};
execve("a.jar",cmd,NULL);

工作正常但当我尝试使用类似的东西来限制此程序可以打开的线程数时:

struct rlimit rlp;
rlp.rlim_cur = rlp.rlim_max = limit_nproc; 
setrlimit(RLIMIT_NPROC,&rlp);

我有一个JVM的问题,它会打开线程而我正在阻止它,所以我有这个错误:

java.lang.OutOfMemoryError: Cannot create GC thread. Out of system resources.

如何防止在java应用程序中打开的线程而不是JVM打开的线程? !

请注意,问题是如何防止用户线程而不是系统线程,我需要限制运行环境,就像我在第二个代码中所做的那样“RLIMIT_NPROC”

谢谢!

2 个答案:

答案 0 :(得分:2)

这可以通过JVMTI代理来实现。

这个想法是截取本地Thread.start0()方法,并在调用时抛出异常。

以下是用C ++编写的示例代理:

#include <jvmti.h>

// Original native implementation of Thread.start0(), if you wish to call it
extern "C" void JNICALL JVM_StartThread(JNIEnv* env, jthread thread);

void JNICALL StartThreadHook(JNIEnv* env, jthread thread) {
    env->ThrowNew(env->FindClass("java/lang/Error"), "Threads forbidden");
}

void JNICALL VMInit(jvmtiEnv* jvmti, JNIEnv* env, jthread thread) {
    // After VM is initialized, intercept Thread.start0() with our hook function
    jclass thread_class = env->FindClass("java/lang/Thread");
    JNINativeMethod start0 = {(char*)"start0", (char*)"()V", (void*)StartThreadHook};
    env->RegisterNatives(thread_class, &start0, 1);
}

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
    jvmtiEnv* jvmti;
    vm->GetEnv((void**)&jvmti, JVMTI_VERSION_1_0);

    jvmtiEventCallbacks callbacks = {0};
    callbacks.VMInit = VMInit;
    jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
    jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);

    return 0;
}

编译代理:

g++ -fPIC -shared -olibnothreads.so -Wl,-soname,libnothreads.so nothreads.cpp

使用代理运行应用程序:

java -agentpath:/path/to/libnothreads.so -jar app.jar

请注意,您还可以使用JVMTI实现自定义逻辑何时允许以及何时拒绝启动新线程。例如,ThreadStartThreadEnd事件将有助于计算创建的线程。 GetStackTrace函数将帮助查找尝试创建线程的类。

答案 1 :(得分:0)

  

如何防止在Java应用程序中打开的线程而不是JVM打开的线程?

我不确定你能不能。防止JVM创建线程就像是说要限制它创建的String的数量。如果代码创建了一个线程,那么你无能为力。

关于唯一可能有用的是安全策略,但在我阅读时,线程创建不受控制。请参阅Java的docs on the permissions under control

  

java.lang.OutOfMemoryError:无法创建GC线程。超出系统资源。

您可能知道,除了“主”线程之外​​,Java还启动了许多其他在后台工作的JVM特定线程。例如,一个简单的main(String[] args)程序启动“main”,另外5个线程对我来说不包括我认为的GC线程。内存管理和其他重要任务需要JVM线程。如果您将线程限制到GC线程无法启动的点,那么JVM根本无法运行。

<HACK>您可以做的一件事是将线程数限制为精确数,以包含系统线程和“main”。在main()内部,在用户代码有机会启动更多线程之前,您可以使用Thread.getAllStackTraces().size()计算正在运行的线程数,或者将操作系统限制设置为该数字。如果仍然失败,则尝试将1或2添加到size()以考虑堆栈跟踪映射中未考虑的其他后台线程。 </HACK>

所有这些都说,我的问题是你想要完成什么?如果您担心Java进程会占用您的服务器,那么我想知道是否有更好的操作系统设置可以控制为该进程提供多少系统资源。如何限制并发而不是线程数。也许寻找线程亲和力设置?但这将取决于操作系统。