Discussion:
[Gc] Strange leak in simple code
Юрий Соколов
2015-07-13 11:12:40 UTC
Permalink
Using libgc from Ubuntu 15.04 (7.2b-6.4) this simple code leaks:

#include <gc.h>
struct list {
struct list* next; };
int main() {
GC_INIT();
struct list *last = NULL;
for (;;) {
struct list* nuo = GC_MALLOC(sizeof(struct list));
nuo->next = NULL;
// if next line is commented, then no leakage
if (last) last->next = nuo;
last = nuo;
} }



stackoverflow question
http://stackoverflow.com/questions/31380641/libgc-why-this-code-leaks
Bruce Hoult
2015-07-14 21:25:17 UTC
Permalink
What do you mean "it leaks?"

You're making a linked list, with every object pointed to either by 'last'
or by the previous object.

I would be very very unhappy if this was garbage collected!!
Post by Юрий Соколов
#include <gc.h>
struct list {
struct list* next; };
int main() {
GC_INIT();
struct list *last = NULL;
for (;;) {
struct list* nuo = GC_MALLOC(sizeof(struct list));
nuo->next = NULL;
// if next line is commented, then no leakage
if (last) last->next = nuo;
last = nuo;
} }
stackoverflow question
http://stackoverflow.com/questions/31380641/libgc-why-this-code-leaks
--
This message has been scanned for viruses and
dangerous content by *MailScanner* <http://www.mailscanner.info/>, and is
believed to be clean.
_______________________________________________
bdwgc mailing list
https://lists.opendylan.org/mailman/listinfo/bdwgc
Юрий Соколов
2015-07-15 02:52:04 UTC
Permalink
Read the code carefully. This code keeps pointer only to last node. So
only two nodes are reachable at max.
What do you mean "it leaks?"

You're making a linked list, with every object pointed to either by 'last'
or by the previous object.

I would be very very unhappy if this was garbage collected!!
Post by Юрий Соколов
#include <gc.h>
struct list {
struct list* next; };
int main() {
GC_INIT();
struct list *last = NULL;
for (;;) {
struct list* nuo = GC_MALLOC(sizeof(struct list));
nuo->next = NULL;
// if next line is commented, then no leakage
if (last) last->next = nuo;
last = nuo;
} }
stackoverflow question
http://stackoverflow.com/questions/31380641/libgc-why-this-code-leaks
--
This message has been scanned for viruses and
dangerous content by *MailScanner* <http://www.mailscanner.info/>, and is
believed to be clean.
_______________________________________________
bdwgc mailing list
https://lists.opendylan.org/mailman/listinfo/bdwgc
Bruce Hoult
2015-07-15 04:00:45 UTC
Permalink
Ok, artificially tricky code, not what it first appears.

However, it is still true that you are linking all the nodes together, just
that you are not *explicitly* retaining a pointer to the first node.

All that is required for this to not be garbage collected is, for example:

1) the compiler to keep a copy of the pointer to the first node allocated
in a register or on the stack. Probably should not happen at -O0.

2) If *any* other value in memory looks like a pointer to one of your nodes
(not just the first one), then all nodes allocated after that one will be
retained.
Post by Юрий Соколов
Read the code carefully. This code keeps pointer only to last node. So
only two nodes are reachable at max.
What do you mean "it leaks?"
You're making a linked list, with every object pointed to either by 'last'
or by the previous object.
I would be very very unhappy if this was garbage collected!!
Post by Юрий Соколов
#include <gc.h>
struct list {
struct list* next; };
int main() {
GC_INIT();
struct list *last = NULL;
for (;;) {
struct list* nuo = GC_MALLOC(sizeof(struct list));
nuo->next = NULL;
// if next line is commented, then no leakage
if (last) last->next = nuo;
last = nuo;
} }
stackoverflow question
http://stackoverflow.com/questions/31380641/libgc-why-this-code-leaks
--
This message has been scanned for viruses and
dangerous content by *MailScanner* <http://www.mailscanner.info/>, and is
believed to be clean.
_______________________________________________
bdwgc mailing list
https://lists.opendylan.org/mailman/listinfo/bdwgc
--
This message has been scanned for viruses and
dangerous content by *MailScanner* <http://www.mailscanner.info/>, and is
believed to be clean.
Юрий Соколов
2015-07-15 04:06:41 UTC
Permalink
Bruce,

This code fails with libgc packaged with Debian and Ubuntu. It works with
libgc compilled from source though.

For "If *any* other value in memory looks like a pointer to one of your
nodes" - i found that setting HEAP_START as high as 0x6000_00000000 and
enabling unmap helps a lot on Linux x86_64.

With regards,
Yura
Post by Bruce Hoult
Ok, artificially tricky code, not what it first appears.
However, it is still true that you are linking all the nodes together,
just that you are not *explicitly* retaining a pointer to the first node.
1) the compiler to keep a copy of the pointer to the first node allocated
in a register or on the stack. Probably should not happen at -O0.
2) If *any* other value in memory looks like a pointer to one of your
nodes (not just the first one), then all nodes allocated after that one
will be retained.
Post by Юрий Соколов
Read the code carefully. This code keeps pointer only to last node. So
only two nodes are reachable at max.
What do you mean "it leaks?"
You're making a linked list, with every object pointed to either by
'last' or by the previous object.
I would be very very unhappy if this was garbage collected!!
Post by Юрий Соколов
#include <gc.h>
struct list {
struct list* next; };
int main() {
GC_INIT();
struct list *last = NULL;
for (;;) {
struct list* nuo = GC_MALLOC(sizeof(struct list));
nuo->next = NULL;
// if next line is commented, then no leakage
if (last) last->next = nuo;
last = nuo;
} }
stackoverflow question
http://stackoverflow.com/questions/31380641/libgc-why-this-code-leaks
--
This message has been scanned for viruses and
dangerous content by *MailScanner* <http://www.mailscanner.info/>, and is
believed to be clean.
_______________________________________________
bdwgc mailing list
https://lists.opendylan.org/mailman/listinfo/bdwgc
--
This message has been scanned for viruses and
dangerous content by *MailScanner* <http://www.mailscanner.info/>, and is
believed to be clean.
Bruce Hoult
2015-07-15 04:15:29 UTC
Permalink
You can get lucky.

