Difference between revisions of "Memory Management"

From Suhrid.net Wiki
Jump to navigationJump to search
 
(10 intermediate revisions by the same user not shown)
Line 3: Line 3:
 
* Many real time systems have constraints on amount of memory available. e.g. because of cost or size constraints.
 
* Many real time systems have constraints on amount of memory available. e.g. because of cost or size constraints.
 
* It is necessary to control how this memory is allocated so that it can be used effectively. By doing this the program can increase its performance and predictability.
 
* It is necessary to control how this memory is allocated so that it can be used effectively. By doing this the program can increase its performance and predictability.
* Assuming a time critical thread is using heap memory, Running garbage collection can have an impact on its response time. (Since the GC Thread cannot be preempted by the RT Thread)
+
* Assuming a time critical thread '''is using heap memory''', Running garbage collection can have an impact on its response time.
* Therefore, tt is necessary to provide memory that is not subject to GC.
+
* It is not safe to preempt GC because memory compaction might be happening.
 +
* There is a ''lack of confidence'' in Real time garbage collection. Therefore, it is necessary to provide memory that is not subject to GC.
 
* The RTSJ provides two alternatives to traditional Java heap memory:
 
* The RTSJ provides two alternatives to traditional Java heap memory:
 
** Immortal Memory
 
** Immortal Memory
Line 13: Line 14:
 
* Schedulable objects '''must explicitly indicate''' the MemoryArea to allocate objects from, otherwise the standard Java heap will be used.  
 
* Schedulable objects '''must explicitly indicate''' the MemoryArea to allocate objects from, otherwise the standard Java heap will be used.  
 
* When a memory area is entered by a SO, all object allocation is performed from within that memory area.
 
* When a memory area is entered by a SO, all object allocation is performed from within that memory area.
 +
* RTSJ requires that the garbage collector can be preempted by real-time threads and that there should be a bounded latency for pre-emption to take place.
  
 
= Heap Memory =
 
= Heap Memory =
Line 109: Line 111:
 
* To avoid this problem, the RTSJ requires that each '''scoped memory area''' (note: only scoped memory area) can only have a single parent.
 
* To avoid this problem, the RTSJ requires that each '''scoped memory area''' (note: only scoped memory area) can only have a single parent.
  
* The parent of an active scoped memory area can be:
+
* The parent of an '''''active''''' scoped memory area can be:
 
** If scoped memory is the first  scoped area on the stack, then its parent is the primordial scope area.
 
** If scoped memory is the first  scoped area on the stack, then its parent is the primordial scope area.
 
** For others, the parent is the ''first scoped area'' below it on the stack.
 
** For others, the parent is the ''first scoped area'' below it on the stack.
Line 122: Line 124:
 
** int getInitialMemoryAreaIndex() : This method returns the position in the memory area stack of the initial memory area. Memory area stacks may include inherited stacks from parent threads. The initial memory area for the current RealtimeThread or AsyncEventHandler is the memory area given as a parameter to the constructor.  
 
** int getInitialMemoryAreaIndex() : This method returns the position in the memory area stack of the initial memory area. Memory area stacks may include inherited stacks from parent threads. The initial memory area for the current RealtimeThread or AsyncEventHandler is the memory area given as a parameter to the constructor.  
 
** MemoryArea getOuterMemoryArea(int index) : Gets the instance of MemoryArea in the memory area stack at the index given. If the given index does not exist in the memory area scope stack then null is returned.
 
** MemoryArea getOuterMemoryArea(int index) : Gets the instance of MemoryArea in the memory area stack at the index given. If the given index does not exist in the memory area scope stack then null is returned.
 +
 +
* Example:
 +
 +
<syntaxhighlight lang="java5">
 +
 +
import javax.realtime.*;
 +
 +
public class IniStackTest {
 +
 +
public static void main(String[] args) {
 +
 +
final ImmortalMemory imm = ImmortalMemory.instance();
 +
 +
RealtimeThread rt = new RealtimeThread() {
 +
public void run() {
 +
imm.enter(new Runnable() {
 +
public void run() {
 +
System.out.println("Current Area : " + RealtimeThread.getCurrentMemoryArea());
 +
System.out.println("Stack Depth : " + RealtimeThread.getMemoryAreaStackDepth());
 +
//Outer Area
 +
System.out.println("Outer Area : " + RealtimeThread.getOuterMemoryArea(RealtimeThread.getInitialMemoryAreaIndex()));
 +
}
 +
});
 +
}
 +
};
 +
 +
rt.start();
 +
}
 +
}
 +
 +
