In This Section

Overview

The X Platform's Application Data Modeling (ADM) framework allows modeling of primitive, message and entity fields as arrays. For applications that are highly latency sensitive, it is useful to be able to work with such fields in a zero garbage fashion so that gc pauses can be avoided. Arrays are tricky to deal with from a garbage perspective because pooling of arrays presented directly to an application is quite difficult to achieve (imagine trying to pool arrays of every possible size needed by an application). 

So, as of the 3.1 release of the X Platform, zero garbage Iterator accessors are provided which allow more efficient array management behind the scenes while also encapsulating the backing storage mechanism. Application messages and entities generated with XBuf encoding type and pooling enabled can achieve zero garbage manipulation of array fields via XBuf's setXXXFrom(XIterator) and getXXXIterator() methods. Protobuf and Json Encoded messages and entities are not pooled and are therefore not zero garbage. This article discusses techniques for avoiding garbage using these accessor variants. 

XIterator

The platform provides a resetable Iterator interface to allow applications to iterate through an entity's backing values in a zero garbage fashion. XIterators differ from java.util.Iterator in that they provide a toFirst() method. Additionally, the platform provides the following primitive versions XIterator implementations to avoid autoboxing garbage: XBooleanIterator, XByteIterator, XCharIterator, XShortIterator, XIntIterator, XLongIterator, XFloatIterator and XDoubleIterator. 

Zero Garbage Accessor Summary

For XBuf encoded messages the follow accessors are zero garbage for an entity or message unless noted below:

AccessorDescription
Array Accessors
void setXXX(T [] val)

Sets the value in the entity.

Note that when setting entity values, this operation transfers ownership of each reference in the array ... the application must not modify them after transferring ownership. The application may first copy() each value before adding it if it intends to modify or reuse them.

For performance reasons, this operation may store a reference to the val itself so after setting the value the application should not modify the array's contents.

void lendXXX(T [] val)

Lends each value in the array to this entity and sets them as the new values for this entity's backing array.

For performance reasons, this operation may store a reference to the val itself so after setting the value the application should not modify the array's contents.

Note that when lending entities, serialization of the values can happen in another thread, the application must therefore not modify the values that it lends. The application may first copy() each entity before adding it if it intends to modify or reuse it.

T [] getXXX()

Gets the field as an array.

This call is not zero garbage ... use getXXXIterator() instead

(warning)  Applications may not retain a reference to the returned array and should treat it as immutable.

T [] takeXXX()

Gets the field as an array acquiring a reference to each element taken.

This call is not zero garbage ... use getXXXIterator() instead, and call acquire on each element that the application will retain.

(warning)   Applications may not retain a reference to the returned array and should treat it as immutable.  

T [] getXXXEmptyIfNull()

Gets the field as an array, If there are no values an empty array is returned.

This call is not zero garbage ... use getXXXIterator() instead.

(warning)   Applications may not retain a reference to the returned array and should treat it as immutable.

Iterative Accessors
void setXXXFrom(XIterator<T> val)

Calls toFirst() on the iterator and sets the value in the entity to the remaining values in val.

Note that when setting entity values, t his operation transfers ownership of each reference in the array ... the application must not modify them after transferring ownership. The application may first copy() each value before adding it if it intends to modify or reuse them.

void lendXXXFrom(XIterator<T> val)

Calls toFirst() on the iterator and lends all of the remaining values as the new values for this entity.

Note that when lending entities, serialization of the values can happen in another thread, the application must therefore not modify the values that it lends. The application may first copy() each entity before adding it if it intends to modify or reuse it.
 

XIterator getXXXIterator()

Provides zero garbage iterative access to the backing values in the array.

This call may return the same instance of the iterator for each invocation; when this is the case each call to get the iterator will result in toFirst() being called for it.

(warning) Applications may not retain a reference to the iterator beyond the scope of a message handler as it is may be tied to the pooling lifecycle of the entity that returns it.
Additionally, if the iterator contains pooled embedded entities, the application may not retain references to the elements itself without first acquiring them.

(warning) This iterator is not thread safe and concurrent modification of the field by either another thread or by the iterating thread will result in unspecified behavior.

  
void addXXX(T val)

Adds a value to the array of values in the message.

Note that when setting an entity value, t his operation transfers ownership of the value ... the application must not modify the value after transferring ownership. The application may first copy() the value before adding it if it intends to modify or reuse it.

void lendXXX(T val)

