SAP Logo


Packed Objects, Object Layout &
Value Types - a Survey

Volker Simonis, SAP

Value vs. Reference Semantics - C++ vs. Java

class Point {
  int x;
  int y;
};

class Line {
  Point p1;
  Point p2;
};

Line la[2];
 
public class Point {
  int x;
  int y;
}

public class Line {
  Point p1;
  Point p2;
}

Line[] la = new Line[2];
 
 

Reference semantics - Drawbacks


Packed Objects


Drawbacks - This is not JavaTM anymore!

Packed Objects example

import com.ibm.jvm.packed.ImportPacked;
import com.ibm.jvm.packed.Packed;
import com.ibm.jvm.packed.PackedObject;
import com.ibm.jvm.packed.reflect.PackedArray;

@Packed
final class Point extends PackedObject { // <- MUST be final and extend PackedObject
  public int x;
  public int y;
  public Point(int x, int y) { this.x = x; this.y = y; }
}
@Packed
@ImportPacked({"Point"})                 // <- MUST declare "packed" usage
final class Line extends PackedObject {
  public Point p1;
  public Point p2;
  public Line() {
    if (!PackedObject.isPackedSupportEnabled()) {
      p1 = new Point();
      p2 = new Point();
    }
  }
  public Line(Point p1, Point p2) {
    if (!PackedObject.isPackedSupportEnabled()) {
      this.p1 = p1;
      this.p2 = p2;
    }
    else {
      this.p1.copyFrom(p1);              // <- Use PackedObject.copyFrom() for initialization
      this.p2.copyFrom(p2);
    }
  }
}
public class PackedTest {
  public static void main(String args[]) {
    Point p1 = new Point(1, 1);
    Point p2 = new Point(2, 2);
    Line l1 = new Line(p1, p2);

    System.out.println("\np1 = " + p1 + "\np2 = " + p2 + "\nl1 = " + l1);
p1 = Point(1, 1)
p2 = Point(2, 2)
l1 = Line(Point(1, 1), Point(2, 2))
    l1.p1.x = 47;
    l1.p1.y = 11;

    System.out.println("\np1 = " + p1 + "\np2 = " + p2 + "\nl1 = " + l1);
> with -XX:-PackedObject
p1 = Point(47, 11)    <===
p2 = Point(2, 2)
l1 = Line(Point(47, 11), Point(2, 2))

> with -XX:+PackedObject
p1 = Point(1, 1)      <===
p2 = Point(2, 2)
l1 = Line(Point(47, 11), Point(2, 2))
    p2.x = 8;
    p2.y = 15;

    System.out.println("\np1 = " + p1 + "\np2 = " + p2 + "\nl1 = " + l1);
> with -XX:-PackedObject
p1 = Point(47, 11)
p2 = Point(8, 15)
l1 = Line(Point(47, 11), Point(8, 15))   <===

> with -XX:+PackedObject
p1 = Point(1, 1)
p2 = Point(8, 15)
l1 = Line(Point(47, 11), Point(2, 2))    <===
    l1.p1 = p2;

    System.out.println("\np1 = " + p1 + "\np2 = " + p2 + "\nl1 = " + l1);
> with -XX:-PackedObject
p1 = Point(47, 11)
p2 = Point(8, 15)
l1 = Line(Point(8, 15), Point(8, 15))    <===

> with -XX:+PackedObject
java.lang.IllegalAccessError: Cannot assign to a nested field of a @Packed type.

Packed Objects array example

@Packed
@ImportPacked({"Point"})
final class Line extends PackedObject {
  ...

  @Packed                    // MUST be @Packed, static and final
  @ImportPacked({"Line"})
  public static final class Array extends PackedObject implements Serializable, Cloneable {
    private Array() {}       // MUST declare private constructor
    public static Array allocate(int length) {
      return PackedArray.newArray(Line.Array.class, length);
    }
    public Line at(int index) {
      return PackedArray.at(this, index);
    }
    public int getLength() {
      return PackedArray.getLength(this);
    }
    public Array clone() throws CloneNotSupportedException {
      return (Array)super.clone();
    }
    // Can't declare any other methods or fields!
  }

public class PackedTest2 {
    Point p1 = new Point(1, 1);
    Point p2 = new Point(2, 2);
    Line l1 = new Line(p1, p2);

    System.out.format("\np1 = " + p1 + "\np2 = " + p2 + "\nl1 = " + l1);
p1 = Point(1, 1)
p2 = Point(2, 2)
l1 = Line(Point(1, 1), Point(2, 2))
    Line.Array la = Line.Array.allocate(3);
    la.at(0).copyFrom(l1);
    la.at(1).p1.copyFrom(p2);
    la.at(2).p1.x = 3;

    System.out.println("\nla = " + Line.toString(la));
la = Line.Array(3) (
	Line(Point(1, 1), Point(2, 2)),
	Line(Point(2, 2), Point(0, 0)),
	Line(Point(3, 0), Point(0, 0)))

Packed Objects - Summary

 

Object Layout

 
 

Object Layout example

public class Point {
  protected int x;
  protected int y;
  public Point() {}
  public Point(int x, int y) {
      this.x = x;
      this.y = y;
  }
  public void set(Point p) {
      if (p != null) {
          this.x = p.x;
          this.y = p.y;
      }
  }
}

public class Line {
  @Intrinsic               // @Intrinsic fields have to be private, final
  private final Point p1 = IntrinsicObjects.constructWithin("p1", this);
  @Intrinsic               // @Intrinsic fields have to be private, final
  private final Point p2 = IntrinsicObjects.constructWithin("p2", this);
  public Line() {}
  public Line(Point p1, Point p2) {
      this.p1.x = p1.x;
      this.p1.y = p1.y;
      this.p2.set(p2);
  }
  public void set(Line l) {
      if (l != null) {
          this.p1.set(l.p1);
          this.p2.set(l.p2);
      }
  }
}

public class Triangle {
  @Intrinsic
  private final Line l = IntrinsicObjects.constructWithin("l", this);
  @Intrinsic
  private final Point p = IntrinsicObjects.constructWithin("p", this);
  public Triangle() {}
  public Triangle(Line l, Point p) {
      this.l.set(l);
      this.p.set(p);
  }
}

public class IntrinsicFieldTest {
  public static void main(String args[]) {
    Point p1 = new Point(1, 1);
    Point p2 = new Point(2, 2);
    Point p3 = new Point(3, 3);

    Line l = new Line();
    Line l1 = new Line(p1, p2);
    Triangle t1 = new Triangle(l1, p3);
    p3.set(new Point(5, 5));

    System.out.println("\np1 = " + p1 +
                       "\np2 = " + p2 +
                       "\np3 = " + p3 +
                       "\nl1 = " + l1 +
                       "\nt1 = " + t1);
  }
}
p1 = Point(1, 1)
p2 = Point(2, 2)
p3 = Point(5, 5)
l1 = Line(Point(1, 1), Point(2, 2))
t1 = Triangle(Line(Point(1, 1), Point(2, 2)), Point(3, 3))

Object Layout array example

public class StructuredArrayOfPoint extends StructuredArray {

  public static StructuredArrayOfPoint newInstance(final long length) {
    return StructuredArray.newInstance(StructuredArrayOfPoint.class, Point.class, length);
  }

  public Point get(long index) {
    return super.get(index);
  }
}

public class StructArrayTest {

  public static void main(String args[]) {
    StructuredArrayOfPoint points = StructuredArrayOfPoint.newInstance(5);

    for (int i = 0; i < points.getLength(); i++) {
      points.get(i).set(new Point(i, i));
    }

    System.out.println("points = " + points);
points = StructuredArrayOfPoint(5) (
	Point(0, 0),
	Point(1, 1),
	Point(2, 2),
	Point(3, 3),
	Point(4, 4))
    StructuredArray lines = StructuredArray.newInstance(StructuredArray.class, Line.class, 5);

    for (int i = 0; i < lines.getLength(); i++) {
      lines.get(i).set(new Line(new Point(i, 2*i), new Point(2*i, 3*i)));
    }

    StringBuffer sb = new StringBuffer("StructuredArray(" + lines.getLength() + ") (");
    for (int i = 0; i < lines.getLength(); i++) {
      sb.append("\n\t" + lines.get(i) + (i == lines.getLength() - 1 ? "" : ","));
    }
    System.out.println("lines = " + sb.append(")\n").toString());
  }
}
lines = StructuredArray(5) (
	Line(Point(0, 0), Point(0, 0)),
	Line(Point(1, 2), Point(2, 3)),
	Line(Point(2, 4), Point(4, 6)),
	Line(Point(3, 6), Point(6, 9)),
	Line(Point(4, 8), Point(8, 12)))

Optimizing Object Layout @Intrinsic





Optimizing the class layout for @Intrinsic fields

$ java -cp ObjectLayout.jar \
       -XX:+PrintFieldLayout IntrinsicFieldTest
IntrinsicFieldTest$Point: field layout
  @ 12 --- instance fields start ---
  @ 12 "x" I
  @ 16 "y" I
  @ 20 --- instance fields end ---
  @ 24 --- instance ends ---
...
IntrinsicFieldTest$Line: field layout
  @ 12 --- instance fields start ---
  @ 12 "p1" LIntrinsicFieldTest$Point;
  @ 16 "p2" LIntrinsicFieldTest$Point;
  @ 20 --- instance fields end ---
  @ 24 --- instance ends ---
...


IntrinsicFieldTest$Triangle: field layout
  @ 12 --- instance fields start ---
  @ 12 "l" LIntrinsicFieldTest$Line;
  @ 16 "p" LIntrinsicFieldTest$Point;
  @ 20 --- instance fields end ---
  @ 24 --- instance ends ---


$ java -cp ObjectLayout.jar -XX:+OptimizeObjectLayout
       -XX:+PrintFieldLayout IntrinsicFieldTest
IntrinsicFieldTest$Point: field layout
  @ 12 --- instance fields start ---
  @ 12 "x" I
  @ 16 "y" I
  @ 20 --- instance fields end ---
  @ 24 --- instance ends ---
...
IntrinsicFieldTest$Line: field layout
  @ 12 --- instance fields start ---
  @ 12 --- instance fields end ---
  @ 16 --- intrinsic fields start ---
  @ 16 "p1" LIntrinsicFieldTest$Point;
  @ 40 "p2" LIntrinsicFieldTest$Point;
  @ 64 --- intrinsic fields end ---
  @ 64 --- instance ends ---
...
IntrinsicFieldTest$Triangle: field layout
  @ 12 --- instance fields start ---
  @ 12 --- instance fields end ---
  @ 16 --- intrinsic fields start ---
  @ 16 "l" LIntrinsicFieldTest$Line;
  @ 80 "p" LIntrinsicFieldTest$Point;
  @104 --- intrinsic fields end ---
  @104 --- instance ends ---

Intrinsify AIOModel.constructElementWithin()

final T constructElementWithin(
        final Object containingObject,
        final Constructor constructor,
        final Object... args)
        throws InstantiationException, IllegalAccessException, InvocationTargetException {
    T element = constructor.newInstance(args);
    field.set(containingObject, set);
    return element;
}
JVM_ENTRY(jobject, JVM_ObjectLayoutConstructWithin(JNIEnv *env, jobject obj,
                                                   jobject enclosing, jobject cstr, jobjectArray args0))
  JVMWrapper("JVM_ObjectLayoutConstructWithin");

  static int foff = ...; // offset of org.objectlayout.AbstractIntrinsicObjectModel.field

  oop field  = JNIHandles::resolve_non_null(obj)->obj_field(foff);
  int slot   = java_lang_reflect_Field::slot(field);
  int offset = InstanceKlass::cast(k)->field_offset(slot);

  oop enclosing_obj = JNIHandles::resolve(enclosing);
  oop constructor_mirror = JNIHandles::resolve(cstr);
  objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0)));

