🚨Application is running in production and appears to be working properly. However, even when there are no API calls or incoming requests, it still results in an OutOfMemory(OOM) error. 👉Memory Leak Objects are created but never deleted - memory fills up slowly over time 👉 Background Jobs Scheduled tasks run silently on timer - not on user requests. They consume memory 24/7. 👉Cron Jobs Cron jobs are fire at fixed intervals - every minute, hour, or day. Each run can load large data into memory and never release it if not handled properly. 👉JVM Schedulers Internal JVM schedulers like ScheduledExecutorService, Timer, and TimeTask run completely inside the JVM. If tasks are registered without cancellation and shutdown, they hold memory reference silently 👉Too Many Threads Each thread eats 1MB of RAM. 1000 idle threads = nearly 1 GB gone - no traffic needed. 👉Cache Has No Limit Your cache keeps storing data but never removes old entries - it grows forever. 👉 Logging Buffers Async loggers hold messages in a queue. If it fills up, it eats your memory. 👉Wrong JVM / Container Configuration JVM asks for 3GB , Container only has 2.5GB - when your app tried to go beyond 2.5, the OS kills your process or application silently ➡️How to Overcome Memory Leaks -> heap dump analysis Background Jobs -> use try-finally cleanup Cron Jobs -> batch processing JVM Schedulers -> shutdown on app exit Threads -> use fixed thread pool size Cache -> configure time to live on every cache Logging -> bounded async queue JVM Config -> match heap to container size 🎯"Memory doesn't need traffic to leak - it just needs time." #OutOfMemory #MemoryLeak #Java #JVM #JavaDeveloper #ProductionIssues #Microservices #Debugging
Preventing Memory Leaks in Production Java Applications
More Relevant Posts
-
A recent issue reminded me that performance optimizations can sometimes become production problems. We had an API that: 1️⃣ Fetches initial details 2️⃣ Extracts IDs from the response 3️⃣ Makes another database call to fetch larger secondary data To speed up step 3, parallel processing was introduced using a fixed thread pool. Sounds reasonable — until load testing began. Under heavy traffic, thread creation kept increasing across instances until limits were hit, leading to: ⚠️ "Can't create new native thread" The interesting part? The optimization worked for individual requests. But at scale, the resource model didn’t. A request with a small number of IDs didn’t always need dedicated worker threads, yet threads were still being allocated repeatedly under concurrent load. The fix was moving to a shared/reusable thread pool model with better resource control. 💡 My takeaway: Code that is fast in isolation may fail under concurrency. When designing for performance, it’s important to ask: - How does this behave at 1 request? - How does this behave at 1000 requests? - What resources grow with traffic? Scalability is often less about speed, more about control. #BackendEngineering #Java #PerformanceTesting #Scalability #Concurrency
To view or add a comment, sign in
-
Recently worked on improving API performance in a backend system ⚡ 📉 Problem: High response time under load 🔧 What I did: Optimized DB queries Introduced caching Refactored inefficient logic 📈 Result: ~40% performance improvement 🚀 💡 Lesson: Performance issues are rarely about one thing — it’s always a combination. Small improvements → Big impact. #BackendDevelopment #PerformanceOptimization #Java #Engineering
To view or add a comment, sign in
-
In this 2nd article : scaling bulk search in Spring Boot with parallel batch jobs and controlled concurrency A few lessons stood out for me: [1] parallelism helps, but only until it starts hurting upstream systems [2] chunking is not just a batch setting, it becomes a stability boundary [3] partial failure handling matters as much as throughput [4] caching repeated enrichment work can remove a surprising amount of unnecessary load One of the biggest shifts for me was moving away from a more limited blocking flow into parallel batch jobs while still keeping pressure on downstream systems under control. I wrote the full breakdown here: https://lnkd.in/ggb2ZCF2 #SpringBoot #Java #Backend #SystemDesign #SoftwareEngineering #SpringBatch #DistributedSystems
To view or add a comment, sign in
-
I had an API returning correct data. But response time was too high. Issue turned out to be unnecessary data being fetched from DB. Reduced fields, optimized query. Small change, big impact. #Backend #Java #SpringBoot #Performance
To view or add a comment, sign in
-
🚀 Deep Dive into JVM Architecture (Must-Know for Backend Engineers) Understanding how Java works internally is a game-changer for writing high-performance and scalable applications. Here’s a quick breakdown of the JVM internals 👇 🔹 Class Loader Subsystem Responsible for loading .class files into memory. It goes through: • Loading • Linking (Verification, Preparation, Resolution) • Initialization 🔹 Runtime Data Areas (Memory Management) JVM divides memory into: • Heap (Shared): Stores objects → managed by Garbage Collector • Method Area (Shared): Class metadata, static variables • Stack (Per Thread): Method calls, local variables • PC Register: Tracks current execution • Native Method Stack: For native calls 🔹 Execution Engine • Interpreter: Executes bytecode line by line • JIT Compiler: Converts hot code to native machine code for performance • Garbage Collector: Automatically manages memory 🔹 JNI (Java Native Interface) Allows Java to interact with native libraries (C/C++) 💡 Why this matters? Understanding JVM internals helps in: ✔ Debugging memory leaks ✔ Optimizing performance (GC tuning, heap sizing) ✔ Designing scalable microservices ✔ Cracking senior-level interviews #Java #JVM #BackendEngineering #SystemDesign #Performance #Microservices
To view or add a comment, sign in
-
-
Logs are not observability Many teams think they have observability because they have logs. That’s not enough. When a production issue happens, I want to know: - Which endpoint degraded? - Which dependency is slow? - Which service is failing? - Which customer flow is impacted? - Where exactly the request broke? That means I need more than logs. I need: - metrics - tracing - health signals - correlation IDs - alerting In distributed systems, this becomes non-negotiable. Because once requests travel through: - API - service layer - DB - Kafka - external integrations - ... Debugging without observability becomes pure guesswork. A backend that cannot be observed Cannot be operated professionally. #Observability #OpenTelemetry #Micrometer #Java #SpringBoot #SRE #Backend
To view or add a comment, sign in
-
-
🚀 Java Streams: Sequential vs Parallel — When to use what? A simple concept, but often misunderstood 👇 🔹 Sequential Stream → Runs on a single thread (one CPU core) → Processes data step-by-step → Lower overhead → Best for: small datasets, simple operations 🔹 Parallel Stream → Uses multiple threads (ForkJoinPool) → Splits data across multiple CPU cores → Processes tasks concurrently → Best for: large datasets, CPU-intensive operations 💡 Key Insight: Parallel streams are NOT always faster. ⚠️ They introduce: - Thread management overhead - Context switching cost - Possible issues with shared mutable state ✔️ Use Parallel Stream when: - Data size is large - Task is CPU-bound - Operations are stateless & independent ❌ Avoid when: - Small datasets - I/O operations (DB calls, API calls) - Order matters strictly 💼 Real-world example: In one of my use cases, processing large collections (like aggregations/search results) using parallel streams improved performance — but only after ensuring operations were stateless and thread-safe. ⚡ Pro Tip: Always benchmark before switching to parallel — assumptions can be misleading. #Java #StreamAPI #Java8 #Performance #Backend #SoftwareEngineerin
To view or add a comment, sign in
-
-
HashMap Performance Bottlenecks in Production 1. Poor hashCode() Implementation → Heavy Collisions If many keys generate same hash bucket: Then many entries go into one bucket. Instead of: O(1) it becomes: O(n) Note: (Java 8+: linked list can treeify into red-black tree after threshold, improving to O(log n)) 2. Frequent Resizing / Rehashing HashMap default capacity: 16, load factor = 0.75 When threshold exceeded: capacity doubles, all entries rehashed This is expensive. e.g, inserting millions of records, sudden latency spikes Fix: Pre-size map. newHashMap<>(1_000_000); 3. Wrong Initial Capacity in High Traffic Systems Small capacity + many inserts = repeated expansions. Causes: CPU spikes, GC pressure, latency jitter 4. Mutable Keys Map<List<String>, String> map = new HashMap<>(); If key changes after insertion: hash changes bucket changes logically lookup fails Fix: Use immutable keys (String, Integer, UUID, immutable DTO) 5. Concurrency Misuse (HashMap in Multi-threaded Code) Using regular HashMap from many threads: HashMap sharedMap = new HashMap<>(); Can cause: race conditions, lost updates, inconsistent reads Fix: use ConcurrentHashMap
To view or add a comment, sign in
-
-
Over the past few months, our team has been facing a reality many engineering teams know well: frequent performance incidents, daily escalations, and growing technical debt in a backend that was never designed to handle heavy load. #Java #BackendDevelopment #SystemDesign #PerformanceEngineering #SQL #Scalability #SoftwareArchitecture #TechLeadership
To view or add a comment, sign in
-
#Post6 In the previous post, we understood how our code runs: Code → JVM → Process → Threads (https://lnkd.in/dns348v6) Now let’s go one step deeper What actually happens inside a process when it executes? When a Java program runs, the JVM creates a process. Inside that process, memory and execution are organized into different parts. 1. Heap Memory (Shared) This is where objects created using the "new" keyword are stored. • Shared by all threads within the same process • Not shared across different processes • Threads can read and modify data Because multiple threads access it → synchronization is required 2. Code Segment (Shared) Contains the bytecode (instructions to execute). • Read-only • Shared across all threads 3. Data Segment (Shared) Stores static and global variables. • Shared across all threads • Can be modified Synchronization is required when multiple threads update data 4. Stack (Thread-specific) Each thread has its own stack. • Stores method calls • Stores local variables • Not shared between threads 5. Program Counter (Thread-specific) Each thread has its own program counter. • Points to the current instruction being executed • Moves forward as execution progresses 6. Registers (Thread-specific) Each thread uses CPU registers to store temporary/intermediate data during execution. (We will explore how registers are used during context switching in upcoming posts) Important Understanding Inside a process: • Heap + Code + Data → Shared across threads • Stack + Program Counter + Registers → Private to each thread This separation is what makes multithreading both powerful and complex. Key takeaway Threads share memory (heap), but execute independently using their own stack and execution state. In the next post, we’ll explore Registers and how CPU switches between threads (context switching). #Java #SoftwareEngineering #Multithreading #BackendDevelopment #Programming
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
Thanks for sharing