 Welcome to part three of the lecture on runtime environments. Today, we will continue with our discussion on static scope, dynamic scope, their implementation, passing of functions as parameters etcetera. To do a bit of recap on static and dynamic scope, so static scope well let me go to the example directly. Static scope implies that we consider the global variable as you know dictated by the program text. For example, in this program we have x equal to 1, y equal to 0 here as global variables and there are occurrences of x here, there are occurrences of here x here. So, when we really call f of 3, the control comes to this function and the activation records would have been set up in this fashion. So, this is the main program activation record, then this is the activation record for the invocation of function f, so within this there is a local variable x. So, there is no ambiguity, this x and this x both refer to the local variable x and it calls g. So, when you go to g there is no x here, so the global variable x is used as the variable that of concern when we consider static scope. So, the value of the global variable is 1 and that is what is used in the value to be returned by g. Dynamic scope says you know do not bother about the textual position of the variables, just consider the activation record the most recent activation record in which the name occurs. So, in the same example we have f you know y equal to f of 3, so the control comes here and within f we refer to the local variable x. So, there is no change as far as the local variable is concerned and then it calls g, within g we have this occurrence of x. So, as per the rule in dynamic scoping we consider the activation record for g, there is not any instance of x here, we go to the previous activation record that is the this f and here there is an occurrence of x. So, even though x is a local variable within f according to the rules of dynamic scoping this is the variable which is used as the value as the variable under consideration. So, x plus z will be returned with a different value here. So, this is dynamic scoping there is another example here as well here is a global variable r. So, small has a local variable r whereas, show does not have any local variable declaration. So, when we print this r is it this r or is it this r that is the question. So, depending on the scoping rules in static scoping it is always this r which is printed from show whereas, under dynamic scoping since show is called from within small it is this r which will be used just like in the previous example. Let us now consider the implementation of dynamic scope static scope implementation we discussed it in great detail in the previous lectures. So, using you know the dynamic link and the static link. So, here for implementing dynamic scope we do not require any static link at all dynamic link itself works as static link. The reason is there is no sanctity as far as the scope rules you know in dynamic scoping the most recent activation record which contains the instance and instance of the variable that is that we are accessing right now is all that matters. So, activation records are searched on the stack to find the first activation record containing the non-local name of our interest the depth of such a search cannot be fixed at compile time it really depends on the input to the program. And of course, we need to maintain some information on the identifiers within the activation record I will show you within a minute why this will be required. And obviously, since we are going to search the stack of activation records the time required to access global variables is much more than the time required to access local variables, but there is absolutely no overhead as far as the activation begin and end is concerned. So, let us look at this example right. So, here is the main, main calls R, R calls Q and Q again calls R in a recursive fashion. And within R there is no you know we are considering the variable x there is no declaration of x here. So, what we really do is start from this activation record and search this stack of activation records to check whether there is an instance of x declared or not. So, Q if Q has the variable x declared in it then this is the activation record in which x will be considered of course, R cannot have x because otherwise this instance would have had x on its own, but we need to continue searching for this activation record if necessary to the main program as well. And if it is found here then that is the x that we require. So, the sequence of calls really matters depending on the length of the sequence of calls the time required to search for this global variable will also change. And that is the reason why the time to access a global variable will also depend on the sequence of calls that we have made. So, this is the deep access method and the reason why it is called deep access is that it goes deep into the activation record. Now, coming to the point that I made here in the previous slide some information on the identifiers need to be maintained at run time within the activation record. So, just look at these this particular example we need to check whether the activation record for q contains the variable x or the activation record for main contains the variable x and so on and so forth. How do we search if we do not you know for such information if we do not you know store it already. So, it is not necessary to store the entire factor string of the variable it is sufficient to you know code it in some way and store that particular coding within the activation record. But, however we definitely want that code to be unique for each name and it should be easy to search as well there is yet another method for implementing the dynamic scoping. So, this is called as shallow access method. So, in let me show you a picture and then come back to this text. So, there is a stack of activation records as usual and we do not require any static you know link here just the dynamic link will be fine. The activation record does not provide for you know any variable programmer defined variable storage at all. It provides space for temporaries and also some space for saving variables from what is known as the static storage. Then where are the variables that the programmer has declared in the procedures stored they are actually stored in a unique static you know in a static area. There is exactly one storage you know certain one fixed amount of storage for each unique name and for the there may be you know the same name may be declared in various procedures with different types. So, the storage requirement for the name in different procedures may be different. So, how do we determine the storage that is required for the name we just take the maximum storage based on the various types for the names. So, this is the storage that we are talking about allocate maximum storage needed for each name. So, in a static area now when a new activation record is created for a procedure the local variable n in the procedure p now takes over the static storage allocated to the name n. So, here for example, let us say r has the variable x declared. So, the variable x is provided space in this static storage. So, exactly one storage space for each name right, but there could have been another x within the same storage right. So, what we really do is this is a local variable. So, we use the you we actually store the value of x that is available here in the storage already in the activation record for this instance of r and now say that from now on the space meant for x will belong to the local variable x in the procedure r. Suppose, r did not have any variable x, but it accessed it then the storage that was already occupied by some other instance of x is already available here. So, that is the storage which is accessed the advantage of this is every name has a unique address you know and it is static right. If there is no need to use a stack pointer to access the variable local variables or global variables in this particular scheme. So, all variables, but not the temporaries of course, temporaries are located space on the activation record itself accesses use static addresses those this is a very fast mechanism. Of course, the previous value of n held in the static storage is saved in the activation record and it has to be restored when the activation of p ends. So, this store and restore you know from here I need to store something here and then restore it to this later. So, sorry we have to store from something from here to here and then restore it to back to the original place when the procedure r ends. So, this is an overhead in this particular scheme whereas, in the other scheme there was no overhead of this kind. So, direct and quick access to globals is possible. So, because everything is static addressing scheme, but some overhead is incurred when activations begin and end. So, this is the disadvantage of shallow access method. Now, let us continue our discussion on implementation of you know various types of mechanisms in programming languages. The next topic which is bit complicated is the passing of functions as parameters to other functions or procedures. What exactly do we mean? So, let us go through a few points before we discuss this aspect. A programming language is said to have first class functions if we can declare functions within any scope. For example, Pascal allows declaring functions within any other function and so on whereas, C does not and then if functions can be passed as arguments to other functions and they can be returned as results of other functions then it is called as a first class function. So, there is a difference between the function parameter in C and in languages in functional languages and so on and so forth. The problem is in C the static function pointer that is used is only the address of the code that is corresponding to that is associated with that particular function whereas, what we really mean with a by a first class function is slightly different it will become clear as we go on. The other programming language which now has some sort of functions as parameters and so is the C plus plus 11 which is the C plus plus 11 which is the most recent upgrade to the language C plus plus. So, I will not deal with the details of such a mechanism C plus plus 11 here, but it suffices to say that the lambda facility or lambda functions in C plus plus 11 are really first class functions. So, in a language with first class functions and let us assume that there is static scope a function value is generally represented by what is known as a closure. So, what exactly is a closure a closure is a pair. So, it has a pointer to the function code and it also has a pointer to a suitable activation record why do we have to you know be familiar why should we be familiar with such facilities. See the such facilities are very useful when we actually use callbacks and java does not have such a facility you know it has a very limited facility for callbacks and things like that, but the research in programming languages is actually going to introduce you know such features into the future programming languages C plus plus 11 lambda functions are already there. So, they will be improved and revised in the future editions of C plus plus. Let us understand the this particular program this is a you know let us assume that there is a static scoping here. So, there is an integer x with initialization as 4 inside the the main program we have defined another function f which takes one integer parameter and returns x star y. So, this x is always bound to this x as far as static scoping rules are concerned there is another function g which is defined within main. So, observe that functions being defined in other functions is a necessary feature of first class functions that is why we are defining the function g inside main g takes a parameter which is special it takes a function as a parameter. So, h is a function which is the parameter to this function g and what is the type of h it is int to int implying h will take one integer parameter and produce a result which is of integer type. So, the body of g says a variable x which is initialized to 7 and then there is a call to h with a parameter 3. So, this is compatible with the declaration here because it takes a value as int as parameter and produces result int as the output h 3 plus x. The question now is when we try to invoke h what would be the function code corresponding to it and number 2 what are the variables which it can access this is the question. Here is a call to g with f as the function parameter. So, obviously h corresponds to f here when g is called by this main program. So, if we say f of 3 it would be implying that we call this function f with 3. So, in this case what is the set of variables that f can use if you use static scoping then this f can use this x as well. And if it is dynamic scoping obviously it would use this x therefore, it is necessary to you know provide when we call f call g with a parameter f. We will also have to say what are the activation records corresponding to the previous activations in this case it is just main that are relevant to this particular call. So, those are also associated with f. So, let me show you an example here. So, to begin with it is main the activation record for main with x equal to 4 then we have the activation record for g and within g we have a static link which correctly points to main according to the scope rules because g can access all the variables of main as well. And there is a parameter to g. So, that is the f part when we actually store the parameter within the activation record of g there is the parameter has two parts one is a pointer to the code for f and the other is a pointer to main. So, the reason for this is that when we consider f here the only activation record which is relevant to f is that of main because just for the sake of argument assume that this is a call. If it is a call then you know for this function f the only activation record which is relevant is that of main and the only variable global variable which is relevant is that of is x. So, therefore, this activation record really points this pointer points to main. So, this is the you know closure for the parameter h and when we invoke h of 3 we call h with the parameter 3 it is really f which is called we create the you know activation record for f the only difference is everything else is done exactly the same way as before the static link for f is now copied from the closure of f in the caller. So, this value is copied here. So, that makes it copy you know point to main this also takes care of possibilities of you know any recursion and things of that kind. So, this is how parameters are passed as you know functions are passed as parameters. So, again just to reinforce what I told you know. So, when executing the call h 3 h is really f and 3 is the parameter y without passing a closure the activation record in the of the main program cannot be accessed and hence the value of x within f will not be 4. In the call g f f is passed as a closure and of course closure may contain a further information needed to set up the activation record such as space for variables and so on and so forth. So, when processing the call h 3 after setting up the activation record for h that is f the static link for the a r is set up using the a r pointer in the closure of f that has been passed to the call f. So, this is a very important and that make sure that we access the global variables from f appropriately. So, now we come to the next topic in runtime management that is the heap memory management. So, what exactly is a heap well we are not looking at the heap sort here you know. So, the heap that we consider here is used for allocating space for objects created at runtime for example, we create link list we create trees. So, and any other dynamic data structure of interest to the program. So, nodes you know in such dynamic data structures are all examples of you know the space which is allocated from the heap. So, dynamic memory allocation and deallocation actually is based on the requirements of the program. So, when we are creating a tree if we want to set up a particular node then we call an appropriate function say malloc or something like that get the storage from the heap then you know put the variable values into rather values into the various fields of that block and attach it to the existing tree that completes our you know tree manipulation. So, how do we actually do allocation and deallocation in programs for example, in C we have malloc and free. So, malloc is used to you know get you a chunk of storage from the heap and free is supposed to return the chunk of storage that is of no more use to the heap. Similarly, in C plus plus we have the equivalence new and delete and in java there is no deletion of storage the new function remains, but the unwanted storage is automatically claimed by the system through a process called garbage collection. So, we are going to study this all these aspects in this part of the lecture. In C and C plus plus the allocation, deallocation are completely manual the programmer is responsible for calling malloc, free, new and delete whereas, in java new has to be called by the programmer whereas, delete is not necessary you know it is not even provided. So, garbage collection takes care of claiming the free storage. So, we can say it is semi automatic allocation and deallocation in java whereas, in the languages such as LISP everything is done automatically by the runtime system. What is a memory manager? Obviously, the heap memory has to be managed by some part of our program. So, heap manager manages heap memory by implementing the mechanisms for allocation, deallocation you know both manual and automatic. In fact, it takes care of even garbage collection and things of that kind. What are the goals of a memory manager? So, space efficiency it must minimize what is known as fragmentation of the heap as you go on in the next few minutes it will become clear about you know the reason for fragmentation. So, fragmentation basically says the heap memory is not one contiguous block of memory, but it is actually pieces of memory put together then program efficiency programs have to run fast and efficiently. So, the memory manager must take advantage of locality of objects in memory and make the program run faster and then the memory manager itself should have less overhead otherwise allocation, deallocation become quite inefficient. Usually, heap is maintained as a doubly link list in some types of runtime systems sometimes it is also maintained as a bin of free memory chunks. So, we will discuss these in detail little later. So, let us assume that heap is one large contiguous block of memory to begin with. So, the program has just begun execution it has been given a certain amount of memory as heap and from which allocation and deallocation can be done. So, to begin with heap is one big block as the allocation request keep coming in chunks are cut out from this block and given to the program. So, far so good the blocks are still the remaining block of memory that is the heap is still a single contiguous block of memory, but the program may also deallocate you know it may remake three requests and two deallocations and then again another three request and another deallocation etcetera. The free chunks are returned to the heap and they can be reused again. So, these free chunks are called usually as holes and after a number of such allocations and deallocations it is quite understandable that memory becomes fragmented, because the allocations you know the allocated allocations and then the deallocations they need not be done in the same order. So, whatever is you know not useful to the program is released, but it is not necessary that we allocate 1 2 3 and then three blocks in that order and then deallocate to exactly in the reverse order 3 2 1 that will never happen. So, it is usually the allocations are in a particular sequence, but deallocations can be in a random sequence and that makes the you know heap a fragmented memory fragmented memory. So, after deallocation we try to coalesce contiguous blocks and make a bigger hole or bigger chunk. So, this is the this is what we try to do in order to remedy the situation. The question is why do we have to do all this you know the allocation can happen, but then when we deallocate it is necessary that we have as big a chunk as possible, because you know smaller chunks will three small chunks will never can never be allocated in the place of one big chunk. So, this is the problem. So, there are two strategies which are used the one of them is called the first fit strategy the other is called as the best fit allocation strategy. The first fit strategy picks the first available chunk that satisfies the allocation request. So, as the name suggests the doubly linked list is traverse from one end and the first block on this doubly linked list which has a size greater than what the programmer has requested satisfies the programmers request and therefore, the you know the first fit the allocator chops of a block of the required size from the chunk and returns it to the program. The rest of the chosen chunk of course, remains in the heap itself, but then what is the best fit strategy best fit strategy says do not pick the first one which satisfies which can satisfy the programmers request, but search the whole list and pick the smallest possible chunk that satisfies the allocation request. So, the advantage of doing that is the fragmentation is reduced we do not have to cut small blocks from large blocks and then you know make the larger block itself smaller whereas, if we pick the smallest block chunk which satisfies the allocation request and then chop off the required size from it we would possibly be reducing the fragmentation in the heap. So, obviously the best fit strategy looks better and in practice it has been shown that it reduces fragmentation better than the first fit strategy, but of course you can always produce theoretically sequence of allocations and deallocations such that best fit strategy fails, but first fit you know succeeds and vice versa. So, these are theoretical, but the in practice best fit is much better there is also another strategy known as the next fit strategy. So, what is next fit? So, suppose that a chunk has been used for allocation. So, a small piece of it has been cut and return to the programmer program the next allocation is done from this particular chunk itself. So, it tries to allocate the object in the chunk that has been split recently what is the advantage of doing it? Obviously, it tries to improve the speed of allocation we are not going to search and then you know allocate as in the case of first fit or best fit and it also tries to tends to improve the spatial locality because objects allocated at about the same time tend to have similar reference patterns and lifetimes. So, if the objects you know are actually cut out from the same storage you know heap then cache performance may become better because these will be cached together and since they may be accessed together the cache performance becomes better and the overall you know the speed of the program increases. That was about the you know doubly linked list approach to storing heaps rather managing heaps. So, there is also another form of storing the heap organizing the heap called as the bin based heap. So, free space is organized into bins according to their sizes. So, this has been implemented in GCC and it is usually called as the Lee memory manager because Lee is the person who invented this particular scheme. So, let me show you the organization of the bin based heap and then go back to the description. So, the this is the index all right. So, and each one of these indexes stores a pointer to a linked list all right there are number of linked list here. So, one part of the bin based heap stores what is known as exact bins or fixed size bins whereas, the second part of the bin based heap stores variable sized bins. The reason for this is there are there is a requirement for you know small objects because there are many more small objects than large objects. So, we have many more bins for smaller sizes and fewer bins for very large sizes. So, 1 to 64 all of them are fixed size or exact size bins. So, a bin for every multiple of 8 byte chunks from 16 bytes to 512 byte sizes. So, there is a bin for chunks of 8 byte size. So, all the chunks in this list. So, let us say this is a 16 byte size list. So, this is the index for the or the pointer to this linked list. So, each node on this linked list will be of 16 byte size here is a list for the 32 byte size. So, all nodes on this list will be of size 32. Then after these fixed size bins we have number of variable size bins and the size of these variable the chunks in the variable size bins is approximately double the previous size. So, for example, you know you can see that we start with 576 slowly increase the size and it goes to 2 to the power 31 here in 1 I know from 65 to 127 we have already risen from 576 to 2 to the power 31. So, the sizes have really increased exponentially with each small size bin chunks are all of the same size within each small size bin whereas, in the others they are ordered by the size. So, here in these they are all of the same size and whereas, from this 576 onwards these sizes are different, but all chunks here will be definitely less than the sizes in the next list. So, in this case this is 576 this is 640. So, all these are less than 640 in this case. So, and these are actually ordered by size here. So, this could be 576 this next one could be 590 and so on and so forth. The last chunk in the last bin is what is known as the wilderness chunk which gets us a chunk by going to the operating system. So, let me explain what happens here. So, this is the bin containing you know the wilderness chunk. So, the size is very large 2 to the power 31. So, extraordinarily large size that does not mean it has a list of such blocks available. This is what is known as the wilderness bin. So, what we really do is allocation happens by looking at the size that we require. If that maps to one of the fixed size bins we go there pull off one chunk from that bin and return it to the program. If that bin does not have any then we for example, we require let us say 24, 24 has a empty list attached to it. So, we go to 32 pull this chunk which is available of size 32, but we do not cut anything from it. We just return it to the program and when it comes back in size 32 we attached it to the same list here. So, if none of the exact size bins satisfy the programs request then we have no option, but to go for the variable size bins. So, we go to the appropriate variable size bin which satisfies the programs request then search you know the bin and the one which best fit in a best fit manner and since it is sorted already the first one itself will be the best fit. So, this is sorted in increasing size. So, the first one will be the best fit size we take this cut the required size from it return it to the program. Now, there is going to be a small piece which is left here this will be return to the appropriate bin. So, if it fits into one of these size bins then it is put there otherwise it will be actually put in one of these bins as possible and now the wilderness chain. So, let us say we come up to this point we did not find any of these bins you know having chunks. So, all of them are empty. So, in other words the program has really used up the entire heap. So, we hit the wilderness bin. So, which implies when we hit the wilderness bin the implication is that there is no more memory available within the program. So, we make a then an automatic call is made to the operating system to release more memory to the heap of this particular program. The once that arrives a chunk is cut off from that and the request of whatever size is made to the program to the operating system. The operating system will return a chunk of the required size are slightly larger and that will be return to the program. So, this is known as the call to the operating system which returns the chunk of the required size. So, that is about the Lee memory manager. Now, how do we manage you know the and coil is free space. So, as I said we should really combine adjacent chunks and try to reduce the fragmentation because many small chunks together cannot hold one large object. You know they are all different chunks they are not contiguous and the program requires the object memory to be allocated in one continuous or contiguous memory space. In the me Lee memory manager there is no coalescing in the exact size bins, but only in the sorted bins and how do we do such coalescing. We require extra information in the form of what are known as boundary tags and the which implies free and used bit and the chunk size. I will go I am going to show you an example which uses boundary tags at the end of each chunk and this is used for both free and used chunks. We maintain the linkless in a doubly linked fashion. So, here is an example chunk a is still in use chunk p has been released just now and sorry chunk a is free sorry chunk p has been free just now and chunk c is still occupied. What is shown here is the memory map starting from one end to the other. In other words the address of chunk p starts just after the address of chunk a. So, these two are actually contiguous since b has been released just now it is possible to combine these two chunks and make a bigger chunk. So, to do that what is it that we require we require the information about the usage of the chunk. So, 0 indicates it is a used chunk. So, we maintain a 0 at both ends of the chunk. Next we maintain the size of the chunk. So, this is 200. So, 200 is maintained at both ends of the chunk similarly this says this is 0 and this is 100. So, 100 and 0 this 1 indicates it is an occupied chunk the size is 120. Why should we maintain information about the chunk size and usage bit at both ends of the chunk. Well you know we are manipulating a doubly linked list for example, this goes to the previous free node and this goes to the next free node. Now, this has been released. So, this is yet to be filled these two are yet to be filled and suppose these were not contiguous as I have shown here you know they are not adjacent let us say. Actually this would have pointed to this and this would be pointing to the next node in the linked list. Now, these two are adjacent. So, there is no need to make two nodes out of it. We can combine these two make a single node of size 300. Actually in practice it is little more than 300 because the storage required used for this 200, 0, 0, 100 will also be available to the big chunk itself. So, we are going to modify these two this part of it will be merged. So, there is no link here there is nothing here as well this entire thing will be one chunk this will be 0 the size now will be 300 and this will be 0 and this will also be 300. So, this points to the previous chunk and whatever this was pointing to this is the next free chunk right that will be pointed to by this link now. So, this will point to the free chunk which this is pointing to. So, that makes this entire thing one big chunk of 300 size and it would be linked into the W linked list in the appropriate manner. So, this is how the merging takes place there is a comment here the merge chunk k b may have to be placed in a different bin if you are using a Lee memory manager because this has now you know increased to size 300. So, it must be put in possibly a different bin. So, if you look at this. So, each of these bins are of different size. So, if the merger happens here and the size goes beyond 576 it may have to be put into one of these bins as necessary. So, this is about the manual allocation and deallocation process what are the problems with such manual strategies it is possible that the memory leaks which occur cannot be detected. So, what is a memory leak failing to delete data that cannot be referenced. So, we have generated garbage which cannot be referenced anymore, but we forgot to delete it. So, the this memory leak implies this data you know these nodes cannot be used again they are not they have not been returned to the heap. So, they cannot be reused, but they just occupy memory and waste it is actually they waste the memory space available to the use by the program. And this becomes very important in running non stop programs or programs which run for a very long time. The reason is suppose a small amount of memory is leaked in a loop right. So, in every iteration of the loop if there are millions and millions of iteration a little bit of memory is not freed it becomes garbage. And in after completing the entire set of a million or more runs it is possible that the unused you know the data space which was leaked becomes quite large. And the program runs out of memory even though the unused you know the memory is within the program and has not been returned to the heap. The second problem with manual allocation is it is almost impossible to detect this dangling pointer dereferencing. So, what we have done is in this case we deleted the we did not delete the data, but we simply you know through away the node and never bother to return it to the heap. Whereas, here we have deleted the data we have returned the you know node to the heap, but some other part of the program is still referencing this deleted data area. This is known as a dangling pointer dereferencing and in such a case since referencing deleted data is illegal and whatever we get there may be some illegal data the program may crash. Both these problems are very serious and very hard to debug a solution rather a partial solution is automatic garbage collection. So, what exactly is garbage collection? Garbage collection is reclamation of chunks of storage holding objects that can no longer be accessed by the program. So, there is no deletion process for example, in java we only allocate, but we do not deallocate, but definitely after sometime the program has no use for certain number of the nodes and these objects you know will have to be collected by the so called garbage collector and return to the heap. So, the garbage collector should be able to you know determine the types of objects the reason being if it does not know the type then the size of the object cannot be determined. So, then the size and pointer fields of the object can be determined by the GC. Why should it determine the pointer fields of the object? Why not just the size? The problem is when an object is of no use any more all the data which is pointed to by the pointer fields within the object will also become of no use you know they will also be useless. So, it is necessary to know which are the pointer fields within the object. So, all this can be done if the type of the object is known to us you know we will know which field is in which field is pointer etcetera. Languages in which types of objects can be determined at compile time or runtime are said to be type safe. So, it is possible that in some cases we cannot determine the type of the object at compile time it can only be done at runtime. For example, in the case of java is type safe when we try to make a virtual method call the type of object cannot be determined at compile time it will have to be determined only at runtime and based on the type of that object the appropriate method will have to be called. So, this is a case where the type of the object is determined at runtime. C and C plus plus are not at all type safe they permit type casting. So, we can create new pointers. So, now suddenly what was looking as a floating point number can suddenly become character or vice versa. So, any memory location theoretically can be accessed at any time because if a floating point number suddenly becomes a pointer we do not know what it is going to point to. So, if there is a error in the program then you know the program may crash. So, it and therefore, any part of the program can be accessed by these pointers. In fact, in some cases there are examples which we can create to show that even the program code itself can be kind of over written by such new pointers. Then having said that we require you know garbage collection and we require type information and so on. Let us see what exactly is done at garbage collection time. There is the concept of what is known as a root set the root set is all the data that can be accessed or reach directly by a program without having to dereference any pointer. So, in other words let us say you know we just take C for that matter even though it does not have automatic garbage collection. We declare many pointers in C right. So, the names of these pointers are at the first level and they are declared by the programmer himself or herself. We can say that the set of all such program programmer defined pointer names is the root set in the case of a C program because this is the data that can be accessed directly by a program without having to go through or dereferencing any other pointer. So, we have not really taken information by going to the address which the pointer points to and then taking the address from there and traversing further. We simply said we have a pointer and the pointer information is all that we are looking at. Now, we can do this recursively any object whose reference is stored in a field of a member of the root set is also reachable. So, the root set you know all the pointer variables are reachable that is the root set. Now, take the objects which are reachable from the pointer variables. So, within that there are many other you know these are all reachable and within these objects there will be other pointers. So, for the second step now says take the object take the members of that particular object and trace further. So, they will also become reachable new objects are introduced through object allocations and added to the set of reachable objects parameter. So, when we create a new object using malloc or new etcetera etcetera. So, these you know objects are created new and they are added to the set of reachable objects because the first time we create an object it is reachable through the pointer which points to it. Parameter passing and assignments can propagate reachability. So, in other words when I pass a parameter I say now the function which gets this parameter will have let us say a call by value parameter. So, I pass a parameter to the function which I am calling and from that function I am going to create a new variable local variable and that would be actually pointing to the object that I passed. So, parameter passing will actually propagate the reachability and of course, assignments will also propagate reachability because if I say a equal to b whatever b points to will also be pointed to by a now. So, now reachability actually propagates assignments and ends of procedures can terminate reachability as well I said here assignments can propagate, but assignments can also terminate reachability because the same case of a equal to b a was pointing to something. Now, after the assignment a equal to b a will now point to what b was pointing to. So, whatever b a was pointing to has been kind of terminated. So, it is not accessible anymore through the pointer in a and once a procedure ends all the local variables which were created within the procedure also die including local pointers. So, all these variables you know now point to kind of truncate the or terminate the reachability of objects that they point to. So, we will stop the lecture at this point and continue with methods of garbage collection in the next part of the lecture. Thank you.