Understanding the Coherence C++ Object Model

Skip to end of metadata
Go to start of metadata

The Coherence Extend C++ API contains a robust C++ object model. The object model is the foundation on which Coherence for C++ is built and in using the Coherence API is will be beneficial to be familiar with this object model.

Using the Object Model

The following section contains general information for writing code which uses the object model.

Coherence Namespaces

This coherence namespace contains the following general purpose namespaces:

  • coherence::lang - the essential classes that make up the object model
  • coherence::util - utility code, including collections
  • coherence::net - network and cache
  • coherence::stl - C++ Standard Template Library integration
  • coherence::io - serialization

Although each class is defined within its own header file, you can use namespace-wide header files to facilitate the inclusion of related classes. We recommend including, at a minimum, coherence/lang.ns in code that uses this object model.

Understanding the Base Object

The ${xhtml} class is the the root of the class hierarchy. This class provides the common interface for abstractly working with Coherence class instances. Object is an instantiable class that provides default implementations for the following functions.

  • equals
  • hashCode
  • clone (optional)
  • toStream (that is, writing an Object to an std::ostream)

See ${xhtml} in the C++ API for more information.

Automatically Managed Memory

In addition to its public interface, the Object class provides several features used internally. Of these features, the reference counter is perhaps the most important. It provides automatic memory management for the object. This automatic management eliminates many of the problems associated with object reference validity and object deletion responsibility.  This management reduces the potential of programming errors which may lead to memory leaks or corruption. This results in a stable platform for building complex systems.

The reference count, as well as other object "life-cycle" information, operates in an efficient and thread-safe manner by using lock-free atomic compare-and-set operations. This allows objects to be safely shared between threads without the risk of corrupting the count or of the object being unexpectedly deleted due to the action of another thread.

Referencing Managed Objects

To track the number of references to a specific object, there must be a level of cooperation between pointer assignments and a memory manager (in this case the object). Essentially the memory manager must be informed each time a pointer is set to reference a managed object. Using regular C++ pointers, the task of informing the memory manager would be left up to the programmer as part of each pointer assignment.  In addition to being quite burdensome, the effects of forgetting to inform the memory manager would lead to memory leaks or corruption. For this reason the task of informing the memory manager is removed from the application developer, and placed on the object model, though the use of smart pointers. Smart pointers offer a syntax similar to normal C++ pointers, but they do the bookkeeping automatically.

The Coherence C++ object model contains a variety of smart pointer types, the most prominent being:

  • View - A smart pointer that can call only const methods on the referenced object
     
  • Handle - A smart pointer that can call both const and non-const methods on the referenced object.
     
  • Holder - A special type of handle that allows you to reference an object as either const or non-const. The holder remembers how the object was initially assigned, and returns only a compatible form.

Other specialized smart pointers are described later in this section, but the View, Handle, and Holder smart pointers will be used most commonly.

In this documentation, the term handle (with a lowercase "h") refers to the various object model smart pointers. The term Handle (with an uppercase "H") refers to the specific Handle smart pointer.

Using handles

By convention each managed class will have these nested-types corresponding to these handles. For instance the managed ${xhtml} class defines String::Handle, String::View, String::Holder.

Assignment of handles

Assignment of handles follows normal inheritance assignment rules. That is, a Handle may be assigned to a View, but a View may not be assigned to a Handle, just like a const pointer cannot be assigned to a non-const pointer.

Dereferencing handles

When dereferencing a handle that references NULL, the system will throw a ${xhtml} instead of triggering a traditional segmentation fault.

For example, this code would throw a NullPointerException if hs == NULL:

Managed Object Instantiation

All managed objects are heap allocated. The reference count - not the stack - determines when an object can be deleted. To prevent against accidental stack-based allocations, all constructors are marked protected, and public factory methods are used to instantiate objects.

The factory method is named create and there is one create method for each constructor. The create method returns a Handle rather than a raw pointer. For example, the following code will create a new instance of a string:

