changeset 59854:2342d5af52b7

8246436: JFR: Avoid parsing metadata.xml during startup Reviewed-by: mgronlun, erikj, redestad
author egahlin
date Mon, 22 Jun 2020 08:09:23 +0200
parents 216c6baa0564
children 1e5438d88425
files make/CompileToolsHotspot.gmk make/Main.gmk make/ToolsHotspot.gmk make/hotspot/gensrc/GensrcJfr.gmk make/modules/jdk.jfr/Copy.gmk make/modules/jdk.jfr/Gendata.gmk make/src/classes/build/tools/jfr/GenerateJfrFiles.java src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataHandler.java src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataLoader.java src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java
diffstat 10 files changed, 919 insertions(+), 689 deletions(-) [+]
line wrap: on
line diff
--- a/make/CompileToolsHotspot.gmk	Sat Jun 20 15:11:19 2020 +0800
+++ b/make/CompileToolsHotspot.gmk	Mon Jun 22 08:09:23 2020 +0200
@@ -36,6 +36,24 @@
 # Hook to include the corresponding custom file, if present.
 $(eval $(call IncludeCustomExtension, hotspot/CompileTools.gmk))
 
+################################################################################
+# Build tools needed for the JFR source code generation
+
+HOTSPOT_TOOLS_SRCDIR := $(TOPDIR)/make/src/classes
+
+HOTSPOT_TOOLS_OUTPUTDIR := $(BUILDTOOLS_OUTPUTDIR)/buildtools/hotspot_tools_classes
+
+$(eval $(call SetupJavaCompilation, BUILD_TOOLS_HOTSPOT, \
+    COMPILER := bootjdk, \
+    SRC := $(HOTSPOT_TOOLS_SRCDIR), \
+    BIN := $(HOTSPOT_TOOLS_OUTPUTDIR), \
+))
+
+TARGETS += $(BUILD_TOOLS_HOTSPOT)
+
+
+################################################################################
+# Graal build tools
 ifeq ($(INCLUDE_GRAAL), true)
   VM_CI_SRC_DIR := $(TOPDIR)/src/jdk.internal.vm.ci/share/classes
 
--- a/make/Main.gmk	Sat Jun 20 15:11:19 2020 +0800
+++ b/make/Main.gmk	Mon Jun 22 08:09:23 2020 +0200
@@ -804,7 +804,7 @@
 
   # Declare dependencies between hotspot-<variant>* targets
   $(foreach v, $(JVM_VARIANTS), \
-      $(eval hotspot-$v-gensrc: java.base-copy) \
+      $(eval hotspot-$v-gensrc: java.base-copy buildtools-hotspot) \
       $(eval hotspot-$v-libs: hotspot-$v-gensrc java.base-copy) \
   )
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/ToolsHotspot.gmk	Mon Jun 22 08:09:23 2020 +0200
@@ -0,0 +1,47 @@
+#
+# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+ifndef _TOOLS_HOTSPOT_GMK
+_TOOLS_HOTSPOT_GMK := 1
+
+include JavaCompilation.gmk
+
+HOTSPOT_TOOLS_OUTPUTDIR := $(BUILDTOOLS_OUTPUTDIR)/buildtools/hotspot_tools_classes
+
+################################################################################
+# To avoid reevaluating the compilation setup for the tools each time this file
+# is included, the actual compilation is handled by CompileToolsHotspot.gmk. The
+# following trick is used to be able to declare a dependency on the built tools.
+BUILD_TOOLS_HOTSPOT := $(call SetupJavaCompilationCompileTarget, \
+    BUILD_TOOLS_HOTSPOT, $(HOTSPOT_TOOLS_OUTPUTDIR))
+
+################################################################################
+
+TOOL_JFR_GEN := $(JAVA_SMALL) -cp $(HOTSPOT_TOOLS_OUTPUTDIR) \
+    build.tools.jfr.GenerateJfrFiles
+
+##########################################################################################
+
+endif # _TOOLS_HOTSPOT_GMK
--- a/make/hotspot/gensrc/GensrcJfr.gmk	Sat Jun 20 15:11:19 2020 +0800
+++ b/make/hotspot/gensrc/GensrcJfr.gmk	Mon Jun 22 08:09:23 2020 +0200
@@ -24,39 +24,23 @@
 #
 
 ################################################################################
-# Build tools needed for the JFR source code generation
-
-JFR_TOOLS_SRCDIR := $(TOPDIR)/make/src/classes
-JFR_TOOLS_OUTPUTDIR := $(JVM_VARIANT_OUTPUTDIR)/buildtools/tools_classes
-
-$(eval $(call SetupJavaCompilation, BUILD_JFR_TOOLS, \
-    COMPILER := bootjdk, \
-    SMALL_JAVA := false, \
-    SRC := $(JFR_TOOLS_SRCDIR), \
-    BIN := $(JFR_TOOLS_OUTPUTDIR), \
-    DISABLED_WARNINGS := try, \
-))
-
-TARGETS += $(BUILD_JFR_TOOLS)
-
-################################################################################
 # Setup make rules for JFR gensrc file generation.
 #
-TOOL_JFR_GEN := $(JAVA_SMALL) -cp $(JFR_TOOLS_OUTPUTDIR) build.tools.jfr.GenerateJfrFiles
 
+include $(TOPDIR)/make/ToolsHotspot.gmk
+
+JFR_SRCDIR := $(TOPDIR)/src/hotspot/share/jfr/metadata
 JFR_OUTPUTDIR := $(JVM_VARIANT_OUTPUTDIR)/gensrc/jfrfiles
-JFR_SRCDIR := $(TOPDIR)/src/hotspot/share/jfr/metadata
 
 # Changing these will trigger a rebuild of generated jfr files.
 METADATA_XML := $(JFR_SRCDIR)/metadata.xml
 METADATA_XSD := $(JFR_SRCDIR)/metadata.xsd
 
