The Java Memory Model - The Basics

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we will have a look at how Java threats access memory and how the memory is organized in side the java virtual machine with relation to threats to explain how memory is organized and accessed inside of the Java VM I will be using mostly diagrams but I also have a few code examples to help illustrate the points made in the diagrams if you look at the diagram here on the right you can see that it contains two threats each threat has a threat stack a threat stack is an as a memory area which can only be accessed by the threat that owns the thread stack so the left thread can only access the left thread stack and the right thread can only access the right thread stack and if there were more threats each threat would have its own thread stack then we have the heap down here at the bottom which can be accessed by all threats so in case you need to share data between threats it has to be shared via the heap you cannot share data between threats via the thread stacks on the thread stack all the local variables are stored for instance these two variables here are local because they are defined inside of a method they are local to this method they can only be accessed from inside of the main method so the my local via here the int is stored on the stack here and the reference to this string object here is also stored on the thread stack now the string object itself however is stored on the heap so that means that in order to represent this string variable here we both have a reference which is stored on the thread stack and we have the string object itself which is stored on the heap if two threats were to reference the same object on the heap they would do so using each their own reference so this left threat here would have its own reference here located on its own thread stack which would reference this object down here on the heap now in case this threat would like this reference here to reference another object for instance this one over here it would be free to choose to reference another object and this one or another one without affecting that the other threat reference here would still be referencing this old object here so the references can reference the same object but they can also be modified independently of each other to reference different objects before we move on let me quickly re-explain this but based on a code example instead so you can connect what I have told you in the diagrams to some actual code and if you look at the code example here on the Left I have created to run apples here and each runnable epi' is passed to a different thread and that means that each thread does have its own runnable object so over here on the heap there will be one runnable object for this thread and another vulnerable object for this thread over here now let's jump into the my runnable implementation so we can discuss a little bit the location of variables in here if we first look at this local variable here I then each thread will create its own copy of I because it's a local variable so this thread here will create an eye on its own thread stack and this thread over here will create an eye over here on its own thread state and that is true even if we have two different variables but it would also be true even if they share the same runnable I will show you later in a later example so local variables are never shared between threads okay however however look at this filled in the council that's a member field it belongs to the my runnable object so there will be one count field in each runnable and in this case where each thread gets his own runnable that means that each state has its own count field also right because each runnable has its own count field however if we look at this example here where we create only one runnable and we pass it to to two different threats then if we go back into the my runnable then all of a sudden what is now true is that the I variable here will still be created in two copies one in each thread stack because local variables are not shared between the threads but now all of a sudden the count field here that they are accessing because it's the same my runnable object they are accessing the two threads then all of a sudden it will be the same count field that they are accessing and that is because now there's only one my runnable object here on the heap both threads are accessing that and that means there's only a single count field and that field is then shared between there the threats if a thread creates an object for instance inside there their run method here then each threat that executes the run method will end up instantiating and new objects and each since each object or each threat will actually create its own local variable then each local variable here will also be assigned to a different object so that means that over here the my object local variable will be created one in this thread stack and one in this thread stack and they will point to one new myopia created for each thread because each thread executing this run method will also execute the new my object here so these instances will not be shared between the threads in this case here where we have even if we have two different runnable but also in case we have one runnable here like we have a share runnable right but it doesn't matter because each thread will execute this and will thus create two different objects if we try to run this code example we can see that this is true let me just try to print out there my object instance here and let me first run this example here where we have one my runnable so it's shared between the threads and you will see that even if we share the runnable the two threads still create different my object instances as you can see here the numbers here means that these two my object instances are different even if we share the same runnable right and the same would be don't mind these numbers I will explain them later in the video the same is true if I run this other example here where we are actually using two different run apples but you can see they also create two different my object instances because these numbers are different let me just show you how it would look if we had shared there my object instance between the two threads let's first modify this my runnable class so now it can take and my objects in the instance let's do like this it can take a myopic instance as a parameter to its constructor right and then instead of down here creating a new my object we simply print the field here and if we get and my object as parameter to the constructor we store that as a field and because it is a field now stored in a field then it can be shared between the threads right instead of here where it was a local variable so it was not shared between the threads let's have a look at how this looks now we also need to create and you new my object and we pass it now to to the constructor here now even if these two runnable are different objects so each thread has a different runnable the runnable internally reference the exact same my object instance and therefore when we run this example you will see that now they still print out the same number now you can see this number here is exactly the same number as down here because now they are referencing the same shared my object instance which is located on the heap right so we have actually two different run apples each of them has a my object field internally so another runnable here but both of these to run apples are referencing the same my object instance on the heap right the same my object instance on the heap and even in this case here with it will be the same we will create a my object instance here right and we pass it as parameter now obviously because we only have one runnable now which is yet between two threads and this round will only has one myopic instance day it will these threats will also now be sharing the same my object instance it is even more obvious right but let me just show you just so that you can see that it is true now look at these numbers here they are the same because these two threats that are started up now share the same runnable instance and the same my object instance now let's have a look at how this Java memory organization looks when we map it on to a hardware architecture and of course hardware architectures are different so this is a bit of a generic architecture that I'm showing you here you can see we have two CPUs and it could be CPU cores or it could be like real separate CPUs and there could be more than two now on this diagram I just have shown you two and as you can see the thread code runs on the CPU and here's another thread that runs on a different CPU in fact it is possible for multiple threads to run on the same CPU but it is better mentally to think about to imagine that the threads are running on different CPUs then each CPU has some registers here which is data storage air that can is local to the CPU and then it has we have an l1 l2 and l3 caches which are fast caches memory cases which are close to the CPU and then we have the big random access memory down here the biggest shared memory area here and in the RAM we have the fetch stack and the heap store so actually we have the thread stacks of both of these threads start down here in the RAM when a threat needs some data either from its threat stack or from the heap so either local variable or an object reference by a local variable or referenced from another object it has to access this variables or this these objects through the RAM now what the hardware architecture will do is it will copy this information this data to the caches here and then eventually possibly to the CPU registers so sometimes it will only end up in the cache here and it will be processed here and sometimes it goes all the way to the CPU registers this depends a little bit on the Java Virtual Machine and what instructions you are executing etc so but this process here is always the same the data has to be loaded from RAM into the caches and into the registers that is the direction of it and the same way when the CPU if it changes some of the data then the data has to go the same way back it has to go from the register to the cache and then back down to the RAM again this organization of memory between the CPUs and the cache is here and the RAM poses some problems that we need to take into consideration when designing while two threaded applications in Java and one of these problems is race conditions so race conditions occur when two threats share this share some data on the heap and they both read and write it and they do not do so in a synchronized fashion let me just explain how the problem can occur imagine we have this object here it has a field called count the value is 1 now thread 1 copies this value up into its CPU registers because it intends to increment the value by 1 so it will be 2 right and the second thread also wants to increment the value by 1 so it will be well if they did it sequentially it would be 3 right it would first be 2 after this threat have incremented it and written it back and then this thread over here would read the 2 and increment that to 3 and write that back however so they are copying the count field at exactly the same time up into the registers well they both get the value one up there now what happens is both of them updates this value to two and then write it back so as you can see one update is now missing and there are ways to solve that in Java I will not get too much into them here I will mention it a little bit later but you can use either synchronized blocks or you can use volatile variables and both of these two key words and their functions are covered in separate videos and separate tutorials but if you check out the description below the video you can find links to those another problem that can arise is data right visibility so for instance let's imagine that these two threats they actually access this object with some time in between so first threat one copies object that count this filled up into its register up here and then it increments the value by one so now the value is two now you are not guaranteed when this CPU here will write this value back to main memory so that it is visible for the next threat so even if the CPU is even if the threats are accessing this object sequentially so after each other not at the same time you can risk that this update up here has not yet been written back to main memory so it is not yet visible to the other threat this is a update visibility and you also use either volatile variables or synchronized blocks in order to to counter that problem to solve that problem so but again I will not cover synchronized and volatile in this video well now you know that they exist right and check out the description below the video for more information finally I just want to talk a little bit about how the caches here work sometimes when a CPU wants to write the information from a register down to the RAM it writes it through the cache here and some CPU architectures have a cashew coherence strategy implemented which means that instead of the data here having to go into the cache and then into the main memory before it is visible to other threats to other CPUs this cache over here when this threat here is requesting this object that count filled the cashier will detect that oh wait a second this is actually available in the cache over here so it can see the update already when it is in the cache here before it hits the main memory here so in that way rights can be seen earlier by other CPUs however remember that you do not have a guarantee of when I picked that count here will be written from the registers down to the caches so you cannot rely on data being written here or variables being changed that they are always visible to other CPUs even if you have cache coherence strategies implemented at the hardware level so to sum it up in order to be sure that you avoid race conditions and that rights are always visible to other threats then you need to use the synchronized blocks or use the volatile a key word for variables now let me just demonstrate quickly two situations which will show you this race condition and the visibility problem here in practice but first let me show you a code example where the problem is not present let me just remove these local variables because we don't need them anymore okay so here we make to run herbals and run apples can be executed by threats so I can also create two threats here threat 1 and threat to I pass one number one and runnable two to each threat and I give the threats a name threat 102 then we start the threats now the runnable that I'm passing here what it does is it counts to 1 million and it it does so by iterating through a loop 1 million times and for each iteration in the loop it increments it account variable which is a member variable as you can see up here so it's not a local variable it's a member variable and then when the loop is done it prints out what the final result of the count is and if we go back here and then we can see that since each thread has its own runnable they are not actually sharing any data at all and that means that we expect the count to be 1 million for each runnable so we expect two times 1 million to be printed out and now let's run this code example here and see what happens look at that we get 1 million here and 1 million here for each thread now let's see what happens if the two threads share the runnable ok now that is how it looks here now we only create one runnable here I create two threads and they get the same runnable that means they are executing the same code within the same object ok so that means that now two threats are executing this loop at the same time and at the same time and they are incrementing this same variable because we have 1 runnable in the application running which is shared between two threads so now if the counting was done exactly precisely synchronized then we would expect the result to be 2 million right because we have two threads which which each iterate through the loop 1 million times and for each iteration in the loop increments the count variable once however as you will see the end result is usually below two million let's run the code here and you can see each thread here got to like 1.3 million 169 right let's try to run it again see now it got a different result c32 got 109 900 1 million and 91 thousand and the other one got 1.9 million and that is because of the effect that I just told you about that the up that the threats are reading the value of the count variable and then updating them and then writing them back so instead of having a precise count they overwrite each other's accounts they cannot see that the one is currently one thread is currently in the process of updating the value so that's why the count will be imprecise if we wanted to fix that we could do so simply by wrapping this block and this the incrementation or the count variable in a synchronized block like this this is not a very pretty implementation it's not very efficient either but it shows that the problem now disappears now you can see threat to doesn't get so high account and that is okay but the last threat to finish the loop with 1 million iterations reaches the result 2 million and that is because now only one thread at a time can ever read increment and write the count variable at a time and that means that we are now avoiding the race conditions and we are also avoiding the right visibility problem that's all for this video about Java threads and how the access memory and how local variables and objects are stored inside of the java virtual machine and how that maps onto a modern cpu architecture if you liked the video hit the like button and if you want to see more videos like this subscribe to my channel remember to check out the description below the video for a link to a textual version of this tutorial as well as links to other videos in my Java concurrency playlist
Info
Channel: Jakob Jenkov
Views: 45,140
Rating: undefined out of 5
Keywords: Java, Java Memory Model, Java Concurrency, Java Multithreading
Id: LCSqZyjBwWA
Channel Id: undefined
Length: 23min 40sec (1420 seconds)
Published: Wed Jul 01 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.