Analyzing Why Spring Boot Startup Is Slow: Detailed Breakdown and Profiling
This article investigates the reasons behind Spring Boot's slow startup by tracing the execution timeline, examining the prepareEnvironment and refreshContext phases, profiling bean creation, configuration loading, and external client initialization, and provides sample Java code for measuring and visualizing each step.
Introduction
After trying several lightweight open‑source frameworks, I realized that Spring Boot’s startup time is noticeably longer, prompting a deep dive into the exact causes of the slowdown.
Macro Overview
Using a minimal MVC project, the timeline shows that the most time‑consuming stages are prepareEnvironment and refreshContext . The following code snippet illustrates the core startup flow in SpringApplication.run() :
public void run(String... args) {
// 3. Trigger startup event
listeners.starting();
// 4. Prepare environment (load config, set profiles, etc.)
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 7. Create application context
context = createApplicationContext();
// 8. Prepare context (register initializers, load sources, etc.)
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 9. Refresh context
refreshContext(context);
// 13. Trigger context started event
listeners.started(context);
// 14. Run ApplicationRunner/CommandLineRunner
callRunners(context, applicationArguments);
// 15. Trigger running event
listeners.running(context);
}Detailed Analysis
3.1 Why Each Phase Is Slow
prepareEnvironment does only in‑memory processing when remote configuration is ignored, so its impact is minimal.
The heavy part lies in refreshContext , where bean creation, post‑processor execution, and web container initialization occur. The three biggest time sinks are:
BeanFactory post‑processors
Web container startup (e.g., Tomcat)
Singleton bean initialization
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();3.2 Production‑Level Project Impact
In a real‑world project (≈35 s startup), additional factors dramatically increase latency:
Multiple container creations (e.g., SpringApplication.run() and Cloud Bootstrap each start a new context).
Configuration listeners that load external files (ConfigFileApplicationListener, BootstrapApplicationListener, etc.).
Repeated refresh cycles causing dozens of bean reloads.
Large numbers of beans leading to extensive processing.
Third‑party client creation (Redis, MySQL) that opens connections and pools.
Instrumentation Code
The following utility records timestamps for arbitrary process lines and prints a formatted table of durations and percentages.
package com.gang.start;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
/**
* Detects program fragment execution time.
*/
public class StopWatchExpand {
private static List<TaskEntry> taskEntries = new ArrayList<>();
public static String start(String processLine, String node) {
try {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
String callerClass = stackTrace[2].getClassName().substring(
stackTrace[2].getClassName().lastIndexOf(".") + 1);
String callerMethod = stackTrace[2].getMethodName();
long currentTimeMillis = System.currentTimeMillis();
taskEntries.add(new TaskEntry(processLine, node, callerClass, callerMethod, currentTimeMillis));
return String.format("[ 流程: %s | 节点: %s | 调用类: %s | 调用方法: %s ] 监测到达时间: %s",
processLine, node, callerClass, callerMethod, formatTimestamp(currentTimeMillis));
} catch (Exception e) {
e.printStackTrace();
return "执行异常: " + node;
}
}
public static void stop() {
logStatistics();
taskEntries.clear();
}
private static void logStatistics() {
if (taskEntries.isEmpty()) return;
// calculate widths, build header, iterate entries, compute intervals and percentages
// (implementation omitted for brevity)
System.out.println(sb);
}
private static String formatTimestamp(long timestamp) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
return sdf.format(timestamp);
}
// Helper methods padString, padChineseNode, getters, and a demo main() omitted for brevity
}Conclusion
The profiling shows that external client connections (e.g., Redis, MySQL) and configuration loading dominate the startup cost, followed by bean initialization. Future Spring Boot releases aim to slim down these phases, and further analysis will compare Spring Boot 2 vs 3 performance improvements.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.