有关java launcher启动JVM时的疑问

yuyinyang 2013-07-11
对于JVM的启动过程我的理解是这样的:
java launcher启动后,会从java.c文件里的 int main(int argc, char ** argv)) 函数进入,然后会create一个新的线程创建JVM并调用方法 int JNICALL JavaMain(void * _args),接下来会在初始化VM的过程中创建一些线程,包括WatcherThread,CompilerThread,GC threads,以及我们运行的java application中create的线程等。在看源码的时候我发现在main()和JavaMain()方法中printf语句并不会在程序运行时打印到标准输出,比如我尝试打印当前线程的kernel tid:
/*
 * Entry point.
 */
int main(int argc, char ** argv)
{
  ...
  CreateExecutionEnvironment(&argc, &argv,
                               jrepath, sizeof(jrepath),
                               jvmpath, sizeof(jvmpath),
                               original_argv);

  printf("Using java runtime at: %s\n", jrepath);
  fprintf(stdout, "Before calling JavaMain, tid is %d\n", syscall(__NR_gettid));
  ifn.CreateJavaVM = 0;
  ...

但是在编译hotspot的时候会打印出来:
引用

...
Linking launcher...
gcc -m64 -Xlinker -O1   -Xlinker -z -Xlinker noexecstack -m64 -Xlinker -export-dynamic  -L `pwd` -o gamma launcher/java_md.o launcher/java.o launcher/jli_util.o launcher/wildcard.o -ljvm -lm -ldl -lpthre
ad
make[6]: Leaving directory `/home/yuyinyang/openjdk_b147_27_yy/openjdk/build/linux-amd64/hotspot/outputdir/linux_amd64_compiler2/product'
All done.
make[5]: Leaving directory `/home/yuyinyang/openjdk_b147_27_yy/openjdk/build/linux-amd64/hotspot/outputdir/linux_amd64_compiler2/product'
cd linux_amd64_compiler2/product && ./test_gamma
java full version "1.6.0_18-b18"
Using java runtime at: /usr/lib/jvm/java-6-openjdk/jre
Before calling JavaMain, tid is 27228
In JavaMain, initializing VM, tid is 27229

所以我想知道这是什么原因呢,难道是运行java程序的时候不是从这entry的吗?感觉不太可能...或者是输出被重定向了?但是我没找到重定向的代码。求解惑
RednaxelaFX 2013-07-11
您的疑问是不是说自己修改了java launcher的代码,但却没看到效果?
那请问您改的具体是什么路径上的java.c,编译是用什么命令在哪个目录执行的?

看您描述的执行结果,看起来您是改了hotspot/share/tools/launcher下的java.c,编译是单独编译了HotSpot VM(只改了那个java.c的话,或者编译整个OpenJDK也一样就是了)。

构建hotspot目录的内容的过程中会生成一个叫做gamma launcher的东西,专门用来针对HotSpot VM做简易测试用的。其主体代码就在hotspot/share/tools/launcher里。您会在代码里看到ifdef GAMMA,那种就是专门给gamma launcher用的代码。

       25 /*
       26  * Gamma (Hotspot internal engineering test) launcher based on 6.0u22 JDK,
       27  * search "GAMMA" for gamma specific changes.
       28  *
       29  * GAMMA: gamma launcher is much simpler than regular java launcher in that
       30  *        JVM is either statically linked in or it is installed in the
       31  *        same directory where the launcher exists, so we don't have to
       32  *        worry about choosing the right JVM based on command line flag, jar
       33  *        file and/or ergonomics. Intead of removing unused logic from source
       34  *        they are commented out with #ifndef GAMMA, hopefully it'll be easier
       35  *        to maintain this file in sync with regular JDK launcher.
       36  */


而实际上“java”这个命令对应的launcher的代码不在hotspot目录里,而在jdk/src/share/bin目录里。所以要修改JDK里的java命令的实现,就得修改jdk这边的java.c(或者jdk/src/<platform>/bin里的平台相关代码),并且构建整个JDK得到j2se-image(或者对应的debug版)才行。或者至少构建jdk目录,而不是hotspot目录,来获得那个java launcher。
yuyinyang 2013-07-12
R大,我按照你说的修改了jdk/src/share/bin目录下的java.c文件,运行的时候看到了效果,多谢。
我还有个问题,应用程序创建的线程(main函数的主线程除外)在hotspot里是如何创建的?我知道貌似在linux平台下,所有的新线程的创建都是在src/os/linux/vm/os_linux.cpp文件中进行pthread_create的,每个新创建的线程都是从一个叫java_start的方法进入的,我尝试对这些进行跟踪并打印线程id,找到了gc thread,compiler thread等线程的构造函数等,但是找不到应用程序里用户创建的线程从哪构造的,请求R大指点。
RednaxelaFX 2013-07-12
yuyinyang 写道
R大,我按照你说的修改了jdk/src/share/bin目录下的java.c文件,运行的时候看到了效果,多谢。

OK,那就好~

yuyinyang 写道
我还有个问题,应用程序创建的线程(main函数的主线程除外)在hotspot里是如何创建的?我知道貌似在linux平台下,所有的新线程的创建都是在src/os/linux/vm/os_linux.cpp文件中进行pthread_create的,每个新创建的线程都是从一个叫java_start的方法进入的,我尝试对这些进行跟踪并打印线程id,找到了gc thread,compiler thread等线程的构造函数等,但是找不到应用程序里用户创建的线程从哪构造的,请求R大指点。

Java层的入口是java.lang.Thread.start()。
它的native实现在:jdk/src/share/native/java/lang/Thread.c
{"start0",           "()V",        (void *)&JVM_StartThread}

对应的HotSpot VM部分的实现在:hotspot/src/share/vm/prims/jvm.cpp
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
yuyinyang 2013-07-15
如果不是您指出来真心找不到那里去,这个entry的代码是用宏定义的,都没法跟踪调用...
我研究一下你说的这部分代码JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)),我发现并非只有java层的mutator线程会从这里调用,貌似还有其他的线程是从这里进入的,您能告诉我是些什么线程吗?
RednaxelaFX 2013-07-15
yuyinyang 写道
如果不是您指出来真心找不到那里去,这个entry的代码是用宏定义的,都没法跟踪调用...
我研究一下你说的这部分代码JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)),我发现并非只有java层的mutator线程会从这里调用,貌似还有其他的线程是从这里进入的,您能告诉我是些什么线程吗?

