《HotSpot实战》—— 2.2 启动

2.2 启动

Launcher(启动器),是用来启动JVM和应用程序的工具。在这一节中,我们将看到HotSpot中提供了两种Launcher类型,分别是通用启动器和调试版启动器。

2.2.1 Launcher

通用启动器(Generic Launcher)是指我们比较熟悉的JDK命令程序:java(含javaw)。java是由JDK自带的启动Java应用程序的工具。为启动一个Java应用程序,java将准备一个Java运行时环境(即JRE)、加载指定的类并调用它的main方法。类加载的前提条件是由JRE在指定路径下找到类加载器和应用程序类。一般来说,JRE将在以下3种路径下搜索类加载器和其他类:

  • 引导类路径(bootstrap class path);
  • 已安装的扩展(installed extensions);
  • 用户类路径(user class path)。

类被加载进来之后,java会将全限定类名或JAR文件名之后的非选项类参数作为参数传递给main方法。

javaw命令等同于java,只是javaw没有控制台窗口。当你不想显示一个命令提示符窗口时,可以使用javaw。但是如果由于某些原因启动失败,javaw仍将显示一个对话框提供错误信息。

1.基本用法

java和javaw的命令格式如下所示:

java [ option ] class [ argument ... ]
java [ option ] -jar file.jar [ argument ... ]
javaw [ option ] class [ argument ... ]
javaw [ option ] -jar file.jar [ argument ... ]

其中class是要调用的类名,而file.jar是要调用的JAR文件名。

值得注意的是,我们需要区分选项和参数的不同用途。

选项(option)是传递给VM的参数。目前,有两类VM选项,包括标准VM选项和非标准VM选项。其中,非标准选择在使用时以“-X”或“-XX”指定。
参数(argument)是传递给main方法的参数。
注意 对于启动器,有一套标准选项(standard options),在当前和将来的版本中都将支持。此外, HotSpot虚拟机默认提供一套非标选项(non-standard options),这些非标选项有可能在将来版本中更改。另外,32位JDK和64位JDK命令选项也会有所不同。

2.标准VM选项

标准VM选项主要包括以下几项。

  • -client、-server:指定HotSpot以client或server模式运行虚拟机。对于64位JDK,将忽略此选项,默认以server模式运行虚拟机。
  • -agentlib:libname[=options]:按照库名libname载入本地代理库(agent library)。如-agentlib:hprof、-agentlib:jdwp=help、-agentlib:hprof=help。
  • -agentpath:pathname[=options]:按照完整路径名pathname载入本地代理库。
  • -classpath、-cp:指定类文件搜索路径。
  • -Dproperty=value:设置系统属性值
  • -jar:执行封装在jar文件中的应用程序。
  • -javaagent:jarpath[=options]:加载Java编程语言代理库,可参阅java.lang.instrument。
  • -verbose、-verbose:class:显示每个被加载的类信息。
  • -verbose:gc:报告每个垃圾回收事件。
  • -verbose:jni:报告关于调用本地方法和其他本地接口的信息。
  • -X:显示非标准选项信息,然后退出。

3.非标准VM选项

以“-X”指定的非标准VM选项主要包括以下几项1。

  • -Xint:以解释模式运行虚拟机。禁用编译本机代码,并由解释器(interpreter)执行所有字节码。
  • -Xbatch:禁用后台编译。一般来说,虚拟机将编译方法作为后台任务,虚拟机在解释器模式下运行某方法时,需要等到后台编译完成该方法的编译任务。该参数将禁用后台编译,使方法的编译作为前台任务直到完成为止。
  • -Xbootclasspath:指定引导类和资源文件的搜索路径。
  • -Xcheck: jni:对于Java 本地接口JNI函数执行额外的检查。JVM验证传递给 JNI 函数的参数。在本机代码中遇到任何无效的数据将导致JVM终止。使用此选项时,会带来一些性能损失。
  • -Xfuture:执行严格的类文件格式检查。
  • -Xnoclassgc:禁用类垃圾回收。
  • -Xincgc:启用增量垃圾回收器。
  • -Xloggc::报告垃圾回收事件,并记录到指定的文件中。
  • -Xms:设置Java堆的初始化大小。
  • -Xmxn:设置Java堆的最大值。
  • -Xssn:设置Java线程的栈大小。
  • -Xprof:输出CPU性能数据。

4.隐藏的非标VM选项

