废话分析jvm

jvm是java虚拟机,这个还是要废话一下,语言这东西,翻译,执行,库基本上是必须的
java的翻译不是编译,而是解释,热点的是编译?靠,大哥,佛家有云,空即是色,那话都没法说了,java是解释,一句一句的解释,碰到一段熟的,就翻译一下,这个从本质上说java还是整体解释,局部编译而已。
翻译执行也在jvm里,也就是我们安装后的一个dll,jvm.dll,还有好几种,什么客户端,服务器端
java的库呢,那就是我们经常说的jdk了,也就是sun帮我写了很多通用的东西,免得我们再写,他们专干这事的。我们也可以干,不过人家专而已,不要随便乱抢饭碗,得有实力。哈哈
所以java=jdk+jvm 或者什么jdk啊,jre啊,都可以,反正晓得是怎么回事就ok。
jvm是一个进程,是os的一个进程。这里要说的就是jvm有什么,jvm啥都没有
有一个翻译java代码成字节码的功能,有一个解释java字节码的功能,有一个内存管理的功能,没了。仅此而已,gc,那就是内存管理啊。线程?java没有线程,真正干活的是os的线程,网络?java也没有网络,干活的也是os。当然jvm干的也不是垃圾活,毕竟os干的有限。文件?java也没有。干了点活,然后交给os了。jni就是这个东东。其实一句话,jvm用c++写的,你想c++能干什么,java就能干什么,c++写的东西(除os外)java就可以干。关于这个,不必争论,见仁见智,只要不是空即是色,都还ok。最可恨的就是空即是色。啥都不是,啥都是,我快崩溃了。
下面就分析一个简单的hello world,运行java 的时候怎么执行的,一颗豌豆开始旅行了
哈哈
首先javac的就不分析了,毫无意思,编译的东西,将java源代码翻译成class字节码,以后会分析jvm如何执行字节码的,头都大了,又多又杂,不是人干的。
甭管它啥玩意,jvm是个dll,也就是一大堆函数,我们得找入口,入口就是main,毫无疑问
java.exe这个就是有main的,在java.c中我们找到了,这里只分析一下windowns的,也就是winmain,看
  return JLI_Launch(margc, margv,
                   sizeof(const_jargs) / sizeof(char *), const_jargs,
                   sizeof(const_appclasspath) / sizeof(char *), const_appclasspath,
                   FULL_VERSION,
                   DOT_VERSION,
                   (const_progname != NULL) ? const_progname : *margv,
                   (const_launcher != NULL) ? const_launcher : *margv,
                   (const_jargs != NULL) ? JNI_TRUE : JNI_FALSE,
                   const_cpwildcard, const_javaw, const_ergo_class);
这个大家伙,winmain做了一些垃圾活后就交给这个函数了,
我们顺藤摸瓜。这个函数不用说了,肯定是首先初始化,分析参数,你想想,你用vc些个main,能干啥?不就是初始化,分析输入参数嘛 LoadJavaVM CreateExecutionEnvironment   InitLauncher ParseArguments 一大堆,热火朝天啊。
初始化以后再说,我们分析大头,看这个 return ContinueInNewThread(&ifn, argc, argv, jarfile, classname, ret);
这个就发上调用ContinueInNewThread0 ,一般在函数后面带有0,1,2等的就是真正干活的,我们跟进去
int
ContinueInNewThread0(int (JNICALL *continuation)(void *), jlong stack_size, void * args) {
    int rslt = 0;
    unsigned thread_id;

#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
#define STACK_SIZE_PARAM_IS_A_RESERVATION  (0x10000)
#endif

    /*
     * STACK_SIZE_PARAM_IS_A_RESERVATION is what we want, but it's not
     * supported on older version of Windows. Try first with the flag; and
     * if that fails try again without the flag. See MSDN document or HotSpot
     * source (os_win32.cpp) for details.
     */
    HANDLE thread_handle =
      (HANDLE)_beginthreadex(NULL,
                             (unsigned)stack_size,
                             continuation,
                             args,
                             STACK_SIZE_PARAM_IS_A_RESERVATION,
                             &thread_id);
    if (thread_handle == NULL) {
      thread_handle =
      (HANDLE)_beginthreadex(NULL,
                             (unsigned)stack_size,
                             continuation,
                             args,
                             0,
                             &thread_id);
    }
    if (thread_handle) {
      WaitForSingleObject(thread_handle, INFINITE);
      GetExitCodeThread(thread_handle, &rslt);
      CloseHandle(thread_handle);
    } else {
      rslt = continuation(args);
    }
    return rslt;
}