/* Output
 +
 +
Current Area : javax.realtime.ImmortalMemory@9ff8bf
 +
Stack Depth : 2
 +
Outer Area : javax.realtime.HeapMemory@83f10d */
 +
 +
</syntaxhighlight>
 +
 +
* OuterArea is heap - base of stack, because rt object was created in heap. If rt was created in Immortal, then the base of the stack would be immortal.
 +
* See below example, in this case since inner rt is created in immortal, its stack consists of only the immortal thread.
 +
 +
<syntaxhighlight lang="java5">
 +
 +
RealtimeThread outer = new RealtimeThread() {
 +
public void run() {
 +
imm.enter(new Runnable() {
 +
public void run() {
 +
RealtimeThread inner = new RealtimeThread() {
 +
public void run() {
 +
System.out.println("Current Area : " + RealtimeThread.getCurrentMemoryArea());
 +
System.out.println("Stack Depth : " + RealtimeThread.getMemoryAreaStackDepth());
 +
//Outer Area
 +
System.out.println("Outer Area : " + RealtimeThread.getOuterMemoryArea(RealtimeThread.getInitialMemoryAreaIndex()));
 +
}
 +
};
 +
inner.start();
 +
}
 +
});
 +
}
 +
};
 +
 +
outer.start();
 +
 +
/* Output:
 +
 +
Current Area : javax.realtime.ImmortalMemory@2055e9
 +
Stack Depth : 1
 +
Outer Area : javax.realtime.ImmortalMemory@2055e9
 +
 +
*/
 +
</syntaxhighlight>
  
 
= MemoryArea ExecuteInArea =  
 
= MemoryArea ExecuteInArea =  
Line 333: Line 406:
  
 
* When a SO is created, it's initial memory area can be set. For e.g. as a constructor parameter for RealtimeThread.
 
* When a SO is created, it's initial memory area can be set. For e.g. as a constructor parameter for RealtimeThread.
 +
* If the initial memory is not set explicitly, it starts executing in the area in which it was created.
 
* The memory stack of a created SO is determined by the value of the initial memory area and the current active memory area from where the SO is created.
 
* The memory stack of a created SO is determined by the value of the initial memory area and the current active memory area from where the SO is created.
 
* For e.g. if SO1 (parent) is creating SO2 (child), then SO2's memory stack is determined by its initial area and the currently active memory area SO1 was executing in, when it created SO2.
 
* For e.g. if SO1 (parent) is creating SO2 (child), then SO2's memory stack is determined by its initial area and the currently active memory area SO1 was executing in, when it created SO2.
Line 338: Line 412:
 
** If the  current active area is heap/immortal and childs initial memory area is heap/immortal and the, then the childs '''new''' stack will contain only heap/immortal area
 
** If the  current active area is heap/immortal and childs initial memory area is heap/immortal and the, then the childs '''new''' stack will contain only heap/immortal area
 
** If the current active area is heap/immortal and childs initial memory area is other than the current active area (e.g. a scoped area), then the childs '''new''' stack will contain the heap/immortal + the other area.
 
** If the current active area is heap/immortal and childs initial memory area is other than the current active area (e.g. a scoped area), then the childs '''new''' stack will contain the heap/immortal + the other area.
** If the current active area is scoped and childs ini mem area is the same scoped area, then the childs '''new''' stack will be the same as the parents stack.  
+
** If the current active area is scoped and childs ini mem area is the same scoped area, then the childs '''new''' stack will be the same as the parents stack up to and including the current memory area.
** If the current active area is scoped and the childs ini mem area is a different scoped area, then the childs '''new''' stack will be the same as the parents stack and include the different scoped area.
+
** If the current active area is scoped and the childs ini mem area is a different scoped area, then the childs '''new''' stack will be the same as the parents stack up to and including the current memory area and include the different scoped area.
  
 
= Using scoped memory areas =
 
= Using scoped memory areas =
Line 347: Line 421:
 
== Competitive Mode ==  
 
== Competitive Mode ==  
  
* The goal is to maximize use of memory. For e.g. requirement may be to use only the amount of memory allocated to the scope at any given time.  So, a maximum of 1 SO can be active in a memory area at any given time. As usual, the memory is reclaimed when the SO leaves the area.
+
* The goal is to maximize use of memory. For e.g. requirement may be to use only the amount of memory allocated to the scope at any given time.  So, a maximum of 1 SO can be active in a memory area at any given time. The intention is that the memory can be reclaimed when each of the schedulable objects leave the area.
 