-$(eval $(call SetupExecute, jfr_gen, \
+$(eval $(call SetupExecute, jfr_gen_headers, \
     INFO := Generating JFR header files, \
-    DEPS := $(METADATA_XML) $(METADATA_XSD) $(BUILD_JFR_TOOLS), \
+    DEPS := $(METADATA_XML) $(METADATA_XSD) $(BUILD_TOOLS_HOTSPOT), \
     OUTPUT_DIR := $(JFR_OUTPUTDIR), \
-    COMMAND := $(TOOL_JFR_GEN) $(METADATA_XML) $(METADATA_XSD) $(JFR_OUTPUTDIR), \
+    COMMAND := $(TOOL_JFR_GEN) --mode headers --xml $(METADATA_XML) --xsd $(METADATA_XSD) --output $(JFR_OUTPUTDIR), \
 ))
 
-JFR_FILES := $(jfr_gen_TARGET)
-TARGETS += $(JFR_FILES)
+TARGETS += $(jfr_gen_headers)
--- a/make/modules/jdk.jfr/Copy.gmk	Sat Jun 20 15:11:19 2020 +0800
+++ b/make/modules/jdk.jfr/Copy.gmk	Mon Jun 22 08:09:23 2020 +0200
@@ -27,14 +27,6 @@
 
 ################################################################################
 
-$(eval $(call SetupCopyFiles, COPY_JFR_METADATA, \
-    SRC := $(TOPDIR)/src/hotspot/share/jfr/metadata, \
-    DEST := $(JDK_OUTPUTDIR)/modules/jdk.jfr/jdk/jfr/internal/types, \
-    FILES := metadata.xml \
-))
-
-TARGETS += $(COPY_JFR_METADATA)
-
 JFR_CONF_DIR := $(TOPDIR)/src/jdk.jfr/share/conf/jfr
 $(eval $(call SetupCopyFiles, COPY_JFR_CONF, \
     DEST := $(LIB_DST_DIR)/jfr, \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/modules/jdk.jfr/Gendata.gmk	Mon Jun 22 08:09:23 2020 +0200
@@ -0,0 +1,48 @@
+#
+# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+################################################################################
+
+include ToolsHotspot.gmk
+include Execute.gmk
+
+JFR_SRCDIR := $(TOPDIR)/src/hotspot/share/jfr/metadata
+JFR_DATA_OUTPUTDIR := $(OUTPUTDIR)/jdk/modules/jdk.jfr/jdk/jfr/internal/types
+JFR_DATA_OUTPUTFILE := $(JFR_DATA_OUTPUTDIR)/metadata.bin
+JFR_DATA_SUPPORTDIR := $(SUPPORT_OUTPUTDIR)/gendata/jdk.jfr
+
+# Changing these will trigger a rebuild of generated jfr files.
+METADATA_XML := $(JFR_SRCDIR)/metadata.xml
+METADATA_XSD := $(JFR_SRCDIR)/metadata.xsd
+
+$(eval $(call SetupExecute, jfr_gen_metadata, \
+    INFO := Generating JFR metadata, \
+    DEPS := $(METADATA_XML) $(METADATA_XSD) $(BUILD_TOOLS_HOTSPOT), \
+    OUTPUT_FILE := $(JFR_DATA_OUTPUTFILE), \
+    SUPPORT_DIR := $(JFR_DATA_SUPPORTDIR), \
+    COMMAND := $(TOOL_JFR_GEN) --mode metadata --xml $(METADATA_XML) --xsd $(METADATA_XSD) --output $(JFR_DATA_OUTPUTFILE), \
+))
+
+TARGETS += $(jfr_gen_metadata)
--- a/make/src/classes/build/tools/jfr/GenerateJfrFiles.java	Sat Jun 20 15:11:19 2020 +0800
+++ b/make/src/classes/build/tools/jfr/GenerateJfrFiles.java	Mon Jun 22 08:09:23 2020 +0200
@@ -1,16 +1,19 @@
 package build.tools.jfr;
 
 import java.io.BufferedOutputStream;
+import java.io.Closeable;
+import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.StringJoiner;
 import java.util.function.Predicate;
 
@@ -25,94 +28,107 @@
 import org.xml.sax.SAXParseException;
 import org.xml.sax.helpers.DefaultHandler;
 
+/**
+ * Purpose of this program is twofold:
+ *
+ * 1) Generate C++ classes to be used when writing native events for HotSpot.
+ *
+ * 2) Generate metadata (label, descriptions, field layout etc.) from XML
+ * (metadata.xml) into a binary format (metadata.bin) that can be read quickly
+ * during startup by the jdk.jfr module.
+ *
+ * INPUT FILES:
+ *
+ * -  metadata.xml  File that contains descriptions of events and types
+ * -  metadata.xsd  Schema that verifies that metadata.xml is legit XML
+ *
+ * OUTPUT FILES:
+ *
+ * MODE: headers
+ *
+ * - jfrEventIds.hpp      List of IDs so events can be identified from native
+ * - jfrTypes.hpp         List of IDs so types can be identified from native
+ * - jfrPeriodic.hpp      Dispatch mechanism so Java can emit native periodic events
+ * - jfrEventControl.hpp  Data structure for native event settings.
+ * - jfrEventClasses.hpp  C++ event classes that can write data into native buffers
+ *
+ * MODE: metadata
+ *
+ *  - metadata.bin        Binary representation of the information in metadata.xml
+ *
+ */
 public class GenerateJfrFiles {
 
+    enum OutputMode {
+        headers, metadata
+    }
+
+    private static void printUsage(PrintStream out) {
+        out.println("Usage: java GenerateJfrFiles[.java]");
+        out.println(" --mode <headers|metadata>");
+        out.println(" --xml <path-to-metadata.xml> ");
+        out.println(" --xsd <path-to-metadata.xsd>");
+        out.println(" --output <output-file-or-directory>");
+    }
+
+    private static String consumeOption(String option, List<String> argList) throws Exception {
+        int index = argList.indexOf(option);
+        if (index >= 0 && index <= argList.size() - 2) {
+            String result = argList.get(index + 1);
+            argList.remove(index);
+            argList.remove(index);
+            return result;
+        }
+        throw new IllegalArgumentException("missing option " + option);
+    }
+
     public static void main(String... args) throws Exception {
-        if (args.length != 3) {
-            System.err.println("Incorrect number of command line arguments.");
-            System.err.println("Usage:");
-            System.err.println("java GenerateJfrFiles[.java] <path-to-metadata.xml> <path-to-metadata.xsd> <output-directory>");
-            System.exit(1);
-        }
         try {
-            File metadataXml = new File(args[0]);
-            File metadataSchema = new File(args[1]);
-            File outputDirectory = new File(args[2]);
+            List<String> argList = new ArrayList<>();
+            argList.addAll(Arrays.asList(args));
+            String mode = consumeOption("--mode", argList);
+            String output = consumeOption("--output", argList);
+            String xml = consumeOption("--xml", argList);
+            String xsd = consumeOption("--xsd", argList);
+            if (!argList.isEmpty()) {
+                throw new IllegalArgumentException("unknown option " + argList);
+            }
+            OutputMode outputMode = OutputMode.valueOf(mode);
+            File xmlFile = new File(xml);
+            File xsdFile = new File(xsd);
 
-            Metadata metadata = new Metadata(metadataXml, metadataSchema);
+            Metadata metadata = new Metadata(xmlFile, xsdFile);
             metadata.verify();
             metadata.wireUpTypes();
 
-            TypeCounter typeCounter = new TypeCounter();
-            printJfrEventIdsHpp(metadata, typeCounter, outputDirectory);
-            printJfrTypesHpp(metadata, typeCounter, outputDirectory);
-            printJfrPeriodicHpp(metadata, outputDirectory);
-            printJfrEventControlHpp(metadata, typeCounter, outputDirectory);
-            printJfrEventClassesHpp(metadata, outputDirectory);
+            if (outputMode == OutputMode.headers) {
+                File outputDir = new File(output);
+                printJfrEventIdsHpp(metadata, new File(outputDir, "jfrEventIds.hpp"));
+                printJfrTypesHpp(metadata, new File(outputDir, "jfrTypes.hpp"));
+                printJfrPeriodicHpp(metadata, new File(outputDir, "jfrPeriodic.hpp"));
+                printJfrEventControlHpp(metadata, new File(outputDir, "jfrEventControl.hpp"));
+                printJfrEventClassesHpp(metadata, new File(outputDir, "jfrEventClasses.hpp"));
+            }
 
+            if (outputMode == OutputMode.metadata) {
+                File outputFile  = new File(output);
+                try (var b = new DataOutputStream(
+                        new BufferedOutputStream(
+                            new FileOutputStream(outputFile)))) {
+                    metadata.persist(b);
+                }
+            }
+            System.exit(0);
+        } catch (IllegalArgumentException iae) {
+            System.err.println();
+            System.err.println("GenerateJfrFiles: " + iae.getMessage());
+            System.err.println();
+            printUsage(System.err);
+            System.err.println();
         } catch (Exception e) {
             e.printStackTrace();
-            System.exit(1);
         }
-    }
-
-    static class TypeCounter {
-        final static long RESERVED_EVENT_COUNT = 2;
-        long typeId = -1;
-        long eventId = -1;
-        long eventCount = 0;
-        String firstTypeName;
-        String lastTypeName;
-        String firstEventName;
-        String lastEventname;
-
-        public long nextEventId(String name) {
-            eventCount++;
-            if (eventId == -1) {
-                eventId = firstEventId();
-                firstEventName = lastEventname = name;
-                return eventId;
-            }
-            lastEventname = name;
-            return ++eventId;
-        }
-
-        public long nextTypeId(String typeName) {
-            if (typeId == -1) {
-                lastTypeName = firstTypeName = typeName;
-                typeId = lastEventId();
-            }
-            lastTypeName = typeName;
-            return ++typeId;
-        }
-
-        public long firstEventId() {
-            return RESERVED_EVENT_COUNT;
-        }
-
-        public long lastEventId() {
-            return eventId == -1 ? firstEventId() : eventId;
-        }
-
-        public long eventCount() {
-            return eventCount;
-        }
-
-        public String firstTypeName() {
-            return firstTypeName;
-        }
-
-        public String lastTypeName() {
-            return lastTypeName;
-        }
-
-        public String firstEventName() {
-            return firstEventName;
-        }
-
-        public String lastEventName() {
-            return lastEventname;
-        }
+        System.exit(1);
     }
 
     static class XmlType {
@@ -121,28 +137,99 @@
         final String parameterType;
         final String javaType;
         final boolean unsigned;
+        final String contentType;
 
-        XmlType(String name, String fieldType, String parameterType, String javaType, boolean unsigned) {
+        XmlType(String name, String fieldType, String parameterType, String javaType, String contentType,
+                boolean unsigned) {
             this.name = name;
             this.fieldType = fieldType;
             this.parameterType = parameterType;
             this.javaType = javaType;
             this.unsigned = unsigned;
+            this.contentType = contentType;
+        }
+    }
+
+    static class XmlContentType {
+        final String name;
+        final String annotation;
+
+        XmlContentType(String name, String annotation) {
+            this.name = name;
+            this.annotation = annotation;
         }
     }
 
     static class TypeElement {
         List<FieldElement> fields = new ArrayList<>();
         String name;
-        String fieldType;
-        String parameterType;
-        boolean supportStruct;
+        String javaType;
+        String label = "";
+        String description = "";
+        String category = "";
+        boolean thread;
+        boolean stackTrace;
+        boolean startTime;
+        String period = "";
+        boolean cutoff;
+        boolean experimental;
+        long id;
+        boolean isEvent;
+        boolean isRelation;
+        boolean supportStruct = false;
+        String commitState;
+        public boolean primitive;
+
+        public void persist(DataOutputStream pos) throws IOException {
+            pos.writeInt(fields.size());
+            for (FieldElement field : fields) {
+                field.persist(pos);
+            }
+            pos.writeUTF(javaType);
+            pos.writeUTF(label);
+            pos.writeUTF(description);
+            pos.writeUTF(category);
+            pos.writeBoolean(thread);
+            pos.writeBoolean(stackTrace);
+            pos.writeBoolean(startTime);
+            pos.writeUTF(period);
+            pos.writeBoolean(cutoff);
+            pos.writeBoolean(experimental);
+            pos.writeLong(id);
+            pos.writeBoolean(isEvent);
+            pos.writeBoolean(isRelation);
+        }
     }
 
     static class Metadata {
+        static class TypeCounter {
+            final long first;
+            long last = -1;
+            long count = 0;
+            long id = -1;
+
+            TypeCounter(long startId) {
+                this.first = startId;
+            }
+
+            long next() {
+                id = (id == -1) ? first : id + 1;
+                count++;
+                last = id;
+                return id;
+            }
+        }
+
+        static int RESERVED_EVENT_COUNT = 2;
         final Map<String, TypeElement> types = new LinkedHashMap<>();
         final Map<String, XmlType> xmlTypes = new LinkedHashMap<>();
-        Metadata(File metadataXml, File metadataSchema) throws ParserConfigurationException, SAXException, FileNotFoundException, IOException {
+        final Map<String, XmlContentType> xmlContentTypes = new LinkedHashMap<>();
+        int lastEventId;
+        private TypeCounter eventCounter;
+        private TypeCounter typeCounter;
+
+        Metadata(File metadataXml, File metadataSchema)
+                throws ParserConfigurationException, SAXException, FileNotFoundException, IOException {
             SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
             SAXParserFactory factory = SAXParserFactory.newInstance();
             factory.setSchema(schemaFactory.newSchema(metadataSchema));
@@ -150,16 +237,19 @@
             sp.parse(metadataXml, new MetadataHandler(this));
         }
 
-        List<EventElement> getEvents() {
-            return getList(t -> t.getClass() == EventElement.class);
+        public void persist(DataOutputStream pos) throws IOException {
+            pos.writeInt(types.values().size());
+            for (TypeElement t : types.values()) {
+                t.persist(pos);
+            }
+        }
+
+        List<TypeElement> getEvents() {
+            return getList(t -> t.isEvent);
         }
 
         List<TypeElement> getEventsAndStructs() {
-            return getList(t -> t.getClass() == EventElement.class || t.supportStruct);
-        }
-
-        List<TypeElement> getTypesAndStructs() {
-            return getList(t -> t.getClass() == TypeElement.class || t.supportStruct);
+            return getList(t -> t.isEvent || t.supportStruct);
         }
 
         @SuppressWarnings("unchecked")
@@ -173,24 +263,25 @@
             return result;
         }
 
-        List<EventElement> getPeriodicEvents() {
-            return getList(t -> t.getClass() == EventElement.class && ((EventElement) t).periodic);
+        List<TypeElement> getPeriodicEvents() {
+            return getList(t -> t.isEvent && !t.period.isEmpty());
         }
 
         List<TypeElement> getTypes() {
-            return getList(t -> t.getClass() == TypeElement.class);
+            return getList(t -> !t.isEvent);
         }
 
         List<TypeElement> getStructs() {
-            return getList(t -> t.getClass() == TypeElement.class && t.supportStruct);
+            return getList(t -> !t.isEvent && t.supportStruct);
         }
 
-        void verify()  {
+        void verify() {
             for (TypeElement t : types.values()) {
                 for (FieldElement f : t.fields) {
                     if (!xmlTypes.containsKey(f.typeName)) { // ignore primitives
                         if (!types.containsKey(f.typeName)) {
-                            throw new IllegalStateException("Could not find definition of type '" + f.typeName + "' used by " + t.name + "#" + f.name);
+                            throw new IllegalStateException("Could not find definition of type '" + f.typeName
+                                    + "' used by " + t.name + "#" + f.name);
                         }
                     }
                 }
@@ -198,26 +289,96 @@
         }
 
         void wireUpTypes() {
+            // Add Java primitives
+            for (var t : xmlTypes.entrySet()) {
+                String name = t.getKey();
+                XmlType xmlType = t.getValue();
+                // Excludes Thread and Class
+                if (!types.containsKey(name)) {
+                    // Excludes u8, u4, u2, u1, Ticks and Ticksspan
+                    if (!xmlType.javaType.isEmpty() && !xmlType.unsigned) {
+                        TypeElement te = new TypeElement();
+                        te.name = name;
+                        te.javaType = xmlType.javaType;
+                        te.primitive = true;
+                        types.put(te.name, te);
+                    }
+                }
+            }
+            // Setup Java fully qualified names
+            for (TypeElement t : types.values()) {
+                if (t.isEvent) {
+                    t.javaType = "jdk." + t.name;
+                } else {
+                    XmlType xmlType = xmlTypes.get(t.name);
+                    if (xmlType != null && !xmlType.javaType.isEmpty()) {
+                        t.javaType = xmlType.javaType;
+                    } else {
+                        t.javaType = "jdk.types." + t.name;
+                    }
+                }
+            }
+            // Setup content type, annotation, constant pool etc. for fields.
             for (TypeElement t : types.values()) {
                 for (FieldElement f : t.fields) {
                     TypeElement type = types.get(f.typeName);
+                    XmlType xmlType = xmlTypes.get(f.typeName);
+                    if (type == null) {
+                        if (xmlType == null) {
+                            throw new IllegalStateException("Unknown type");
+                        }
+                        if (f.contentType.isEmpty()) {
+                            f.contentType = xmlType.contentType;
+                        }
+                        String javaType = xmlType.javaType;
+                        type = types.get(javaType);
+                        Objects.requireNonNull(type);
+                    }
+                    if (type.primitive) {
+                        f.constantPool = false;
+                    }
+
+                    if (xmlType != null) {
+                        f.unsigned = xmlType.unsigned;
+                    }
+
                     if (f.struct) {
+                        f.constantPool = false;
                         type.supportStruct = true;
                     }
                     f.type = type;
+                    XmlContentType xmlContentType = xmlContentTypes.get(f.contentType);
+                    if (xmlContentType == null) {
+                        f.annotations = "";
+                    } else {
+                        f.annotations = xmlContentType.annotation;
+                    }
+                    if (!f.relation.isEmpty()) {
+                        f.relation = "jdk.types." + f.relation;
+                    }
                 }
             }
+
+            // Low numbers for event so most of them
+            // can fit in one byte with compressed integers
+            eventCounter = new TypeCounter(RESERVED_EVENT_COUNT);
+            for (TypeElement t : getEvents()) {
+                t.id = eventCounter.next();
+            }
+            typeCounter = new TypeCounter(eventCounter.last + 1);
+            for (TypeElement t : getTypes()) {
+                t.id = typeCounter.next();
+            }
         }
-    }
 
-    static class EventElement extends TypeElement {
-        String representation;
-        boolean thread;
-        boolean stackTrace;
-        boolean startTime;
-        boolean periodic;
-        boolean cutoff;
-        String commitState;
+        public String getName(long id) {
+            for (TypeElement t : types.values()) {
+                if (t.id == id) {
+                    return t.name;
+                }
+            }
+            throw new IllegalStateException("Unexpected id " + id );
+        }
     }
 
     static class FieldElement {
@@ -225,12 +386,36 @@
         TypeElement type;
         String name;
         String typeName;
-        boolean struct;
+        boolean constantPool = true;
+        public String transition;
+        public String contentType;
+        private String label;
+        private String description;
+        private String relation;
+        private boolean experimental;
+        private boolean unsigned;
+        private boolean array;
+        private String annotations;
+        public boolean struct;
 
         FieldElement(Metadata metadata) {
             this.metadata = metadata;
         }
 
+        public void persist(DataOutputStream pos) throws IOException {
+            pos.writeUTF(name);
+            pos.writeUTF(type.javaType);
+            pos.writeUTF(label);
+            pos.writeUTF(description);
+            pos.writeBoolean(constantPool);
+            pos.writeBoolean(array);
+            pos.writeBoolean(unsigned);
+            pos.writeUTF(annotations);
+            pos.writeUTF(transition);
+            pos.writeUTF(relation);
+            pos.writeBoolean(experimental);
+        }
+
         String getParameterType() {
             if (struct) {
                 return "const JfrStruct" + typeName + "&";
@@ -262,50 +447,75 @@
         final Metadata metadata;
         FieldElement currentField;
         TypeElement currentType;
+
         MetadataHandler(Metadata metadata) {
             this.metadata = metadata;
         }
+
         @Override
         public void error(SAXParseException e) throws SAXException {
-          throw e;
+            throw e;
         }
+
         @Override
-        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+        public void startElement(String uri, String localName, String qName, Attributes attributes)
+                throws SAXException {
             switch (qName) {
+            case "XmlContentType":
+                String n = attributes.getValue("name"); // mandatory
+                String a = attributes.getValue("annotation"); // mandatory
+                metadata.xmlContentTypes.put(n, new XmlContentType(n, a));
+                break;
             case "XmlType":
-                String name = attributes.getValue("name");
-                String parameterType = attributes.getValue("parameterType");
-                String fieldType = attributes.getValue("fieldType");
-                String javaType = attributes.getValue("javaType");
+                String name = attributes.getValue("name"); // mandatory
+                String parameterType = attributes.getValue("parameterType"); // mandatory
+                String fieldType = attributes.getValue("fieldType"); // mandatory
+                String javaType = getString(attributes, "javaType");
+                String contentType = getString(attributes, "contentType");
                 boolean unsigned = getBoolean(attributes, "unsigned", false);
-                metadata.xmlTypes.put(name, new XmlType(name, fieldType, parameterType, javaType, unsigned));
+                metadata.xmlTypes.put(name,
+                        new XmlType(name, fieldType, parameterType, javaType, contentType, unsigned));
                 break;
             case "Relation":
             case "Type":
+            case "Event":
                 currentType = new TypeElement();
-                currentType.name = attributes.getValue("name");
-                break;
-            case "Event":
-                EventElement eventType = new EventElement();
-                eventType.name = attributes.getValue("name");
-                eventType.thread = getBoolean(attributes, "thread", false);
-                eventType.stackTrace = getBoolean(attributes, "stackTrace", false);
-                eventType.startTime = getBoolean(attributes, "startTime", true);
-                eventType.periodic = attributes.getValue("period") != null;
-                eventType.cutoff = getBoolean(attributes, "cutoff", false);
-                eventType.commitState = attributes.getValue("commitState");
-                currentType = eventType;
+                currentType.name = attributes.getValue("name"); // mandatory
+                currentType.label = getString(attributes, "label");
+                currentType.description = getString(attributes, "description");
+                currentType.category = getString(attributes, "category");
+                currentType.experimental = getBoolean(attributes, "experimental", false);
+                currentType.thread = getBoolean(attributes, "thread", false);
+                currentType.stackTrace = getBoolean(attributes, "stackTrace", false);
+                currentType.startTime = getBoolean(attributes, "startTime", true);
+                currentType.period = getString(attributes, "period");
+                currentType.cutoff = getBoolean(attributes, "cutoff", false);
+                currentType.commitState = getString(attributes, "commitState");
+                currentType.isEvent = "Event".equals(qName);
+                currentType.isRelation = "Relation".equals(qName);
                 break;
             case "Field":
                 currentField = new FieldElement(metadata);
+                currentField.name = attributes.getValue("name"); // mandatory
+                currentField.typeName = attributes.getValue("type"); // mandatory
+                currentField.label = getString(attributes, "label");
+                currentField.description = getString(attributes, "description");
+                currentField.contentType = getString(attributes, "contentType");
                 currentField.struct = getBoolean(attributes, "struct", false);
-                currentField.name = attributes.getValue("name");
-                currentField.typeName = attributes.getValue("type");
+                currentField.array = getBoolean(attributes, "array", false);
+                currentField.transition = getString(attributes, "transition");
+                currentField.relation = getString(attributes, "relation");
+                currentField.experimental = getBoolean(attributes, "experimental", false);
                 break;
             }
         }
 
-        private boolean getBoolean(Attributes attributes, String name, boolean defaultValue) {
+        private static String getString(Attributes attributes, String name) {
+            String value = attributes.getValue(name);
+            return value != null ? value : "";
+        }
+
+        private static boolean getBoolean(Attributes attributes, String name, boolean defaultValue) {
             String value = attributes.getValue(name);
             return value == null ? defaultValue : Boolean.valueOf(value);
         }
@@ -327,10 +537,11 @@
         }
     }
 
-    static class Printer implements AutoCloseable {
+    static class Printer implements Closeable {
         final PrintStream out;
-        Printer(File outputDirectory, String filename) throws FileNotFoundException {
-            out = new PrintStream(new BufferedOutputStream(new FileOutputStream(new File(outputDirectory, filename))));
+
+        Printer(File outputFile) throws FileNotFoundException {
+            out = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
             write("/* AUTOMATICALLY GENERATED FILE - DO NOT EDIT */");
             write("");
         }
@@ -341,13 +552,13 @@
         }
 
         @Override
-        public void close() throws Exception {
+        public void close() throws IOException {
             out.close();
         }
     }
 
-    private static void printJfrPeriodicHpp(Metadata metadata, File outputDirectory) throws Exception {
-        try (Printer out = new Printer(outputDirectory, "jfrPeriodic.hpp")) {
+    private static void printJfrPeriodicHpp(Metadata metadata, File outputFile) throws Exception {
+        try (var out = new Printer(outputFile)) {
             out.write("#ifndef JFRFILES_JFRPERIODICEVENTSET_HPP");
             out.write("#define JFRFILES_JFRPERIODICEVENTSET_HPP");
             out.write("");
@@ -361,7 +572,7 @@
             out.write("  static void requestEvent(JfrEventId id) {");
             out.write("    switch(id) {");
             out.write("  ");
-            for (EventElement e : metadata.getPeriodicEvents()) {
+            for (TypeElement e : metadata.getPeriodicEvents()) {
                 out.write("      case Jfr" + e.name + "Event:");
                 out.write("        request" + e.name + "();");
                 out.write("        break;");
@@ -374,7 +585,7 @@
             out.write("");
             out.write(" private:");
             out.write("");
-            for (EventElement e : metadata.getPeriodicEvents()) {
+            for (TypeElement e : metadata.getPeriodicEvents()) {
                 out.write("  static void request" + e.name + "(void);");
                 out.write("");
             }
@@ -385,8 +596,8 @@
         }
     }
 
-    private static void printJfrEventControlHpp(Metadata metadata, TypeCounter typeCounter, File outputDirectory) throws Exception {
-        try (Printer out = new Printer(outputDirectory, "jfrEventControl.hpp")) {
+    private static void printJfrEventControlHpp(Metadata metadata, File outputFile) throws Exception {
+        try (var out = new Printer(outputFile)) {
             out.write("#ifndef JFRFILES_JFR_NATIVE_EVENTSETTING_HPP");
             out.write("#define JFRFILES_JFR_NATIVE_EVENTSETTING_HPP");
             out.write("");
@@ -426,8 +637,8 @@
         }
     }
 
-    private static void printJfrEventIdsHpp(Metadata metadata, TypeCounter typeCounter, File outputDirectory) throws Exception {
-        try (Printer out = new Printer(outputDirectory, "jfrEventIds.hpp")) {
+    private static void printJfrEventIdsHpp(Metadata metadata, File outputFile) throws Exception {
+        try (var out = new Printer(outputFile)) {
             out.write("#ifndef JFRFILES_JFREVENTIDS_HPP");
             out.write("#define JFRFILES_JFREVENTIDS_HPP");
             out.write("");
@@ -438,23 +649,28 @@
             out.write("  JfrMetadataEvent = 0,");
             out.write("  JfrCheckpointEvent = 1,");
             for (TypeElement t : metadata.getEvents()) {
-                String name = "Jfr" + t.name +"Event";
-                out.write("  " + name + " = " + typeCounter.nextEventId(name) + ",");
+                out.write("  " + jfrEventId(t.name) + " = " + t.id + ",");
             }
             out.write("};");
             out.write("typedef enum JfrEventId JfrEventId;");
             out.write("");
-            out.write("static const JfrEventId FIRST_EVENT_ID = " + typeCounter.firstEventName() + ";");
-            out.write("static const JfrEventId LAST_EVENT_ID = " + typeCounter.lastEventName() + ";");
-            out.write("static const int NUMBER_OF_EVENTS = " + typeCounter.eventCount() + ";");
-            out.write("static const int NUMBER_OF_RESERVED_EVENTS = " + TypeCounter.RESERVED_EVENT_COUNT + ";");
+            String first = metadata.getName(metadata.eventCounter.first);
+            String last = metadata.getName(metadata.eventCounter.last);
+            out.write("static const JfrEventId FIRST_EVENT_ID = " + jfrEventId(first) + ";");
+            out.write("static const JfrEventId LAST_EVENT_ID = " + jfrEventId(last) + ";");
+            out.write("static const int NUMBER_OF_EVENTS = " + metadata.eventCounter.count + ";");
+            out.write("static const int NUMBER_OF_RESERVED_EVENTS = " + Metadata.RESERVED_EVENT_COUNT + ";");
             out.write("#endif // INCLUDE_JFR");
             out.write("#endif // JFRFILES_JFREVENTIDS_HPP");
         }
     }
 
-    private static void printJfrTypesHpp(Metadata metadata, TypeCounter typeCounter, File outputDirectory) throws Exception {
-        try (Printer out = new Printer(outputDirectory, "jfrTypes.hpp")) {
+    private static String jfrEventId(String name) {
+        return "Jfr" + name + "Event";
+    }
+
+    private static void printJfrTypesHpp(Metadata metadata, File outputFile) throws Exception {
+        try (var out = new Printer(outputFile)) {
             out.write("#ifndef JFRFILES_JFRTYPES_HPP");
             out.write("#define JFRFILES_JFRTYPES_HPP");
             out.write("");
@@ -465,36 +681,27 @@
             out.write("#include \"memory/allocation.hpp\"");
             out.write("");
             out.write("enum JfrTypeId {");
-            Map<String, XmlType> javaTypes = new LinkedHashMap<>();
-            for (var t : metadata.xmlTypes.entrySet()) {
-                String name = t.getKey();
-                XmlType xmlType = t.getValue();
-                if (xmlType.javaType != null && !xmlType.unsigned) {
-                    String typeName = "TYPE_" + name.toUpperCase();
-                    long typeId = typeCounter.nextTypeId(typeName);
-                    out.write("  " + typeName + " = " + typeId + ",");
-                    javaTypes.put(name, xmlType);
-                }
-            }
             for (TypeElement type : metadata.getTypes()) {
-                String name = type.name;
-                if (!javaTypes.containsKey(name)) {
-                    String typeName = "TYPE_" + name.toUpperCase();
-                    long typeId = typeCounter.nextTypeId(typeName);
-                    out.write("  " + typeName + " = " + typeId + ",");
-                }
+                out.write("  " + jfrTypeId(type.name) + " = " + type.id + ",");
             }
             out.write("};");
             out.write("");
-            out.write("static const JfrTypeId FIRST_TYPE_ID = " + typeCounter.firstTypeName() + ";");
-            out.write("static const JfrTypeId LAST_TYPE_ID = " + typeCounter.lastTypeName() + ";");
-
+            String first = metadata.getName(metadata.typeCounter.first);
+            String last = metadata.getName(metadata.typeCounter.last);
+            out.write("static const JfrTypeId FIRST_TYPE_ID = " + jfrTypeId(first) + ";");
+            out.write("static const JfrTypeId LAST_TYPE_ID = " + jfrTypeId(last) + ";");
             out.write("");
             out.write("class JfrType : public AllStatic {");
             out.write(" public:");
             out.write("  static jlong name_to_id(const char* type_name) {");
-            for (Entry<String, XmlType> m : javaTypes.entrySet()) {
-                XmlType xmlType = m.getValue();
+
+            Map<String, XmlType> javaTypes = new LinkedHashMap<>();
+            for (XmlType xmlType : metadata.xmlTypes.values()) {
+                if (!xmlType.javaType.isEmpty()) {
+                    javaTypes.put(xmlType.javaType, xmlType);
+                }
+            }
+            for (XmlType xmlType : javaTypes.values()) {
                 String javaName = xmlType.javaType;
                 String typeName = xmlType.name.toUpperCase();
                 out.write("    if (strcmp(type_name, \"" + javaName + "\") == 0) {");
@@ -508,11 +715,14 @@
             out.write("#endif // INCLUDE_JFR");
             out.write("#endif // JFRFILES_JFRTYPES_HPP");
         }
-        ;
     }
 
-    private static void printJfrEventClassesHpp(Metadata metadata, File outputDirectory) throws Exception {
-        try (Printer out = new Printer(outputDirectory, "jfrEventClasses.hpp")) {
+    private static String jfrTypeId(String name) {
+        return  "TYPE_" + name.toUpperCase();
+    }
+
+    private static void printJfrEventClassesHpp(Metadata metadata, File outputFile) throws Exception {
+        try (var out = new Printer(outputFile)) {
             out.write("#ifndef JFRFILES_JFREVENTCLASSES_HPP");
             out.write("#define JFRFILES_JFREVENTCLASSES_HPP");
             out.write("");
@@ -566,7 +776,7 @@
             printType(out, t, empty);
             out.write("");
         }
-        for (EventElement e : metadata.getEvents()) {
+        for (TypeElement e : metadata.getEvents()) {
             printEvent(out, e, empty);
             out.write("");
         }
@@ -576,81 +786,83 @@
         out.write("struct JfrStruct" + t.name);
         out.write("{");
         if (!empty) {
-          out.write(" private:");
-          for (FieldElement f : t.fields) {
-              printField(out, f);
-          }
-          out.write("");
+            out.write(" private:");
+            for (FieldElement f : t.fields) {
+                printField(out, f);
+            }
+            out.write("");
         }
         out.write(" public:");
         for (FieldElement f : t.fields) {
-           printTypeSetter(out, f, empty);
+            printTypeSetter(out, f, empty);
         }
         out.write("");
         if (!empty) {
-          printWriteData(out, t.fields, null);
+            printWriteData(out, t);
         }
         out.write("};");
         out.write("");
     }
 
-    private static void printEvent(Printer out, EventElement event, boolean empty) {
+    private static void printEvent(Printer out, TypeElement event, boolean empty) {
         out.write("class Event" + event.name + " : public JfrEvent<Event" + event.name + ">");
         out.write("{");
         if (!empty) {
-          out.write(" private:");
-          for (FieldElement f : event.fields) {
-              printField(out, f);
-          }
-          out.write("");
+            out.write(" private:");
+            for (FieldElement f : event.fields) {
+                printField(out, f);
+            }
+            out.write("");
         }
         out.write(" public:");
         if (!empty) {
-          out.write("  static const bool hasThread = " + event.thread + ";");
-          out.write("  static const bool hasStackTrace = " + event.stackTrace + ";");
-          out.write("  static const bool isInstant = " + !event.startTime + ";");
-          out.write("  static const bool hasCutoff = " + event.cutoff + ";");
-          out.write("  static const bool isRequestable = " + event.periodic + ";");
-          out.write("  static const JfrEventId eventId = Jfr" + event.name + "Event;");
-          out.write("");
+            out.write("  static const bool hasThread = " + event.thread + ";");
+            out.write("  static const bool hasStackTrace = " + event.stackTrace + ";");
+            out.write("  static const bool isInstant = " + !event.startTime + ";");
+            out.write("  static const bool hasCutoff = " + event.cutoff + ";");
+            out.write("  static const bool isRequestable = " + !event.period.isEmpty() + ";");
+            out.write("  static const JfrEventId eventId = Jfr" + event.name + "Event;");
+            out.write("");
         }
         if (!empty) {
-          out.write("  Event" + event.name + "(EventStartTime timing=TIMED) : JfrEvent<Event" + event.name + ">(timing) {}");
+            out.write("  Event" + event.name + "(EventStartTime timing=TIMED) : JfrEvent<Event" + event.name
+                    + ">(timing) {}");
         } else {
-          out.write("  Event" + event.name + "(EventStartTime timing=TIMED) {}");
+            out.write("  Event" + event.name + "(EventStartTime timing=TIMED) {}");
         }
         out.write("");
         int index = 0;
         for (FieldElement f : event.fields) {
             out.write("  void set_" + f.name + "(" + f.getParameterType() + " " + f.getParameterName() + ") {");
             if (!empty) {
-              out.write("    this->_" + f.name + " = " + f.getParameterName() + ";");
-              out.write("    DEBUG_ONLY(set_field_bit(" + index++ + "));");
+                out.write("    this->_" + f.name + " = " + f.getParameterName() + ";");
+                out.write("    DEBUG_ONLY(set_field_bit(" + index++ + "));");
             }
             out.write("  }");
         }
         out.write("");
         if (!empty) {
-          printWriteData(out, event.fields, event.commitState);
-          out.write("");
+            printWriteData(out, event);
+            out.write("");
         }
-        out.write("  using JfrEvent<Event" + event.name + ">::commit; // else commit() is hidden by overloaded versions in this class");
+        out.write("  using JfrEvent<Event" + event.name
+                + ">::commit; // else commit() is hidden by overloaded versions in this class");
         printConstructor2(out, event, empty);
         printCommitMethod(out, event, empty);
         if (!empty) {
-          printVerify(out, event.fields);
+            printVerify(out, event.fields);
         }
         out.write("};");
     }
 
-    private static void printWriteData(Printer out, List<FieldElement> fields, String commitState) {
+    private static void printWriteData(Printer out, TypeElement type) {
         out.write("  template <typename Writer>");
         out.write("  void writeData(Writer& w) {");
-        if (("_thread_in_native").equals(commitState)) {
+        if (("_thread_in_native").equals(type.commitState)) {
             out.write("    // explicit epoch synchronization check");
             out.write("    JfrEpochSynchronization sync;");
         }
-        for (FieldElement field : fields) {
+        for (FieldElement field : type.fields) {
             if (field.struct) {
                 out.write("    _" + field.name + ".writeData(w);");
             } else {
@@ -662,9 +874,10 @@
 
     private static void printTypeSetter(Printer out, FieldElement field, boolean empty) {
         if (!empty) {
-          out.write("  void set_" + field.name + "(" + field.getParameterType() + " new_value) { this->_" + field.name + " = new_value; }");
+            out.write("  void set_" + field.name + "(" + field.getParameterType() + " new_value) { this->_" + field.name
+                    + " = new_value; }");
         } else {
-          out.write("  void set_" + field.name + "(" + field.getParameterType() + " new_value) { }");
+            out.write("  void set_" + field.name + "(" + field.getParameterType() + " new_value) { }");
         }
     }
 
@@ -674,13 +887,14 @@
         out.write("  void verify() const {");
         int index = 0;
         for (FieldElement f : fields) {
-            out.write("    assert(verify_field_bit(" + index++ + "), \"Attempting to write an uninitialized event field: %s\", \"_" + f.name + "\");");
+            out.write("    assert(verify_field_bit(" + index++
+                    + "), \"Attempting to write an uninitialized event field: %s\", \"_" + f.name + "\");");
         }
         out.write("  }");
         out.write("#endif");
     }
 
-    private static void printCommitMethod(Printer out, EventElement event, boolean empty) {
+    private static void printCommitMethod(Printer out, TypeElement event, boolean empty) {
         if (event.startTime) {
             StringJoiner sj = new StringJoiner(",\n              ");
             for (FieldElement f : event.fields) {
@@ -689,12 +903,12 @@
             out.write("");
             out.write("  void commit(" + sj.toString() + ") {");
             if (!empty) {
-              out.write("    if (should_commit()) {");
-              for (FieldElement f : event.fields) {
-                  out.write("      set_" + f.name + "(" + f.name + ");");
-              }
-              out.write("      commit();");
-              out.write("    }");
+                out.write("    if (should_commit()) {");
+                for (FieldElement f : event.fields) {
+                    out.write("      set_" + f.name + "(" + f.name + ");");
+                }
+                out.write("      commit();");
+                out.write("    }");
             }
             out.write("  }");
         }
@@ -715,23 +929,23 @@
         }
         out.write("  static void commit(" + sj.toString() + ") {");
         if (!empty) {
-          out.write("    Event" + event.name + " me(UNTIMED);");
-          out.write("");
-          out.write("    if (me.should_commit()) {");
-          if (event.startTime) {
-              out.write("      me.set_starttime(startTicks);");
-              out.write("      me.set_endtime(endTicks);");
-          }
-          for (FieldElement f : event.fields) {
-              out.write("      me.set_" + f.name + "(" + f.name + ");");
-          }
-          out.write("      me.commit();");
-          out.write("    }");
+            out.write("    Event" + event.name + " me(UNTIMED);");
+            out.write("");
+            out.write("    if (me.should_commit()) {");
+            if (event.startTime) {
+                out.write("      me.set_starttime(startTicks);");
+                out.write("      me.set_endtime(endTicks);");
+            }
+            for (FieldElement f : event.fields) {
+                out.write("      me.set_" + f.name + "(" + f.name + ");");
+            }
+            out.write("      me.commit();");
+            out.write("    }");
         }
         out.write("  }");
     }
 
-    private static void printConstructor2(Printer out, EventElement event, boolean empty) {
+    private static void printConstructor2(Printer out, TypeElement event, boolean empty) {
         if (!event.startTime) {
             out.write("");
             out.write("");
@@ -744,14 +958,14 @@
                 sj.add(f.getParameterType() + " " + f.name);
             }
             if (!empty) {
-              out.write("    " + sj.toString() + ") : JfrEvent<Event" + event.name + ">(TIMED) {");
-              out.write("    if (should_commit()) {");
-              for (FieldElement f : event.fields) {
-                  out.write("      set_" + f.name + "(" + f.name + ");");
-              }
-              out.write("    }");
+                out.write("    " + sj.toString() + ") : JfrEvent<Event" + event.name + ">(TIMED) {");
+                out.write("    if (should_commit()) {");
+                for (FieldElement f : event.fields) {
+                    out.write("      set_" + f.name + "(" + f.name + ");");
+                }
+                out.write("    }");
             } else {
-              out.write("    " + sj.toString() + ") {");
+                out.write("    " + sj.toString() + ") {");
             }
             out.write("  }");
         }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataHandler.java	Sat Jun 20 15:11:19 2020 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,418 +0,0 @@
-/*
- * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.jfr.internal;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.annotation.Annotation;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import jdk.internal.org.xml.sax.Attributes;
-import jdk.internal.org.xml.sax.EntityResolver;
-import jdk.internal.org.xml.sax.SAXException;
-import jdk.internal.org.xml.sax.helpers.DefaultHandler;
-import jdk.internal.util.xml.SAXParser;
-import jdk.internal.util.xml.impl.SAXParserImpl;
-import jdk.jfr.AnnotationElement;
-import jdk.jfr.Category;
-import jdk.jfr.Description;
-import jdk.jfr.Enabled;
-import jdk.jfr.Experimental;
-import jdk.jfr.Label;
-import jdk.jfr.Period;
-import jdk.jfr.Relational;
-import jdk.jfr.StackTrace;
-import jdk.jfr.Threshold;
-import jdk.jfr.TransitionFrom;
-import jdk.jfr.TransitionTo;
-import jdk.jfr.Unsigned;
-
-final class MetadataHandler extends DefaultHandler implements EntityResolver {
-
-    // Metadata and Checkpoint event
-    private final long RESERVED_EVENT_COUNT = 2;
-
-    static class TypeElement {
-        List<FieldElement> fields = new ArrayList<>();
-        String name;
-        String label;
-        String description;
-        String category;
-        String superType;
-        String period;
-        boolean thread;
-        boolean startTime;
-        boolean stackTrace;
-        boolean cutoff;
-        boolean isEvent;
-        boolean isRelation;
-        boolean experimental;
-        boolean valueType;
-    }
-
-    static class FieldElement {
-        TypeElement referenceType;
-        String name;
-        String label;
-        String description;
-        String contentType;
-        String typeName;
-        String transition;
-        String relation;
-        boolean struct;
-        boolean array;
-        boolean experimental;
-        boolean unsigned;
-    }
-
-    static class XmlType {
-        String name;
-        String javaType;
-        String contentType;
-        boolean unsigned;
-    }
-
-    final Map<String, TypeElement> types = new LinkedHashMap<>(200);
-    final Map<String, XmlType> xmlTypes = new LinkedHashMap<>(20);
-    final Map<String, List<AnnotationElement>> xmlContentTypes = new LinkedHashMap<>(20);
-    FieldElement currentField;
-    TypeElement currentType;
-    long eventCount;
-
-    @Override
-    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
-        switch (qName) {
-        case "XmlType":
-            XmlType xmlType = new XmlType();
-            xmlType.name = attributes.getValue("name");
-            xmlType.javaType = attributes.getValue("javaType");
-            xmlType.contentType = attributes.getValue("contentType");
-            xmlType.unsigned = Boolean.valueOf(attributes.getValue("unsigned"));
-            xmlTypes.put(xmlType.name, xmlType);
-            break;
-        case "Relation":
-        case "Type":
-        case "Event":
-            currentType = new TypeElement();
-            currentType.name = attributes.getValue("name");
-            currentType.label = attributes.getValue("label");
-            currentType.description = attributes.getValue("description");
-            currentType.category = attributes.getValue("category");
-            currentType.thread = getBoolean(attributes, "thread", false);
-            currentType.stackTrace = getBoolean(attributes, "stackTrace", false);
-            currentType.startTime = getBoolean(attributes, "startTime", true);
-            currentType.period = attributes.getValue("period");
-            currentType.cutoff = getBoolean(attributes, "cutoff", false);
-            currentType.experimental = getBoolean(attributes, "experimental", false);
-            currentType.isEvent = qName.equals("Event");
-            currentType.isRelation = qName.equals("Relation");
-            break;
-        case "Field":
-            currentField = new FieldElement();
-            currentField.struct = getBoolean(attributes, "struct", false);
-            currentField.array = getBoolean(attributes, "array", false);
-            currentField.name = attributes.getValue("name");
-            currentField.label = attributes.getValue("label");
-            currentField.typeName = attributes.getValue("type");
-            currentField.description = attributes.getValue("description");
-            currentField.experimental = getBoolean(attributes, "experimental", false);
-            currentField.contentType = attributes.getValue("contentType");
-            currentField.relation = attributes.getValue("relation");
-            currentField.transition = attributes.getValue("transition");
-            break;
-        case "XmlContentType":
-            String name = attributes.getValue("name");
-            String annotation = attributes.getValue("annotation");
-            xmlContentTypes.put(name, createAnnotationElements(annotation));
-            break;
-        }
-    }
-
-    private List<AnnotationElement> createAnnotationElements(String annotation) throws InternalError {
-        String[] annotations = annotation.split(",");
-        List<AnnotationElement> annotationElements = new ArrayList<>();
-        for (String a : annotations) {
-            a = a.trim();
-            int leftParenthesis = a.indexOf("(");
-            if (leftParenthesis == -1) {
-                annotationElements.add(new AnnotationElement(createAnnotationClass(a)));
-            } else {
-                int rightParenthesis = a.lastIndexOf(")");
-                if (rightParenthesis == -1) {
-                    throw new InternalError("Expected closing parenthesis for 'XMLContentType'");
-                }
-                String value = a.substring(leftParenthesis + 1, rightParenthesis);
-                String type = a.substring(0, leftParenthesis);
-                annotationElements.add(new AnnotationElement(createAnnotationClass(type), value));
-            }
-        }
-        return annotationElements;
-    }
-
-    @SuppressWarnings("unchecked")
-    private Class<? extends Annotation> createAnnotationClass(String type) {
-        try {
-            if (!type.startsWith("jdk.jfr.")) {
-                throw new IllegalStateException("Incorrect type " + type + ". Annotation class must be located in jdk.jfr package.");
-            }
-            Class<?> c = Class.forName(type, true, null);
-            return (Class<? extends Annotation>) c;
-        } catch (ClassNotFoundException cne) {
-            throw new IllegalStateException(cne);
-        }
-    }
-
-    private boolean getBoolean(Attributes attributes, String name, boolean defaultValue) {
-        String value = attributes.getValue(name);
-        return value == null ? defaultValue : Boolean.valueOf(value);
-    }
-
-    @Override
-    public void endElement(String uri, String localName, String qName) {
-        switch (qName) {
-        case "Type":
-        case "Event":
-        case "Relation":
-            types.put(currentType.name, currentType);
-            if (currentType.isEvent) {
-                eventCount++;
-            }
-            currentType = null;
-            break;
-        case "Field":
-            currentType.fields.add(currentField);
-            currentField = null;
-            break;
-        }
-    }
-
-    public static List<Type> createTypes() throws IOException {
-        SAXParser parser = new SAXParserImpl();
-        MetadataHandler t = new MetadataHandler();
-        try (InputStream is = new BufferedInputStream(SecuritySupport.getResourceAsStream("/jdk/jfr/internal/types/metadata.xml"))) {
-            Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, "Parsing metadata.xml");
-            try {
-                parser.parse(is, t);
-                return t.buildTypes();
-            } catch (Exception e) {
-                throw new IOException(e);
-            }
-        }
-    }
-
-    private List<Type> buildTypes() {
-        removeXMLConvenience();
-        Map<String, Type> typeMap = buildTypeMap();
-        Map<String, AnnotationElement> relationMap = buildRelationMap(typeMap);
-        addFields(typeMap, relationMap);
-        return trimTypes(typeMap);
-    }
-
-    private Map<String, AnnotationElement> buildRelationMap(Map<String, Type> typeMap) {
-        Map<String, AnnotationElement> relationMap = new HashMap<>();
-        for (TypeElement t : types.values()) {
-            if (t.isRelation) {
-                Type relationType = typeMap.get(t.name);
-                AnnotationElement ae = PrivateAccess.getInstance().newAnnotation(relationType, Collections.emptyList(), true);
-                relationMap.put(t.name, ae);
-            }
-        }
-        return relationMap;
-    }
-
-    private List<Type> trimTypes(Map<String, Type> lookup) {
-        List<Type> trimmedTypes = new ArrayList<>(lookup.size());
-        for (Type t : lookup.values()) {
-            t.trimFields();
-            trimmedTypes.add(t);
-        }
-        return trimmedTypes;
-    }
-
-    private void addFields(Map<String, Type> lookup, Map<String, AnnotationElement> relationMap) {
-        for (TypeElement te : types.values()) {
-            Type type = lookup.get(te.name);
-            if (te.isEvent) {
-                boolean periodic = te.period!= null;
-                TypeLibrary.addImplicitFields(type, periodic, te.startTime && !periodic, te.thread, te.stackTrace && !periodic, te.cutoff);
-            }
-            for (FieldElement f : te.fields) {
-                Type fieldType = Type.getKnownType(f.typeName);
-                if (fieldType == null) {
-                    fieldType = Objects.requireNonNull(lookup.get(f.referenceType.name));
-                }
-                List<AnnotationElement> aes = new ArrayList<>();
-                if (f.unsigned) {
-                    aes.add(new AnnotationElement(Unsigned.class));
-                }
-                if (f.contentType != null) {
-                    aes.addAll(Objects.requireNonNull(xmlContentTypes.get(f.contentType)));
-                }
-                if (f.relation != null) {
-                    String relationTypeName = Type.TYPES_PREFIX + f.relation;
-                    AnnotationElement t = relationMap.get(relationTypeName);
-                    aes.add(Objects.requireNonNull(t));
-                }
-                if (f.label != null) {
-                    aes.add(new AnnotationElement(Label.class, f.label));
-                }
-                if (f.experimental) {
-                    aes.add(new AnnotationElement(Experimental.class));
-                }
-                if (f.description != null) {
-                    aes.add(new AnnotationElement(Description.class, f.description));
-                }
-                if ("from".equals(f.transition)) {
-                    aes.add(new AnnotationElement(TransitionFrom.class));
-                }
-                if ("to".equals(f.transition)) {
-                    aes.add(new AnnotationElement(TransitionTo.class));
-                }
-                boolean constantPool = !f.struct && f.referenceType != null;
-                type.add(PrivateAccess.getInstance().newValueDescriptor(f.name, fieldType, aes, f.array ? 1 : 0, constantPool, null));
-            }
-        }
-    }
-
-    private Map<String, Type> buildTypeMap() {
-        Map<String, Type> typeMap = new HashMap<>();
-        Map<String, Type> knownTypeMap = new HashMap<>();
-        for (Type kt :Type.getKnownTypes()) {
-            typeMap.put(kt.getName(), kt);
-            knownTypeMap.put(kt.getName(), kt);
-        }
-        long eventTypeId = RESERVED_EVENT_COUNT;
-        long typeId = RESERVED_EVENT_COUNT + eventCount + knownTypeMap.size();
-        for (TypeElement t : types.values()) {
-            List<AnnotationElement> aes = new ArrayList<>();
-            if (t.category != null) {
-                aes.add(new AnnotationElement(Category.class, buildCategoryArray(t.category)));
-            }
-            if (t.label != null) {
-                aes.add(new AnnotationElement(Label.class, t.label));
-            }
-            if (t.description != null) {
-                aes.add(new AnnotationElement(Description.class, t.description));
-            }
-            if (t.isEvent) {
-                if (t.period != null) {
-                    aes.add(new AnnotationElement(Period.class, t.period));
-                } else {
-                    if (t.startTime) {
-                        aes.add(new AnnotationElement(Threshold.class, "0 ns"));
-                    }
-                    if (t.stackTrace) {
-                        aes.add(new AnnotationElement(StackTrace.class, true));
-                    }
-                }
-                if (t.cutoff) {
-                    aes.add(new AnnotationElement(Cutoff.class, Cutoff.INFINITY));
-                }
-            }
-            if (t.experimental) {
-                aes.add(new AnnotationElement(Experimental.class));
-            }
-            Type type;
-            if (t.isEvent) {
-                aes.add(new AnnotationElement(Enabled.class, false));
-                type = new PlatformEventType(t.name,  eventTypeId++, false, true);
-            } else {
-                if (knownTypeMap.containsKey(t.name)) {
-                    type = knownTypeMap.get(t.name);
-                } else {
-                    if (t.isRelation) {
-                        type = new Type(t.name, Type.SUPER_TYPE_ANNOTATION, typeId++);
-                        aes.add(new AnnotationElement(Relational.class));
-                    } else {
-                        type = new Type(t.name, null, typeId++);
-                    }
-                }
-            }
-            type.setAnnotations(aes);
-            typeMap.put(t.name, type);
-        }
-        return typeMap;
-    }
-
-    private String[] buildCategoryArray(String category) {
-        List<String> categories = new ArrayList<>();
-        StringBuilder sb = new StringBuilder();
-        for (char c : category.toCharArray()) {
-            if (c == ',') {
-                categories.add(sb.toString().trim());
-                sb.setLength(0);
-            } else {
-                sb.append(c);
-            }
-        }
-        categories.add(sb.toString().trim());
-        return categories.toArray(new String[0]);
-    }
-
-    private void removeXMLConvenience() {
-        for (TypeElement t : types.values()) {
-            XmlType xmlType = xmlTypes.get(t.name);
-            if (xmlType != null && xmlType.javaType != null) {
-                t.name = xmlType.javaType; // known type, i.e primitive
-            } else {
-                if (t.isEvent) {
-                    t.name = Type.EVENT_NAME_PREFIX + t.name;
-                } else {
-                    t.name = Type.TYPES_PREFIX + t.name;
-                }
-            }
-        }
-
-        for (TypeElement t : types.values()) {
-            for (FieldElement f : t.fields) {
-                f.referenceType = types.get(f.typeName);
-                XmlType xmlType = xmlTypes.get(f.typeName);
-                if (xmlType != null) {
-                    if (xmlType.javaType != null) {
-                        f.typeName = xmlType.javaType;
-                    }
-                    if (xmlType.contentType != null) {
-                        f.contentType = xmlType.contentType;
-                    }
-                    if (xmlType.unsigned) {
-                        f.unsigned = true;
-                    }
-                }
-                if (f.struct && f.referenceType != null) {
-                    f.referenceType.valueType = true;
-                }
-            }
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataLoader.java	Mon Jun 22 08:09:23 2020 +0200
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import jdk.jfr.AnnotationElement;
+import jdk.jfr.Category;
+import jdk.jfr.Description;
+import jdk.jfr.Enabled;
+import jdk.jfr.Experimental;
+import jdk.jfr.Label;
+import jdk.jfr.Period;
+import jdk.jfr.Relational;
+import jdk.jfr.StackTrace;
+import jdk.jfr.Threshold;
+import jdk.jfr.TransitionFrom;
+import jdk.jfr.TransitionTo;
+import jdk.jfr.Unsigned;
+
+public final class MetadataLoader {
+
+    // Caching to reduce allocation pressure and heap usage
+    private final AnnotationElement RELATIONAL = new AnnotationElement(Relational.class);
+    private final AnnotationElement ENABLED = new AnnotationElement(Enabled.class, false);
+    private final AnnotationElement THRESHOLD = new AnnotationElement(Threshold.class, "0 ns");
+    private final AnnotationElement STACK_TRACE = new AnnotationElement(StackTrace.class, true);
+    private final AnnotationElement TRANSITION_TO = new AnnotationElement(TransitionTo.class);
+    private final AnnotationElement TRANSITION_FROM = new AnnotationElement(TransitionFrom.class);
+    private final AnnotationElement EXPERIMENTAL = new AnnotationElement(Experimental.class);
+    private final AnnotationElement UNSIGNED = new AnnotationElement(Unsigned.class);
+    private final List<Object> SMALL_TEMP_LIST = new ArrayList<>();
+    private final Type LABEL_TYPE = TypeLibrary.createAnnotationType(Label.class);
+    private final Type DESCRIPTION_TYPE = TypeLibrary.createAnnotationType(Description.class);
+    private final Type CATEGORY_TYPE = TypeLibrary.createAnnotationType(Category.class);
+    private final Type PERIOD_TYPE = TypeLibrary.createAnnotationType(Period.class);
+
+    // <Event>, <Type> and <Relation>
+    private final static class TypeElement {
+        private final List<FieldElement> fields;
+        private final String name;
+        private final String label;
+        private final String description;
+        private final String category;
+        private final String period;
+        private final boolean thread;
+        private final boolean startTime;
+        private final boolean stackTrace;
+        private final boolean cutoff;
+        private final boolean isEvent;
+        private final boolean isRelation;
+        private final boolean experimental;
+        private final long id;
+
+        public TypeElement(DataInputStream dis) throws IOException {
+            int fieldCount = dis.readInt();
+            fields = new ArrayList<>(fieldCount);
+            for (int i = 0; i < fieldCount; i++) {
+                fields.add(new FieldElement(dis));
+            }
+            name = dis.readUTF();
+            label = dis.readUTF();
+            description = dis.readUTF();
+            category = dis.readUTF();
+            thread = dis.readBoolean();
+            stackTrace = dis.readBoolean();
+            startTime = dis.readBoolean();
+            period = dis.readUTF();
+            cutoff = dis.readBoolean();
+            experimental = dis.readBoolean();
+            id = dis.readLong();
+            isEvent = dis.readBoolean();
+            isRelation = dis.readBoolean();
+        }
+    }
+
+    // <Field>
+    private static class FieldElement {
+        private final String name;
+        private final String label;
+        private final String description;
+        private final String typeName;
+        private final String annotations;
+        private final String transition;
+        private final String relation;
+        private final boolean constantPool;
+        private final boolean array;
+        private final boolean experimental;
+        private final boolean unsigned;
+
+        public FieldElement(DataInputStream dis) throws IOException {
+            name = dis.readUTF();
+            typeName = dis.readUTF();
+            label = dis.readUTF();
+            description = dis.readUTF();
+            constantPool = dis.readBoolean();
+            array = dis.readBoolean();
+            unsigned = dis.readBoolean();
+            annotations = dis.readUTF();
+            transition = dis.readUTF();
+            relation = dis.readUTF();
+            experimental = dis.readBoolean();
+        }
+    }
+
+    private final List<TypeElement> types;
+    private final Map<String, List<AnnotationElement>> anotationElements = new HashMap<>(20);
+    private final Map<String, AnnotationElement> categories = new HashMap<>();
+
+    MetadataLoader(DataInputStream dis) throws IOException {
+        SMALL_TEMP_LIST.add(this); // add any object to expand list
+        int typeCount = dis.readInt();
+        types = new ArrayList<>(typeCount);
+        for (int i = 0; i < typeCount; i++) {
+            types.add(new TypeElement(dis));
+        }
+    }
+
+    private List<AnnotationElement> createAnnotationElements(String annotation) throws InternalError {
+        String[] annotations = annotation.split(",");
+        List<AnnotationElement> annotationElements = new ArrayList<>();
+        for (String a : annotations) {
+            a = a.trim();
+            int leftParenthesis = a.indexOf("(");
+            if (leftParenthesis == -1) {
+                annotationElements.add(new AnnotationElement(createAnnotationClass(a)));
+            } else {
+                int rightParenthesis = a.lastIndexOf(")");
+                if (rightParenthesis == -1) {
+                    throw new InternalError("Expected closing parenthesis for 'XMLContentType'");
+                }
+                String value = a.substring(leftParenthesis + 1, rightParenthesis);
+                String type = a.substring(0, leftParenthesis);
+                annotationElements.add(new AnnotationElement(createAnnotationClass(type), value));
+            }
+        }
+        return annotationElements;
+    }
+
+    @SuppressWarnings("unchecked")
+    private Class<? extends Annotation> createAnnotationClass(String type) {
+        try {
+            if (!type.startsWith("jdk.jfr.")) {
+                throw new IllegalStateException("Incorrect type " + type + ". Annotation class must be located in jdk.jfr package.");
+            }
+            Class<?> c = Class.forName(type, true, null);
+            return (Class<? extends Annotation>) c;
+        } catch (ClassNotFoundException cne) {
+            throw new IllegalStateException(cne);
+        }
+    }
+
+    public static List<Type> createTypes() throws IOException {
+        try (DataInputStream dis = new DataInputStream(
+                SecuritySupport.getResourceAsStream("/jdk/jfr/internal/types/metadata.bin"))) {
+            MetadataLoader ml = new MetadataLoader(dis);
+            return ml.buildTypes();
+        } catch (Exception e) {
+            throw new InternalError(e);
+        }
+    }
+
+    private List<Type> buildTypes() {
+        Map<String, Type> typeMap = buildTypeMap();
+        Map<String, AnnotationElement> relationMap = buildRelationMap(typeMap);
+        addFields(typeMap, relationMap);
+        return new ArrayList<>(typeMap.values());
+    }
+
+    private Map<String, AnnotationElement> buildRelationMap(Map<String, Type> typeMap) {
+        Map<String, AnnotationElement> relationMap = new HashMap<>(20);
+        for (TypeElement t : types) {
+            if (t.isRelation) {
+                Type relationType = typeMap.get(t.name);
+                AnnotationElement ae = PrivateAccess.getInstance().newAnnotation(relationType, Collections.emptyList(), true);
+                relationMap.put(t.name, ae);
+            }
+        }
+        return relationMap;
+    }
+
+    private void addFields(Map<String, Type> lookup, Map<String, AnnotationElement> relationMap) {
+        for (TypeElement te : types) {
+            Type type = lookup.get(te.name);
+            if (te.isEvent) {
+                boolean periodic = !te.period.isEmpty();
+                TypeLibrary.addImplicitFields(type, periodic, te.startTime && !periodic, te.thread, te.stackTrace && !periodic, te.cutoff);
+            }
+            for (FieldElement f : te.fields) {
+                Type fieldType = Type.getKnownType(f.typeName);
+                if (fieldType == null) {
+                    fieldType = Objects.requireNonNull(lookup.get(f.typeName));
+                }
+                List<AnnotationElement> aes = new ArrayList<>();
+                if (f.unsigned) {
+                    aes.add(UNSIGNED);
+                }
+                if (!f.annotations.isEmpty()) {
+                    var ae = anotationElements.get(f.annotations);
+                    if (ae == null) {
+                        ae = createAnnotationElements(f.annotations);
+                        anotationElements.put(f.annotations, ae);
+                    }
+                    aes.addAll(ae);
+                }
+                if (!f.relation.isEmpty()) {
+                    AnnotationElement t = relationMap.get(f.relation);
+                    aes.add(Objects.requireNonNull(t));
+                }
+                if (!f.label.isEmpty()) {
+                    aes.add(newAnnotation(LABEL_TYPE, f.label));
+                }
+                if (f.experimental) {
+                    aes.add(EXPERIMENTAL);
+                }
+                if (!f.description.isEmpty()) {
+                    aes.add(newAnnotation(DESCRIPTION_TYPE, f.description));
+                }
+                if ("from".equals(f.transition)) {
+                    aes.add(TRANSITION_FROM);
+                }
+                if ("to".equals(f.transition)) {
+                    aes.add(TRANSITION_TO);
+                }
+                type.add(PrivateAccess.getInstance().newValueDescriptor(f.name, fieldType, aes, f.array ? 1 : 0, f.constantPool, null));
+            }
+        }
+    }
+
+    private AnnotationElement newAnnotation(Type type, Object value) {
+        SMALL_TEMP_LIST.set(0, value);
+        return PrivateAccess.getInstance().newAnnotation(type, SMALL_TEMP_LIST, true);
+    }
+
+    private Map<String, Type> buildTypeMap() {
+        Map<String, Type> typeMap = new HashMap<>(2 * types.size());
+        Map<String, Type> knownTypeMap = new HashMap<>(20);
+        for (Type kt : Type.getKnownTypes()) {
+            typeMap.put(kt.getName(), kt);
+            knownTypeMap.put(kt.getName(), kt);
+        }
+        for (TypeElement t : types) {
+            List<AnnotationElement> aes = new ArrayList<>();
+            if (!t.category.isEmpty()) {
+                AnnotationElement cat = categories.get(t.category);
+                if (cat == null) {
+                    String[] segments = buildCategorySegments(t.category);
+                    cat = newAnnotation(CATEGORY_TYPE, segments);
+                    categories.put(t.category, cat);
+                }
+                aes.add(cat);
+            }
+            if (!t.label.isEmpty()) {
+                aes.add(newAnnotation(LABEL_TYPE, t.label));
+            }
+            if (!t.description.isEmpty()) {
+                aes.add(newAnnotation(DESCRIPTION_TYPE, t.description));
+            }
+            if (t.isEvent) {
+                if (!t.period.isEmpty()) {
+                    aes.add(newAnnotation(PERIOD_TYPE, t.period));
+                } else {
+                    if (t.startTime) {
+                        aes.add(THRESHOLD);
+                    }
+                    if (t.stackTrace) {
+                        aes.add(STACK_TRACE);
+                    }
+                }
+                if (t.cutoff) {
+                    aes.add(new AnnotationElement(Cutoff.class, Cutoff.INFINITY));
+                }
+            }
+            if (t.experimental) {
+                aes.add(EXPERIMENTAL);
+            }
+            Type type;
+            if (t.isEvent) {
+                aes.add(ENABLED);
+                type = new PlatformEventType(t.name, t.id, false, true);
+            } else {
+                type = knownTypeMap.get(t.name);
+                if (type == null) {
+                    if (t.isRelation) {
+                        type = new Type(t.name, Type.SUPER_TYPE_ANNOTATION, t.id);
+                        aes.add(RELATIONAL);
+                    } else {
+                        type = new Type(t.name, null, t.id);
+                    }
+                }
+            }
+            type.setAnnotations(aes);
+            typeMap.put(t.name, type);
+        }
+        return typeMap;
+    }
+
+    private String[] buildCategorySegments(String category) {
+        String[] segments = category.split(",");
+        for (int i = 0; i < segments.length; i++) {
+            segments[i] = segments[i].trim();
+        }
+        return segments;
+    }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java	Sat Jun 20 15:11:19 2020 +0800
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java	Mon Jun 22 08:09:23 2020 +0200
@@ -26,6 +26,7 @@
 package jdk.jfr.internal;
 
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Repeatable;
 import java.lang.reflect.Field;
@@ -56,10 +57,12 @@
 import jdk.jfr.Timespan;
 import jdk.jfr.Timestamp;
 import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.tool.PrettyWriter;
 
 public final class TypeLibrary {
 
     private static TypeLibrary instance;
+    private static boolean implicitFieldTypes;
     private static final Map<Long, Type> types = new LinkedHashMap<>(100);
     static final ValueDescriptor DURATION_FIELD = createDurationField();
     static final ValueDescriptor THREAD_FIELD = createThreadField();
@@ -108,7 +111,7 @@
             if (instance == null) {
                 List<Type> jvmTypes;
                 try {
-                    jvmTypes = MetadataHandler.createTypes();
+                    jvmTypes = MetadataLoader.createTypes();
                     Collections.sort(jvmTypes, (a,b) -> Long.compare(a.getId(), b.getId()));
                 } catch (IOException e) {
                     throw new Error("JFR: Could not read metadata");
@@ -315,10 +318,13 @@
 
     // By convention all events have these fields.
     static void addImplicitFields(Type type, boolean requestable, boolean hasDuration, boolean hasThread, boolean hasStackTrace, boolean hasCutoff) {
-        createAnnotationType(Timespan.class);
-        createAnnotationType(Timestamp.class);
-        createAnnotationType(Label.class);
-        defineType(long.class, null,false);
+        if (!implicitFieldTypes) {
+            createAnnotationType(Timespan.class);
+            createAnnotationType(Timestamp.class);
+            createAnnotationType(Label.class);
+            defineType(long.class, null, false);
+            implicitFieldTypes = true;
+        }
         addFields(type, requestable, hasDuration, hasThread, hasStackTrace, hasCutoff);
     }