Your Spring Boot app has 200 users waiting. Your CPU is at 10%. No errors. No crashes. The culprit? 20 threads — all blocked, all doing nothing. I built a benchmark project to see exactly what happens inside the JVM when a classic thread pool hits this wall, and what changes when Java 21 virtual threads take over. Same app. Same business logic. One executor swap. Results with 200 concurrent users over 30 seconds: → Throughput: 1.95 req/s (classic) vs 252.89 req/s (loom) → p(95) latency: 56 seconds (classic) vs 0.81 seconds (loom) → Requests completed: 117 (classic) vs 7,784 (loom) → 140 users never got a response in classic mode. In Loom, all 200 finished cleanly with zero interruptions. What surprised me most was not the throughput number. It was looking inside the JVM mid-load and seeing 1,846 virtual threads active while the OS thread count barely moved. Loom does not add OS threads — it parks tasks as objects on the heap and frees the carrier thread instantly. One non-obvious gotcha for anyone adopting this in production: Thread.getAllStackTraces() intentionally excludes virtual threads in Java 21. Your existing monitoring tools may already be blind to them. Full benchmark with thread snapshots, heap data, GC numbers, and code walkthrough in the article below. #Java #Java21 #ProjectLoom #SpringBoot #BackendDevelopment #SoftwareEngineering #Programming
Java 21 Virtual Threads Boost Spring Boot App Performance
More Relevant Posts
-
thanks a lot Ragasudha R really fantastic work on Performance benchmarking for Classic Platform / OS Threads vs Virtual Threads loved reading this blog working as a Performance Engineer
Advanced Software Engineer at Gartner | Java | Spring Boot | AWS Certified | Certified Kubernetes Application Developer
Your Spring Boot app has 200 users waiting. Your CPU is at 10%. No errors. No crashes. The culprit? 20 threads — all blocked, all doing nothing. I built a benchmark project to see exactly what happens inside the JVM when a classic thread pool hits this wall, and what changes when Java 21 virtual threads take over. Same app. Same business logic. One executor swap. Results with 200 concurrent users over 30 seconds: → Throughput: 1.95 req/s (classic) vs 252.89 req/s (loom) → p(95) latency: 56 seconds (classic) vs 0.81 seconds (loom) → Requests completed: 117 (classic) vs 7,784 (loom) → 140 users never got a response in classic mode. In Loom, all 200 finished cleanly with zero interruptions. What surprised me most was not the throughput number. It was looking inside the JVM mid-load and seeing 1,846 virtual threads active while the OS thread count barely moved. Loom does not add OS threads — it parks tasks as objects on the heap and frees the carrier thread instantly. One non-obvious gotcha for anyone adopting this in production: Thread.getAllStackTraces() intentionally excludes virtual threads in Java 21. Your existing monitoring tools may already be blind to them. Full benchmark with thread snapshots, heap data, GC numbers, and code walkthrough in the article below. #Java #Java21 #ProjectLoom #SpringBoot #BackendDevelopment #SoftwareEngineering #Programming
To view or add a comment, sign in
-
Thread Pools to Virtual Threads. 🧵 OutOfMemoryError it is a nightmare that has always haunted Java developers. We're always been stuck with Platform Threads (heavyweight wrappers around OS threads). Since each one cost about 1MB of memory, handling 10,000 concurrent requests meant you either needed a massive, expensive server or had to write complex reactive code that nobody actually wants to debug. Enter Project Loom (Java 21+). I’ve been diving into Virtual Threads, and the "blocking" game has completely changed. Here’s why this matters for the modern backend: Cheap as Chips: Virtual threads are managed by the JVM, not the OS. They only cost a few hundred bytes. You can literally spawn one million threads on a standard laptop without breaking a sweat. The "Thread-per-Request" Revival: We can go back to writing simple, readable, synchronous code. No more "Callback Hell" or complex Mono/Flux chains just to keep the CPU busy while waiting for a database response. Massive Throughput: In I/O-heavy applications (which most Spring Boot apps are), Virtual Threads allow the CPU to switch to other tasks instantly while one thread waits for a slow API or SQL query. How to use it in Spring Boot 3.2+? It’s literally one line in your application.properties: spring.threads.virtual.enabled=true By flipping this switch, Tomcat/Undertow starts using Virtual Threads to handle web requests. It’s a complete paradigm shift that lets us build more scalable systems with less infrastructure cost. The takeaway for teams: We no longer have to choose between "easy-to-read code" and "high-performance code." With Java 21, we get both. #Java #SpringBoot #BackendDevelopment #ProjectLoom #SoftwareEngineering #Scalability #JVM
To view or add a comment, sign in
-
-
While building a recent Spring Boot application, I realized... The Hook: Stop defaulting to .parallelStream() to make your Java code "faster." 🛑 The Insight: It’s a common misconception that parallel streams always improve performance. Under the hood, parallelStream() uses the common ForkJoinPool. If you are executing CPU-intensive tasks on a massive dataset, it’s great. But if you are doing I/O operations (like database calls or network requests) inside that stream, you will exhaust the thread pool and bottleneck your entire application. The Pro Tip: Always benchmark. For I/O bound tasks, look into asynchronous programming (like CompletableFuture) or Java 21's Virtual Threads instead of parallel streams. #Java #PerformanceOptimization #SoftwareEngineering #CleanCode
To view or add a comment, sign in
-
Most Spring Boot apps I've reviewed in production don't fail because of bad logic. They fail because of 𝗹𝗮𝘇𝘆 𝗱𝗲𝗳𝗮𝘂𝗹𝘁 𝗰𝗼𝗻𝗳𝗶𝗴𝘂𝗿𝗮𝘁𝗶𝗼𝗻𝘀 that nobody questioned. Here's what I wish someone told me earlier. Stop returning entity objects directly from your controllers. It seems convenient until you accidentally expose sensitive fields or break your API contract when the schema changes. Always map to a dedicated response DTO. Your future self will thank you when a database column rename doesn't trigger a client-side outage. Use constructor injection instead of `@Autowired` on fields. It makes your dependencies explicit, simplifies testing, and lets the compiler catch missing beans before runtime does. Spring actually recommends this approach. Java @RestController public class OrderController { private final OrderService orderService; public OrderController(OrderService orderService) { this.orderService = orderService; } } Configure 𝘀𝗲𝗽𝗮𝗿𝗮𝘁𝗲 𝗽𝗿𝗼𝗳𝗶𝗹𝗲𝘀 for each environment from day one. Hardcoding values in `application.properties` and "fixing it later" is technical debt that compounds fast. Use `application-dev.yml`, `application-prod.yml`, and externalize secrets through environment variables or a vault. One more thing people overlook: 𝗮𝗹𝘄𝗮𝘆𝘀 𝘀𝗲𝘁 𝗲𝘅𝗽𝗹𝗶𝗰𝗶𝘁 𝘁𝗿𝗮𝗻𝘀𝗮𝗰𝘁𝗶𝗼𝗻 𝗯𝗼𝘂𝗻𝗱𝗮𝗿𝗶𝗲𝘀 with `@Transactional` on your service layer, not your repository layer. It gives you control over rollback behavior when multiple repositories are involved. What's one Spring Boot default you learned the hard way should have been overridden? #SpringBoot #Java #BackendDevelopment #SoftwareEngineering #CleanCode
To view or add a comment, sign in
-
Java 21 Virtual Threads will not make your API faster. In fact, they might take your entire system down. Right now, everyone is rushing to upgrade to Spring Boot 3.2 to flip this magical switch: spring.threads.virtual.enabled=true The promise is intoxicating: Tomcat is no longer limited to 200 heavy OS threads. You can now effortlessly handle 10,000+ concurrent requests. You deploy it. You expect blazing-fast performance. Instead, your app freezes, throws endless SQLTransientConnectionExceptions, and completely dies. 💥 What went wrong? You just DDoS'd your own database. Virtual Threads scale your compute layer infinitely. But they do absolutely nothing to scale your infrastructure. Let’s look at the math: If your API gets a sudden spike of 5,000 requests, Virtual Threads will instantly accept all 5,000. But your HikariCP database connection pool only has 10 connections by default. You now have 5,000 lightweight threads violently racing to acquire one of 10 heavy database connections. 9,990 threads are blocked. The Hikari pool reaches its timeout. The application cascades into a catastrophic failure. The Senior Architect Reality Check: Virtual threads do not execute code faster. They just allow you to wait more efficiently. • If your bottleneck was waiting for 3rd-party APIs, Virtual Threads are pure magic. • If your bottleneck was your Database, Virtual Threads will just crash it faster. How to survive the upgrade to Java 21: 1. Implement Bulkheads: Use Semaphores to limit exactly how many Virtual Threads are allowed to query the database at the same time. Protect your downstream! 2. Watch for "Pinning": If you have legacy code using synchronized blocks or native JNI calls, it will "pin" the OS thread, completely breaking the Virtual Thread magic. Upgrade to ReentrantLock. 3. Never pool them: Virtual threads are incredibly cheap. Never put them in a ThreadPoolExecutor. Create them, use them, throw them away. Have you made the jump to Java 21 yet? Did you hit the database bottleneck? 👇 #Java21 #VirtualThreads #SpringBoot #SystemDesign #Microservices #BackendArchitecture #SoftwareEngineering #PerformanceTuning #LogicLedaMagic
To view or add a comment, sign in
-
Java just quietly changed how servers handle millions of requests. And most developers haven't noticed yet. I'm talking about Virtual Threads — introduced in Java 21 (LTS). 🧵 Before Virtual Threads: → 1 request = 1 OS thread → OS threads are expensive (~1MB stack each) → 10,000 requests = system struggling → The only fix was reactive programming (complex, hard to debug) After Virtual Threads: → Millions of virtual threads, managed by the JVM → Blocking a virtual thread doesn't block an OS thread → Write simple, readable blocking code — JVM handles the rest → No reactive spaghetti needed How easy is it to use? Thread.ofVirtual().start(() -> handleRequest(req)); Spring Boot 3.2+ supports it out of the box. Set spring.threads.virtual.enabled=true and you're done. This is Project Loom — years in the making. Production-ready right now. If your team is still on Java 8 or 11 — it's time to have that upgrade conversation. Have you tried Virtual Threads yet? Drop your experience below 👇 #Java #Java21 #VirtualThreads #ProjectLoom #BackendDevelopment #SpringBoot
To view or add a comment, sign in
-
-
Java Streams are killing your performance Java Streams look clean… but they can silently destroy your performance. I saw this in a production audit last week. A simple loop processing 1M records was replaced with streams “for readability”. // ❌ Stream version list.stream() .filter(x -> x.isActive()) .map(x -> transform(x)) .collect(Collectors.toList()); // ✅ Optimized loop List<Result> result = new ArrayList<>(); for (Item x : list) { if (x.isActive()) { result.add(transform(x)); } } 🚨 What happened in production: • CPU usage increased by 35% • GC pressure exploded • Latency x2 under load ❗ Why? Streams: • Create more objects • Add hidden overhead • Are NOT always optimized by JVM ✅ Fix: • Use streams for readability (small datasets) • Use loops for performance-critical paths • Benchmark before choosing https://www.joptimize.io/ Clean code ≠ fast code. Are you using streams in performance-sensitive code? #JavaDev #SpringBoot #JavaPerformance #Backend #SoftwareEngineering
To view or add a comment, sign in
-
-
Continuing my recent posts on JVM internals and performance, today I’m sharing a look at Java 21 Virtual Threads (from Project Loom). For a long time, Java handled concurrency using platform threads (OS threads)—which are powerful but expensive, especially for I/O-heavy applications. This led to complex patterns like thread pools, async programming, and reactive frameworks to achieve scalability. With Virtual Threads, Java introduces a lightweight threading model where thousands (even millions) of threads can be managed efficiently. 👉 When a virtual thread performs a blocking I/O operation, the underlying carrier (platform) thread is released to do other work. This brings Java closer to the efficiency of event-loop models (like in Node.js), while still allowing developers to write simple, synchronous code without callback-heavy complexity. However, in real-world scenarios, especially when teams migrate from Java 8/11 to Java 21, it’s important to keep a few things in mind: • Virtual Threads are not a silver bullet—they primarily improve I/O-bound workloads, not CPU-bound ones • If the architecture is not aligned, you may not see significant latency improvements • Legacy codebases often contain synchronized blocks or locking, which can lead to thread pinning and reduce the benefits of Virtual Threads Project Loom took years to evolve because it required deep changes to the JVM, scheduling, and thread management—while preserving backward compatibility and Java’s simplicity. Sharing a diagram that illustrates: • Platform threads vs Virtual Threads • Carrier thread behavior • Pinning scenarios Curious to hear—are you exploring Virtual Threads in your applications, or still evaluating? 👇 #Java #Java21 #VirtualThreads #ProjectLoom #Concurrency #Performance #SoftwareEngineering
To view or add a comment, sign in
-
-
Spring Boot Bean Scope — Not just Singleton 🤯 Most developers only know this: 👉 Default scope = Singleton But there’s more 👇 ✅ Prototype → New instance every time ✅ Request → Per HTTP request ✅ Session → Per user session 💡 Why it matters: ✔ Memory optimization ✔ Better state handling ⚠️ Mistake: Using singleton for stateful data ❌ 👉 Leads to concurrency issues 🔥 Real lesson: Choose scope based on use case, not default Backend bugs often start here 🚨 #SpringBoot #Java #Architecture
To view or add a comment, sign in
-
Java 21 shipped Virtual Threads — and they're one of the most impactful JVM changes in years. Here's the 60-second version 🧵 𝗧𝗵𝗲 𝗽𝗿𝗼𝗯𝗹𝗲𝗺: Every OS thread costs ~1 MB of stack memory and a kernel context switch. A typical server can handle ~10,000 threads before performance collapses. With classic thread-per-request models, most threads are just sitting idle — waiting on DB queries or HTTP calls. 𝗧𝗵𝗲 𝘀𝗼𝗹𝘂𝘁𝗶𝗼𝗻: Virtual Threads (Project Loom) decouple concurrency from OS threads entirely. Instead of 1 thread blocking on I/O, the JVM: → Unmounts the virtual thread from its carrier thread → Parks the virtual thread's stack on the heap (just a cheap object) → Picks up another virtual thread to run immediately → Resumes the original thread when I/O completes The result? A handful of carrier threads (one per CPU core) serve millions of virtual threads — with zero idle waiting. 𝗪𝗵𝘆 𝘁𝗵𝗶𝘀 𝗺𝗮𝘁𝘁𝗲𝗿𝘀: ✅ Write plain blocking code — no async/await, no reactive chains ✅ Virtual threads cost ~few KB vs ~1 MB for OS threads ✅ Spring Boot 3.2+ enables this with a single property ✅ Creation is ~1000x faster than platform threads 𝗧𝗵𝗲 𝗰𝗮𝘁𝗰𝗵: CPU-bound tasks see no benefit — virtual threads help I/O-bound workloads. Also, synchronized blocks "pin" the virtual thread to its carrier thread, so prefer ReentrantLock in hot paths. 𝗕𝗼𝘁𝘁𝗼𝗺 𝗹𝗶𝗻𝗲: Virtual threads aren't about doing more things at once — they're about wasting zero time waiting. Same simple code. Dramatically better throughput. If you're running Java 21+ on any I/O-heavy service, this is worth exploring today.
To view or add a comment, sign in
Explore content categories
- Career
- Productivity
- Finance
- Soft Skills & Emotional Intelligence
- Project Management
- Education
- Technology
- Leadership
- Ecommerce
- User Experience
- Recruitment & HR
- Customer Experience
- Real Estate
- Marketing
- Sales
- Retail & Merchandising
- Science
- Supply Chain Management
- Future Of Work
- Consulting
- Writing
- Economics
- Artificial Intelligence
- Employee Experience
- Workplace Trends
- Fundraising
- Networking
- Corporate Social Responsibility
- Negotiation
- Communication
- Engineering
- Hospitality & Tourism
- Business Strategy
- Change Management
- Organizational Culture
- Design
- Innovation
- Event Planning
- Training & Development
Fantastic Ragasudha 👏🏻keep going 👍