<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vlad</title>
    <description>The latest articles on DEV Community by Vlad (@vbochenin).</description>
    <link>https://dev.to/vbochenin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F560467%2F6b4cadc7-1569-4feb-8e6c-fb396871ee0f.png</url>
      <title>DEV Community: Vlad</title>
      <link>https://dev.to/vbochenin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vbochenin"/>
    <language>en</language>
    <item>
      <title>Java 17 migration: bias locks performance regression</title>
      <dc:creator>Vlad</dc:creator>
      <pubDate>Thu, 16 Feb 2023 09:53:51 +0000</pubDate>
      <link>https://dev.to/vbochenin/java-17-migration-bias-locks-regression-2c5m</link>
      <guid>https://dev.to/vbochenin/java-17-migration-bias-locks-regression-2c5m</guid>
      <description>&lt;h2&gt;
  
  
  What issue do I need to solve
&lt;/h2&gt;

&lt;p&gt;We are about to switch from java 11 to java 17 finally, but a few performance tests failed and failed dramatically with around 50% of regression.&lt;br&gt;
It was 6 seconds in java 11 vs. 9 seconds in java 17 in absolute number in some tests.&lt;br&gt;
And this happens just by switching runtime.&lt;/p&gt;

&lt;p&gt;So I've got to investigate why the tests failed and fix the issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I did to get some data
&lt;/h2&gt;

&lt;p&gt;First, I've decided to run the test with a profiler. I'm using &lt;a href="https://www.jetbrains.com/help/idea/profiler-intro.html" rel="noopener noreferrer"&gt;Intellij IDEA Profiler&lt;/a&gt; with the default settings for the smoke test.&lt;br&gt;
Once I ran it, I found strange plateaus on a flame graph that was missing in the java 11 profiling report.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7uno14a410j3x9wtuc9b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7uno14a410j3x9wtuc9b.jpg" alt="Plateaus"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I've decided to look closer into the method.&lt;/p&gt;

&lt;h2&gt;
  
  
  Digging deeper
&lt;/h2&gt;

&lt;p&gt;The method looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public class LazyInputStream extends InputStream{
    private InputStream is;
    ...

    protected synchronized InputStream getInstance() {
       if (is == null) {
           is = factory.create();
       }
       return is;
    }
    ...
    public int read() throws IOException {  
        return getInstance().read();  
    }
} 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The method doesn't do any long operation. It only creates an instance of an input stream when required and uses it later.&lt;br&gt;
But we are calling the method for each &lt;code&gt;read&lt;/code&gt; in the external input stream. In other words, each time when we are going to read the next portion of data.&lt;br&gt;
And the method is &lt;code&gt;synchronized&lt;/code&gt; to guarantee a single instance of input stream creation in a multithreading environment.&lt;br&gt;
And there is no this kind of plateau in java 11 profiling. I've rerun it and checked several times on different machines. &lt;/p&gt;

&lt;p&gt;After some googling, I've found this &lt;a href="https://openjdk.org/jeps/374" rel="noopener noreferrer"&gt;JEP&lt;/a&gt;, which says that bias locks are disabled from java 15.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a bias lock
&lt;/h2&gt;

&lt;p&gt;Bias lock is optimization for &lt;code&gt;synchronized&lt;/code&gt; in java.&lt;br&gt;
It works when the method is not so concurrent, and only a single thread usually acquires a lock.&lt;br&gt;
JVM raises a flag in the monitor object that some thread acquires the lock, so reacquiring and releasing the lock by the same thread is lightweight. But the lock must be revoked when another thread tries to acquire the bias lock. And the revocation is a costly operation.&lt;/p&gt;

&lt;p&gt;So community decided to deprecate bias locks and remove them later.&lt;/p&gt;

&lt;p&gt;Because if you really have a multithreaded application, you would like to avoid costly bias lock revocation, and if your application is not so multithreaded, you don't need the lock at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  How did I fix it
&lt;/h2&gt;

&lt;p&gt;First, let's run the tests with &lt;code&gt;-XX:+UseBiasedLocking&lt;/code&gt;JVM option to turn on the bias locks.&lt;br&gt;
The regression issue disappears, but the warning appears:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

OpenJDK 64-Bit Server VM warning: Option UseBiasedLocking was deprecated in version 15.0 and will likely be removed in a future release.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Ok, so let's implement our lazy initialization more smartly to avoid acquiring the lock every time and use old fashion but still working &lt;a href="https://en.wikipedia.org/wiki/Double-checked_locking" rel="noopener noreferrer"&gt;double-checked locking&lt;/a&gt;.&lt;br&gt;
I've found it implemented by &lt;code&gt;Suppliers.memoize&lt;/code&gt; in &lt;a href="https://github.com/google/guava" rel="noopener noreferrer"&gt;guava&lt;/a&gt; library. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public class LazyInputStream extends InputStream {  

    private final Supplier&amp;lt;InputStream&amp;gt; initializer;

    public LazyInputStream(InputStreamFactory factory) {  
        this.initializer = Suppliers.memoize(() -&amp;gt; {  
            try {  
                return factory.create();  
            } catch (IOException e) {  
                throw new IllegalStateException("Failed to create input stream", e);  
            }  
        });  
    }

    ...

    protected synchronized InputStream getInstance() {
       return initialized.get();
    }
    ...
    public int read() throws IOException {  
        return getInstance().read();  
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The memoizing supplier uses a 2-fields variant of double-checked locking, but it is because the supplier may return &lt;code&gt;null&lt;/code&gt; as a valid value.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public T get() {  
  if (!initialized) {          // boolean flag  
    synchronized (this) {  
      if (!initialized) {      // double check to avoid races
        T t = delegate.get();  // calling delegate to get a value
        value = t;             // and remember it
        initialized = true;    // rise the boolean flag
        return t;  
      }  
    }  
  }  
  return value;  
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once I rerun the tests, I found that regression did not disappear completely, so it looks like we have similar locks, but they are not too hot to be visible on flame graphs. &lt;br&gt;
Finally, we decided to go with turned-on bias locks and continue working on our code to improve it continuously.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You may get some performance boost only by turning on bias locks if you have something similar to us and using java 15+.
But it is always better to rewrite problem pieces.&lt;/li&gt;
&lt;li&gt;Test your code performance in every java version. It will help you find regression earlier and only spend a little time looking into strange plateaus.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;If you find the post helpful, please support me and:&lt;/em&gt;&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/vbocheninQ" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwve1hh3j8ewl5aowo7r.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>performance</category>
      <category>multithreading</category>
      <category>devjournal</category>
    </item>
    <item>
      <title>3 ways to know used memory</title>
      <dc:creator>Vlad</dc:creator>
      <pubDate>Mon, 13 Feb 2023 11:44:03 +0000</pubDate>
      <link>https://dev.to/vbochenin/3-way-to-know-used-memory-1b0o</link>
      <guid>https://dev.to/vbochenin/3-way-to-know-used-memory-1b0o</guid>
      <description>&lt;h1&gt;
  
  
  What task I would like to solve
&lt;/h1&gt;

&lt;p&gt;Recently I tried to know how much memory an application wastes during some operation.&lt;br&gt;
The operation I want to measure is data snapshot creation.&lt;br&gt;
The application stores a sequence of operations with objects and periodically creates object snapshots. &lt;br&gt;
So if we want to know the object's state at any point in time, we won't replay all operations from the beginning to this point but get the object's state from the nearest snapshot and play only a limited amount of operation from this snapshot to our point.&lt;/p&gt;

&lt;p&gt;Obviously, we would like to store as many recent snapshots as possible in heap memory, but in the real world, we have limited memory. We should limit the amount of memory for snapshots, and we are doing it traditionally by setting the threshold via application properties.&lt;/p&gt;

&lt;p&gt;Next step, we want to provide some meaningful defaults for this property. &lt;br&gt;
The issue here is that we cannot predict the value because different customers have different: environments, data, sizes of snapshots, and so on. &lt;br&gt;
This means we need to have more data to make a decision.&lt;/p&gt;

&lt;p&gt;Finally, we decided to log how much heap was occupied before and after snapshot creation and end with something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Snapshot buildSnapshot() {
    long usedMemory = calculateUsedMemory();
    try {
        return doBuildSnapshot();
    } finally {
        log.debug("Used memory: " + (calculateUsedMemory() - usedMemory));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  &lt;code&gt;java.lang.Runtime&lt;/code&gt; way
&lt;/h1&gt;

&lt;p&gt;If you google "java used memory in runtime" you will see this method: &lt;code&gt;Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()&lt;/code&gt; and different variations over it.&lt;/p&gt;

&lt;p&gt;Let's look closer at what this method returns to us.&lt;/p&gt;

&lt;p&gt;For total memory:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Returns the total amount of memory in the Java virtual machine. The value returned by this method may vary over time, depending on the host environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For free memory:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Returns the amount of free memory in the Java Virtual Machine. Calling the gc method may result in increasing the value returned by freeMemory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, total memory may contain references no longer needed objects. The object will be freed after garbage collection, but we don't have any control over GC and don't know when it is executed.&lt;/p&gt;

&lt;p&gt;So we need to find some better solution.&lt;/p&gt;

&lt;h1&gt;
  
  
  JMX way
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.oracle.com/java/technologies/javase/docs-jmx-jsp.html" rel="noopener noreferrer"&gt;JMX&lt;/a&gt; is stated from &lt;strong&gt;Java Management Extensions&lt;/strong&gt;.  We may use these built-in extensions to get different information about Runtime internals. &lt;/p&gt;

&lt;p&gt;In &lt;code&gt;java.lang.management.ManagementFactory&lt;/code&gt;, we may find a lot of factory methods for any task we like, but we are most interested in &lt;code&gt;MemoryMXBean getMemoryMXBean()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The method returns a &lt;a href="https://docs.oracle.com/javase/tutorial/jmx/mbeans/index.html" rel="noopener noreferrer"&gt;manageble bean&lt;/a&gt;  we can use to request heap memory usage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private static final MemoryMXBean MEMORY_MX_BEAN = ManagementFactory.getMemoryMXBean();

private long calculateUsedMemory() {
    return  MEMORY_MX_BEAN.getHeapMemoryUsage().getUsed();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, good, but the methods above show only used memory at some specific time for the whole application, and we still need to find out how much memory the application waste on storing the snapshot.&lt;/p&gt;

&lt;p&gt;Let's look at the task from another point, how much memory we used to complete the operation.&lt;/p&gt;

&lt;p&gt;In other words, how effective is the code from a memory usage perspective?&lt;br&gt;
Knowing this and GC information, we could predict the default value.&lt;/p&gt;
&lt;h1&gt;
  
  
  Secret JMX way
&lt;/h1&gt;

&lt;p&gt;JMX has two &lt;code&gt;ThreadMXBean&lt;/code&gt; interfaces, &lt;code&gt;java.lang.management.ThreadMXBean&lt;/code&gt; and &lt;code&gt;com.sun.management.ThreadMXBean&lt;/code&gt;.&lt;br&gt;
Both could be used to get information about threads.&lt;/p&gt;

&lt;p&gt;The first one is public and returned by &lt;code&gt;ManagementFactory&lt;/code&gt;, but it does not have any memory-related methods.&lt;/p&gt;

&lt;p&gt;The second one declares 2 excellent methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;long getThreadAllocatedBytes(long id)&lt;/code&gt; - Returns an approximation of the total amount of memory, in bytes, allocated in heap memory for each thread whose ID is in the input array ids.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;long getCurrentThreadAllocatedBytes()&lt;/code&gt; - Returns an approximation of the total amount of memory, in bytes, allocated in heap memory for the current thread.
More than &lt;code&gt;com.sun.management.ThreadMXBean&lt;/code&gt; extends &lt;code&gt;java.lang.management.ThreadMXBean&lt;/code&gt;, so we can use the interface instead of the first one.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But you will not find any methods returning these interfaces.&lt;/p&gt;

&lt;p&gt;If will look at &lt;code&gt;ThreadMXBean&lt;/code&gt; hierarchy, I may see that, in my JDK, both interfaces are implemented by a single class. &lt;br&gt;
&lt;a href="/assets/img/posts/2023-02-08-3-ways-to-know-used-memory/threadmxbean-hierarchy.png" class="article-body-image-wrapper"&gt;&lt;img src="/assets/img/posts/2023-02-08-3-ways-to-know-used-memory/threadmxbean-hierarchy.png" alt="ThreadMxBean hierarchy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've used &lt;a href="https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/downloads-list.html" rel="noopener noreferrer"&gt;Amazon Correto 11&lt;/a&gt;, but the same picture in other JDKs (checked on &lt;a href="https://adoptium.net/temurin/releases/?version=11" rel="noopener noreferrer"&gt;Eclipse Temurin&lt;/a&gt; and &lt;a href="https://www.azul.com/downloads/?version=java-11-lts&amp;amp;package=jdk" rel="noopener noreferrer"&gt;Azul&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;Now my code may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private final LongSupplier allocatedByThread = initAllocatedMemoryProvider(); 

private static LongSupplier initAllocatedMemoryProvider() {  

    ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();  
    if (threadMXBean instanceof com.sun.management.ThreadMXBean) {  
        com.sun.management.ThreadMXBean casted = (com.sun.management.ThreadMXBean) threadMXBean;  
        return casted::getCurrentThreadAllocatedBytes;  
    }  
    return () -&amp;gt; 0;  
}

private long calculateUsedMemory() {
    return initAllocatedMemoryProvider().getAsLong();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The issue is only that &lt;code&gt;com.sun.management.ThreadMXBean&lt;/code&gt; in &lt;code&gt;jdk.management&lt;/code&gt; module, so if a customer runs our application with JRE we will not have the interface in the classpath. The application will crush with &lt;code&gt;NoClassDefFoundError&lt;/code&gt;, but we may fix it with Reflection API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private static LongSupplier initAllocatedMemoryProvider() {  
    try {  
        Class&amp;lt;?&amp;gt; internalIntf = Class.forName("com.sun.management.ThreadMXBean");  
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();  
        if (!internalIntf.isAssignableFrom(bean.getClass())) {  
            // Attempts to get the interface from PlatformMXBean
            Class&amp;lt;?&amp;gt; pmo = Class.forName("java.lang.management.PlatformManagedObject");  
            Method m = ManagementFactory.class.getMethod("getPlatformMXBean", Class.class, pmo);  
            bean = (ThreadMXBean) m.invoke(null, internalIntf);  
            if (bean == null) {  
                throw new UnsupportedOperationException("No way to access private ThreadMXBean");  
            }  
        }  

        ThreadMXBean allocMxBean = bean;  
        Method allocMxBeanGetter = internalIntf.getMethod("getCurrentThreadAllocatedBytes");  

        return () -&amp;gt; (long)allocMxBeanGetter.invoke(allocMxBean);
    } catch (Exception e) {  
        return () -&amp;gt; 0;  
    } 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;You may get access to different runtime information with JMX, but you may find a treasure if you dig slightly deeper.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You may find the code on &lt;a href="https://github.com/vbochenin/code.vbochenin.github.io/tree/main/memory-usage" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you find the post helpful, please support me and:&lt;/em&gt;&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/vbocheninQ" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwve1hh3j8ewl5aowo7r.png" alt="Buy Me A Coffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>Running JMH benchmark from Eclipse</title>
      <dc:creator>Vlad</dc:creator>
      <pubDate>Fri, 18 Nov 2022 22:07:18 +0000</pubDate>
      <link>https://dev.to/vbochenin/just-another-way-to-run-jmh-benchmark-with-eclipse-4565</link>
      <guid>https://dev.to/vbochenin/just-another-way-to-run-jmh-benchmark-with-eclipse-4565</guid>
      <description>&lt;h2&gt;
  
  
  What issue I'm trying to solve
&lt;/h2&gt;

&lt;p&gt;A few months ago, we started to use &lt;a href="https://github.com/openjdk/jmh"&gt;JMH&lt;/a&gt; in our project to test and find performance issues. &lt;br&gt;
The tool provides multiple modes and profilers, and we found this useful for our purposes. &lt;/p&gt;

&lt;p&gt;Intellij IDEA, which I'm using, has a useful &lt;a href="https://github.com/artyushov/idea-jmh-plugin"&gt;Intellij IDEA plugin for Java Microbenchmark Harness&lt;/a&gt;. The plugin's functionality is similar to JUnit Plugin. It simplifies benchmarks development and debugging by allowing running some benchmarks together or separately.&lt;/p&gt;

&lt;p&gt;But... half of our team uses Eclipse as a major IDE and the IDE does not have any plugins or support for the tool.&lt;br&gt;
Even if we may run it with the &lt;code&gt;main&lt;/code&gt; method, it is inconvenient to change the &lt;code&gt;include&lt;/code&gt; pattern and do not forget to revert the changes before committing them into git.&lt;/p&gt;

&lt;p&gt;So, after small brainstorming, we decided to write a custom JUnit Runner with functionality to run benchmark.&lt;/p&gt;
&lt;h2&gt;
  
  
  JUnit 4 runners
&lt;/h2&gt;

&lt;p&gt;JUnit 4 has API to run any class as a test suite. &lt;br&gt;
All you need is only to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;write class extending &lt;code&gt;org.junit.runner.Runner&lt;/code&gt; class
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BenchmarkRunner&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Runner&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;implement constructor and few methods
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BenchmarkRunner&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Runner&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;BenchmarkRunner&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;benchmarkClass&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;

   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt; &lt;span class="nf"&gt;getDescription&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;//...&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;  

   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RunNotifier&lt;/span&gt; &lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;//...&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;add the runner to your test class
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RunWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BenchmarkRunner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomCollectionBenchmark&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Implementing Benchmark JUnit runner
&lt;/h2&gt;

&lt;p&gt;First, we need to provide information about tests to the JUnit engine.&lt;/p&gt;

&lt;p&gt;JUnit runners have the &lt;code&gt;getDescription()&lt;/code&gt; method for it. But how to get information about test classes and test methods? &lt;/p&gt;

&lt;p&gt;From JavaDoc: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When creating a custom runner, in addition to implementing the abstract methods here you must also provide a constructor that takes as an argument the Class containing the tests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, we may get the class as a constructor argument and get all information with &lt;a href="https://www.oracle.com/technical-resources/articles/java/javareflection.html#:~:text=Reflection%20is%20a%20feature%20in,its%20members%20and%20display%20them."&gt;reflection&lt;/a&gt;'s help.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BenchmarkRunner&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Runner&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;benchmarkClass&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// (1)  &lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;benchmarkMethods&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// (2) &lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;BenchmarkRunner&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;benchmarkClass&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;benchmarkClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;benchmarkClass&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;benchmarkMethods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;benchmarkClass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDeclaredMethods&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;  
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isAnnotationPresent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Benchmark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;  
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I have all the required information to provide a test suite description to the JUnit engine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Actual benchmark class&lt;/li&gt;
&lt;li&gt;All methods marked with &lt;code&gt;@Benchmark&lt;/code&gt; in this class
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BenchmarkRunner&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Runner&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
    &lt;span class="c1"&gt;//... &lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;  
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt; &lt;span class="nf"&gt;getDescription&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
        &lt;span class="nc"&gt;Description&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createSuiteDescription&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;benchmarkClass&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  
        &lt;span class="n"&gt;benchmarkMethods&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;  
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createTestDescription&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;benchmarkClass&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;  
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;result:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;addChild&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;  
    &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we run the test we may see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3orH15K3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q07stcrduvv16bbnv9yc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3orH15K3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q07stcrduvv16bbnv9yc.png" alt="Eclipse JUnit run" width="465" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's run our benchmarks. &lt;br&gt;
We need to implement the method &lt;code&gt;org.junit.runner.Runner.run(RunNotifier)&lt;/code&gt; where &lt;code&gt;RunNotifier&lt;/code&gt; is responsible to notify the engine about test runs.&lt;/p&gt;

&lt;p&gt;The idea is we run sequentially one-by-one benchmark methods, each in separate &lt;code&gt;org.openjdk.jmh.runner.Runner&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BenchmarkRunner&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Runner&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RunNotifier&lt;/span&gt; &lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Method&lt;/span&gt; &lt;span class="n"&gt;benchmarkMethod&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;benchmarkMethods&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Description&lt;/span&gt; &lt;span class="n"&gt;testDescription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getBenchmarkMethodDescription&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;benchmarkMethod&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fireTestStarted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testDescription&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="nc"&gt;Options&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OptionsBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;".*"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;benchmarkClass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"."&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;benchmarkMethod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;".*"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jvmArgsAppend&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-Djmh.separateClasspathJAR=true"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openjdk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jmh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;runner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Runner&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

                &lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fireTestFinished&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testDescription&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fireTestFailure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Failure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testDescription&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt; &lt;span class="nf"&gt;getBenchmarkMethodDescription&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Method&lt;/span&gt; &lt;span class="n"&gt;benchmarkMethod&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createTestDescription&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;benchmarkClass&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;benchmarkMethod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Options mean follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;include&lt;/code&gt; - benchmark we would like to include in the run.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jvmArgsAppend("-Djmh.separateClasspathJAR=true")&lt;/code&gt; - specific option, telling JMH to build &lt;code&gt;classpath.jar&lt;/code&gt; to avoid the  &lt;a href="https://stackoverflow.com/questions/65340027/jmh-1-27-fails-to-work-with-long-arguments-list-in-jvm"&gt;The filename or extension is too long&lt;/a&gt; error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you may see, we notify &lt;code&gt;RunNotifier&lt;/code&gt; when we start and finish our test, successful or not.&lt;br&gt;
Looks good, but we are running all tests even when choosing to run only a single one.&lt;/p&gt;
&lt;h2&gt;
  
  
  Filtering
&lt;/h2&gt;

&lt;p&gt;Our runner should implement the &lt;code&gt;org.junit.runner.manipulation.Filterable&lt;/code&gt; interface to allow to JUnit engine to tell our code what tests should be run.&lt;br&gt;
The interface has only a single method &lt;code&gt;void filter(Filter)&lt;/code&gt; and &lt;code&gt;org.junit.runner.manipulation.Filter&lt;/code&gt; argument has the &lt;code&gt;shouldRun(Description)&lt;/code&gt; method we may use to know if the test requested to run.&lt;br&gt;
The method doesn't return anything, so looks like we need to store filtered results and use them later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BenchmarkRunner&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Runner&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Filterable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;//...&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;readyToRunMethods&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;= add new field to store filter result  &lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;  
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt; &lt;span class="nf"&gt;getDescription&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
        &lt;span class="nc"&gt;Description&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createSuiteDescription&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;benchmarkClass&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  
        &lt;span class="n"&gt;readyToRunMethods&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// &amp;lt;= use the field here&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;getBenchmarkMethodDescription&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;result:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;addChild&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;  

    &lt;span class="c1"&gt;//...&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;  
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RunNotifier&lt;/span&gt; &lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Method&lt;/span&gt; &lt;span class="n"&gt;benchmarkMethod&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;readyToRunMethods&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// &amp;lt;= and here&lt;/span&gt;
            &lt;span class="c1"&gt;//...  &lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;  

    &lt;span class="nd"&gt;@Override&lt;/span&gt;  
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Filter&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;NoTestsRemainException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
        &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filteredMethods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;  

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Method&lt;/span&gt; &lt;span class="n"&gt;benchmarkMethod&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;benchmarkMethods&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shouldRun&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getBenchmarkMethodDescription&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;benchmarkMethod&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
                &lt;span class="n"&gt;filteredMethods&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;benchmarkMethod&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  
            &lt;span class="o"&gt;}&lt;/span&gt;  
        &lt;span class="o"&gt;}&lt;/span&gt;  

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filteredMethods&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NoTestsRemainException&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  
        &lt;span class="o"&gt;}&lt;/span&gt;  

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readyToRunMethods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filteredMethods&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;  
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it runs only methods we ask to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Finally, we've got unified way to run benchmarks from any IDE during development and debugging. This is really simplify our daily life.&lt;br&gt;
We are still running the benchmarks  with &lt;code&gt;main&lt;/code&gt; method to reduce environment noise and get reliable results for analysis.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You may find the code in &lt;a href="https://github.com/vbochenin/code.vbochenin.github.io/tree/main/benchmark-runner"&gt;GitHub&lt;/a&gt;.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you find the post helpful, please support me and:&lt;/em&gt;&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/vbocheninQ"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rJvAnNew--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buymeacoffee.com/buttons/default-black.png" alt="Buy Me A Coffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>jmh</category>
      <category>junit</category>
      <category>devjournal</category>
    </item>
    <item>
      <title>How to change already written code: Aspects</title>
      <dc:creator>Vlad</dc:creator>
      <pubDate>Thu, 20 Oct 2022 11:17:44 +0000</pubDate>
      <link>https://dev.to/vbochenin/how-to-change-already-written-code-aspects-1gnn</link>
      <guid>https://dev.to/vbochenin/how-to-change-already-written-code-aspects-1gnn</guid>
      <description>&lt;h2&gt;
  
  
  What an issue I'm trying to solve
&lt;/h2&gt;

&lt;p&gt;Now I'm trying to solve some generic issue. We have a lot of idempotent methods, methods without side effects and returning the same result for the same arguments.&lt;br&gt;
Additionally, we have some methods of reading data outside, but the data are rarely changed.&lt;/p&gt;

&lt;p&gt;We may use, and we are using, caching to improve response time in this case. &lt;br&gt;
The issue is that all these methods are different, as well as the ways we are using caches (from local value holders and hash maps to EhCache).&lt;br&gt;
So we would like to unify a caching approach to be able to configure different caches, their eviction, and capacity policies and ways how we store them.&lt;/p&gt;

&lt;p&gt;Also, I wouldn't like to pollute the business code with extra supporting code.&lt;/p&gt;

&lt;p&gt;Please compare without cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Value&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;readValue&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and with cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Value&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;readValue&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Changes I've made before
&lt;/h2&gt;

&lt;p&gt;First, I've unified caching interface and replaced it in already-known places:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="no"&gt;V&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CacheManager&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now code with caching looks even uglier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Value&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CacheManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"region"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; 
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;readValue&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Luckily, mankind invents AOP to solve this kind of task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aspect oriented programming overview
&lt;/h2&gt;

&lt;p&gt;AOP allows you to add actions (called &lt;code&gt;Advice&lt;/code&gt;) to some points in an application (called &lt;code&gt;Join points&lt;/code&gt;) specified by expressions (called &lt;code&gt;Pointcut&lt;/code&gt;).&lt;br&gt;
All this stuff together is called &lt;code&gt;Aspect&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So in a few steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have a method you would like to improve. This is your &lt;code&gt;Join point&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.apects&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  

        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;calculateTwoPlusTwo&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
        &lt;span class="o"&gt;}&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Write what you would like to do. It is your &lt;code&gt;Advice&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleAspect&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  

        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;returnFive&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
        &lt;span class="o"&gt;}&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt; Specify where you would like to apply it. It is you &lt;code&gt;Pointcut&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="nd"&gt;@Aspect&lt;/span&gt;  
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleAspect&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  

        &lt;span class="nd"&gt;@Pointcut&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"execution(public int com.example.apects.Example.calculateTwoPlusTwo())"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;returnFivePointcut&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
        &lt;span class="o"&gt;}&lt;/span&gt;  
        &lt;span class="o"&gt;...&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt; Add bind all together. Now you have &lt;code&gt;Aspect&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="nd"&gt;@Aspect&lt;/span&gt;  
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleAspect&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  

        &lt;span class="nd"&gt;@Pointcut&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"execution(public int com.example.apects.Example.calculateTwoPlusTwo())"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;returnFivePointcut&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
        &lt;span class="o"&gt;}&lt;/span&gt;  

        &lt;span class="nd"&gt;@Around&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"returnFivePointcut()"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="nf"&gt;returnFive&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
        &lt;span class="o"&gt;}&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Applying aspects for the caches problem