看到没有,线程 windowns的线程调用 beginthreadex,调用哪个函数呢? javaMain
我们看看
int JNICALL
JavaMain(void * _args)
{
    JavaMainArgs *args = (JavaMainArgs *)_args;
    int argc = args->argc;
    char **argv = args->argv;
    char *jarfile = args->jarfile;
    char *classname = args->classname;
    InvocationFunctions ifn = args->ifn;

    JavaVM *vm = 0;
    JNIEnv *env = 0;
    jstring mainClassName;
    jclass mainClass;
    jmethodID mainID;
    jobjectArray mainArgs;
    int ret = 0;
    jlong start, end;

    /* Initialize the virtual machine */
    start = CounterGet();
    if (!InitializeJVM(&vm, &env, &ifn)) {
        JLI_ReportErrorMessage(JVM_ERROR1);
        exit(1);
    }

    if (printVersion || showVersion) {
        PrintJavaVersion(env, showVersion);
        CHECK_EXCEPTION_LEAVE(0);
        if (printVersion) {
            ret = 0;
            goto leave;
        }
    }

    /* If the user specified neither a class name nor a JAR file */
    if (printXUsage || printUsage || (jarfile == 0 && classname == 0)) {
        PrintUsage(env, printXUsage);
        CHECK_EXCEPTION_LEAVE(1);
        goto leave;
    }

    FreeKnownVMs();  /* after last possible PrintUsage() */

    if (JLI_IsTraceLauncher()) {
        end = CounterGet();
        JLI_TraceLauncher("%ld micro seconds to InitializeJVM\n",
               (long)(jint)Counter2Micros(end-start));
    }

    /* At this stage, argc/argv have the applications' arguments */
    if (JLI_IsTraceLauncher()){
        int i;
        printf("Main-Class is '%s'\n", classname ? classname : "");
        printf("Apps' argc is %d\n", argc);
        for (i=0; i < argc; i++) {
            printf("    argv[%2d] = '%s'\n", i, argv[i]);
        }
    }

    ret = 1;

    /*
     * Get the application's main class.
     *
     * See bugid 5030265.  The Main-Class name has already been parsed
     * from the manifest, but not parsed properly for UTF-8 support.
     * Hence the code here ignores the value previously extracted and
     * uses the pre-existing code to reextract the value.  This is
     * possibly an end of release cycle expedient.  However, it has
     * also been discovered that passing some character sets through
     * the environment has "strange" behavior on some variants of
     * Windows.  Hence, maybe the manifest parsing code local to the
     * launcher should never be enhanced.
     *
     * Hence, future work should either:
     *     1)   Correct the local parsing code and verify that the
     *          Main-Class attribute gets properly passed through
     *          all environments,
     *     2)   Remove the vestages of maintaining main_class through
     *          the environment (and remove these comments).
     */
    if (jarfile != 0) {
        mainClass = LoadMainClass(env, JNI_TRUE, jarfile);
    } else {
        mainClass = LoadMainClass(env, JNI_FALSE, classname);
    }
    CHECK_EXCEPTION_NULL_LEAVE(mainClass);

    /*
     * The LoadMainClass not only loads the main class, it will also ensure
     * that the main method's signature is correct, therefore further checking
     * is not required. The main method is invoked here so that extraneous java
     * stacks are not in the application stack trace.
     */
    mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                       "([Ljava/lang/String;)V");
    CHECK_EXCEPTION_NULL_LEAVE(mainID);

    /* Build argument array */
    mainArgs = NewPlatformStringArray(env, argv, argc);
    CHECK_EXCEPTION_NULL_LEAVE(mainArgs);

    /* Invoke main method. */
    (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);

    /*
     * The launcher's exit code (in the absence of calls to
     * System.exit) will be non-zero if main threw an exception.
     */
    ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;

leave:
    /*
     * Always detach the main thread so that it appears to have ended when
     * the application's main method exits.  This will invoke the
     * uncaught exception handler machinery if main threw an
     * exception.  An uncaught exception handler cannot change the
     * launcher's return code except by calling System.exit.
     */
    if ((*vm)->DetachCurrentThread(vm) != 0) {
        JLI_ReportErrorMessage(JVM_ERROR2);
        ret = 1;
    }
    /*
     * Wait for all non-daemon threads to end, then destroy the VM.
     * This will actually create a trivial new Java waiter thread
     * named "DestroyJavaVM", but this will be seen as a different
     * thread from the one that executed main, even though they are
     * the same C thread.  This allows mainThread.join() and
     * mainThread.isAlive() to work as expected.
     */
    (*vm)->DestroyJavaVM(vm);

    return ret;
}