这一类选项以“-XX”指定。该类VM选项数量十分可观,可以说有成百上千个也不为过。本书将在各章节中,附上一些相关的虚拟机选项和功能描述,以供参考。

5.gamma:调试版启动器

HotSpot提供了一个精简调试Launcher,称为gamma。相对于通用Launcher,gamma就安装在与JVM库相同的目录下,或者与JVM库静态链接为一个库文件,因此可以把gamma看作是精简了虚拟机选项解析等逻辑的java命令。

事实上,为便于维护,OpenJDK就是基于同一套Launcher代码维护了gamma launcher和通用launcher的,对于差异代码则使用#ifndef GAMMA进行注释区分。gamma启动器入口位于hotspot/src/share/tools/luncher/java.c;通用Launcher的入口并不在hotspot工程下,感兴趣的读者可以在与hotspot同级目录jdk下找到hotspot/../jdk/src/share/bin/main.c。

从本节开始,我们将以Launcher作为切入点,对HotSpot进行实战调试和分析。为方便调试,我们将在Linux平台上基于gamma启动器来讲解HotSpot启动过程。

2.2.2 虚拟机生命周期

图2-6描述了一个完整的虚拟机生命周期,具体过程如下。

(1)Launcher启动后,首先进入Launcher的入口,即main函数。正像稍后看到的那样,main工作的重点是:创建一个运行环境,为接下来启动一个新的线程创建JVM并跳到Java主方法做好一切准备工作。

(2)环境就绪后,Launcher启动JavaMain线程,将程序参数传递给它。如清单2-21所示,Launcher调用ContinueInNewThread()函数启动新的线程并继续执行任务。新的线程将要执行的任务由该函数的第一个参数指定,即JavaMain()函数。这时,新线程将要阻塞当前线程,并在新线程中开启一段相对独立的历程,去完成Launcher赋予它的使命。

清单2-21

来源:hotspot/src/share/tools/luncher/java.c

描述:Launcher启动JavaMain线程
return ContinueInNewThread(JavaMain, threadStackSize, (void*)&args);

(3)一般来说,JavaMain线程将伴随应用程序的整个生命周期。首先,它要做的便是在Launcher模块内调用InitializeJVM()函数,初始化JVM。值得一提的是,在理解虚拟机生命周期复杂的模块调用过程时,我们不能对Launcher模块本身抱有过高的期待。毕竟,Launcher模块本身无力实现这些核心功能,它必须借助其他专门模块来提供相应功能。因此,在阅读源代码时,我们应当培养这样的意识,在遇到某个核心功能或重要组件时,首先问自己几个问题:核心功能是由哪个模块提供的?它最终是为系统哪个组件提供服务的?它是以什么形式向调用者提供服务的?养成这种意识,对于独立分析和思考系统运作具有重要的意义。

Launcher模块本身并不具有创建虚拟机的能力。下面我们将看到,有哪些模块参与了这个过程。由于Launcher模块需要借助自身以外的力量完成任务,理所当然地,它需要拥有访问外部接口的能力。稍后将提到一些数据结构,它们持有外部接口的函数指针,Launcher通过它们可以达到调用外部接口的目的。

(4)虚拟机在Prims模块中定义了一些以“JNI_”为前缀而命名的函数,并向外部提供这些jni接口。JNI_CreateJavaVM()函数就是其中一个,它为外部程序提供创建JVM的服务。前面提到的创建JVM的任务,实际上就是调用了JNI_CreateJavaVM()函数。JNI模块是连接虚拟机内部与外部程序的桥梁,JVM系统内部的命名空间对JNI模块都是可见的,因此它可以调用内部模块并通过接口向外提供查看和操纵JVM的能力。JNI_CreateJavaVM()函数调用Threads模块create_vm()函数完成最终的虚拟机的创建和初始化工作。

(5)可以说,create_vm()函数是JVM启动过程的精华部分,它初始化了JVM系统中绝大多数的模块。

(6)调用add()函数,将线程加入线程队列。

(7)调用create()函数,创建虚拟机线程“VMThread”;

(8)调用vm_init_globals()函数,初始化全局数据结构;

(9)调用init_globals()函数,初始化全局模块;

(10)调用LoadClass()函数,加载应用程序主类;

(11)调用jni_CallStaticVoidMethod()函数,实现对Java应用程序的主方法的调用;

(12)调用jni_DetachCurrentThread()函数;

