Discussion:
[Gc] GC needs to be invoked manually
Martin Wartens
2005-08-08 22:46:43 UTC
Permalink
Hello,
in my program the GC refuses to collect anything which causes the program
to use unlimited amounts of memory. I can "fix" this only by placing a
forced GC_gcollect in the inner loop of the program. I am using the 6.5
version with C++, VC7.1, WinXP. All my classes derive from gc_cleanup. I
have already learned today that it is impossible to have pointers in
built-in arrays, so I removed them all. I have pointers in various standard
containers, and I remembered to give the traceable_allocator to all of them
[but this most likely solves only the opposite problem, a premature
collection]. Now I have no clue what can be wrong, and I don't know for
which error pattern I have to look. Help please.
Thanks,
Martin
___________________________________________________________________________
mymail - der unschlagbare und kostenlose E-Mail-Dienst der Schweiz!
http://mymail.ch/?redirect=9999
Bei uns finden Sie trendige Geschenkideen und Wohnaccessoires!
http://mymail.ch/?redirect=1610
Martin Wartens
2005-08-11 20:48:46 UTC
Permalink
I was able to narrow down the problem to the code given below. Running it gives
Elements created: 20001
Elements destroyed: 0
I still can't see what could be wrong with it.

#include "gc_allocator.h"
#include "gc_cpp.h"
#include <iostream>
using namespace std;

struct Element;
//row of sparse matrix, pointers to elements keyed by unsigned
typedef std::map<unsigned, Element*, std::less<unsigned>,
traceable_allocator< std::pair<const unsigned, Element*> > > Row_T;
//sparse matrix, pointers to rows keyed by unsigned
typedef std::map<unsigned, Row_T, std::less<unsigned>,
traceable_allocator< std::pair<const unsigned, Row_T> > > Matrix_T;

//each element holds a sparse matrix of elements,
//this is a tree-like data structure
struct Element: public gc_cleanup{
static unsigned created;
static unsigned destroyed;
Matrix_T mat;
Element(){
++created;
}
Element(const Element& n){
++created;
}
~Element()
{
cout<<"delete"<<endl;
++destroyed;
}
};
unsigned Element::created(0);
unsigned Element::destroyed(0);

void testgc(){
Element* someElement = new Element();
for (unsigned i=0; i<10000; i++)
{
Element *n1 = new Element();
Element *n2 = new Element();

n2->mat[1][1] = n1;
n2->mat[2][2] = n1;
n2->mat[3][3] = someElement;
n2->mat[4][4] = someElement;
}
cout << "Elements created: " << Element::created << endl;
cout << "Elements destroyed: " << Element::destroyed << endl;
}

