{"id":223068,"date":"2022-01-31T18:32:12","date_gmt":"2022-01-31T17:32:12","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=idea&#038;p=223068"},"modified":"2024-07-31T10:43:02","modified_gmt":"2024-07-31T09:43:02","slug":"fixing-the-parrot-party","status":"publish","type":"idea","link":"https:\/\/blog.jetbrains.com\/pt-br\/idea\/2022\/01\/fixing-the-parrot-party","title":{"rendered":"Profiling Tutorial: Fixing the Parrot Party"},"content":{"rendered":"<p>We often find ourselves in situations where code is not working properly, and we have no idea where to even begin investigating.<\/p>\n<p>Can\u2019t we just stare at the code until the solution eventually comes to us? Sure, but this method probably won\u2019t work without deep knowledge of the project and a lot of mental effort. A smarter approach would be to use the tools you have at hand. They can point you in the right direction.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/10\/banner.png\" alt=\"\" \/><\/p>\n<p>In this post, we\u2019ll look at how we can use some of IntelliJ IDEA\u2019s built-in tools to investigate a runtime problem.<\/p>\n<p><b>Note:<\/b> This post shows how to use features of both the Community and Ultimate versions of IntelliJ IDEA. To use <a href=\"https:\/\/www.jetbrains.com\/pages\/intellij-idea-profiler\/\" target=\"_blank\" rel=\"noopener\">the profiling tools<\/a>, make sure you have IntelliJ IDEA Ultimate.<\/p>\n\n\n<h1 class=\"wp-block-heading\" id=\"the-problem\">The problem<\/h1>\n\n\n<p>Let\u2019s start with cloning the following repository: <a href=\"https:\/\/github.com\/flounder4130\/party-parrot\" target=\"_blank\" rel=\"noopener\">https:\/\/github.com\/flounder4130\/party-parrot<\/a><\/p>\n<p>Launch the application using the Parrot run configuration included with the project. The app seems to work well: you can tweak the animation color and speed. However, it\u2019s not long before things start going wrong.<\/p>\n<p>After working for some time, the animation freezes with no indication of what the cause is. There can also be a <code>OutOfMemoryError<\/code>, whose stack trace doesn\u2019t tell us anything about the origin of the problem.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/parrot-frozen.png\" alt=\"A frozen animation with still responsive UI\" width=\"256\" \/><\/p>\n<p>There is no reliable way of telling how exactly the problem will manifest itself. The interesting thing about the animation freeze is that we can still use the rest of the UI.<\/p>\n<p><b>Important:<\/b> We run this using Amazon Corretto 11. The result may differ on other JVMs or even on Corretto 11 if it uses a custom configuration.<\/p>\n\n\n<h1 class=\"wp-block-heading\" id=\"debugger\">Debugger<\/h1>\n\n\n<p>It seems we have a bug. Let\u2019s try using the debugger! Launch the application in debug mode, wait until the animation freezes, then hit <b>Pause<\/b>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/debugger-1.png\" width=\"344\" alt=\"The debugger shows that all threads are waiting.\"><\/p>\n<p>Unfortunately, this did not tell us much because all the threads involved in the parrot party are in the waiting state. We don\u2019t even know if the threads are waiting for a lock or have just finished their current work. Clearly, we need to try another approach.<\/p>\n\n\n<h1 class=\"wp-block-heading\" id=\"cpu-and-memory-live-charts\">CPU and Memory Live Charts<\/h1>\n\n\n<p>Since we are getting an <code>OutOfMemoryError<\/code>, a good starting point for analysis is <b>CPU and Memory Live Charts<\/b>. They allow us to visualize real-time resources usage for the processes that are running. Let\u2019s open the charts for our parrot app and see if we can spot anything when the animation freezes.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/memory-leak-1.png\" alt=\"Memory usage chart\" width=\"566\"><\/p>\n<p>Indeed, we see that the memory usage is going up continually before reaching a plateau. This is precisely the moment when the animation hangs, and there seems to be no way out of this.<\/p>\n<p>This gives us a clue. Usually, the memory usage curve is saw-shaped: the chart goes up when new objects are allocated and periodically goes down when the memory is reclaimed by the garbage collector. You can see an example of this in the picture below:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/saw-teeth-memory-chart.png\" alt=\"Normal memory usage\" width=\"563\"><\/p>\n<p>If the saw teeth become too frequent, it means that the garbage collector is having a hard time trying to reclaim the memory. A plateau means it can\u2019t free up any.<\/p>\n<p>We can test it by requesting garbage collection from <b>CPU and memory live charts<\/b>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/gc.png\" alt=\"GC button in CPU and Memory Live Charts\" width=\"163\"><\/p>\n<p>Memory usage does not go down after our app reaches the plateau. This supports our hypothesis that there are no objects eligible for garbage collection.<\/p>\n<p>A na\u00efve solution would be to just add more memory.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/rc.png\" alt=\"Specifying VM option to add more memory\" width=\"731\"><\/p>\n<p>However, regardless of the available memory, the parrot runs out of memory anyway. Again, we see the same picture. The only visible effect of extra memory was that we delayed the end of the &#8220;party&#8221;.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/leak-with-more-memory.png\" alt=\"The memory usage chart shows that memory is still insufficient\" width=\"559\"><\/p>\n\n\n<h1 class=\"wp-block-heading\" id=\"allocation-profiling\">Allocation Profiling<\/h1>\n\n\n<p>Since we know our application never gets enough memory, we might want to analyze its memory usage.<\/p>\n<p>Let\u2019s run the application with Async Profiler attached. It\u2019s a great choice for CPU profiling. But that\u2019s not all \u2013 starting with version 2.0, which is integrated into IntelliJ IDEA, we get the ability to profile memory allocations as well.<\/p>\n<p><b>Note:<\/b> If you\u2019re on Windows, use Java Flight Recorder for allocation profiling.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/run-with-profiler.png\" width=\"502\" alt=\"Launching the same run configuration with the profiler attached.\"><\/p>\n<p>While running, the profiler records the application state when objects are placed on the heap. This data is then aggregated in a human-readable form to give us an idea of what the application was doing when allocating these objects.<\/p>\n<p>After running the profiler for some time, let\u2019s open the report and see what\u2019s there.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/open-report.png\" width=\"242\" alt=\"Click the balloon to open the profiling report.\"><\/p>\n<p>There are several views available for the collected data. In this tutorial, we will use the flame graph. It aggregates the collected stacks in a single stack-like structure, adjusting the element width according to the number of collected samples. The widest elements represent the most massively allocated types during the profiling period.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/flame-graph.png\" width=\"1124\" alt=\"Click the balloon to open the profiling report.\"><\/p>\n<p>An important thing to note here is that a lot of allocations don\u2019t necessarily indicate a problem. A memory leak happens only if the allocated objects are not garbage-collected. While allocation profiling doesn\u2019t tell us anything about the garbage collection, it can still give us hints for further investigation.<\/p>\n<p>Let\u2019s see where the two most massive elements, <code>byte[]<\/code> and <code>int[]<\/code>, come from. The top of the stack tells us that these arrays are created during image processing by the code from the <code>java.awt.image<\/code> package. The bottom of the stack tells us that all this happens in a separate thread managed by an executor service. We aren\u2019t looking for bugs in library code, so let\u2019s look at the user code that\u2019s in between. <\/p>\n<p>Going from top to bottom, the first application method we see is <code>recolor()<\/code>, which in turn is called by <code>updateParrot()<\/code>. Judging by the name, this method is exactly what makes our parrot move. Let\u2019s see how this is implemented and why it needs <i>that many<\/i> arrays.<\/p>\n<p><img decoding=\"async\" width=\"531\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/jump-to-source.png\" alt=\"Click Jump to Source to navigate to the corresponding piece of code\"><\/p>\n<p>Clicking at the frame and selecting <b>Jump to Source<\/b> takes us to the source code of the corresponding method:<\/p>\n<p><img decoding=\"async\" width=\"911\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/update-parrot.png\" alt=\"The implementation of the updateParrot() method\"><\/p>\n<p>It seems that <code>updateParrot()<\/code> takes some base image and then recolors it. In order to avoid extra work, the implementation first tries to retrieve the image from some cache. The key for retrieval is a <code>State<\/code> object, whose constructor takes a base image and a hue.<\/p>\n\n\n<h1 class=\"wp-block-heading\" id=\"analyzing-data-flow\">Analyzing Data Flow<\/h1>\n\n\n<p>Using the built-in static analyzer, we can trace the range of input values for the <code>State<\/code> constructor call. Right-click the <code>baseImage<\/code> constructor argument, then from the menu, select <b>Analyze<\/b> | <b>Data Flow to Here<\/b>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/dataflow-to-here-3.png\" alt=\"\" width=\"1212\"><\/p>\n<p>Expand the nodes and pay attention to <code>ImageIO.read(path.toFile())<\/code>. It shows us that the base images come from a set of files. If we double-click this line and look at the <code>PARROTS_PATH<\/code> constant that is nearby, we discover the files location:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/image5.png\" alt=\"\" width=\"238\"><\/p>\n<p>That\u2019s ten base images that correspond to the possible positions of the parrot. Well, what about the <code>hue<\/code> constructor argument?<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/dataflow-to-here-1.png\" alt=\"\" width=\"728\"><\/p>\n<p>If we inspect the code that modifies the <code>hue<\/code> variable, we see that it has a starting value of <code>50<\/code>. Then it is either set with a slider or updated automatically from the <code>updateHue()<\/code> method. Either way, it is always within the range of <code>1<\/code> to <code>100<\/code>.<\/p>\n<p>So, we have 100 variants of hue and 10 base images, which should guarantee that the cache never grows bigger than 1000 elements. Let\u2019s check if that holds true.<\/p>\n\n\n<h1 class=\"wp-block-heading\" id=\"conditional-breakpoints\">Conditional Breakpoints<\/h1>\n\n\n<p>\nNow, this is where the debugger can be useful. We can check the size of the cache with a conditional breakpoint.<\/p>\n<p>Let\u2019s set a breakpoint at the update action and add a condition so that it only suspends the application when the cache size exceeds 1000 elements.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/image1-2-1200x534.png\" alt=\"\" width=\"600\"><\/p>\n<p>Now run the app in debug mode.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/image3-1-1200x147.png\" alt=\"\" width=\"600\"><\/p>\n<p>Indeed, we stop at this breakpoint after running the program for some time. So we can be sure that the problem is in the cache.<\/p>\n\n\n<h1 class=\"wp-block-heading\" id=\"inspecting-code\">Inspecting the Code<\/h1>\n\n\n<p><b>Cmd<\/b> + <b>B<\/b> on <code>cache<\/code> takes us to its declaration site:<\/p>\n<p><code style=\"block\">private static final Map&lt;State, BufferedImage&gt; cache = new HashMap&lt;&gt;();<\/code><\/p>\n<p>If we check the documentation for <code>HashMap<\/code>, we\u2019ll find that its implementation relies on the <code>equals()<\/code> and <code>hashcode()<\/code> methods, and the type that is used as the key has to correctly override them. Let\u2019s check it. <b>Cmd<\/b> + <b>B<\/b> on <code>State<\/code> takes us to the class definition.<\/p>\n<p>Seems like we have found the culprit: the implementation of <code>equals()<\/code> and <code>hashcode()<\/code> isn&#8217;t just incorrect. It&#8217;s completely missing!<\/p>\n\n\n<h1 class=\"wp-block-heading\" id=\"override-methods\">Override Methods<\/h1>\n\n\n<p>Writing implementations for <code>equals()<\/code> and <code>hashcode()<\/code> is a mundane task. Luckily, IntelliJ IDEA can generate them for us. While in the <code>State<\/code> class, start typing <code>equals<\/code>, and the IDE will understand what we want.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/image17.png\" alt=\"\" width=\"544\"><\/p>\n<p>Just accept the suggestion and click <b>Next<\/b> until the methods appear at the caret.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/image16.png\" alt=\"\" width=\"600\" height=\"466\"><\/p>\n<p><b>Hint:<\/b> You can also use this as a quick way to override other members or generate accessor methods. For example, typing <code>get<\/code> will show you the suggestions to generate getter methods.<\/p>\n\n\n<h1 class=\"wp-block-heading\" id=\"checking-the-fix\">Checking the Fix<\/h1>\n\n\n<p>Let\u2019s restart the application and see if things have improved. Again, we can use <b>CPU and Memory Live Charts<\/b> for that:<\/p>\n<p><img decoding=\"async\" width=\"562\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/01\/image12-2.png\" alt=\"\"><\/p>\n<p>That is much better!<\/p>\n\n\n<h1 class=\"wp-block-heading\" id=\"summary\">Summary<\/h1>\n\n\n<p>In this post, we looked at how we can start with the general symptoms of a problem and then, using our reasoning and the variety of tools IntelliJ IDEA offers us, narrow the scope of the search step-by-step until we find the exact line of code that&#8217;s causing the problem. More importantly, we made sure that the parrot party will go on no matter what!<\/p>\n<p>If you are interested in more detailed instructions on how to use the features from this post, feel free to check out the documentation:<\/p>\n<ul>\n<li><a href=\"https:\/\/www.jetbrains.com\/help\/idea\/cpu-and-memory-live-charts.html\" target=\"_blank\" rel=\"noopener\">CPU and memory live charts<\/a><\/li>\n<li><a href=\"https:\/\/www.jetbrains.com\/help\/idea\/cpu-profiler.html\" target=\"_blank\" rel=\"noopener\">Profiler<\/a><\/li>\n<li><a href=\"https:\/\/www.jetbrains.com\/help\/idea\/analyzing-data-flow.html\" target=\"_blank\" rel=\"noopener\">Dataflow analysis<\/a><\/li>\n<li><a href=\"https:\/\/www.jetbrains.com\/help\/idea\/using-breakpoints.html#breakpoint-properties\" target=\"_blank\" rel=\"noopener\">Advanced breakpoint properties<\/a><\/li>\n<li><a href=\"https:\/\/www.jetbrains.com\/help\/idea\/generating-code.html\" target=\"_blank\" rel=\"noopener\">Generate code<\/a><\/li>\n<\/ul>\n<p>As always, we will be happy to hear your feedback. Please use the comments section to tell us if there are scenarios or features that you would like us to cover in future posts in this series.<\/p>\n<p>For feature requests feel free to reach out to us in <a href=\"https:\/\/youtrack.jetbrains.com\/\" target=\"_blank\" rel=\"noopener\">YouTrack<\/a>.<\/p>\n<p>Happy developing!<\/p>","protected":false},"author":1206,"featured_media":73920,"comment_status":"closed","ping_status":"closed","template":"","categories":[4759,601],"tags":[3386,264,263,3381,1717,667,1123,489],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/idea\/223068"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/idea"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/types\/idea"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/users\/1206"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/comments?post=223068"}],"version-history":[{"count":10,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/idea\/223068\/revisions"}],"predecessor-version":[{"id":436974,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/idea\/223068\/revisions\/436974"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/media\/73920"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/media?parent=223068"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/categories?post=223068"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/tags?post=223068"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/cross-post-tag?post=223068"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}