Master Java Backend: ThreadLocal, Memory Leaks, Spring Bean Lifecycle & AOP

This guide covers evaluating engineer hourly wages, interview self‑introduction tips, local vs distributed cache choices, ThreadLocal usage, memory‑leak detection with MAT, deadlock troubleshooting, Spring Bean lifecycle, AOP implementation, and Redis‑based distributed locks, plus resources for Spring Boot projects.

macrozheng
macrozheng
macrozheng
Master Java Backend: ThreadLocal, Memory Leaks, Spring Bean Lifecycle & AOP

This article provides a comprehensive technical guide for Java backend engineers, covering salary evaluation, interview preparation, caching strategies, concurrency utilities, debugging techniques, Spring framework internals, and distributed locking.

Hourly Wage Ranking

When choosing a company, total compensation (salary, bonuses, stock) is often emphasized, but hourly wage is a more accurate measure of work efficiency. A site that calculates hourly wages from salary and working hours offers a reliable reference.

2025 junior engineer hourly wage ranking (illustrated in the image below) shows that PDD, Xiaohongshu, and Microsoft remain top three, with Tencent entering the list and Bilibili rising to fifth place.

Hourly wage ranking 2025
Hourly wage ranking 2025

Self‑Introduction Tips for Interviews

Personal suggestion: interviewers rarely ask about the full Spring Bean lifecycle.

A concise self‑introduction (1‑2 minutes) should highlight:

Briefly state your main tech stack, e.g., Java backend development, distributed systems.

Emphasize strengths with a concrete example (e.g., solving a performance bottleneck).

Briefly mention 1‑2 projects or competition results that match the role.

If time permits, express enthusiasm for the position and the company.

Local Cache vs Distributed Cache

Local Cache stores data in the application process memory.

Implementation: ConcurrentHashMap, Guava Cache, Caffeine (recommended), Ehcache.

Advantages: Extremely fast access, no network overhead, simple implementation.

Disadvantages: Limited by single‑machine memory, no data sharing across instances, data loss on restart.

Suitable Scenarios: Low‑frequency changes, small‑size hot data such as configuration, dictionaries, permission lists; single‑machine environments.

Distributed Cache stores data in an external cache cluster accessed over the network.

Implementation: Redis (recommended), Memcached, KeyDB, Dragonfly.

Advantages: Horizontally scalable, shared across all instances, survives application restarts.

Disadvantages: Network latency, additional operational complexity.

Suitable Scenarios: High read/write, large data volume such as user profiles, product details, sessions; distributed environments requiring data consistency.

ThreadLocal Usage and Precautions

The core idea of ThreadLocal is to use space to save time by giving each thread its own variable copy, thus eliminating contention.

Typical use cases:

Passing contextual information (e.g., TraceID, authentication data) through a request call chain without modifying method signatures.

Managing thread‑exclusive resources such as a database connection during a transaction.

Important precautions:

Always call remove() after use to avoid memory leaks, especially in thread pools.

Wrap usage in a try‑finally block to guarantee removal even on exceptions.

In asynchronous scenarios, ThreadLocal values are not automatically propagated. Use InheritableThreadLocal for simple parent‑child thread inheritance, or TransmittableThreadLocal (TTL) for thread‑pool environments.

Memory Leak Diagnosis

Tools such as MAT, JVisualVM, jmap, and jcmd can generate heap dumps to locate OutOfMemoryError causes.

MAT’s “Leak Suspects” report identifies the most suspicious objects. If unclear, switch to the Dominator Tree view to see which objects dominate memory usage.

Example code that deliberately creates a memory leak:

import java.util.ArrayList;
import java.util.List;

public class SimpleLeak {
    // Static collection lives as long as the application
    public static List<byte[]> staticList = new ArrayList<>();

    public void leakMethod() {
        // Add a 1MB byte array each call
        staticList.add(new byte[1024 * 1024]);
    }

    public static void main(String[] args) throws InterruptedException {
        SimpleLeak leak = new SimpleLeak();
        System.out.println("Starting leak simulation...");
        for (int i = 0; i < 200; i++) {
            leak.leakMethod();
            System.out.println("Added " + (i + 1) + " MB to the list.");
            Thread.sleep(200);
        }
        System.out.println("Leak simulation finished. Keeping process alive for Heap Dump.");
        Thread.sleep(Long.MAX_VALUE);
    }
}

Running with a small heap (e.g.,

-Xmx128m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=simple_leak.hprof

) quickly triggers an OutOfMemoryError and generates a heap dump for analysis.

Deadlock Investigation

Use jmap and jstack to view thread stacks; a deadlock is indicated by the line “Found one Java‑level deadlock:” in the jstack output. System‑level tools like top, df, and free help assess resource consumption.

Visual tools such as VisualVM, JConsole, or the JConsole UI (shown below) can also detect deadlocks.

JConsole deadlock detection
JConsole deadlock detection

Spring Bean Lifecycle

The lifecycle consists of four main phases: instantiation → property population → initialization → destruction.

Instantiate Bean: Spring creates the bean instance via reflection.

Populate Properties: Dependency injection occurs (e.g., @Autowired, @Value, setters).

Initialize Bean:

If the bean implements BeanNameAware, setBeanName() is called.

If it implements BeanClassLoaderAware, setBeanClassLoader() is called.

If it implements BeanFactoryAware, setBeanFactory() is called. BeanPostProcessor methods postProcessBeforeInitialization() and postProcessAfterInitialization() are invoked.

If the bean implements InitializingBean, afterPropertiesSet() runs.

Custom init-method is executed if defined.

Destroy Bean:

If the bean implements DisposableBean, destroy() is called.

Custom destroy-method or @PreDestroy methods are invoked.

The core method AbstractAutowireCapableBeanFactory.doCreateBean() orchestrates these steps.

Spring AOP Implementation

Spring AOP relies on dynamic proxies. If a bean implements an interface, Spring uses JDK dynamic proxies; otherwise, it falls back to CGLIB to create a subclass proxy.

Spring AOP process
Spring AOP process

Typical AOP use cases include logging, performance monitoring, transaction management ( @Transactional), permission checks ( @PreAuthorize), rate limiting, and cache management.

Distributed Lock with Redis

Redis is a common choice for distributed locking. Redisson provides a high‑level API with an automatic “watch‑dog” mechanism that extends lock expiration while the owning thread is still active.

Redisson watch dog
Redisson watch dog

Additional Resources

For hands‑on practice, the open‑source mall (SpringBoot3 + Vue) and mall‑swarm projects are recommended. They include complete e‑commerce functionality and demonstrate microservice architecture, Docker/K8s deployment, and the technologies discussed above.

Project demo GIF
Project demo GIF
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javaaopcachingmemory leakThreadLocal
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.