Backend Development 13 min read

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.

Architecture Digest
Architecture Digest
Architecture Digest
Analyzing Why Spring Boot Startup Is Slow: Detailed Breakdown and Profiling

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.

backendJavaperformanceoptimizationSpringBootprofilingstartup
Architecture Digest
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.