如果你是一名Java应用开发工程师,你应该对“public static void main(String[] args)”这段代码再熟悉不过了,然而你是否了解main方法是如何调用的,为什么我们运行java.exe,就能启动应用程序?下面,让我们来一探究竟吧!

首先,聊一聊,java.exe文件是怎么来的

如果你下载了OpenJDK源码,在源码目录src\java.base\share\native\libjli目录下有java.c这样一个文件,java.exe文件是通过编译java.c文件生成的可执行文件。java.c文件是Java Runtime Environment(JRE)的一部分,它是用C语言编写的,用于启动Java应用程序并运行Java字节码。

其次,讲一讲,运行java.exe后发生了什么

在java.c文件当中,有这样一个方法:

/*
* Entry point.
*/
JNIEXPORT int JNICALL
JLI_Launch(int argc, char ** argv, /* main argc, argv */
int jargc, const char** jargv, /* java args */
int appclassc, const char** appclassv, /* app classpath */
const char* fullversion, /* full version defined */
const char* dotversion, /* UNUSED dot version defined */
const char* pname, /* program name */
const char* lname, /* launcher name */
jboolean javaargs, /* JAVA_ARGS */
jboolean cpwildcard, /* classpath wildcard*/
jboolean javaw, /* windows-only javaw */
jint ergo /* unused */
)

这个方法是Java虚拟机的入口点,负责解析命令行参数、加载Java应用程序并启动Java虚拟机。在JLI_Launch方法中,会解析命令行参数,包括指定Java类路径、指定JVM参数等,然后调用JVM的启动函数,启动Java虚拟机并加载Java应用程序。也就是说,当你运行“java.exe YourClassName”这个命令时,调用的就是JLI_Launch这个方法。

接着,看一看,JLI_Launch这个方法做了哪些事情

我们进入方法内部,我们看到方法的最后一行代码:

return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);

该代码作用是对Java虚拟机进行初始化,如果你现在是windows系统中运行java应用程序,那么该方法的实现是在src\java.base\windows\native\libjli\java_md.c这个文件中:

int
JVMInit(InvocationFunctions* ifn, jlong threadStackSize,
int argc, char **argv,
int mode, char *what, int ret)
{
ShowSplashScreen();
return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);
}

我们继续进入ContinueInNewThread这个方法,发现了

rslt = CallJavaMainInNewThread(threadStackSize, (void*)&args);

这段代码,继续进入CallJavaMainInNewThread,找到了这段代码

rslt = JavaMain(args);

因此我们知道了:Java类中的main方法public static void main(String[] args),就是由java.c文件中JavaMain方法调用的。

接着,瞧一瞧,JavaMain这个方法是如何调用main方法的

上代码:

int JavaMain(void* _args)
{
JavaMainArgs *args = (JavaMainArgs *)_args;
...
//初始化Java虚拟机
if (!InitializeJVM(&vm, &env, &ifn)) {
JLI_ReportErrorMessage(JVM_ERROR1);
exit(1);
}
...
//加载主运行类
mainClass = LoadMainClass(env, mode, what);
CHECK_EXCEPTION_NULL_LEAVE(mainClass);
...
//通过加载的主运行类,获取main方法
mainID = (*env)->GetStaticMethodID(env, mainClass,"main","([Ljava/lang/String;)V");
CHECK_EXCEPTION_NULL_LEAVE(mainID);
//调用main函数
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
...
}

这里可能就有读者要问了,C程序是怎么调用Java程序的?
实际上,通过JNI(java本地接口,是Java Native Interface的缩写)技术可以实现C语言调用Java,JNI是Java平台提供的一种编程框架,用于实现Java应用程序与本地(非Java)应用程序之间的相互调用。JNI提供了一组规范和工具,使Java应用程序能够调用C、C++和其他本地语言编写的代码,反之亦然。通过JNI,Java程序员可以利用现有的本地代码库,并与其他本地应用程序进行交互,从而扩展Java应用程序的功能和性能。

最后,我们用流程图总结一下