*** The ScopedMemory provides methods for competitive mode. e.g. join(), joinandenter().  
 
*** The ScopedMemory provides methods for competitive mode. e.g. join(), joinandenter().  
 
*** join() -  Make the SO wait until the reference count of this ScopedMemory goes down to zero.
 
*** join() -  Make the SO wait until the reference count of this ScopedMemory goes down to zero.
Line 628: Line 702:
  
  
[[Category:RealTimeJava]]
+
[[Category:RealtimeJava]]

Latest revision as of 13:24, 9 January 2012

Intro

  • Many real time systems have constraints on amount of memory available. e.g. because of cost or size constraints.
  • It is necessary to control how this memory is allocated so that it can be used effectively. By doing this the program can increase its performance and predictability.
  • Assuming a time critical thread is using heap memory, Running garbage collection can have an impact on its response time.
  • It is not safe to preempt GC because memory compaction might be happening.
  • There is a lack of confidence in Real time garbage collection. Therefore, it is necessary to provide memory that is not subject to GC.
  • The RTSJ provides two alternatives to traditional Java heap memory:
    • Immortal Memory
    • Scoped Memory
  • Immortal and Scoped memory areas exist outside the heap and are NOT subject to GC.
  • Both types of area are represented by the abstract javax.realtime.MemoryArea class.
  • Only javax.realtime.Schedulable objects are allowed to enter into a MemoryArea. If standard Java threads attempt to get in then an IllegalThreadStateException is thrown.
  • Schedulable objects must explicitly indicate the MemoryArea to allocate objects from, otherwise the standard Java heap will be used.
  • When a memory area is entered by a SO, all object allocation is performed from within that memory area.
  • RTSJ requires that the garbage collector can be preempted by real-time threads and that there should be a bounded latency for pre-emption to take place.

Heap Memory

  • Standard Java Heap. Realtime Threads are allowed to use heap memory. Of course, reclamation of this memory is subject to when the JVM runs GC.

Immortal Memory

  • The memory associated with objects allocated in immortal memory is never GC'ed and never released during the lifetime of the application.
  • Immortal memory is shared among all threads.
  • The programmer can reuse the memory by other means. e.g. by maintaining a pool of reusable objects.
  • Class objects and their associated static memories, along with objects created by static initialisation and interned strings (the list of program-defined constant strings that is maintained by the String class) are ALWAYS allocated in immortal memory. This is important, so if an object's static reference is assigned to an object created on scoped memory it will fail, because immortal to scope assignment is not allowed !
  • There is only one instance of ImmortalMemory - it is a singleton object.

Scoped Memory

  • Scoped memory is a memory area where objects with a well-defined lifetime can be allocated.
  • Two types:
    • LTMemory : the allocation time for objects is directly proportional to the size of the object being allocated ,
    • VTMemory : allocation can occur in a variable time.
  • The expectation is that allocation time for objects in VTMemory will be faster but less predictable than allocation in LTMemory.
  • Each scoped memory object has a reference count which indicates the number of times the scope has been entered. Note it doesnt refer to the normal idea of Java GC reference counting.
  • When that reference count goes from 1 to 0, the memory allocated in the scoped memory area can be reclaimed (after running any finalization code associated with the allocated objects).
  • The reference count of a scoped memory area is the count of the number of active calls (explicit or implicit) to its enter method. It is NOT a count of the number of objects that have references to the objects allocated in the scoped memory.

MemoryParameters

  • Can be supplied when creating SO's. They can specify the max amount of memory a SO can consume in the memory area, the maximum immortal area and the rate of allocation.
  • When a SO exceeds its allocation or allocation rate limit, the error is handled as if the allocation failed because of insufficient memory.

MemoryArea Enter

  • The MemoryArea class has a enter() method.
  • Calling the enter() method on a MemoryArea object, will associate the memory area with the current schedulable object for the duration of the execution of the run() method of the instance of Runnable given in the constructor of the MemoryArea or passed as a parameter to the enter method.
  • In the below example, firstMemArea.enter(task) will associate firstMemArea to MyRTThread object for the duration of the RTTask's run() method - so all the objects allocated in the run() method are being allocated from firstMemArea.
  • Note: however the RTTask object is allocated in the heap, because that was the active memory when new RTTask() was being run.
import javax.realtime.*;

public class MAEnter1 {
	
