Memory management Any meaningful program requires allocation and release of memory. With the complexity, size growth and performance of the size, memory management technologies become more important. D provides a variety of ways of managing memory.
The three main allocation memory of D is:
Static data, allocated within the default data segment. Stack data, allocated in the program stack. Garbage collection data, dynamically distributed on the garbage collection stack. This chapter describes the technology that uses them, and there are some advanced topics:
When a string (and array), copy real-time smoothing operation Free Links Reference Count, Explicit class instance assignment tag / release RAII (resource gain, initialization) assigning class instances on stack assigning uninited array in stack
When String (and array), copy consider the case of passing an array to a function, and may modify the value of the array and return the modified array. Because arrays are passed by reference, rather than passing values, a key issue is: Who has the contents of array? For example, a function that converts all the letters:
Char [] Toupper (char [] s) {INT i;
For (i = 0; i IF ('A' <= C && C <= 'Z') S [I] = C - (CAST (CHAR) 'A' - 'A');} Return S; Note that the S [] of the caller is also modified. This may completely violate your original intention, or worse, s [] may be in a read-only memory. If TouPper () is always replicating S [], it will consume unnecessary time and memory for a string that is already all uppercase. This solution is to be copied, which means that only when the string needs to be modified, it will be copied. Some string handle languages use it as a default behavior, but this is huge. String "abcdef" will be copied 5 times in the function. If you want to use the protocol to get the highest efficiency, you must display it in the code. The TouPper () here is rewritten to copy when using efficient ways: Char [] Toupper (char [] s) { INT CHANGED; INT I; Changed = 0; For (i = 0; i { CHAR C = S [I]; IF ('A' <= C && C <= 'Z') { IF (! Changed) {char [] r = new char [s.ley]; R [] = S; s = R; Changed = 1; } S [i] = C - (CAST (CAR) 'A' - 'A'); } } Return S; } The array processing function of the D PhoBOS runtime library implements a write-time replication protocol. Real-time real-time programming means that the program must ensure a maximum delay, or the maximum time to complete the operation. In most memory allocation schemes, including Malloc / Free and garbage collection, theoretical delay is boundless. The most reliable method of ensuring delay is to allocate all of the data for the demanding partial requirements. If there is no call to the memory, the garbage collector will not run, and the program runtime will not exceed the allowable range of the maximum delay. Smooth operation is related to real-time programming is the need for smoothing operations, that is, when the garbage collection program stops all other activities, the program will not be arbitrarily paused. An example of this program is an interactive shooting game. If the game is unregulated, although this is not a fatal error of the program, it will irritate users. There are several techniques that can be used to remove or mitigate this role: all data is pre-allocated before running those that must be smooth. Manually run garbage collection when the program is suspended. This example can be that the program has just shown a prompt for the user to input and the user has no response. This reduces discipline of running garbage when you need to smoothing code. Call gc.disable () before performing the code that needs to be smooth, call gc.enable () after it is executed. This will make GC to allocate more memory so that the opportunity to run garbage collection will be reduced. Free-chain free linked list is a good way to accelerate the type of access to frequent allocation and abandonment. Its idea is very simple - when the object is released, it does not really release them, but places them on the free linked list. When allocated, it is used directly from the free linked list. Class foo { Static foo freeelist; // free linked table Static foo allocate () {Foo f; IF (freeelist) {f = freeelist; Freelist = f.next; } Else f = new foo (); Return F; } Static void deallocate (foo f) { f.next = freeelist; Freelist = f; } Foo next; // is used by foofreelist ... } Void test () { Foo f = foo.allocate (); ... Foo.deallocate (f); } This free chain method is very efficient. If you are used in multi-threaded environments, you need to synchronize allocate () and deallocate () functions. When allocate () is allocated from the Free Lin table, the Foo constructor will not run again, so the allocation program needs to reinitialize those need to initialize members. Do not have to implement RAII for it, because it is not passed to deallocate if there is an object due to throwing anomalies, and will eventually be collected by GC. The reference count reference count requires that a count domain is kept inside each object. When there is a reference point to it, the increment reference count; when the reference to its reference disappears, the reference count counts. This object is deleted when the reference count is reduced to 0. D does not provide any automatic support for the reference count, and the reference count must be explicitly implemented by the programmer. Use member functions addRef () and release () in Win32 COM program to maintain the reference count. Explicit class instance assignment D provides a method for allocating programs and release programs for custom object instances. Normally, the object instance should be allocated on the garbage collection, which releases the useless object instance when the garbage collection program is running. For those special circumstances, you can use the New declaration and delete declaration processing. For example, using C running time library Malloc and Free: import std.c.stdlib; Import std.outofmemory; IMPORT std.gc; Class foo { NEW (uint SZ) { Void * p; P = std.c.stdlib.malloc (SZ); IF (! p) Throw new outofmemory (); GC.Addrange (p, p sz); Return P; } Delete (void * p) { IF (p) {gc.removerange (p); STD.c.stdlib.free (p); } } } The key features of New () are: new () cannot specify the return type, but the return type is defined as a void *. NEW () must return void *. If new () cannot allocate memory, it is not necessary to return null, but exceptions must be thrown. The pointer returned by New () must be aligned in the default stack. 8 is 8 in the Win32 system. When this assignment is called from the foo class, the SIZE parameter needs to be provided, and should be greater than the value passed to the FOO. If you cannot assign the required storage space, no null is returned, and an exception should be thrown. What kind of exception throws is determined by the programmer, and in the foregoing case, OutofMemory () is thrown. When the scan memory collects the root pointer to the garbage collection, the static data segment and the stack are automatically scanned, but the pile of C is not scanned. Therefore, if the allocation of foo or its derived class contains references to objects assigned by the garbage collection program, it is necessary to inform the GC in some way. You can use the gc.addrange () method to achieve this. Don't initialize memory, because the compiler will automatically set the code after calling new () to set the members of the class instance to their default values, and then run the constructor (if any). Delete () key features are: The destructor (if any) has been called the parameter P, so the data it points to it will be considered garbage. The pointer P can be NULL. If GC.AddRange () is notified, gc.removerange () must be called in the release program. If delete () is defined, you must define the corresponding new (). If you use the class-specific allocation program and the release program to allocate memory, you must be careful to avoid the appearance of memory leaks and suspension references. If an exception is involved, RAII must also be implemented to avoid memory leakage. Tag / Release Tag / Release is equivalent to assigning and releaseing memory on the stack. This creates a 'stack' in memory. The allocation of the object only needs to move the stack pointer down. The pointer is "tag", and only the stack pointer is only required to reset the stack pointer to the markup point when the entire memory area is released. IMPORT std.c.stdlib; Import std.outofmemory; Class foo { STATIC void [] buffer; Static int bufindex; Static const Int buffsize = 100; Static this () {void * p; P = malloc (bufsize); IF (! p) Throw New OutofMemory; GC.Addrange (p, p bufsize); Buffer = p [0 .. buffsize]; } static ~ this () { IF (buffer.length) { gc.removeRange (BUFFER); Free (buffer); Buffer = NULL; } } NEW (uint SZ) {void * p; P = & buffer [bufindex]; BUFINDEX = SZ; IF (BUFINDEX> Buffer.LENGTH) Throw New OutofMemory; Return P; } Delete (void * p) { Assert (0); } Static int mark () { Return bufindex; } Static Void Release (INT i) { BUFINDEX = I; } } Void test () { INT m = foo.mark (); Foo f1 = new foo; // assign foo f2 = new foo; // assignment ... Foo.release (m); // Release F1 and F2 } When assignment, buffer [] is added to the GC as a zone, so it is not necessary to complete this in the foo.new () inside the foo.new (). RAII (Resource Getting Initialization) RAII technology can be used to avoid possible memory leakage issues when explicit memory allocation and release. Add an Auto feature to such a class to solve this problem. Assigning class instances on the stack The assignment class instance on the stack can be used to assign a temporary object that needs to be released when the function exits. When the function exits because it is abnormal, special processing is not required when the stack is expanded. If the above-described operation is valid, these objects absolutely cannot have a destructive function because the destructive function of the object is not called during the above process. If you want to assign a class object with a destructor on the stack, you can use the Auto feature to declare the object. Although the current implementation does not put the object on the stack, it may do this in the future version. IMPORT std.c.stdlib; Class foo { New (uint sz, void * p) { Return P; } Delete (void * p) { Assert (0); } } Void test () { Foo f = new (std.c.stdlib.alloca (foo.classinfo.init.length) foo; ... } No need to check whether alloca () fails and throws an exception while failing, because if the stack overflows, alloca () will generate a stack overflow exception. You don't need to call gc.addrange () or gc.removerange () because the GC will automatically scan the stack. The virtual delete () function is used to ensure that the attempt to have a delete stack object will succeed. The arrays in the array d assigned to the stack are always initialized. So, the following statement: void foo () {BYTE [1024] buffer; FillBuffer; ... } Will not run as fast as you think, because you need to initialize Buffer []. If the performance analysis of the program indicates that the performance problem causes the performance problem, it can be solved by the following method: import std.c.stdlib; Void foo () {BYTE [] Buffer = (Cast (byte *) std.c.stdlib.alloca (1024)) [0 .. 1024]; FillBuffer; ... } A good D implement will identify alloca () parameters as constants, and replaced it with an uninitialized array located on the stack. This will result in the same efficiency as the stack array in C. Before using unmelted data on the stack, you need to carefully consider these warnings: