Reduce allocation rate by minimizing number of fields in RecordedEvent JEP-349-branch
authoregahlin
Mon, 08 Jul 2019 23:08:05 +0200
branchJEP-349-branch
changeset 57460 bcbc53560c77
parent 57459 df39f8d8f4d6
child 57461 b72401c57125
Reduce allocation rate by minimizing number of fields in RecordedEvent
src/jdk.jfr/share/classes/jdk/jfr/consumer/EventParser.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/ObjectFactory.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClass.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClassLoader.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedFrame.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedStackTrace.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThread.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThreadGroup.java
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventParser.java	Mon Jul 08 18:11:26 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventParser.java	Mon Jul 08 23:08:05 2019 +0200
@@ -54,6 +54,7 @@
     private boolean ordered;
     private long firstNanos;
     private long thresholdNanos = -1;
+    private ObjectContext objectContext;
 
     EventParser(TimeConverter timeConverter, EventType type, Parser[] parsers) {
         this.timeConverter = timeConverter;
@@ -63,7 +64,8 @@
         this.startIndex = hasDuration ? 2 : 1;
         this.length = parsers.length - startIndex;
         this.valueDescriptors = type.getFields();
-        this.unorderedEvent = new RecordedEvent(eventType, valueDescriptors, new Object[length], 0L, 0L, timeConverter);
+        this.objectContext = new ObjectContext(type, valueDescriptors, timeConverter);
+        this.unorderedEvent = new RecordedEvent(objectContext, new Object[length], 0L, 0L);
     }
 
     private RecordedEvent cachedEvent() {
@@ -75,7 +77,7 @@
             }
             RecordedEvent event = eventCache[index];
             if (event == null) {
-                event = new RecordedEvent(eventType, valueDescriptors, new Object[length], 0L, 0L, timeConverter);
+                event = new RecordedEvent(objectContext, new Object[length], 0L, 0L);
                 eventCache[index] = event;
             }
             index++;
@@ -134,7 +136,7 @@
                 for (int i = 0; i < length; i++) {
                     values[i] = parsers[startIndex + i].parse(input);
                 }
-                return new RecordedEvent(eventType, valueDescriptors, values, startTicks, endTicks, timeConverter);
+                return new RecordedEvent(objectContext, values, startTicks, endTicks);
             }
         }
         return null;
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ObjectFactory.java	Mon Jul 08 18:11:26 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/ObjectFactory.java	Mon Jul 08 23:08:05 2019 +0200
@@ -25,9 +25,6 @@
 
 package jdk.jfr.consumer;
 