	private class RTTask implements Runnable {
		public void run() {
				System.out.println("Executing in " + RealtimeThread.getCurrentMemoryArea());
				for(int i=1; i <=100; i++) {
					Object obj = new Object();
					System.out.println("Created object " + i);
				}
		}
	}
	
	private final RTTask task = new RTTask();
	
	private final ScopedMemory firstMemArea = new VTMemory(1000000);
	private final ScopedMemory secondMemArea = new VTMemory(1000000);
	
	private class MyRTThread extends RealtimeThread {
		public void run() {
			firstMemArea.enter(task);
			secondMemArea.enter(task);
		}
	}

	public static void main(String[] args) {
		MAEnter1 mpt = new MAEnter1();
		mpt.go();
	}
	
	private void go() {
		MyRTThread rtThread = new MyRTThread();
		rtThread.start();
	}

}

Memory Assignment Rules

  • Different memory areas have differed collection mechansims, so there has to be some restrictions on assignments in different types of memory.
  • For e.g. A reference from ImmortalMemory cannot point to an object created in scope memory, because the memory in the scope area can be reclaimed anytime.
  • Once the object in scope memory is gone, the reference in the ImmortalMemory is a dangling reference.
  • Therefore no asssignments can be made from immortal or heap memory areas to objects created in scoped memory areas.
  • For scope memory areas themselves, assignments to outer scope area is OK - because the outer scope is guaranteed to be active longer than the current scope. Following a similar logic, assignments to inner scopes are forbidden.
  • If a program violates these rules a run time IllegalAssignmentError is thrown.
  • The memory assignment checks are performed at run time because one of the requirements for the RTSJ is that any compiler should be able to compile the code.
  • To keep track of the memory areas used by a SO, the RTJVM uses a stack.
  • From the above rules, it is OK to make assignments to memory areas higher up the stack (the inner scope) but not OK to make assignments to areas lower the stack (the outer scope).
  • The stack can therefore be used to check for violation of memory assignment rules.
  • Now what if a SO enters ScopedA, then ScopedB and re-enters ScopedA. Now when SO is in innermost ScopedA (top of stack), reference can be made to an object in Scoped B. Now when the Top of stack ScopedA exits, its memory is not reclaimed because the current SO is still active in ScopedA at bottom of stack. When ScopedB exits, its memory is reclaimed and ScopedA is left with a dangling reference.
  • To avoid this problem, the RTSJ requires that each scoped memory area (note: only scoped memory area) can only have a single parent.
  • The parent of an active scoped memory area can be:
    • If scoped memory is the first scoped area on the stack, then its parent is the primordial scope area.
    • For others, the parent is the first scoped area below it on the stack.
  • The base of the stack is what is the current memory when the RT Thread object is created. If it is created in the Java heap, then the Heap will be at the base, if its immortal, then immortal is at the base.

Memory Stack Information

  • The RealtimeThread class has static methods to get information about the memory stack.
    • int getMemoryAreaStackDepth() : Gets the size of the stack of MemoryArea instances to which the current schedulable object has access.
    • The current memory area (getCurrentMemoryArea()) is found at memory area stack index getMemoryAreaStackDepth() - 1. (Stack size - 1 -> Top of stack)
    • int getInitialMemoryAreaIndex() : This method returns the position in the memory area stack of the initial memory area. Memory area stacks may include inherited stacks from parent threads. The initial memory area for the current RealtimeThread or AsyncEventHandler is the memory area given as a parameter to the constructor.
    • MemoryArea getOuterMemoryArea(int index) : Gets the instance of MemoryArea in the memory area stack at the index given. If the given index does not exist in the memory area scope stack then null is returned.
  • Example:
import javax.realtime.*;

public class IniStackTest {

	public static void main(String[] args) {
		
		final ImmortalMemory imm = ImmortalMemory.instance();
		
		RealtimeThread rt = new RealtimeThread() {
			public void run() {
				imm.enter(new Runnable() {
					public void run() {
						System.out.println("Current Area : " + RealtimeThread.getCurrentMemoryArea());
						System.out.println("Stack Depth : " + RealtimeThread.getMemoryAreaStackDepth());
						//Outer Area
						System.out.println("Outer Area : " + RealtimeThread.getOuterMemoryArea(RealtimeThread.getInitialMemoryAreaIndex()));
					}
				});
			}
		};
		
		rt.start();
	}
}