好家伙,一大堆
别急,慢慢分析
InitializeJVM LoadMainClass  (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
这三是干活的,2,8定理啊,汗
初始化jvm,该进程的jvm,一个进程一个jvm,加载helloworld类,调用helloworld的main方法,开始执行 ,要入正题了,七拐八拐,还算ok。
明天继续。。。

static jboolean
InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn)
{
    JavaVMInitArgs args;
    jint r;

    memset(&args, 0, sizeof(args));
    args.version  = JNI_VERSION_1_2;
    args.nOptions = numOptions;
    args.options  = options;
    args.ignoreUnrecognized = JNI_FALSE;

    if (JLI_IsTraceLauncher()) {
        int i = 0;
        printf("JavaVM args:\n    ");
        printf("version 0x%08lx, ", (long)args.version);
        printf("ignoreUnrecognized is %s, ",
               args.ignoreUnrecognized ? "JNI_TRUE" : "JNI_FALSE");
        printf("nOptions is %ld\n", (long)args.nOptions);
        for (i = 0; i < numOptions; i++)
            printf("    option[%2d] = '%s'\n",
                   i, args.options[i].optionString);
    }

    r = ifn->CreateJavaVM(pvm, (void **)penv, &args);
    JLI_MemFree(options);
    return r == JNI_OK;
}看这个 InitializeJVM 有个   r = ifn->CreateJavaVM(pvm, (void **)penv, &args);
就是创建jvm示例了。
好家伙,到jni了
HS_DTRACE_PROBE_DECL3(hotspot_jni, CreateJavaVM__entry, vm, penv, args);
DT_RETURN_MARK_DECL(CreateJavaVM, jint);

_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args) {
  HS_DTRACE_PROBE3(hotspot_jni, CreateJavaVM__entry, vm, penv, args);

  jint result = JNI_ERR;
  DT_RETURN_MARK(CreateJavaVM, jint, (const jint&)result);

  // We're about to use Atomic::xchg for synchronization.  Some Zero
  // platforms use the GCC builtin __sync_lock_test_and_set for this,
  // but __sync_lock_test_and_set is not guaranteed to do what we want
  // on all architectures.  So we check it works before relying on it.
#if defined(ZERO) && defined(ASSERT)
  {
    jint a = 0xcafebabe;
    jint b = Atomic::xchg(0xdeadbeef, &a);
    void *c = &a;
    void *d = Atomic::xchg_ptr(&b, &c);
    assert(a == (jint) 0xdeadbeef && b == (jint) 0xcafebabe, "Atomic::xchg() works");
    assert(c == &b && d == &a, "Atomic::xchg_ptr() works");
  }
#endif // ZERO && ASSERT

  // At the moment it's only possible to have one Java VM,
  // since some of the runtime state is in global variables.

  // We cannot use our mutex locks here, since they only work on
  // Threads. We do an atomic compare and exchange to ensure only
  // one thread can call this method at a time

  // We use Atomic::xchg rather than Atomic::add/dec since on some platforms
  // the add/dec implementations are dependent on whether we are running
  // on a multiprocessor, and at this stage of initialization the os::is_MP
  // function used to determine this will always return false. Atomic::xchg
  // does not have this problem.
  if (Atomic::xchg(1, &vm_created) == 1) {
    return JNI_ERR;   // already created, or create attempt in progress
  }
  if (Atomic::xchg(0, &safe_to_recreate_vm) == 0) {
    return JNI_ERR;  // someone tried and failed and retry not allowed.
  }

  assert(vm_created == 1, "vm_created is true during the creation");

  /**
   * Certain errors during initialization are recoverable and do not
   * prevent this method from being called again at a later time
   * (perhaps with different arguments).  However, at a certain
   * point during initialization if an error occurs we cannot allow
   * this function to be called again (or it will crash).  In those
   * situations, the 'canTryAgain' flag is set to false, which atomically
   * sets safe_to_recreate_vm to 1, such that any new call to
   * JNI_CreateJavaVM will immediately fail using the above logic.
   */
  bool can_try_again = true;

  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);
    *(JNIEnv**)penv = thread->jni_environment();

    // Tracks the time application was running before GC
    RuntimeService::record_application_start();

    // Notify JVMTI
    if (JvmtiExport::should_post_thread_life()) {
       JvmtiExport::post_thread_start(thread);
    }
    // Check if we should compile all classes on bootclasspath
    NOT_PRODUCT(if (CompileTheWorld) ClassLoader::compile_the_world();)
    // Since this is not a JVM_ENTRY we have to set the thread state manually before leaving.
    ThreadStateTransition::transition_and_fence(thread, _thread_in_vm, _thread_in_native);
  } else {
    if (can_try_again) {
      // reset safe_to_recreate_vm to 1 so that retrial would be possible
      safe_to_recreate_vm = 1;
    }

    // Creation failed. We must reset vm_created
    *vm = 0;
    *(JNIEnv**)penv = 0;
    // reset vm_created last to avoid race condition. Use OrderAccess to
    // control both compiler and architectural-based reordering.
    OrderAccess::release_store(&vm_created, 0);
  }

  return result;
}

Threads::create_vm((JavaVMInitArgs*) args, &can_try_again);
看这个,这个是不折不扣的jni也就是jvm的函数了,开始都是壳子而已。脱壳不容易啊

猜你喜欢

转载自cooldatabase.iteye.com/blog/547649