However filling all of memory with a single very long linked list (which it
is, until the first items are reclaimed) remains a very dangerous thing to
do with any conservative garbage collector.

Real programs tend to create many lists, and if from time to time one of
them is accidentally retained by a false pointer then you probably won't
even notice (unless you're expecting a finalizer to be run, which is
*never* guaranteed)
Post by Юрий Соколов
Bruce,
This code fails with libgc packaged with Debian and Ubuntu. It works with
libgc compilled from source though.
For "If *any* other value in memory looks like a pointer to one of your
nodes" - i found that setting HEAP_START as high as 0x6000_00000000 and
enabling unmap helps a lot on Linux x86_64.
With regards,
Yura
Post by Bruce Hoult
Ok, artificially tricky code, not what it first appears.
However, it is still true that you are linking all the nodes together,
just that you are not *explicitly* retaining a pointer to the first node.
1) the compiler to keep a copy of the pointer to the first node allocated
in a register or on the stack. Probably should not happen at -O0.
2) If *any* other value in memory looks like a pointer to one of your
nodes (not just the first one), then all nodes allocated after that one
will be retained.
Post by Юрий Соколов
Read the code carefully. This code keeps pointer only to last node. So
only two nodes are reachable at max.
What do you mean "it leaks?"
You're making a linked list, with every object pointed to either by
'last' or by the previous object.
I would be very very unhappy if this was garbage collected!!
Post by Юрий Соколов
#include <gc.h>
struct list {
struct list* next; };
int main() {
GC_INIT();
struct list *last = NULL;
for (;;) {
struct list* nuo = GC_MALLOC(sizeof(struct list));
nuo->next = NULL;
// if next line is commented, then no leakage
if (last) last->next = nuo;
last = nuo;
} }
stackoverflow question
http://stackoverflow.com/questions/31380641/libgc-why-this-code-leaks
--
This message has been scanned for viruses and
dangerous content by *MailScanner* <http://www.mailscanner.info/>, and is
believed to be clean.
_______________________________________________
bdwgc mailing list
https://lists.opendylan.org/mailman/listinfo/bdwgc
--
This message has been scanned for viruses and
dangerous content by *MailScanner* <http://www.mailscanner.info/>, and is
believed to be clean.
--
This message has been scanned for viruses and
dangerous content by *MailScanner* <http://www.mailscanner.info/>, and is
believed to be clean.
Brian Beuning
2015-07-14 21:47:09 UTC
Permalink
Garbage is defined as unreachable objects.
All of your objects are reachable by following
the next chain. If you added some code like
this in the loop

if( i++ % 1024 == 0 ) list = NULL;

then only the last bunch would be accessible
and all previous would be garbage and collected.

Brian Beuning


----- Original Message -----
From: "Юрий Соколов" <***@gmail.com>
To: ***@lists.opendylan.org
Sent: Monday, July 13, 2015 7:12:40 AM
Subject: [Gc] Strange leak in simple code

Using libgc from Ubuntu 15.04 (7.2b-6.4) this simple code leaks:

#include <gc.h> struct list { struct list * next ; }; int main () { GC_INIT (); struct list * last = NULL ; for (;;) { struct list * nuo = GC_MALLOC ( sizeof ( struct list )); nuo -> next = NULL ; // if next line is commented, then no leakage if ( last ) last -> next = nuo ; last = nuo ; } }



stackoverflow question http://stackoverflow.com/questions/31380641/libgc-why-this-code-leaks
_______________________________________________
bdwgc mailing list
***@lists.opendylan.org
https://lists.opendylan.org/mailman/listinfo/bdwgc
Ingo Albrecht
2015-07-15 15:32:24 UTC
Permalink
| #include <gc.h>
struct list {
struct list* next;
};
int main() {
GC_INIT();
struct list *last = NULL;
for (;;) {
struct list* nuo = GC_MALLOC(sizeof(struct list));
nuo->next = NULL;
// if next line is commented, then no leakage
if (last) last->next = nuo;
last = nuo;
}
}|
Note that this code works, even on Debian and Ubuntu, when a call to
GC_gcollect() is inserted anywhere in the loop. This may either be because
libgc doesn't collect otherwise (unlikely) or because the additional
call clobbers
registers that would otherwise contain an ambiguous reference to the first
list node (more likely). Note that compiler optimization will affect
results here!

Your example, while short and concise, is a bad test for this kind of
conservative
GC since it does not create enough perturbance on the stack to
effectively erase
old references. Also your allocation pattern, while entirely valid, is
unusual in that
it contains extremely long reference chains that are hard to collect for
any collector.

Rest assured that this will not happen for more realistic usage. It is
just an artifact
of how conservative GC works.
stackoverflow question
http://stackoverflow.com/questions/31380641/libgc-why-this-code-leaks
_______________________________________________
bdwgc mailing list
https://lists.opendylan.org/mailman/listinfo/bdwgc
Loading...