Java虚拟机启动,本质上是java启动器创建JVM、初始化运行时、加载主类并调用main方法的一条链路。 启动快慢通常不取决于某一行代码,而取决于启动参数、类加载规模、资源限制和框架初始化。
很多人把JVM启动理解成“敲下java命令,程序就跑起来”,实际链路更长,启动阶段出问题,表现可能是启动慢、启动报错、容器里被OOM Killer杀掉,也可能是本地正常、服务器异常,下面按启动流程、参数设置、系统差异、排查方法拆开讲。
Java虚拟机启动流程详解:从java命令到main方法
启动入口:java命令背后发生了什么
你执行java -jar app.jar时,最先工作的不是业务代码,而是Java启动器。
- 启动器解析命令行参数,比如
-Xms、-Xmx、-XX:+UseG1GC、-Dfile.encoding=UTF-8。 - 启动器准备JVM创建参数,据Oracle官方文档,它会通过JNI调用创建JVM。
- JVM创建堆、方法区或元空间、线程栈、GC、解释器、JIT编译器、主线程等运行时结构。
- JVM找到主类,加载并初始化,最后调用
main方法。 - 业务框架开始初始化,Spring、日志、数据库连接池等通常在这个阶段消耗较多时间。
可以把启动阶段简化为下表。
| 阶段 | 关键动作 | 可观测方式 |
|---|---|---|
| java启动器 | 解析参数、准备JVM | java -XX:+PrintFlagsFinal -version |
| JVM创建 | 内存布局、GC、线程 | -Xlog:gc:file=gc.log:time,uptime,level,tags |
| 类加载 | 加载主类、依赖类 | java -verbose:class -jar app.jar |
| main执行 | 静态块、框架、业务初始化 | JFR、应用日志、time java -jar |
类加载和初始化什么时候发生
JVM启动并不等于加载所有类,多数类是按需加载的。
- 主类会先被加载、链接、初始化。
- 静态变量赋值和静态代码块在初始化阶段执行。
- 双亲委派模型决定类由哪个加载器加载。
- 依赖冲突、重复类、大量反射扫描,都会拉长启动时间。
-verbose:class能看到类加载顺序,适合定位“谁在启动时拖后腿”。

main方法执行前的关键准备
main执行前,JVM还要完成主线程创建、参数数组准备、方法调用绑定等动作。
- 主线程通过JNI进入Java世界,再调用主类
main。 - 早期代码通常由解释器执行,热点代码才逐步被JIT编译。
- 安全点、偏向锁、线程初始化等也会影响启动体感。
- 如果日志里
main之前耗时很长,优先查JVM参数、类加载和系统资源,而不是先改业务代码。
Java虚拟机启动参数怎么设置才稳
内存参数先定边界
启动参数里,内存参数最容易拍脑袋写。
-Xms和-Xmx尽量设成相同值,减少运行中堆动态扩缩。- 元空间用
-XX:MetaspaceSize和-XX:MaxMetaspaceSize限制上限。 - 线程栈用
-Xss,线程越多,栈内存总量越大。 - 堆外内存用
-XX:MaxDirectMemorySize显式限制。 - 容器环境优先用
-XX:MaxRAMPercentage,不要只写死-Xmx。
示例:
java -Xms2g -Xmx2g -XX:MaxMetaspaceSize=512m -XX:MaxDirectMemorySize=256m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/dumps -jar app.jar
行业共识认为,容器里JVM要同时看cgroup限制和JVM参数,否则容易出现“容器内存不大,JVM却按宿主机内存估算”的问题。
GC与日志参数怎么配
JDK 8和JDK 9以后日志参数差异明显。
- JDK 8常用:
-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps。 - JDK 9以后统一日志:
-Xlog:gc:file=gc.log:time,uptime,level,tags。 - 低延迟场景可评估G1、ZGC,但不要只看GC名称,还要看堆大小、CPU配额和暂停目标。
- 启动阶段加日志,能区分是JVM初始化慢,还是业务初始化慢。
启动调优的实操检查清单
- 用
time java -jar app.jar记录冷启动时间。 - 用JFR抓启动:
-XX:StartFlightRecording=filename=startup.jfr,duration=60s
。
- 用
-XX:+PrintFlagsFinal确认参数是否生效。 - 用
-verbose:class观察类加载数量和顺序。 - 用AppCDS减少重复类加载:先
-Xshare:dump,再配合-XX:ArchiveClassesAtExit=app.jsa和-XX:SharedArchiveFile=app.jsa。 - 检查框架是否开启不必要的扫描、远程配置拉取、连接池预热。
Windows和Linux下Java虚拟机启动区别
进程与脚本差异
Windows上常见java.exe和javaw.exe,前者带控制台,后者常用于GUI或服务,Linux上通常直接执行java,再由shell脚本、systemd或容器入口启动。
- Windows路径可能带空格,启动脚本要处理引号。
- Linux更依赖
ulimit、环境变量和文件权限。 - Windows服务包装器和Linux systemd的信号处理不同,停止应用时行为也可能不同。
资源限制与信号
Linux下要关注文件句柄、进程数、内存cgroup和OOM Killer,Windows下要关注服务账户权限、作业对象和页面文件。
- Linux可用
ulimit -n、ulimit -u检查限制。 - 容器里可用
cat /sys/fs/cgroup/memory.max查看内存上限。 - Linux收到
SIGTERM后,JVM会触发关闭钩子;Windows服务停止逻辑则取决于包装方式。 - 启动时DNS解析慢、随机数熵不足、时区不对,都可能在Linux服务器上放大。
容器和云服务器场景
Docker和Kubernetes里,JVM看到的是容器资源,不一定是宿主机资源。
- 设置CPU request和limit,避免启动时被限流。
- 设置内存limit,并让JVM用
MaxRAMPercentage感知。 - 给足启动探针时间,避免应用还没起来就被重启。
- 如果考虑购买第三方调优,北京Java虚拟机启动调优服务价格通常要按应用规模、驻场天数和交付范围评估,一线城市的人天成本往往高于二三线城市,固定报价反而要谨慎。
Java虚拟机启动慢怎么排查?线上和面试场景都常问
先量化再定位
启动慢不要凭感觉,先拆时间线。
- 操作系统进程创建耗时。
- JVM初始化耗时。
- 类加载耗时。
- 框架初始化耗时。
- 业务
main到端口监听耗时。 - 用JFR、GC日志、应用日志时间戳交叉验证。