(13)调用jni_DestroyJavaVM()函数,销毁JVM后退出。

接下来,我们将选取一些重要过程展开详解。

2.2.3 入口:main函数

与其他应用程序一样,Launcher的入口是一个main函数。在不同操作系统中,main函数的原型看起来会有些差异。例如在UNIX或Linux系统中,按照POSIX规范的函数原型如清单2-22所示。

清单2-22

来源:hotspot/src/share/tools/luncher/java.c

描述:launcher入口
int
main(int argc, char ** argv)

而在Windows平台上,其原型如清单2-23所示。

清单2-23

来源:jdk/src/share/bin/main.c

描述:launcher入口
int WINAPI
WinMain(HINSTANCE inst, HINSTANCE previnst, LPSTR cmdline, int cmdshow)

main函数的程序流程如图2-7所示。

在main函数执行的最后一步,程序将启动一个新的线程并将Java程序参数传递给它,接下来阻塞自己,并在新线程中继续执行。新线程也可称为为主线程,它的执行入口是JavaMain。

2.2.4 主线程

一般来说,主线程将伴随应用程序的整个生命周期。打个形象的比喻:JavaMain好比一个外壳,应用程序便是在这个外壳的包裹下完成执行的。它的函数原型如清单2-24(a)所示。

清单2-24(a)

来源:hotspot/src/share/tools/luncher/java.c

描述:启动新线程执行该方法
int JNICALL
JavaMain(void * _args)```
在介绍JavaMain的主要流程前,我们先了解几个重要的基础数据结构,它们在调用主方法、断开主线程和销毁JVM的过程中发挥重要作用的数据结构。它们分别是JavaVM、JNIEnv和InvocationFunctions。

JavaVM类型是一个结构体,它拥有一组少而精的函数指针2。顾名思义,这几个函数为JVM提供了诸如连接线程、断开线程和销毁虚拟机等重要功能。在JavaMain的流程中,我们也可以看到这些功能的执行。HotSpot定义了大量的运行时接口,上述功能实际上是由这些接口提供的。如图2-8所示,在JavaMain运行时由InitializeJVM模块将JavaVM的这些成员赋上正确的值,指向相应的JNI接口函数上。

同JavaVM类型类似,JNIEnv也是拥有一组函数指针的结构体。不过,相对于JavaVM来说,它是一个重量级类型,JNIEnv容纳了大量的函数指针成员。同样地,在JavaMain运行时由InitializeJVM模块将JNIEnv的这些成员赋上正确的值,指向相应的JNI接口函数上。

InvocationFunctions中定义了2个函数指针,CreateJavaVM和GetDefaultJavaVMInitArgs,如图2-9所示,这2个函数在加载libjvm时中已经指派好了。

<div style="text-align: center"><img src="https://yqfile.alicdn.com/96605d8896aee78c16258ead7b0fad7f037b192f.png" width="" height="">
</div>

在JavaMain中,拥有3个局部变量:vm、env和ifn,分别对应着上述JavaVM、JNIEnv和InvocationFunctions这三种类型。JavaMain的主要流程如图2-10所示。

(1)初始化虚拟机:调用InitializeJVM模块,将JavaVM和JNIEnv类型的成员指向正确的jni函数上。

(2)获取应用程序主类(main class),如清单2-24(b)所示。

清单2-24(b)

jclass mainClass = LoadClass(env, classname);

(3)获取应用程序主方法(main method),如清单2-24(c)所示。

清单2-24(c)

jmethodID mainID = (*env)->GetStaticMethodID(env, mainClass, "main",

                                   "([Ljava/lang/String;]V"];
(4)传递应用程序参数并执行主方法,如清单2-24(d)所示。

清单2-24(d)

(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);


<div style="text-align: center"><img src="https://yqfile.alicdn.com/f36bc6fc40177c9cfcdea3fa5183949542faf4ba.png" width="" height="">
</div>

(5)与主线程断开连接,如清单2-24(e)所示。

清单2-24(e)

(*vm)->DetachCurrentThread(vm);

(6)主方法执行完毕,等待非守护线程结束,然后创建一个名为“DestroyJavaVM”的Java线程执行销毁JVM任务,如清单2-24(f)所示。

清单2-24(f)

(*vm)->DestroyJavaVM(vm);

练习3

阅读源代码,认真分析JavaMain函数,体会它在JVM中的作用和地位。如果通过前面的学习,你已经掌握了调试的基本方法,请仔细调试这部分程序。
####2.2.5 InitializeJVM函数
在第1章中,我们知道,在编译HotSpot项目后,启动脚本hotspot中会默认设置一个断点,即InitializeJVM。启动GDB调试HotSpot,JVM开始运行HelloWorld程序,但是程序并不急于打印“Hello hotspot!”,而是先停在了断点“Breakpoint 1”上(如图1-6所示),即InitializeJVM函数。

现在,我们想将断点往前挪一点,以便于我们详细了解JavaMain的运行细节。利用GDB,我们将断点设置在JavaMain函数的入口处,GDB界面中输入如下命令:

(gdb)break java.c:JavaMain

或者直接使用代码行数:

(gdb)break java.c:396`
断点设置完毕后,我们再次启动调试,如图2-11所示。

