Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.forth > #26940 > unrolled thread

Reclaiming Allocated Memory

Started byDoug Hoffman <glidedog@gmail.com>
First post2013-11-25 07:41 -0500
Last post2013-12-06 17:42 +0000
Articles 20 on this page of 24 — 6 participants

Back to article view | Back to comp.lang.forth


Contents

  Reclaiming Allocated Memory Doug Hoffman <glidedog@gmail.com> - 2013-11-25 07:41 -0500
    Re: Reclaiming Allocated Memory Paul Rubin <no.email@nospam.invalid> - 2013-11-25 07:04 -0800
      Re: Reclaiming Allocated Memory albert@spenarnc.xs4all.nl (Albert van der Horst) - 2013-11-25 16:16 +0000
        Re: Reclaiming Allocated Memory Paul Rubin <no.email@nospam.invalid> - 2013-11-25 09:03 -0800
          Re: Reclaiming Allocated Memory Doug Hoffman <glidedog@gmail.com> - 2013-11-26 06:30 -0500
            Re: Reclaiming Allocated Memory AKK <akk@nospam.org> - 2013-11-26 13:07 +0100
      Re: Reclaiming Allocated Memory Doug Hoffman <glidedog@gmail.com> - 2013-11-26 05:11 -0500
    Re: Reclaiming Allocated Memory anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2013-12-04 16:46 +0000
      Re: Reclaiming Allocated Memory Doug Hoffman <glidedog@gmail.com> - 2013-12-05 06:43 -0500
        Re: Reclaiming Allocated Memory Paul Rubin <no.email@nospam.invalid> - 2013-12-05 06:30 -0800
          Re: Reclaiming Allocated Memory albert@spenarnc.xs4all.nl (Albert van der Horst) - 2013-12-05 14:37 +0000
            Re: Reclaiming Allocated Memory Paul Rubin <no.email@nospam.invalid> - 2013-12-05 07:21 -0800
          Re: Reclaiming Allocated Memory Doug Hoffman <glidedog@gmail.com> - 2013-12-06 04:57 -0500
            Re: Reclaiming Allocated Memory Paul Rubin <no.email@nospam.invalid> - 2013-12-06 07:38 -0800
              Re: Reclaiming Allocated Memory Doug Hoffman <glidedog@gmail.com> - 2013-12-07 06:14 -0500
        Re: Reclaiming Allocated Memory anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2013-12-05 18:14 +0000
          Re: Reclaiming Allocated Memory Doug Hoffman <glidedog@gmail.com> - 2013-12-06 04:58 -0500
            Re: Reclaiming Allocated Memory Bernd Paysan <bernd.paysan@gmx.de> - 2013-12-06 17:30 +0100
              Re: Reclaiming Allocated Memory Doug Hoffman <glidedog@gmail.com> - 2013-12-07 05:59 -0500
                Re: Reclaiming Allocated Memory anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2013-12-07 11:57 +0000
                  Re: Reclaiming Allocated Memory albert@spenarnc.xs4all.nl (Albert van der Horst) - 2013-12-07 17:48 +0000
                    Re: Reclaiming Allocated Memory Bernd Paysan <bernd.paysan@gmx.de> - 2013-12-07 19:15 +0100
                      Re: Reclaiming Allocated Memory anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2013-12-09 13:45 +0000
            Re: Reclaiming Allocated Memory anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2013-12-06 17:42 +0000

Page 1 of 2  [1] 2  Next page →


#26940 — Reclaiming Allocated Memory

FromDoug Hoffman <glidedog@gmail.com>
Date2013-11-25 07:41 -0500
SubjectReclaiming Allocated Memory
Message-ID<5293458b$0$296$14726298@news.sunsite.dk>
The potential problems with using allocate/resize/free are well known. 
However, while it would be great to have garbage collection (GC) the 
problems of having an acceptable Forth GC seem difficult or not possible 
to solve.

I have settled on staying with the allocate/resize/free model.  Perhaps 
analogous to type-checking, which most Forths also don't have, when 
using manual memory reclamation one simply has to be careful and to 
thoroughly test.  I find it helps greatly to use a simple tool during 
development to check for memory leaks.  It works well.  One can have a 
Forth GC or even a region based memory utility, but from what I have 
seen the use of these are not at all straightforward and add a possibly 
significant burden to the programmer.

By using a Forth object system with the above manual memory reclamation 
scheme we can have, for example, an easy to use string package that 
integrates well with the rest of Forth, does not use GC, a region-based 
memory management scheme, reference counting, or string stacks.  Note 
how objects can keep the number of stack items small.  We can keep 
RESIZE and easily test for memory leaks.  Of course the object system is 
general purpose and can be used for many other things as well.


A string example:

0 [if]
\ general object creation/destruction
 >heap ( "class-name" -- obj ) \ create object using ALLOCATE
<free ( obj -- ) \ destroy object using FREE, thus reclaiming memory

\ possible string messages
s+: ( ... n -- obj c-a u ) \ concatenate n strings
@:  ( -- c-a u ) \ leave entire string on stack
size: ( -- n ) \ size of string
[then]

: dir  ( -- c-a u ) s" my-folder/" ;
: file ( -- c-a u ) s" my-file" ;

\ Case 1) reclaim memory immediately
: (open-path) ( ... n obj -- fileid obj )
   s+: r/o open-file throw swap ;
: open-path ( ... n -- fileid obj )
   heap> string (open-path) ;

file dir s" /" 3 open-path \ uses: /my-folder/my-file
<free \ => fileid


