Home / Fix an issue

Fix an issue

Slow First Request on a Java App: Cold Starts and JVM Startup

The app is fast once it runs, but the first visitor after a quiet hour waits. Two things cause that delay, and they have different fixes: the platform putting the app to sleep, and the JVM starting up.

Checked November 2026

A sleeping server module under a glass dome next to a small silver alarm clock

A Java app that answers in milliseconds during the day and takes many seconds after a quiet hour is showing a cold start. Two separate things can cause it, and each has its own fix: the platform stopped the app because nobody used it, or the JVM needs time to start and warm up. Find out which one you have before you change anything.

Plan rules checked in November 2026.

Sleep or startup? Tell them apart

Look at the platform's event log around a slow request. If the app was stopped and started again just before it, the delay is sleep plus a full startup. If the app kept running and the slow request came right after a deploy or restart, the cause is JVM warm-up alone.

Free tiers are the usual source of sleep. In the plans we compare, Render's free web services spin down after 15 minutes without traffic, Koyeb's free instance scales to zero after an hour idle, and Heroku Eco sleeps after 30 minutes. Each wake-up means a complete JVM start while the visitor waits.

Measure the startup time

Spring Boot prints how long the application took to start in its log. Note that number on the host, not on your laptop: a small shared CPU can make the same app several times slower to start. Everything below is judged against this one measurement, so take it before and after each change.

Measure from the outside

The log tells you how long the JVM took to start; the visitor experiences more than that, including the platform waking the instance and routing the request. Measure what the visitor sees with curl after the app has been idle:

curl -s -o /dev/null -w "connect %{time_connect}s  first byte %{time_starttransfer}s  total %{time_total}s\n" https://your-app.example.com/

Run it once after a quiet period and once immediately after. The difference between the two runs is the cold start. If the first run is much slower than the startup time in the log, most of the delay is the platform waking the instance, and only an always-on plan removes it. If both numbers are close, the JVM startup is the part to work on.

Spring Boot can also report which beans take longest to start. With BufferingApplicationStartup configured and Actuator's startup endpoint exposed, the app lists each startup step with its duration, which shows where lazy initialization or removing a dependency will help most.

Start less at startup

Many apps create objects at startup that the first request does not need. Spring Boot can postpone creating beans until they are used:

spring.main.lazy-initialization=true

This shortens startup and moves part of the cost to the first request that touches each bean, so test the result. Beyond that, remove starters and libraries the app does not use, avoid scanning large packages, and move heavy work, such as warming caches, out of the startup path or into a background task.

Use the AOT cache on Java 25

Recent JDKs can save part of the startup work and reuse it. The Ahead-of-Time cache, introduced in JDK 24, stores artifacts from a training run, such as loaded and linked classes. The Java 25 documentation shows how a training run writes the cache:

java -XX:AOTCacheOutput=app.aot -jar app.jar

Run the app through typical requests during this training run, then start it in production with the cache:

java -XX:AOTCache=app.aot -jar app.jar

The cache must be created with the same JDK and the same JAR it is used with, so build it as part of your image. The gain depends on the app; compare the startup time from the log with and without it.

Choose a plan that stays awake

When users must never wait, no startup tuning can remove the time a sleeping app needs to wake up. An always-on plan removes the sleep entirely. The free Java hosting page shows each free tier's sleep rule, and the Java Launchpad switches to always-on plans when you tell it the app has real users.

Repeated cold starts can also come from the app being killed and restarted for memory, which looks like slowness from the outside. If the event log shows restarts without idle time, check the exit code 137 page.

Sources

  1. docs.oracle.com/en/java/javase/25/docs/specs/man/java.html
  2. render.com/docs/free
  3. docs.spring.io/spring-boot/reference/features/spring-application.html

Fix an issue / FAQ

Good questions.
Clear answers.

Why is my app slow only on the first request?

Either the platform stopped it while idle and has to start it again, or the JVM is still warming up. Free tiers on Render, Koyeb and Heroku Eco stop idle apps; the first request then waits for a full JVM start.

How long before a free tier puts the app to sleep?

It depends on the plan. In our comparison, Render's free web services spin down after 15 minutes without traffic, Koyeb's free instance scales to zero after an hour, and Heroku Eco sleeps after 30 minutes idle. The free Java hosting page keeps these rules up to date.

Can I stop a free app from sleeping with a ping service?

Keeping a free app awake with scheduled pings works against how the free tier is meant to be used and can break a provider's terms or exhaust free hours. If the app needs to answer quickly at any time, an always-on plan is the honest fix.

Does the Java 25 AOT cache help on a small host?

It can. The AOT cache, introduced in JDK 24, stores work from a training run so later starts load classes faster. You create the cache once from a representative run and start the app with it; the gain depends on the app, so measure before and after.

Find your next step.