/* Output

Current Area : javax.realtime.ImmortalMemory@9ff8bf
Stack Depth : 2
Outer Area : javax.realtime.HeapMemory@83f10d */
  • OuterArea is heap - base of stack, because rt object was created in heap. If rt was created in Immortal, then the base of the stack would be immortal.
  • See below example, in this case since inner rt is created in immortal, its stack consists of only the immortal thread.
RealtimeThread outer = new RealtimeThread() {
			public void run() {
				imm.enter(new Runnable() {
					public void run() {
						RealtimeThread inner = new RealtimeThread() {
							public void run() {
								System.out.println("Current Area : " + RealtimeThread.getCurrentMemoryArea());
								System.out.println("Stack Depth : " + RealtimeThread.getMemoryAreaStackDepth());
								//Outer Area
								System.out.println("Outer Area : " + RealtimeThread.getOuterMemoryArea(RealtimeThread.getInitialMemoryAreaIndex()));
							}
						};
						inner.start();
					}
				});
			}
		};
		
		outer.start();

/* Output:

Current Area : javax.realtime.ImmortalMemory@2055e9
Stack Depth : 1
Outer Area : javax.realtime.ImmortalMemory@2055e9

*/

MemoryArea ExecuteInArea

  • Now, since it is not possible to re enter an active scoped area - because it will violate the single parent rule, we need a mechanism to move between active memory areas.
  • The executeInArea(Runnable logic) method of the MemoryArea class allows us to do this. It executes the run method from the logic parameter using the memory area as the current allocation context. So it

looks like the enter method, but the difference is that while enter will result in a new memory area being created on the scope stack, execute just shifts the current allocation context.

  • The MemoryArea class also has convenience methods such as newInstance() which can allocate a new object in the given memory area.
  • executeInArea is only possible on a memory area which is ON the scoped stack. Attempting to execute in area on a MemoryArea not on the scope stack (for e.g. the SO has already exited the MemoryArea) will result in javax.realtime.InaccessibleAreaException.
  • See below example, the SO enters scoped A, then enters scoped B - while in scoped B it attempts to executeInArea in scopedA, this fails because scopedA is not on the stack anymore !
import javax.realtime.*;

public class ExecInAreaTest {
	
	private ScopedMemory scopedA = new LTMemory(1000000,1000000);
	private ScopedMemory scopedB = new LTMemory(1000000,1000000);
	
	class Task implements Runnable {
		
		private String name;
		
		Task(String taskName) {
			name = taskName;
		}
		
		public void run() {
			System.out.println("RT Thread : " + RealtimeThread.currentRealtimeThread() + " performing task " + name + " in " + RealtimeThread.getCurrentMemoryArea());
			if(name.equals("B")) {
				scopedA.executeInArea(new Runnable() { //This will fail
					public void run() {
						System.out.println("RT Thread : " + RealtimeThread.currentRealtimeThread() + " executing area in " + RealtimeThread.getCurrentMemoryArea());		
					}
				});
			}
		}
	}
	
	

	public static void main(String[] args) {
		ExecInAreaTest et = new ExecInAreaTest();
		et.go();
	}
	
	private void go() {
		
		RealtimeThread rtThread = new RealtimeThread() {
			public void run() {
				Task taskA = new Task("A");
				scopedA.enter(taskA);
				Task taskB = new Task("B");
				scopedB.enter(taskB);
			}
		};
		
		rtThread.start();
		
	}
	
}
  • Here's one more example. Here executeInArea works well. SO enters ScopedA, then enters ScopedB while in ScopedB it executesInArea() for ScopedA - this works well. Now while in ScopedB, when it attempts

to re-enter ScopedA - the single parent for ScopedA is violated and a ScopedCycleException is thrown:

import javax.realtime.*;

public class ExecInAreaTest2 {
	
	private ScopedMemory scopedA = new LTMemory(1000000,1000000);
	private ScopedMemory scopedB = new LTMemory(1000000,1000000);
	
	class ExecInAreaLogic implements Runnable {
		public void run() {
			System.out.println("RT Thread : " + RealtimeThread.currentRealtimeThread() + " executing area in  " + RealtimeThread.getCurrentMemoryArea());
		}
	}
	
	class Task implements Runnable {
		
		private String name;
		
		Task(String taskName) {
			name = taskName;
		}
		
