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
xand Thread B modifies variabley, but bothxandyhappen 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
ThreadLocalvariables
5.3. Use Existing Concurrency Tools
- Use
LongAdder,AtomicLong, and other java.util.concurrent classes. These are often implemented with padding internally. LongAdderin 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.