业内专家指出,启动调优先看时间线,再改参数,否则容易把内存问题误判成类加载问题。
常见瓶颈
- 类加载过多,依赖冲突或重复扫描。
- 日志框架初始化慢,配置文件解析慢。
- DNS解析、远程配置中心、数据库连接超时。
- 容器CPU被限制,JIT编译和类加载都变慢。
- 随机数源阻塞,旧版JDK在Linux上更常见。
- 堆设置过大,启动时提交内存和GC初始化变慢。
面试回答框架
回答“JVM启动过程”时,不要只背“加载、验证、准备、解析、初始化”。
- 先讲Java启动器如何解析参数并创建JVM。
- 再讲JVM运行时初始化,包括堆、栈、元空间、GC、解释器。
- 接着讲类加载和主类初始化。
- 最后讲
main方法执行和框架启动。 - 如果能补一句“用JFR和
-verbose:class定位”,答案会更像实战经验。
Java虚拟机启动常见疑问解答
Java虚拟机启动和JVM运行有什么关系?
启动是JVM运行的前置阶段,启动完成不等于运行稳定,它只表示JVM已创建、主类已加载、main已进入,后续运行还要看GC、JIT、线程池和业务流量。
Java虚拟机启动参数怎么设置才不影响性能?
先设内存边界,再设GC和日志。-Xms与-Xmx一致,元空间和堆外内存设上限,容器里用MaxRAMPercentage,参数不是越多越好,能稳定复现和观测才有意义。
Java虚拟机启动慢一定是代码问题吗?
不一定,系统资源、容器限制、类加载规模、DNS、随机数、日志配置都可能让启动变慢,先用time、JFR和-verbose:class分段量化,再决定改参数、改依赖还是改代码,多数情况下,启动问题都能通过时间线定位到具体阶段。
Java虚拟机启动不是黑盒,把链路拆成java启动器、JVM初始化、类加载、main执行四段,再用参数和日志逐段验证,启动速度和稳定性都会更可控。
图片来源于AI模型,如侵权请联系管理员。作者:酷小编,如若转载,请注明出处:https://www.kufanyun.com/ask/916533.html


评论列表(5条)
这篇文章写得非常好,内容丰富,观点清晰,让我受益匪浅。特别是关于启动器的部分,分析得很到位,给了我很多新的启发和思考。感谢作者的精心创作和分享,期待看到更多这样高质量的内容!
@大果8748:读了这篇文章,我深有感触。作者对启动器的理解非常深刻,论述也很有逻辑性。内容既有理论深度,又有实践指导意义,确实是一篇值得细细品味的好文章。希望作者能继续创作更多优秀的作品!
这篇文章的内容非常有价值,我从中学习到了很多新的知识和观点。作者的写作风格简洁明了,却又不失深度,让人读起来很舒服。特别是启动器部分,给了我很多新的思路。感谢分享这么好的内容!
这篇文章的内容非常有价值,我从中学习到了很多新的知识和观点。作者的写作风格简洁明了,却又不失深度,让人读起来很舒服。特别是启动器部分,给了我很多新的思路。感谢分享这么好的内容!
读了这篇文章,我深有感触。作者对启动器的理解非常深刻,论述也很有逻辑性。内容既有理论深度,又有实践指导意义,确实是一篇值得细细品味的好文章。希望作者能继续创作更多优秀的作品!