\ Case 2) reclaim memory later
: sdir  ( -- c-a u ) s" my-sub-folder/" ;

file sdir dir s" /" 4 open-path \ uses: /my-folder/my-sub-folder/my-file
value path2 \ => fileid
...
path2 <free \ performed anytime later

-Doug

[toc] | [next] | [standalone]


#26942

FromPaul Rubin <no.email@nospam.invalid>
Date2013-11-25 07:04 -0800
Message-ID<7x1u24o749.fsf@ruckus.brouhaha.com>
In reply to#26940
Doug Hoffman <glidedog@gmail.com> writes:
> ..., while it would be great to have garbage collection
> (GC) the problems of having an acceptable Forth GC seem difficult or
> not possible to solve.

I think you mentioned trying this:

  http://www.complang.tuwien.ac.at/forth/garbage-collection.zip

What are the main problems it faces?  I haven't tried it myself, but it
looks really nice, and the method it uses was originally implemented for
C, where it has had some success.

> By using a Forth object system with the above manual memory
> reclamation scheme we can have, for example, an easy to use string
> package that integrates well with the rest of Forth,

I'm having trouble believing such a system can be easy to use, if the
object references can be freely shared around the program.  C++ has
evolved towards dealing with this by explicitly invoked automatic
reference counting (the STL shared_ptr template) and other languages do
everything through pervasive refcounting (Python) or GC.  I think Forth
and C these days simply work best in applications where there's not much
use of dynamically managed memory.

[toc] | [prev] | [next] | [standalone]


#26943

Fromalbert@spenarnc.xs4all.nl (Albert van der Horst)
Date2013-11-25 16:16 +0000
Message-ID<529377f6$0$1658$e4fe514c@dreader35.news.xs4all.nl>
In reply to#26942
In article <7x1u24o749.fsf@ruckus.brouhaha.com>,
Paul Rubin  <no.email@nospam.invalid> wrote:
>Doug Hoffman <glidedog@gmail.com> writes:
>> ..., while it would be great to have garbage collection
>> (GC) the problems of having an acceptable Forth GC seem difficult or
>> not possible to solve.
>
>I think you mentioned trying this:
>
>  http://www.complang.tuwien.ac.at/forth/garbage-collection.zip
>
>What are the main problems it faces?  I haven't tried it myself, but it
>looks really nice, and the method it uses was originally implemented for
>C, where it has had some success.
>
>> By using a Forth object system with the above manual memory
>> reclamation scheme we can have, for example, an easy to use string
>> package that integrates well with the rest of Forth,
>
>I'm having trouble believing such a system can be easy to use, if the
>object references can be freely shared around the program.  C++ has
>evolved towards dealing with this by explicitly invoked automatic
>reference counting (the STL shared_ptr template) and other languages do
>everything through pervasive refcounting (Python) or GC.  I think Forth
>and C these days simply work best in applications where there's not much
>use of dynamically managed memory.

Forth can do better than C++ IMO for a very principal reason.
C++ has this dynamic memory allocation built in. It must be there
and it must work in all C++ ever to be invented.

Forth extends the language and adapt the garbage collection for
strings to the application.

Let me try to sketch it in an Elisa program. Part of the strings are
temporary and are there to build a response. As soon as there is no
reference on the stack their storage can be collected. Then there is but
one input line buffer, and it is reused all the time.
Some strings are more permanent, such as a subject brought up by
the patient:
P: I'm sad because I wasn't nice to my mother
...
C: last time we were talking about your mother.
At some place "mother" was allocate at HERE.

The discipline about using strings is not a complicated one-size-fits-all
but it is part of the application. In the above there where three
different classes of strings.

This may also be the reason that general string packages have not
caught on in Forth. It is not Forth like to use 10 features of
a 100 feature package, then the next time use some other 10 features.
Investment in learning time which caveats are imposed by 90 non-used
features is not easily earned back.

Instead we reprogram the 10 features.

Compare this to the explanation of Elizabeth Rather about arrays.

: ARRAY CREATE CELLS ALLOT DOES> SWAP CELLS + ;

This is what is mostly needed. Now I typed this in with less effort
than needed to attach to a namespace in Java.

If I need an array of objects in my style of oo:

( ...) class object
...
endclass
5 CELLS CONSTANT |object|   \ Normally: replace 5 with a HERE trick.
: objects |object| * ;
: ARRAY CREATE objects ALLOT DOES> SWAP objects + ( ...) ;
Quite the same!

Now if I work with current objects I can fill in
   ^object !
at the dots.

Groetjes Albert
-- 
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

[toc] | [prev] | [next] | [standalone]


#26944

FromPaul Rubin <no.email@nospam.invalid>
Date2013-11-25 09:03 -0800
Message-ID<7xmwksv2f5.fsf@ruckus.brouhaha.com>
In reply to#26943
albert@spenarnc.xs4all.nl (Albert van der Horst) writes:
>>C++ ...automatic reference counting (the STL shared_ptr template)
> Forth can do better than C++ IMO for a very principal reason.
> C++ has this dynamic memory allocation built in. It must be there
> and it must work in all C++ ever to be invented.

I don't understand what you mean there.  shared_ptr is a template
defined in a library.  It's not part of the underlying C++ language.
C++ itself has an operator loading feature which means an object can
have some code that gets called when any given operation (including
assigment like a=b) gets called on the object, and a "~" operation
that gets called when an object goes out of scope.  So you can say

  int func(shared_ptr<Foo> x) {
     shared_ptr<Foo> y = x;
     ...
     otherfunc(y); ...
  }

