Bryan,

In your example below, I'm referring to TcpServerEndpoint and its associated 
classes (it's its ListenEndpoint and Endpoint implementations for which 
Object.equals/hashCode are critical), because that's what you're using in your 
exporters.  And the standard TcpServerEndpoint should be fine in this regard.

-- Peter


On Jan 13, 2012, at 9:44 AM, Bryan Thompson wrote:

> Peter,
> 
> Can you clarify what you mean by the end point in this context?  That would 
> be the server side object which is being exported?  I.e., the proxy wrapping 
> the Future?  Or would this be the long lived service which is exported and 
> against which the requests are being made?
> 
> If you are talking about the RemoteFutureImpl class (in the code below), then 
> it does not override equals()/hashCode().  However, I am unsure how anything 
> would benefit from that it if did as no two futures (or their server side 
> wrappers) should be equals().  
> 
> The code to export the Future from the long lived service looks like this:
> 
>    public <E> Future<E> getProxy(final Future<E> future) {
> 
>        /*
>         * Setup the Exporter for the Future.
>         * 
>         * Note: Distributed garbage collection is enabled since the proxied
>         * future CAN become locally weakly reachable sooner than the client 
> can
>         * get() the result. Distributed garbage collection handles this for us
>         * and automatically unexports the proxied iterator once it is no 
> longer
>         * strongly referenced by the client.
>         */
>        final Exporter exporter = getExporter(true/* enableDGC */);
> 
>        // wrap the future in a proxyable object.
>        final RemoteFuture<E> impl = new RemoteFutureImpl<E>(future);
> 
>        /*
>         * Export the proxy.
>         */
>        final RemoteFuture<E> proxy;
>        try {
> 
>            // export proxy.
>            proxy = (RemoteFuture<E>) exporter.export(impl);
> 
>            if (log.isInfoEnabled()) {
> 
>                log.info("Exported proxy: proxy=" + proxy + "("
>                        + proxy.getClass() + ")");
> 
>            }
> 
>        } catch (ExportException ex) {
> 
>            throw new RuntimeException("Export error: " + ex, ex);
> 
>        }
> 
>        // return proxy to caller.
>        return new ClientFuture<E>(proxy);
> 
>    } 
> 
>    /**
>     * Return an {@link Exporter} for a single object that implements one or
>     * more {@link Remote} interfaces.
>     * <p>
>     * Note: This uses TCP Server sockets.
>     * <p>
>     * Note: This uses [port := 0], which means a random port is assigned.
>     * <p>
>     * Note: The VM WILL NOT be kept alive by the exported proxy (keepAlive is
>     * <code>false</code>).
>     * 
>     * @param enableDGC
>     *            if distributed garbage collection should be used for the
>     *            object to be exported.
>     * 
>     * @return The {@link Exporter}.
>     */
>    protected Exporter getExporter(final boolean enableDGC) {
> 
>        return new BasicJeriExporter(TcpServerEndpoint
>                .getInstance(0/* port */), invocationLayerFactory, enableDGC,
>                false/* keepAlive */);
> 
>    }
> 
> The code to export the long lived service looks like this:
> 
>            /*
>             * Extract how the service will provision itself from the
>             * Configuration.
>             */
> 
>            // The exporter used to expose the service proxy.
>            exporter = (Exporter) config.getEntry(//
>                    getClass().getName(), // component
>                    ConfigurationOptions.EXPORTER, // name
>                    Exporter.class, // type (of the return object)
>                    /*
>                     * The default exporter is a BasicJeriExporter using a
>                     * TcpServerEnpoint.
>                     */
>                    new BasicJeriExporter(TcpServerEndpoint.getInstance(0),
>                            new BasicILFactory())
>                    );
> 
>        /*
>         * Export a proxy object for this service instance.
>         * 
>         * Note: This must be done before we start the join manager since the
>         * join manager will register the proxy.
>         */
>        try {
> 
>            proxy = exporter.export(impl);
> 
>            if (log.isInfoEnabled())
>                log.info("Proxy is " + proxy + "(" + proxy.getClass() + ")");
> 
>        } catch (ExportException ex) {
> 
>            fatal("Export error: "+this, ex);
> 
>        }
> 
> Thanks,
> Bryan
> 
>> -----Original Message-----
>> From: Peter Jones [mailto:p...@roundroom.net] 
>> Sent: Friday, January 13, 2012 9:33 AM
>> To: dev@river.apache.org
>> Subject: Re: DGC threads issue
>> 
>> Which is why the Object.equals/hashCode for an endpoint 
>> implementation is critical.
>> 
>> -- Peter
>> 
>> 
>> On Jan 13, 2012, at 2:05 AM, Gregg Wonderly wrote:
>> 
>>> I would say, that it's very easy to just code up a 
>> configuration entry, or a dynamic construction in code where 
>> a new endpoint is also created per each Exporter.  That can 
>> quickly turn into a problematic situation in cases like this, 
>> where there are lots of "quick" exports followed by 
>> termination without "unexport" being done directly as part of 
>> the returning context.
>>> 
>>> Gregg Wonderly
>>> 
>>> On Jan 13, 2012, at 12:01 AM, Peter Firmstone wrote:
>>> 
>>>> Is there another way to create an Endpoint per exported 
>> object?  I'm just thinking, it seems unlikely that Brian's 
>> implemented his own Endpoint, but are there any other error 
>> conditions or incorrect use scenarios that could produce the 
>> same problem?
>>>> 
>>>> Cheers,
>>>> 
>>>> Peter.
>>>> 
>>>> Peter Jones wrote:
>>>>> Bryan,
>>>>> 
>>>>> DGC threads should not be per exported object.  Generally 
>> speaking, they tend to be per endpoint (at which there are 
>> one or more remote objects exported).  Are you using any sort 
>> of custom endpoint implementation?  Problems like this can 
>> occur when an endpoint implementation doesn't implement 
>> Object.equals and hashCode appropriately, so the expected 
>> batching of threads (and communication) per endpoint does not occur.
>>>>> 
>>>>> It might help to see, from a thread dump, exactly which 
>> DGC threads are causing this problem.  And they are in the 
>> server JVM (with all the exported remote objects), not the 
>> remote callers' JVM(s)?
>>>>> 
>>>>> -- Peter
>>>>> 
>>>>> 
>>>>> On Jan 12, 2012, at 3:45 PM, Tom Hobbs wrote:
>>>>> 
>>>>> 
>>>>>> Hi Bryan,
>>>>>> 
>>>>>> Sorry that no one got back to you about this.  I'm afraid that I 
>>>>>> don't know the answer to your question, I've copied the dev list 
>>>>>> into this email in case someone who monitors that list (but not 
>>>>>> this one) has any ideas.
>>>>>> 
>>>>>> Best regards,
>>>>>> 
>>>>>> Tom
>>>>>> 
>>>>>> On Thu, Jan 12, 2012 at 2:29 PM, Bryan Thompson 
>> <br...@systap.com> wrote:
>>>>>> 
>>>>>>> Just to follow up on this thread myself.  I modified 
>> the pattern to return a "thick" future rather than a proxy 
>> for the future.  This caused the RMI call to wait on the 
>> server until the future was done and then sent back the 
>> outcome.  This "fixed" the DGC memory/thread leak by reducing 
>> the number of exported proxies drammatically.
>>>>>>> 
>>>>>>> In terms of best practices, is distributed DGC simply 
>> not useful for exported objects with short life spans?  Can 
>> it only be used with proxies for relatively long lived services?
>>>>>>> 
>>>>>>> Thanks,
>>>>>>> Bryan
>>>>>>> 
>>>>>>> 
>>>>>>>> -----Original Message-----
>>>>>>>> From: Bryan Thompson
>>>>>>>> Sent: Tuesday, January 03, 2012 12:06 PM
>>>>>>>> To: u...@river.apache.org
>>>>>>>> Subject: DGC threads issue
>>>>>>>> 
>>>>>>>> Hello,
>>>>>>>> 
>>>>>>>> Background:
>>>>>>>> 
>>>>>>>> I am seeing what would appear to be one DGC thread 
>> allocated per 
>>>>>>>> exported object.  This is using River 2.2 and Sun JDK 
>> 1.6.0_17.  
>>>>>>>> Relevant configuration parameters are below.
>>>>>>>> 
>>>>>>>> I am observing problems with the DGC threads not being 
>> retired on 
>>>>>>>> a timely basis.  The exported objects are proxies for Futures 
>>>>>>>> which are being executed on the service.  The code pattern is 
>>>>>>>> such that the proxied Future goes out of lexical scope quite 
>>>>>>>> quickly.  E.g., rmiCallReturningProxyForFuture().get().
>>>>>>>> 
>>>>>>>> Under a modest load, a large number of such Futures 
>> are exported 
>>>>>>>> which results in a large number of long lived DGC 
>> threads.  This 
>>>>>>>> turns into a problem for the JVM due to the stack 
>> allocation per 
>>>>>>>> thread.  Presumably this is not good for other reasons as well 
>>>>>>>> (e.g., scheduling).
>>>>>>>> 
>>>>>>>> I have tried to override the leaseValue and checkInterval 
>>>>>>>> defaults per the configuration options below.  I 
>> suspect that the 
>>>>>>>> lease interval is somehow not being obeyed, which is 
>> presumably a 
>>>>>>>> problem on my end.  However, I can verify that the 
>> configuration 
>>>>>>>> values are in fact showing up in
>>>>>>>> System.getProperties() for at least some of the JVMs involved 
>>>>>>>> (the one which drives the workload and the one that I am 
>>>>>>>> monitoring with the large number of DGC lease threads).
>>>>>>>> 
>>>>>>>> Some questions:
>>>>>>>> 
>>>>>>>> Is this one-thread-per-exported proxy the expected 
>> behavior when 
>>>>>>>> DGC is requested for the exported object?
>>>>>>>> 
>>>>>>>> The DGC lease checker threads appear to expire ~14 - 
>> 15 minutes 
>>>>>>>> after I terminate the process which was originating the RMI 
>>>>>>>> requests.  This is close the sum of the default 
>> leaseValue (10m) 
>>>>>>>> and checkInterval (5m) parameters, but maybe there is 
>> some other 
>>>>>>>> timeout which is controlling this?  If this is the sum 
>> of those 
>>>>>>>> parameters, why would the DGC lease threads live until 
>> the sum of 
>>>>>>>> those values?  I thought that the lease would expire after the 
>>>>>>>> leaseValue (10m default).
>>>>>>>> 
>>>>>>>> Can the issue I am observing be caused by a low heap 
>> pressure on 
>>>>>>>> the JVM to which the RMI proxies were exported?  If it 
>> fails to 
>>>>>>>> GC those proxies, even though they are reachable, could that 
>>>>>>>> cause DGC to continue to retain those proxies on the JVM which 
>>>>>>>> exported them?
>>>>>>>> 
>>>>>>>> Is there any way to configure DGC to use a thread pool 
>> or to have 
>>>>>>>> the leases managed by a single thread?
>>>>>>>> 
>>>>>>>> Is it possible that there is an interaction with the 
>> useNIO option?
>>>>>>>> 
>>>>>>>> Relevant options that I am using include:
>>>>>>>> 
>>>>>>>> -Dcom.sun.jini.jeri.tcp.useNIO=true
>>>>>>>> -Djava.rmi.dgc.leaseValue=30000
>>>>>>>> -Dsun.rmi.dgc.checkInterval=15000  
>>>>>>>> -Dsun.rmi.transport.tcp.connectionPool=true
>>>>>>>> 
>>>>>>>> Thanks in advance,
>>>>>>>> Bryan
>>>>>>>> 
>>>>> 
>>>>> 
>>>>> 
>>>> 
>>> 
>> 

Reply via email to