Your SlideShare is downloading. ×
  • Like
  • Save
Java Memory Model
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Now you can save presentations on your phone or tablet

Available for both IPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Java Memory Model

  • 2,856 views
Published

Java Memory Model

Java Memory Model

Published in Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
2,856
On SlideShare
0
From Embeds
0
Number of Embeds
3

Actions

Shares
Downloads
0
Comments
0
Likes
16

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Java Memory Model The enigma of Java James Perry
  • 2. About Me Software Engineer at Barclays Capital http://www.natureinspiredcode.com http://github.com/natureinspired/8queen-ea g-graphs Twitter: @nature_inspired
  • 3. JMM in a Nutshell JMM is a universal contract for how threads interact with memory in Java programs. Happens-Before ordering: "official" reasoning for thread interaction. synchronized provides mutual exclusion and memory visibility. volatile provides atomic memory visibility.
  • 4. public class SleepyThreadDemo { private static boolean awake; public static void main(String[] args) throws InterruptedException { Thread sleepyThread = new Thread(new Runnable() { public void run() { int zCount = 0; while(!awake) { zCount++; } } }); sleepyThread.start(); TimeUnit.SECONDS.sleep(1); awake = true; } }
  • 5. public class SleepyThreadDemo { private static boolean awake; public static void main(String[] args) throws InterruptedException { Thread sleepyThread = new Thread(new Runnable() { public void run() { int zCount = 0; if (!awake) { while(true) { zCount++; } } } }); sleepyThread.start(); TimeUnit.SECONDS.sleep(1); awake = true; }
  • 6. Causality Cause Symmetric Multiprocessing (SMP) Memory Ordering Memory Barriers Compiler Optimisation Effect History Objectives Happens-Before Ordering Concurrency Constructs
  • 7. Symmetrical Multiprocessing Memory Contention Cache Coherency
  • 8. Symmetrical Multiprocessing T1 T2 Tn
  • 9. Symmetrical Multiprocessing foo = 42 foo = 1 foo = 12 public static int foo = 1
  • 10. Symmetrical Multiprocessing foo = 42 foo = 42 foo = 42 public static int foo = 42
  • 11. Memory Ordering 1. CPU 0 executes a=1. int a = 1; 2. CPU 0 looks “a” up in the cache, and finds int b = a + 1; that it is missing. assert(b == 2) 3. CPU 0 therefore sends a “read invalidate” mes- sage in order to get exclusive ownership of the cache line containing “a”. 4. CPU 0 records the store to “a” in its store buffer. 5. CPU 1 receives the “read invalidate” message, and responds by transmitting the cache line and removing that cacheline from its cache.
  • 12. Memory Ordering 6. CPU 0 starts executing the b=a+1. int a = 1; 7. CPU 0 receives the cache line from CPU 1, int b = a + 1; which still has a value of zero for “a”. assert(b == 2) 8. CPU 0 loads “a” from its cache, finding the value zero. 9. CPU 0 applies the entry from its store queue to the newly arrived cache line, setting the value of “a” in its cache to one. 10. CPU 0 adds one to the value zero loaded for “a” above, and stores it into the cache line containing “b” (which we will assume is already owned by CPU 0). 11. CPU 0 executes assert(b==2), which fails.
  • 13. Memory Ordering 1. CPU 0 executes a=1. The cache line is not in CPU 0’s cache, so CPU 0 places the new value of “a” in its store @Singleton buffer and transmits a “read invalidate” class FooBar { message. 2. CPU 1 executes while(b==0)continue, private int a, b; but the cache line containing “b” is not in its cache. It therefore transmits a “read” message. public void foo() { 3. CPU 0 executes b=1. It already owns a = 1; this cache line, so it stores the new b = 1; value of “b” in its cache line. } 4. CPU 0 receives the “read” message, and transmits the cache line public void bar() { containing the now-updated value of while(b==0) continue; “b” to CPU 1, also marking the line as assert(a==1); “shared” in its own cache. } }
  • 14. Memory Ordering 5. CPU 1 receives the cache line containing “b” and installs it in its cache. 6. CPU 1 can now finish executing while (b==0) continue, and since it finds that @Singleton the value of “b” is 1, it proceeds to the class FooBar { next statement. 7. CPU 1 executes the assert(a==1), private int a, b; and, since CPU 1 is working with the old value of “a”, this assertion fails. public void foo() { 8. CPU 1 receives the “read invalidate” a = 1; message, and transmits the cache line containing “a” to CPU 0 and invalidates b = 1; this cache line from its own cache. But it } is too late. 9. CPU 0 receives the cache line public void bar() { containing “a” and applies the buffered while(b==0) continue; store just in time to fall victim to CPU 1’s assert(a==1); failed assertion. } }
  • 15. 1. CPU 0 executes a=1. The cache line is not in Memory Ordering CPU 0’s cache, so CPU 0 places the new value of “a” in its store buffer and transmits a “read invalidate” message. @Singleton 2. CPU 1 executes while(b==0)continue, but class FooBar { the cache line containing “b” is not in its cache. It therefore transmits a “read” message. private int a, b; 3. CPU 0 executes fence(), and marks all current store-buffer entries (namely, the a=1). public void foo() { 4. CPU 0 executes b=1. It already owns this a = 1; cache line, but there is a marked entry in the fence(); store buffer. Therefore, rather than store the new value of “b” in the cache line, it instead b = 1; places it in the store buffer (but in an } unmarked entry). 5. CPU 0 receives the “read” message, and trans- mits the cache line containing the public void bar() { original value of “b” to CPU 1. It also marks while(b==0) continue; its own copy of this cache line as “shared”. assert(a==1); 6. CPU 1 receives the cache line containing “b” and installs it in its cache. } 7. CPU 1 can now finish executing while(b==0) continue, but since it finds that the value of } 'b' i still 0, it repeats the while statement. The new value of 'b' is safely hidden in CPU 0's store buffer.
  • 16. Memory Fencing 8. CPU 1 receives the “read invalidate” message, and transmits the cache line containing 'a' to CPU 0 and invalidates this @Singleton cache line from its own cache. class FooBar { 9. CPU 0 receives the cache line containing 'a' and applies the buffered store. 10. Since the store to 'a' was the only entry in private int a, b; the store buffer that was marked by fence(), CPU 0 can also store the new value 'b' - expect for the fact that the cache line public void foo() { containing 'b' is now in 'shared' state. a = 1; 11. CPU 0 therefore sends an 'invalidate fence(); message to CPU 1'. 12. CPU 1 receives the 'invalidate' message, b = 1; invalidates the cache line containing 'b' from } its cache, and sends an 'acknowledgement' message to CPU 0. 13. CPU 1 executes while(b==0)continue, public void bar() { but the cache line containing 'b' is not in while(b==0) continue; cache. It therefore transmits a 'read' message assert(a==1); to CPU 0. } }
  • 17. Memory Fencing 14. CPU 0 receives the 'acknowledgement' message, and puts the cache line containing 'b' @Singleton into the 'exclusive' state. CPU 0 now stores the new value of 'b' into the cache line. class FooBar { 15. CPU 0 receives the 'read' message, and transmits the cache line containing the original private int a, b; value of 'b' to CPU 1. It also marks its own copy of this cache as 'shared'. 16. CPU 1 receives the cache line containing 'b' public void foo() { and installs it in its cache. a = 1; 17. CPU 1 can now finish executing while (b==0)continue and since it finds that the value fence(); of 'b' is 1, it proceeds to the next statement. b = 1; 18. CPU 1 executes the assert(a==1) but the } cache line containing 'a' is no longer in its cache. Once it gets this cache from CPU 0, it will be working with the up-to-date value of 'a', public void bar() { thus assertion passes. while(b==0) continue; assert(a==1); } }
  • 18. Compiler Optimisations Loop Fusion int i = 0, size = 100; int[] a = new int[size], b = new int[size]; for (i = 0; i < size; i++) { a[i] = 1; } for (i = 0; i < size; i++) { b[i] = 2; }
  • 19. Compiler Optimisations Loop Fusion int i = 0, size = 100; int[] a = new int[size], b = new int[size]; for (i = 0; i < size; i++) { a[i] = 1; b[i] = 2; }
  • 20. Compiler Optimisations Loop Fission int i = 0, size = 100; int[] a = new int[size], b = new int[size]; for (i = 0; i < size; i++) { a[i] = 1; b[i] = 2; }
  • 21. Compiler Optimisations Loop Fission int i = 0, size = 100; int[] a = new int[size], b = new int[size]; for (i = 0; i < size; i++) { a[i] = 1; } for (i = 0; i < size; i++) { b[i] = 2; }
  • 22. Compiler Optimisations Lock Coarsening BigDecimal total = account.getTotal(); synchronized(account) { BigDecimal newTotal = total.multiply(INTEREST_RATE); account.setTotal(newTotal); }
  • 23. Compiler Optimisations Lock Coarsening synchronized(account) { BigDecimal total = account.getTotal(); BigDecimal newTotal = total.multiply(INTEREST_RATE); account.setTotal(newTotal); }
  • 24. Compiler Optimisations Lock Elision public static void main(String... args) { ConcurrentMap<String,Integer> map; map = new ConcurrentHashMap<String, Integer>(); map.put("one", Integer.valueOf(1)); map.put("two", Integer.valueOf(2)); map.put("three", Integer.valueOf(3)); }
  • 25. Compiler Optimisations Lock Elision public class Foo implements Runnable { public void run() { synchronized(new Object()) { //Do something } } }
  • 26. What is a Memory Model A model to describe thread interaction with memory. Multiprocessors have different memory models. Inconsistently weak Enables to instruct memory fencing. Helps to write date race free Preserving program order.
  • 27. Java Memory Model A Memory Model for consistent thread behaviour with memory for all CPU architectures Alpha RISC PowerPC x86 AMD64 Pioneering Influential
  • 28. Java Memory Model History Initially broken volatile was not very volatile final was not very final Developers originally worked around locks. Huge open research area Constantly finding new ways to improve the JMM. People looking to go towards different ways of reasoning.
  • 29. Java Memory Model Goals 1. Allow as many compiler and hardware optimisations as possible. 2. Provide balance between optimisations and correctly synchronised code. 3. Provide developers to write and reason about multithreaded code.
  • 30. Happens-Before Ordering
  • 31. Java's synchronized keyword public class BankAccount { private BigDecimal total = new BigDecimal(0); public BigDecimal withdraw(BigDecimal amount) { total = total.minus(amount); return total; } public BigDecimal deposit(BigDecimal amount) { total = total.add(amount); return total; } public BigDecimal total() { return total; } }
  • 32. Symmetrical Multiprocessing deposit(200) withdraw(100) total() == 0 T1 T2 T3 BankAccount account = new BankAccount();
  • 33. Java's synchronized keyword public class BankAccount { private BigDecimal total = new BigDecimal(0); public synchronized BigDecimal withdraw(BigDecimal amount) { total = total.minus(amount); return total; } public synchronized BigDecimal deposit(BigDecimal amount) { total = total.add(amount); return total; } public synchronized BigDecimal total() {return total;} }
  • 34. Symmetrical Multiprocessing deposit(200) withdraw(100) total() == 100 T1 T2 T3 BankAccount account = new BankAccount();
  • 35. Java's volatile keyword @Singleton class FooFactory { private Foo foo; public Foo getFoo() { if (foo == null) { synchronized(this) { if (foo == null) { foo = new Foo("bar"); } } } return foo;
  • 36. Java's volatile keyword class Foo { private String name; public Foo(String name) { setName(name); } public void setName(String name) { this.name = name; } public String getName() { return name; } }
  • 37. Symmetrical Multiprocessing new Foo() name == null name == "bar" foo = "bar"
  • 38. Java's volatile keyword @Singleton class FooFactory { private volatile Foo foo; public Foo getFoo() { if (foo == null) { synchronized(this) { if (foo == null) { foo = new Foo(); } } } return foo;
  • 39. Symmetrical Multiprocessing new Foo("bar") name == "bar" name == "bar" name == "bar"
  • 40. Java's volatile keyword @ThreadSafe class FooFactory { private static class FooHelper { public static Foo foo = new Foo(); } public static Foo getFoo() { return FooHelper.foo; } }
  • 41. Java's final keyword class MeaningOfLife { private final int answer; public MeaningOfLife(int answer){ this.answer = answer; } public int answer() { return this.answer; } }
  • 42. Final int answer = 42 int answer = 42 int answer = 42 MeaningOfLife life = new MeaningOfLife(42);
  • 43. Summary Memory ordering can change the program order of multithreaded code. Memory barriers inhibits optimisations for correct synchronisation. Java Concurrency Constructs: ensures mutual exclusion and memory visibility Happens-Before ordering: any subsequent lock acquisitions will see changes made by previous lock releases.