See:
Description
Packages | |
---|---|
javax.realtime |
This section defines classes directly related to memory and memory management. These classes:
Schedulable objects that use the enter
method of MemoryArea
behave effectively as if they kept the memory areas they enter in a scope stack which enter
pushes and pops.
This chapter defines memory area classes. Two memory areas may be associated with each MemoryArea
instance, the memory area containing the instance and the backing memory that contains memory managed by the MemoryArea
instance.
Some memory area classes implement portals. These are a tool that associates a reference value with a memory area. It is normally used to give code that has a reference to the memory area a way to go from that to a reference to an object stored in that memory area.
For purposes of scoped memory reference counting, the following are treated as execution contexts:
RealtimeThread
objects that have been started and have not terminated,
The initial memory area for a schedulable object is non-default if it is not the memory area where the schedulable object was created.
An AsyncEventHandler
is fireable whenever there is an agent that can release it. This includes cases when the AsyncEventHandler is:
The following list establishes the semantics and requirements that are applicable across the classes of this section. Semantics that apply to particular classes, constructors, methods, and fields will be found in the class description and the constructor, method, and field detail sections.
MemoryArea
class. When a memory area, m, is entered by calling m.enter
(or another method from the family of enter-like methods in MemoryArea
or ScopedMemory
) m becomes the allocation context of the current schedulable object. When control returns from the enter method, the allocation context is restored to the value it had immediately before enter was called.
newInstance
and newArray
methods.
MemoryArea
and ScopedMemory
classes: all the enter
and joinAndEnter
methods, executeInArea
, and both newInstance
methods. See the semantics in Maintaining the Scope Stack for details.
executeInArea, newInstance
and newArray
methods, when invoked on an instance of ScopedMemory
require that instance to be an outer allocation context on the current schedulable object's current scope stack.
ScopedMemory
is said to be in use if it has a non-zero reference count as defined by semantic (17) below.ScopedMemory
object becomes in use, its parent is the nearest ScopedMemory
object outside it on the current scope stack. If there is no outside ScopedMemory
object in the current scope stack, the parent is the primordial scope which is not actually a memory area, but only a marker that constrains the parentage of ScopedMemory
objects.
RealtimeThread
, it completes execution
IllegalAssignmentError
.
executeInArea
, and the newInstance
and newArray
methods from the ImmortalMemory
and HeapMemory
classes. These methods allow it to execute with an immortal current allocation context, but semantic (15) applies even during execution of these methods.
ScopedMemory
or its subclasses must maintain a reference count which is greater than zero if and only if either:
AsyncEventHandler
.
AsyncEventHandler
becomes non-fireable, that real-time thread or AsyncEventHandler
is considered in control of the scope and must execute the finalizers.runFinalizersOnExit
, the system need not execute finalizers for immortal objects that remain unfinalized when the JVM begins termination.
ImmortalMemory.instance().executeInArea(r)
where r
is a Runnable
that executes the <clinit>
method of the class being initialized.
Stored In
|
Reference
to Heap |
Reference
to Immortal |
Reference to Scoped
|
null
|
---|---|---|---|---|
|
|
|
|
Permit |
|
|
|
|
Permit |
|
|
Permit
|
Permit, if the reference is from the same scope, or an outer scope
|
Permit |
|
|
|
|
Permit |
For this table, ImmortalMemory
and ImmortalPhysicalMemory
are equivalent, and all sub-classes of ScopedMemory
are equivalent.
The scope stack is implicitly visible through the assignment rules, and the stack is explicitly visible through the static getOuterMemoryArea(int index) method on RealtimeThread.
Four operations effect the scope stack: the enter methods in MemoryArea
and ScopedMemory, construction of a new schedulable object, the executeInArea method in MemoryArea, and the new instance methods in MemoryArea.
ma,
is entered by calling a ma.enter
method, ma
is pushed on the scope stack and becomes the allocation context of the current schedulable object. When control returns from the enter
method, the allocation context is popped from the scope stack
m
, is entered by calling m
's executeInArea
method or one of the m.newInstance
methods the scope stack before the method call is preserved and replaced with a scope stack constructed as follows:
ma
is a scoped memory area the new scope stack is a copy of the schedulable object's previous scope stack up to and including ma
.
ma
is not a scoped memory area the new scope stack includes only ma
.
executeInArea
method, the scope stack is restored to the value it had before ma.executeInArea
or ma.newInstance
was called.
Notes:
Forma.enter(logic):
push ma on the scope stack belonging to the current schedulable object -- which may throw ScopedCycleException execute logic.run method pop ma from the scope stack
if ma is an instance of heap immortal or ImmortalPhysicalMemory start a new scope stack containing only ma make the new scope stack the scope stack for the current schedulable object else ma is scoped if ma is in the scope stack for the current schedulable object start a new scope stack containing ma and all scopes below ma on the scope stack. make the new scope stack the scope stack for the current schedulable object else throw InaccessibleAreaException execute logic.run or construct the object restore the previous scope stack for the current schedulable object discard the new scope stack
cma
with initial memory area of ima
:
if cma is heap, immortal or ImmortalPhysicalMemory create a new scope stack containing cma else start a new scope stack containing the entire current scope stack if ima != cma push ima on the new scope stack -- which may throw ScopedCycleException
The above pseudocode illustrates a straightforward implementation of this specification's semantics, but any implementation that behaves effectively like this one with respect to reference count values of zero and one is permissible. An implementation may be eager or lazy in maintenance of its reference count provided that it correctly implements the semantics for reference counts of zero and one.
The parent of a scoped memory area is identified by the following rules (for a stack that grows up):
The operational effect of the single parent rule is that when a scoped memory area has a parent, the only legal change to that value is to "no parent." Thus an ordering imposed by the first assignments of parents of a series of nested scoped memory areas is the only nesting order allowed until control leaves the scopes; then a new nesting order is possible. Thus a schedulable object attempting to enter a scope can only do so by entering in the established nesting order.
Each scoped memory has a reference to its parent memory area, ma.parent. The parent reference may indicate a specific scoped memory area, no parent, or the primordial parent.
If a scoped memory area is the non-default initial memory area of an async event handler, or the non-default initial memory area of a real-time thread that has not terminated, it is referred to as pinned.
precondition: ma.parent is set to the correct parent (either a scoped memory area or the primordial scope) or to noParent t.scopeStack is the scope stack of the current schedulable object if ma is scoped parent = findFirstScope(t.scopeStack) if ma.parent == noParent ma.parent = parent else if ma.parent != parent throw ScopedCycleException else t.scopeStack.push(ma)findFirstScope is a convenience function that looks down the scope stack for the next entry that is a reference to an instance of ScopedMemoryArea.
findFirstScope(scopeStack) { for s = top of scope stack to bottom of scope stack if s is an instance of scopedMemory return s return primordial scope }
ma = t.scopeStack.pop() if ma is scoped if !(ma.in_use || ma.pinned) ma.parent = noParent
Languages that employ automatic reclamation of blocks of memory allocated in what is traditionally called the heap by program logic also typically use an algorithm called a garbage collector. Garbage collection algorithms and implementations vary in the amount of non-determinacy they add to the execution of program logic. Rather than require a garbage collector, and require it to meet real-time constraints that would necessarily be a compromise, this specification constructs alternative systems for "safe" management of memory. The scoped and immortal memory areas allow program logic to allocate objects in a Java-like style, ignore the reclamation of those objects, and not incur the latency of the implemented garbage collection algorithm.
The term scope stack might mislead a reader to infer that it contains only scoped memory areas. This is incorrect. Although the scope stack may contain scoped memory references, it may also contain heap and immortal memory areas. Also, although the scope stack's behavior is specified as a stack, an implementation is free to use any data structure that preserves the stack semantics.
This specification does not specifically address the lifetime of objects allocated in immortal memory areas. If they were reclaimed while they were still referenced, the referential integrity of the JVM would be compromised which is not permissible. Recovering immortal objects only at the termination of the application, or never recovering them under any circumstances is consistent with this specification.
If a scoped memory area is used by both heap and non-heap SOs, there could be cases where a finalizer executed in non-heap context could attempt to use a heap reference left by a heap-using SO. The code in the finalizer would throw a memory access error. If that exception is not caught in the finalizer, it will be handled by the implementation so finalization will continue undisturbed, but the problem in finalizer that caused the illegal memory access could be hard to locate. So, catch clauses in finalizers for objects allocated in scoped memory are even more useful than they are for normal finalizers.