The assignment "y = x" makes y and x point to the same object, and
increments the reference count in the object.  y gets passed to
otherfunc, which might or might not create further references to the
object.  Then func finishes, and y goes out of scope, automatically
decrementing the reference.  Or if func throws an exception, the
exception unwinding also decrements the reference.  Finally, if the
refcount goes to 0, the object is freed.  The programmer doesn't have to
track stuff at all, other than avoiding reference cycles.  It's almost
like GC.

I don't see how to do that in Forth, which doesn't have any notion of
types or scopes.  It takes a fair amount of compiler hair.

> The discipline about using strings is not a complicated one-size-fits-all
> but it is part of the application. In the above there where three
> different classes of strings.

Sure, fair enough, you can get a bit more performance by not gc'ing
everything.

> This may also be the reason that general string packages have not
> caught on in Forth.

It could also just be that Forth applications tend to not do much with
strings.

> Now if I work with current objects I can fill in
>    ^object !
> at the dots.

One of these days I'd like to figure out how to use a Forth object
package.  But, that still doesn't address the storage reclamation issue.

[toc] | [prev] | [next] | [standalone]


#26967

FromDoug Hoffman <glidedog@gmail.com>
Date2013-11-26 06:30 -0500
Message-ID<52948643$0$296$14726298@news.sunsite.dk>
In reply to#26944
On 11/25/13 12:03 PM, Paul Rubin wrote:

> One of these days I'd like to figure out how to use a Forth object
> package.

One nice thing about OOP is the concepts transcend the language.  So if 
you understand OOP in one programming language you shouldn't have much 
trouble understanding it in Forth.  Perhaps you have heard of Python? 
Here is a Python class definition followed by the equivalent definition 
written Forth:

reference:
http://www.tutorialspoint.com/python/python_classes_objects.htm

\ *** begin Python ***
class Employee:
    empCount = 0

    def __init__(self, name, salary):
       self.name = name
       self.salary = salary
       Employee.empCount += 1

    def displayCount(self):
      print "Total Employee %d" % Employee.empCount

    def displayEmployee(self):
       print "Name : ", self.name,  ", Salary: ", self.salary

emp1 = Employee("Zara", 2000)
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount
\ *** end Python ***

\ *** begin Forth (FMS) ***
\ reference: http://soton.mpeforth.com/flag/fms/index.html
\ download: FMS-SI.zip Package
:class Employee
  priv
   variable empCount  0 empCount !
  pub
   string+ name \ embedded object-as-instance-variable
   ivar salary  \ instance variable primitive

   :m init: 1 empCount +! ;m
   :m set: ( c-addr len salary -- )
      salary !  name !: ;m
   :m displayCount
      cr ." Total Employee " empCount @ . ;m
   :m displayEmployee
      cr ." Name : " name p: ." , Salary: "
      salary @ . ;m
;class

Employee emp1
s" Zara" 2000 emp1 set:
emp1 displayEmployee
\ => Name : Zara, Salary: 2000

Employee emp2
s" Manni" 5000 emp2 set:
emp2 displayEmployee
\ => Name : Manni, Salary: 5000

emp1 displayCount
\ => Total Employee 2
emp2 displayCount
\ => Total Employee 2

empCount  \ error: undefined word
name      \ error: undefined word
salary    \ error: undefined word
\ *** end Forth ***

-Doug

[toc] | [prev] | [next] | [standalone]


#26968

FromAKK <akk@nospam.org>
Date2013-11-26 13:07 +0100
Message-ID<52948eea$0$6643$9b4e6d93@newsspool2.arcor-online.net>
In reply to#26967
Thank you for these examples.

However they also show that - at least to me - OOP does not "feel right" 
in Forth. IMO the major reason is the clumsiness of handling dynamic 
strings. In standard Forth they do net even exist as data primitive.

It is no counter-argument that the given example uses one string 
parameter for creating a fixed-length dictionary entry.

Andreas



On 26.11.2013 12:30, Doug Hoffman wrote:
> On 11/25/13 12:03 PM, Paul Rubin wrote:
>
>> One of these days I'd like to figure out how to use a Forth object
>> package.
>
> One nice thing about OOP is the concepts transcend the language.  So if
> you understand OOP in one programming language you shouldn't have much
> trouble understanding it in Forth.  Perhaps you have heard of Python?
> Here is a Python class definition followed by the equivalent definition
> written Forth:
>
> reference:
> http://www.tutorialspoint.com/python/python_classes_objects.htm
>
> \ *** begin Python ***
> class Employee:
>     empCount = 0
>
>     def __init__(self, name, salary):
>        self.name = name
>        self.salary = salary
>        Employee.empCount += 1
>
>     def displayCount(self):
>       print "Total Employee %d" % Employee.empCount
>
>     def displayEmployee(self):
>        print "Name : ", self.name,  ", Salary: ", self.salary
>
> emp1 = Employee("Zara", 2000)
> emp2 = Employee("Manni", 5000)
> emp1.displayEmployee()
> emp2.displayEmployee()
> print "Total Employee %d" % Employee.empCount
> \ *** end Python ***
>
> \ *** begin Forth (FMS) ***
> \ reference: http://soton.mpeforth.com/flag/fms/index.html
> \ download: FMS-SI.zip Package
> :class Employee
>   priv
>    variable empCount  0 empCount !
>   pub
>    string+ name \ embedded object-as-instance-variable
>    ivar salary  \ instance variable primitive
>
>    :m init: 1 empCount +! ;m
>    :m set: ( c-addr len salary -- )
>       salary !  name !: ;m
>    :m displayCount
>       cr ." Total Employee " empCount @ . ;m
>    :m displayEmployee
>       cr ." Name : " name p: ." , Salary: "
>       salary @ . ;m
> ;class
>
> Employee emp1
> s" Zara" 2000 emp1 set:
> emp1 displayEmployee
> \ => Name : Zara, Salary: 2000
>
> Employee emp2
> s" Manni" 5000 emp2 set:
> emp2 displayEmployee
> \ => Name : Manni, Salary: 5000
>
> emp1 displayCount
> \ => Total Employee 2
> emp2 displayCount
> \ => Total Employee 2
>
> empCount  \ error: undefined word
> name      \ error: undefined word
> salary    \ error: undefined word
> \ *** end Forth ***
>
> -Doug

