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
Migrating from ThreadLocal to ScopedValue in Java 21
More Relevant Posts
-
Remember a time when we all agreed that blocking JDBC couldn't possibly scale? Java 25 just changed the game. Between Virtual Threads and the latest JVM optimizations, the "simple" blocking pattern has been vindicated. We can finally have both i.e. our application scalability and our readable code, too. I just posted another update to my series on Non-Blocking vs. Blocking JDBC. After many years of tracking this, the answer seems to be Java 25. Check it out: https://lnkd.in/daW7aWrM #Java #Programming #Backend #TechTrends
The Great Reconvergence: Why Java 25 Just Vindicated the Blocking JDBC Pattern suchakjani.medium.com To view or add a comment, sign in
-
⚠️ Why Java Killed PermGen (And What Replaced It) Before Java 8, JVM had PermGen (Permanent Generation) A special memory region inside the heap used for: Class metadata Method metadata String intern pool (pre-Java 7) Static variables The Problem was with PermGen as it had a fixed size: -XX:MaxPermSize=256m Sounds fine until: Applications dynamically load classes Frameworks create proxies (Spring, Hibernate) ClassLoaders don’t get garbage collected 👉 Boom: OutOfMemoryError: PermGen space Very common in: App servers Long-running systems Hot-deploy environments 🧠 Enter Metaspace (Java 8+) PermGen was removed and replaced with Metaspace Key change: Moved class metadata OUT of heap → into native memory ⚡ What Changed? Memory Location Native memory Size Dynamic (auto grows) Tuning Minimal OOM Errors Frequent Much rarer 🧠 Why This Was a Big Deal Metaspace: Grows dynamically (no fixed ceiling by default) Reduces OOM crashes Simplifies JVM tuning Handles dynamic class loading better But It’s Not “Unlimited" If not controlled It can still cause: OutOfMemoryError: Metaspace So you can still set limits: -XX:MaxMetaspaceSize=512m 🧠 What Actually Lives in Metaspace? Class metadata Method metadata Runtime constant pool NOT: Objects (Heap) Stack frames (Stack) PermGen failed because it was fixed. Metaspace works because it adapts. #Java #JVM #MemoryManagement #Metaspace #PerformanceEngineering #BackendDevelopment #JavaInternals #LearnInPublic
To view or add a comment, sign in
-
Recently, while working on a backend application in Java, I encountered a common scalability issue. Even with thread pools in place, the system struggled under high load, particularly during multiple external API and database calls. Most threads were waiting but still consuming resources. While multithreading in Java is crucial for developing scalable backend systems, it often introduces complexity, from managing thread pools to handling synchronization. The introduction of Virtual Threads (Project Loom) in Java is changing the landscape. Here’s a simple breakdown: - Traditional Threads (Platform Threads) - Backed by OS threads - Expensive to create and manage - Limited scalability - Requires careful thread pool tuning - Virtual Threads (Lightweight Threads) - Managed by the JVM - Extremely lightweight (can scale to millions) - Ideal for I/O-bound tasks (API calls, DB operations) - Reduces the need for complex thread pool management Why this matters: In most backend systems, threads spend a lot of time waiting during I/O operations. With platform threads, resources get blocked, while with virtual threads, blocking becomes cheap. This leads to: - Better scalability - Simpler code (more readable, less callback-heavy) - Improved resource utilization When to use what? - Virtual Threads → I/O-heavy, high-concurrency applications - Platform Threads → CPU-intensive workloads Virtual Threads are not just a performance improvement; they simplify our approach to concurrency in Java. This feels like a significant shift for backend development. #Java #Multithreading #Concurrency #BackendDevelopment #SoftwareEngineering
To view or add a comment, sign in
-
-
🚀 Day 3 – Why String is Immutable in Java (and why it matters) One question I explored today: Why are "String" objects immutable in Java? String s = "hello"; s.concat(" world"); System.out.println(s); // still "hello" 👉 Instead of modifying the existing object, Java creates a new String But why was it designed this way? ✔ Security – Strings are widely used in sensitive areas (like class loading, file paths, network connections). Immutability prevents accidental or malicious changes. ✔ Performance (String Pool) – Since Strings don’t change, they can be safely reused from the pool, saving memory. ✔ Thread Safety – No synchronization needed, multiple threads can use the same String safely. 💡 This also explains why classes like "StringBuilder" and "StringBuffer" exist—for mutable operations when performance matters. Small design decision, but huge impact on how Java applications behave internally. #Java #BackendDevelopment #JavaInternals #LearningInPublic #SoftwareEngineering
To view or add a comment, sign in
-
**Memory Leaks in Production: how to detect and prevent them(Spring Boot)** - In Java, we often say "Garbage Collector handles memory so we don’t worry about leaks." But that’s only half true. Java doesn’t leak memory like C/C++, but it can leak references and that’s enough to crash our production system. * What Is a Memory Leak? --A memory leak is a scenario that happens when objects are no longer needed but are still referenced, so the Garbage Collector cannot reclaim them. The JVM is working correctly but our code is holding references longer than it should. * Common Causes in Spring Boot Applications * -Unbounded caches -Static collections that keep growing -ThreadLocal not cleared in thread pools -Large objects stored in HTTP session -Event listeners not deregistered -Reactive streams that never terminate These issues don’t fail the system immediately, they grow silently. *How to Detect a Memory Leak * 1️. Monitor JVM Memory Use: -Spring Boot Actuator (jvm.memory.used) -Prometheus + Grafana -JMC / VisualVM Red flag: Memory keeps increasing even after Full GC. 2️. Check GC Behavior If you see: -Frequent Full GC -Increasing GC pause time -Old Gen not shrinking It indicates memory pressure or possible leak. 3️. Take a Heap Dump > jmap -dump:live,format=b,file=heap.hprof <PID> Then analyze using: -Eclipse MAT -Java Mission Control -VisualVM Focus on: -Dominator Tree -Retained Size -Reference chains from GC roots This shows what is preventing memory from being reclaimed. * How to Prevent Memory Leaks * Prevention is architectural, not accidental. -Use bounded caches (set max size & TTL) -Always remove ThreadLocal values - Avoid large objects in HTTP session -Close resources using try-with-resources -Avoid unnecessary static collections -Monitor memory in production (don’t wait for OOM) #MemoryLeak #Java #SpringBoot #JVM #BackendDevelopment #Microservices #PerformanceEngineering
To view or add a comment, sign in
-
📖 New Post: Java Memory Model Demystified: Stack vs. Heap Where do your variables live? We explain the Stack, the Heap, and the Garbage Collector in simple terms. #java #jvm #memorymanagement
To view or add a comment, sign in
-
I wrote an article for JAVAPRO about the Foreign Function and Memory (FFM) API, added in #Java 22, and how it got used to add a new and better plugin to The Pi4J Project. You can read it here: https://lnkd.in/em6K5xhM
To view or add a comment, sign in
-
🚦 Thread Safety in Java - Why Your Code Breaks Under Concurrency Your code works perfectly with 1 user. But when multiple threads hit the same object together… chaos starts. Two threads may try to update the same data at the same time → causing race conditions, inconsistent values, and hard-to-debug production issues. 🔍 What is Thread Safety? A class or block of code is called thread-safe when it behaves correctly even when accessed by multiple threads simultaneously. Meaning: ✔ No corrupted data ✔ No unexpected outputs ✔ Predictable execution ⚠ Common Problem count++; Looks harmless, right? But internally it is: 1. Read count 2. Increment 3. Write back If two threads do this together, one update can be lost. ✅ How Java Handles Thread Safety 1. synchronized keyword Allows only one thread at a time inside critical section. public synchronized void increment() { count++; } 2. Atomic Classes For lightweight thread-safe operations. AtomicInteger count = new AtomicInteger(); count.incrementAndGet(); 3. Concurrent Collections Use thread-safe collections like: 1. ConcurrentHashMap 2. CopyOnWriteArrayList instead of normal HashMap/List in multithreaded apps. 4. Immutability Objects that never change are naturally thread-safe. 💡 Rule of Thumb If multiple threads share mutable data, protection is mandatory. Otherwise bugs won't appear in local testing... they appear directly in production 😄 👉 If you are preparing for Java backend interviews, connect & follow - I share short, practical backend concepts regularly. #Java #Multithreading #ThreadSafety #BackendDevelopment #SpringBoot #JavaDeveloper #Programming #InterviewPrep #Concurrency #SoftwareEngineering
To view or add a comment, sign in
-
-
♻️ Ever wondered how Java manages memory automatically? Java uses Garbage Collection (GC) to clean up unused objects — so developers don’t have to manually manage memory. Here’s the core idea in simple terms 👇 🧠 Java works on reachability It starts from GC Roots: • Variables in use • Static data • Running threads Then checks: ✅ Reachable → stays in memory ❌ Not reachable → gets removed 💡 Even objects referencing each other can be cleaned if nothing is using them. 🔍 Different types of Garbage Collectors in Java: 1️⃣ Serial GC • Single-threaded • Best for small applications 2️⃣ Parallel GC • Uses multiple threads • Focuses on high throughput 3️⃣ CMS (Concurrent Mark Sweep) • Runs alongside application • Reduces pause time (now deprecated) 4️⃣ G1 (Garbage First) • Splits heap into regions • Balanced performance + low pause time 5️⃣ ZGC • Ultra-low latency GC • Designed for large-scale applications ⚠️ One important thing: If an object is still referenced (even accidentally), it won’t be cleaned → which can lead to memory issues. 📌 In short: Java automatically removes unused objects by checking whether they are still reachable — using different GC strategies optimized for performance and latency. #Java #Programming #JVM #GarbageCollection #SoftwareDevelopment #TechConcepts
To view or add a comment, sign in
-
-
Day 7 of #100DaysOfCode — Java is getting interesting ☕ Today I explored the Java Collections Framework. Before this, I was using arrays for everything. But arrays have one limitation — fixed size. 👉 What if we need to add more data later? That’s where Collections come in. 🔹 Key Learnings: ArrayList grows dynamically — no size worries Easy operations: add(), remove(), get(), size() More flexible than arrays 🔹 Iterator (Game changer) A clean way to loop through collections: hasNext() → checks next element next() → returns next element remove() → safely removes element 🔹 Concept that clicked today: Iterable → Collection → List → ArrayList This small hierarchy made everything much clearer. ⚡ Array vs ArrayList Array → fixed size ArrayList → dynamic size Array → stores primitives ArrayList → stores objects Still exploring: Set, Map, Queue next 🔥 Consistency is the only plan. Showing up every day 💪 If you’re also learning Java or working with Collections — let’s connect 🤝 #Java #Collections #ArrayList #100DaysOfCode #JavaDeveloper #LearningInPublic
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