Java JNI Thread Error (EINVAL)

contents

  1. 1. Description
  2. 2. 問題描述

Keywords: JNI, Java, ProcessBuilder, Thread Error

Error message: [os,thread] Failed to start thread - pthread_create failed (EINVAL) for attributes: stacksize: 136k, guardsize: 0k, detached.

Solution: -Djdk.lang.processReaperUseDefaultStackSize=true

Description

From JNI process, it is fine to execute Java internal multi-threading, but can not use Runetime.exec or ProcessBuilder to do anything. Even through adjust java stack-related option

1
setenv _JAVA_OPTIONS "-ea -Xlog -XX:ThreadStackSize=31744 -XX:VMThreadStackSize=1024 -XX:CompilerThreadStackSize=1024"

cgroup (Linux control group) limition, and set_rlimit. Same error message will be displayed.

When you link large C/C++ program with large thread local storage (TLS), default minimum stacksize computation is not enough to create pthread in OS-level. Only to set -Djdk.lang.processReaperUseDefaultStackSize=true to apply default pthread arguments. Then, it will be working for you.

問題描述

無法透過 JNI 啟動的 JVM 運行外部程序,如 Runtime.exec 或者是 Process 都無法使用,卻可以使用內部執行緒,甚至達到了數百個執行緒。

首先,JVM 內部拆分了好幾種不同的執行緒使用策略,也有不同的資源需求判斷,去挖原始碼的時候會追到底層的 C 部分,就可以看出這些問題。但是錯誤訊息仍然不足,到了 JDK15 後,才能透過 -Xlog dump 更多的配置訊息,但有些配置訊息的翻譯仍然有錯,還是得看原始碼去得知實際情況。

追了好幾天,調適了許多不同的 stack 配置都沒有起色。自己寫的小短碼 JNI 都沒有事情,架在產品上就突然運行失敗。因此,只能去找有沒有現有的產品開發或使用上發生類似的問題,不該去找任何的教學文件。最後挖到幾篇安裝不同版本的產品失敗,據說更改 JDK 版本就能解決,然後有人分析說因為 TLS 的大小變動才導致新版本突然無法工作,才明白果然是 JNI 上的問題。

實作 JNI 介面的時候,若連接的 C++ 程式中存在大量的 thread_local 變數時,那麼在每一個執行緒中都必須給予相對應的大小進行初始化。而對於 JVM 內部會透過一些預設 stack 配置進行最小化,透過 JNI 連接的時候,便會受到影響。

透過 -Djdk.lang.processReaperUseDefaultStackSize=true 取消最小化 stack 配置,直接使用預設的構造行為,這樣就能暫時解決 JNI 啟動的 JVM 無法運行外部程序的問題。很明顯地,每一條執行緒都會開相當大的 stack size,大部分的情況下預設最多為 4 MB,而實際工作的產品中預設就要 20 MB,這個得從其關聯函數庫去分析為什麼需要這麼大的堆疊,可能是寫錯,也或者是連結太多沒有必要的庫進來。