		public void run() {
			System.out.println("RT Thread : " + RealtimeThread.currentRealtimeThread() + " performing task " + name + " in " + RealtimeThread.getCurrentMemoryArea());
			if(name.equals("A")) {
				scopedB.enter(new Runnable() { 
					public void run() {
						System.out.println("RT Thread : " + RealtimeThread.currentRealtimeThread() + "running in  " + RealtimeThread.getCurrentMemoryArea());
						//Attempt to execute in Scoped A - still on the stack.
						scopedA.executeInArea(new ExecInAreaLogic());
						//Attempt to enter in A - this will fail, because A's parent is already primordial and it cant have a new parent of B.
						try {
						scopedA.enter(new Runnable() {
							public void run() {
								System.out.println("This will not print");
							}
						});
						} catch(ScopedCycleException sce) {
							System.out.println("Caught Scoped Cycle Exception - cant reenter A");
						}
					}
				});
			}
		}
	}

	public static void main(String[] args) {
		ExecInAreaTest2 et = new ExecInAreaTest2();
		et.go();
	}
	
	private void go() {
		
		RealtimeThread rtThread = new RealtimeThread() {
			public void run() {
				Task taskA = new Task("A");
				scopedA.enter(taskA);
			}
		};
		
		rtThread.start();
		
	}
	
}
  • Finally, If executeInArea is called on a heap (or immortal) memory, this results in a new memory stack being created, with the heap (or immortal) memory area at its base.
  • Example:
import javax.realtime.*;

public class ExecInAreaImmortal {
	
	private ScopedMemory scopedA = new LTMemory(1000000,1000000);
	private ImmortalMemory imm = ImmortalMemory.instance();
	
	class Task implements Runnable {
		
		private String name;
		
		Task(String taskName) {
			name = taskName;
		}
		
		public void run() {
			//In immortal
			System.out.println("RT Thread : " + RealtimeThread.currentRealtimeThread() + " performing task " + name + " in " + RealtimeThread.getCurrentMemoryArea());
			System.out.println("Stack Depth a : " + RealtimeThread.getMemoryAreaStackDepth()); //Stack Depth : 2
			scopedA.enter(new Runnable() { //In scoped A
				public void run() {
					System.out.println("Executing in " + RealtimeThread.getCurrentMemoryArea());
					System.out.println("Stack Depth b 1: " + RealtimeThread.getMemoryAreaStackDepth()); //Stack Depth : 3
					imm.executeInArea(new Runnable() {
						public void run() {
							System.out.println("Executing in immortal area "); //New stack with only immortal - no scoped A
							System.out.println("Stack Depth c : " + RealtimeThread.getMemoryAreaStackDepth()); //Stack Depth : 1 (New stack with only immortal)
							scopedA.executeInArea(new Runnable() { //Will fail because scoped A is not on the new stack.
								public void run() {
									System.out.println("This should fail");
								}
							});
						}
					});
				}
			});
		}
	}

	public static void main(String[] args) {
		ExecInAreaImmortal et = new ExecInAreaImmortal();
		et.go();
	}
	
	private void go() {
		
		RealtimeThread rtThread = new RealtimeThread() {
			public void run() {
				Task taskA = new Task("A");
				imm.enter(taskA);
			}
		};
		
		rtThread.start();
		
	}
	
}

Sharing Memory Areas

  • Memory areas can be shared between different SO's. Consequently the cactus stack for the SO's are linked together.

Inheritance of Stacks

  • When a SO is created, it's initial memory area can be set. For e.g. as a constructor parameter for RealtimeThread.
  • If the initial memory is not set explicitly, it starts executing in the area in which it was created.
  • The memory stack of a created SO is determined by the value of the initial memory area and the current active memory area from where the SO is created.
  • For e.g. if SO1 (parent) is creating SO2 (child), then SO2's memory stack is determined by its initial area and the currently active memory area SO1 was executing in, when it created SO2.
  • There are some rules:
    • If the current active area is heap/immortal and childs initial memory area is heap/immortal and the, then the childs new stack will contain only heap/immortal area
    • If the current active area is heap/immortal and childs initial memory area is other than the current active area (e.g. a scoped area), then the childs new stack will contain the heap/immortal + the other area.
    • If the current active area is scoped and childs ini mem area is the same scoped area, then the childs new stack will be the same as the parents stack up to and including the current memory area.
    • If the current active area is scoped and the childs ini mem area is a different scoped area, then the childs new stack will be the same as the parents stack up to and including the current memory area and include the different scoped area.

Using scoped memory areas

  • Scoped memory areas can be used by SO's in two modes: Competitive and Cooperative.