从JDK源码级深入剖析main方法的运行机制的更多相关文章

  1. 源码级强力分析hadoop的RPC机制

    分析对象: hadoop版本:hadoop 0.20.203.0 必备技术点: 1. 动态代理(参考 :http://weixiaolu.iteye.com/blog/1477774 )2. Java ...

  2. jdk源码剖析:Synchronized

    开启正文之前,先说一下源码剖析这一系列,就以"死磕到底"的精神贯彻始终,最少追踪到JVM指令(再往下C语言实现了). =========正文分割线===========  Sync ...

  3. jdk源码剖析三:锁Synchronized

    一.Synchronized作用 (1)确保线程互斥的访问同步代码 (2)保证共享变量的修改能够及时可见 (3)有效解决重排序问题.(Synchronized同步中的代码JVM不会轻易优化重排序) 二 ...

  4. 从JDK源码角度看java并发的原子性如何保证

    JDK源码中,在研究AQS框架时,会发现很多地方都使用了CAS操作,在并发实现中CAS操作必须具备原子性,而且是硬件级别的原子性,java被隔离在硬件之上,明显力不从心,这时为了能直接操作操作系统层面 ...

  5. 【Java编程实战】Metasploit_Java后门运行原理分析以及实现源码级免杀与JRE精简化

    QQ:3496925334 文章作者:MG1937 CNBLOG博客ID:ALDYS4 未经许可,禁止转载 某日午睡,迷迷糊糊梦到Metasploit里有个Java平台的远控载荷,梦醒后,打开虚拟机, ...

  6. Timer的故事----Jdk源码解读

    咱们今天也来说说定时器Timer Timer是什么? Timer  n. [电子] 定时器:计时器:计时员 从翻译来看,我们可以知道Timer的本意是,定时定点. 而JDK中Timer类也的确是这个本 ...

  7. MapReduce的MapTask任务的运行源码级分析

    TaskTracker任务初始化及启动task源码级分析 这篇文章中分析了任务的启动,每个task都会使用一个进程占用一个JVM来执行,org.apache.hadoop.mapred.Child方法 ...

  8. TaskTracker任务初始化及启动task源码级分析

    在监听器初始化Job.JobTracker相应TaskTracker心跳.调度器分配task源码级分析中我们分析的Tasktracker发送心跳的机制,这一节我们分析TaskTracker接受JobT ...

  9. JDK源码学习--String篇(二) 关于String采用final修饰的思考

    JDK源码学习String篇中,有一处错误,String类用final[不能被改变的]修饰,而我却写成静态的,感谢CTO-淼淼的指正. 风一样的码农提出的String为何采用final的设计,阅读JD ...

  10. JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue

    JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...

随机推荐

  1. 一文快速入门任务调度框架-Quartz

    前言 还不会 Quartz?如果你还没有接触过Quartz,那么你可能错过了一个很棒的任务调度框架!Quartz 提供了一种灵活.可靠的方式来管理和执行定时任务,让咱们的定时任务更加优雅.本篇文章将为 ...

  2. 系统评价——理想点TOPSIS法的R语言实现(五)

    TOPSIS 法是一种常用的综合评价方法,能充分利用原始数据的信息,其结果能精确地反映各评价方案之间的差距.TOPSIS全称Technique for Order Preference by Simi ...

  3. Redis(一)五种基本数据类型

    1 NoSQl数据库 1.1 技术的发展 技术的分类: ①解决功能性问题:javase ②解决扩展性问题:框架 ③解决性能问题:redis 1.2 NoSQL数据库概述 NoSQL(Not Only ...

  4. SPI-SPI主机硬件片选功能使用说明

    SPI主机硬件片选功能使用说明 SPI协议最早的标准,是由摩托罗拉公司制定.在协议使用的过程中,根据实际需求可能会进行一些扩展和修改. 在一份由飞思卡尔半导体发布的SPI V4.01版本规范中,对片选 ...

  5. Linux rsyslogd服务学习

    本篇笔记来自该博客: http://c.biancheng.net/view/1097.html 服务简介 在CentOS 6.x 中日志服务已经由 rsyslogd 取代了原先的 syslogd.r ...

  6. Sitecore10 Demo演示环境Azure一键部署(Step By Step Guide to installing Sitecore10 in Azure Paas)

    本文演示Sitecore XP Single(XP0)在Azure上的一键部署,即"30分钟生成Sitecore演示环境"的一环. 关于XP(即Sitecore Experienc ...

  7. Caused by: java.net.BindException: Address already in use: JVM_Bind(ActiveMq已经启动)

    1.本地启动项目开启两个启动类出错. Error creating bean with name 'brokerService' defined in class path resource [com ...

  8. vue3.0

    https://www.yuque.com/gdnnth/vue-v3 http://www.liulongbin.top:8085/#/ https://www.yuque.com/woniuppp ...

  9. MySQL高频面试题

    什么是MySQL MySQL是一个关系型数据库,它采用表的形式来存储数据.你可以理解成是Excel表格,既然是表的形式存储数据,就有表结构(行和列).行代表每一行数据,列代表该行中的每个值.列上的值是 ...

  10. 【Ubuntu】1. 创建虚拟机

    这一篇主要写了虚拟机的创建,不包含操作系统的安装,中间有些步骤没有提到的根据默认操作即可,也可以根据个人情况设置. 点击创建新的虚拟机 这一步可以选择典型安装,过程更简单些,这里我选择自定义. 在安装 ...