views:

150

answers:

4

Exposition:

In general, Reference Counting has the weakness of "it can't detect loops." However, in some cases, Reference Counting is really useful:

class EmergencyPatient {
  DoctorPtr doctor;
  EmergencyPatient() { doctor = Doctor::acquire(); }
  ~EmergencyPatient() { Doctor::release(doctor); } 
};

Now, in a reference counted world, as soon as we no longer refer to an EmergencyPatient, the doctor is released.

In Java's non-refcounted world, this depends largely on when the EmergencyPatient is garbage collected -- and since the garbage collector is generational, the EmergencyPatient can be in an older generation, and not collected for a long time.

Problem:

To me, a doctor is a very precious resource; other EmergencyPatient need doctors. However, to Java, the EmergencyPatient object is just a few bytes of memory.

Question:

What is the right way to solve this problem? (There are some resources that I want to be freed as soon as I know they're no longer being used).

Thanks!

+4  A: 

The right way to solve this, within the Java framework, is to use a try-finally construct:

Doctor doctor = doctorFactory.acquire();
try
{
    EmergencyPatient  patient = new EmergencyPatient(doctor);
    doctor.examinePatient();
}
finally
{
    doctor.release();
}

You'll notice, by the way, that in my implementation (as in the real world!), the patient does not pick the doctor. This enables you to provide mock doctors to the patient in your unit tests.


Edit: moving away from manufactured classes, this is the structure that you should be using for every java.sql.Connection, every java.io.InputStream, or any other object that manages non-memory resources.

kdgregory
+1 just for the phrase "mock doctors". That's quite a mental picture.
Michael Myers
glad you like it ... it was actually toned down from my original text :-)
kdgregory
I never understood this construct. It assumes that doctor only exists for the time spanning the current method. How do you implement this in the real world--for instance where allocation is done by an "Allocate" button on a GUI and freeing is done by a "Free" button on that (or another) page of the GUI?
Bill K
PS: I understand the cases where your rare resource is something that can be opened, used and closed in one set of code, but that is not the same problem addressed by GC--a class that you create an instance of, pass around to any number of other classes and eliminate when you are sure nobody is using it. Seems like a completely different problem.
Bill K
@Bill - it of course depends on the resource. You certainly don't want to start a database transaction in one part of a GUI and commit it in another ... unless you want to lock up your database.
kdgregory
@Bill #2 - on the other hand, the OP's scenario actually reflects a resource that might be allocated in one place and freed in another: your application could be a doctor's casebook. In which case there is no language-level construct, in Java, C++, or any other language that is appropriate.
kdgregory
(actually, that last statement isn't quite true: in the Actor model, you can take a "consume or pass onward" approach to messages, and it's quite likely that try-finally is sufficient for that)
kdgregory
-1 for attempting to use a non-definitely assigned variable. Remember always: `acquire(); try { use(); } finally { release(); }`.
Tom Hawtin - tackline
A: 

Garbage Collection manages the Memory Resource. If more memory is required, GC will make it available. If you have another type of resource, file handles, database connections, etc., you can manage the lifetime of those however you like.

Robert Christie
A: 

If you examine the 4 reference objects, there is one that will notify you when your object becomes "Eligible for garbage collection". This means that you don't have to wait for a "Big" GC, it can let you know during one of the minor passes if your object has become unreachable, even if it's in one of the older generation areas.

I believe this is supposed to be the correct way to do what you are attempting.

Bill K
And how do you know how frequently minor GC runs? As far as I know, the Java specification makes to guarantee about the frequency of garbage collection.
meriton
Although it makes no guarantee--the GC in Suns recent VMs are artistic masterpieces. If you use the threaded collector then there are threads running in the background constantly evaluating reachability of objects, but yeah, it's dependent on the GC system but should generally be extremely quick (in my experience, things are freed as soon as the machine goes idle without the threaded GC, with it I believe it should always be instant, but I'd test that assumption on your VM of choice)
Bill K
+3  A: 

You're conflating allocating resources (as in Doctors) with allocating memory. If it is memory that is your precious resource, then you're right, Java doesn't give you the fine grain control that other languages might. The benefit you get in exchange for not having that fine grain control is that you're freed from micromanaging memory, something that can increase productivity as well as stability.

If you're managing a resource other than memory, for example doctors in this case, you can use other patterns to ensure that they are released when the object using them doesn't require them any more. One way to do this would be to have the patient lock the doctor while being treated and then release the lock once treated.

jamie mccrindle
Exactly what I was writing. I think the nearest to "explicit" deallocation in java is System.gc() (and I wouldn't suggest it).
3lectrologos
Even System.gc() provides no guarantee that all garbage is reclaimed (it merely "attempts" to free memory according to its JavaDoc)
meriton
And calling System.gc() is generally inadviseable since it behaves differently per GC algorithm and JVM and can negatively affect the performance of the application. A bit like a patient taking treatment into his or her own hands...
jamie mccrindle