Daniel Dekany wrote:

Friday, December 12, 2003, 8:29:41 PM, Stephen McConnell wrote:

[snip]


The trick is that a proxy is generated that holds a reference to the
actual component. If the proxy is finalized it knows that the component it is referencing has no outstanding references - but the proxy has a reference to the appliance managing the component - so the proxy invokes the decommissioning cycle on the appliance which invokes decomissioning actions on the component which results in component disposal.




It's not accident that I asked this. It's something I waned to solve
earlier. This proxy trick was occurred to me, but I found that it is not
correct:

a) It is possible that the finalize() of the component, or the
finalize() of any other object referred by the component is already
invoked before the finalize() of the proxy is invoked. So dispose() will
find itself in an abnormal situation, where some of the object are
already finalized, or even worst, just being concurrently finalized in
another thread.



No - its not possible. The proxy holds a hard reference to the component so the component cannot be finalized before the proxy.



This what all programmer would thing first, but I believe it does not stand. At least I don't find anything the VM spec that states this. (But I find "12.6.2 Finalizer Invocations are Not Ordered" which is not too encouraging...)


The proxy and the component are not finalized in the same pass. What happens is that the actual component instance does not get onto the finalize queue so long as the proxy has a has a hard reference to it. The proxy itself gets finalized when there are no references to proxy (which triggers the decommissioning of the component and the releasing of the hard reference by the proxy to the component). At this point the component can be finalized by the GC (which is ok because it has been fully decommissioned).


Anyway, just consider this example: O1 stores hard reference to O2, and
O2 stores hard reference to O1. Now, if it would be guaranteed what you
assume regarding the finalization order, it would be impossible GC this
two objects because of the circularly decency. But, as a matter of fact,
they can be GC-ed, so the assumption regarding finalization order must
not strand.


I agree that the circular reference creates a case where the two objects get placed into the queue at the same time - however - this is not our case. In our case it is explicity the proxy that holds a hard reference and according to the spec for soft and weak refs - the GC will collect the proxy in a pass *before* attempting to collect the component (because the component is referenced and therefore is excluded from collection on the current GC pass).


And still, what's about the normal VM termination and the
never-finalized objects?



Normal VM termination triggers a shutdown hook. The shutdown hook triggers shutdown of the kernel. Kernel shutdown triggers decommissioning of the root application block. Block decomissioning triggers orderly decommissioning of the appliances contained within it. Appliance decommissioning trigers disposal of the associated lifestyle handler. The lifestyle handler maintains a soft references queue and from this can locate and non-finalized component instances and invoke component decommissioning.



I'm hurry to note: It's not that I want to dispute with you. I would love if I'm mistaken, because I would like to hook this damn GC too... but I didn't find solution yet.