[toc] | [prev] | [next] | [standalone]


#26965

FromDoug Hoffman <glidedog@gmail.com>
Date2013-11-26 05:11 -0500
Message-ID<529473ea$0$297$14726298@news.sunsite.dk>
In reply to#26942
On 11/25/13 10:04 AM, Paul Rubin wrote:
> Doug Hoffman <glidedog@gmail.com> writes:
>> ..., while it would be great to have garbage collection
>> (GC) the problems of having an acceptable Forth GC seem difficult or
>> not possible to solve.
>
> I think you mentioned trying this:
>
>    http://www.complang.tuwien.ac.at/forth/garbage-collection.zip
>
> What are the main problems it faces?

Albert van der Horst gives a good answer.  I would add that the 
mentioned GC extension imposes important restrictions on where the 
memory reference must reside.  Memory resizing is not supported.  I 
don't mean to criticize the author's code because it is very well done, 
but there are problems inherent to designing an add-on GC for Forth.


>> By using a Forth object system with the above manual memory
>> reclamation scheme we can have, for example, an easy to use string
>> package that integrates well with the rest of Forth,
>
> I'm having trouble believing such a system can be easy to use, if the
> object references can be freely shared around the program.

There is no free lunch.  One just has to be careful.  Some think it 
would also be nice if there were type checking in Forth.  Actually there 
is with StrongForth.  But there is pain involved and type checking and 
GC have not caught on in Forth for a reason.


> I think Forth
> and C these days simply work best in applications where there's not much
> use of dynamically managed memory.

Define "not much use".  On a desktop application, such as MacForth's 
built in editor, the application can be highly dynamic.

-Doug

[toc] | [prev] | [next] | [standalone]


#27139

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2013-12-04 16:46 +0000
Message-ID<2013Dec4.174638@mips.complang.tuwien.ac.at>
In reply to#26940
Doug Hoffman <glidedog@gmail.com> writes:
>I have settled on staying with the allocate/resize/free model.  Perhaps 
>analogous to type-checking, which most Forths also don't have, when 
>using manual memory reclamation one simply has to be careful and to 
>thoroughly test.  I find it helps greatly to use a simple tool during 
>development to check for memory leaks.  It works well.

What tool do you use and how simple is it?  If you can detect a memory
leak, you can also collect garbage, so a memory leak detector is just
as complex.  If you can build a simple one, you can also build a
simple gargabe collector.

>One can have a 
>Forth GC or even a region based memory utility, but from what I have 
>seen the use of these are not at all straightforward and add a possibly 
>significant burden to the programmer.

The whole point of both garbage collection and region-based memory
allocation is to reduce the burden on the application programmer, in
particular the burden of keeping track of ALLOCATEd things, and when
they can be FREEd.

In particular, with garbage collection you ALLOCATE at will, and never
have to FREE.

Region-based allocation is between garbage collection and FREE: You
allocate things in particular regions, and then free the whole region.

>0 [if]
>\ general object creation/destruction
> >heap ( "class-name" -- obj ) \ create object using ALLOCATE
><free ( obj -- ) \ destroy object using FREE, thus reclaiming memory
>
>\ possible string messages
>s+: ( ... n -- obj c-a u ) \ concatenate n strings
>@:  ( -- c-a u ) \ leave entire string on stack
>size: ( -- n ) \ size of string
>[then]
>
>: dir  ( -- c-a u ) s" my-folder/" ;
>: file ( -- c-a u ) s" my-file" ;
>
>\ Case 1) reclaim memory immediately
>: (open-path) ( ... n obj -- fileid obj )
>   s+: r/o open-file throw swap ;
>: open-path ( ... n -- fileid obj )
>   heap> string (open-path) ;
>
>file dir s" /" 3 open-path \ uses: /my-folder/my-file
><free \ => fileid
>
>
>\ Case 2) reclaim memory later
>: sdir  ( -- c-a u ) s" my-sub-folder/" ;
>
>file sdir dir s" /" 4 open-path \ uses: /my-folder/my-sub-folder/my-file
>value path2 \ => fileid
>...
>path2 <free \ performed anytime later

I don't see any advantage from using an object here.  These examples
could be done just as easily with FREE.  I also don't see how this
makes freeing easier in other cases.  It seems to me that you have to
keep the object around until you want to free the memory, which does
not seem easier than just keeping the address to be freed around, on
the contrary: S+: returns an object in addition to the address; with
classical FREE you don't need to maintain the object.

- anton
-- 
M. Anton Ertl  http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
     New standard: http://www.forth200x.org/forth200x.html
   EuroForth 2013: http://www.euroforth.org/ef13/

[toc] | [prev] | [next] | [standalone]


#27152

