{"id":870,"date":"2017-09-21T20:15:30","date_gmt":"2017-09-21T19:15:30","guid":{"rendered":"http:\/\/hirt.se\/blog\/?p=870"},"modified":"2017-09-21T22:18:35","modified_gmt":"2017-09-21T21:18:35","slug":"jfr-in-jdk-9","status":"publish","type":"post","link":"https:\/\/hirt.se\/blog\/?p=870","title":{"rendered":"Java Flight Recorder in JDK 9"},"content":{"rendered":"<p>As you probably already know, JFR is on the road to be open sourced. The plans are being drawn, and if everything goes well, this could happen as early as in JDK 10 (JDK 18.3). No promises, and please do not give anyone a hard time if this doesn\u2019t work out. Know that we tried. Anyways, what we did recently do was to release JDK 9, and there are a LOT of exciting stuff happening for JFR already in JDK 9. <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" alt=\"Smile\" src=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2017\/09\/wlEmoticon-smile-1.png\"><\/p>\n<p>Let\u2019s dive into it\u2026<\/p>\n<h1>Supported APIs<\/h1>\n<p>Yep. The JFR APIs in the JDK are supported. This means that unlike all my other posts on JFR related APIs, this post does not contain any disclaimer for using these APIs. They are supported! By Oracle! Woho!<\/p>\n<p>On the sad side, this also means that the APIs have changed package, and signatures. On the plus side, they have been immensely enhanced for readability, performance and ease of use.<\/p>\n<p>I will group the API as follows:<\/p>\n<ul>\n<li>API for Custom Events<\/li>\n<li>API for Controlling the Flight Recorder<\/li>\n<li>API for Consuming Recordings<\/li>\n<\/ul>\n<p>Now, there is an advanced API for consuming recordings included with JMC. That API sports internal iteration, aggregation in various forms, JDK 7, 8 and 9 JFR file compatibility, JDK 7 and later execution compatibility, POJO (no weird dependencies), rules evaluation, and more. This API will also be open sourced, and I will discuss it in a <em><strong>later<\/strong><\/em> post.<\/p>\n<h2>Creating Custom Events<\/h2>\n<p>The API for creating and recording your own events is very simple. All you need to do is to subclass <font color=\"#ff0000\">jdk.jfr.Event<\/font>.and annotate the fields you wish to record with <font color=\"#ff0000\">jdk.jfr.Label<\/font>.<\/p>\n<p><pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nimport jdk.jfr.Event;\r\nimport jdk.jfr.Label;\r\n\r\npublic class Hello {\r\n\t@Label(&quot;Hello World&quot;)\r\n\tstatic class HelloWorldEvent extends Event {\r\n\t\t@Label(&quot;Message&quot;)\r\n\t\tString message;\r\n\t}\r\n\tpublic static void main(String... args) {\r\n\t\tHelloWorldEvent event = new HelloWorldEvent();\r\n\t\tevent.message = &quot;Hello World event message!&quot;;\r\n\t\tevent.commit();\r\n\t}\r\n}\r\n<\/pre>\n<p>It is good form to provide a description for your event class and for non-trivial attributes. Here is an example of an event from a JUnit plug-in for JUnit 5 (that I have not released for JDK 9 yet):<\/p>\n<p><\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n\/**\r\n * Event for a test run and completed normally (without exception).\r\n * \r\n * @author Marcus Hirt\r\n *\/\r\n@Label(&quot;Test Run&quot;)\r\n@Description(&quot;JUnit test executed and completed normally without exception&quot;)\r\n@Category(&quot;JUnit&quot;)\r\npublic class TestEvent extends Event {\r\n    @Label(&quot;Display Name&quot;)\r\n    @Description(&quot;The JUnit display name for the test&quot;)\r\n    private String displayName;\r\n\r\n    public void setDisplayName(String displayName) {\r\n        this.displayName = displayName;\r\n    }\r\n\r\n    public String getDisplayName() {\r\n        return displayName;\r\n    }\r\n}\r\n<\/pre>\n<\/p>\n<p>Other important annotations are, for example, the annotation for providing information about the content. These annotations are put on fields to explain to tools how to interpret the data. For example, Java Mission Control would be able to properly render timestamps and durations properly, if the appropriate @Timestamp and @Timespan annotations were used. I have provided a quick overview of some of the most commonly used ones below:<\/p>\n<table width=\"80%\" border=\"1\" cellspacing=\"0\" cellpadding=\"2\">\n<tbody>\n<tr>\n<th valign=\"top\">Annotation<\/th>\n<th valign=\"top\">Comment<\/th>\n<\/tr>\n<tr>\n<td valign=\"top\">@Address<\/td>\n<td valign=\"top\">The value is a memory address<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">@BooleanFlag<\/td>\n<td valign=\"top\">The value is a boolean flag. Strictly speaking perhaps not the most useful one.<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">@Frequency<\/td>\n<td valign=\"top\">The value is a frequency, in Hz<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">@MemoryAddress<\/td>\n<td valign=\"top\">The value is a memory address.<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">@MemoryAmount<\/td>\n<td valign=\"top\">The value is an amount of memory, in bytes.<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">@NetworkAddress<\/td>\n<td valign=\"top\">The value is a network address. Value is a long below 2^32 or an unsigned integer and clients will likely display it in the usual octet format (e.g. 127.0.0.1).<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">@Percentage<\/td>\n<td valign=\"top\">The event is a fraction, typically between 0 and 1, and is best represented as a percentage<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">@Timespan<\/td>\n<td valign=\"top\">The event is a duration. Defaults to nanoseconds, but can be changed by setting value to one of the static fields defined in Timespan: MICROSECONDS, MILLISECONDS, NANOSECONDS, SECONDS, TICKS. It is always nice to explicitly set value (even for NANOSECONDS), so that it can be readily seen what was intended.<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">@Timestamp<\/td>\n<td valign=\"top\">The event is a timestamp. Defaults to MILLISECONDS_TO_EPOCH, but can be changed by setting value to one of the static fields defined in Timestamp: MILLISECONDS_SINCE_EPOCH, TICKS. It is always nice to explicitly set value (even for MILLISECONDS_SINCE_EPOCH), so that it can be readily seen what was intended.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><font style=\"font-size: 12pt\"><\/font><\/p>\n<p>There are other annotations worth knowing about such as these:<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"2\">\n<tbody>\n<tr>\n<th valign=\"top\">Annotation<\/th>\n<th valign=\"top\">Comment<\/th>\n<\/tr>\n<tr>\n<td valign=\"top\">@Enabled<\/td>\n<td valign=\"top\">Determines if the event should be enabled by default. Remember the bad old days when you added an event, and then did not see it recorded, since the template did not include it? No more. Also, events are enabled by default, so be careful to explicitly set this to false if the event is expensive. Enable is true by default.<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">@Experimental<\/td>\n<td valign=\"top\">The element is experimental, and may change in future versions without prior notice.<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">@Label<\/td>\n<td valign=\"top\">The human readable label for the element. Use headline-style capitalization, capitalize first and last words, and all nouns, pronouns, verbs and adverbs. Do not include punctuation.<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">@Period<\/td>\n<td valign=\"top\">The default period for a periodic event.<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">@Registered<\/td>\n<td valign=\"top\">Events are automatically registered by default when they are first used. This annotation can override this behaviour. If you want to make sure that an event which is not likely to be encountered is registered, perhaps to make the metadata available to tools, you can use FlightRecorder.register(class) to register the event programatically.<\/td>\n<tr>\n<td valign=\"top\">@Threshold<\/td>\n<td valign=\"top\">Defines the default threshold for the event. When an even have a duration lower than the threshold, it will not be recorded. The user can override this threshold in the template settings.<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">@Unsigned<\/td>\n<td valign=\"top\">The field value should be treated as an unsigned value.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\n<br \/>This is by no means an exhaustive list of all the annotations, and as soon as I find out where the javadocs will end up, I will add a link to them.<\/p>\n<p>Note that the programming model is simpler, compared to previous releases. There is no Producer with which you need to register the events. The life cycle of an event simply follows the life cycle of the event class. Also, new event types are enabled by default. As a matter of fact, all the defaults for an event type can be configured using annotations. Also, compared to previous releases, you do not need to explicitly reuse event objects and keep event tokens around; care has been taken to ensure that events on the hot path should be easy to scalarize. <\/p>\n<h2>API for Controlling the FlightRecorder<\/h2>\n<p>In JDK 9 there are actually two, related, APIs for controlling the FlightRecorder. Just like before, there will be a JMX API. This API provides similar capabilities as the JDK 8 JMX API, but the API has been cleaned up and moved from the com.oracle.jrockit domain to the jdk.management.flightrecorder domain.<\/p>\n<p><a href=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2017\/09\/image-4.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1028\" height=\"530\" title=\"image\" alt=\"image\" src=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2017\/09\/image_thumb-4.png\" border=\"0\"><\/a><\/p>\n<p>I will not spend much time on the JMX API. A good example on how to use the different versions of it will be provided in the JMC source, once it is open sourced.<\/p>\n<p><\/p>\n<p>Another addition is an easy to use local API for controlling the Flight Recorder. Here is an example which starts a recording, records the calculation of the first 50 Fibonacci numbers and writes the recording to disk:<\/p>\n<p><pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nimport java.io.IOException;\r\nimport java.nio.file.Path;\r\nimport java.nio.file.Paths;\r\n\r\nimport com.oracle.example.jdk9jfr.fib.Fibonacci;\r\nimport com.oracle.example.jdk9jfr.fib.FibonacciEvent;\r\n\r\nimport jdk.jfr.Recording;\r\n\r\n\/**\r\n * Example program both controlling the recorder and parsing the resulting data.\r\n * \r\n * The program will calculate the 50 first Fibonacci numbers, then print them\r\n * out.\r\n *\/\r\npublic class RecordOnly {\r\n\tpublic static void main(String&#x5B;] args) throws IOException {\r\n\t\tif (args.length == 0) {\r\n\t\t\tSystem.out.println(&quot;Need to specify a location for the recording!&quot;);\r\n\t\t\tSystem.exit(2);\r\n\t\t}\r\n\t\t\r\n\t\tPath path = Paths.get(args&#x5B;0]);\r\n\r\n\t\ttry (Recording recording = new Recording()) {\r\n\t\t\trecording.setName(&quot;Fibonacci Recording&quot;);\r\n\t\t\trecording.start();\r\n\t\t\trecording.enable(FibonacciEvent.class);\r\n\t\t\tfor (int n = 0; n &amp;lt; 50; n++) {\r\n\t\t\t\tSystem.out.println(&quot;Calculating fib &quot; + n);\r\n\t\t\t\tFibonacciEvent event = new FibonacciEvent();\r\n\t\t\t\tevent.number = n;\r\n\t\t\t\tevent.begin();\r\n\t\t\t\tevent.value = Fibonacci.fibonacciIterative(n);\r\n\t\t\t\tevent.commit();\r\n\t\t\t}\r\n\t\t\trecording.stop();\r\n\t\t\trecording.dump(path);\r\n\t\t}\r\n\t}\r\n}\r\n<\/pre>\n<\/p>\n<h2>Consuming Recordings<\/h2>\n<p>There is a very easy to use API for consuming the recordings included in the JDK as well. It is only supported to use it to read recordings produced from the same JDK as the API reading it, and it is only supported to run the parser in the same JDK with which it is provided. So, in short, JFR recordings produced by JDK 9, and the parser running on JDK 9.<\/p>\n<p>As I mentioned in the beginning, there is another parser included in JMC, with supported reading JDK 7, 8 and 9 recordings, and which supports internal iteration, aggregation in various forms, JDK 7 and later execution compatibility, and rules evaluation. It is still POJO, so it can be included into anything. Which parser to use depends on what you are trying to accomplish.<\/p>\n<p>Here is an addition to the previous example which also reads the produced recording using the Parser built into the JDK, and which then writes out information from the captured events on stdout:<\/p>\n<p><pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nimport java.io.IOException;\r\nimport java.nio.file.Path;\r\nimport java.nio.file.Paths;\r\n\r\nimport com.oracle.example.jdk9jfr.fib.Fibonacci;\r\nimport com.oracle.example.jdk9jfr.fib.FibonacciEvent;\r\n\r\nimport jdk.jfr.Recording;\r\nimport jdk.jfr.consumer.RecordedEvent;\r\nimport jdk.jfr.consumer.RecordingFile;\r\n\r\n\/**\r\n * Example program both controlling the recorder and parsing the resulting data.\r\n * \r\n * The program will calculate the 50 first Fibonacci numbers, then print them\r\n * out.\r\n *\/\r\npublic class RecordAndConsume {\r\n\tpublic static void main(String&#x5B;] args) throws IOException {\r\n\t\tif (args.length == 0) {\r\n\t\t\tSystem.out.println(&quot;Need to specify a location for the recording!&quot;);\r\n\t\t\tSystem.exit(2);\r\n\t\t}\r\n\t\t\r\n\t\tPath path = Paths.get(args&#x5B;0]);\r\n\r\n\t\ttry (Recording recording = new Recording()) {\r\n\t\t\trecording.setName(&quot;Fibonacci Recording&quot;);\r\n\t\t\trecording.start();\r\n\t\t\trecording.enable(FibonacciEvent.class);\r\n\t\t\tfor (int n = 0; n &amp;lt; 50; n++) {\r\n\t\t\t\tSystem.out.println(&quot;Calculating fib &quot; + n);\r\n\t\t\t\tFibonacciEvent event = new FibonacciEvent();\r\n\t\t\t\tevent.number = n;\r\n\t\t\t\tevent.begin();\r\n\t\t\t\tevent.value = Fibonacci.fibonacciIterative(n);\r\n\t\t\t\tevent.commit();\r\n\t\t\t}\r\n\t\t\trecording.stop();\r\n\t\t\trecording.dump(path);\r\n\t\t\tfor (RecordedEvent event : RecordingFile.readAllEvents(path)) {\r\n\t\t\t\tint number = event.getValue(&quot;number&quot;);\r\n\t\t\t\tlong value = event.getValue(&quot;value&quot;);\r\n\t\t\t\tSystem.out.printf(&quot;fibonacci(%d) = %d (time: %dns)\\n&quot;, number, value, event.getDuration().getNano());\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n<\/pre>\n<\/p>\n<h1>Other Improvements to JFR in JDK 9<\/h1>\n<p>Here is a list of some other improvements to JFR in JDK9:<\/p>\n<ul>\n<li>Modularized APIs (if you do not need the JMX API, don\u2019t include it).<\/li>\n<li>Extensible APIs (can provide custom settings for event types, can provide custom metadata using annotations)<\/li>\n<li>Performance improvements (more on this later)<\/li>\n<li>Can emit data even in bad situations (Out of Memory for example)<\/li>\n<li>New events<\/li>\n<ul>\n<li>More detailed safe point information<\/li>\n<li>More detailed code cache information<\/li>\n<li>New PLAB (promotion) events<\/li>\n<li>New compiler events for detailed inlining information<\/li>\n<li>New H1 specific information for better visualization of region states<\/li>\n<li>Module events (loaded modules, full transitive closure)<\/li>\n<li>NativeLibrary (load, a periodic event, by default emitted each chunk)<\/li>\n<li>\u2026and more. Just make a test recording and include them all. <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-winkingsmile\" alt=\"Winking smile\" src=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2017\/09\/wlEmoticon-winkingsmile.png\"><\/li>\n<\/ul>\n<\/ul>\n<h2><\/h2>\n<h2>Performance Enhancements<\/h2>\n<p>Here is a graphic (provided by my excellent friend Erik Gahlin at the Oracle JPG Serviceability Team), which shows some of the areas where JFR has improved in JDK 9:<\/p>\n<p><a href=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2017\/09\/image-5.png\"><img loading=\"lazy\" decoding=\"async\" width=\"644\" height=\"269\" title=\"image\" alt=\"image\" src=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2017\/09\/image_thumb-5.png\" border=\"0\"><\/a><\/p>\n<p>Let\u2019s take them in order:<\/p>\n<ol>\n<li>The time spent in safepoints due to chunk rotation to disk has been reduced from a bit over 7ms to almost nothing.<\/li>\n<li>If you have code with a disabled Java event, the overhead of having the event related code in your method will be zero (if that code is on the hot path). After optimization, any trace of the event related code will be optimized away.<\/li>\n<li>The overhead of writing an event (without capturing stack traces), has gone from 8ns to 6ns.<\/li>\n<li>The file size (and buffer memory usage) has roughly been cut by half, due to the introduction of compressed integers.<\/li>\n<li>The time required to capture a stack trace has been almost cut in half.<\/li>\n<li>Even the startup has been slightly improved.<\/li>\n<\/ol>\n<p>So, not only have the APIs become easier to use, and more comprehensive. JFR has also become significantly faster. That is a bit of a feat, considering that it wasn\u2019t very slow to begin with.<\/p>\n<h2>Summary<\/h2>\n<p>The new Java release, JDK 9, provides plenty of improvements to JFR:<\/p>\n<ol>\n<li>Supported API<\/li>\n<li>Simplified programming model<\/li>\n<li>Higher performance<\/li>\n<\/ol>\n<p>This blog provided a quick introduction to JFR in JDK 9, with some simple examples.<\/p>\n<p>Here is a link to the full source for the examples: <a href=\"http:\/\/hirt.se\/downloads\/oracle\/src\/source_jdk9_jfr_examples.zip\">source_jdk9_jfr_examples.zip<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>As you probably already know, JFR is on the road to be open sourced. The plans are being drawn, and if everything goes well, this could happen as early as in JDK 10 (JDK 18.3). No promises, and please do not give anyone a hard time if this doesn\u2019t work out. Know that we tried. [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[12,16,14,13,15],"class_list":["post-870","post","type-post","status-publish","format-standard","hentry","category-java-mission-control-fair-dj","tag-java","tag-java-mission-control","tag-jdk-9","tag-jfr","tag-jmc"],"_links":{"self":[{"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=\/wp\/v2\/posts\/870","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=870"}],"version-history":[{"count":14,"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=\/wp\/v2\/posts\/870\/revisions"}],"predecessor-version":[{"id":934,"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=\/wp\/v2\/posts\/870\/revisions\/934"}],"wp:attachment":[{"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=870"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=870"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=870"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}