Thread overview | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
May 15, 2002 real destructors | ||||
---|---|---|---|---|
| ||||
You no longer need to manually release dynamically allocated memory. So no destructors anymore? Wrong! You have to release thousand other types of phisical and logical resources. This is considered very rare and unimportant. I would rather say it has a moderate chance and has the same priority than releasing memory. That is: essential and inevitable. You should not accept workarounds here. I think having real and deterministic destructors does not conflict with the garbage collection concept. I propose the following solution: The destructor should be called when an object is ready for garbage collection (ref. count is 0), not when it is actually collected. In this case you can release any resources in the destructor. What use is a destructor called anywhere else? Any example for a useful destructor with the current garbage collection concept? You could say this will be slow. Well only a few of the objects need destuction anyway. Let's consider this code. It is beyond awful: int fn() { my_type1 my_obj1 = new my_obj1; my_type2 my_obj2 = new my_obj2; my_type3 my_obj3 = new my_obj3; try { try { try { whatever(); } finally { my_obj3.close(); } } finally { my_obj2.release(); } } finally { my_obj1.destroy(); } } Ideas? Sandor Hojtsy |
May 15, 2002 Re: real destructors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sandor Hojtsy | "Sandor Hojtsy" <hojtsy@index.hu> wrote in message news:abttuj$1ak$1@digitaldaemon.com... > The destructor should be called when an object is ready for garbage collection (ref. count is 0), not when it is actually collected. In this case you can release any resources in the destructor. D garbage collector is not reference-counting. So, even if you nullify the reference, it'll take time for garbage collector to make a cycle and detect the null pointer. > What use is a destructor called anywhere else? Any example for a useful destructor with the current garbage collection concept? Yes. I use destructors in my WinD project. Since GC is run at least once, at the end of the program, you can be sure that all non-closed windows will be closed, all DCs, brushes and pens freed, etc. By the way, the same applies to files. If you forget to close() the File, it will be closed from the destructor, as soon as GC will run. > Let's consider this code. It is beyond awful: > > int fn() > { > my_type1 my_obj1 = new my_obj1; > my_type2 my_obj2 = new my_obj2; > my_type3 my_obj3 = new my_obj3; > try { > try { > try { > whatever(); > } finally { my_obj3.close(); } > } finally { my_obj2.release(); } > } finally { my_obj1.destroy(); } > } > > Ideas? Yep. Rewrite it as: try try try { whatever() } finally my_obj3.close(); finally my_obj2.release(); finally my_obj1.destroy(); =) |
May 15, 2002 Re: real destructors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pavel Minayev | Pavel Minayev wrote: > > What use is a destructor called anywhere else? Any example for a useful destructor with the current garbage collection concept? > > Yes. I use destructors in my WinD project. Since GC is run at least once, at the end of the program, you can be sure that all non-closed windows will be closed, all DCs, brushes and pens freed, etc. > > By the way, the same applies to files. If you forget to close() the File, it will be closed from the destructor, as soon as GC will run. You still have the potential for (temporary) memory leaks, even if they are cleaned up eventually. In Windows, you could get a LOT of allocated objects very quickly if the GC didn't get around to running soon enough. It would be very useful for the OS to be able to send a message/signal to the program when memory ran low, asking it to run the GC. In such an architecture, you wouldn't even need to schedule a regular GC run...whenever the OS detected a low memory condition, every program would get a "GC signal", and the various GC's would release whatever memory they were holding on to that wasn't needed. -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ] |
May 15, 2002 Re: real destructors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russ Lewis | "Russ Lewis" <spamhole-2001-07-16@deming-os.org> wrote in message news:3CE29345.44CEF1D7@deming-os.org... > Pavel Minayev wrote: > > > > What use is a destructor called anywhere else? Any example for a useful > > > destructor with the current garbage collection concept? > > > > Yes. I use destructors in my WinD project. Since GC is run at least once, > > at the end of the program, you can be sure that all non-closed windows will be closed, all DCs, brushes and pens freed, etc. > > > > By the way, the same applies to files. If you forget to close() the File, it will be closed from the destructor, as soon as GC will run. > > You still have the potential for (temporary) memory leaks, even if they are > cleaned up eventually. In Windows, you could get a LOT of allocated objects > very quickly if the GC didn't get around to running soon enough. > > It would be very useful for the OS to be able to send a message/signal to the > program when memory ran low, asking it to run the GC. In such an architecture, you wouldn't even need to schedule a regular GC run...whenever > the OS detected a low memory condition, every program would get a "GC signal", > and the various GC's would release whatever memory they were holding on to that wasn't needed. > > -- > The Villagers are Online! villagersonline.com > > .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] > .[ (a version.of(English).(precise.more)) is(possible) ] > ?[ you want.to(help(develop(it))) ] > Or better yet, make the OS able of doing garbage collection! -- Stijn OddesE_XYZ@hotmail.com http://OddesE.cjb.net _________________________________________________ Remove _XYZ from my address when replying by mail |
May 15, 2002 Re: real destructors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russ Lewis | "Russ Lewis" <spamhole-2001-07-16@deming-os.org> wrote in message news:3CE29345.44CEF1D7@deming-os.org... > You still have the potential for (temporary) memory leaks, even if they are > cleaned up eventually. In Windows, you could get a LOT of allocated objects > very quickly if the GC didn't get around to running soon enough. Yes, you are right. Nothing I can do much with, though - window handles are released when windows are closed, and DCs are deleted, but pens and brushes might be used somewhere else, and thus can't be disposed that easy. |
May 15, 2002 Re: real destructors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pavel Minayev | "Pavel Minayev" <evilone@omen.ru> wrote in news:abu1gg$47v$1 @digitaldaemon.com:
>
> Yep. Rewrite it as:
>
> try try try
> {
> whatever()
> }
> finally my_obj3.close();
> finally my_obj2.release();
> finally my_obj1.destroy();
>
What might be a nice piece of syntactic sugar
that would be something like this.
try(File f("data.dat"),Foo b(12))
{
f.read(...)// blah blah
}
which would be rewriten as:
try
{
f = new File("data.dat");
b = new Foo(12);
f.read(...)//blah blah
}
finally
{
delete b;
delete f;
}
This deals with stack lifetime issues.
For object lifetime issues maybe a new keyword
"owned"
class Foo
{
owned File f;
}
When Foo is deleted or collected the finalizer
goes through all the objects "owned" pointers and
deletes them.
|
May 15, 2002 Re: real destructors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russ Lewis | "Russ Lewis" <spamhole-2001-07-16@deming-os.org> wrote in message news:3CE29345.44CEF1D7@deming-os.org... > You still have the potential for (temporary) memory leaks, even if they are > cleaned up eventually. In Windows, you could get a LOT of allocated objects > very quickly if the GC didn't get around to running soon enough. It's not that bad. Just use: f = new Foo(); f.acquire_resource(); f.operations(); f.release_resource(); In the normal case, your resources get released. Only when an exception occurs does the release_resource() get skipped, but it still gets run some time later when the GC runs (if Foo is written right). If that isn't enough, just use try-finally. > > It would be very useful for the OS to be able to send a message/signal to the > program when memory ran low, asking it to run the GC. In such an architecture, you wouldn't even need to schedule a regular GC run...whenever > the OS detected a low memory condition, every program would get a "GC signal", > and the various GC's would release whatever memory they were holding on to that wasn't needed. Just execute: gc.fullCollect(); |
May 16, 2002 Re: real destructors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | I have to say I strongly disagree with this strong coupling of object destruction/finalisation with memory garbage collection. It is this very model that is one of the three big criticisms of Java, along with lack of out parameters (big thanks for putting that in D, Walter) and lack of const. Java could also do with templates, but one can forgive them for avoiding the difficulties in that case. One of my friends is a major a Java guru, having worked in the inner circle at Sun for a long time, and he poo-poos my criticism of Java's denial of the so-useful-we-don't-even-think-about-it-any-more "resource acquisition is initialisation" idiom, and its built-in support in C++. However, it seems to me to be the most basic (fundamental, as well as easily understood) tenet of object-orientation that an object clears up after itself. I have heard many times, from Java proponents, that finalisation eventuates and so resource cleanup is carried out, but isn't this like a naughty child who will clean up his room "later". The analogy is not as purile as it may seem, since in order to get, say, a .NET file stream to clear up after itself before disappearing out of scope, we have to call Close(), rather like making your child do the room tidy before going out for the evening. Seriously, if a file is locked and preventing another thread/process from accessing it, then this is a bug in any practical sense, despite the original locker begin guaranteed to unlock it sometime later. The answere in Java & .NET is to pepper one's code with try/finally blocks. Wasn't part of the reason for the introduction of exception handling to reduce error-handling clutter obfuscating that actual task-specific code? Even the use of a using/scope block to enforce dtor/finalise call at the end of the scope is adding code. Another criticism from Java/.NET proponents is that this is forcing an idiom on a lot of code that does not need/want it. This is not the case, since one could quite easily omit a definition of Finalise/~dtor, and presumably the runtime system can take advantage of this omission for the purposes of its internal efficiencies, only calling the destructor when explicitly there (or in a superordinate class). If we get them off the soap-box for a mo, however, we surely have to ask what is the point of defining a function that _may_ get called at some time in the future, or just as well may not? We witness some acknowledgement of this horrible omission in Java in the C#/.NET implementation, although they provide the most gauche "solution" in their Dispose idiom: an utter load of nonsense. If one doesn't need destruction, don't define a destructor. If one needs (deterministic) destruction, let's have it. If one wants non-deterministic destruction, I have to ask: what are you asking for? why do you want it? Hopefully someone can enlighten me. I hope that D can avoid this coupling, and continue with what seems in all other regards to be a superb idea. Matthew "Walter" <walter@digitalmars.com> wrote in message news:<abunoi$o4u$1@digitaldaemon.com>... > > "Russ Lewis" <spamhole-2001-07-16@deming-os.org> wrote in message news:3CE29345.44CEF1D7@deming-os.org... > > You still have the potential for (temporary) memory leaks, even if they > are > > cleaned up eventually. In Windows, you could get a LOT of allocated > objects > > very quickly if the GC didn't get around to running soon enough. > > It's not that bad. Just use: > > f = new Foo(); > f.acquire_resource(); > f.operations(); > f.release_resource(); > > In the normal case, your resources get released. Only when an exception occurs does the release_resource() get skipped, but it still gets run some time later when the GC runs (if Foo is written right). If that isn't enough, > just use try-finally. > > > > > It would be very useful for the OS to be able to send a message/signal to > the > > program when memory ran low, asking it to run the GC. In such an architecture, you wouldn't even need to schedule a regular GC > run...whenever > > the OS detected a low memory condition, every program would get a "GC > signal", > > and the various GC's would release whatever memory they were holding on to > > that wasn't needed. > > Just execute: > > gc.fullCollect(); > > "Walter" <walter@digitalmars.com> wrote in message news:abunoi$o4u$1@digitaldaemon.com... > > "Russ Lewis" <spamhole-2001-07-16@deming-os.org> wrote in message news:3CE29345.44CEF1D7@deming-os.org... > > You still have the potential for (temporary) memory leaks, even if they > are > > cleaned up eventually. In Windows, you could get a LOT of allocated > objects > > very quickly if the GC didn't get around to running soon enough. > > It's not that bad. Just use: > > f = new Foo(); > f.acquire_resource(); > f.operations(); > f.release_resource(); > > In the normal case, your resources get released. Only when an exception occurs does the release_resource() get skipped, but it still gets run some time later when the GC runs (if Foo is written right). If that isn't enough, > just use try-finally. > > > > > It would be very useful for the OS to be able to send a message/signal to > the > > program when memory ran low, asking it to run the GC. In such an architecture, you wouldn't even need to schedule a regular GC > run...whenever > > the OS detected a low memory condition, every program would get a "GC > signal", > > and the various GC's would release whatever memory they were holding on to > > that wasn't needed. > > Just execute: > > gc.fullCollect(); > > |
May 16, 2002 Re: real destructors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pavel Minayev | > > int fn()
> > {
> > my_type1 my_obj1 = new my_obj1;
> > my_type2 my_obj2 = new my_obj2;
> > my_type3 my_obj3 = new my_obj3;
> > try {
> > try {
> > try {
> > whatever();
> > } finally { my_obj3.close(); }
> > } finally { my_obj2.release(); }
> > } finally { my_obj1.destroy(); }
> > }
> >
> > Ideas?
>
> Yep. Rewrite it as:
>
> try try try
> {
> whatever()
> }
> finally my_obj3.close();
> finally my_obj2.release();
> finally my_obj1.destroy();
I was interested if anyone will notice that correctly this is written as:
int fn()
{
my_type1 my_obj1 = new my_obj1;
try {
my_type2 my_obj2 = new my_obj2;
try {
my_type3 my_obj3 = new my_obj3;
try {
whatever();
} finally my_obj3.close();
} finally my_obj2.release();
} finally my_obj1.destroy();
}
I consider that noone has noticed this,
a sign of lack of understandability, and of difficult debugging.
I cannot think of any syntactic sugar that could help here.
It needs more fundamental changes.
Yours,
Sandor Hojtsy
|
May 16, 2002 Re: real destructors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | Walter wrote: > > It would be very useful for the OS to be able to send a message/signal to > the > > program when memory ran low, asking it to run the GC. In such an architecture, you wouldn't even need to schedule a regular GC > run...whenever > > the OS detected a low memory condition, every program would get a "GC > signal", > > and the various GC's would release whatever memory they were holding on to that wasn't needed. > > Just execute: > > gc.fullCollect(); I would also like to be able to register objects with the GC such that when fullCollect() is called, these objects get callbacks (before garbage collection runs). The callback would allow the object to release any memory that it is holding but is not critical. For instance, when I allocate & deallocate hundreds of little objects, I like to use a "pool class," which, in C++, is like this: template <class X> class Pool { protected: X *array; // dynamically sized public: X *Get() { if(array_is_empty) return new Pool; else return remove_first_element_from_array; } void Put(X *ptr) { add_ptr_to_array; } void Flush(int count=0) { while(count_in_array > count) delete remove_first_element_from_array; reallocate_array_to_size_count } } When gc.fullCollect() is called, I would like the garbage collector to call Flush(0) on all of my registered pool objects. -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ] |
Copyright © 1999-2021 by the D Language Foundation