By comparison, these examples are incorrect and will not compile:

Managed Strings

All objects within the model, including strings, are managed and extend from Object. Instead of using char* or std::string, the object model uses its own managed ${xhtml} class. The String class supports ASCII as well as the full Unicode BML character set.

String Instantiation

String objects can easily be constructed from char* or std::string strings, as shown in these examples:

The managed string is a copy of the supplied string and contains no references or pointers to the original. You can convert back, from a managed String to any other string type, by using getCString() method. This returns a pointer to the original const char*.  Strings can also be created using the standard C++ << operator, when coupled with the COH_TO_STRING macro.

Auto-Boxed Strings

To facilitate the use of quoted string literals, the String::Handle and String::View support auto-boxing from const char*, and const std::string. This allows you to write the code shown in the prior samples as:

Auto-boxing is available for other types as well, see ${xhtml} for details.

Type Safe Casting

Handles are type safe, in the following example, the compiler will not allow you to assign an Object::Handle to a String::Handle, because not all Objects are Strings.

However, this example will compile, as all Strings are Objects.

Down casting

For situations in which you wish to down-cast to a derived Object type, you must perform a dynamic cast using the C++ RTTI (runtime type information) check and ensure that the cast is valid. The Object model provides helper functions to ease the syntax.

  • cast<H>(o) attempt to transform the supplied handle o to type H, throwing an ClassCastException on failure
  • instanceof<H>(o) test if a cast of o to H is allowable, returning true for success, or false for failure

These functions are similar to the standard C++ dynamic_cast<T>, but do not require access to the raw pointer.

The following example shows how to down cast a Object::Handle to a String::Handle:

The cast<H> function will throw a ${xhtml} if the supplied object was not of the expected type, the instanceof<H> function can be used to test if an Object is of a particular type without risking an exception being thrown. Such checks or generally only needed for places where the actual type is in doubt.

Managed Arrays

Managed arrays are provided via the coherence::lang::Array<T> template class. In addition to being managed and adding safe and automatic memory management, this class includes the overall length of the array, and bounds checked indexing.

You can index an array by using its handle's subscript operator, as shown in this example:

The object model supports arrays of C++ primitives and managed Objects. Array's of derived Object types are not supported, only Arrays of Object, casting must be employed to retrieve the derived handle type. Arrays of Objects are technically Array<MemberHolder<Object> >, and typedef'd to ObjectArray for easier readability.

Collection Classes

The coherence::util* namespace includes a number of collection classes and interfaces that may be useful in your application. These include:

  • ${xhtml} - interface
  • ${xhtml} - interface
  • ${xhtml} - interface
  • ${xhtml} - interface
  • ${xhtml} - interface
  • ${xhtml} - implementation
  • ${xhtml} - implementation
  • ${xhtml} - implementation
  • ${xhtml} - implementation
  • ${xhtml} - implementation
  • ${xhtml} - implementation
  • ${xhtml} - implementation
  • ${xhtml} - implementation

These classes also appear as part of the Coherence Extend API.

Similar to ObjectArray, Collections contain Object::Holders, allowing them to store any managed object instance type.

Managed Exceptions

In the object model, exceptions are also managed objects. This allows you to hold onto caught exceptions as a local variable or data member without the risk of object slicing.

All Coherence exceptions are defined via a throwable_spec and derive from the ${xhtml} class, which derives from Object. Managed exceptions are not explicitly thrown by using the standard C+/+ throw statement, but rather via a COH_THROW macro. This macro will set stack information, and then call the exception's raise method, which ultimately calls throw. The resulting thrown object may be caught an the corresponding exceptions View type, or an inherited View type. Additionally these managed exceptions may be caught as standard const std::exception classes.

The following examples show a try/catch block with managed exceptions:

This exception could also have been caught as Exception::View or const std::exception&.

Object Immutability

