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-subshell 开头);没有分段错误。
【讨论】:
发现一些认为旧的文章可能证实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 的未定义引用
Eclipse:JVM 共享库不包含 JNI_CreateJavaVM 符号
架构 x86_64 的未定义符号:JNI_CreateJavaVM OS-X Xcode