&lt;/h2&gt;

&lt;p&gt;As you may see above, I need to know and specify all methods I would like to wrap with caching.&lt;br&gt;
Bad news, there is no pattern in method names I could use to reduce the number of pointcuts.&lt;br&gt;
The good news, Java has annotations and Aspects that may work with them.&lt;/p&gt;

&lt;p&gt;So I can do something like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create custom annotation
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Target&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt;&lt;span class="nc"&gt;ElementType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;METHOD&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;  
&lt;span class="nd"&gt;@Retention&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RetentionPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RUNTIME&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nd"&gt;@interface&lt;/span&gt; &lt;span class="nc"&gt;Cacheable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="s"&gt;"common"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Write Aspect
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Aspect&lt;/span&gt;  
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CachingAspect&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  

    &lt;span class="nd"&gt;@Pointcut&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@annotation(cacheable)"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;cachingAnnotation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Cacheable&lt;/span&gt; &lt;span class="n"&gt;cacheable&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;  

    &lt;span class="nd"&gt;@Around&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cachingAnnotation(cacheable)"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="nf"&gt;checkCache&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ProceedingJoinPoint&lt;/span&gt; &lt;span class="n"&gt;pjp&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Cacheable&lt;/span&gt; &lt;span class="n"&gt;cacheable&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CacheManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cachable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;  
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
        &lt;span class="o"&gt;}&lt;/span&gt;  
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pjp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;proceed&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  
        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;  
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Put annotation to target method:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Cacheble&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"region"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Value&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;readValue&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Looks good. I have caching separated from business code but:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I need to solve an issue with the cache key