FromDoug Hoffman <glidedog@gmail.com>
Date2013-12-05 06:43 -0500
Message-ID<52a066dc$0$302$14726298@news.sunsite.dk>
In reply to#27139
On 12/4/13 11:46 AM, Anton Ertl wrote:
> Doug Hoffman <glidedog@gmail.com> writes:
>> I have settled on staying with the allocate/resize/free model.  Perhaps
>> analogous to type-checking, which most Forths also don't have, when
>> using manual memory reclamation one simply has to be careful and to
>> thoroughly test.  I find it helps greatly to use a simple tool during
>> development to check for memory leaks.  It works well.
>
> What tool do you use and how simple is it?

\ *** begin mem tool
50000 constant mem-size \ choose a large enough size

create mem-list mem-size cells allot
mem-list mem-size cells erase

: list-bounds ( -- end beg ) mem-list mem-size cells +  mem-list ;

: store-ptr ( ptr -- )
   list-bounds do i @ 0= if i ! unloop exit then cell +loop
   true abort" no room left in mem-list" ;

: allocate ( n -- ptr ior )
   allocate
   2dup 0=
   if store-ptr
   else drop
   then ;

: free-ptr ( ptr -- )
   list-bounds do dup i @ = if 0 i ! drop unloop exit then
              cell +loop ;

: free ( ptr -- ior )
   dup free dup 0=
   if
     swap
     ( ior ptr ) free-ptr
   then ;

: resize-mem ( ptr2 ptr1 -- )
   list-bounds do dup i @ = if drop i ! unloop exit then
              cell +loop ;

: resize ( ptr1 n -- ptr2 ior | ptr1 ior )
   over >r
   resize
   dup 0=
   if ( ptr2 ior  )
     swap dup ( ior ptr2 ptr2 ) r@ =
        if r> drop ( ior ptr1 ) swap exit
        else
        ( ior ptr2 ) r@ ( ior ptr2 ptr1 ) over >r resize-mem r>
        ( ior ptr2)
        then
   then r> drop swap ;

: .mem 0 locals| cnt |
   list-bounds do i @ if cr cnt . i @ . cnt 1+ to cnt then
              cell +loop cr cnt . ." unFREEd pointers " ;

: clr-mem
   list-bounds do i @ if  i @ free throw 0 i ! then
              cell +loop ;
\ *** end mem tool

The tool is inefficient and only meant for use during development 
(analogous to an array index check).  Run the program and execute .MEM 
to see if all pointers have been freed.  Example:

: foo ( -- obj )
   heap> string { s }
   s" some" s !:
   s" text" s add: s
   ;

foo dup @: type \ => sometext
.mem
0 13965088
1 13734992
2 unFREEd pointers
<free .mem
0 unFREEd pointers



> If you can detect a memory
> leak, you can also collect garbage, so a memory leak detector is just
> as complex.  If you can build a simple one, you can also build a
> simple gargabe collector.

I don't think the above tool would work for that, but I could be wrong.


> The whole point of both garbage collection and region-based memory
> allocation is to reduce the burden on the application programmer, in
> particular the burden of keeping track of ALLOCATEd things, and when
> they can be FREEd.
>
> In particular, with garbage collection you ALLOCATE at will, and never
> have to FREE.

It would be wonderful if such a GC could work in Forth without extra 
effort by and restrictions for the programmer.  But from what I've seen 
the extra effort and restrictions that don't fit my programming style.


> Region-based allocation is between garbage collection and FREE: You
> allocate things in particular regions, and then free the whole region.
>
>> 0 [if]
>> \ general object creation/destruction
>>> heap ( "class-name" -- obj ) \ create object using ALLOCATE
>> <free ( obj -- ) \ destroy object using FREE, thus reclaiming memory
>>
>> \ possible string messages
>> s+: ( ... n -- obj c-a u ) \ concatenate n strings
>> @:  ( -- c-a u ) \ leave entire string on stack
>> size: ( -- n ) \ size of string
>> [then]
>>
>> : dir  ( -- c-a u ) s" my-folder/" ;
>> : file ( -- c-a u ) s" my-file" ;
>>
>> \ Case 1) reclaim memory immediately
>> : (open-path) ( ... n obj -- fileid obj )
>>    s+: r/o open-file throw swap ;
>> : open-path ( ... n -- fileid obj )
>>    heap> string (open-path) ;
>>
>> file dir s" /" 3 open-path \ uses: /my-folder/my-file
>> <free \ => fileid
>>
>>
>> \ Case 2) reclaim memory later
>> : sdir  ( -- c-a u ) s" my-sub-folder/" ;
>>
>> file sdir dir s" /" 4 open-path \ uses: /my-folder/my-sub-folder/my-file
>> value path2 \ => fileid
>> ...
>> path2 <free \ performed anytime later
>
> I don't see any advantage from using an object here.  These examples
> could be done just as easily with FREE.  I also don't see how this
> makes freeing easier in other cases.

The definition of <FREE is:

: <free  ( obj -- ) dup free: free throw ;

So the free: message is first sent to the object before the object 
itself is FREEd.  This gives the object the ability to first FREE any 
memory allocated within the object, nested to any level.  All objects 
are designed to do the right thing in response to free:.  In the foo 
example above one can see that <free resulted in two calls to FREE. 
Granted, not much saving of effort there.  But if the object were an 
array or list of allocated objects then a single <free could result in 
thousands of calls to free.

-Doug

[toc] | [prev] | [next] | [standalone]


#27156