In C++ the information of how an object was declared (such as const) is not available from a pointer or reference to an object. For instance a pointer of type const Foo*, only indicates that the user of that pointer cannot change the objects state. It does not indicate if the referenced object was actually declared const, and is guaranteed not to change. The object model adds a runtime immutability feature to allow the identification of objects which can no longer change state.

The Object class maintains two reference counters: one for Handles and one for Views. If an object is referenced only from Views, then it is by definition immutable, as Views cannot change the state, and Handles cannot be obtained from Views. The isImmutable() method (included in the Object class) can test for this condition. The method is virtual, allowing subclasses to alter the definition of immutable. For example, String contains no non-const methods, and therefore has an isImmutable() method that always returns true.

Note that once immutable, an object cannot revert to being mutable. You cannot cast away const-ness to turn a View into a Handle as this would violate the proved immutability.

Immutability is important with respect to caching. The Coherence NearCache and ContinuouQueryCache can take advantage of the immutability to determine if a direct reference of an object can be stored in the cache, or if a copy must be created. Additionally, knowing that an object cannot change allows safe multi-threaded interaction without synchronization.

Integrating existing classes into the object model

Frequently there will be the need to integrate existing classes into the object model. A typical example would be the need to store a data-object into a Coherence cache, which only supports storage of managed objects. As it would not be reasonable to require that pre-existing classes be modified to extend from coherence::lang::Object, the object model provides an adapter which will automatically convert a non-managed plain old C++ class instance into a managed class instance at runtime.

This is accomplished via the coherence::lang::Managed<T> template class. This template class extends from Object as well as from the supplied template parameter type T, effectively producing a new class which is both an Object and a T. The new class can be initialized from a T, and converted back to a T. The result is an easy to use, yet very powerful bridge between managed and non-managed code.

See the API doc for ${xhtml} for details and examples.

Writing new managed classes

The following section provides information necessary to write new managed classes, i.e. classes which extend from Object. The creation of new managed classes is required when you are creating new EventListeners, EntryProcessors, or Filter types. They are not required when you are working with existing C++ data objects or making use of the Coherence C++ API. See the previous section for details on integration non-managed classes into the object model.

Specification-Based Managed Class Definition

Specification-based definitions, or "specs" allows you to quickly define managed classes in C++.

Specification-based definitions are helpful when you are writing your own implementation of managed objects.

There are various forms of specs used to create different class types:

  • class_spec standard instantiatable class definitions
  • cloneable_spec cloneable class definitions
  • abstract_spec non-instantiatable class definitions, with zero or more pure virtual methods
  • interface_spec for defining interfaces (pure virtual, multiply inheritable classes)
  • throwable_spec managed classes capable of being thrown as exceptions

Specs automatically define these features on the class being spec'd:

  • Handles, Views, Holders
  • static create() methods which delegate to protected constructors
  • virtual clone() method delegating to the copy constructor
  • virtual sizeOf() method based on ::sizeof()
  • super typedef for referencing the class from which the defined class derives
  • inheritance from coherence::lang::Object, when no parent class is specified via extends<>

To define a class using specs, the class publicly inherits from one of the above specs. Each of these specs are parametrized templates. The parameters are as follows:

  • The name of the class being defined.
  • The class to publicly inherit from, specified via an extends<> statement, defaults to extends<Object>
    • This element is not supplied in interface_spec
    • Except in the case of extends<Object>, the parent class is not derived from virtually
  • A list of interfaces implemented by the class, specified via an implements<> statement
    • All interfaces are derived from using public virtual inheritance

Note that the extends<> parameter is note used in defining interfaces.

The following example illustrates using the interface_spec to define a Comparable interface:

A derived interface Number can then be defined as:

Next a cloneable_spec is used to produce an implementation.

To support the auto-generated create methods, instantiatable classes must declare the coherence::lang::factory<> template as a friend. By convention this is the first statement within the class body.