&lt;ul&gt;
&lt;li&gt;Somehow the logic should depend on the method name and argument&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I don't have any clue if the aspect was really applied to a method or not (just IDE highlights)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For first issue, I would use a full class and target method names and args as array. &lt;br&gt;
I would need to override &lt;code&gt;equals\hashCode&lt;/code&gt; for arguments, and some arguments can be excluded. But I will care about it later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Aspect&lt;/span&gt;  
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CachingAspect&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  

    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="nd"&gt;@Around&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cachingAnnotation(cacheable)"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="nf"&gt;checkCache&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ProceedingJoinPoint&lt;/span&gt; &lt;span class="n"&gt;pjp&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Cacheable&lt;/span&gt; &lt;span class="n"&gt;cacheable&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CacheManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;  
        &lt;span class="nc"&gt;Signature&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pjp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSignature&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!(&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;MethodSignature&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pjp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;proceed&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;  

        &lt;span class="nc"&gt;MethodSignature&lt;/span&gt; &lt;span class="n"&gt;methodSignature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MethodSignature&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
        &lt;span class="nc"&gt;Method&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;methodSignature&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMethod&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  
        &lt;span class="nc"&gt;CacheKey&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CacheKey&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;  
                &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDeclaringClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getCanonicalName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;  
                &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;  
                &lt;span class="n"&gt;pjp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getArgs&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;  
        &lt;span class="o"&gt;);&lt;/span&gt;  

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
        &lt;span class="o"&gt;}&lt;/span&gt;  
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pjp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;proceed&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  

        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;  

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CacheKey&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  

        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CacheKey&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;methodName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
        &lt;span class="o"&gt;}&lt;/span&gt;  

        &lt;span class="nd"&gt;@Override&lt;/span&gt;  
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
            &lt;span class="o"&gt;...&lt;/span&gt;    
        &lt;span class="o"&gt;}&lt;/span&gt;  

        &lt;span class="nd"&gt;@Override&lt;/span&gt;  
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
            &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;About the second issue, I will use the AspectJ maven plugin for compile time weaving.&lt;br&gt;