这里传入的jthread参数只能是java.lang.Thread及其子类的实例,也就是说只能是Java线程。虽然不是所有Java线程都是用户线程(例如说FinalizerThread、Service Thread之类,这些是提供JVM底层服务的线程而不是用户线程),但从JVM的角度看它们有共同特点就是可以执行Java代码。

另外JVM_StartThread当然是可以跟踪调用的。JVM_ENTRY只是个很简单的宏,并不改变声明的函数的名字,在函数体里设断点没问题的。最好用fastdebug build来调试而不要用product build
yuyinyang 2013-07-16
R大,我看到这篇帖子:http://hllvm.group.iteye.com/group/topic/28934
您之后有用到intel的icc编过openjdk吗?或者您知道用icc编具体需要修改些哪些文件、如何修改吗?
RednaxelaFX 2013-07-16
yuyinyang 写道
R大,我看到这篇帖子:http://hllvm.group.iteye.com/group/topic/28934
您之后有用到intel的icc编过openjdk吗?或者您知道用icc编具体需要修改些哪些文件、如何修改吗?

我自己没用ICC编译过OpenJDK,所以抱歉我没啥经验可分享了。可以试试问那帖的楼主看他积累了些什么经验~
yuyinyang 2013-07-16
RednaxelaFX 写道
yuyinyang 写道
R大,我看到这篇帖子:http://hllvm.group.iteye.com/group/topic/28934
您之后有用到intel的icc编过openjdk吗?或者您知道用icc编具体需要修改些哪些文件、如何修改吗?

我自己没用ICC编译过OpenJDK,所以抱歉我没啥经验可分享了。可以试试问那帖的楼主看他积累了些什么经验~

好的,谢谢R大~
Global site tag (gtag.js) - Google Analytics