So far I'm seeing predictable behaviour. Initially there were some problems with components that were being GC'ed when created at startup but not referenced until some later stage but that problem was fixed with the introduction of hard references to components declaring a activation on startup policy (see below). Basically what was happening was that the appliance was creating a new component instance during startup, a GC pass occurs and recognizes that the proxy is not referenced (because the consuming component hasn't accessed it), the proxy was finalized triggering decommissioning of the component, the consumer component then requested the service - triggering recreation of a new proxy wrapping the component.


I have somewhere a couple of testcases/demonstrations of different collection policies and how this impacts things in Merlin (just need to find them).


(Also, note that as I said earlier in this thread, seems to me that with
Merlin 3.2 transient objects were never GC-ed. Maybe that's a bug.)




There is a special case with coponents that are maked as startup policy enabled.



(Ammm... I don't know what's "startup policy enabled" :), but these were plain simple components. They had no entry in block.xml or something like that... I smell bug here...)


When you declare a component in a block using <component> the default policy is to instantiate the component on startup. For example - the following reference will result in the instantiation of the fred component on startup.


<component name="fred" class="Fred"/>

The following declaration will modify the startup policy to lazy (or on demand activation):

<component name="fred" class="Fred" activation="lazy"/>

If a component is not declared in the block defintion - then its activation policy is lazy by default. Lazy simply means that the final instantiation of a component is delayed until a request for the service is issued by a consumer component (however the full metamodel and scenario handler is already established and validated).

For any component the default collection policy is CONSERVATIVE which basically transles to a component that will stick around until its appliance terminates it. Alternative collection policies include DEMOCRAT and LIBERAL. A DEMOCRAT sticks around unless there is memory contention (soft ref) and a LIBERAL basically translates to a weak ref.

To kick in LIBERAL or DEMOCRAT collection policies you nbeed to declare this at the component type level. For example, the following declares that a component implementation expects weak reference semantics:

@avalon.component name="fred" collection="liberal"

Here is a small demonstration of a transient component declaring the liberal collection policy that is requested 5 times by a testcase, the test case then invokes the GC, and then terminates.

   [INFO   ] (kernel): installing: file:/${user.dir}/target/classes/
   Creating: 5 transient instances.
   [INFO   ] (tutorial.hello): logging
   [INFO   ] (tutorial.hello): initialization
   [INFO   ] (tutorial.hello): execution
   [INFO   ] (tutorial.hello): logging
   [INFO   ] (tutorial.hello): initialization
   [INFO   ] (tutorial.hello): execution
   [INFO   ] (tutorial.hello): logging
   [INFO   ] (tutorial.hello): initialization
   [INFO   ] (tutorial.hello): execution
   [INFO   ] (tutorial.hello): logging
   [INFO   ] (tutorial.hello): initialization
   [INFO   ] (tutorial.hello): execution
   [INFO   ] (tutorial.hello): logging
   [INFO   ] (tutorial.hello): initialization
   [INFO   ] (tutorial.hello): execution
   Invoking GC
   [INFO   ] (tutorial.hello): disposal
   [INFO   ] (tutorial.hello): disposal
   [INFO   ] (tutorial.hello): disposal
   [INFO   ] (tutorial.hello): disposal
   [INFO   ] (tutorial.hello): disposal
   GC complete
   [INFO   ] (kernel): dissassembly phase

If we drop the collection policy (i.e. revert to default conservative) we see that the GC pass has no impact but that all of the transient instances are decommissioned on the termination of the testcase.

   [INFO   ] (kernel): installing: file:/${user.dir}/target/classes/
   Creating: 5 transient instances.
   [INFO   ] (tutorial.hello): logging
   [INFO   ] (tutorial.hello): initialization
   [INFO   ] (tutorial.hello): execution
   [INFO   ] (tutorial.hello): logging
   [INFO   ] (tutorial.hello): initialization
   [INFO   ] (tutorial.hello): execution
   [INFO   ] (tutorial.hello): logging
   [INFO   ] (tutorial.hello): initialization
   [INFO   ] (tutorial.hello): execution
   [INFO   ] (tutorial.hello): logging
   [INFO   ] (tutorial.hello): initialization
   [INFO   ] (tutorial.hello): execution
   [INFO   ] (tutorial.hello): logging
   [INFO   ] (tutorial.hello): initialization
   [INFO   ] (tutorial.hello): execution
   Invoking GC
   GC complete
   [INFO   ] (tutorial.hello): disposal
   [INFO   ] (tutorial.hello): disposal
   [INFO   ] (tutorial.hello): disposal
   [INFO   ] (tutorial.hello): disposal
   [INFO   ] (tutorial.hello): disposal
   [INFO   ] (kernel): dissassembly phase

Cheers, Stephen.

--

Stephen J. McConnell
mailto:[EMAIL PROTECTED]

|------------------------------------------------|
| Magic by Merlin                                |
| Production by Avalon                           |
|                                                |
| http://avalon.apache.org/merlin                |
| http://dpml.net/                               |
|------------------------------------------------|





---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]



Reply via email to