The above class definition is the equivalent the following non-spec based definitions.

An example of using the spec'd class is as follows:

Equality, Hashing, Cloning, Immutability, and Serialization

What do all these concepts have in common? They all identify the state of an object, and as such will generally have similar implementation concerns. Simply put all data members referenced in one of these methods, will likely need to be referenced in all of the methods. Conversely any data members which are not referenced by one, should likely not be referenced by any of these methods. Consider the simple case of a HashMap::Entry, which contains the well known key and value data members. Certainly these are to be considered in the equals method, and would likely be tested for equality via a call to their own equals method, rather then via reference equality. Now what if this Entry also contains as part of the implementation of the HashMap a handle to the next Entry within the HashMap's bucket, and perhaps also contains a handle back to the HashMap itself. Should these be considered in equals as well? Likely not, it would seem reasonable that comparing two entries consisting of equal keys and values, from two maps should be considered equal. Following this line of thought the hashCode method on Entry would completely ignore data members except for key and value, and the Entry's hashCode would be computed using the results of its key and value hashCode, rather then using their identity hashCode. i.e. a deep equality check in equals implies a deep hash in hashCode. Moving onto clone it can be seen that in cloneing an Entry, we would not want to clone all its data member, but only the key and value. Obviously cloning the parent Map as part of clone the Entry would make no sense, and a similar argument can be made for cloning the handle to the next Entry. This line of thinking can be extended to the isImmutable method, and to serialization as well. While it is certainly not hard and fast rule it is worth considering this approach when implementing any of these methods.

Threading

The object model includes managed threads, which allows for easy creation of platform independent, multi-threaded, applications. The threading abstraction includes support for creating, interrupting, and joining threads. Thread local storage is available from the ${xhtml} class. Thread dumps are also available for diagnostic and troubleshooting purposes. The managed threads are ultimately wrappers around the system's native thread type, such as POSIX or Windows Threads. This threading abstraction is used internally by Coherence, but is available for the application, if necessary.

The following sample shows how to create a new Runnable and spawn a thread:

Refer to ${xhtml} and ${xhtml} for more information.

Weak References

The primary functional limitation of a reference counting scheme is automatic cleanup of cyclical object graphs. Consider a simple bi-directional relationship.

In this picture, both A and B have a reference count of one, which keeps them alive. What they don't realize is that they are the only things keeping each other alive, and that no external references to them exist. Reference counting alone in unable to handle these self sustaining graphs, and memory would be leaked.

The provided mechanism for dealing with graphs is weak references. A weak reference is one which will reference an object, but not prevent it from being deleted. So for instance the A->B->A issue could be resolved by changing it to.

Where A now has a weak reference to B. If B were to reach a point where it was only referenced weakly, it would clear all weak references to itself and then be deleted. In this simple example that would also trigger the deletion of A, as B had held the only reference to A.

Weak references allow for construction of more complicated structures then this. But it becomes necessary to adopt a convention for which references are weak and which are strong. Consider a tree, consisting of nodes A, B, C; and two external references to the tree X, and Y.

In this tree parent (A) use strong references to children (B, C), and children use weak references to their parent. With the picture as it is, reference Y could navigate the entire tree, starting at child B, and moving up to A, and then down to C. But what if reference X were to be reset to NULL? This would leave A only being weakly referenced and it would clear all weak references to itself, and be deleted. In deleting itself there would no longer be any references to C, which would also be deleted. At this point reference Y, without having taken any action would now refer to the following.

This is not necessarily a problem, just a possibility which must be considered when using weak references. To work around this issue, the holder of Y would also likely need to maintain a reference to A to ensure the tree did not dissolve away unexpectedly.

See ${xhtml}, ${xhtml}, and ${xhtml} for usage details.

Virtual Constructors

