承接上一篇
这篇这要解析Java虚拟机创建时候配置读取时候命令行参数的读取过程,这次采取逆向思维分析,从结尾顺藤摸瓜找到源头的方式去找到读取命令行参数的地方,就从下面这个InitializeJVM函数为缺口慢慢展开全流程:
/*
* Initializes the Java Virtual Machine. Also frees options array when
* finished.
*/
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;
}
注意到这一函数开始的注释为初始化java虚拟机,并释放列表清单
最后 r = ifn->CreateJavaVM(pvm, (void **)penv, &args);这句为创建虚拟机
后面这句JLI_MemFree(options);就是释放options的内存空间了
那么既然找到释放的地方,那也会有个创建的地方
搜索在java.c找到开头有个地方定义了这个options
为JavaVMOption的结构体,顺着找,在jni.h找到该结构体的定义位置
optionString为设置字段,extraInfo为额外信息
在java.c查找到对JavaVMOption操作的地方,AddOption为在原来的基础上添加设置,下面为其源码:
/*
* Adds a new VM option with the given given name and value.
*/
void
AddOption(char *str, void *info)
{
/*
* Expand options array if needed to accommodate at least one more
* VM option.
*/
if (numOptions >= maxOptions) {
if (options == 0) {
maxOptions = 4;
options = JLI_MemAlloc(maxOptions * sizeof(JavaVMOption));
} else {
JavaVMOption *tmp;
maxOptions *= 2;
tmp = JLI_MemAlloc(maxOptions * sizeof(JavaVMOption));
memcpy(tmp, options, numOptions * sizeof(JavaVMOption));
JLI_MemFree(options);
options = tmp;
}
}
options[numOptions].optionString = str;
options[numOptions++].extraInfo = info;
if (JLI_StrCCmp(str, "-Xss") == 0) {
jlong tmp;
if (parse_size(str + 4, &tmp)) {
threadStackSize = tmp;
}
}
if (JLI_StrCCmp(str, "-Xmx") == 0) {
jlong tmp;
if (parse_size(str + 4, &tmp)) {
maxHeapSize = tmp;
}
}
if (JLI_StrCCmp(str, "-Xms") == 0) {
jlong tmp;
if (parse_size(str + 4, &tmp)) {
initialHeapSize = tmp;
}
}
}
该函式为动态扩容函数,一旦要读取的命令行参数数量超过max自动扩容,非常值得分析
这个部分的扩容部分非常值得分析,我加了注释的解析如下:
/*
* Expand options array if needed to accommodate at least one more
* VM option.
* 人工翻译:如果需要至少容纳一个或多个参数,会展开选项容纳空间
*/
//当选项数大于最大空间时
if (numOptions >= maxOptions) {
//如果没有设置options,那么将最大设置变为4,然后分配空间
if (options == 0) {
maxOptions = 4;
options = JLI_MemAlloc(maxOptions * sizeof(JavaVMOption));
} else {
//如果设置了options,将maxOptions扩容为原来的二倍
JavaVMOption *tmp;
maxOptions *= 2;
//先拿中间变量存储分配新大小的一个空间,
tmp = JLI_MemAlloc(maxOptions * sizeof(JavaVMOption));
//用原来的options内容复制到tmp中间变量中
/*memcpy(tmp, options, numOptions * sizeof(JavaVMOption))将numOptions个
*/JavaVMOption存储空间的options拷贝到tmp,从而完成对numOptions的赋值
memcpy(tmp, options, numOptions * sizeof(JavaVMOption));
//将原来的空间释放掉
JLI_MemFree(options);
//将保存过的旧的数据放入新空间中
options = tmp;
}
}
插入:
注意memcpy,C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字节到存储区 str1,这是个不安全的函数,使用时候一定要多加注意
回到正题:
那么接着去查找调用AddOption的地方,看看配置是从哪里进,就在上面的ParseArguments,命令参数语法分析函数,以下为其源码
static jboolean
ParseArguments(int *pargc, char ***pargv,
int *pmode, char **pwhat,
int *pret, const char *jrepath)
{
int argc = *pargc;
char **argv = *pargv;ed
int mode = LM_UNKNOWN;
char *arg;
*pret = 0;
while ((arg = *argv) != 0 && *arg == '-') {
argv++; --argc;
if (JLI_StrCmp(arg, "-classpath") == 0 || JLI_StrCmp(arg, "-cp") == 0) {
ARG_CHECK (argc, ARG_ERROR1, arg);
SetClassPath(*argv);
mode = LM_CLASS;
argv++; --argc;
} else if (JLI_StrCmp(arg, "-jar") == 0) {
ARG_CHECK (argc, ARG_ERROR2, arg);
mode = LM_JAR;
} else if (JLI_StrCmp(arg, "-help") == 0 ||
JLI_StrCmp(arg, "-h") == 0 ||
JLI_StrCmp(arg, "-?") == 0) {
printUsage = JNI_TRUE;
return JNI_TRUE;
} else if (JLI_StrCmp(arg, "-version") == 0) {
printVersion = JNI_TRUE;
return JNI_TRUE;
} else if (JLI_StrCmp(arg, "-showversion") == 0) {
showVersion = JNI_TRUE;
} else if (JLI_StrCmp(arg, "-X") == 0) {
printXUsage = JNI_TRUE;
return JNI_TRUE;
/*
* The following case checks for -XshowSettings OR -XshowSetting:SUBOPT.
* In the latter case, any SUBOPT value not recognized will default to "all"
*/
} else if (JLI_StrCmp(arg, "-XshowSettings") == 0 ||
JLI_StrCCmp(arg, "-XshowSettings:") == 0) {
showSettings = arg;
} else if (JLI_StrCmp(arg, "-Xdiag") == 0) {
AddOption("-Dsun.java.launcher.diag=true", NULL);
/*
* The following case provide backward compatibility with old-style
* command line options.
*/
} else if (JLI_StrCmp(arg, "-fullversion") == 0) {
JLI_ReportMessage("%s full version \"%s\"", _launcher_name, GetFullVersion());
return JNI_FALSE;
} else if (JLI_StrCmp(arg, "-verbosegc") == 0) {
AddOption("-verbose:gc", NULL);
} else if (JLI_StrCmp(arg, "-t") == 0) {
AddOption("-Xt", NULL);
} else if (JLI_StrCmp(arg, "-tm") == 0) {
AddOption("-Xtm", NULL);
} else if (JLI_StrCmp(arg, "-debug") == 0) {
AddOption("-Xdebug", NULL);
} else if (JLI_StrCmp(arg, "-noclassgc") == 0) {
AddOption("-Xnoclassgc", NULL);
} else if (JLI_StrCmp(arg, "-Xfuture") == 0) {
AddOption("-Xverify:all", NULL);
} else if (JLI_StrCmp(arg, "-verify") == 0) {
AddOption("-Xverify:all", NULL);
} else if (JLI_StrCmp(arg, "-verifyremote") == 0) {
AddOption("-Xverify:remote", NULL);
} else if (JLI_StrCmp(arg, "-noverify") == 0) {
AddOption("-Xverify:none", NULL);
} else if (JLI_StrCCmp(arg, "-prof") == 0) {
char *p = arg + 5;
char *tmp = JLI_MemAlloc(JLI_StrLen(arg) + 50);
if (*p) {
sprintf(tmp, "-Xrunhprof:cpu=old,file=%s", p + 1);
} else {
sprintf(tmp, "-Xrunhprof:cpu=old,file=java.prof");
}
AddOption(tmp, NULL);
} else if (JLI_StrCCmp(arg, "-ss") == 0 ||
JLI_StrCCmp(arg, "-oss") == 0 ||
JLI_StrCCmp(arg, "-ms") == 0 ||
JLI_StrCCmp(arg, "-mx") == 0) {
char *tmp = JLI_MemAlloc(JLI_StrLen(arg) + 6);
sprintf(tmp, "-X%s", arg + 1); /* skip '-' */
AddOption(tmp, NULL);
} else if (JLI_StrCmp(arg, "-checksource") == 0 ||
JLI_StrCmp(arg, "-cs") == 0 ||
JLI_StrCmp(arg, "-noasyncgc") == 0) {
/* No longer supported */
JLI_ReportErrorMessage(ARG_WARN, arg);
} else if (JLI_StrCCmp(arg, "-version:") == 0 ||
JLI_StrCmp(arg, "-no-jre-restrict-search") == 0 ||
JLI_StrCmp(arg, "-jre-restrict-search") == 0 ||
JLI_StrCCmp(arg, "-splash:") == 0) {
; /* Ignore machine independent options already handled */
} else if (ProcessPlatformOption(arg)) {
; /* Processing of platform dependent options */
} else if (RemovableOption(arg)) {
; /* Do not pass option to vm. */
} else {
AddOption(arg, NULL);
}
}
if (--argc >= 0) {
*pwhat = *argv++;
}
if (*pwhat == NULL) {
*pret = 1;
} else if (mode == LM_UNKNOWN) {
/* default to LM_CLASS if -jar and -cp option are
* not specified */
mode = LM_CLASS;
}
if (argc >= 0) {
*pargc = argc;
*pargv = argv;
}
*pmode = mode;
return JNI_TRUE;
}
哈哈,看到平常在命令行输入的-help参数没有
就是在这里读取的,读取了在cmd输入的命令行参数,具体还有很多细节,没分析到位,水平有限谅解一下哈