From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: 103376,cb73ffe253a5caf1 X-Google-Attributes: gid103376,public X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!news3.google.com!news.glorb.com!newscon02.news.prodigy.com!newscon06.news.prodigy.com!prodigy.net!newsfeed-00.mathworks.com!nntp.TheWorld.com!not-for-mail From: Robert A Duff Newsgroups: comp.lang.ada Subject: Re: Memory management clarification Date: 26 Jul 2005 10:17:58 -0400 Organization: The World Public Access UNIX, Brookline, MA Message-ID: References: NNTP-Posting-Host: shell01.theworld.com Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: pcls4.std.com 1122387479 9097 192.74.137.71 (26 Jul 2005 14:17:59 GMT) X-Complaints-To: abuse@TheWorld.com NNTP-Posting-Date: Tue, 26 Jul 2005 14:17:59 +0000 (UTC) User-Agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.2 Xref: g2news1.google.com comp.lang.ada:3785 Date: 2005-07-26T10:17:58-04:00 List-Id: Maciej Sobczak writes: > Hi, > > Trying to learn a bit of Ada I came across a statement that memory > allocated from the pool will be implicitly reclaimed when the acces > variable used to reference it goes out of scope. If you use the default storage pool(s), the language standard does not specify when heap memory is reclaimed. Most implementations do not reclaim any such memory unless you explicitly call Unchecked_Deallocation. If you put "for T'Storage_Size use 1_000_000;", then implementations should reclaim all memory allocated for type T when the scope of T is left. The *type* -- not individual variables of the type. This is not a very useful capability, because most access types need to be at library level, so the memory won't be reclaimed until the whole program is done. You should look up user defined storage pools. You can say: for T'Storage_Pool use My_Pool; and then you can control when memory will be reclaimed. You can reclaim all memory in My_Pool whenever you like -- but beware dangling pointers. > That's nice, but I would like to learn a bit more about the exact > mechanics of this and about the guarantees that it can provide. > > Let's say that there is some MyType definition and this: > > type MyTypeRef is access MyType; > > 1. > > declare > X : MyTypeRef; > begin > loop > X := new MyType; > end loop; -- infinite loop just for the sake of discussion > end; > > Note that X does not goes out of scope when the loop is executing. > Will the memory be reclaimed? No. >... When? What can be said about the memory > consumption of such program? Is it bounded and guaranteed? No. Unless the implementation supports garbage collection, you will eventually run out of memory. Most implementations do not support GC. >... Is it > necessarily larger than without the loop (just single allocation)? > > 2. > > loop > declare > X : MyTypeRef; > begin > X := new MyType; > end; > end loop; > > What now? Is this any different from the memory management point of view? No. X.all will never be reclaimed (on most implementations). The only difference here is that you're allocating only one object. Suppose we added a call P(X) inside the begin/end. And suppose P saves X in a global variable. The implementation cannot deallocate the memory X points to, because that would leave a dangling pointer in the global variable. A garbage collector's job is to tell whether or not that happened -- then it can reclaim all memory not reachable. But most Ada implementations don't do GC -- they just assume X.all *might* be reachable, and never reclaim it unless you do U_D. > 3. > > declare > X : MyTypeRef; > begin > X := new MyType; > X := new MyType; > X := new MyType; > X := new MyType; > -- ... > end; > > When is the memory reclaimed for each allocated object? At each > subsequent assignment? Or maybe at the end of the block? Or even > "sometime later"? Or maybe all subsequent assignments are eliminated by > compiler? Never. (Well, "never" really means "when the process exits".) > 4. > > Is it possible to associate some function with object allocated by new, > which would be called at the time (or maybe after) the object is > reclaimed? > Yes, I'm asking about destructors or finalizers. Yes. Look up "controlled types". These allow you to associate a Finalize operation with a type. For local variables, Finalize will be automatically called when the procedure is left. For heap objects, Finalize will be called when you do Unchecked_Deallocation (or, if you never do U_D, when the program is done). > 5. > > Is it possible to "overload" new for MyType so that the X := new MyType; > statement will do whatever *I* want it to do, including actual memory > allocation? If yes, is it possible to hook on memory reclamation as well? "new" does two things: allocate memory, and initialize it (explicitly or implicltly). You can't exactly "overload new", but you can use storage pools as described above -- this gives you control over the memory allocation part of "new". The initialization still happens in the normal way. > 6. > > What about reference cycles between dynamically allocated objects? > > declare > type ListNode; > type ListNodeRef is access ListNode; > type ListNode is record > SomeData : Integer; > Other : ListNodeRef; > end record; > First, Second : ListNodeRef; > begin > First := new ListNode; > First.all.SomeData := 7; > Second := new ListNode; > Second.all.SomeData := 8; > > First.all.Other := Second; > Second.all.Other := First; -- cycle > end; > > Will the memory be reclaimed? Cycles don't make any difference. - Bob