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.
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.
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.
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.
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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
