Lifetime of data
Scope
The comments below specify the scope of each field.
namespace ns1 class class1 ab ,a10 ;Valid within the class subroutine sub1 record ac ,d2 ;Valid within the subroutine proc begin data ad ,i4 ;Valid within the BEGIN-END block end xreturn endsubroutine endclass endnamespace
Access to a structure and all of its members declared within a routine are limited to the scope of the routine in which it was declared.
Characteristics
Constant data
Constant data is not modified at all during program execution. It is defined with the LITERAL statement or by the class READONLY or CONST field modifiers.
Local data
Local data is specific to a routine. It can be defined within a RECORD statement that is not part of a GLOBAL-ENDGLOBAL block or in a subroutine or function that is declared outside of a class. The LOCAL and STATIC qualifiers on the RECORD, FUNCTION, and SUBROUTINE statements can also designate data as local. See the RECORD-ENDRECORD Discussion for a more thorough discussion of LOCAL and STATIC.
The lifetime of local data is determined by the lifetime of the routine, and the data goes out of scope when the routine ends.
Instance data
Instance data is tied to an object variable. When you instantiate an object, it creates data and assigns a reference of that newly created object to an instance variable. Once there are no more references, either because the variable was explicitly cleared or because it went out of scope, the instance is destroyed and the data goes away.
An instance variable is specific to the block in which it is defined. The DATA statement is one way to define instance variables. The lifetime of an instance variable is determined by the block in which the object variable is declared. When the block ends, the variable goes out of scope.
All class instance data operates the same as if the -qcheck compiler option was specified. Class instance data cannot be subscripted, nor can arrays be accessed beyond their bounds for any class instance data. This is also true for local DATA statement variables. |
Global data
Global data remains resident in memory for as long as the program is running. It is defined in a global data section, in a COMMON statement, or as static class data (in other words, as a field within a class that has the STATIC qualifier). Static class data is shared by all instances of the class.
The compiler resolves identifiers in the order given below. (For resolution purposes, two identifiers are considered the same if they are identical after being lowercased.)
- A local data statement in the current scope or in a higher scope if not found in the current scope. This includes a CATCH variable.
- A locally defined variable (including common record and global data section) or a routine parameter.
- Class data members (static or instance fields).
- Class non-data members (includes properties, methods, etc.). The search starts with the local class and then checks for inherited members.
- Routines or structures in the current compilation unit, including locally defined functions (external functions) and functions and subroutines in the current compilation unit.
- Routines, structures, or data members in an imported namespace. This includes implicitly imported namespaces such as the System and Synergex.SynergyDE namespaces where all the predefined Synergex-supplied routines are defined.
Because functions can be called without a % character (and in traditional Synergy, subroutines can be called without “XCALL”), it’s especially important to understand how the compiler resolves identifiers used in the procedure division that are not also the name of a Synergy statement. Note that if the identifier is preceded by a % character, the compiler restricts its search to only look for a function. Similarly, if the identifier is preceded by “XCALL,” the compiler only searches for a subroutine. |
Memory management
Memory can be managed either statically or dynamically.
Static memory is persistent throughout the life of the program. A static record’s data is only initialized when the program is started.
Dynamic memory management is the allocation, usage, and deallocation of memory that is controlled at execution time, rather than compile time. When the program requires memory, it simply tells the system how much it needs. The system allocates the memory and returns a pointer to the base of the memory segment. We refer to this pointer as a handle. (See Memory access using “handles” (^M) below.) There is no practical restriction on the amount of memory available, and once used, the memory can be released back to the system. The amount of memory is totally dynamic: it can grow or shrink as required by the application. As new memory is allocated, it is logically appended to the existing memory, and as memory is released, it is logically truncated from the existing memory.
Dynamic memory is allocated and manipulated by the %MEM_PROC function. The other elements of the language that support dynamic memory are
- STACK qualifier on the RECORD, SUBROUTINE, and FUNCTION statements.
- STRUCTURE statement.
- ^M data reference operation.
- The dynamic arrays [#,#]@class and [#,#]structure.
- System.Collections.ArrayList class.
- Instance variables in classes.
- Variables defined with DATA statements.
Memory access using “handles” (^M)
A memory handle is an integer identifier that the Synergy runtime associates with a given memory segment. All future references to this memory will use this handle.
A memory handle is either volatile (dynamic) or static. Volatile memory handles remain valid until one of the following is true:
- The entire program is exited.
- The activation level of the routine in which the memory handle was generated is exited.
- The memory handle is explicitly released.
Static memory handles are “persistent” and remain valid until one of the following is true:
- The entire program is exited.
- The memory handle is explicitly released.
A memory handle is essentially an index into an access array that is maintained by the runtime. A positive value is a one-based index into the static handle array, while a negative value is a negative one-based index into the volatile handle array. Any given memory handle can be stored in whatever numeric Synergy DBL field can represent that handle’s value, although we strongly recommend an aligned i4 field, like this:
.align long record handle1 ,i4 ;The handle for one segment handle2 ,i4 ;The handle for another segment
Note that we refer to operations in terms of memory handles, not memory (for example, “generate a memory handle,” “release a memory handle,” and so forth). We do so because, even though all memory handles have a memory segment associated with them, different operations can occur for a given memory handle function depending on the class of its associated memory. For example, releasing a memory handle associated with a Synergy DBL window’s user data set does not “free” its user data set’s memory.