linux 上的 JNI_CreateJavaVM 会破坏堆栈?

Posted

技术标签:

【中文标题】linux 上的 JNI_CreateJavaVM 会破坏堆栈?【英文标题】:JNI_CreateJavaVM on linux destroys stack? 【发布时间】:2011-11-01 11:06:51 【问题描述】:

这发生在带有 64 位应用程序的 linux 2.6.18-238.5.1.el5 上。我的进程堆栈大小为 10MB。但是,在(成功)调用 JNI_CreateJavaVM 之后,堆栈上似乎只剩下 1-2 MB。如果我通过它 - 我会出现内存错误,就好像我溢出堆栈一样。

一些注意事项:

    如果我不创建 JVM,那么我可以访问整个 10MB 堆栈。 具有相同 makefile 的相同测试程序在 Solaris 上运行良好,即使调用 JVM 也是如此

测试来源:

#include <jni.h>
#include <stdio.h>
#include <stdlib.h>

void CreateVM(JavaVM ** jvm) 

    JNIEnv *env;
    JavaVMInitArgs vm_args;
    JavaVMOption options[1];
    options[0].optionString = (char*)"-Xcheck:jni";

    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 0;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = 0;

    int ret = JNI_CreateJavaVM(jvm, (void**)&env, &vm_args);
    if(ret < 0) 
        printf("\nUnable to Launch JVM\n");
        exit(1);
    

    if ( env->ExceptionCheck() == JNI_TRUE ) 
        printf("exception\n");
        exit(1);
    


void f() 
    printf("inside...\n");
    //eat up a few megs of stack
    char stackTest[0x2FFFFF];
    printf("...returning");


int main(int argc, char* argv[]) 
    JavaVM * jvm;
    CreateVM(&jvm);

    f();

    printf("exiting...\n");
    return 0;

构建命令:

g++ -m64 CTest.cpp -I/import/bitbucket/JDK/jdk1.6.0_26/include -I/import/bitbucket/JDK/jdk1.6.0_26/include/linux -L/import/bitbucket/JDK/ jdk1.6.0_26/jre/lib/amd64 -L/import/bitbucket/JDK/jdk1.6.0_26/jre/lib/amd64/server -ljava -ljvm

【问题讨论】:

你能做一个strace -f a.out 并在互联网上发布结果吗? 【参考方案1】:

好的。现在我可以在f() 中重现一个 SIGSEGV;我用的是 i386 jvm,而且有点老。让我们调试一下内存分配。

$ cat gdb.how
b main
r
b mmap
commands
x/x $sp+4
x/x $sp+8
bt
c
end
c

您可以将$sp+4$sp+8 更改为+8 和+16 等。 gdb 的第一个输出应该看起来像“00000000, 00001000”。我建议你有 jvm 的调试符号(我有)。

$ gdb  -x gdb.how ./a.out  > gdb.log
quit
y

现在,让我们看看如何为线程分配内存:

$ grep Breakpoint\ 2, -A4 gdb.log | grep pthread_create -B 2 | grep 0x00 |cut -d : -f 2 |
perl -e '$a=0;while(<>)s/0x0//;$a+=$_;;print "ibase=16\n".uc($a)."\n";'|bc
4616192

它是所有线程堆栈大小的总和。您可以删除此命令的某些部分以查看实际分配情况,我使用 Sun JVM 创建了 7 个线程。

现在您可以尝试更改一些选项并检查分配给线程堆栈的内存量。

我得到了什么……这很有趣。我有 ulimit -s 作为 8192。 如果我确实以./a.out 开头./a.out,我得到了一个SEGV

$ ./a.out
Segmentation fault

但如果我确实以 (./a.out ) 开头(以 bash-subshel​​l 开头);没有分段错误。

【讨论】:

发现一些认为旧的文章可能证实JVM确实将主线程的大小限制为2MB。至少对于 linux,它可能仍然是一个硬限制,可能是由于线程库。 嗨。我不明白,为什么ulimit -s 做一个 SIGSEGV。主线程(您的函数 main)堆栈很小(3.5-4 MB);检查 RLIMIT_STACK(检查 nptl 和内核源)时不计入额外线程的堆栈。可能 SEGV 来自另一个 ulimit,而不是堆栈的那个? 是的,请为 NPTL 或 LinuxThreads 运行“/lib*/libc.so.6”和grep -i。会有线程库的版本和 glibc 本身的版本(在第一行)。请在此处或问题中发布这两个版本。 获得了 NPTL 2.5。我没有看到LiuxThreads,只有pthreads。根据我所看到的(以及我发现的文章似乎正在确认),JVM 完全忽略了您可能为主线程设置的任何限制,无论是通过 ulimit -s 还是 setrlimit()。我试过 setrlimit,函数调用成功完成,没有错误,但堆栈仍然限制为 2MB。【参考方案2】:

你的 stack-eater 似乎有问题,但如果使用 -O0 则不是

此外,sun 上的 JVM 可能会有所不同,或者它在 solaris 上运行时可以使用更少的堆栈空间。

您如何限制 Linux 和 Solaris 上的堆栈大小?

更新:是的,JVM 使用different settings on OS Solaris and OS Linux:

-XX:ThreadStackSize=512 线程堆栈大小(以千字节为单位)。 (0 表示使用默认堆栈大小) [Sparc: 512; Solaris x86:320(在 5.0 及更早版本中为 256); Sparc 64位:1024; Linux amd64:1024(在 5.0 及更早版本中为 0);所有其他 0。]

我不知道这个设置是关于主线程的,但这表明solaris jvm将使用与linux amd64 jvm不同的内存设置。

=== 更新2

JNI_CreateJavaVM 中的第一个操作是 thread creation,因为 JVM 本身是高度线程化的:

  result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again);
  if (result == JNI_OK) 
    JavaThread *thread = JavaThread::current();
    /* thread is thread_in_vm here */
    *vm = (JavaVM *)(&main_vm);

所以,线程是在CreateJavaVM的调用中创建的

更改“CompilerThreadStackSize”全局变量,因为(!!!!!!!!!AAA 我丢失了我添加回答的所有来源!!!为什么编辑时没有草稿自动保存???)

默认打开AMD64 Linux, JVM has 4M 编译器线程堆栈,默认打开Solaris SPARC64's has 2M 编译器线程堆栈。通常线程在 linux 上获得 1M 堆栈,在 Solaris 上获得 2M 堆栈。

使用1 限制Linux 上的编译器堆栈大小

-XX:CompilerThreadStackSize 调整堆栈大小

值以 kb 为单位。尝试在两个操作系统上将其设置为 2048。

【讨论】:

为什么我的吃栈者有问题?我使用 pmap 监控堆栈使用情况,它告诉我堆栈已正确分配给变量。 我在我的 solaris 和 linux 机器上使用默认堆栈限制,如 ulimit -s 所示。 在linux和solaris上是一样的吗?您的堆栈吞噬者可能已被编译器优化(该变量未使用,因此无法分配)。 linux和solaris上的编译器是一样的吗? 你的 solaris 是 32 还是 64? sparc 还是 x86? 关于 JVM 的线程堆栈大小,我尝试了不同的大小,但无济于事。无论如何,这无关紧要,因为我没有创建任何新线程或执行任何 Java 代码。我所做的只是创建 JVM。 bot 系统是 64 位 linux 是 x86 而 solaris 是 sparcv9。

以上是关于linux 上的 JNI_CreateJavaVM 会破坏堆栈?的主要内容,如果未能解决你的问题,请参考以下文章

对 `JNI_CreateJavaVM' linux 的未定义引用

未定义对“JNI_CreateJavaVM”窗口的引用

Eclipse:JVM 共享库不包含 JNI_CreateJavaVM 符号

架构 x86_64 的未定义符号:JNI_CreateJavaVM OS-X Xcode

Android NDK编译之undefined reference to 'JNI_CreateJavaVM'

来自 JNI_CreateJavaVM (jvm.dll) 的异常 0xC0000005