HotSpot运行至第396行,停了下来。实际上,我们在这里共设置了2个断点(JavaMain和InitializeJVM)。输入continue命令让程序继续运行至第1270行,即InitializeJVM。

InitializeJVM的原型如清单2-25所示。

清单2-25

来源:hotspot/src/share/tools/launcher/java.c & jdk/src/share/bin/java.c

描述:InitializeJVM
static jboolean
InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn)

数字1270的含义是下一条将要运行的语句行数,如图2-12所示,这行代码是一条memset语句,用来对main()函数的参数args进行初始化填零。

利用GDB调试工具,我们还可以深入到InitializeJVM内部,看看InitializeJVM是如何初始化JVM的。由前文可以,InitializeJVM的任务之一就是需要完成对vm和env指派接口函数的重任。在调用InitializeJVM返回后,通过GDB查看命令,我们可以看到vm的函数指针成员得到了赋值,如图2-13所示。

此外,InitializeJVM中还会打印一些额外信息,如图2-12所示,可以看到InitializeJVM打印了一些与JVM的版本和选项相关的信息。

通过这些调试过程,相信读者对使用GDB调试HotSpot又有了新的认识。可是,到现在为止,我们仍然没有接触到与JVM的创建或初始化相关的实质内容,只是知道在调用CreateJavaVM之后,得到了大量的JNI函数。显然,这一过程向我们屏蔽了很多细节。但是换句话说,现在我们距离JVM初始化的核心内容仅一步之遥了。

在继续深入了解JVM的创建和初始化过程之前,我们希望你能够做些小的练习,以便巩固刚才学过的知识,同时为接下来的深入学习打下良好的实践基础。

练习4

将断点设置在JavaMain跟踪调试,在InitializeJVM返回之后,确认env成员已指派到了正确的jni接口函数上。

练习5

试一试在你安装的正式版JDK中,找到下面这些函数符号:

JNI_CreateJavaVM

JNI_GetDefaultJavaVMInitArgs
提示 在Windows上,可以使用DLL export Viewer等dll查看工具列出其中包含的符号;在UNIX上,可以通过nm等工具查看。

2.2.6 JNI_CreateJavaVM函数

创建JVM的程序模块是JNI_CreateJavaVM。JNI_CreateJavaVM主要任务是调用Threads模块的create_vm()函数,以完成最终的虚拟机创建和初始化工作。

在Threads模块中,实现了对虚拟机各个模块的初始化,以及创建虚拟机线程。这些被初始化的模块,在本书后续章节中均有大量涉及,因此理解这一过程对于其余章节的理解十分重要。为了保证知识的连贯性,避免打断对启动过程的叙述,我们将具体的初始过程安排在2.3小节中继续探讨。

注意 vm和env是在JNI_CreateJavaVM接口中实现赋值的。

此外,JNI_CreateJavaVM还将为vm和env分配JNI接口函数。

练习6:

设置断点并调试HotSpot,跟踪vm和env的赋值。

2.2.7 调用Java主方法

在JavaMain中,虚拟机得到初始化之后,接下来就将执行应用程序的主方法。通过env引用jni_CallStaticVoidMethod函数(原型如清单2-26所示),可以执行一个由“static”和“void”修饰的方法,即Java应用程序主类的main方法。

清单2-26

来源:hotspot/src/share/vm/prims/jni.h

描述:JNI函数:调用静态void方法

void
CallStaticVoidMethod(jclass cls, jmethodID methodID, ...)

