Stop wasting memory on threads that do nothing. 🛑 If you’re building Java backends, you’ve probably seen this: More users → more threads → more RAM usage I recently explored Virtual Threads (Java 21 / Project Loom), and this concept finally clicked for me. 💡 The Problem In standard Java: 1 request = 1 Platform Thread During DB/API call → thread gets blocked It’s like a waiter standing idle while food is cooking 🍽️ 👉 Wasted resources + poor scalability 🔍 The Solution: Virtual Threads 👉 Lightweight threads managed by JVM (not OS) Cheap to create Can run thousands easily Perfect for I/O-heavy backend systems ⚙️ How it actually works (Mounting / Unmounting) 1️⃣ Mounting Virtual Thread runs on a Carrier Thread (Platform Thread) 2️⃣ I/O Call (DB/API) Your code looks blocking 3️⃣ Unmounting (Parking) Virtual Thread is paused & parked in heap memory 👉 It releases the Carrier Thread 4️⃣ Carrier Thread is free Handles another request immediately 5️⃣ Remounting (Resume) Once response comes → Virtual Thread continues 💻 The "magic" in code // Looks like blocking code Runnable task = () -> { System.out.println("Processing: " + Thread.currentThread()); String data = fetchDataFromDB(); // DB/API call System.out.println("Result: " + data); }; // Run using Virtual Thread Thread.ofVirtual().start(task); 🧩 What’s happening behind the scenes? 👉 Thread.ofVirtual() Creates a lightweight thread (stored in heap, not OS-level) 👉 During DB/API call Virtual Thread gets unmounted (parked) Carrier Thread becomes free 👉 While waiting Same thread handles other requests 👉 When response comes Scheduler remounts Virtual Thread Execution continues 📈 Result No idle threads Better resource usage Simple synchronous code High scalability without complex async code 🧠 Biggest takeaway 👉 “Code looks blocking… but system is not blocked.” That’s the mindset shift. Have you tried Virtual Threads in your services yet? Did you see any real performance improvement? 🤔 #Java #BackendEngineering #VirtualThreads #ProjectLoom #Java21 #Microservices #Performance
Java Virtual Threads Improve Backend Scalability
More Relevant Posts
-
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
-
-
Most Java performance issues don’t show up in code reviews They show up in object lifetimes. Two pieces of code can look identical: same logic same complexity same output But behave completely differently in production. Why? Because of how long objects live. Example patterns: creating objects inside tight loops → short-lived → frequent GC holding references longer than needed → objects move to old gen caching “just in case” → memory pressure builds silently Nothing looks wrong in the code. But at runtime: GC frequency increases pause times grow latency becomes unpredictable And the worst part? 👉 It doesn’t fail immediately. 👉 It degrades slowly. This is why some systems: pass load tests work fine initially then become unstable weeks later Takeaway: In Java, performance isn’t just about what you do. It’s about how long your data stays alive while doing it. #Java #JVM #Performance #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
-
-
#Post11 In the previous post(https://lnkd.in/dynAvNrN), we saw how to create threads in Java. Now let’s talk about a problem. If creating threads is so simple… why don’t we just create a new thread every time we need one? Let’s say we are building a backend system. For every incoming request/task, we create a new thread: new Thread(() -> { // process request }).start(); This looks simple. But this approach breaks very quickly in real systems because of below mentioned problems. Problem 1: Thread creation is expensive Creating a thread is not just creating an object. It involves: • Allocating memory (stack) • Registering with OS • Scheduling overhead Creating thousands of threads = performance degradation Problem 2: Too many threads → too much context switching We already saw this earlier(https://lnkd.in/dYG3v-vb). More threads does NOT mean more performance. Instead: • CPU spends more time switching • Less time doing actual work Problem 3: No control over thread lifecycle When you create threads manually: • No limit on number of threads • No reuse • Hard to manage failures This quickly becomes difficult to manage as the system grows. So what’s the solution? Instead of creating threads manually: we use something called the Executor Framework. In simple words consider the framework to be like: Earlier, we were manually hiring a worker (thread) for every task. With Executor, we have a team of workers (thread pool), and we just assign tasks to them. Key idea Instead of: Creating a new thread for every task We do: Submit tasks to a pool of reusable threads This is exactly what Java provides using: Executor Framework Key takeaway Manual thread creation works for learning, but does not scale in real-world systems. Thread pools help: • Control number of threads • Reduce overhead • Improve performance We no longer manage threads directly — we delegate that responsibility to the Executor Framework. In the next post, we’ll see how Executor Framework works and how to use it in Java. #Java #Multithreading #Concurrency #BackendDevelopment #SoftwareEngineering
To view or add a comment, sign in
-
Most Java developers have used ThreadLocal to pass context — user IDs, request IDs, tenant info — across method calls. It works fine with a few hundred threads. But with virtual threads in Java 21, "fine" becomes a memory problem fast. With 1 million virtual threads, you get 1 million ThreadLocalMap instances — each holding mutable, heap-allocated state that GC has to clean up. And because ThreadLocal is mutable and global, silent overwrites like this are a real risk in large systems: userContext.set(userA); // ... deep somewhere ... userContext.set(userB); // overrides without warning Java 21 introduces ScopedValue — the right tool for virtual threads: ScopedValue.where(USER, userA).run(() -> { // USER is safely available here, immutably }); It's immutable, scoped to an execution block, requires no per-thread storage, and cleans itself up automatically. No more silent overrides. No memory bloat. No manual remove() calls. In short: ThreadLocal was designed for few, long-lived threads. ScopedValue is designed for millions of short-lived virtual threads. If you're building high-concurrency APIs with Spring Boot + virtual threads and still using ThreadLocal for request context — this switch can meaningfully reduce your memory footprint and make your code safer. Are you already using ScopedValue in production, or still on ThreadLocal? Would love to hear what's holding teams back. #Java #Java21 #VirtualThreads #ProjectLoom #BackendEngineering #SpringBoot #SoftwareEngineering
To view or add a comment, sign in
-
Understanding the Magic Under the Hood: How the JVM Works ☕️⚙️ Ever wondered how your Java code actually runs on any device, regardless of the operating system? The secret sauce is the Java Virtual Machine (JVM). The journey from a .java file to a running application is a fascinating multi-stage process. Here is a high-level breakdown of the lifecycle: 1. The Build Phase 🛠️ It all starts with your Java Source File. When you run the compiler (javac), it doesn't create machine code. Instead, it produces Bytecode—stored in .class files. This is the "Write Once, Run Anywhere" magic! 2. Loading & Linking 🔗 Before execution, the JVM's Class Loader Subsystem takes over: • Loading: Pulls in class files from various sources. • Linking: Verifies the code for security, prepares memory for variables, and resolves symbolic references. • Initialization: Executes static initializers and assigns values to static variables. 3. Runtime Data Areas (Memory) 🧠 The JVM manages memory by splitting it into specific zones: • Shared Areas: The Heap (where objects live) and the Method Area are shared across all threads. • Thread-Specific: Each thread gets its own Stack, PC Register, and Native Method Stack for isolated execution. 4. The Execution Engine ⚡ This is the powerhouse. It uses two main tools: • Interpreter: Quickly reads and executes bytecode instructions. • JIT (Just-In-Time) Compiler: Identifies "hot methods" that run frequently and compiles them directly into native machine code for massive performance gains. The Bottom Line: The JVM isn't just an interpreter; it’s a sophisticated engine that optimizes your code in real-time, manages your memory via Garbage Collection (GC), and ensures platform independence. Understanding these internals makes us better developers, helping us write more efficient code and debug complex performance issues. #Java #JVM #SoftwareEngineering #Programming #BackendDevelopment #TechExplainers #JavaVirtualMachine #CodingLife
To view or add a comment, sign in
-
-
I was debugging a backend issue recently in a Java service. At first nothing looked wrong. No errors, no obvious problems. But something was off. After checking a bit more, it turned out to be a small mismatch between the data model and the repository. The code worked fine in most cases, but not with certain data. Fixing it was simple. Finding it was not. Sometimes the problem is not complex. It’s just hidden in small details. #Java #BackendEngineering #Debugging #SpringBoot
To view or add a comment, sign in
-
JVM Architecture - what actually runs your Java code ⚙️ While working with Java and Spring Boot, I realized something: We spend a lot of time writing code, but not enough time understanding what executes it. That’s where the JVM (Java Virtual Machine) comes in. A simple breakdown: • Class Loader Loads compiled `.class` files into memory. • Runtime Data Areas * Heap → stores objects (shared across threads) 🧠 * Stack → stores method calls and local variables (per thread) * Method Area → stores class metadata and constants * PC Register → tracks current instruction * Native Method Stack → handles native calls • Execution Engine * Interpreter - runs bytecode line by line * JIT Compiler - optimizes frequently used code into native machine code ⚡ • Garbage Collector Automatically removes unused objects from memory --- Why this matters: Understanding JVM helps in: * Debugging memory issues (like OutOfMemoryError) * Improving performance * Writing more efficient backend systems --- The more I learn, the more I see this pattern: Good developers write code. Better developers understand how it runs. #Java #JVM #BackendDevelopment #SpringBoot #SystemDesign
To view or add a comment, sign in
-
🔥 Java 21 just solved the problem that made us over-engineer everything. Virtual Threads. Here's what changes for Java developers. 🔴 OLD WAY - Platform Threads → 1 request = 1 OS thread → Thread blocked on DB query? Wasted. Just sitting there. → 500 concurrent users = 500 threads = tune your thread pool or crash → We added async, reactive, CompletableFuture - just to avoid blocking → Code became unreadable. Debugging became a nightmare. 🟢 JAVA 21 - Virtual Threads → Write simple, readable, synchronous-style code → JVM handles the concurrency underneath → Thread blocks on DB/API? JVM parks it, picks up another task → 100,000 virtual threads use the same memory as 200 platform threads → No reactive gymnastics. No callback hell. Just clean Java. ✅ What this means for your Spring Boot app: // Before - you needed async to avoid thread exhaustion @Async public CompletableFuture<Payment> processPayment(Request req) { ... } // After - simple blocking code, virtual threads handle the scale public Payment processPayment(Request req) { ... } spring.threads.virtual.enabled=true That's it. One config line. ✅ Real gains in Telecom & BFSI apps: → Payment APIs handling 3x concurrent load - zero infra change → Eliminated thread pool tuning from our deployment checklist → Removed 40% of reactive boilerplate from our codebase → New devs can read and debug the code again ❌ One caveat every developer must know: → synchronized blocks can PIN virtual threads to platform threads → If your DB driver or library uses synchronized internally - you lose the benefit → Check with: -Djdk.tracePinnedThreads=full → JDBC drivers like Oracle 23c and PostgreSQL 42.6+ are already fixed → Test. Measure. Then celebrate. 💡 The best part? You don't need to learn reactive programming to write scalable Java anymore. Virtual threads give you the performance of async with the simplicity of blocking code. That's the trade-off we've been waiting 20 years for. 📌 If you're still tuning thread pools and adding @Async everywhere - upgrade to Java 21. Your codebase will thank you. #Java #Java21 #VirtualThreads #SpringBoot #BFSI #Telecom #BackendEngineering #SoftwareEngineering #Performance #SystemDesign #JavaDeveloper
To view or add a comment, sign in
-
If your class name looks like CoffeeWithMilkAndSugarAndCream… you’ve already lost. This is how most codebases slowly break: You start with one clean class. Then come “small changes”: add logging add validation add caching So you create: a few subclasses… then a few more or pile everything into if-else Now every change touches existing code. And every change risks breaking something. That’s not scaling. That’s slow decay. The Decorator Pattern fixes this in a simple way: Don’t modify the original class. Wrap it. Start with a base object → then layer behavior on top of it. Each decorator: adds one responsibility doesn’t break existing code can be combined at runtime No subclass explosion. No god classes. No fragile code. Real-world example? Java I/O does this everywhere: you wrap streams on top of streams. The real shift is this: Stop thinking inheritance. Start thinking composition. Because most “just one more feature” problems… are actually design problems. Have you ever seen a codebase collapse under too many subclasses or flags? #DesignPatterns #LowLevelDesign #SystemDesign #CleanCode #Java #SoftwareEngineering #OOP Attaching the decorator pattern diagram with a simple example.
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
Well explained.