The plugin has a &lt;code&gt;showWeaveInfo&lt;/code&gt; option to log all information about what and how is weaved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;
&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.codehaus.mojo&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;aspectj-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.14.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;complianceLevel&amp;gt;&lt;/span&gt;11&lt;span class="nt"&gt;&amp;lt;/complianceLevel&amp;gt;&lt;/span&gt;  
                &lt;span class="nt"&gt;&amp;lt;source&amp;gt;&lt;/span&gt;11&lt;span class="nt"&gt;&amp;lt;/source&amp;gt;&lt;/span&gt;  
                &lt;span class="nt"&gt;&amp;lt;target&amp;gt;&lt;/span&gt;11&lt;span class="nt"&gt;&amp;lt;/target&amp;gt;&lt;/span&gt;  
                &lt;span class="nt"&gt;&amp;lt;showWeaveInfo&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/showWeaveInfo&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;compile&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;  
                    &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once build is done, I may check logs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INFO] Join point 'method-call(com.example.apects.Example$Value com.example.apects.Example.getValue())' in Type 'com.example.apects.Example' (Example.java:7) advised by around advice from 'com.example.apects.CachingAspect' (CachingAspect.java:22)
[INFO] Join point 'method-execution(com.example.apects.Example$Value com.example.apects.Example.getValue())' in Type 'com.example.apects.Example' (Example.java:12) advised by around advice from 'com.example.apects.CachingAspect' (CachingAspect.java:22)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, here I may see that my join point was advised twice, so my code will also be called twice.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the first time in method invocation point (&lt;code&gt;method-call&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;the second time in method (&lt;code&gt;method-execution&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I would like to modify the pointcut to be used only in execution because I would like to cache only methods in my project.&lt;br&gt;
If I cache some methods in a third-party library, I need to use a &lt;code&gt;method-call&lt;/code&gt; advice to put cache-related code around method invocation.&lt;/p&gt;

&lt;p&gt;So I will add &lt;code&gt;execution(* *.*(..))&lt;/code&gt; into advice. You may read it like this: execution of any method in any class with any arguments and any modifier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Around&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"execution(* *.*(..)) &amp;amp;&amp;amp; cachingAnnotation(cacheable)"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"pjp,cacheable"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="nf"&gt;checkCache&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ProceedingJoinPoint&lt;/span&gt; &lt;span class="n"&gt;pjp&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Cacheable&lt;/span&gt; &lt;span class="n"&gt;cacheable&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rebuild and check logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INFO] Join point 'method-execution(com.example.apects.Example$Value com.example.apects.Example.getValue())' in Type 'com.example.apects.Example' (Example.java:12) advised by around advice from 'com.example.apects.CachingAspect' (CachingAspect.java:22)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;If you find the post helpful, please support me and:&lt;/em&gt;&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/vbocheninQ"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rJvAnNew--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buymeacoffee.com/buttons/default-black.png" alt="Buy Me A Coffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>devjournal</category>
      <category>cache</category>
      <category>aspects</category>
    </item>
    <item>
      <title>How to change author in pushed commit</title>
      <dc:creator>Vlad</dc:creator>
      <pubDate>Mon, 10 Oct 2022 21:36:42 +0000</pubDate>
      <link>https://dev.to/vbochenin/how-to-change-author-in-pushed-commit-5glf</link>
      <guid>https://dev.to/vbochenin/how-to-change-author-in-pushed-commit-5glf</guid>
      <description>&lt;h2&gt;
  
  
  What issue I've tried to solve
&lt;/h2&gt;

&lt;p&gt;Some time ago, I set up a new VM to work on a new project, and after a few days of intensive work and multiple pushed commits, I figured out a typo in my email. Luckily, I've made commits to the dedicated feature branch, so I may fix it without disturbing the team&lt;/p&gt;

&lt;h2&gt;
  
  
  How to fix it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Open the terminal (console, command prompt, etc.) and go to the source code folder.&lt;/li&gt;
&lt;li&gt;Switch to rebase mode
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ➜  git rebase &lt;span class="nt"&gt;-i&lt;/span&gt; HEAD~&amp;lt;N&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;&amp;lt;N&amp;gt;&lt;/code&gt; is amount of commits from HEAD.&lt;br&gt;
   Or you may specify commit hash or branch&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ➜  git rebase &lt;span class="nt"&gt;-i&lt;/span&gt; 0893420285a5813ddd4a2c86f40054fa4968c9cf
   ➜  git rebase &lt;span class="nt"&gt;-i&lt;/span&gt; master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Mark required commits to edit (with &lt;code&gt;e&lt;/code&gt; letter)&lt;/li&gt;
&lt;li&gt;Repeat required times
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ➜  git commit &lt;span class="nt"&gt;--amend&lt;/span&gt; &lt;span class="nt"&gt;--author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Vladislav Bochenin &amp;lt;vladislav.bochenin@email.com&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;--no-edit&lt;/span&gt;
   ➜  git rebase &lt;span class="nt"&gt;--continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Check if everything is ok
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ➜  git log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Push changes
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ➜  git push &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;If you find the post helpful, please support me and:&lt;/em&gt;&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/vbocheninQ"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rJvAnNew--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buymeacoffee.com/buttons/default-black.png" alt="Buy Me A Coffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>devjournal</category>
      <category>tipsandtricks</category>
    </item>
    <item>
      <title>How to create limited-size directory</title>
      <dc:creator>Vlad</dc:creator>
      <pubDate>Fri, 07 Oct 2022 14:37:56 +0000</pubDate>
      <link>https://dev.to/vbochenin/how-to-create-limited-size-directory-39bp</link>
      <guid>https://dev.to/vbochenin/how-to-create-limited-size-directory-39bp</guid>
      <description>&lt;h2&gt;
  
  
  What an issue I've got
&lt;/h2&gt;

&lt;p&gt;Today I've got a task to test (and fix) how the application behaves during a backup when it does not have a space on the device.&lt;br&gt;
As result, there shouldn't be any new files once the backup is failed with "no space left" error.&lt;/p&gt;

&lt;h2&gt;
  
  
  How did I investigate the issue
&lt;/h2&gt;

&lt;p&gt;So far, I see the following options for how to reproduce the issue: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to have enormous application data to backup, either on some shared machine or generate the content locally&lt;/li&gt;
&lt;li&gt;to create a limited-size directory and try to write a backup there&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First option is time-consuming to reproduce it locally, so let's start with creating limited-size directories.&lt;/p&gt;

&lt;p&gt;I have two target environments for testing: Windows 10 and Debian-based Linux. &lt;br&gt;
I've chosen Ubuntu 18.04 because I have the VM already installed.&lt;/p&gt;

&lt;p&gt;So, let's make a backup for the newly installed application and check its size. &lt;br&gt;
And empty backup is 8.5Mb. It is suspicious the app has an 8Mb backup from scratch, and I will write it down to fix later, but I have another issue so far.&lt;/p&gt;

&lt;h3&gt;
  
  
  Windows
&lt;/h3&gt;

&lt;p&gt;I haven't found a way to create a limited-size directory on Windows, but I can create a limited-size virtual disk and use it for my purposes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to Disk management&lt;/li&gt;
&lt;li&gt;Select your local disk and choose "Create VHD" in "Action"
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5uCIcaQ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0i9jrhbdf17ticbc6ohy.png" alt="Create VHD" width="750" height="301"&gt;
&lt;/li&gt;
&lt;li&gt;Choose Virtual Disk file location and set disk size. For my pupose I need 10Mb (8Mb for backup plus some extra for partitions and system information) and I need "Fixed size".
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PXl10aQv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nvcce7qtwgjakhtqsk21.png" alt="Create VHD Wizard" width="753" height="538"&gt;
&lt;/li&gt;
&lt;li&gt;Initialize the new disk (choose Master Boot Record)
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JMfsXBot--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ursrow2n7w9738f8leol.png" alt="Initialize disk" width="751" height="594"&gt;
&lt;/li&gt;
&lt;li&gt;Create new simple volume.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BKWq8tqa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fnegz5ekpbmz8v39ocpr.png" alt="New colume" width="751" height="593"&gt;
&lt;/li&gt;
&lt;li&gt;Next... Next... Next... and disk is ready
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r7LhD0sv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q1k0rcvbade0wia6gd2i.png" alt="Disk is ready" width="614" height="137"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wow!!! It took almost 7Mb from the original 10Mb for system data. WOW!!! &lt;/p&gt;

&lt;p&gt;But... it is enough for my purposes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linux
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create file with size you need
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  &lt;span class="nb"&gt;touch &lt;/span&gt;10mbarea
➜  &lt;span class="nb"&gt;truncate&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; 10M 10mbarea
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create file system with the file
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  mke2fs &lt;span class="nt"&gt;-t&lt;/span&gt; ext4 &lt;span class="nt"&gt;-F&lt;/span&gt; 10mbarea
mke2fs 1.46.5 &lt;span class="o"&gt;(&lt;/span&gt;30-Dec-2021&lt;span class="o"&gt;)&lt;/span&gt;
Discarding device blocks: &lt;span class="k"&gt;done                            
&lt;/span&gt;Creating filesystem with 10240 1k blocks and 2560 inodes
Filesystem UUID: 03249ba7-6abc-4afb-a496-b5857f529859
Superblock backups stored on blocks: 
        8193

Allocating group tables: &lt;span class="k"&gt;done                            
&lt;/span&gt;Writing inode tables: &lt;span class="k"&gt;done                            
&lt;/span&gt;Creating journal &lt;span class="o"&gt;(&lt;/span&gt;1024 blocks&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="k"&gt;done
&lt;/span&gt;Writing superblocks and filesystem accounting information: &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a directory you will use later
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  &lt;span class="nb"&gt;mkdir &lt;/span&gt;10mbup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Mount the file system to the folder
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  &lt;span class="nb"&gt;sudo &lt;/span&gt;mount 10mbarea 10mbup 
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; password &lt;span class="k"&gt;for &lt;/span&gt;vbochenin: 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;And limited size directory is ready
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  &lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; 10mbup              
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop1      8.3M   14K  7.5M   1% /home/vbochenin/10mbup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once limited size directories created, I may specify them as backup target directory and  run the same empty backup. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you find the post helpful, please support me and:&lt;/em&gt;&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/vbocheninQ"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rJvAnNew--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buymeacoffee.com/buttons/default-black.png" alt="Buy Me A Coffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>linux</category>
      <category>windows</category>
    </item>
    <item>
      <title>When I'm using JMH</title>
      <dc:creator>Vlad</dc:creator>
      <pubDate>Thu, 06 Oct 2022 21:22:40 +0000</pubDate>
      <link>https://dev.to/vbochenin/why-im-using-jmh-4f5g</link>
      <guid>https://dev.to/vbochenin/why-im-using-jmh-4f5g</guid>
      <description>&lt;h2&gt;
  
  
  What an issue I've tried to solve
&lt;/h2&gt;

&lt;p&gt;My task was to investigate the reason why typed enumeration occupies 13 Mb per instance in a customer environment. The typed enumeration is a domain object allowing to users configure enumeration type for custom fields.&lt;br&gt;
The use case is reproducible once enumeration has around 1k elements.&lt;/p&gt;

&lt;p&gt;So I may assume the following possible causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a memory leak

&lt;ul&gt;
&lt;li&gt;keeping in memory some stale caches values &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;storing big files in memory

&lt;ul&gt;
&lt;li&gt;the enumeration is uploaded by user as XML file, so some intermediate big object can be cached &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;duplicating of data (if so, then why?). 

&lt;ul&gt;
&lt;li&gt;multiple copies of same data sorted in cache for different keys &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I did to find the cause
&lt;/h2&gt;

&lt;p&gt;First, I've rerun the scenario locally, made a heap dump, and analyzed it with &lt;a href="https://www.eclipse.org/mat/" rel="noopener noreferrer"&gt;MemoryAnalizer&lt;/a&gt;. &lt;br&gt;
It is visible from the report below (&lt;em&gt;Histogram -&amp;gt; Choose TypedEnumeration in filter -&amp;gt; List of object with outgoing reference -&amp;gt; Sort by Retained Heap&lt;/em&gt;), that we are storing duplicated data for each key in &lt;code&gt;control2Options&lt;/code&gt; field.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Class Name                                                              | Shallow Heap | Retained Heap
-------------------------------------------------------------------------------------------------------
TypedEnumeration @ 0xdedf8050                                           |           64 |     4,637,960
|- control2Options java.util.HashMap @ 0xdef01f68                       |           48 |     3,925,856
|  |- table java.util.HashMap$Node[32] @ 0xdf7d5b10                     |          144 |     3,925,808
|  |  |- [26] java.util.HashMap$Node @ 0xdf7d5ba0                       |           32 |       712,256
|  |  |- [4] java.util.HashMap$Node @ 0xdfc3b790                        |           32 |       712,256
|  |  |- [14] java.util.HashMap$Node @ 0xdf12a800                       |           32 |       356,128
|  |  |- [13] java.util.HashMap$Node @ 0xdf24eab8                       |           32 |       356,128
|  |  |- [22] java.util.HashMap$Node @ 0xdf3a9608                       |           32 |       356,128
|  |  |- [23] java.util.HashMap$Node @ 0xdfa2e9a0                       |           32 |       356,128
|  |  |- [11] java.util.HashMap$Node @ 0xdfa50ff0                       |           32 |       356,128
|  |  |- [6] java.util.HashMap$Node @ 0xdfb91928                        |           32 |       356,128
|  |  |- [5] java.util.HashMap$Node @ 0xdfd3ca10                        |           32 |       356,128
|  |  |- [0] java.util.HashMap$Node @ 0xde742630                        |           32 |         4,128
|  |  |- [19] java.util.HashMap$Node @ 0xdf38cad0                       |           32 |         4,128
|  |  |-  class java.util.HashMap$Node[] @ 0xd444d5a8                   |            0 |             0
|  |  '- Total: 12 entries                                              |              |              
|  |- &amp;lt;class&amp;gt; class java.util.HashMap @ 0xd4450dd0 System Class         |           40 |           144
|  '- Total: 2 entries                                                  |              |              
|- allOptions java.util.ArrayList @ 0xdedf8090                          |           24 |         8,040
.....
'- Total: 10 entries                                                    |              |              
TypedEnumeration @ 0xe0064a60                                           |           64 |     4,637,960
|- control2Options java.util.HashMap @ 0xe055ecb0                       |           48 |     3,925,856
|  |- table java.util.HashMap$Node[32] @ 0xe08dd030                     |          144 |     3,925,808
|  |  |- [4] java.util.HashMap$Node @ 0xe086f630                        |           32 |       712,256
|  |  |- [26] java.util.HashMap$Node @ 0xe08dd0c0                       |           32 |       712,256
|  |  |- [23] java.util.HashMap$Node @ 0xe053daf8                       |           32 |       356,128
|  |  |- [5] java.util.HashMap$Node @ 0xe084cd10                        |           32 |       356,128
|  |  |- [13] java.util.HashMap$Node @ 0xe08521f0                       |           32 |       356,128
|  |  |- [6] java.util.HashMap$Node @ 0xe08dc138                        |           32 |       356,128
|  |  |- [11] java.util.HashMap$Node @ 0xe09de780                       |           32 |       356,128
|  |  |- [14] java.util.HashMap$Node @ 0xe0a665c0                       |           32 |       356,128
|  |  |- [22] java.util.HashMap$Node @ 0xe0acc8c0                       |           32 |       356,128
|  |  |- [0] java.util.HashMap$Node @ 0xdfa1e7d0                        |           32 |         4,128
|  |  |- [19] java.util.HashMap$Node @ 0xe0aefcb0                       |           32 |         4,128
|  |  |-  class java.util.HashMap$Node[] @ 0xd444d5a8                   |            0 |             0
|  |  '- Total: 12 entries                                              |              |              
|  |- &amp;lt;class&amp;gt; class java.util.HashMap @ 0xd4450dd0 System Class         |           40 |           144
|  '- Total: 2 entries                                                  |              |              
|- allOptions java.util.ArrayList @ 0xe0064b08                          |           24 |         8,040
....
'- Total: 10 entries                                                    |              |              



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Lets  rerun the scenario with the profiler turned on.&lt;br&gt;
I've used &lt;a href="https://jdk.java.net/jmc/8/" rel="noopener noreferrer"&gt;JMC&lt;/a&gt; and &lt;a href="https://docs.oracle.com/javacomponents/jmc-5-4/jfr-runtime-guide/about.htm#JFRUH170" rel="noopener noreferrer"&gt;Flight Records&lt;/a&gt;, and it showed me that the application occupies the extra memory during bootstrap.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjadbqovox8tmzyk0zta.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjadbqovox8tmzyk0zta.png" alt="Memory allocations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not exactly the place, but it reduced the scope significantly and after some code investigation, I found where the application is wasting memory.&lt;/p&gt;

&lt;p&gt;Success? Nope, I still need to fix the issue and prove that our fix is fixing something.&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://github.com/openjdk/jmh" rel="noopener noreferrer"&gt;JMH&lt;/a&gt; is coming to the stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we can do with the tool
&lt;/h2&gt;

&lt;p&gt;JMH is a microbenchmark framework for Java. &lt;br&gt;
It cares about memory alignments, method inlines, warmups, and all other stuff that makes benchmarks hard to write.&lt;br&gt;
It also has multiple profilers you may use in your benchmarks.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

- Profiler (org.openjdk.jmh.profile)
    |- ExternalProfiler (org.openjdk.jmh.profile)
    |   |- AbstractPerfAsmProfiler (org.openjdk.jmh.profile)
    |   |   |- DTraceAsmProfiler (org.openjdk.jmh.profile)
    |   |   |- LinuxPerfAsmProfiler (org.openjdk.jmh.profile)
    |   |   '- WinPerfAsmProfiler (org.openjdk.jmh.profile)
    |   |- AsyncProfiler (org.openjdk.jmh.profile)
    |   |- JavaFlightRecorderProfiler (org.openjdk.jmh.profile)
    |   |- LinuxPerfC2CProfiler (org.openjdk.jmh.profile)
    |   |- LinuxPerfNormProfiler (org.openjdk.jmh.profile)
    |   |- LinuxPerfProfiler (org.openjdk.jmh.profile)
    |   '- SafepointsProfiler (org.openjdk.jmh.profile)
    '- InternalProfiler (org.openjdk.jmh.profile)
        |- AsyncProfiler (org.openjdk.jmh.profile)
        |- ClassloaderProfiler (org.openjdk.jmh.profile)
        |- CompilerProfiler (org.openjdk.jmh.profile)
        |- GCProfiler (org.openjdk.jmh.profile)
        |- JavaFlightRecorderProfiler (org.openjdk.jmh.profile)
        |- PausesProfiler (org.openjdk.jmh.profile)
        '- StackProfiler (org.openjdk.jmh.profile)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;One I've used is &lt;code&gt;GCProfiler&lt;/code&gt;. &lt;br&gt;
The profiler calculates memory allocations and garbage collections during benchmark execution.&lt;br&gt;
Statistics it shows, I'm interested in, is &lt;code&gt;gc.alloc.rate (MB/sec)&lt;/code&gt; and &lt;code&gt;gc.alloc.rate.norm (B/op)&lt;/code&gt; &lt;br&gt;
&lt;code&gt;gc.alloc.rate&lt;/code&gt; shows how much memory the benchmark occupies during the trial, but &lt;code&gt;gc.alloc.rate.norm&lt;/code&gt; is about how much memory was allocated during single benchmark execution.&lt;/p&gt;

&lt;p&gt;Having investigation results in mind, I may assume that less memory the application allocated, less GC pressure and less data application is storing in cache. &lt;/p&gt;

&lt;p&gt;Warning: the assumption is fair only for the issue above because the investigation showed application is duplicating data it may never use.&lt;/p&gt;

&lt;p&gt;So I've ended up with the following plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;write benchmarks for suspicious methods &lt;/li&gt;
&lt;li&gt;run benchmarks with GCProfiler and get some numbers&lt;/li&gt;
&lt;li&gt;improve code or fix the issue&lt;/li&gt;
&lt;li&gt;run the benchmark again &lt;/li&gt;
&lt;li&gt;analyze numbers&lt;/li&gt;
&lt;li&gt;repeat until successful &lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 java
public class TypeEnumerationAllocationBenchmark {  

    @Benchmark  
    public Object newObject(TypedEnumerationBenchmarkExecution execution) {  
        return new BenchmarkTypeEnumeration(...);  
    }  

    @Benchmark  
    public Object getAvailableOptions(TypedEnumerationBenchmarkExecution execution) {  
        return new BenchmarkTypeEnumeration(...).getAvailableOptions(null);  
    }  

    @Benchmark  
    public Object getAllOptions(TypedEnumerationBenchmarkExecution execution) {  
        return new BenchmarkTypeEnumeration(...).getAllSortedOptions();  
    }  

    @Benchmark  
    public void preLoadEnumeration(TypedEnumerationBenchmarkExecution execution, Blackhole blackhole) {  
        BenchmarkTypeEnumeration enumeration = new BenchmarkTypeEnumeration(...);  
        blackhole.consume(enumeration.getAllSortedOptions());  
        for (String controlKey : execution.controlKeys) {  
            blackhole.consume(enumeration.getAvailableOptions(controlKey));  
        }  
    }  
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In parallel, I would run the simplest performance benchmark for the nominal use cases to prevent dramatic performance degradation.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 java
public class TypeEnumerationBenchmark {  

    @Benchmark  
    public Object getAvailableOptions(Execution execution) {  
        return execution.enumeration.getAvailableOptions(null);  
    }  

    @Benchmark  
    public Object getAllOptions(Execution execution) {  
        return execution.enumeration.getAllSortedOptions();  
    }  

    public static void main(String[] args) throws Exception {  
        Options opt = new OptionsBuilder()  
                .parent(new CommandLineOptions(args))  
                .include(".*TypeEnumerationBenchmark.*")  
                .build();  

        new Runner(opt).run();  
    }  
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  How does it work
&lt;/h2&gt;

&lt;p&gt;JMH works with an &lt;a href="https://jcp.org/en/jsr/detail?id=269" rel="noopener noreferrer"&gt;Annotation Processing API&lt;/a&gt; and generates multiple classes for each benchmark method (method marked with &lt;code&gt;@Benchmark&lt;/code&gt; annotation).&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 java
benchmark
    jmh_generated
        benchmark.jmh_generated.TypedEnumerationBenchmarkExecution_jmhType
        benchmark.jmh_generated.TypedEnumerationBenchmarkExecution_jmhType_B1
        benchmark.jmh_generated.TypedEnumerationBenchmarkExecution_jmhType_B2
        benchmark.jmh_generated.TypedEnumerationBenchmarkExecution_jmhType_B3
        benchmark.jmh_generated.TypeEnumerationAllocationBenchmark_getAllOptions_jmhTest
        benchmark.jmh_generated.TypeEnumerationAllocationBenchmark_getAvailableOptions_jmhTest
        benchmark.jmh_generated.TypeEnumerationAllocationBenchmark_jmhType
        benchmark.jmh_generated.TypeEnumerationAllocationBenchmark_jmhType_B1
        benchmark.jmh_generated.TypeEnumerationAllocationBenchmark_jmhType_B2
        benchmark.jmh_generated.TypeEnumerationAllocationBenchmark_jmhType_B3
        benchmark.jmh_generated.TypeEnumerationAllocationBenchmark_newObject_jmhTest
        benchmark.jmh_generated.TypeEnumerationAllocationBenchmark_preLoadEnumeration_jmhTest
        benchmark.jmh_generated.TypeEnumerationBenchmark_Execution_jmhType
        benchmark.jmh_generated.TypeEnumerationBenchmark_Execution_jmhType_B1
        benchmark.jmh_generated.TypeEnumerationBenchmark_Execution_jmhType_B2
        benchmark.jmh_generated.TypeEnumerationBenchmark_Execution_jmhType_B3
        benchmark.jmh_generated.TypeEnumerationBenchmark_getAllOptions_jmhTest
        benchmark.jmh_generated.TypeEnumerationBenchmark_getAvailableOptions_jmhTest
        benchmark.jmh_generated.TypeEnumerationBenchmark_jmhType
        benchmark.jmh_generated.TypeEnumerationBenchmark_jmhType_B1
        benchmark.jmh_generated.TypeEnumerationBenchmark_jmhType_B2
        benchmark.jmh_generated.TypeEnumerationBenchmark_jmhType_B3
    benchmark.TypeEnumerationAllocationBenchmark
    benchmark.TypeEnumerationBenchmark


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So, once you run your benchmark, it will try to find the generated classes in a classpath and use them to run the actual method and collect results.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run it
&lt;/h2&gt;

&lt;p&gt;There are the few ways how to run it.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/openjdk/jmh#preferred-usage-command-line" rel="noopener noreferrer"&gt;best one&lt;/a&gt; with more precise results is to write the &lt;code&gt;main&lt;/code&gt; method, build a .jar file, and run it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 java
public class Benchmark {

    ....

    public static void main(String[] args) throws Exception {  
        Options opt = new OptionsBuilder()  
                .jvmArgsAppend("-Djmh.separateClasspathJAR=true")  
                .parent(new CommandLineOptions(args))  
                .include(".*Benchmark.*")  
                .build();  

        new Runner(opt).run();
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You may also run the &lt;code&gt;main&lt;/code&gt; method from IDE, but likely your IDE will add some agent into JVM, so it makes the result less precise.&lt;/p&gt;

&lt;p&gt;If you don't need to have precise results but rather to see performance trends, you may run the test as a JUnit test.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 java
public class Benchmark {

    ....
    @Benchmark  
    public void benchmarkMethod(...) {
        ...
    }

    @Test  
    public void shouldRunBenchmark() throws Exception {  
        Options opt = new OptionsBuilder()  
                .include(".*Benchmark.benchmarkMethod*")  
                .build();  

        new Runner(opt).run();  
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I've chosen to run from IDE because I'm interesting in memory allocations (a small side effect from IDE agents) and I'm going to use it during bugfix (I don't want to switch between IDE and console).&lt;/p&gt;

&lt;p&gt;Once you run it you will see something like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 bash
# JMH version: 1.35
# VM version: JDK 11.0.15, OpenJDK 64-Bit Server VM, 11.0.15+9-LTS
# VM invoker: C:\workdir\bin\amazon-correto\jdk11.0.15_9\bin\java.exe
# VM options: -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2022.2.1\lib\idea_rt.jar=50905:C:\Program Files\JetBrains\IntelliJ IDEA 2022.2.1\bin -Dfile.encoding=UTF-8 -Djmh.separateClasspathJAR=true
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: benchmark.TypeEnumerationAllocationBenchmark.preLoadEnumeration
# Parameters: (controlKeysCount = 10, enumsCount = 10, prefixesCount = 1)

# Run progress: 0.00% complete, ETA 00:08:20
# Fork: 1 of 5
# Warmup Iteration   1: 100917.316 ops/s
# Warmup Iteration   2: 105528.728 ops/s
# Warmup Iteration   3: 105569.479 ops/s
# Warmup Iteration   4: 104947.364 ops/s
# Warmup Iteration   5: 105071.139 ops/s
Iteration   1: 104137.948 ops/s
                 ·gc.alloc.rate:               1900.765 MB/sec
                 ·gc.alloc.rate.norm:          20120.000 B/op
                 ·gc.churn.G1_Eden_Space:      1906.794 MB/sec
                 ·gc.churn.G1_Eden_Space.norm: 20183.824 B/op
                 ·gc.churn.G1_Old_Gen:         0.002 MB/sec
                 ·gc.churn.G1_Old_Gen.norm:    0.018 B/op
                 ·gc.count:                    33.000 counts
                 ·gc.time:                     22.000 ms

Iteration   2: 104582.692 ops/s
....

# Run complete. Total time: 00:08:22

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark                                                                            (controlKeysCount)  (enumsCount)  (prefixesCount)   Mode  Cnt        Score       Error   Units
TypeEnumerationAllocationBenchmark.preLoadEnumeration                                                10          1000                1  thrpt   10     104137.948 ±    31.242   ops/s
TypeEnumerationAllocationBenchmark.preLoadEnumeration:·gc.alloc.rate                                 10          1000                1  thrpt   10     1900.765 ±    48.356  MB/sec
TypeEnumerationAllocationBenchmark.preLoadEnumeration:·gc.alloc.rate.norm                            10          1000                1  thrpt   10  20120.000   ±    39.771    B/op
TypeEnumerationAllocationBenchmark.preLoadEnumeration:·gc.churn.G1_Eden_Space                        10          1000                1  thrpt   10     1906.794 ±    58.341  MB/sec
TypeEnumerationAllocationBenchmark.preLoadEnumeration:·gc.churn.G1_Eden_Space.norm                   10          1000                1  thrpt   10  20183.824   ± 40835.901    B/op
TypeEnumerationAllocationBenchmark.preLoadEnumeration:·gc.churn.G1_Old_Gen                           10          1000                1  thrpt   10        0.002 ±     0.027  MB/sec
TypeEnumerationAllocationBenchmark.preLoadEnumeration:·gc.churn.G1_Old_Gen.norm                      10          1000                1  thrpt   10       0.018 ±    0.012    B/op
TypeEnumerationAllocationBenchmark.preLoadEnumeration:·gc.count                                      10          1000                1  thrpt   10      33.000              counts
TypeEnumerationAllocationBenchmark.preLoadEnumeration:·gc.time                                       10          1000                1  thrpt   10      22.000                  ms
....


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Fork&lt;/code&gt; - single benchmark execution. Every method executed several times. The number could be changed with &lt;code&gt;-f&lt;/code&gt; option. Default value is &lt;code&gt;5&lt;/code&gt;. &lt;code&gt;0&lt;/code&gt; means no fork and could be useful during benchmark debugging.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Warmup Iteration&lt;/code&gt; - some benchmark execution for warmup. Warmup results will not be used as benchmark result and requires mainly to eliminate some internal Java compilations and optimizations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Iteration #&lt;/code&gt; - actual benchmark execution and intermediate results. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More execution options you may find &lt;a href="https://github.com/guozheng/jmh-tutorial/blob/master/README.md" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you find the post helpful, please support me and:&lt;/em&gt;&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/vbocheninQ" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwve1hh3j8ewl5aowo7r.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>jmh</category>
      <category>performance</category>
      <category>devjournal</category>
    </item>
  </channel>
</rss>