FromPaul Rubin <no.email@nospam.invalid>
Date2013-12-05 06:30 -0800
Message-ID<7xr49rpdz0.fsf@ruckus.brouhaha.com>
In reply to#27152
Doug Hoffman <glidedog@gmail.com> writes:
> So the free: message is first sent to the object before the object
> itself is FREEd.  This gives the object the ability to first FREE any
> memory allocated within the object, nested to any level.

How does it know that it should do that, if there might be other
pointers around to the internal objects?  Also you mentioned a desire to
resize objects.  How do you do that, if the resize might relocate the
object, and there are shared pointers?  How do you go about tracking
them all?

[toc] | [prev] | [next] | [standalone]


#27158

Fromalbert@spenarnc.xs4all.nl (Albert van der Horst)
Date2013-12-05 14:37 +0000
Message-ID<52a08fae$0$4637$e4fe514c@dreader34.news.xs4all.nl>
In reply to#27156
In article <7xr49rpdz0.fsf@ruckus.brouhaha.com>,
Paul Rubin  <no.email@nospam.invalid> wrote:
>Doug Hoffman <glidedog@gmail.com> writes:
>> So the free: message is first sent to the object before the object
>> itself is FREEd.  This gives the object the ability to first FREE any
>> memory allocated within the object, nested to any level.
>
>How does it know that it should do that, if there might be other
>pointers around to the internal objects?  Also you mentioned a desire to
>resize objects.  How do you do that, if the resize might relocate the
>object, and there are shared pointers?  How do you go about tracking
>them all?

This hits the nail on the head. The main problem with garbage collection
is:"what is garbage". This may be answered in the general context of
Java programming, but in the general context of Forth it is not.
In a special context of Forth garbage, we need a special collector,
probably order of magnitude simpler than Java's.

Groetjes Albert
-- 
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

[toc] | [prev] | [next] | [standalone]


#27159

FromPaul Rubin <no.email@nospam.invalid>
Date2013-12-05 07:21 -0800
Message-ID<7xbo0vfhng.fsf@ruckus.brouhaha.com>
In reply to#27158
albert@spenarnc.xs4all.nl (Albert van der Horst) writes:
> In a special context of Forth garbage, we need a special collector,
> probably order of magnitude simpler than Java's.

Anton's implementation is probably the only practical approach for
Forth: the Boehm algorithm basically scans all reachable objects for
anything that might be a pointer to a GC'd object, and treats those
locations as non-garbage.  This is called an imprecise or conservative
collector, because it can mistake data for pointers and therefore
occasionally fail to free something that is actually garbage.  While
that may sound scary, it is indeed relatively simple, and it works
pretty well in practice.  It was originally written for C and is not
unique to Forth:

   http://www.hpl.hp.com/personal/Hans_Boehm/gc/

Java's GC (Oracle version) is very complicated and highly optimized,
works with parallel processes, etc.  Java could use a simpler GC,
including the Boehm GC, at a cost in performance.  GCJ in fact uses the
Boehm GC.

[toc] | [prev] | [next] | [standalone]


#27164

FromDoug Hoffman <glidedog@gmail.com>
Date2013-12-06 04:57 -0500
Message-ID<52a19f9f$0$303$14726298@news.sunsite.dk>
In reply to#27156
On 12/5/13 9:30 AM, Paul Rubin wrote:
> Doug Hoffman <glidedog@gmail.com> writes:
>> So the free: message is first sent to the object before the object
>> itself is FREEd.  This gives the object the ability to first FREE any
>> memory allocated within the object, nested to any level.
>
> How does it know that it should do that, if there might be other
> pointers around to the internal objects?

The programmer is responsible for keeping track of things.

> Also you mentioned a desire to
> resize objects.   How do you do that, if the resize might relocate the
> object, and there are shared pointers?

I don't do something that will corrupt an allocated memory pointer.

> How do you go about tracking them all?

The programmer is responsible for keeping track of things, just like the 
programmer is responsible for keeping track of everything else.

Maybe it depends on the complexity and type of program.  If one is 
writing something that is highly dynamic and in a style where memory 
pointers can become so intertwined that keeping track is too difficult, 
then a language other than Forth should be used.  I don't write such 
programs.

-Doug

[toc] | [prev] | [next] | [standalone]


#27166

FromPaul Rubin <no.email@nospam.invalid>
Date2013-12-06 07:38 -0800
Message-ID<7xmwkekn01.fsf@ruckus.brouhaha.com>
In reply to#27164
Doug Hoffman <glidedog@gmail.com> writes:
>>> This gives the object the ability to first FREE any
>>> memory allocated within the object, nested to any level.
>> How does it know that it should do that,
> The programmer is responsible for keeping track of things.

You mean when you free an object, you have to tell the object exactly
which of its internal objects it should also free, and so on
recursively?  That sounds painful.

>> if the resize might relocate the object,
> I don't do something that will corrupt an allocated memory pointer.

I don't understand what resizing is supposed to do then, unless it's a
pure ALLOT-like allocator and you haven't allocated any further objects
between the original allocation and the resize.

> If one is writing something that is highly dynamic and in a style
> where memory pointers can become so intertwined that keeping track is
> too difficult, then a language other than Forth should be used.

I haven't tried Anton's GC but it really does sound pretty workable,
especially with some implementation-specific hacks to deal with stuff
like scanning the locals.  It even be worth writing something into the
next standard about it.

[toc] | [prev] | [next] | [standalone]


#27171