读到这里,细心的读者可能会想弄明白:由清单2-24(c)和清单2-26可知,主方法是根据JVM内部一个唯一的方法ID(即methodID)定位到的。那么,我们不禁想问,JVM是如何根据methodID定位到要执行的方法的?方法在JVM内部又是什么样的呢?如果你还没想过这个问题,那么请闭上眼睛,花上几分钟思考一下这个问题。

这里我们暂时不急着回答这个问题,通过本书后续章节对类的解析以及方法区等知识点的学习,这些疑惑就可以迎刃而解了。

为了执行主类的main方法,将在jni_invoke_static中通过调用JavaCalls模块完成最终的执行Java方法。在HotSpot中,所有对Java方法的调用都需要通过类JavaCalls来完成。

清单2-27

来源:hotspot/src/share/vm/prims/jni.cpp

描述:JNI函数:jni_invoke_static

methodHandle method(THREAD, JNIHandles::resolve_jmethod_id(method_id));
JavaCalls::call(result, method, &java_args, CHECK);

清单2-27中是这部分逻辑的实现:首先根据method_id转换成方法句柄,然后调用JavaCalls模块方法实现从JVM对Java方法的调用。

2.2.8 JVM退出路径

前面讲述了JVM启动的过程,这里介绍JVM退出的过程。一般来说,JVM有两条退出路径。其中一条路径称为虚拟机销毁(destroy vm):当程序运行到主方法的结尾处,系统将调用jni_DestroyJavaVM()函数销毁虚拟机。而另外一条路径则是虚拟机退出(vm exit):当程序调用System.exit()函数,或当JVM遇到错误时,将通过这条路径直接退出。

这两条退出途径并不完全相同,但它们在Java层共享Shutdown.shutdown()和before_exit()函数,并在JVM层共享VM_Exit函数。

这里,介绍一下destroy_vm的退出流程。

  • 当前线程等待直到成为最后一条非守护线程。此时,所有工作仍在继续。
  • Java层调用java.lang.Shutdown.shutdown()函数。
  • 调用before_exit()函数,为JVM退出做一些准备工作:首先,运行JVM层的关闭钩子函数(shutdown hooks)。这些钩子函数是通过JVM_OnExit进行注册的。目前唯一使用了这套机制的钩子函数是File.deleteOnExit()函数;其次,停止一些系统线程,如“StatSampler”,“watcher thread”和“CMS threads”等,并向JVMTI发送“thread end”和“vm death”事件;最后,停止信号线程。
  • 调用JavaThread::exit()函数,这将释放JNI句柄块,并从线程列表中移除本线程。
  • 停止虚拟机线程,使虚拟机进入安全点(safepoint)并停止编译器线程。
  • 禁用JNI/JVM 跟踪。
  • 为那些仍在运行本地代码的线程设置“_vm_exited”标记。
  • 删除当前线程。
  • 调用exit_globals()函数,删除tty和PerfMemory等资源。
  • 返回到上层调用者。

到目前为止,我们对启动过程已经有了较为整体的认识。接下来,在2.3小节中,我们将深入了解系统初始化过程。

时间: 2024-08-03 22:40:03

《HotSpot实战》—— 2.2 启动的相关文章

《HotSpot实战》—— 1.3 实战:在HotSpot内调试HelloWorld

1.3 实战:在HotSpot内调试HelloWorld 本节讲解的是Java入门程序HelloWorld在HotSpot上的执行过程.我们通过一个普通Java程序的运行过程,能够以点带面地讲解到涉及HotSpot内部实现的基础概念. 虽然是调试简单的HelloWorld程序,但在这个过程中会涉及HotSpot的基本数据结构以及环境准备等内容.理解这些,一方面使读者对HotSpot项目有个感性认识,其实调试HotSpot没有想象的那么困难,这利于我们增强驾驭HotSpot的自信心:另一方面,让我

《HotSpot实战》—— 第 2 章 启动

第 2 章 启动 "物有本末,事有终始.知其先后,则近道矣." -<大学> 本章内容 HotSpot内核模块 启动器Launcher和启动过程 JVM初始化过程 全局模块初始化 本章是HotSpot内核的入门导读.首先介绍阅读源代码的方式,接下来讲解了HotSpot内核模块组成和功能框架,最后重点讲解了JVM的启动和初始化过程. 本文仅用于学习和交流目的,不代表异步社区观点.非商业转载请注明作译者.出处,并保留本文的原始链接.

