阻止 java 程序打开线程

prevent java program from opening threads

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

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 打开的线程? !

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

谢谢!

how can I prevent the threads opened in the java application but not those opened by the JVM ?

我不确定你能做到。阻止 JVM 创建线程就像说您想限制它创建的 String 的数量一样。如果代码创建了一个线程,那么您将无能为力。

唯一可能有用的是安全策略,但正如我所读,线程创建不受控制。参见 Java 的 docs on the permissions under control

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

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

<HACK> 您可以做的一件事是将线程数限制为精确的数量以包括系统线程和 "main"。在 main() 内部,在用户代码有机会启动更多线程之前,您可以使用 Thread.getAllStackTraces().size() 或然后设置 OS 限制来计算 运行ning 线程的数量到那个数字。如果仍然失败,则尝试将 1 或 2 添加到 size() 以说明堆栈跟踪图中未说明的其他后台线程。 </HACK>

综上所述,我的问题是您想要完成什么?如果您担心 Java 进程接管您的服务器,那么我想知道是否有 OS 设置可以更好地控制分配给该进程的系统资源数量。如何限制并发而不是线程数。也许寻找线程关联设置?然而,这将非常 OS 依赖。

这可以通过 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 来实现自定义逻辑,何时允许以及何时拒绝启动新线程。例如,ThreadStart and ThreadEnd events will help to count created threads. GetStackTrace 函数将有助于查找哪些 类 正在尝试创建线程。