1. Introduction
In modern multi-threaded Java applications, performance issues may arise from subtle low-level hardware behavior. One such issue is false sharing — a CPU cache-related performance bottleneck. This tutorial explores what false sharing is, how it impacts Java concurrency, and how Java handles or mitigates it, especially through memory padding and Java 8+ features.
2.What Is False Sharing?
False sharing occurs when multiple threads modify variables that reside on the same cache line, even though the variables are logically unrelated.
1.1. CPU Cache Lines
- Modern CPUs use cache lines (usually 64 bytes) to load memory.
- When a variable is accessed, its entire cache line is loaded into the CPU cache.
1.2. Problem in Multithreading
- If Thread A modifies variable
x
and Thread B modifies variabley
, but bothx
andy
happen to reside on the same cache line, they will invalidate each other’s cache line, even though they don’t share data logically. - This causes excessive cache invalidation traffic, degrading performance.
3. How to Detect False Sharing in Java
There’s no compile-time error for false sharing. It shows up as poor scalability and performance in multithreaded code. It’s common in:
- High-performance concurrent systems
- Frequently updated variables in loops
- Shared counters, flags, buffers
To test for false sharing:
- Use performance benchmarks (e.g., JMH)
- Use profiling tools or JVM options (e.g.,
-XX:+PrintCompilation
)
4. Example of False Sharing in Java
public class FalseSharingExample { public static class SharedData { public volatile long value = 0L; } static final int NUM_THREADS = 4; static final long ITERATIONS = 1_000_000_000L; public static void main(String[] args) throws InterruptedException { SharedData[] data = new SharedData[NUM_THREADS]; for (int i = 0; i < data.length; i++) { data[i] = new SharedData(); } Thread[] threads = new Thread[NUM_THREADS]; for (int i = 0; i < NUM_THREADS; i++) { final int index = i; threads[i] = new Thread(() -> { for (long j = 0; j < ITERATIONS; j++) { data[index].value = j; } }); threads[i].start(); } for (Thread t : threads) { t.join(); } } }
Problem: All SharedData
objects may reside close in memory (within the same cache lines), causing false sharing.
5. How Java Handles or Avoids False Sharing
Java does not automatically prevent false sharing, but it provides mechanisms that allow developers to mitigate it manually.
4.1. Memory Padding
Memory padding adds unused fields to push frequently updated variables onto separate cache lines.
Manual Padding (Pre-Java 8)
public class PaddedData { public volatile long value = 0L; // Padding variables to separate 'value' from other variables public long p1, p2, p3, p4, p5, p6, p7; }
This helps ensure that each PaddedData
instance occupies its own cache line.
Downsides
- Manual and error-prone
- Not maintainable
- JVM may rearrange fields, defeating padding
4.2. Java 8: @Contended
Annotation (Best Practice)
Starting from Java 8, the @sun.misc.Contended
annotation was introduced to prevent false sharing in a cleaner way.
import sun.misc.Contended; public class SafeData { @Contended public volatile long value; }
Requirements:
- Use JVM flag:
-XX:-RestrictContended
- The annotation works only if:
- Placed on a field or class
- Field is volatile or frequently accessed
- JVM respects it only if enabled explicitly
Example with @Contended
:
import sun.misc.Contended; public class ContendedExample { @Contended public volatile long value = 0L; }
Add this to your VM Options
:
-XX:-RestrictContended
Internally, JVM inserts memory padding around value
to isolate it from adjacent fields or instances, thus reducing cache invalidation.
4.3. Java 9+: jdk.internal.vm.annotation.Contended
In Java 9+, @Contended
moved to the jdk.internal.vm.annotation
package. However, it’s still not part of the public API and requires --add-exports
in Java modules.
5. Best Practices to Avoid False Sharing in Java
5.1. Use @Contended
in Hot Fields
Use the @Contended
annotation for fields that are:
- Accessed and modified frequently
- Accessed by multiple threads in tight loops
Enable it using -XX:-RestrictContended
.
5.2. Avoid Array-Based Sharing of Hot Variables
Be cautious when using array elements to store thread-specific values (e.g., counters). Elements are often packed closely in memory.
Instead, use:
- Separate instances of classes
ThreadLocal
variables
5.3. Use Existing Concurrency Tools
- Use
LongAdder
,AtomicLong
, and other java.util.concurrent classes. These are often implemented with padding internally. LongAdder
in Java 8+ was designed with false sharing in mind.
6. JVM Internals and False Sharing
6.1. JVM Layout of Objects
The JVM may rearrange object fields unless they’re marked as volatile
or affected by @Contended
. Padding using empty variables is not guaranteed unless you use @Contended
.
6.2. Unsafe Techniques
Advanced developers sometimes use sun.misc.Unsafe
to allocate memory manually and simulate padding — but this is not recommended for most users due to portability and maintainability issues.