Competitive Mode

  • The goal is to maximize use of memory. For e.g. requirement may be to use only the amount of memory allocated to the scope at any given time. So, a maximum of 1 SO can be active in a memory area at any given time. The intention is that the memory can be reclaimed when each of the schedulable objects leave the area.
      • The ScopedMemory provides methods for competitive mode. e.g. join(), joinandenter().
      • join() - Make the SO wait until the reference count of this ScopedMemory goes down to zero.
      • joinAndEnter() - The will make the SO wait for the reference count on this ScopedMemory to reach zero, then the SO will enter the ScopedMemory.
      • There are overloaded versions which take a HighResolutionTime(), but then if the time expires, the memory is entered even if the referencecount is non-zero!
  • Competitive use example:
import javax.realtime.*;

public class ScopeShare {
	
	private ScopedMemory scoped = new VTMemory(100000,10000000);

	public static void main(String[] args) {
		ScopeShare s = new ScopeShare();
		s.go();
	}
	
	class Task implements Runnable {
		public void run() {
			try {
				System.out.println(RealtimeThread.currentRealtimeThread().getName() + " entered scoped area.);
				Thread.sleep(5000);
				System.out.println(RealtimeThread.currentRealtimeThread().getName() + " left scoped area. ");
			} catch(InterruptedException e) {
				
			}
		}
	}
	