As is typical in C++, refrencing an object under construction can be dangerous. Specifically references to this are to be avoided within a constructor, as the object initialization has not yet completed. For managed objects, creating a handle to this from the constructor will in most cases cause the object to be destructed before it ever finishes being created. To address this, the object model includes support for virtual constructors. The virtual constructor onInit is defined by Object and can be overriden on derived classes. This method is called automatically by the object model just after construction completes, and just before the new object is returned from its static create method. Within the onInit method it is safe to reference this, to call virtual functions, and to hand out references to the new object to other class instances. Any derived implementation of onInit must include a call to super::onInit() to allow the parent class to also initialize itself.

Advanced handle types

In addition to the Handle and View smart pointers (discussed previously), the object model contains several other specialized variants that can be used. For the most part use of these specialized smart pointers is limited to writing new managed classes, and they will not show up in normal application code.

Type Thread-safe View Notes
coherence:lang:TypedHandle<T> N Conditional on T The implementation of Handle and View
coherence:lang:BoxHandle<T> N Conditional on T Allows automatic creating of managed objects from primitive types.
coherence:lang:TypedHolder<T> N M May act as a Handle or a View. Basic types stored in collections
coherence:lang:Immutable<T> N Y Ensures constness of referring object.
coherence:lang:WeakHandle<T> Y N Does not prevent destruction of referring object.
coherence:lang:WeakView<T> Y Y Does not prevent destruction of referring object.
coherence:lang:WeakHolder<T>* Y Y Does not prevent destruction of referring object.
coherence:lang:MemberHandle<T> Y N Thread-safe const transferring Handle.
coherence:lang:MemberView<T> Y Y Thread-safe View.
coherence:lang:MemberHolder<T> Y M May act a thread-safe Handle or View.
coherence:lang:FinalHandle<T>* Y N Thread-safe const transferring read-only Handle.
coherence:lang:FinalView<T>* Y Y Thread-safe read-only View.
coherence:lang:FinalHolder<T>* Y M May act a thread-safe read-only Handle or View.

* new to 3.5

Thread Safety

Although the base Object class is thread-safe, this cannot provide automatic thread safety for the state of derived classes. As is typical it is up to each individual derived class implementation to provide for higher level thread-safety. The object model provides a number of facilities to aid in writing thread-safe code.

Synchronization and Notification

Every Object in the object model can be a point of synchronization and notification. To synchronize an object and acquire its internal monitor, use a COH_SYNCHRONIZED macro code block, as shown in this example:

The COH_SYNCHRONIZED block performs the monitor acquisition and release. You can safely exit the block with return, throw, COH_THROW, break, continue, and goto statements.

The Object class includes wait(), wait(timed), notify(), and notifyAll() methods for notification purposes. To call these methods, the caller must have acquired the Objects's monitor. Refer to ${xhtml} for details.

Read-write locks are also provided see ${xhtml} for details.

Thread safe handles

The Handle, View, and Holder nested types defined on managed classes are intentionally not thread-safe. That is it is not safe to have multiple threads share a single handle. There is an important distinction here, we are discussing the thread-safety of the handle, not the object referenced by the handle. It is safe to have multiple distinct handles reference the same object from different threads without additional synchronization.

This lack of thread-safety for these handle types offers a significant performance optimization as the vast majority of handles are stack allocated. So long as references to these stack allocated handles are not shared across threads, there is no thread-safety issue to be concerned with.

Thread-safe handles are needed any time a single handle may be referenced by multiple threads. Typical cases include:

  • Global handles - using the standard handle types as global or static variable is not safe.
  • Non-managed multi-threaded application code - Use of standard handles within data structures which may be shared across threads is unsafe.
  • Managed classes with handles as data members - It should be assumed that any instance of a managed class may be shared by multiple threads, and thus using standard handles as data members is unsafe. Note that while it may not be strictly true that all managed classes may be shared across threads, if an instance is passed to code outside of your explicit control (for instance put into a cache), there is no guarantee that the object will not made be visible to other threads.