int main()
{
testgc();
}
Larry Evans
2005-08-13 22:02:48 UTC
Permalink
Post by Martin Wartens
I was able to narrow down the problem to the code given below. Running it gives
[snip]
Post by Martin Wartens
struct Element;
//row of sparse matrix, pointers to elements keyed by unsigned
typedef std::map<unsigned, Element*, std::less<unsigned>,
traceable_allocator< std::pair<const unsigned, Element*> > > Row_T;
//sparse matrix, pointers to rows keyed by unsigned
typedef std::map<unsigned, Row_T, std::less<unsigned>,
traceable_allocator< std::pair<const unsigned, Row_T> > > Matrix_T;
//each element holds a sparse matrix of elements,
//this is a tree-like data structure
^^^^^^^^^
Post by Martin Wartens
struct Element: public gc_cleanup{
[snip]
Post by Martin Wartens
Matrix_T mat;
[snip]
Post by Martin Wartens
};
[snip]
Post by Martin Wartens
void testgc(){
Element* someElement = new Element();
for (unsigned i=0; i<10000; i++)
{
Element *n1 = new Element();
Element *n2 = new Element();
n2->mat[1][1] = n1;
n2->mat[2][2] = n1;
n2->mat[3][3] = someElement;
n2->mat[4][4] = someElement;
Don't the above 2 assignments make the data structure a graph instead of
a tree?
Post by Martin Wartens
}
[snip]
I vaguely recall something about the collector not being able to
collect cycles in nodes with finalizers because it cannot know
in which order to call the finalizers. I can't even remember
whether finalizer is the correct term either. Anyway, by finalizer,
I mean something that cleans up the node, somewhat like a c++ DTOR.

HTH.
Martin Wartens
2005-08-15 11:39:26 UTC
Permalink
Thanks for looking at my program. But it really is a tree structure. Sorry for
the bad names in the code, I should have replaced them to make it more clear.
To be on the safe side, I tested my program with cycle collection on - no
effect. [cycle collection can be turned on, I believe, by replacing
GC_register_finalizer_ignore_self by GC_register_finalizer_no_order in
gc_cpp.h. I think this should be on by default or at least be selectable by a
compile switch].
Somebody else suggested to derive from gc instead of gc_cleanup, but that has
no effect either. I also tried to overwrite global new/delete by the gc-
versions. I also tried all the flavors of threaded/non-threaded static/shared
libraries. No success.
There must be something evil in the example code (or something extremely
stupid). The bug is quite breakable, meaning that you can make the example work
by changing a few things. Meanwhile I replaced all the suspicious data
structures in my actual program, but I still would like to know what caused
this problem to avoid it in the future.
Greetings, Martin
Larry Evans
2005-08-15 17:39:50 UTC
Permalink
Post by Martin Wartens
I was able to narrow down the problem to the code given below. Running it gives
Elements created: 20001
Elements destroyed: 0
[snip]
Post by Martin Wartens
void testgc(){
Element* someElement = new Element();
for (unsigned i=0; i<10000; i++)
[snip]
Post by Martin Wartens
cout << "Elements created: " << Element::created << endl;
cout << "Elements destroyed: " << Element::destroyed << endl;
Nothing's destroyed yet because there's still a root pointer,
someElement, on the stack at this point.
Post by Martin Wartens
}
int main()
{
testgc();
Try adding an explicit call to the collector here, then move the 2 cout
calls in testgc to after the collector call.
Post by Martin Wartens
}
HTH.
Martin Wartens
2005-08-15 21:24:43 UTC
Permalink
Post by Larry Evans
Nothing's destroyed yet because there's still a root pointer,
someElement, on the stack at this point.
That someElement is still alive shouldn't prevent the collection of n1 and n2.
someElement is a shared "leaf" from the standpoint of the tree structure, so it
shouldnt't prevent the collection of elements that point to it. Also moving the
creation of someElement into the loop doesn't change the problem.
Post by Larry Evans
Try adding an explicit call to the collector here, then move the 2 cout
calls in testgc to after the collector call.
I did that, the result is: with the first call to GC_gcollect(), all the n2s
are collected. n1 and someElement are only collected with a second call to
GC_gcollect().
Best, Martin
Larry Evans
2005-08-15 22:16:36 UTC
Permalink
Post by Martin Wartens
Post by Larry Evans
Nothing's destroyed yet because there's still a root pointer,
someElement, on the stack at this point.
That someElement is still alive shouldn't prevent the collection of n1 and n2.
someElement is a shared "leaf" from the standpoint of the tree structure, so it
OOPS. My mistake. Somehow, because I'd written test code which had
someElement as the root, I mistakenly thought you'd done the same. But
now I can see that someElement indeed is a "leaf".

Sorry for not reading your code more carefully :(
Martin Wartens
2005-08-26 20:56:50 UTC
Permalink
Meanwhile I discovered that I have to replace "traceable_allocator"
with "gc_allocator" to make the sample program work. traceable_allocator uses
MALLOC_UNCOLLECTABLE internally. There seems to be some problem with
MALLOC_UNCOLLECTABLE. The test program below exposes this problem. There is a
collectable class Element which creates a dynamic array on initialization. If
MALLOC_UNCOLLECTABLE is used for allocating the array, no Element object is
ever collected. This behavior also depends on the size of the array. Collection
fails if the array has more than 26 entries (=104 bytes).


#include <iostream>
using namespace std;
#include "gc_cpp.h"

struct Element: public gc_cleanup
{
static unsigned created;
static unsigned destroyed;
int* dynarray;
Element(unsigned size)
{
/*this does not work*/
dynarray = (int*) GC_MALLOC_UNCOLLECTABLE( size * sizeof(int) );

/*the following three variants work*/
//dynarray = (int*) GC_MALLOC_ATOMIC( size * sizeof(int) );
//dynarray = (int*) malloc( size * sizeof(int) );
//dynarray = new int[size];

for(unsigned i=0; i<size; i++)
{
dynarray[i] = 0;
}
++created;
}

~Element()
{
/*call appropriate free here*/
//delete[] dynarray;
//free(dynarray);
//GC_FREE(dynarray);
++destroyed;
}
private:
Element(const Element& n){ }
};
unsigned Element::created(0);
unsigned Element::destroyed(0);

//last working size with MALLOC_UNCOLLECTABLE is 26
#define SIZE 27
void testgc(){
for (unsigned i=0; i<20000; i++)
{
Element *root = new Element(SIZE);
}
cout << "Elements created: " << Element::created << endl;
cout << "Elements destroyed: " << Element::destroyed << endl;
}

int main(int argc,char **argv)
{
testgc();
}
Larry Evans
2005-08-28 20:04:43 UTC
Permalink
Post by Martin Wartens
Meanwhile I discovered that I have to replace "traceable_allocator"
with "gc_allocator" to make the sample program work. traceable_allocator uses
[snip]
Post by Martin Wartens
Post by Martin Wartens
ever collected. This behavior also depends on the size of the array. Collection
fails if the array has more than 26 entries (=104 bytes).
The code at:

http://cvs.sourceforge.net/viewcvs.py/boost-sandbox/boost-sandbox/libs/policy_ptr/test/wartens_sparse_matrix_test.cpp?rev=1.1&view=auto

uses reference counting, but that's just one gc policy. It's fairly
easy to convert to just use a single boolean mark or bit instead of
a reference count to save memory and time to do the refcount updates.

The code also demonstrates that cycles are no problem with refcounting
if it's combined with the backup marksweep of [chri84] cited here:

http://www.cs.kent.ac.uk/people/staff/rej/gcbib/gcbibC.html

I'd be happy to add this extra gc policy if you're interested.

HTH.

-regards,
Larry
Martin Wartens
2005-08-29 22:12:34 UTC
Permalink
Post by Larry Evans
http://cvs.sourceforge.net/viewcvs.py/boost-sandbox/boost-
sandbox/libs/policy_ptr/test/wartens_sparse_matrix_test.cpp?rev=1.1&view=auto
Post by Larry Evans
uses reference counting, but that's just one gc policy. It's fairly
easy to convert to just use a single boolean mark or bit instead of
a reference count to save memory and time to do the refcount updates.
Sounds like an interesting idea. So this is going to be some kind of garbage-
collecting smart pointer? Some random thoughts about this:
In the past I measured the timings of Boehm GC versus new/delete versus
boost::shared_ptr. It turned out that the gc can create a speedup of up to 2 vs
new/delete. This, I guess, most likely comes from the bundled deallocations
that it does, which seems to be very effective for small objects. So maybe you
should integrate the allocation/deallocation into the ptr.
The penalty for reference counting is usually small, but I have seen situations
where it really hurts. This happens when there are many long-lived objects with
short-lived references. Then, the reference counter does a lot of work for
nothing. And reference counting uses only a small amount of memory, but
sometimes even that is to much. So if you can do without reference counting,
that is clearly better.
If I remember right, there exists an exotic problem with cycle collection, when
the destructor of an collected object tries to follow pointers (which may be
already dead at this time). I don't recollect the details, but it could be a
potential headache.
One fundamental problem of smart ptrs seems to be that they don't see
references to an object, while the Boehm gc can handle both references and
pointers to an object. I don't know if there are more problems like that. It
seems the Boehm gc uses a lot of voodoo that can't be imitated using only
language features.

Anyway, the policy_ptrs look like an interesting project, keep on going!
Best, Martin
Larry Evans
2005-08-30 10:16:37 UTC
Permalink
Post by Larry Evans
Post by Larry Evans
http://cvs.sourceforge.net/viewcvs.py/boost-sandbox/boost-
sandbox/libs/policy_ptr/test/wartens_sparse_matrix_test.cpp?rev=1.1&view=auto
Post by Larry Evans
uses reference counting, but that's just one gc policy. It's fairly
[snip]
Post by Larry Evans
Sounds like an interesting idea. So this is going to be some kind of garbage-
collecting smart pointer?
Yes.
[snip]
Post by Larry Evans
should integrate the allocation/deallocation into the ptr.
Well, I've thought about substituting the pointer descriptor
( GC_descr) method described here:

http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gc_typedh.txt

with the method used in the policy_ptr code. This method is a
modification of [detl92]:

http://www.cs.kent.ac.uk/people/staff/rej/gcbib/gcbibD.html

which finds pointers in containers as well. The fields_visitor
library implements this method (actually a generalization of the
melthod since it can be applied to any field type, not just
smart_ptrs):

http://cvs.sourceforge.net/viewcvs.py/boost-sandbox/boost-sandbox/boost/fields_visitor/

If fields_visitor replaced GC_descr, then, of course, the problems with
refcounts would disappear since the resulting code, except for the
method for finding pointers, would be boehmgc.
[snip]
Post by Larry Evans
If I remember right, there exists an exotic problem with cycle collection, when
the destructor of an collected object tries to follow pointers (which may be
already dead at this time). I don't recollect the details, but it could be a
potential headache.
I think I know what you're refering to. The solution imposed by the
policy_ptr code is to 1st traverse the pointer graph determining
any cycles of objects to be collected, and then breaking the cycle
(by simply nulling the pointer which closes the cycle) at some
arbitrary point. It's maybe not what the programmer of the
pointer graph intended, but he obviously did not intend to have
a cycle either, because otherwise he would have used a weak_ptr
to close the cycle.
Post by Larry Evans
One fundamental problem of smart ptrs seems to be that they don't see
references to an object, while the Boehm gc can handle both references and
pointers to an object. I don't know if there are more problems like that. It
Yep. I don't know a work-around for that, but I'm guessing the boost
shared_ptr people have considered that and haven't been too bothered by
it.

[snip]
Post by Larry Evans
Anyway, the policy_ptrs look like an interesting project, keep on going!
Thanks. I'm now stuck on learning how to xml document fields_visitor :(
Post by Larry Evans
Best, Martin
Regards, Larry.
Boehm, Hans
2005-08-30 21:53:37 UTC
Permalink
I'm sorry I didn't have a chance to look at this thread until now.

Traceable_allocator and GC_MALLOC_UNCOLLECTABLE allocate memory that
is traced for pointers by the collector (i.e. such objects are
viewed as always reachable, and anything they point to is also
reachable.) Hence I think the behavior you saw earlier is the expected
behavior.

I don't see the behavior you describe below. But I do see that many
objects
do not have their destructor called. If I insert the GC_FREE call,
nearly all objects do have their destructors called. This is also
expected,
but for more complicated reasons:

- The program below, as written, leaks memory. Lots of it, since the
arrays
are not collectable. Hence the heap grows substantially and without
bound.
There is nothing the collector can do about that, since it was asked not
to
collect this memory.

- The collector tries to trigger a GC every N bytes, where N is
proportional
to the heap or live data size. Reasonable garbage collectors generally
need to do that, since otherwise the amount of GC time spent per byte
allocated gets larger and larger as the heap grows. Which is bad.

- Since the heap is growing without bound, collections are triggered
less
and less frequently. Hence the expected number of objects that were
allocated since the last GC gets larger and larger. These won't
have their destructors run when the program finishes unless you insert
an explicit GC_gcollect() call.

- It may be that in your environment no collections take place between
the program starts and the time it exits. Set the GC_PRINT_STATS
environment variable to verify.

Hans
-----Original Message-----
Sent: Friday, August 26, 2005 1:57 PM
Subject: [Gc] Re: GC needs to be invoked manually
Meanwhile I discovered that I have to replace "traceable_allocator"
with "gc_allocator" to make the sample program work.
traceable_allocator uses
MALLOC_UNCOLLECTABLE internally. There seems to be some problem with
MALLOC_UNCOLLECTABLE. The test program below exposes this
problem. There is a
collectable class Element which creates a dynamic array on
initialization. If
MALLOC_UNCOLLECTABLE is used for allocating the array, no
Element object is
ever collected. This behavior also depends on the size of the
array. Collection
fails if the array has more than 26 entries (=104 bytes).
#include <iostream>
using namespace std;
#include "gc_cpp.h"
struct Element: public gc_cleanup
{
static unsigned created;
static unsigned destroyed;
int* dynarray;
Element(unsigned size)
{
/*this does not work*/
dynarray = (int*) GC_MALLOC_UNCOLLECTABLE( size
* sizeof(int) );
/*the following three variants work*/
//dynarray = (int*) GC_MALLOC_ATOMIC( size *
sizeof(int) );
//dynarray = (int*) malloc( size * sizeof(int) );
//dynarray = new int[size];
for(unsigned i=0; i<size; i++)
{
dynarray[i] = 0;
}
++created;
}
~Element()
{
/*call appropriate free here*/
//delete[] dynarray;
//free(dynarray);
//GC_FREE(dynarray);
++destroyed;
}
Element(const Element& n){ }
};
unsigned Element::created(0);
unsigned Element::destroyed(0);
//last working size with MALLOC_UNCOLLECTABLE is 26
#define SIZE 27
void testgc(){
for (unsigned i=0; i<20000; i++)
{
Element *root = new Element(SIZE);
}
cout << "Elements created: " << Element::created << endl;
cout << "Elements destroyed: " << Element::destroyed << endl; }
int main(int argc,char **argv)
{
testgc();
}
_______________________________________________
Gc mailing list
http://www.hpl.hp.com/hosted/linux/mail-archives/gc/
Martin Wartens
2005-08-31 11:38:06 UTC
Permalink
Post by Boehm, Hans
Traceable_allocator and GC_MALLOC_UNCOLLECTABLE allocate memory that
is traced for pointers by the collector (i.e. such objects are
viewed as always reachable, and anything they point to is also
reachable.) Hence I think the behavior you saw earlier is the expected
behavior.
No, why? I don't understand that.
Post by Boehm, Hans
I don't see the behavior you describe below. But I do see that many
objects
do not have their destructor called. If I insert the GC_FREE call,
nearly all objects do have their destructors called. This is also
expected,
Ok, I forget to comment in the GC_FREE. Just imagine the program with the
GC_FREE commented in. To be sure that I didn't post something embarrassingly
stupid, I tried this program on a linux machine. The compile line was:
g++ -g -I./include -o mygctest mygctest.cpp ./lib/libgc.a ./lib/libgccpp.a -
lpthread -ldl
I saw the same behaviour as reported before, but it turned out that the last
working array size on this machine was 56, not 26 as on the windows machine.
Please retry with a large number for the array size, and look if the
destructors still get called.
Post by Boehm, Hans
- It may be that in your environment no collections take place between
the program starts and the time it exits. Set the GC_PRINT_STATS
environment variable to verify.
I did that. The output is given below. There is only one collection indicated.
Thanks for looking at my problem.
Best regards,
Martin

Increasing heap size by 65536 after 0 allocated bytes
Initiating full world-stop collection 1 after 0 allocd bytes
--> Marking for collection 1 after 0 allocd bytes + 0 wasted bytes
Collection 0 finished ---> heapsize = 65536 bytes
World-stopped marking took 0 msecs
Complete collection took 0 msecs
Grew fo table to 1 entries
Grew fo table to 2 entries
Grew fo table to 4 entries
Grew fo table to 8 entries
Grew fo table to 16 entries
Grew fo table to 32 entries
Grew fo table to 64 entries
Grew fo table to 128 entries
Increasing heap size by 65536 after 37624 allocated bytes
Grew fo table to 256 entries
Increasing heap size by 65536 after 93448 allocated bytes
Grew fo table to 512 entries
Increasing heap size by 69632 after 166756 allocated bytes
Grew fo table to 1024 entries
Increasing heap size by 90112 after 230656 allocated bytes
Increasing heap size by 122880 after 319360 allocated bytes
Grew fo table to 2048 entries
Increasing heap size by 163840 after 436476 allocated bytes
Increasing heap size by 217088 after 598044 allocated bytes
Grew fo table to 4096 entries
Increasing heap size by 290816 after 811960 allocated bytes
Increasing heap size by 385024 after 1099192 allocated bytes
Increasing heap size by 548864 after 1476088 allocated bytes
Grew fo table to 8192 entries
Increasing heap size by 696320 after 2023636 allocated bytes
Increasing heap size by 929792 after 2712756 allocated bytes
Grew fo table to 16384 entries
Increasing heap size by 1241088 after 3635824 allocated bytes
Increasing heap size by 1654784 after 4865808 allocated bytes
Grew fo table to 32768 entries
Increasing heap size by 2203648 after 6504908 allocated bytes
Elements created: 20000
Elements destroyed: 0
Boehm, Hans
2005-08-31 20:15:08 UTC
Permalink
-----Original Message-----
From: Martin Wartens
Sent: Wednesday, August 31, 2005 4:38 AM
Subject: [Gc] Re: GC needs to be invoked manually
Post by Boehm, Hans
Traceable_allocator and GC_MALLOC_UNCOLLECTABLE allocate
memory that
Post by Boehm, Hans
is traced for pointers by the collector (i.e. such objects
are viewed
Post by Boehm, Hans
as always reachable, and anything they point to is also
reachable.) Hence I think the behavior you saw earlier is the
expected behavior.
No, why? I don't understand that.
Looking at your earlier example again, I think I may have
oversimplified.

It looks to me like you set up elements n2 which pointed to an STL
map, which point to n1. (The source as posted didn't work for me,
due to bad line wrapping of // comments, and missing includes.
I fixed it. Apologies if I got this wrong.)

The STL map uses traceable_allocator. Hence pieces of its data
structure
will be uncollectable. Those pieces point to n1, which will be
uncollectable. I agree that n2 should be collectable.

The reason the n2 pieces end up not getting collected is that the large
majority of the allocated memory is uncollectable and inside STL maps.
As a result, the collector decides it's not worthwhile to trigger
a collection, because the heap occupancy will be high even after
it finishes. Hence collections don't happen.

The heuristics used by the collector are always debatable. But in
this case I think it's actually dead on.

However, in the case you describe below, it turns out that they do
have serious problems. Basically the same thing is happening here;
most of the heap appears to be uncollectable, and hence collections
appear futile. However, if it did collect, finalizers would run, the
destructors would get executed, and a bunch of memory would be
explicitly
deallocated.

The collector actually tries to increase GC frequency once it notices
that finalization results in a lot of explicit deallocation. But
it never triggers a GC, and hence never notices.

It turns out this is already fixed in gc7.0alpha5, which uses a
more robust heuristic.

In general, this code is well outside the comfort zone for tracing
collectors. You really don't want a very large fraction of the
objects to be finalizable (e.g. by inheriting from gc_cleanup),
or reachable from finalizable objects. Making all the objects
collectable works MUCH better.

Hans
Post by Boehm, Hans
I don't see the behavior you describe below. But I do see
that many
Post by Boehm, Hans
objects do not have their destructor called. If I insert
the GC_FREE
Post by Boehm, Hans
call, nearly all objects do have their destructors called. This is
also expected,
Ok, I forget to comment in the GC_FREE. Just imagine the
program with the
GC_FREE commented in. To be sure that I didn't post something
embarrassingly
g++ -g -I./include -o mygctest mygctest.cpp ./lib/libgc.a
./lib/libgccpp.a -
lpthread -ldl
I saw the same behaviour as reported before, but it turned
out that the last
working array size on this machine was 56, not 26 as on the
windows machine.
Please retry with a large number for the array size, and look if the
destructors still get called.
Post by Boehm, Hans
- It may be that in your environment no collections take
place between
Post by Boehm, Hans
the program starts and the time it exits. Set the GC_PRINT_STATS
environment variable to verify.
I did that. The output is given below. There is only one
collection indicated.
Thanks for looking at my problem.
Best regards,
Martin
Increasing heap size by 65536 after 0 allocated bytes
Initiating full world-stop collection 1 after 0 allocd bytes
--> Marking for collection 1 after 0 allocd bytes + 0 wasted bytes
Collection 0 finished ---> heapsize = 65536 bytes
World-stopped marking took 0 msecs
Complete collection took 0 msecs
Grew fo table to 1 entries
Grew fo table to 2 entries
Grew fo table to 4 entries
Grew fo table to 8 entries
Grew fo table to 16 entries
Grew fo table to 32 entries
Grew fo table to 64 entries
Grew fo table to 128 entries
Increasing heap size by 65536 after 37624 allocated bytes
Grew fo table to 256 entries
Increasing heap size by 65536 after 93448 allocated bytes
Grew fo table to 512 entries
Increasing heap size by 69632 after 166756 allocated bytes
Grew fo table to 1024 entries
Increasing heap size by 90112 after 230656 allocated bytes
Increasing heap size by 122880 after 319360 allocated bytes
Grew fo table to 2048 entries Increasing heap size by 163840
after 436476 allocated bytes Increasing heap size by 217088
after 598044 allocated bytes Grew fo table to 4096 entries
Increasing heap size by 290816 after 811960 allocated bytes
Increasing heap size by 385024 after 1099192 allocated bytes
Increasing heap size by 548864 after 1476088 allocated bytes
Grew fo table to 8192 entries Increasing heap size by 696320
after 2023636 allocated bytes Increasing heap size by 929792
after 2712756 allocated bytes Grew fo table to 16384 entries
Increasing heap size by 1241088 after 3635824 allocated bytes
Increasing heap size by 1654784 after 4865808 allocated bytes
Grew fo table to 32768 entries Increasing heap size by
2203648 after 6504908 allocated bytes Elements created: 20000
Elements destroyed: 0
_______________________________________________
Gc mailing list
http://www.hpl.hp.com/hosted/linux/mail-archives/gc/
Martin Wartens
2005-08-31 21:44:40 UTC
Permalink
Post by Boehm, Hans
(The source as posted didn't work for me,
due to bad line wrapping of // comments, and missing includes.
I fixed it. Apologies if I got this wrong.)
Ouch, you're right, I am sorry again for posting buggy code.
Post by Boehm, Hans
The heuristics used by the collector are always debatable. But in
this case I think it's actually dead on.
I think this case is not different from the other: In both cases there is a
large amount of uncollectable memory that is only destroyed by finalizing a
collectable object.
Post by Boehm, Hans
It turns out this is already fixed in gc7.0alpha5, which uses a
more robust heuristic.
What does it do?
Post by Boehm, Hans
In general, this code is well outside the comfort zone for tracing
collectors. You really don't want a very large fraction of the
objects to be finalizable (e.g. by inheriting from gc_cleanup),
or reachable from finalizable objects. Making all the objects
collectable works MUCH better.
I agree that traceable_allocator/MALLOC_UNCOLLECTABLE is useful only in rare
situations. What is so bad about gc_cleanup? Does finalization slow down the
collection?

Thanks,
Martin
Boehm, Hans
2005-08-31 22:50:24 UTC
Permalink
-----Original Message-----
From: Martin Wartens
Post by Boehm, Hans
The heuristics used by the collector are always debatable. But in
this case I think it's actually dead on.
I think this case is not different from the other: In both
cases there is a
large amount of uncollectable memory that is only destroyed
by finalizing a
collectable object.
Loading...