《HotSpot实战》—— 2.1 HotSpot内核

2.1 HotSpot内核 在引入HotSpot内核模块之前,我们有必要掌握一些阅读源代码的技巧. 2.1.1 如何阅读源代码 我们知道,HotSpot项目主要是由C++语言开发的,对于Java程序员来说,直接阅读这部分源代码可能会有些吃力.因此,我们有必要先阐释一些语言上的差异,扫清这些学习障碍. 1.宏 实际上,Java语言在语法上与C和C++是颇为相似的.除了一些在Java中没有提供的语法和特性,大多数C/C++代码还是很容易被Java程序员理解的.在这里,我们首先对在C和C++中大量使用

《HotSpot实战》—— 1.2 动手编译虚拟机

1.2 动手编译虚拟机 源码面前,了无秘密.对于OpenJDK和HotSpot项目来说也是如此.因此,研究虚拟机实现机制的最佳途径就是阅读和调试源代码.我们希望能够动手编译一个完整的OpenJDK(含HotSpot)项目,或者仅编译HotSpot,这样就可以对虚拟机展开调试了. 虽然官方也支持在Windows操作系统下构建编译环境.但是经验表明,选择在Linux环境下搭建编译环境,可以避免不少弯路.理由有以下两点: Windows上为了得到完整的编译环境,需要借助Cygwin等虚拟环境,而在Li

《HotSpot实战》—— 1.1 JDK概述

1.1 JDK概述 Java是一门不断发展和壮大的语言,随着理论和应用的飞速发展,它不断地吸收有益营养,许多优良特性也在JDK的各个版本中逐渐添加进来. JDK在一开始并不是开源的.随着开源运动的蓬勃发展,Sun公司在2006年的JavaOne大会上声称将对Java开放源代码,开源的Java平台开发主要集中在OpenJDK项目上.Sun公司于2009年4月15日正式发布OpenJDK.Java 7则是Java开源后发布的第一个正式版本. 任何组织和个人都可以为Java的发展做出贡献,如果你愿意为

《HotSpot实战》—— 第 1 章 初识HotSpo

第 1 章 初识HotSpo "知止而后有定,定而后能静,静而后能安,安而后能虑,虑而后能得." -<大学> 本章内容 VM与HotSpot VM 开源项目OpenJDK与HotSpot项目 Java语言特性的发展,以及JCP和JSR的推动作用 Coin项目为Java 7贡献的新特性 GDB调试工具的基本使用方式 HotSpot工程的编译与调试方法 对于Java程序员来说,启动一个应用服务器是再平常不过的工作了.不知读者是否留意过,在启动应用服务器时,控制台可能会有关于Ho

《HotSpot实战》—— 2.4 小结

2.4 小结 本章首先介绍了HotSpot内核的结构,并引导读者掌握一些阅读源代码的技巧.在内核模块中,介绍了Prims.Service和Runtime模块,它们为HotSpot提供外部接口,并为内核其他模块提供部分公共功能. 启动过程是了解HoSpot内部实现的入口.HotSpot提供了两种启动器,一个是产品级的,另一个则是调试级的.后者对于我们调试和学习HotSpot起到重要的作用.在整个启动过程中,create_vm()函数是其精华部分,它完成了JVM系统绝大多数模块的初始化工作. 为了帮

java虚拟机启动参数分类详解

HotSpot是较新的Java虚拟机技术,用来代替JIT(Just in Time)技术,可以大大提高Java运行的性能.Java原先是把源代码编译为字节码在虚拟机执行,这样执行速度较慢.而该技术将常用的部分代码编译为本地(原生,native)代码,这样显著提高了性能.用于服务器版和标准版的HotSpot有所不同. java启动参数共分为三类: 其一是标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容: 其二是非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有

华东理工大学学生赴温州“创业实战”

□记者 俞陶然 近日,华东理工大学第九届学生创业实战赛正式启动,500多名本科生和研究生报名参赛.该赛事原名"生存实践大赛",要求学生仅带100元在某外地城市生存10天,引起社会广泛关注.如今,在鼓励大学生创业的背景下,这项比赛转型为"创业实战",参赛选手将前往温州,须用创业而不能靠干体力活来求得生存. 据华东理工大学有关负责人介绍,在经济危机的背景下,培养大学生的创业精神和创业素养显得尤为重要.在以往的异地生存实践大赛中,不少学生没有完成创业,而是"和农