* Allocators and memory reclamation @ 2008-01-28 13:49 Maciej Sobczak 2008-01-28 14:52 ` gpriv ` (4 more replies) 0 siblings, 5 replies; 15+ messages in thread From: Maciej Sobczak @ 2008-01-28 13:49 UTC (permalink / raw) Hi, Consider the following: procedure Foo is type Int_Ptr is access Integer; P : Int_Ptr; begin P := new Integer; P := new Integer; P := new Integer; end Foo; procedure Main is begin loop Foo; end loop; end Main; In Foo above, three objects of type Integer are allocated and the storage is taken from the storage pool associated with the Int_Ptr access type. What I understood before is that this storage pool is torn down when the access type itself goes out of scope and it is also when the memory is reclaimed. As a result no memory is leaked in the above code - I can call Foo as many times as I want without any risk of running out of memory. This is at least what I can observe with controlled types. The problem is that my understanding conflicts with what I've just found in AARM (13.11): "By default, the implementation might choose to have a single global storage pool, which is used (by default) by all access types, which might mean that storage is reclaimed automatically only upon partition completion." This means that the implementation might turn the above well-behaving procedure into a memory leak. Is this correct? Can I influence this behaviour to portably ensure that memory is reclaimed when the access type goes out of scope? Another question relates to the order of finalizing objects. If the storage pool is torn down when the access type goes out of scope, is the order of finalizing objects guaranteed? -- Maciej Sobczak * www.msobczak.com * www.inspirel.com ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Allocators and memory reclamation 2008-01-28 13:49 Allocators and memory reclamation Maciej Sobczak @ 2008-01-28 14:52 ` gpriv 2008-01-28 14:53 ` Lucretia ` (3 subsequent siblings) 4 siblings, 0 replies; 15+ messages in thread From: gpriv @ 2008-01-28 14:52 UTC (permalink / raw) On Jan 28, 8:49 am, Maciej Sobczak <see.my.homep...@gmail.com> wrote: > Hi, > > Consider the following: > > procedure Foo is > type Int_Ptr is access Integer; > P : Int_Ptr; > begin > P := new Integer; > P := new Integer; > P := new Integer; > end Foo; > > procedure Main is > begin > loop > Foo; > end loop; > end Main; > > In Foo above, three objects of type Integer are allocated and the > storage is taken from the storage pool associated with the Int_Ptr > access type. > What I understood before is that this storage pool is torn down when > the access type itself goes out of scope and it is also when the > memory is reclaimed. As a result no memory is leaked in the above code > - I can call Foo as many times as I want without any risk of running > out of memory. This is at least what I can observe with controlled > types. Your understanding is incorrect. By default Ada provides deterministic deallocation only similar to C++, and your program eventually will crash running out of memory. You may define your own behavior by deriving from Root_Storage_Pool and assign your own pool implementation for particular objects. > > The problem is that my understanding conflicts with what I've just > found in AARM (13.11): > "By default, the implementation might choose to have a single global > storage pool, which is used (by default) by all access types, which > might mean that storage is reclaimed automatically only upon partition > completion." > > This means that the implementation might turn the above well-behaving > procedure into a memory leak. Is this correct? I would say it is a stretch to say that program above is well- behaving. What would be bad-behaving then? > Can I influence this behaviour to portably ensure that memory is > reclaimed when the access type goes out of scope? > > Another question relates to the order of finalizing objects. If the > storage pool is torn down when the access type goes out of scope, is > the order of finalizing objects guaranteed? that will be irrelevant since default deallocation is deterministic. You may implement any behavior if use your own pool. > > -- > Maciej Sobczak *www.msobczak.com*www.inspirel.com ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Allocators and memory reclamation 2008-01-28 13:49 Allocators and memory reclamation Maciej Sobczak 2008-01-28 14:52 ` gpriv @ 2008-01-28 14:53 ` Lucretia 2008-01-28 16:00 ` gpriv 2008-01-28 15:15 ` Dmitry A. Kazakov ` (2 subsequent siblings) 4 siblings, 1 reply; 15+ messages in thread From: Lucretia @ 2008-01-28 14:53 UTC (permalink / raw) On Jan 28, 1:49 pm, Maciej Sobczak <see.my.homep...@gmail.com> wrote: > "By default, the implementation might choose to have a single global > storage pool, which is used (by default) by all access types, which > might mean that storage is reclaimed automatically only upon partition > completion." I didn't realise this either. Luke. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Allocators and memory reclamation 2008-01-28 14:53 ` Lucretia @ 2008-01-28 16:00 ` gpriv 2008-01-28 22:46 ` Alex R. Mosteo 0 siblings, 1 reply; 15+ messages in thread From: gpriv @ 2008-01-28 16:00 UTC (permalink / raw) On Jan 28, 9:53 am, Lucretia <lucret...@lycos.co.uk> wrote: > On Jan 28, 1:49 pm, Maciej Sobczak <see.my.homep...@gmail.com> wrote: > > > "By default, the implementation might choose to have a single global > > storage pool, which is used (by default) by all access types, which > > might mean that storage is reclaimed automatically only upon partition > > completion." > > I didn't realise this either. > > Luke. I think what they mean here is as one allocates memory it will be requested on the system level. This memory might be only released when all objects are properly deallocated from the entire pool. However it seems to be recommendation ("might") and is not what I see with GNAT. I see memory footprint growing and shrinking as it runs and it is never deallocating all dynamic objects at any time. George ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Allocators and memory reclamation 2008-01-28 16:00 ` gpriv @ 2008-01-28 22:46 ` Alex R. Mosteo 0 siblings, 0 replies; 15+ messages in thread From: Alex R. Mosteo @ 2008-01-28 22:46 UTC (permalink / raw) gpriv@axonx.com wrote: > On Jan 28, 9:53 am, Lucretia <lucret...@lycos.co.uk> wrote: >> On Jan 28, 1:49 pm, Maciej Sobczak <see.my.homep...@gmail.com> wrote: >> >> > "By default, the implementation might choose to have a single global >> > storage pool, which is used (by default) by all access types, which >> > might mean that storage is reclaimed automatically only upon partition >> > completion." >> >> I didn't realise this either. >> >> Luke. > > I think what they mean here is as one allocates memory it will be > requested on the system level. This memory might be only released when > all objects are properly deallocated from the entire pool. However it > seems to be recommendation ("might") and is not what I see with GNAT. > I see memory footprint growing and shrinking as it runs and it is > never deallocating all dynamic objects at any time. GNAT 2007 (at least) ships with two utility storage pools; one which is the global one and other for local allocations that I guess is closer to the behavior Maciej expected: with System.Pool_Local; declare Pool : System.Unbounded_Reclaim_Pool; type IA is access integer; for IA'Storage_Pool use Pool; begin -- Allocate like crazy; end; -- Storage is reclaimed here. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Allocators and memory reclamation 2008-01-28 13:49 Allocators and memory reclamation Maciej Sobczak 2008-01-28 14:52 ` gpriv 2008-01-28 14:53 ` Lucretia @ 2008-01-28 15:15 ` Dmitry A. Kazakov 2008-01-28 22:27 ` Maciej Sobczak 2008-01-28 23:07 ` Randy Brukardt 2008-01-28 22:00 ` Aurele 2008-01-29 0:41 ` Robert A Duff 4 siblings, 2 replies; 15+ messages in thread From: Dmitry A. Kazakov @ 2008-01-28 15:15 UTC (permalink / raw) On Mon, 28 Jan 2008 05:49:35 -0800 (PST), Maciej Sobczak wrote: > The problem is that my understanding conflicts with what I've just > found in AARM (13.11): > "By default, the implementation might choose to have a single global > storage pool, which is used (by default) by all access types, which > might mean that storage is reclaimed automatically only upon partition > completion." > > This means that the implementation might turn the above well-behaving > procedure into a memory leak. Is this correct? It does not leak for Deallocate is called. (It would be difficult to formalize "leaking" otherwise than "for some Allocate there was no Deallocate called.") The language does not prescribe the effect of a call to Deallocate on the program's environment. Note that it might be impossible to return once allocated system memory back to OS. So taking it naively any program would leak. > Can I influence this behaviour to portably ensure that memory is > reclaimed when the access type goes out of scope? Write your own pool, which takes its memory from the standard pool or else statically. > Another question relates to the order of finalizing objects. If the > storage pool is torn down when the access type goes out of scope, is > the order of finalizing objects guaranteed? AFAIK, it is not. Why should it be? -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Allocators and memory reclamation 2008-01-28 15:15 ` Dmitry A. Kazakov @ 2008-01-28 22:27 ` Maciej Sobczak 2008-01-28 23:54 ` Adam Beneschan 2008-01-29 9:38 ` Dmitry A. Kazakov 2008-01-28 23:07 ` Randy Brukardt 1 sibling, 2 replies; 15+ messages in thread From: Maciej Sobczak @ 2008-01-28 22:27 UTC (permalink / raw) On 28 Sty, 16:15, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > The language does not prescribe the effect of a call to Deallocate on the > program's environment. I'm not interested in the interaction on this level. I'm interested in the pattern of calls to Deallocate in the given program. My understanding, initially, was that Deallocate is called kind of implicitly when the storage pool is torn down together with its associated access type. It does not seem to be the case, but the storage pool itself can keep track of the allocated blocks and at the end do "something" with them. For example, the storage pool might be based on static memory block that just gives new addresses for each Allocate and have empty Deallocate - a fairly compliant implementation. When the pool goes out of scope, so does its memory. I understood that the memory that was used by the pool is deallocated even in the following simple case: type Integer_Ptr is access Integer; but the AARM claims it can be otherwise (that the memory might be bound to library-level pool). I understand that I can create my own pool with any behaviour I need. Let's try: with Ada.Text_IO; with Ada.Finalization; with System.Storage_Pools; with System.Storage_Elements; procedure Main is use System.Storage_Pools; -- used to acquire reference to the standard storage pool type Integer_Ptr is access Integer; procedure Test is type My_Type is new Ada.Finalization.Controlled with null record; procedure Finalize (T : in out My_Type) is begin Ada.Text_IO.Put_Line ("Finalize"); end Finalize; use System; type My_Pool_Type is new Root_Storage_Pool with null record; procedure Allocate (Pool : in out My_Pool_Type; Storage_Address : out Address; Size_In_Storage_Elements : in Storage_Elements.Storage_Count; Alignment : in Storage_Elements.Storage_Count); procedure Deallocate (Pool : in out My_Pool_Type; Storage_Address : in Address; Size_In_Storage_Elements : in Storage_Elements.Storage_Count; Alignment : in Storage_Elements.Storage_Count); function Storage_Size (Pool : in My_Pool_Type) return Storage_Elements.Storage_Count; procedure Allocate (Pool : in out My_Pool_Type; Storage_Address : out Address; Size_In_Storage_Elements : in Storage_Elements.Storage_Count; Alignment : in Storage_Elements.Storage_Count) is begin Allocate (Integer_Ptr'Storage_Pool, Storage_Address, Size_In_Storage_Elements, Alignment); Ada.Text_IO.Put_Line ("allocating " & Storage_Elements.Storage_Count'Image (Size_In_Storage_Elements)); end Allocate; procedure Deallocate (Pool : in out My_Pool_Type; Storage_Address : in Address; Size_In_Storage_Elements : in Storage_Elements.Storage_Count; Alignment : in Storage_Elements.Storage_Count) is begin Deallocate (Integer_Ptr'Storage_Pool, Storage_Address, Size_In_Storage_Elements, Alignment); Ada.Text_IO.Put_Line ("deallocating"); end Deallocate; function Storage_Size (Pool : in My_Pool_Type) return Storage_Elements.Storage_Count is begin return Storage_Size (Integer_Ptr'Storage_Pool); end Storage_Size; My_Pool : My_Pool_Type; type My_Type_Ptr is access My_Type; for My_Type_Ptr'Storage_Pool use My_Pool; P : My_Type_Ptr; begin Ada.Text_IO.Put_Line ("starting to allocate objects"); P := new My_Type; P := new My_Type; Ada.Text_IO.Put_Line ("finished with objects and leaving the scope"); end Test; begin Ada.Text_IO.Put_Line ("Running test"); Test; Ada.Text_IO.Put_Line ("Test finished"); end Main; (the code has lengthy lines and can get broken in your readers) In the above example I tried to create my own pool that is backed up by the standard pool acquired from some dummy access type. The new pool just forwards all primitive operations to the standard pool and prints some messages to see what's going on. It looks like I got it correctly, because it compiles and even works: Running Test Starting To Allocate Objects Allocating 12 Allocating 12 Finished With Objects And Leaving The Scope Finalize Finalize Test Finished I understand (now) that two objects are allocated with the help of my own pool. Since nobody deallocated explicitly (no Unchecked_Deallocation was used), the Deallocate procedure was not called and the memory is effectively leaked. I could have remembered the allocated addresses and deallocate the dangling blocks in the finalizer of my own pool (it is controlled after all) and this way the pool would guard the memory and prevent the leak. It didn't, so effectively there is a leak. More precisely, the allocated blocks come from the standard pool and they will be deallocated when the whole program will finish. The interesting part is that finalizers of My_Type were called and this is even before the pool gets finalized itself (I've checked it by overriding Finalize for My_Pool_Type). This is good, because after that only raw memory remained to be reclaimed. The question is - who called the finalizers of allocated objects? Any AARM paragraphs for this would be welcome. > > Another question relates to the order of finalizing objects. If the > > storage pool is torn down when the access type goes out of scope, is > > the order of finalizing objects guaranteed? > > AFAIK, it is not. Why should it be? Why not? :-) If not, then there is additional (automatic) question: how can I make it guaranteed? Since finalizers seem to be called by some magic, then I don't think there is any way for me to influence the behaviour here - at least not by providing my own pool, which does not even get a chance to have anything to say on this subject. There is a related problem which I've found while playing with the above code example: I cannot have a pointer to the standard pool. In other words, this: type Pool_Ptr is access Root_Storage_Pool'Class; PP : Pool_Ptr := Integer'Storage_Pool'Access; is not possible, because the standard storage pool is not aliased. How can I get a reference to the standard pool so that I don't have to repeat the "Integer'Storage_Pool" million times? What about this: P : Root_Storage_Pool'Class := Integer_Ptr'Storage_Pool; I expect that P will be a reference to the standard pool, not its copy (it cannot be a copy, because Root_Storage_Pool is limited). Is this reasoning correct? Last but not least: does AARM 13.11/20 mean that the above program is incorrect? How to implement it correctly, then? -- Maciej Sobczak * www.msobczak.com * www.inspirel.com ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Allocators and memory reclamation 2008-01-28 22:27 ` Maciej Sobczak @ 2008-01-28 23:54 ` Adam Beneschan 2008-01-29 9:38 ` Dmitry A. Kazakov 1 sibling, 0 replies; 15+ messages in thread From: Adam Beneschan @ 2008-01-28 23:54 UTC (permalink / raw) On Jan 28, 2:27 pm, Maciej Sobczak <see.my.homep...@gmail.com> wrote: > I understand that I can create my own pool with any behaviour I need. > Let's try: > > with Ada.Text_IO; > with Ada.Finalization; > with System.Storage_Pools; > with System.Storage_Elements; > > procedure Main is > > use System.Storage_Pools; > > -- used to acquire reference to the standard storage pool > type Integer_Ptr is access Integer; > > procedure Test is > > type My_Type is new Ada.Finalization.Controlled with null > record; > > procedure Finalize (T : in out My_Type) is > begin > Ada.Text_IO.Put_Line ("Finalize"); > end Finalize; > > use System; > > type My_Pool_Type is new Root_Storage_Pool with null record; > > procedure Allocate (Pool : in out My_Pool_Type; > Storage_Address : out Address; > Size_In_Storage_Elements : in > Storage_Elements.Storage_Count; > Alignment : in > Storage_Elements.Storage_Count); > procedure Deallocate (Pool : in out My_Pool_Type; > Storage_Address : in Address; > Size_In_Storage_Elements : in > Storage_Elements.Storage_Count; > Alignment : in > Storage_Elements.Storage_Count); > function Storage_Size (Pool : in My_Pool_Type) return > Storage_Elements.Storage_Count; > > procedure Allocate (Pool : in out My_Pool_Type; > Storage_Address : out Address; > Size_In_Storage_Elements : in > Storage_Elements.Storage_Count; > Alignment : in > Storage_Elements.Storage_Count) is > begin > Allocate (Integer_Ptr'Storage_Pool, Storage_Address, > Size_In_Storage_Elements, Alignment); > Ada.Text_IO.Put_Line ("allocating " & > Storage_Elements.Storage_Count'Image > (Size_In_Storage_Elements)); > end Allocate; > > procedure Deallocate (Pool : in out My_Pool_Type; > Storage_Address : in Address; > Size_In_Storage_Elements : in > Storage_Elements.Storage_Count; > Alignment : in > Storage_Elements.Storage_Count) is > begin > Deallocate (Integer_Ptr'Storage_Pool, Storage_Address, > Size_In_Storage_Elements, Alignment); > Ada.Text_IO.Put_Line ("deallocating"); > end Deallocate; > > function Storage_Size (Pool : in My_Pool_Type) return > Storage_Elements.Storage_Count is > begin > return Storage_Size (Integer_Ptr'Storage_Pool); > end Storage_Size; > > My_Pool : My_Pool_Type; > > type My_Type_Ptr is access My_Type; > for My_Type_Ptr'Storage_Pool use My_Pool; > > P : My_Type_Ptr; > > begin > Ada.Text_IO.Put_Line ("starting to allocate objects"); > P := new My_Type; > P := new My_Type; > Ada.Text_IO.Put_Line ("finished with objects and leaving the > scope"); > end Test; > > begin > Ada.Text_IO.Put_Line ("Running test"); > Test; > Ada.Text_IO.Put_Line ("Test finished"); > end Main; > > (the code has lengthy lines and can get broken in your readers) > > In the above example I tried to create my own pool that is backed up > by the standard pool acquired from some dummy access type. The new > pool just forwards all primitive operations to the standard pool and > prints some messages to see what's going on. > It looks like I got it correctly, because it compiles and even works: > > Running Test > Starting To Allocate Objects > Allocating 12 > Allocating 12 > Finished With Objects And Leaving The Scope > Finalize > Finalize > Test Finished > > I understand (now) that two objects are allocated with the help of my > own pool. Since nobody deallocated explicitly (no > Unchecked_Deallocation was used), the Deallocate procedure was not > called and the memory is effectively leaked. I could have remembered > the allocated addresses and deallocate the dangling blocks in the > finalizer of my own pool (it is controlled after all) and this way the > pool would guard the memory and prevent the leak. It didn't, so > effectively there is a leak. > More precisely, the allocated blocks come from the standard pool and > they will be deallocated when the whole program will finish. > > The interesting part is that finalizers of My_Type were called and > this is even before the pool gets finalized itself (I've checked it by > overriding Finalize for My_Pool_Type). This is good, because after > that only raw memory remained to be reclaimed. > The question is - who called the finalizers of allocated objects? Any > AARM paragraphs for this would be welcome. As Randy pointed out, 7.6.1(11) is the key; it discusses when objects created by an <allocator> are finalized. It also says that those objects are finalized in an arbitrary order. > > > > Another question relates to the order of finalizing objects. If the > > > storage pool is torn down when the access type goes out of scope, is > > > the order of finalizing objects guaranteed? > > > AFAIK, it is not. Why should it be? > > Why not? :-) > > If not, then there is additional (automatic) question: how can I make > it guaranteed? Since finalizers seem to be called by some magic, then > I don't think there is any way for me to influence the behaviour here > - at least not by providing my own pool, which does not even get a > chance to have anything to say on this subject. I'm not completely clear on what you are trying to accomplish. Are you trying to ensure that if you allocate a number of objects of a controlled type, then when those objects are finalized, you want them to be finalized in a particular order, for example in the reverse order in which you allocated them? If this is what you want, you'll need to invent your own solution, but I think it's doable. I can envision an implementation where all the allocated records of a type are linked together in a list; then when one of these is finalized according to 7.6.1(11), the Finalize routine recognizes this and finalizes *all* of the objects in the linked list, perhaps setting a flag in the records to indicate that the finalization took place. Then when the other objects are finalized according to 7.6.1(11), the Finalize routine would recognize that the finalization has already been done, and do nothing. If you do this, you can certainly control the order in which allocated objects are finalized. But I think something like this has to be done. There's nothing in the language that I know of to assert any control over the order in which allocated objects are finalized. > > There is a related problem which I've found while playing with the > above code example: I cannot have a pointer to the standard pool. > In other words, this: > > type Pool_Ptr is access Root_Storage_Pool'Class; > PP : Pool_Ptr := Integer'Storage_Pool'Access; > > is not possible, because the standard storage pool is not aliased. > How can I get a reference to the standard pool so that I don't have to > repeat the "Integer'Storage_Pool" million times? > What about this: > > P : Root_Storage_Pool'Class := Integer_Ptr'Storage_Pool; > > I expect that P will be a reference to the standard pool, not its copy > (it cannot be a copy, because Root_Storage_Pool is limited). Is this > reasoning correct? No. Declaring an object of a limited type does *not* create a reference; it creates an object. Here, the compiler will think you're trying to make a copy of a limited object, and it will complain. Try "renames". > Last but not least: does AARM 13.11/20 mean that the above program is > incorrect? How to implement it correctly, then? You're probably OK in this particular case, since you're simply passing through the arguments that the compiler is passing to another Deallocate. But the potential for trouble is that when the implementation generates code for an <allocator> or for a call to an Unchecked_Deallocation instance, it may do other things besides call Allocate or Deallocate, and a direct call to Allocate or Deallocate doesn't do those things. It seems unlikely to me that, for an implementation, those "other things" would do anything that involves the storage pool, which means that this pass-through usage should be fine. But I could be wrong. -- Adam ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Allocators and memory reclamation 2008-01-28 22:27 ` Maciej Sobczak 2008-01-28 23:54 ` Adam Beneschan @ 2008-01-29 9:38 ` Dmitry A. Kazakov 1 sibling, 0 replies; 15+ messages in thread From: Dmitry A. Kazakov @ 2008-01-29 9:38 UTC (permalink / raw) On Mon, 28 Jan 2008 14:27:23 -0800 (PST), Maciej Sobczak wrote: > I'm interested in the pattern of calls to Deallocate in the given > program. > My understanding, initially, was that Deallocate is called kind of > implicitly when the storage pool is torn down together with its > associated access type. > It does not seem to be the case, but the storage pool itself can keep > track of the allocated blocks and at the end do "something" with them. In my understanding Deallocate is just not called. One can enforce this behavior when there is GC by applying the pragma Controlled, but not the reverse. > The interesting part is that finalizers of My_Type were called and > this is even before the pool gets finalized itself (I've checked it by > overriding Finalize for My_Pool_Type). This is good, because after > that only raw memory remained to be reclaimed. > The question is - who called the finalizers of allocated objects? Any > AARM paragraphs for this would be welcome. There is a linked list of controlled objects allocated in the access type scope. I don't think this is "good". The language should be consistent in its choices, if the design choice was to leak memory, then it finalization must leak as well! (:-)) >>> Another question relates to the order of finalizing objects. If the >>> storage pool is torn down when the access type goes out of scope, is >>> the order of finalizing objects guaranteed? >> >> AFAIK, it is not. Why should it be? > > Why not? :-) > > If not, then there is additional (automatic) question: how can I make > it guaranteed? Again, what for? (:-)) A properly designed program should never rely on such stuff. I mean either there should be a GC with the properties defined, and BTW, no GC usually tells anything about not only in which order but also when and if something gets collected; or else it should do absolutely nothing. > There is a related problem which I've found while playing with the > above code example: I cannot have a pointer to the standard pool. > In other words, this: > > type Pool_Ptr is access Root_Storage_Pool'Class; > PP : Pool_Ptr := Integer'Storage_Pool'Access; > > is not possible, because the standard storage pool is not aliased. > How can I get a reference to the standard pool so that I don't have to > repeat the "Integer'Storage_Pool" million times? > > What about this: > > P : Root_Storage_Pool'Class := Integer_Ptr'Storage_Pool; type Integer_Ptr is access Integer; P : Root_Storage_Pool'Class renames Integer_Ptr'Storage_Pool; -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Allocators and memory reclamation 2008-01-28 15:15 ` Dmitry A. Kazakov 2008-01-28 22:27 ` Maciej Sobczak @ 2008-01-28 23:07 ` Randy Brukardt 1 sibling, 0 replies; 15+ messages in thread From: Randy Brukardt @ 2008-01-28 23:07 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:lneim0ua86uj$.5q4owyndn3kj$.dlg@40tude.net... > On Mon, 28 Jan 2008 05:49:35 -0800 (PST), Maciej Sobczak wrote: ... > > Another question relates to the order of finalizing objects. If the > > storage pool is torn down when the access type goes out of scope, is > > the order of finalizing objects guaranteed? > > AFAIK, it is not. Why should it be? I have no idea what Dmitry is talking about. The language does require finalization of objects when the owning access type goes out of scope. (Another good reason to avoid conversions between access types!) In particular, read 7.6.1(11/2) [which defines "finalization of the collection".] Note that there is no requirement of an order on the particular allocators, only that they all get finalized at the point of the type declaration (in reverse, of course). Randy. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Allocators and memory reclamation 2008-01-28 13:49 Allocators and memory reclamation Maciej Sobczak ` (2 preceding siblings ...) 2008-01-28 15:15 ` Dmitry A. Kazakov @ 2008-01-28 22:00 ` Aurele 2008-01-29 0:41 ` Robert A Duff 4 siblings, 0 replies; 15+ messages in thread From: Aurele @ 2008-01-28 22:00 UTC (permalink / raw) On Jan 28, 8:49 am, Maciej Sobczak <see.my.homep...@gmail.com> wrote: > > procedure Foo is > type Int_Ptr is access Integer; > P : Int_Ptr; > begin > P := new Integer; > P := new Integer; > P := new Integer; > end Foo; > > procedure Main is > begin > loop > Foo; > end loop; > end Main; > To avoid memory leak, rewrite as... with Ada.Unchecked_Deallocation; type Int_Ptr is access Integer; procedure Free is new Ada.Unchecked_Deallocation( Integer, Int_Ptr ); procedure Foo is P : Int_Ptr; : begin P := new Integer; : Free( P ); : end Foo; procedure Main is begin loop Foo; end loop; end Main; ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Allocators and memory reclamation 2008-01-28 13:49 Allocators and memory reclamation Maciej Sobczak ` (3 preceding siblings ...) 2008-01-28 22:00 ` Aurele @ 2008-01-29 0:41 ` Robert A Duff 2008-01-29 11:12 ` Maciej Sobczak 4 siblings, 1 reply; 15+ messages in thread From: Robert A Duff @ 2008-01-29 0:41 UTC (permalink / raw) Maciej Sobczak <see.my.homepage@gmail.com> writes: > Hi, > > Consider the following: > > procedure Foo is > type Int_Ptr is access Integer; > P : Int_Ptr; > begin > P := new Integer; > P := new Integer; > P := new Integer; > end Foo; > > procedure Main is > begin > loop > Foo; > end loop; > end Main; > > In Foo above, three objects of type Integer are allocated and the > storage is taken from the storage pool associated with the Int_Ptr > access type. > What I understood before is that this storage pool is torn down when > the access type itself goes out of scope and it is also when the > memory is reclaimed. As a result no memory is leaked in the above code > - I can call Foo as many times as I want without any risk of running > out of memory. Some implementations might behave that way, but you should not rely on it. >...This is at least what I can observe with controlled > types. I don't understand what you mean about controlled types. There are none in the above example, and I don't know what example you have in mind. > The problem is that my understanding conflicts with what I've just > found in AARM (13.11): > "By default, the implementation might choose to have a single global > storage pool, which is used (by default) by all access types, which > might mean that storage is reclaimed automatically only upon partition > completion." The implementation is allowed to use a local pool for Int_Ptr, and reclaim the memory, but it is not required to. I think most implementations use a global pool by default. That's certainly the case with GNAT. To force the reclamation, you can say: for Int_Ptr'Storage_Size use 1000; -- or some number or: for Int_Ptr'Storage_Pool use My_Pool; where My_Pool gets reclaimed somehow (that's up to you). My_Pool might allocate its data on the stack, so it will be reclaimed. Or it might allocate its data on the global heap, and have a Finalize that reclaims it. Or some of each. I think implementations should not do such reclamation by default -- a global heap is a better default way to do things. > This means that the implementation might turn the above well-behaving > procedure into a memory leak. Is this correct? Well, yeah, but that's a strange way to put it. The above procedure is not well behaved, unless you are relying on some non-portable implementation-depdendent behavior. > Can I influence this behaviour to portably ensure that memory is > reclaimed when the access type goes out of scope? Yes. > Another question relates to the order of finalizing objects. If the > storage pool is torn down when the access type goes out of scope, is > the order of finalizing objects guaranteed? Finalization of heap objects happens when you do Unchecked_Deallocation. In addition, when an access type goes out of scope, all remaining objects (the ones you did not Unchecked_Deallocate) are finalized in an arbitrary order, but this does not free the memory. Finalization of heap objects has nothing to do with storage pools. It has to do with where the access type is declared, not when its pool is finalized. In implementation terms, each finalizable heap object is chained onto a doubly-linked list. U_D removes the object from the list, finalizes it, and frees the memory. When the access type goes out of scope, the implementation walks down the list and finalizes everything on it. There is one list per access type. Many variations of this scheme are possible, of course. - Bob ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Allocators and memory reclamation 2008-01-29 0:41 ` Robert A Duff @ 2008-01-29 11:12 ` Maciej Sobczak 0 siblings, 0 replies; 15+ messages in thread From: Maciej Sobczak @ 2008-01-29 11:12 UTC (permalink / raw) On 29 Sty, 01:41, Robert A Duff <bobd...@shell01.TheWorld.com> wrote: > Finalization of heap objects happens when you do > Unchecked_Deallocation. In addition, when an access type goes > out of scope, all remaining objects (the ones you did not > Unchecked_Deallocate) are finalized in an arbitrary order, > but this does not free the memory. [...] > In implementation terms, each finalizable heap object is > chained onto a doubly-linked list. U_D removes the object > from the list, finalizes it, and frees the memory. > When the access type goes out of scope, the implementation > walks down the list and finalizes everything on it. [...] This, and the replies from Adam and Dmitry give me the complete picture of how it works. Thanks for the explanation. -- Maciej Sobczak * www.msobczak.com * www.inspirel.com ^ permalink raw reply [flat|nested] 15+ messages in thread
* Allocators and memory reclamation @ 2008-01-29 11:06 Grein, Christoph (Fa. ESG) 2008-01-29 12:50 ` Dmitry A. Kazakov 0 siblings, 1 reply; 15+ messages in thread From: Grein, Christoph (Fa. ESG) @ 2008-01-29 11:06 UTC (permalink / raw) To: comp.lang.ada Dmitry wrote: > In my understanding Deallocate is just not called. One can enforce this > behavior when there is GC by applying the pragma Controlled, but not the > reverse. No, just the reverse, pragma Controlled prevents automatic garbage collection, see RM 13.11.3(1). Eurocopter Deutschland GmbH Sitz der Gesellschaft/Registered Office: Donauwoerth Registergericht/Registration Court: Amtsgericht Augsburg HRB 16508 Vorsitzender des Aufsichtsrates/Chairman of the Supervisory Board: Dr. Lutz Bertling Geschaeftsfuehrung/Board of Management: Dr. Wolfgang Schoder, Vorsitzender/CEO; Friedrich-Wilhelm Hormel; Ralf Barnscheidt CONFIDENTIALITY NOTICE This communication and the information it contains is intended for the addressee(s) named above and for no other persons or organizations. It is confidential and may be legally privileged and protected by law. The unauthorized use, copying or disclosure of this communication or any part of it is prohibited and may be unlawful. If you have received this communication in error, kindly notify us by return e-mail and discard and/or delete the communication. Thank you very much. It is possible for e-mails to be intercepted or affected by viruses. Whilst we maintain virus checks on our e-mails, we accept no liability for viruses or other material which might be introduced with this message. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Allocators and memory reclamation 2008-01-29 11:06 Grein, Christoph (Fa. ESG) @ 2008-01-29 12:50 ` Dmitry A. Kazakov 0 siblings, 0 replies; 15+ messages in thread From: Dmitry A. Kazakov @ 2008-01-29 12:50 UTC (permalink / raw) On Tue, 29 Jan 2008 12:06:19 +0100, Grein, Christoph (Fa. ESG) wrote: > Dmitry wrote: >> In my understanding Deallocate is just not called. One can enforce this >> behavior when there is GC by applying the pragma Controlled, but not the >> reverse. > > No, just the reverse, pragma Controlled prevents automatic garbage > collection, see RM 13.11.3(1). Yes, that is what I meant type My_Type_Ptr is access ...; pragma Controlled (My_Type_Ptr); -- No Deallocate ever implicitly called Without the pragma it isn't called anyway, but it could be if there were GC present. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2008-01-29 12:50 UTC | newest] Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2008-01-28 13:49 Allocators and memory reclamation Maciej Sobczak 2008-01-28 14:52 ` gpriv 2008-01-28 14:53 ` Lucretia 2008-01-28 16:00 ` gpriv 2008-01-28 22:46 ` Alex R. Mosteo 2008-01-28 15:15 ` Dmitry A. Kazakov 2008-01-28 22:27 ` Maciej Sobczak 2008-01-28 23:54 ` Adam Beneschan 2008-01-29 9:38 ` Dmitry A. Kazakov 2008-01-28 23:07 ` Randy Brukardt 2008-01-28 22:00 ` Aurele 2008-01-29 0:41 ` Robert A Duff 2008-01-29 11:12 ` Maciej Sobczak -- strict thread matches above, loose matches on Subject: below -- 2008-01-29 11:06 Grein, Christoph (Fa. ESG) 2008-01-29 12:50 ` Dmitry A. Kazakov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox