






{"id":20476,"date":"2023-12-31T11:16:00","date_gmt":"2023-12-31T11:16:00","guid":{"rendered":"https:\/\/dataforpublicgood.org.in\/?p=20476"},"modified":"2025-01-24T11:19:40","modified_gmt":"2025-01-24T11:19:40","slug":"profiling-java-applications-using-async-profiler-and-flame-graphs","status":"publish","type":"post","link":"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/","title":{"rendered":"Profiling Java Applications using Async Profiler and Flame Graphs"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"20476\" class=\"elementor elementor-20476\">\n\t\t\t\t\t\t<div class=\"elementor-inner\">\n\t\t\t\t<div class=\"elementor-section-wrap\">\n\t\t\t\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-f2eab70 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"f2eab70\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t\t\t<div class=\"elementor-row\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-b87a89a\" data-id=\"b87a89a\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-column-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t<div class=\"elementor-widget-wrap\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-3c06cee elementor-widget elementor-widget-text-editor\" data-id=\"3c06cee\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-text-editor elementor-clearfix\">\n\t\t\t\t<h2>Introduction<\/h2><p>A key requirement of a data exchange is good platform performance. Deciding what is good performance is usually determined by the data characteristics and query statistics. Serving a hundred small JSON documents per second may be considered slow, while serving a hundred 5-minute-long video files per second could be considered as excellent performance. There are various metrics which can be used to describe the performance of a data exchange such as requests per second. Different performance testing strategies are used to find bottlenecks in platforms which do not meet adequate performance metrics. We often turn to debuggers (or print statements) when trying to fix logic bugs when writing applications. But what happens when we find performance issues when load testing or stress testing the application? Using a debugger or observing debug logs may help, but they may not be enough. If a close reading of the code does not help in finding the issue, profiling the application would be the next best step to take.<br \/>This article aims to provide a technical overview to profile Java applications using Async Profiler and how to interpret the results in the form of flame graphs.<\/p><h3>Async Profiler<\/h3><p>Async Profiler is the profiler we are going to use. Apart from being actively developed and easy to use, the main advantage of Async Profiler is that it avoids safepoint bias when profiling. Understanding what safepoints in the JVM are is beyond the scope of this article, so take a look at this article to learn more about them.<\/p><h3>Flame Graphs<\/h3><p>After profiling the application, we need a way to interpret the results. Flame graphs were pioneered by Brendan Gregg as an intuitive way to visualize and interact with profiling results. I highly recommend his article that explains the motivations behind the creation of flame graphs and how to gather insights from them.<\/p><div id=\"cb1\" class=\"sourceCode\"><pre class=\"sourceCode java\"><code class=\"sourceCode java\"><span id=\"cb1-1\"><span class=\"kw\">public<\/span> <span class=\"kw\">class<\/span> FlameTest {<\/span>\n<span id=\"cb1-2\"><\/span>\n<span id=\"cb1-3\">  <span class=\"kw\">public<\/span> <span class=\"dt\">static<\/span> <span class=\"dt\">void<\/span> <span class=\"fu\">divisibleByTwo<\/span>() {<\/span>\n<span id=\"cb1-4\">    <span class=\"bu\">System<\/span>.<span class=\"fu\">out<\/span>.<span class=\"fu\">println<\/span>(<span class=\"st\">\"Divisible by 2\"<\/span>);<\/span>\n<span id=\"cb1-5\">    <span class=\"kw\">return<\/span>;<\/span>\n<span id=\"cb1-6\">  }<\/span>\n<span id=\"cb1-7\"><\/span>\n<span id=\"cb1-8\">  <span class=\"kw\">public<\/span> <span class=\"dt\">static<\/span> <span class=\"dt\">void<\/span> <span class=\"fu\">divisibleByFive<\/span>() {<\/span>\n<span id=\"cb1-9\">    <span class=\"bu\">System<\/span>.<span class=\"fu\">out<\/span>.<span class=\"fu\">println<\/span>(<span class=\"st\">\"Divisible by 5\"<\/span>);<\/span>\n<span id=\"cb1-10\">    <span class=\"kw\">return<\/span>;<\/span>\n<span id=\"cb1-11\">  }<\/span>\n<span id=\"cb1-12\"><\/span>\n<span id=\"cb1-13\">  <span class=\"kw\">public<\/span> <span class=\"dt\">static<\/span> <span class=\"dt\">void<\/span> <span class=\"fu\">main<\/span>(<span class=\"bu\">String<\/span>[] args) {<\/span>\n<span id=\"cb1-14\">    <span class=\"dt\">int<\/span> i = <span class=\"dv\">1<\/span>;<\/span>\n<span id=\"cb1-15\"><\/span>\n<span id=\"cb1-16\">    <span class=\"kw\">while<\/span> (i &lt; <span class=\"dv\">10000<\/span>) {<\/span>\n<span id=\"cb1-17\">      <span class=\"kw\">if<\/span> (i % <span class=\"dv\">2<\/span> == <span class=\"dv\">0<\/span>) {<\/span>\n<span id=\"cb1-18\">        <span class=\"fu\">divisibleByTwo<\/span>();<\/span>\n<span id=\"cb1-19\">      } <span class=\"kw\">else<\/span> <span class=\"kw\">if<\/span> (i % <span class=\"dv\">5<\/span> == <span class=\"dv\">0<\/span>) {<\/span>\n<span id=\"cb1-20\">        <span class=\"fu\">divisibleByFive<\/span>();<\/span>\n<span id=\"cb1-21\">      }<\/span>\n<span id=\"cb1-22\"><\/span>\n<span id=\"cb1-23\">      i++;<\/span>\n<span id=\"cb1-24\">    }<\/span>\n<span id=\"cb1-25\">  }<\/span>\n<span id=\"cb1-26\">}<\/span><\/code><\/pre><\/div><p>This simple program iterates over numbers from 1 to 10000, checks if the given number is divisible by 2 or by 5. Accordingly, a method divisibleByTwo or divisibleByFive is called. You can see the flame graph we obtained from profiling the application using Async Profiler.<\/p><p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15719 size-large\" src=\"https:\/\/iudx.org.in\/wp-content\/uploads\/2023\/09\/Flame-graph-generated-from-profiling-FlameTest-1024x207.png\" alt=\"\" width=\"1024\" height=\"207\" \/><\/p><p style=\"text-align: center;\"><strong><em>Fig:<\/em><\/strong><em> Flame graph generated from profiling <\/em><em>FlameTest<\/em><\/p><p>Observe the different method names that are present across the graph. Methods that are on top of each other are nested method calls. There are the methods that we have written like divisibleByTwo and divisibleByFive, internal Java methods and Linux system calls.<br \/>As a simple rule-of-thumb, the wider a method is on the x-axis, the more time it spent being processed by the CPU during profiling. Similarly, taller lists of functions denote deeply-nested method calls.<br \/>Notice that divisibleByTwo is much larger than divisibleByFive in the flame graph. Since there are more numbers divisible by 2 than 5 from 1 to 10000, divisibleByTwo was present on the CPU more often than divisbleByFive.<\/p><p>In the way that we notice expected behaviour when profiling this program, we can discover unexpected behaviour when profiling real world applications.<\/p><h2>Setting up and using Async Profiler<\/h2><p>You can download async profiler from <a href=\"https:\/\/github.com\/async-profiler\/async-profiler#download\" target=\"_blank\" rel=\"noopener\">here<\/a> for the appropriate architecture. Once downloaded and unzipped, the profiler.sh script is what we would use.<\/p><ul><li>Start your Java application normally and then find out the PID of the associated running process (you can use jps, ps aux or htop for this).<\/li><\/ul><p>$ # starting the Java application<br \/>$ java -jar target\/profilertest-1.0.0-SNAPSHOT-fat.jar<\/p><p>$ # in another terminal, use jps to find the PID of the running JAR<br \/>$ # jps is usually installed automatically when installing the JDK<br \/>$ jps<br \/>3008 XMLServerLauncher<br \/>6947 Jps<br \/>6661 profilertest-1.0.0-SNAPSHOT-fat.jar<br \/>2874 org.eclipse.equinox.launcher_1.5.800.v20200727-1323.jar<\/p><ul><li>Start profiling the application by calling sh with the PID of the process. Note that we configure Async Profiler with the <a href=\"https:\/\/github.com\/async-profiler\/async-profiler\/issues\/272\" target=\"_blank\" rel=\"noopener\">itimer<\/a><a href=\"https:\/\/github.com\/async-profiler\/async-profiler\/issues\/272\" target=\"_blank\" rel=\"noopener\"> event flag<\/a>. The preferred way is to set it to cpu, but this requires a little more work to <a href=\"https:\/\/github.com\/async-profiler\/async-profiler\/wiki\/Basic-Usage\" target=\"_blank\" rel=\"noopener\">configure<\/a>. itimer is good enough for quick tests, but it\u2019s advised to use cpu when you need more accurate results.<\/li><\/ul><p>$ # start profiling<br \/>$ profiler.sh start -e itimer 6661<br \/>Profiling started<\/p><ul><li>Once profiling has started, we need to execute the code paths that are to be inspected. Since we are testing a web server here, a tool like <a href=\"https:\/\/jmeter.apache.org\/\" target=\"_blank\" rel=\"noopener\">jMeter<\/a> or <a href=\"https:\/\/github.com\/wg\/wrk\" target=\"_blank\" rel=\"noopener\">wrk<\/a> can be configured to call the API repeatedly for some amount of time or for n number of requests. Ensure that the code path is hit a good number of times so that an accurate profiling result can be obtained.<\/li><\/ul><p>$ # using wrk to call the API we want to test repeatedly for 30 seconds<br \/>$ .\/wrk -c 10 -t 5 -d 30s http:\/\/127.0.0.1:8080\/file\/some-file<\/p><ul><li>We can stop the profiling once we feel that the application has been adequately tested. Async Profiler allows us to stop profiling and immediately dump the results as a flame graph in a HTML file.<\/li><\/ul><p>$ # stop profiling and dump the output as a flame graph<br \/>$ profiler.sh stop -o flamegraph -f flamegraph-op.html 6661<\/p><h2>A profiling example<\/h2><p>We have a web server written in Vert.x that serves a 50MB file called sample in a directory called files. On Linux we can quickly create such a file with the command head -c 50M \/dev\/random &gt; sample<\/p><pre><code>\npackage com.example.profilertest;\nimport io.vertx.core.AbstractVerticle;\nimport io.vertx.core.Promise;\nimport io.vertx.core.http.HttpServer;\nimport io.vertx.core.http.HttpServerResponse;\nimport io.vertx.ext.web.Router;\n\npublic class MainVerticle extends AbstractVerticle {\n  <span class=\"keyword\">@Override<\/span>\n  <span class=\"keyword\">public void<\/span> start(Promise startPromise) <span class=\"keyword\">throws<\/span> Exception {\n    HttpServer server = vertx.createHttpServer();\n    Router router = Router.router(vertx);\n    router.route(\"\/files\/sample\").handler(ctx -&gt; {\n      HttpServerResponse response = ctx.response();\n      <span class=\"comment\">\/\/ Write to the response and end it<\/span>\n      vertx.fileSystem().readFile(\"files\/sample\").onSuccess(file -&gt; {\n        response.end(file);\n      }).onFailure(fail -&gt; {\n        response.setStatusCode(500).end();\n      });\n    });\n    server.requestHandler(router).listen(8080);\n  }\n}\n    <\/code><\/pre><p>Let\u2019s try using wrk on the server without profiling. wrk will call the GET \/files\/sample API repeatedly for 30 seconds using 10 connections.<\/p><p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15722 size-full\" src=\"https:\/\/iudx.org.in\/wp-content\/uploads\/2023\/09\/Using-wrk-to-test-the-API-without-profiling.png\" alt=\"\" width=\"843\" height=\"274\" \/><\/p><p style=\"text-align: center;\"><strong><em>Fig:<\/em><\/strong><em> Using wrk to test the API without profiling<\/em><\/p><p>A performance of 5 requests per seconds is quite poor, even for such a simple API. Using the steps from the previous section, we can tell Async Profiler to start profiling the server, use wrk again to call the API, stop profiling and view the resulting flame graph.<\/p><p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15723 size-large\" src=\"https:\/\/iudx.org.in\/wp-content\/uploads\/2023\/09\/Flame-graph-generated-from-profiling-the-server-1024x643.png\" alt=\"\" width=\"1024\" height=\"643\" \/><\/p><p style=\"text-align: center;\"><strong><em>Fig:<\/em><\/strong><em> Flame graph generated from profiling the server<\/em><\/p><p>We see that the file-system methods dominate the processing time. You may have already guessed it; reading from the file system at every API call is surely a bottleneck.<\/p><h3>Where profiling comes handy<\/h3><p>Profiling really shines when trying to find <strong>non-obvious<\/strong> performance bottlenecks in applications. Some bottlenecks are quite easy to spot when you read the code, like a nested loop. But profiling combined with a good visualization tool like flame graphs gives an idea of what our application is doing under the hood and therefore aids in rooting out the issue.<br \/>There have been several times that profiling has come to the rescue at IUDX when debugging performance issues. Some of the instances it\u2019s been useful are:<\/p><ul><li><strong>When an application is using a library that we don\u2019t have much experience with<\/strong>. When we include a library in our application, and observe that there are performance issues, we reach for profiling to make sure that it\u2019s not due to the inclusion of the particular library. Any indication of the library being the cause of the bottleneck prompts us to re-read the library documentation to check if we are using the library correctly or reconsider using the library itself.<\/li><li><strong>When an application has many external dependencies like databases, other services etc.<\/strong> Sometimes it may be difficult to add debug loggers to a poorly performing application to identify a troublesome dependency. Profiling is the best option here since the application does not need to be modified at all to be profiled. Async Profiler can be <a href=\"https:\/\/github.com\/async-profiler\/async-profiler#profiling-java-in-a-container\" target=\"_blank\" rel=\"noopener\">used in a container too<\/a>, which makes profiling possible for applications that are in production or staging.<\/li><\/ul><h2>Solving the issue<\/h2><p>The right way to solve the issue in our file server would be to fetch the requested file from the filesystem the first time it\u2019s requested and then cache it in memory so that it can be accessed faster for every subsequent request. We can implement this logic ourselves, but let&#8217;s check the <a href=\"https:\/\/vertx.io\/docs\/vertx-web\/java\/\" target=\"_blank\" rel=\"noopener\">Vert.x-Web documentation<\/a> to see if they have any built-in solutions.<\/p><h3>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u201c<em>Serving static resources<\/em><\/h3><p><em>Vert.x-Web comes with an out of the box handler for serving static web resources so you can write static web servers very easily.<br \/><\/em><em>To serve static resources such as .html, .css, .js or any other static resource, you use an instance of <\/em><em>StaticHandler.<br \/><\/em><em>Any requests to paths handled by the static handler will result in files being served from a directory on the file system or from the classpath. The default static file directory is <\/em><em>webroot but this can be configured.<br \/><\/em><em>\u2026<br \/><\/em><em>When Vert.x <strong>finds a resource on the classpath for the first time it extracts it and caches it in a temporary directory on disk so it doesn\u2019t have to do this each time<\/strong>.\u201d<br \/><\/em><a href=\"https:\/\/vertx.io\/docs\/vertx-web\/java\/#_serving_static_resources\" target=\"_blank\" rel=\"noopener\"><strong><em>Vert.x <\/em><\/strong><\/a><a href=\"https:\/\/vertx.io\/docs\/vertx-web\/java\/#_serving_static_resources\" target=\"_blank\" rel=\"noopener\"><strong><em>StaticHandler<\/em><\/strong><\/a><a href=\"https:\/\/vertx.io\/docs\/vertx-web\/java\/#_serving_static_resources\" target=\"_blank\" rel=\"noopener\"><strong><em> documentation<\/em><\/strong><\/a><br \/>StaticHandler looks like the solution to our performance problems! Let\u2019s modify our server code to use it.<\/p><pre><code>\npackage com.example.profilertest;\nimport io.vertx.core.AbstractVerticle;\nimport io.vertx.core.Promise;\nimport io.vertx.core.http.HttpServer;\nimport io.vertx.ext.web.Router;\nimport io.vertx.ext.web.handler.FileSystemAccess;\nimport io.vertx.ext.web.handler.StaticHandler;\n\npublic class MainVerticle extends AbstractVerticle {\n  <span class=\"keyword\">@Override<\/span>\n  <span class=\"keyword\">public void<\/span> start(Promise startPromise) <span class=\"keyword\">throws<\/span> Exception {\n    HttpServer server = vertx.createHttpServer();\n    Router router = Router.router(vertx);\n    <span class=\"comment\">\/*\n     * route any API calls for the endpoint `\/files\/*` to serve the requested files (if present)\n     * from the `files` directory\n     *\/<\/span>\n    outer.route(\"\/files\/*\").handler(StaticHandler.create(FileSystemAccess.RELATIVE, \"files\"));\n    server.requestHandler(router).listen(8080);\n  }\n}\n    <\/code><\/pre><p>Let\u2019s run the server and test it with wrk again.<\/p><p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15724 size-full\" src=\"https:\/\/iudx.org.in\/wp-content\/uploads\/2023\/09\/Using-wrk-to-test-the-updated-API.png\" alt=\"\" width=\"850\" height=\"277\" \/><\/p><p style=\"text-align: center;\"><strong><em>Fig:<\/em><\/strong><em> Using wrk to test the updated API<\/em><\/p><p>We\u2019ve got almost a 7x increase in performance!<br \/>We can profile the application and observe how different the flame graph is from the \u2018bad\u2019 implementation.<\/p><p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15725 size-large\" src=\"https:\/\/iudx.org.in\/wp-content\/uploads\/2023\/09\/Flame-graph-generated-from-profiling-the-good-implementation-of-the-server-872x1024.png\" alt=\"\" width=\"872\" height=\"1024\" \/><\/p><p style=\"text-align: center;\"><strong><em>Fig:<\/em><\/strong><em> Flame graph generated from profiling the good implementation of the server<\/em><\/p><p>sendfile shows up quite a lot, this is a Linux system call used to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Zero-copy\" target=\"_blank\" rel=\"noopener\">zero-copy<\/a> the file for efficient transmission.<br \/>Flame graphs were designed to be interactive; you can search for particular method names and click on methods to explore its nested methods more closely. We have linked the flame graphs for the bad and good implementations of the server here so that you can download and play around with them.<\/p><h2>Conclusion<\/h2><p>I hope that through this article, you\u2019ve seen how profiling and Async Profiler can become an essential part of your Java debugging toolkit and even be integrated into your development and testing lifecycle. If you\u2019re interested in understanding how profiling actually works, I can recommend this <a href=\"https:\/\/mostlynerdless.de\/blog\/2023\/03\/27\/writing-a-profiler-in-240-lines-of-pure-java\/\" target=\"_blank\" rel=\"noopener\">article<\/a> that guides you in implementing a toy Java profiler.<\/p><p>Author: <strong>Bryan Paul Robert<\/strong><br \/>Reviewed by: <strong>Mahidhar Chellamani<\/strong><\/p>\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-dc48607 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"dc48607\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t\t\t<div class=\"elementor-row\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-1759180\" data-id=\"1759180\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-column-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t<div class=\"elementor-widget-wrap\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-ff2effd elementor-widget elementor-widget-html\" data-id=\"ff2effd\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<style>\r\n    code{white-space: pre-wrap;}<br \/>    span.smallcaps{font-variant: small-caps;}<br \/>    span.underline{text-decoration: underline;}<br \/>    div.column{display: inline-block; vertical-align: top; width: 50%;}<br \/>    div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}<br \/>    ul.task-list{list-style: none;}<br \/>    pre > code.sourceCode { white-space: pre; position: relative; }<br \/>    pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }<br \/>    pre > code.sourceCode > span:empty { height: 1.2em; }<br \/>    code.sourceCode > span { color: inherit; text-decoration: inherit; }<br \/>    div.sourceCode { margin: 1em 0; }<br \/>    pre.sourceCode { margin: 0; }<br \/>    @media screen {<br \/>    div.sourceCode { overflow: auto; }<br \/>    }<br \/>    @media print {<br \/>    pre > code.sourceCode { white-space: pre-wrap; }<br \/>    pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }<br \/>    }<br \/>    pre.numberSource code<br \/>      { counter-reset: source-line 0; }<br \/>    pre.numberSource code > span<br \/>      { position: relative; left: -4em; counter-increment: source-line; }<br \/>    pre.numberSource code > span > a:first-child::before<br \/>      { content: counter(source-line);<br \/>        position: relative; left: -1em; text-align: right; vertical-align: baseline;<br \/>        border: none; display: inline-block;<br \/>        -webkit-touch-callout: none; -webkit-user-select: none;<br \/>        -khtml-user-select: none; -moz-user-select: none;<br \/>        -ms-user-select: none; user-select: none;<br \/>        padding: 0 4px; width: 4em;<br \/>        color: #aaaaaa;<br \/>      }<br \/>    pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }<br \/>    div.sourceCode<br \/>      {   }<br \/>    @media screen {<br \/>    pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }<br \/>    }<br \/>    code span.al { color: #ff0000; font-weight: bold; }<br \/>    code span.an { color: #60a0b0; font-weight: bold; font-style: italic; }<br \/>    code span.at { color: #7d9029; }<br \/>    code span.bn { color: #40a070; }<br \/>    code span.bu { } \/* BuiltIn *\/<br \/>    code span.cf { color: #007020; font-weight: bold; }<br \/>    code span.ch { color: #4070a0; }<br \/>    code span.cn { color: #880000; }<br \/>    code span.co { color: #60a0b0; font-style: italic; }<br \/>    code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; }<br \/>    code span.do { color: #ba2121; font-style: italic; }<br \/>    code span.dt { color: #902000; }<br \/>    code span.dv { color: #40a070; }<br \/>    code span.er { color: #ff0000; font-weight: bold; }<br \/>    code span.ex { }<br \/>    code span.fl { color: #40a070; }<br \/>    code span.fu { color: #06287e; }<br \/>    code span.im { }<br \/>    code span.in { color: #60a0b0; font-weight: bold; font-style: italic; }<br \/>    code span.kw { color: #007020; font-weight: bold; }<br \/>    code span.op { color: #666666; }<br \/>    code span.ot { color: #007020; }<br \/>    code span.pp { color: #bc7a00; }<br \/>    code span.sc { color: #4070a0; }<br \/>    code span.ss { color: #bb6688; }<br \/>    code span.st { color: #4070a0; }<br \/>    code span.va { color: #19177c; }<br \/>    code span.vs { color: #4070a0; }<br \/>    code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; }<br \/>  <\/style>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<div style=\"margin-top: 0px; margin-bottom: 0px;\" class=\"sharethis-inline-share-buttons\" ><\/div>","protected":false},"excerpt":{"rendered":"<p>Introduction A key requirement of a data exchange is good platform performance. Deciding what is good performance is usually determined by the data characteristics and query statistics. Serving a hundred small JSON documents per second may be considered slow, while serving a hundred 5-minute-long video files per second could be considered as excellent performance. There are various metrics which can &hellip;<\/p>\n","protected":false},"author":2,"featured_media":20477,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[1],"tags":[],"class_list":["post-20476","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v20.12 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Profiling Java Applications using Async Profiler and Flame Graphs - Data for Public Good<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Profiling Java Applications using Async Profiler and Flame Graphs - Data for Public Good\" \/>\n<meta property=\"og:description\" content=\"Introduction A key requirement of a data exchange is good platform performance. Deciding what is good performance is usually determined by the data characteristics and query statistics. Serving a hundred small JSON documents per second may be considered slow, while serving a hundred 5-minute-long video files per second could be considered as excellent performance. There are various metrics which can &hellip;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/\" \/>\n<meta property=\"og:site_name\" content=\"Data for Public Good\" \/>\n<meta property=\"article:published_time\" content=\"2023-12-31T11:16:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-01-24T11:19:40+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/dataforpublicgood.org.in\/cdpg\/wp-content\/uploads\/2025\/01\/Profiling-Java-Applications-using-Async-Profiler-and-Flame-Graphs-copy.webp\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"1158\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/webp\" \/>\n<meta name=\"author\" content=\"CDPG\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@AgriDataXchange\" \/>\n<meta name=\"twitter:site\" content=\"@AgriDataXchange\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"CDPG\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/\"},\"author\":{\"name\":\"CDPG\",\"@id\":\"https:\/\/dataforpublicgood.org.in\/#\/schema\/person\/c521da9a85ddb366900e8ab41c092fb4\"},\"headline\":\"Profiling Java Applications using Async Profiler and Flame Graphs\",\"datePublished\":\"2023-12-31T11:16:00+00:00\",\"dateModified\":\"2025-01-24T11:19:40+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/\"},\"wordCount\":1627,\"publisher\":{\"@id\":\"https:\/\/dataforpublicgood.org.in\/#organization\"},\"articleSection\":[\"Blog\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/\",\"url\":\"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/\",\"name\":\"Profiling Java Applications using Async Profiler and Flame Graphs - Data for Public Good\",\"isPartOf\":{\"@id\":\"https:\/\/dataforpublicgood.org.in\/#website\"},\"datePublished\":\"2023-12-31T11:16:00+00:00\",\"dateModified\":\"2025-01-24T11:19:40+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/dataforpublicgood.org.in\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Profiling Java Applications using Async Profiler and Flame Graphs\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/dataforpublicgood.org.in\/#website\",\"url\":\"https:\/\/dataforpublicgood.org.in\/\",\"name\":\"Data for Public Good\",\"description\":\"Data for Public Good\",\"publisher\":{\"@id\":\"https:\/\/dataforpublicgood.org.in\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/dataforpublicgood.org.in\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/dataforpublicgood.org.in\/#organization\",\"name\":\"Data for Public Good\",\"url\":\"https:\/\/dataforpublicgood.org.in\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/dataforpublicgood.org.in\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/adex.org.in\/wp-content\/uploads\/2022\/12\/adex-logo.png\",\"contentUrl\":\"https:\/\/adex.org.in\/wp-content\/uploads\/2022\/12\/adex-logo.png\",\"width\":720,\"height\":270,\"caption\":\"Data for Public Good\"},\"image\":{\"@id\":\"https:\/\/dataforpublicgood.org.in\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/twitter.com\/AgriDataXchange\",\"https:\/\/www.linkedin.com\/company\/agricultural-data-exchange\/?viewAsMember=true\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/dataforpublicgood.org.in\/#\/schema\/person\/c521da9a85ddb366900e8ab41c092fb4\",\"name\":\"CDPG\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/dataforpublicgood.org.in\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/07c2cc51f2149e714f30055318fd3fbb92c6e503091ab59827d9bd261e19342a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/07c2cc51f2149e714f30055318fd3fbb92c6e503091ab59827d9bd261e19342a?s=96&d=mm&r=g\",\"caption\":\"CDPG\"},\"url\":\"https:\/\/dataforpublicgood.org.in\/cdpg\/author\/iudx\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Profiling Java Applications using Async Profiler and Flame Graphs - Data for Public Good","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/","og_locale":"en_US","og_type":"article","og_title":"Profiling Java Applications using Async Profiler and Flame Graphs - Data for Public Good","og_description":"Introduction A key requirement of a data exchange is good platform performance. Deciding what is good performance is usually determined by the data characteristics and query statistics. Serving a hundred small JSON documents per second may be considered slow, while serving a hundred 5-minute-long video files per second could be considered as excellent performance. There are various metrics which can &hellip;","og_url":"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/","og_site_name":"Data for Public Good","article_published_time":"2023-12-31T11:16:00+00:00","article_modified_time":"2025-01-24T11:19:40+00:00","og_image":[{"width":1920,"height":1158,"url":"https:\/\/dataforpublicgood.org.in\/cdpg\/wp-content\/uploads\/2025\/01\/Profiling-Java-Applications-using-Async-Profiler-and-Flame-Graphs-copy.webp","type":"image\/webp"}],"author":"CDPG","twitter_card":"summary_large_image","twitter_creator":"@AgriDataXchange","twitter_site":"@AgriDataXchange","twitter_misc":{"Written by":"CDPG","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/#article","isPartOf":{"@id":"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/"},"author":{"name":"CDPG","@id":"https:\/\/dataforpublicgood.org.in\/#\/schema\/person\/c521da9a85ddb366900e8ab41c092fb4"},"headline":"Profiling Java Applications using Async Profiler and Flame Graphs","datePublished":"2023-12-31T11:16:00+00:00","dateModified":"2025-01-24T11:19:40+00:00","mainEntityOfPage":{"@id":"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/"},"wordCount":1627,"publisher":{"@id":"https:\/\/dataforpublicgood.org.in\/#organization"},"articleSection":["Blog"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/","url":"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/","name":"Profiling Java Applications using Async Profiler and Flame Graphs - Data for Public Good","isPartOf":{"@id":"https:\/\/dataforpublicgood.org.in\/#website"},"datePublished":"2023-12-31T11:16:00+00:00","dateModified":"2025-01-24T11:19:40+00:00","breadcrumb":{"@id":"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/dataforpublicgood.org.in\/cdpg\/blog\/profiling-java-applications-using-async-profiler-and-flame-graphs\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/dataforpublicgood.org.in\/"},{"@type":"ListItem","position":2,"name":"Profiling Java Applications using Async Profiler and Flame Graphs"}]},{"@type":"WebSite","@id":"https:\/\/dataforpublicgood.org.in\/#website","url":"https:\/\/dataforpublicgood.org.in\/","name":"Data for Public Good","description":"Data for Public Good","publisher":{"@id":"https:\/\/dataforpublicgood.org.in\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/dataforpublicgood.org.in\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/dataforpublicgood.org.in\/#organization","name":"Data for Public Good","url":"https:\/\/dataforpublicgood.org.in\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/dataforpublicgood.org.in\/#\/schema\/logo\/image\/","url":"https:\/\/adex.org.in\/wp-content\/uploads\/2022\/12\/adex-logo.png","contentUrl":"https:\/\/adex.org.in\/wp-content\/uploads\/2022\/12\/adex-logo.png","width":720,"height":270,"caption":"Data for Public Good"},"image":{"@id":"https:\/\/dataforpublicgood.org.in\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/twitter.com\/AgriDataXchange","https:\/\/www.linkedin.com\/company\/agricultural-data-exchange\/?viewAsMember=true"]},{"@type":"Person","@id":"https:\/\/dataforpublicgood.org.in\/#\/schema\/person\/c521da9a85ddb366900e8ab41c092fb4","name":"CDPG","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/dataforpublicgood.org.in\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/07c2cc51f2149e714f30055318fd3fbb92c6e503091ab59827d9bd261e19342a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/07c2cc51f2149e714f30055318fd3fbb92c6e503091ab59827d9bd261e19342a?s=96&d=mm&r=g","caption":"CDPG"},"url":"https:\/\/dataforpublicgood.org.in\/cdpg\/author\/iudx\/"}]}},"_links":{"self":[{"href":"https:\/\/dataforpublicgood.org.in\/cdpg\/wp-json\/wp\/v2\/posts\/20476","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dataforpublicgood.org.in\/cdpg\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dataforpublicgood.org.in\/cdpg\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dataforpublicgood.org.in\/cdpg\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/dataforpublicgood.org.in\/cdpg\/wp-json\/wp\/v2\/comments?post=20476"}],"version-history":[{"count":8,"href":"https:\/\/dataforpublicgood.org.in\/cdpg\/wp-json\/wp\/v2\/posts\/20476\/revisions"}],"predecessor-version":[{"id":20485,"href":"https:\/\/dataforpublicgood.org.in\/cdpg\/wp-json\/wp\/v2\/posts\/20476\/revisions\/20485"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/dataforpublicgood.org.in\/cdpg\/wp-json\/wp\/v2\/media\/20477"}],"wp:attachment":[{"href":"https:\/\/dataforpublicgood.org.in\/cdpg\/wp-json\/wp\/v2\/media?parent=20476"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dataforpublicgood.org.in\/cdpg\/wp-json\/wp\/v2\/categories?post=20476"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dataforpublicgood.org.in\/cdpg\/wp-json\/wp\/v2\/tags?post=20476"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}