The use of standard handles should be replaced with thread-safe handles in such cases. The object model includes the following set of thread-safe handles.

  • ${xhtml}<T> - thread-safe version of T::Handle
  • ${xhtml}<T> - thread-safe version of T::View
  • ${xhtml}<T> - thread-safe version of T::Holder
  • ${xhtml}<T> - thread-safe final version of T::Handle
  • ${xhtml}<T> - thread-safe final version of T::View
  • ${xhtml}<T> - thread-safe final version of T::Holder
  • ${xhtml}<T> - thread-safe weak handle to T
  • ${xhtml}<T> - thread-safe weak view to T
  • ${xhtml}<T> - thread-safe weak T::Holder

These handle types may be read and written from multiple thread without the need for additional synchronization. They are primarily intended for use as the data-members of other managed classes, each instance is provided with a reference to a guardian managed Object. The guardian's internal thread-safe atomic state is used to provide thread-safety to the handle. When using these handle types it is recommended that they be read into a normal stack based handle if they will be accessed more then once within a code block. This assignment to a standard stack based handle is thread-safe, and once completed allows for essentially free dereferencing of the stack based handle. Note that when initializing thread-safe handles a reference to a guardian Object must be supplied as the first parameter, this reference can be obtained by calling self() on the enclosing object.

A trivial example of such a usage would be:

The same basic technique can be applied to non-managed classes as well. Since non-managed classes do not extend coherence::lang::Object they cannot be used as the guardian of thread-safe handles. It is possible however to use another Object as the guardian. When taking this approach it is crucial to ensure that the guardian Object outlives the guarded thread-safe handle. To facilitate this Coherence starting with Coherence 3.4.1 you can obtain a random immortal guardian from ${xhtml} via a call to System::common().

When writing managed classes it is preferable to obtain a guardian via a call to self() then to System::common().

Note: In the rare case that one of these handles is declared via the mutable keyword, it must be informed of this fact by setting fMutable to true during construction.

Thread-safe handles can be utilize in non-class shared data as well such as global handles.

Escape Analysis

As of Coherence 3.5 the object model includes escape analysis based optimizations. The escape analysis is used to automatically identify when a managed object is only visible to a single thread and in such cases optimize out unnecessary synchronizations. The following types of operations are optimized for non-escaped objects.

  • reference count updates
  • COH_SYNCHRONIZED acquisition and release
  • reading/writing of thread-safe handles
  • reading of thread-safe handles from immutables

Escape analysis is automatic and is completely safe so long as you follow the rules of using the object model. Most specifically is that it is not safe to pass a managed object between threads without using one of the provided thread-safe handles. Passing it by an external mechanism will not allow escape analysis to identify the "escape" which could cause memory corruption or other runtime errors.

Shared handles

Each manged class type includes nested definitions for a Handles, View, and Holder. These handles are used extensively throughout the Coherence API, and is application code. They are intended for use as stack based references to managed objects. They are not intended to be made visible to multiple threads. That is a single handle should not be shared between two or more threads, though it is safe to have a managed Object referenced from multiple threads, so long as it is by distinct Handles, or a thread-safe MemberHandle/View/Holder.

It is important to remember that global handles to managed Objects should be considered to be "shared", and therefor must be thread-safe, as demonstrated previously. The failure to use thread-safe handles for globals will cause escaped objects to not be properly identified leading to memory corruption.

In 3.4 these non thread-safe handles could be shared across threads so long as external synchronization was employed, or if the handles were read-only. In 3.5 and later this is no longer true, even when used in a read-only mode or enclosed within external synchronization these handles are not thread-safe. This is due to a fundamental change in implementation which drastically reduces the cost of assigning one handle to another, which is an operation which occurs constantly. Any code which was using handles in this fashion should be updated to make use of thread-safe handles.

Const correctness