FromDoug Hoffman <glidedog@gmail.com>
Date2013-12-07 06:14 -0500
Message-ID<52a30333$0$302$14726298@news.sunsite.dk>
In reply to#27166
On 12/6/13 10:38 AM, Paul Rubin wrote:
> Doug Hoffman <glidedog@gmail.com> writes:
>>>> This gives the object the ability to first FREE any
>>>> memory allocated within the object, nested to any level.
>>> How does it know that it should do that,
>> The programmer is responsible for keeping track of things.
>
> You mean when you free an object, you have to tell the object exactly
> which of its internal objects it should also free, and so on
> recursively?  That sounds painful.

I suppose one could design an object that works that way but I wouldn't 
recommend it.  I've always designed objects such that the correct 
FREEing behavior is built in in response to a <free.


>>> if the resize might relocate the object,
>> I don't do something that will corrupt an allocated memory pointer.
>
> I don't understand what resizing is supposed to do then,

I think we have miss-communicated somewhere because I don't follow what 
you aren't understanding.


>> If one is writing something that is highly dynamic and in a style
>> where memory pointers can become so intertwined that keeping track is
>> too difficult, then a language other than Forth should be used.
>
> I haven't tried Anton's GC but it really does sound pretty workable,
> especially with some implementation-specific hacks to deal with stuff
> like scanning the locals.  It even be worth writing something into the
> next standard about it.

Anyone can submit an Rfd.

-Doug

[toc] | [prev] | [next] | [standalone]


#27160

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2013-12-05 18:14 +0000
Message-ID<2013Dec5.191410@mips.complang.tuwien.ac.at>
In reply to#27152
Doug Hoffman <glidedog@gmail.com> writes:
>On 12/4/13 11:46 AM, Anton Ertl wrote:
>> Doug Hoffman <glidedog@gmail.com> writes:
>>> I find it helps greatly to use a simple tool during
>>> development to check for memory leaks.  It works well.
>>
>> What tool do you use and how simple is it?
[code snipped]
>The tool is inefficient and only meant for use during development 
>(analogous to an array index check).  Run the program and execute .MEM 
>to see if all pointers have been freed.  Example:
>
>: foo ( -- obj )
>   heap> string { s }
>   s" some" s !:
>   s" text" s add: s
>   ;
>
>foo dup @: type \ => sometext
>.mem
>0 13965088
>1 13734992
>2 unFREEd pointers
><free .mem
>0 unFREEd pointers

Ok, so this just lists all the unfreed memory, and you would use it at
the end of the program.  But right at the end of the program it just
costs (sometimes dearly, see below) to free memory, so you should not
free the memory reported by that tool, unless it should have been
freed earlier.  Unfortunately the use of such tools encourages
programmers to free everything at the end.

As for costing dearly: Mozilla used to consume huge amounts of memory
with some usage patterns, much of which was swapped out to disk.  When
I wanted to quit Mozilla, it took many minutes of disk activity
(making the rest of the computer sluggish) before it finally exited.
Apparently it went through all that memory it had allocated and freed
it (and paged it in in the process) and only then it exited (which
unmaps all the memory automatically without paging it in).

>> The whole point of both garbage collection and region-based memory
>> allocation is to reduce the burden on the application programmer, in
>> particular the burden of keeping track of ALLOCATEd things, and when
>> they can be FREEd.
>>
>> In particular, with garbage collection you ALLOCATE at will, and never
>> have to FREE.
>
>It would be wonderful if such a GC could work in Forth without extra 
>effort by and restrictions for the programmer.  But from what I've seen 
>the extra effort and restrictions that don't fit my programming style.

Yes, there are some restrictions on the way you keep the addresses of
allocated memory:

a) Such addresses must not reside (exclusively) on the return stack or
in a local.  That's because the garbage collector is a standard
program, and standard programs cannot access all of the return stack
and all locals.  With system-specific changes this restriction can be
lifted.

b) Such addresses in the dictionary must reside in places declared as
root-addresses.  Again, there is no standard way to access all of the
dictionary, but with system-specific changes one could use all of the
dictionary as potential roots; besides being non-standard, this would
lead to longer garbage collection times and possibly to more garbage
being kept around (from spurious pointers).

c) In the allocated memory the addresses must reside at naturally
aligned boundaries.  That's also a portability requirement (there is
hardware that requires this), but one could make it configurable (so
one could also treat unaligned cells as potential addresses on
hardware that supports this).  Dropping this requirement would again
slow the garbage collector down and possibly increase the garbage that
is kept around.

d) An allocated object is only kept alive by a pointer to the start of
the object.  This requirement could be dropped, again at a cost in
garbage collection time and at a potential increase in garbage that is
kept around.

Which of these restrictions don't fit your programming style?

>> I don't see any advantage from using an object here.  These examples
>> could be done just as easily with FREE.  I also don't see how this
>> makes freeing easier in other cases.
>
>The definition of <FREE is:
>
>: <free  ( obj -- ) dup free: free throw ;
>
>So the free: message is first sent to the object before the object 
>itself is FREEd.  This gives the object the ability to first FREE any 
>memory allocated within the object, nested to any level.  All objects 
>are designed to do the right thing in response to free:.  In the foo 
>example above one can see that <free resulted in two calls to FREE. 
>Granted, not much saving of effort there.  But if the object were an 
>array or list of allocated objects then a single <free could result in 
>thousands of calls to free.

Ok, so you are doing region-based allocation here, but use an object
as a region handle.

- anton
-- 
M. Anton Ertl  http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
     New standard: http://www.forth200x.org/forth200x.html
   EuroForth 2013: http://www.euroforth.org/ef13/