	class MyRTThread extends RealtimeThread {
		public void run() {
			try {
				System.out.println(RealtimeThread.currentRealtimeThread().getName() + " waiting to enter scoped area..");
				scoped.joinAndEnter(new Task());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void go() {
		
		RealtimeThread rt1 = new MyRTThread();
		RealtimeThread rt2 = new MyRTThread();
		
		rt1.start();
		rt2.start();
	}

}

Cooperative

  • Cooperative Mode: The SO's use the scoped memory area to communicate shared objects and therefore multiple SO's will enter the shared area simultaneously. When all of them leave, memory is reclaimed.
  • See example below, parent and child threads share the scope area.
import javax.realtime.*;

public class ScopeShare {
	
	private ScopedMemory sharedScope = new VTMemory(10000000,10000000);
	
	class Task implements Runnable {
		
		Task() {
		}
		
		public void run() {
			System.out.println(RealtimeThread.currentRealtimeThread() + " creating shared buffer");
			final StringBuffer shared = new StringBuffer("Lorem Ipsum");
			System.out.println("Memory area for shared buffer : " + MemoryArea.getMemoryArea(shared));
			RealtimeThread child = new RealtimeThread() {
				public void run() {
					System.out.println(RealtimeThread.currentRealtimeThread() + " Reading shared buffer : " + shared);
				}
			};
			child.start();
		}
	}
	
	class FooRT extends RealtimeThread {
		public void run() {
			Task t = new Task();
			System.out.println("Memory are for task t " + MemoryArea.getMemoryArea(t));
			sharedScope.enter(t);
		}
	}

	public static void main(String[] args) {
		ScopeShare s = new ScopeShare();
		s.go();
	}
	
	private void go() {
		FooRT parent = new FooRT();
		parent.start();
	}

}

Portals

  • But what if the SO's dont have any relationship with each other (like parent-child), how will they share the object in scoped memory ?
  • Reference to the shared scoped memory object cannot be created in heap/immortal/outer-scope, because it will violate memory assignment rules. So how can multiple threads which start in heap memory and enter the same scoped memory pass references from one to another ?
  • So the ScopedMemory class itself provides a portal object. Multiple SO's can get and set the portal object when they are running in the scoped memory area.
  • Definition:
  • When schedulable objects are using scoped memory in a cooperative manner and there is no other relationship between the schedulable objects, it becomes difficult to see how they can effectively share objects created in the scoped memory area. To share an object requires each schedulable object to have a reference to that object. A reference to an object can only be stored in an object in the same scoped area or in an object in a nested scoped area. It cannot be stored in the immortal or heaped memory areas. Consequently, unless there is some relationship between the schedulable objects, one schedulable object cannot pass a reference to an object it has just created to another schedulable object. The RTSJ provides portals to solve this problem. Each scoped memory area can have one object that can act as a gateway into that memory area. Schedulable objects can use this mechanism to facilitate communication.
  • See example below, one thread writes a string buffer and stores it in the portal, other reads it.
  • Synchronization is done on the scoped memory area itself.
  • Once a SO completes, it has to wait - using a barrier - otherwise when the SO exits, the portal is destroyed - scoped memory is reclaimed.
import javax.realtime.*;

class Barrier {
	
	private int requiredParticipants = 0;
	private int arrived = 0;
	
	public Barrier(int participants) {
		requiredParticipants = participants;
	}
	
	public synchronized void waitB() {
		arrived++;
		try {
			while(arrived != requiredParticipants) {
				wait();
			}
			//All participants  have arrived.
			notifyAll();
		} catch (InterruptedException e) {
		}
	}
}



public class ScopeSharePortal {
	
	private ScopedMemory sharedScope = new VTMemory(10000000,10000000);
	
	class MyPortal {
		
		private StringBuffer buffer;
		private Barrier barrier;
		
		public StringBuffer getBuffer() {
			return buffer;
		}
		
		public void setBuffer(StringBuffer buffer) {
			this.buffer = buffer;
		}
		
		public Barrier getBarrier() {
			return barrier;
		}
		
		public void setBarrier(Barrier barrier) {
			this.barrier = barrier;
		}
		
	}
	
	class Task implements Runnable {
		
		Task() {
		}
		
		public void run() {
			
			Barrier barrier = null;
			
			synchronized(RealtimeThread.getCurrentMemoryArea()) {
				Object portalObj = sharedScope.getPortal();
				if(portalObj == null) {
					System.out.println(RealtimeThread.currentRealtimeThread() + " writing portal object ");
					MyPortal mp = new MyPortal();
					barrier = new Barrier(2);
					StringBuffer shared = new StringBuffer("Lorem Ipsum");
					mp.setBarrier(barrier);
					mp.setBuffer(shared);
					sharedScope.setPortal(mp);
				} else {
					System.out.println(RealtimeThread.currentRealtimeThread() + " reading portal object ");
					MyPortal mp = (MyPortal) portalObj;
					barrier = mp.getBarrier();
					System.out.println(mp.getBuffer());
				}
				
			}
			
			System.out.println(RealtimeThread.currentRealtimeThread() + " waiting at barrier");
			barrier.waitB();
			System.out.println(RealtimeThread.currentRealtimeThread() + " exiting ");
		}
	}
	
	class FooRT extends RealtimeThread {
		
		private Task t;
		
		public FooRT(Task t) {
			this.t = t;
		}
		
		public void run() {
			sharedScope.enter(t);
		}
	}

	public static void main(String[] args) {
		ScopeSharePortal s = new ScopeSharePortal();
		s.go();
	}
	
	private void go() {
		Task t = new Task();
		
		FooRT rt1 = new FooRT(t);
		FooRT rt2 = new FooRT(t);
		
		rt1.start();
		rt2.start();
	}

}

RT Issues with Scoped Memory

Using Scoped Memory

  • Supposed a class creates a scoped memory and runs a task in it. The class is expected to be accessed sequentially.
  • Now what if the class is accessed concurrently ? A ScopedCycleException can be thrown. Say the class creates a scoped area, called ScopedNew.
  • Now thread 1 can have a current active area of ScopedA, when it accesses the class, then ScopedNew's parent will be ScopedA.
  • Similarly thread 2 can have an area of ScopedB, then ScopedNew's parent will be ScopedB. ScopedCycleException !
  • Therefore
    • The client schedulable objects must either ensure that the scoped memory stack is empty or, ensure that all clients call with the same stack.
    • Clients need to have details of when a class is using scoped memory areas.
  • Suppose the class expects to be accessed concurrently. Then:
    • It must either assume clients have the same scoped memory stacks, or class code must “execute in” a heap or an immortal area first (to create new stacks).
    • If it chooses the heap, the class cannot be used by a no-heap client;
    • If it chooses immortal memory, named inner classes must be used — otherwise every time it creates an object from an anonymous class, some memory will be created in immortal memory and this will not be reclaimed.

Entry into Scoped Memory

  • A SO may be blocked when it used join() etc.
  • Block time difficult to bound, block time will be maximum time lower priority SO's take to get out of the scoped memory.
  • When a SO attempts to enter, it may also have to wait for the scope memory's object to be reclaimed first.

Memory Allocation

  • Memory alloc in Scoped memory consists of time to allocate space(LT vs VT) and the time taken to run the constructor.

Exit from Scoped Memory

  • Flexibility lies with implementation on when Scoped Memory can be reclaimed.
  • Can be immediately after refcount == 0 or after some time, but has to before a new so enters the scoped memory.
  • An SO may suffer when it first enters and is made to wait for reclamation.

Misc

  • Time taken to run object finalizers being run needs to be accounted.
  • GC might scan scoped memory looking for scope-to-heap references when heap is being GC'ed.