Coherence escape analysis amongst other things leverages the computed mutability of an object to determine if state changes on data members are still possible. Namely once an object is only referenced from views it is assumed that its data members will not undergo further updates. The C++ language provides a number of mechanisms to bypass this const-only access and allow mutation from const methods. For instance the use of the mutable keyword in a data member declaration, or the casting away of constness. The arguably cleaner as well as supported approach for the object model is the mutable keyword. In the case of the Coherence object model, when a thread-safe data member handle is declared as mutable this information must be communicated to the data member. All thread-safe data members support an optional third parameter fMutable which should be set to true if the data member has been declared with the mutable keyword. This will inform the escape analysis routine to not consider the data member as "const" once the enclosing object is only referenced via Views. Casting away of the constness of managed object is not supported, and can lead to run time errors if the object model believes that the object can no longer undergo state changes.

Thread-local allocator

Coherence for C++ includes a thread-local allocator to improve performance of dynamic allocations which are heavily used within the API. By default each thread will grow a pool to contain up to 64KB of reusable memory blocks to satisfy the majority of dynamic object allocations. The pool is configurable via the following system properties:

  • tangosol.coherence.slot.size controls the maximum size of an object which will be considered for allocation from the pool, the default is 128 bytes. Larger objects will call through to the system's malloc routine to obtain the required memory.
  • tangosol.coherence.slot.count controls the number of slots available to each thread for handling allocations, the default is 512 slots. If there are no available slots allocations will fall back on malloc
  • tangosol.coherence.slot.refill controls the rate at which slots misses trigger refilling the pool. The default of 10000 causes 1/10000 pool misses to force an allocation which will be eligible for refilling the pool.

The pool allocator can be disabled by setting the size or count to 0.

Diagnostics and Troubleshooting

The following section provides information which can aid in diagnosing issues in applications which make
use of the object mode.

Thread Dumps

Thread dumps are available for diagnostic and troubleshooting purposes. These thread dumps also include the stack trace. You can generate a thread dump by performing a CTRL+BREAK (Windows) or a CTRL+BACKSLASH (UNIX). The following is a sample thread dump:

Thread dump Oracle Coherence for C++ v3.4b397 (Pre-release) (Apple Mac OS X x86 debug) pid=0xf853; spanning 190ms

"main" tid=0x101790 runnable: <native>
    at coherence::lang::Object::wait(long long) const
    at coherence::lang::Thread::dumpStacks(std::ostream&, long long)
    at main
    at start

"coherence::util::logging::Logger" tid=0x127eb0 runnable: Daemon{State=DAEMON_RUNNING, Notification=false, 
StartTimeStamp=1216390067197, WaitTime=0, ThreadName=coherence::util::logging::Logger}
    at coherence::lang::Object::wait(long long) const
    at coherence::component::util::Daemon::onWait()
    at coherence::component::util::Daemon::run()
    at coherence::lang::Thread::run()

Memory Leak Detection

While the managed object model reference counting helps to prevent memory leaks they are still possible. The most common way in which they are triggered is via cyclical object graphs. The object model includes heap analysis support to help identify if leaks are occurring, by tracking the number of live objects in the system. Comparing this value over time provides a simple means of detecting if the object count is consistently increasing, and thereby likely leaking. Once a probable leak has been detected, the heap analyzer can help to track it down as well, by provided statistics on what types of objects appeared to have leaked.

Coherence provides a pluggable ${xhtml} interface. The HeapAnalyzer implementation can be specified by using the tangosol.coherence.heap.analyzer system property. The property can be set to one of the following values:

  • none - No heap analysis will be performed. This is the default.
  • object - The ${xhtml} will be used. It provides simple heap analysis based solely on the count of the number of live objects in the system.
  • class - The ${xhtml} will be used. It provides heap analysis at the class level, that is it tracks the number of live instances of each class, and the associated byte level usage.
  • alloc - Specialization of ${xhtml} which additionally tracks the allocation counts at the class level.
  • custom - Lets you define your own analysis routines. You specify the name of a class registered with the SystemClassLoader.