  Reflection::construct_within(enclosing_obj, offset, constructor_mirror, args, CHECK_NULL);
  ...
JVM_END
oop Reflection::construct_within(oop enclosing_obj, jlong offset, oop constructor_mirror, objArrayHandle args, TRAPS) {
  ...
  char* o = (char*)enclosing_obj + offset; // plain pointer to the embedded receiver
  // Install the mark word and the klass pointer into the embedded object
  CollectedHeap::post_allocation_setup_common(klass, (HeapWord*)o);
  Handle receiver(THREAD, (oop)o);

  // Ignore result from call and return receiver
  invoke(klass, method, receiver, override, ptypes, T_VOID, args, false, CHECK_NULL);
  return receiver();
}

Optimizing @Intrinsic fields in the interpreter




void TemplateTable::fast_accessfield(TosState state) {
  ...
  case Bytecodes::_fast_agetfield:
    __ load_heap_oop(rax, field);
    __ verify_oop(rax);
    break;
  case Bytecodes::_fast_agetfield_intr:
    __ leaq(rax, field);
    __ verify_oop(rax);

Optimizing @Intrinsic fields C2

void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) {
  ...
  if (OptimizeObjectLayout && field->is_intrinsic()) {
    if (Verbose) {
      tty->print("do_field_access() - ");
      tty->print("found getfield for intrinsic field %s at bci: %d in ", field->name()->as_quoted_ascii(), bci());
      method()->print_name();
      tty->cr();
    }
    const Type *type = TypeOopPtr::make_from_klass_unique(field_klass->as_klass())->cast_to_ptr_type(TypePtr::NotNull);
    Node *cast = new CastIntrinsicNode(adr, type);
    cast = _gvn.transform(cast);
    push(cast);
    return;
  }
//------------------------------CastIntriniscNode-------------------------------------
// cast pointer of an ecnclosing class to pointer of an intrinsified field (different type)
class CastIntrinsicNode: public CastPPNode {
  public:
  CastIntrinsicNode (Node *n, const Type *t ): CastPPNode(n, t) { init_class_id(Class_CastIntrinsic); }
  ...
instruct castIntrinsic(rRegP dst, immL32 src, rFlagsReg cr)
%{
  match(Set dst (CastIntrinsic (AddP dst src)));
  effect(KILL cr);

  format %{ "addq    $dst, $src\t# intrinisc getfield" %}
  ...

Optimizing @Intrinsic field access in C2

public static void shift(Triangle t, int x, int y) {
    t.getPoint().x += x;
    t.getPoint().setY(t.getPoint().getY() + y);
    t.getLine().getP1().x += x;
    t.getLine().getP1().y += y;
    t.getLine().getP2().x += x;
    t.getLine().getP2().y += y;
}

public static void main(String args[]) {
    ...
    Triangle t1 = new Triangle(l1, p3);

    for (int i = 0; i < COUNT; i++) {
        shift(t1, 1, 1);
    }
}
 movl    R11, [RSI + #12 (8-bit)]	! Field: IntrinsicFieldDeadReckonTest$Triangle.l
 movl    R10, [R11 + #12 (8-bit)]	! Field: IntrinsicFieldDeadReckonTest$Line.p1
 NullCheck R11
 addl    [R10 + #12 (8-bit)], RDX	! Field: IntrinsicFieldDeadReckonTest$Point.x
 NullCheck R10
 addl    [RSI + #44 (8-bit)], RDX
Node *AddPNode::Ideal(PhaseGVN *phase, bool can_reshape) {

  // If the left input is an add of a constant, flatten the expression tree.
  const Node *n = in(Address);
  const Node *intr = n->isa_CastIntrinsic();
  if (n->is_AddP() && n->in(Base) == in(Base) || intr && intr->in(Base)->is_AddP()) {
    const AddPNode *addp = intr ? intr->in(Base)->as_AddP() : n->as_AddP(); // Left input is an AddP

Value Types / Value Objects

Value Types / Value Objects

Construction example (from "Value types for Java"):

final __ByValue class Point {
  public final int x;
  public final int y;

  public Point(int x1, int y1) {
    ; public static <new>(II)QPoint;
    //implicit initialize 'this' to zero
    ; vnew Point;    // stack [ this ]
    this.x = x1;
    ; iload 0 ('x1') // stack [ this x1 ]
    ; vputfield 'x'  // stack [ this ]
    this.y = y1;
    ; iload 1 ('y1') // stack [ this, y1 ]
    ; vputfield 'y'  // stack [ this ]
    //implicit: return 'this'
    ; vreturn
  }
}
Point p = new Point(1, 2);
  ; iconst_1
  ; iconst_2
  ; invokestatic Point.<new>(II)QPoint;
  ; vstore 6 (create 'p')

Classic allocation:

Point p = new Point(1, 2);
  ; new Point;
  ; dup
  ; iconst_1
  ; iconst_2
  ; invokespecial Point.<init>(II)V;
  ; astore 6 (set 'p')

Value Types / Value Objects

Nested values and array example (from "Value types for Java"):

final __ByValue class Path {
  final Point start;
  final Point end;
}

class Foo {      // regular class
  Path path;     // regular field
}

int startX = path.start.x;
  ; aload 0 ('this')     // stack [ Foo ]
  ; getfield Foo.path    // stack [ path ]
  ; vgetfield Path.start // stack [ start ]
  ; vgetfield Point.x    // stack [ x ]
  ; istore 1             // stack [ ]
Point[] points = new Point[100];
  ; bipush 100
  ; vnewarray Point
  ; astore 1 (create 'points')
points[3] = new Point(2, 19);
  ; aload 1 ('points')
  ; iconst_3
  ; iconst_2
  ; bipush 19
  ; invokestatic Point.<new>(II)QPoint;
  ; vastore

Object Layout and Arrays 2.0 in Project Panama

"Project Panama: Interconnecting JVM and native code"

But also:

This mail nicely explains the relation between the various subprojects.





Questions?