Lends the provided value to the entity and adds it to the backing array for the field.

Note that when lending an entity, serialization of the value can happen in another thread, the application must therefore not modify the value that it lends. The application may first copy() the value before adding it if it intends to modify or reuse it.

clearXXX()Clears all of the values in the backing array. Each value is disposed().


Use Cases 

 The examples below show how to work with an enum array field of type MyEnum.

Array Field Access and Iteration

// use a for loop when iterating a list element to avoid allocating an Iterator
// the code below is garbage free
XIterator<MyEnum> iterator = message.getMyEnumsIterator();
while(iterator.hasNext() {
  MyEnum myenum = iterator.next();
}

iterator.toFirst() {
while(iterator.hasNext() {
  MyEnum myenum = iterator.next();
  //Do some more things. 
}
 
// The following is not zero garbage!
MyEnum [] array = message.getMyEnums();
for(MyEnum enum : array ) {
  // do something
}

Transfering Entity Array Values from an Inbound Message

For an array of entity fields on an inbound message, each instance in the array is owned by the inbound message and is read only. When the inbound message is disposed each of the array values will also be disposed and cleared. To set the values on an outbound message, each element therefore needs to be acquired to ensure that their contents aren't cleared out from under the outbound message. Generated messages and entities provide a 'lend' accessor ensure that the each value is properly acquired in this case:

public void onNewOrder(NewOrderEvent order) {
 
    OrderReceivedEvent orderEvent = OrderReceivedEvent.create();
	XIterator<Product> products = orderEvent.getProductsIterator();
    orderEvent.lendProductIdsFrom(productCodes);
	...
    send(orderEvent);
 
    //Reset product codes iterator for reuse:
	products.toFirst();
	
	//Also lend the values to the inventory event:
    InventoryUpdateEvent inventoryEvent = InventoryUpdateEvent .create();
    inventoryEvent.lendProductsFrom(productCodes);
	...
    send(inventoryEvent);
    
}

Enumeration and primitive array fields are not pooled so lend semantics are not required. Therefore an application can safely call outboundMessage.setMyIntListFrom(inboundMessage.getMyIntListIterator()) ... taking and lending the values is not needed.

Copying Array Fields to Application State

Because messages are pooled, it is illegal for an application to retain references to the returned Iterators or their Iterator values beyond the scope of a message handler. When an array from a message needs to be stored in application state it is recommended that a target list or array be preallocated at application startup to avoid promotions and garbage during normal application processing. The following illustrates an application coded stats object that preallocates an ArrayList and copies the values from a message.

public class MyStateObject {
  	// preallocated array to hold values from a message
	private final ArrayList<MyEnum> values = new ArrayList<MyEnum>(5); 
 
	public final void update(final Message message) {
       values.clear();
       XIterator<MyEnum> iterator = message.getMyEnumsIterator();
       while(iterator.hasNext()) {
          values.add(iterator.next());
       }
    }
}

Copying Entity Array Fields to Application State

Because a value iterator for an entity array field may not be retained by the scope of an event handler, it is recommended that applications copy the field values that need to be retained into a collection preallocated by the application. Additionally since the values returned by the iterator will be cleared and disposed by the source message, if the application decides to retain an entity value, it must first be acquired to prevent it from being disposed along with the message. 

public class MyStateObject {
  	// preallocated array to hold values from a message
	private final ArrayList<MyEntity> myEntities = new ArrayList<MyEntity>(5); 
 
	public final void update(final Message message) {
       clearEntities();
       XIterator<MyEntity> iterator = message.getMyEntityIterator();
       while(iterator.hasNext()) {
		  final MyEntity entity = iterator.next();	
		  if(entity.getQuantity() > 0) {
              myEntities.add(entity.copy()); 
          }
       }
    }
 
    public void clearEntities() {
       //Dereference and clear previously acquired entities:
       if(!myEntities.isEmpty()) {
          for(int i = 0; i < myEntities.size(); i++) {
             myEntities.get(i).dispose();
          }
		  myEntities.clear();
       }
    }
}

Note that the embedded entity copy() method was introduced in the 3.5.1 and 3.6.1 versions of the ADM code generator. Prior versions can use the copy() method, but be advised that the clone operation does not preserve pass-through fields. 

Known Limitations

At this time Arrays Accessors for Wrapped primitive types are not zero garbage. This includes String[], Uuid[] and Date[] fields. For zero garbage operation with these types consider using XBuf - Repeated Field Bulk Copies