6.6. Managing Object Allocation

The Java virtual machine reclaims unused objects with garbage collection, which is not deterministic and cannot be forced to happen in the Java virtual machine.[1] An object is unused if it is not referenced by anyone. Thus, care must be taken to ensure that references to unused objects are removed so that the garbage collector can recover the memory occupied by these objects.

[1] The gc method on the java.lang.System class can be invoked to run the garbage collector, which makes a best effort to reclaim the memory occupied by unused objects. Its effect is specific to the Java virtual machine implementation.

Because a bundle can be stopped and uninstalled, it is especially important not to leave behind any objects that have been allocated by the stopped bundle. In many situations, a bundle may need to be restarted multiple times, or it may be updated. If each time it is stopped, some of its objects continue to take up memory, the gateway will run out of memory very quickly. This is clearly unacceptable for the intended application of a gateway, which is expected to be up and running for months, even years, to come.

This section provides some suggestions on how to keep object allocation under control.

6.6.1. Nullify References After Use

Because the existence of a reference to it prevents an object from being “garbage collected,” you should keep track of the object use and nullify the references to an object when it is no longer of use.

Explicitly assigning null to a reference often is not necessary. However, doing so helps you maintain the discipline of managing object allocations and writing nonmemory-leaking code. It may also make it easier for certain garbage collectors to spot “garbage.”

We know that the bundle activator is the first object to be instantiated when the bundle is started. You are responsible for whatever objects are created in the activator and subsequently within the services (because the earliest time a service can be registered is in the activator), and you should make sure that they are no longer referenced when the bundle is stopped. For example,

import java.util.*;
import org.osgi.framework.*;

public class Activator implements BundleActivator {
   private ServiceRegistration reg;
   private Object service;
   private Properties props;

   public void start(BundleContext ctxt) {
      service = new MyService();
      props = new Properties();
      props.put("description","The greatest service of all times");
      reg = ctxt.registerService("MyService", service, props);
   }

   public void stop(BundleContext ctxt) {
      if (reg != null)
         reg.unregister();
      reg = null;
      props = null;
      service = null;
   }
}

Although not strictly necessary, this practice makes it obvious that all three objects we have created in the start method—service, props, and reg—are all nullified in the stop method. As long as the service implementation itself cleans up properly (which we address in the next section), we can rest assured that our bundle won't leak memory.

The scope and access control enforced by the Java programming language is a great help in managing object references. Data members with private modifiers are only accessible within the same class. Data members with package-level access control won't be accessed outside their package. These all narrow the scope in which we need to keep track of object allocation.

6.6.2. Managing References among Bundles

Although cleaning up references within a bundle is relatively straightforward, managing them across bundles can be complicated. The primary mechanism by which a bundle gets a reference to a service object in another bundle is the getService call. Figure 6.1 shows the process.

Figure 6.1. References to a service


Assume the type of the service under discussion is FooService. Reference s1 is created when bundle b1 instantiated an instance of FooService like

FooService s1 = new FooServiceImpl();

Reference sr is established when b1 registers s1 with the registry:

bundleContext.registerService("FooService", s, null);

Reference s2 is established when bundle b2 calls getService and obtains the service from the registry:

ServiceReference ref =
   bundleContext.getServiceReference("FooService");
FooService s2 = (FooService) bundleContext.getService(ref);

The service object occupies memory as long as the three references remain pointing to it. To allow the garbage collector to reclaim the memory used by the service when bundle b1 is uninstalled, you must make sure that

  1. b1 unregisters the service in the stop method of its activator. This removes reference sr, which is done automatically.

  2. b1 removes the reference s1 by assigning null to it

  3. b2 calls ungetService and also removes the reference s2 by assigning null to it. For this to work, b2 must listen to service unregistration events so that it can be notified when b1 unregisters its service.

If b2 is the bundle to be stopped, it should simply call ungetService and nullify its reference to the service object. This removes reference s2.

If bundle b1 registers a service factory rather than a service with the registry, you still need to follow the previously mentioned steps to ensure proper cleanup of the references. Figure 6.2 illustrates this new situation.

Figure 6.2. The references when b1 registers a service factory


The reference sf is established by b1 instantiating a service factory:

ServiceFactory sf = new MyServiceFactory();

The reference sr is established by b1 registering the service factory:

bundleContext.registerService("FooService", sf, null);

The reference s is established in b2 by

ServiceReference ref =
   bundleContext.getServiceReference("FooService");
FooService s = (FooService) bundleContext.getService(ref);

For the last step, the framework calls the service factory to create a service, and caches it internally. The reference sc points to this cached service instance.

Suppose b1 is going away. In this case, when b1 unregisters its service factory sf, it removes reference sr; it removes reference sf by assigning null to it. When bundle b2 calls ungetService, the framework removes the reference to the cached service sc, and b2 removes reference s by assigning null to it.

If b2 is going away, it then calls ungetService and nullifies its reference to the service, which removes references sc and s.

This discussion makes it clear that we should use service as the only coupling mechanism among bundles, because clean decoupling strategies are in place so that unused object references always get a chance to be removed.

We have just discussed a technique of using delegation and callback, which demands extra care with respect to cleaning up cross-bundle object references. In these circumstances, the caller usually passes an object to the callee as one of the parameters, or the callee returns an object to the caller as the return value. Let's take another look at our news service example. The following could be an implementation of the getNews method of NewsService:

class NewsServiceImpl implements NewsService {
   public InputStream getNews(ContentTransformer ct) {
      String[] tags = ct.getTagsForRemoval();
      ...
      stream = new TagFilteredInputStream(...);
      return stream;
   }
}

The reference ct points to an object created in the calling bundle. If NewsService does not further reference it within itself (as is the case earlier, because ct is a reference local to the scope of the method and is popped off the stack as the method returns), the calling bundle has full control of its disuse. However, if for some reason, NewsService assigns ct to some data member, then the calling bundle can no longer reclaim the object all by itself. Therefore, don't reference an object passed in from another bundle without good reason.

Conversely, after the calling bundle obtained the returned InputStream object, it should read from the stream, then close the stream, and nullify the reference:

InputStream in = null;
try {
   in = newsService.getNews(ct);
   // read from the input stream
} catch (Exception e) {
   // handle exception
} finally {
   if (in != null) {
      try {
         in.close();
      } catch (IOException e) { }
      in = null;
   }
}

By consciously cleaning up cross-bundle object references, we can develop well-behaved bundles that do not hamper the work of the garbage collector and that do not accumulate useless objects in the Java virtual machine over time.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.97.202