Heap information is returned when you perform a CTRL+BREAK (Windows) or CTRL+BACKSLASH (UNIX). The following is an example of heap analysis information is returned by the class based analayzer:

Space           Count           Class
44 B            1               coherence::lang::Integer32
70 B            1               coherence::lang::String
132 B           1               coherence::util::SafeHashMap::Entry

Total: 246 B, 3 objects, 3 classes

The above example was the heap analysis delta resulting from the insertion of a new entry into a Map.

Memory Corruption Detection

For all that the object model does to prevent memory corruption, it will typically be used along side non-managed code which could cause corruption. To combat this, the object model includes memory corruption detection support. When enabled, the object model's memory allocator will pad the beginning and end of each object allocation by a configurable number of pad bytes. This padding is encoded with a pattern which can later be validated to ensure that the pad has not been touched. If memory corruption occurs, and hits one of the pads, subsequent validations will detect the corruption. Validation is performed when the object is destroyed.

The debug version of the Coherence C++ API has padding enabled by default, using a pad size of 2*(word size), on each side of an object allocation. In a 32-bit build, this adds 16 bytes per object. Increasing the size of the padding will increase the chances of corruption hitting a pad, and thus the chance of detecting corruption.

The size of the pad can be configured by using the tangosol.coherence.heap.padding system property, which can be set to the number of bytes for the pre/post pad. Setting this system property to a non-zero value will enable the feature, and is available even in release builds.

Example of results from detection:

Error during ~MemberHolder: coherence::lang::IllegalStateException: memory corruption detected
in 5B post-padding at offset 4 of memory allocated at 0x132095

Application Launcher - Sanka

Coherence 3.5 adds an application launcher for invoking executable classes embedded within a shared library. The launcher allows for a shared library to contain a number of utility or test executables without the need to ship individual standalone executable binaries.

Command line syntax

The launcher named sanka works similar to java, in that it is provided with one or more shared libraries to load, and a fully qualified class name to execute.

usage: sanka [-options] <native class> [args...]

available options include:
    -l <native library list>  dynamic libraries to load, separated by : or ;
    -D<property>=<value>      set a system property
    -version                  print the Coherence version
    -?                        print this help message
    <native class>            the fully qualified class e.g. coherence::net::CacheFactory

The specified libraries must either be accessible from the operating system library path (PATH, LD_LIBRARY_PATH, DYLD_LIBRARY_PATH), or they may be specified with an absolute or relative path. Library names may also leave off any operating specific prefix or suffix. For instance the UNIX "libfoo.so" or Windows "foo.dll" can be specified simply as "foo". The Coherence shared library which the application was linked against must be accessible from the system's library path as well.

Built-in Executables

A number of utility executables classes are included in the Coherence shared library:

  • coherence::net::CacheFactory runs the Coherence C++ console
  • coherence::lang::SystemClassLoader prints out the registered managed classes
  • coherence::io::pof::SystemPofContext prints out the registered POF types

The later two executables can be optionally supplied with shared libraries to inspect, in which case they will output the registrations which exist in the supplied library rather then all registrations.

Note the console which was formerly shipped as an example, is now shipped as a built-in executable class.

Sample custom executable class

Applications can of course still be made executable in the traditional C++ means via a global main function. If desired you can make your own classes executable via Sanka as well. The following is a simple example of an executable class:

As you can see the specified class must have been registered as an ExecutableClass and have a main method matching the following signature:

static void main(ObjectArray::View)

The supplied ObjectArray parameter is an array of String::View objects corresponding the the command line arguments which followed the executable class name.

Once linked into a shared library for instance libecho.so or echo.dll, the Echo class can be run as follows:

> sanka -l echo my::test::Echo Hello World
Hello
World

The Coherence examples directory includes a helper script buildlib for building simple shared libraries.

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.