[toc] | [prev] | [next] | [standalone]


#27165

FromDoug Hoffman <glidedog@gmail.com>
Date2013-12-06 04:58 -0500
Message-ID<52a19fc6$0$302$14726298@news.sunsite.dk>
In reply to#27160
On 12/5/13 1:14 PM, Anton Ertl wrote:
> Doug Hoffman <glidedog@gmail.com> writes:

> Unfortunately the use of such tools encourages
> programmers to free everything at the end.

Agreed.  Probably one should test and if that is a problem redesign so 
more freeing is done sooner if possible.


>> It would be wonderful if such a GC could work in Forth without extra
>> effort by and restrictions for the programmer.  But from what I've seen
>> the extra effort and restrictions don't fit my programming style.
>
> Yes, there are some restrictions on the way you keep the addresses of
> allocated memory:
>
> a) Such addresses must not reside (exclusively) on the return stack or
> in a local.  That's because the garbage collector is a standard
> program, and standard programs cannot access all of the return stack
> and all locals.  With system-specific changes this restriction can be
> lifted.
>
> b) Such addresses in the dictionary must reside in places declared as
> root-addresses.  Again, there is no standard way to access all of the
> dictionary, but with system-specific changes one could use all of the
> dictionary as potential roots; besides being non-standard, this would
> lead to longer garbage collection times and possibly to more garbage
> being kept around (from spurious pointers).
>
> c) In the allocated memory the addresses must reside at naturally
> aligned boundaries.  That's also a portability requirement (there is
> hardware that requires this), but one could make it configurable (so
> one could also treat unaligned cells as potential addresses on
> hardware that supports this).  Dropping this requirement would again
> slow the garbage collector down and possibly increase the garbage that
> is kept around.
>
> d) An allocated object is only kept alive by a pointer to the start of
> the object.  This requirement could be dropped, again at a cost in
> garbage collection time and at a potential increase in garbage that is
> kept around.

e) RESIZE may not be not available without even more effort (extra 
indirection?  manual copying of contents from old to new?).


> Which of these restrictions don't fit your programming style?

a), b), and e) would be a problem for me.


>> : <free  ( obj -- ) dup free: free throw ;

> Ok, so you are doing region-based allocation here, but use an object
> as a region handle.

Yes.  Except a true region-based scheme has the advantage of fast 
freeing compared to the above even after a large number of allocations, 
if I understand correctly.  The lack of a resize may be a disadvantage.

-Doug

[toc] | [prev] | [next] | [standalone]


#27167

FromBernd Paysan <bernd.paysan@gmx.de>
Date2013-12-06 17:30 +0100
Message-ID<l7su3a$jv7$1@online.de>
In reply to#27165
Doug Hoffman wrote:
> e) RESIZE may not be not available without even more effort (extra
> indirection?  manual copying of contents from old to new?).

In a simple allocater like the one for region based memory, you can shrink 
all objects in place, and to grow them, you need to copy them to a new 
location, unless they are the last allocated object (in which case growing 
them is almost for free).

Of course if you want to access objects that can resize, you need a pointer 
for that, and you need to adjust that pointer with the return value of 
resize (the new location).

-- 
Bernd Paysan
"If you want it done right, you have to do it yourself"
http://bernd-paysan.de/

[toc] | [prev] | [next] | [standalone]


#27170

FromDoug Hoffman <glidedog@gmail.com>
Date2013-12-07 05:59 -0500
Message-ID<52a2ff77$0$302$14726298@news.sunsite.dk>
In reply to#27167
On 12/6/13 11:30 AM, Bernd Paysan wrote:
> Doug Hoffman wrote:
>> e) RESIZE may not be not available without even more effort (extra
>> indirection?  manual copying of contents from old to new?).
>
> In a simple allocater like the one for region based memory, you can shrink
> all objects in place, and to grow them, you need to copy them to a new
> location, unless they are the last allocated object (in which case growing
> them is almost for free).

That last point is a good one.  Thanks.

> Of course if you want to access objects that can resize, you need a pointer
> for that, and you need to adjust that pointer with the return value of
> resize (the new location).

Yes.  That's the way a pointer object (class PTR) works in the provided 
FMS example classes.

-Doug

[toc] | [prev] | [next] | [standalone]


#27172

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2013-12-07 11:57 +0000
Message-ID<2013Dec7.125713@mips.complang.tuwien.ac.at>
In reply to#27170
Doug Hoffman <glidedog@gmail.com> writes:
>On 12/6/13 11:30 AM, Bernd Paysan wrote:
>> In a simple allocater like the one for region based memory, you can shrink
>> all objects in place, and to grow them, you need to copy them to a new
>> location, unless they are the last allocated object (in which case growing
>> them is almost for free).
>
>That last point is a good one.  Thanks.

Yes, if you RESIZE the last allocated thing, and the result fits in
the current block, then it's cheap.  But with my current
implementation, every other RESIZE, even if it shrinks, is expensive:
I don't know the old size of the resized thing, so I always allocate
new memory, and the amount of memory I copy there is usually bigger
(possibly much bigger) than the old size.

I have been thinking about a different implementation for resized
stuff, but if RESIZE is not going to be used much in combination with
region-based memory allocation, the benefit is not worth the effort.
That kind of implementation would put each resized thing in its own
block, plus some meta-data.

- anton
-- 
M. Anton Ertl  http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
     New standard: http://www.forth200x.org/forth200x.html
   EuroForth 2013: http://www.euroforth.org/ef13/

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.forth


csiph-web