-import java.util.List;
-
-import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.Type;
 
 /**
@@ -65,10 +62,10 @@
         return null;
     }
 
-    private final List<ValueDescriptor> valueDescriptors;
+    private final ObjectContext objectContext;
 
-    ObjectFactory(Type type) {
-        this.valueDescriptors = type.getFields();
+    ObjectFactory(Type type, TimeConverter timeConverter) {
+        this.objectContext = new ObjectContext(null, type.getFields(), timeConverter);
     }
 
     T createObject(long id, Object value) {
@@ -76,10 +73,10 @@
             return null;
         }
         if (value instanceof Object[]) {
-            return createTyped(valueDescriptors, id, (Object[]) value);
+            return createTyped(objectContext, id, (Object[]) value);
         }
         throw new InternalError("Object factory must have struct type. Type was " + value.getClass().getName());
     }
 
-    abstract T createTyped(List<ValueDescriptor> valueDescriptors, long id, Object[] values);
+    abstract T createTyped(ObjectContext objectContextm, long id, Object[] values);
 }
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClass.java	Mon Jul 08 18:11:26 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClass.java	Mon Jul 08 23:08:05 2019 +0200
@@ -26,9 +26,7 @@
 package jdk.jfr.consumer;
 
 import java.lang.reflect.Modifier;
-import java.util.List;
 
-import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.Type;
 
 /**
@@ -39,10 +37,10 @@
 public final class RecordedClass extends RecordedObject {
 
     static ObjectFactory<RecordedClass> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedClass>(type) {
+        return new ObjectFactory<RecordedClass>(type, timeConverter) {
             @Override
-            RecordedClass createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedClass(desc, id, object, timeConverter);
+            RecordedClass createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return new RecordedClass(objectContext, id, values);
             }
         };
     }
@@ -50,8 +48,8 @@
     private final long uniqueId;
 
     // package private
-    private RecordedClass(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) {
-        super(descriptors, values, timeConverter);
+    private RecordedClass(ObjectContext objectContext, long id, Object[] values) {
+        super(objectContext, values);
         this.uniqueId = id;
     }
 
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClassLoader.java	Mon Jul 08 18:11:26 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClassLoader.java	Mon Jul 08 23:08:05 2019 +0200
@@ -25,9 +25,6 @@
 
 package jdk.jfr.consumer;
 
-import java.util.List;
-
-import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.Type;
 
 /**
@@ -38,10 +35,10 @@
 public final class RecordedClassLoader extends RecordedObject {
 
     static ObjectFactory<RecordedClassLoader> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedClassLoader>(type) {
+        return new ObjectFactory<RecordedClassLoader>(type, timeConverter) {
             @Override
-            RecordedClassLoader createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedClassLoader(desc, id, object, timeConverter);
+            RecordedClassLoader createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return new RecordedClassLoader(objectContext, id, values);
             }
         };
     }
@@ -49,8 +46,8 @@
     private final long uniqueId;
 
     // package private
-    private RecordedClassLoader(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) {
-        super(descriptors, values, timeConverter);
+    private RecordedClassLoader(ObjectContext objectContext, long id, Object[] values) {
+        super(objectContext, values);
         this.uniqueId = id;
     }
 
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java	Mon Jul 08 18:11:26 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java	Mon Jul 08 23:08:05 2019 +0200
@@ -39,14 +39,12 @@
  * @since 9
  */
 public final class RecordedEvent extends RecordedObject {
-    private final EventType eventType;
     long startTimeTicks;
     long endTimeTicks;
 
     // package private
-    RecordedEvent(EventType type, List<ValueDescriptor> vds, Object[] values, long startTimeTicks, long endTimeTicks, TimeConverter timeConverter) {
-        super(vds, values, timeConverter);
-        this.eventType = type;
+    RecordedEvent(ObjectContext objectContext, Object[] values, long startTimeTicks, long endTimeTicks) {
+        super(objectContext, values);
         this.startTimeTicks = startTimeTicks;
         this.endTimeTicks = endTimeTicks;
     }
@@ -77,7 +75,7 @@
      * @return the event type, not {@code null}
      */
     public EventType getEventType() {
-        return eventType;
+        return objectContext.eventType;
     }
 
     /**
@@ -118,7 +116,7 @@
      */
     @Override
     public List<ValueDescriptor> getFields() {
-        return getEventType().getFields();
+        return objectContext.fields;
     }
 
     protected final Object objectAt(int index) {
@@ -135,14 +133,14 @@
     }
 
     private boolean hasDuration() {
-        return objects.length + 2 == descriptors.size();
+        return objects.length + 2 == objectContext.fields.size();
     }
 
     private long getStartTimeNanos() {
-        return timeConverter.convertTimestamp(startTimeTicks);
+        return objectContext.timeConverter.convertTimestamp(startTimeTicks);
     }
 
     private long getEndTimeNanos() {
-        return timeConverter.convertTimestamp(endTimeTicks);
+        return objectContext.timeConverter.convertTimestamp(endTimeTicks);
     }
 }
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedFrame.java	Mon Jul 08 18:11:26 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedFrame.java	Mon Jul 08 23:08:05 2019 +0200
@@ -26,9 +26,7 @@
 package jdk.jfr.consumer;
 
 import java.lang.reflect.Modifier;
-import java.util.List;
 
-import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.Type;
 
 /**
@@ -39,17 +37,17 @@
 public final class RecordedFrame extends RecordedObject {
 
     static ObjectFactory<RecordedFrame> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedFrame>(type) {
+        return new ObjectFactory<RecordedFrame>(type, timeConverter) {
             @Override
-            RecordedFrame createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedFrame(desc, object, timeConverter);
+            RecordedFrame createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return new RecordedFrame(objectContext, values);
             }
         };
     }
 
     // package private
-    RecordedFrame(List<ValueDescriptor> desc, Object[] objects, TimeConverter timeConverter) {
-        super(desc, objects, timeConverter);
+    RecordedFrame(ObjectContext objectContext, Object[] objects) {
+        super(objectContext, objects);
     }
 
     /**
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java	Mon Jul 08 18:11:26 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java	Mon Jul 08 23:08:05 2019 +0200
@@ -26,9 +26,7 @@
 package jdk.jfr.consumer;
 
 import java.lang.reflect.Modifier;
-import java.util.List;
 
-import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.Type;
 
 /**
@@ -39,16 +37,16 @@
 public final class RecordedMethod extends RecordedObject {
 
     static ObjectFactory<RecordedMethod> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedMethod>(type) {
+        return new ObjectFactory<RecordedMethod>(type, timeConverter) {
             @Override
-            RecordedMethod createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedMethod(desc, object, timeConverter);
+            RecordedMethod createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return new RecordedMethod(objectContext, values);
             }
         };
     }
 
-    private RecordedMethod(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
-        super(descriptors, objects, timeConverter);
+    private RecordedMethod(ObjectContext objectContext, Object[] values) {
+        super(objectContext, values);
     }
 
     /**
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java	Mon Jul 08 18:11:26 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java	Mon Jul 08 23:08:05 2019 +0200
@@ -96,14 +96,12 @@
     }
 
     final Object[] objects;
-    protected final List<ValueDescriptor> descriptors;
-    protected final TimeConverter timeConverter;
+    final ObjectContext objectContext;
 
     // package private, not to be subclassed outside this package
-    RecordedObject(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
-        this.descriptors = descriptors;
+    RecordedObject(ObjectContext objectContext, Object[] objects) {
+        this.objectContext = objectContext;
         this.objects = objects;
-        this.timeConverter = timeConverter;
     }
 
     // package private
@@ -133,7 +131,7 @@
      */
     public boolean hasField(String name) {
         Objects.requireNonNull(name);
-        for (ValueDescriptor v : descriptors) {
+        for (ValueDescriptor v : objectContext.fields) {
             if (v.getName().equals(name)) {
                 return true;
             }
@@ -141,7 +139,7 @@
         int dotIndex = name.indexOf(".");
         if (dotIndex > 0) {
             String structName = name.substring(0, dotIndex);
-            for (ValueDescriptor v : descriptors) {
+            for (ValueDescriptor v : objectContext.fields) {
                 if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
                     RecordedObject child = getValue(structName);
                     if (child != null) {
@@ -208,7 +206,7 @@
     private Object getValue(String name, boolean allowUnsigned) {
         Objects.requireNonNull(name);
         int index = 0;
-        for (ValueDescriptor v : descriptors) {
+        for (ValueDescriptor v : objectContext.fields) {
             if (name.equals(v.getName())) {
                 Object object = objectAt(index);
                 if (object == null) {
@@ -236,7 +234,7 @@
                         return structifyArray(v, array, 0);
                     }
                     // struct
-                    return new RecordedObject(v.getFields(), (Object[]) object, timeConverter);
+                    return new RecordedObject(objectContext.getInstance(v), (Object[]) object);
                 }
             }
             index++;
@@ -245,7 +243,7 @@
         int dotIndex = name.indexOf(".");
         if (dotIndex > 0) {
             String structName = name.substring(0, dotIndex);
-            for (ValueDescriptor v : descriptors) {
+            for (ValueDescriptor v : objectContext.fields) {
                 if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
                     RecordedObject child = getValue(structName);
                     String subName = name.substring(dotIndex + 1);
@@ -297,7 +295,7 @@
     private <T> T getTypedValue(String name, String typeName) {
         Objects.requireNonNull(name);
         // Validate name and type first
-        getValueDescriptor(descriptors, name, typeName);
+        getValueDescriptor(objectContext.fields, name, typeName);
         return getValue(name);
     }
 
@@ -306,15 +304,16 @@
             return null;
         }
         Object[] structArray = new Object[array.length];
+        ObjectContext objContext = objectContext.getInstance(v);
         for (int i = 0; i < structArray.length; i++) {
             Object arrayElement = array[i];
             if (dimension == 0) {
                 // No general way to handle structarrays
                 // without invoking ObjectFactory for every instance (which may require id)
                 if (isStackFrameType(v.getTypeName())) {
-                    structArray[i] = new RecordedFrame(v.getFields(), (Object[]) arrayElement, timeConverter);
+                    structArray[i] = new RecordedFrame(objContext, (Object[]) arrayElement);
                 } else {
-                    structArray[i] = new RecordedObject(v.getFields(), (Object[]) arrayElement, timeConverter);
+                    structArray[i] = new RecordedObject(objContext, (Object[]) arrayElement);
                 }
             } else {
                 structArray[i] = structifyArray(v, (Object[]) arrayElement, dimension - 1);
@@ -339,7 +338,7 @@
      * @return the fields, not {@code null}
      */
     public List<ValueDescriptor> getFields() {
-        return descriptors;
+        return objectContext.fields;
     }
 
     /**
@@ -761,7 +760,7 @@
     }
 
     private Duration getDuration(long timespan, String name) throws InternalError {
-        ValueDescriptor v = getValueDescriptor(descriptors, name, null);
+        ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null);
         if (timespan == Long.MIN_VALUE) {
             return Duration.ofSeconds(Long.MIN_VALUE, 0);
         }
@@ -777,7 +776,7 @@
             case Timespan.NANOSECONDS:
                 return Duration.ofNanos(timespan);
             case Timespan.TICKS:
-                return Duration.ofNanos(timeConverter.convertTimespan(timespan));
+                return Duration.ofNanos(objectContext.timeConverter.convertTimespan(timespan));
             }
             throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timespan unit " + ts.value());
         }
@@ -840,7 +839,7 @@
     }
 
     private Instant getInstant(long timestamp, String name) {
-        ValueDescriptor v = getValueDescriptor(descriptors, name, null);
+        ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null);
         Timestamp ts = v.getAnnotation(Timestamp.class);
         if (ts != null) {
             if (timestamp == Long.MIN_VALUE) {
@@ -850,7 +849,7 @@
             case Timestamp.MILLISECONDS_SINCE_EPOCH:
                 return Instant.ofEpochMilli(timestamp);
             case Timestamp.TICKS:
-                return Instant.ofEpochSecond(0, timeConverter.convertTimestamp(timestamp));
+                return Instant.ofEpochSecond(0, objectContext.timeConverter.convertTimestamp(timestamp));
             }
             throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timestamp unit " + ts.value());
         }
@@ -930,7 +929,7 @@
         if (instant.equals(Instant.MIN)) {
             return OffsetDateTime.MIN;
         }
-        return OffsetDateTime.ofInstant(getInstant(name), timeConverter.getZoneOffset());
+        return OffsetDateTime.ofInstant(getInstant(name), objectContext.timeConverter.getZoneOffset());
     }
 
     private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) {
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedStackTrace.java	Mon Jul 08 18:11:26 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedStackTrace.java	Mon Jul 08 23:08:05 2019 +0200
@@ -29,7 +29,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.Type;
 
 /**
@@ -40,16 +39,16 @@
 public final class RecordedStackTrace extends RecordedObject {
 
     static ObjectFactory<RecordedStackTrace> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedStackTrace>(type) {
+        return new ObjectFactory<RecordedStackTrace>(type, timeConverter) {
             @Override
-            RecordedStackTrace createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedStackTrace(desc, object, timeConverter);
+            RecordedStackTrace createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return new RecordedStackTrace(objectContext, values);
             }
         };
     }
 
-    private RecordedStackTrace(List<ValueDescriptor> desc, Object[] values, TimeConverter timeConverter) {
-        super(desc, values, timeConverter);
+    private RecordedStackTrace(ObjectContext objectContext, Object[] values) {
+        super(objectContext, values);
     }
 
     /**
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThread.java	Mon Jul 08 18:11:26 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThread.java	Mon Jul 08 23:08:05 2019 +0200
@@ -25,9 +25,6 @@
 
 package jdk.jfr.consumer;
 
-import java.util.List;
-
-import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.Type;
 
 /**
@@ -38,18 +35,18 @@
 public final class RecordedThread extends RecordedObject {
 
     static ObjectFactory<RecordedThread> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedThread>(type) {
+        return new ObjectFactory<RecordedThread>(type, timeConverter) {
             @Override
-            RecordedThread createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedThread(desc, id, object, timeConverter);
+            RecordedThread createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return new RecordedThread(objectContext, id, values);
             }
         };
     }
 
     private final long uniqueId;
 
-    private RecordedThread(List<ValueDescriptor> descriptors, long id, Object[] values,  TimeConverter timeConverter) {
-        super(descriptors, values, timeConverter);
+    private RecordedThread(ObjectContext objectContext, long id, Object[] values) {
+        super(objectContext, values);
         this.uniqueId = id;
     }
 
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThreadGroup.java	Mon Jul 08 18:11:26 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThreadGroup.java	Mon Jul 08 23:08:05 2019 +0200
@@ -25,9 +25,6 @@
 
 package jdk.jfr.consumer;
 
-import java.util.List;
-
-import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.Type;
 
 /**
@@ -38,16 +35,16 @@
 public final class RecordedThreadGroup extends RecordedObject {
 
     static ObjectFactory<RecordedThreadGroup> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedThreadGroup>(type) {
+        return new ObjectFactory<RecordedThreadGroup>(type, timeConverter) {
             @Override
-            RecordedThreadGroup createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedThreadGroup(desc, object, timeConverter);
+            RecordedThreadGroup createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return new RecordedThreadGroup(objectContext, values);
             }
         };
     }
 
-    private RecordedThreadGroup(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
-        super(descriptors, objects, timeConverter);
+    private RecordedThreadGroup(ObjectContext objectContext, Object[] values) {
+        super(objectContext, values);
     }
 
     /**