To archtyrael and others. ;-)
–
Finalize Guardian, once again.
In this article, I will talk about finalize guardian pattern and its purpose; why do we have to concern about it.
Joshua Bloch, who was JAVA API architect, wrote a brilliant book “EFFECTIVE JAVA.”, and finalize guardian idiom was introduced in that book. Type safe enum, which was also covered in his book, was adpoted in JDK 1.5, adding some nice features on his own idea.
Note that codes in this article should be considered in the context of inheritance. Though it is proved that inheritance is worse than composition,inheritance is still widely used. And implementation inheritance, on the contrary to interface inheritance, is one of the most important feature of OOP paradigm to reuse legacy codes.
However, you must note that decorator pattern may meet the need of code reuse in practice, and it is preferable to inheritance.
Please refer to PROGRAMMING LANGUAGE by SEBESTA for the explanation of implementation/interface inheritance.
1. Constructor and Destructor
As you know, objects can be inherited. Suppose that you have two class like:
#include
using namespace std;
class Parent {
public:
Parent() {
cout << "Parent Construction" << endl;
}
~Parent() {
cout << "Parent Destruction" << endl;
}
};
class Child: public Parent {
public:
Child() {
cout << "Child construction" << endl;
}
~Child() {
cout << "Child destruction" << endl;
}
};
int main() {
Child c;
}
[/code]
What do you think the result of this program? In gcc-c++-3.2.3-4 and Microsoft Visual Studio .NET, this program will shows you:
[code lang="cpp"]
[pool007@dke tmp]$ ./a.out
Parent Construction
Child construction
Child destruction
Parent Destruction
[/code]
However, you must know the fact that this is not always the case how C++ compilers runs the code. In C/C++ ISO standard, it is not defined what the compilers do when the destructor of parent class is not virtual.
If the constructor of child class does not call any constructor of parent class, default constructor (constructor without any parameters) is called. On the contrary, if the destructor of child class does not explicitly call destructor and the destructor of the parent class is not virtual, we don't know what the program will do. Standard says that to call the destructor of parent class automatically, destructor of parent class should be virtual.
This fact also applies to JAVA and .NET. You must call finalize of parent class if you want to finalize your parent class.
2. Destructors that does not work.
2.1. Resurrection
Suppose that you have put your class instance into hashtable like:
[code lang="java"]
fHashtable.put(key, your_class);
fHashtable.put(key, class_which_is_implemented_by_others);
[/code]
What if the destructor of class class_which_is_implemented_by_others is programmed like this:
[code lang="java"]
void finalize() {
// to clear me, I have to call your_class instance
your_class.some_method();
}
[/code]
Suppose that garbage collector have removed your_class from heap. And then, to garbage collect class_which_is_implemented_by_others, the virtual machine have to use your class. That is to say, your class is resurrected. Then the garbage collector will finalize your_class once again. This the problem so called 'resurrection.'
2.2. Cursed Cycles
It is also possible your_class, by any chance, called class_which_is_implemented_by_others to clean itself. Things become complicated in this situation. Due to the cycle of finalize methods and continuous resurrections, garbage collector can not stop the program.
2.3. Infinite loop
And code like this is also possible:
[code lang="java"]
void finalize() {
while (true) { job(); }
}
[/code]
This finalize method will never end.
2.4. Uncaught exception
Consider the following:
[code lang="java"]
void finalize() {
throw new XXXException(); // This Exception inherited RuntimeException
}
[/code]
You can easily guess that finalize method caller would be coded like:
[code lang="java"]
for(item in list) {
item.finalize();
}
[/code]
In this case, not all finalize will be called because the for-loop is breaked due to the uncatchable exception.
3. How finalize are called?
To prevent such situations of Chap 2, in .NET and JAVA environment, calling finalize is not guranteed.
What makes things more complicated is the fact that thread which garbage collect objects and thread which call finazlie is different. Hence, we don't know whether the objects are finalized before garbage collected. Also, these thread execution order is not guranteed.
4. What I have to do, then?
4.1. Dispose pattern
DO NOT RELY ON FINALIZE. Instead, use dispose pattern:
[code lang="java"]
public interface Dispose {
public void dispose();
}
public class MyClass implements Dispose {
...
public void dispose() {
// clean
}
}
....
public static void main(String[] args) {
MyClass mc = new MyClass();
mc.dispose();
}
...
[/code]
The above code will work perfectly because you explicitly do the finalization. In managed environment(by managed environment, I mean JAVA or .NET virtual machine), memory is controlled by virutal machine. However, more precious resources such as network connection, file, and database connections are not controlled by the VM. You have to manage those resources.
One of the example of explicitly managing resources is java.sql.Connection. That class provides close() method to close the datbase connection. As database connection is very high-cost(It tooks lots of time, and there are limited number of connections.), programmers must call close() method after they finished their job.
4.2. finalize is of no use?
Finalize can be the last place of resource releasing. Suppose that users forgot to call Close() method. For such situations, you must make your finalize method like:
[code lang="java"]
public void finalize() {
Close();
}
[/code]
Though, as I said before, calling finalize is not guranteed. Making finalize like this is only for the purpose of safety net.
5. finalize and inheritance
Now, we are prepared to talk about finalize guardian. The form of this pattern is:
[code lang="java"]
public class MyClass {
public Object o = new Object() {
public void finalize() {
dispose();
}
}
public void dispose() {
...
}
}
[/code]
Suppose that other user inherited your class like:
[code lang="java"]
public class Child extends MyClass {
public void doJob() {
...
}
public finalize() {
doJob();
}
[/code]
Unfortunately, he or she did not call the destructor of the parent. So, finalize method of MyClass will never be called; of course, the destructor can be called in some implementation of virtual machine.
This is the place where finalize guardian come into play. To garbage collect instance of MyClass, VM will garbage collect variable o of MyClass. And to garbage collect the o, dispose() will be called automatically.
6. Conclusion
Finalize guardian is the last resort to gurantee finalizing objects to some unguranteed degree.
Hope this helps.
References
- Effective JAVA
- Effective C++
Leave a Reply