<?xml version="1.0"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Benelog Tech Notes</title>
    <link>https://tech.benelog.net/</link>
    <atom:link href="https://tech.benelog.net/feed.xml" rel="self" type="application/rss+xml" />
    <description>Engineering notes by Sanghyuk Jung on Java and backend systems</description>
    <language>en</language>
    <pubDate>Sat, 3 Oct 2026 00:33:24 +0000</pubDate>
    <lastBuildDate>Sat, 3 Oct 2026 00:33:24 +0000</lastBuildDate>

    <item>
      <title>Call by Value vs. Call by Reference</title>
      <link>https://tech.benelog.net/call-by-value-vs-call-by-reference.html</link>
      <pubDate>Sat, 3 Oct 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="false">call-by-value-vs-call-by-reference.html</guid>
      	<description>
	&lt;div id=&quot;toc&quot; class=&quot;toc&quot;&gt;
&lt;div id=&quot;toctitle&quot;&gt;Table of Contents&lt;/div&gt;
&lt;ul class=&quot;sectlevel1&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#call_by_value_and_call_by_reference&quot;&gt;Call by Value and Call by Reference&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#the_terms_pass_by_and_call_by&quot;&gt;The Terms &quot;Pass by&quot; and &quot;Call by&quot;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#call_by_sharing&quot;&gt;Call by Sharing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#java&quot;&gt;Java&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#result_of_the_swap_method&quot;&gt;Result of the swap Method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#why_reassigning_a_parameter_does_not_affect_the_callers_variable&quot;&gt;Why Reassigning a Parameter Does Not Affect the Caller&amp;#8217;s Variable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#mutating_object_state_the_source_of_the_misconception&quot;&gt;Mutating Object State: The Source of the Misconception&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#java_references_and_terminology_confusion&quot;&gt;Java References and Terminology Confusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#a_new_frame_for_every_method_invocation&quot;&gt;A New Frame for Every Method Invocation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#kotlin&quot;&gt;Kotlin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#javascript&quot;&gt;JavaScript&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#the_by_reference_wording_in_javascript_the_definitive_guide&quot;&gt;The By Reference Wording in JavaScript: The Definitive Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#c&quot;&gt;C&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#c_2&quot;&gt;C&amp;#43;&amp;#43;&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#pointer_parameters_vs_reference_parameters&quot;&gt;Pointer Parameters vs. Reference Parameters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#references_in_the_c_standard&quot;&gt;References in the C&amp;#43;&amp;#43; Standard&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#c_3&quot;&gt;C#&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#python&quot;&gt;Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#go&quot;&gt;Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#rust&quot;&gt;Rust&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ada_call_by_reference_vs_copy_restore&quot;&gt;Ada: Call by Reference vs. Copy-Restore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#summary&quot;&gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#references&quot;&gt;References&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div id=&quot;preamble&quot;&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;You often hear that &quot;in Java method calls, primitive types are passed by call by value and objects are passed by call by reference.&quot;
Java, however, has only call by value.
When you pass an object to a method, the value that gets copied is simply a reference that points to the object.
This article lays out the definitions of the two terms and compares how Java, Kotlin, JavaScript, C, C&amp;#43;&amp;#43;, C#, Python, Go, Rust, and Ada pass arguments.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;call_by_value_and_call_by_reference&quot;&gt;Call by Value and Call by Reference&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;Call by value&lt;/strong&gt; initializes a separate variable, the parameter, with the value passed as the argument. The value is usually copied, though some languages such as Rust move ownership instead. Assigning a different value to the parameter inside the function does not affect the caller&amp;#8217;s variable.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;Call by reference&lt;/strong&gt; passes the variable itself. The parameter becomes an alias of the caller&amp;#8217;s variable, so assigning a new value to the parameter inside the function also changes the caller&amp;#8217;s variable.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;These definitions follow the standard treatment in programming language theory.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Section 1.6.6 of &lt;strong&gt;Compilers: Principles, Techniques, and Tools&lt;/strong&gt;, 2nd edition, describes the two mechanisms as follows.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In call-by-value, the actual parameter is evaluated (if it is an expression) or copied (if it is a variable). The value is placed in the location belonging to the corresponding formal parameter of the called procedure. This method is used in C and Java, and is a common option in C++, as well as in most other languages. &amp;#8230;&amp;#8203;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In call-by-reference, the address of the actual parameter is passed to the callee as the value of the corresponding formal parameter. Uses of the formal parameter in the code of the callee are implemented by following this pointer to the location indicated by the caller. Changes to the formal parameter thus appear as changes to the actual parameter.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Alfred V. Aho, Monica S. Lam, Ravi Sethi, Jeffrey D. Ullman&lt;br&gt;
&lt;cite&gt;Compilers: Principles, Techniques, and Tools, 2nd edition, Section 1.6.6&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &quot;actual parameter&quot; in the quote corresponds to what this article calls an argument, and the &quot;formal parameter&quot; corresponds to what this article calls a parameter. Together with its 1977 predecessor &lt;strong&gt;Principles of Compiler Design&lt;/strong&gt;, this book has served as the standard textbook for university compiler courses. Its cover illustration earned it the nickname &quot;the Dragon Book.&quot;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;img/call-by-value/dragon-book-2nd.jpg&quot; alt=&quot;Cover of Compilers: Principles, Techniques, and Tools, 2nd edition&quot; width=&quot;200&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;title&quot;&gt;Figure 1. Cover of Compilers: Principles, Techniques, and Tools, 2nd edition (image source: &lt;a href=&quot;https://openlibrary.org/isbn/9780321486813&quot;&gt;Open Library&lt;/a&gt;)&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Christopher Strachey explained the distinction in terms of L-values and R-values in Section 3.4.2 of &lt;a href=&quot;https://www.cs.cmu.edu/~crary/819-f09/Strachey67.pdf&quot;&gt;Fundamental Concepts in Programming Languages&lt;/a&gt;, his 1967 lecture notes. He called passing the R-value of an expression to a parameter call by value, and passing its L-value call by reference.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When the function is used (or called or applied) we write f[ε] where ε can be an expression. If we are using a referentially transparent language all we require to know about the expression ε in order to evaluate f[ε] is its value. There are, however, two sorts of value, so we have to decide whether to supply the R-value or the L-value of ε to the function f. Either is possible, so that it becomes a part of the definition of the function to specify for each of its bound variables (also called its formal parameters) whether it requires an R-value or an L-value. These alternatives will also be known as calling a parameter by value (R-value) or reference (L-value).&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Christopher Strachey&lt;br&gt;
&lt;cite&gt;Fundamental Concepts in Programming Languages, Section 3.4.2&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;An L-value is the storage location denoted by an expression on the left side of an assignment, and an R-value is the value stored in that location, denoted by an expression on the right side. These lecture notes are a classic of programming language theory and are still cited as the origin of terms such as L-value, R-value, and first-class object.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;A practical test for telling the two mechanisms apart fits in one sentence: &lt;strong&gt;When a function assigns a new value to its parameter, does the caller&amp;#8217;s variable change?&lt;/strong&gt; If it does, the mechanism is call by reference; if it does not, it is call by value. The test holds under the assumptions of this article: only these two mechanisms are compared, the argument is a reassignable variable, and the parameter can be assigned to. Once other evaluation strategies such as call by copy-restore and call by name come into play, this question alone cannot classify every argument-passing mechanism.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The assignment in this test means putting a different value into the parameter variable itself, as in &lt;code&gt;targetTv = new Tv()&lt;/code&gt;. If the parameter points to an object, this makes it point to a different object. Changing the internal state of the object the parameter points to, as in &lt;code&gt;targetTv.setChannel(&quot;Rock&quot;)&lt;/code&gt;, is a separate matter. The misconception discussed later comes from confusing the two.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;the_terms_pass_by_and_call_by&quot;&gt;The Terms &quot;Pass by&quot; and &quot;Call by&quot;&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Call by value and call by reference are also called pass by value and pass by reference. The &lt;code&gt;call by ~&lt;/code&gt; form dates back to the ALGOL 60 report published in 1960. Section 4.7.3 of the &lt;a href=&quot;https://www.masswerk.at/algol60/report.htm&quot;&gt;revised report of 1963&lt;/a&gt; specifies how parameters are passed, with 4.7.3.1 titled &quot;Value assignment (call by value)&quot; and 4.7.3.2 titled &quot;Name replacement (call by name).&quot; The &lt;code&gt;pass by ~&lt;/code&gt; form is also widely used in practice and in textbooks. This article uses &lt;code&gt;call by ~&lt;/code&gt; throughout.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;call_by_sharing&quot;&gt;Call by Sharing&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The argument-passing behavior of Java, JavaScript, and Python, which shares an object rather than the caller&amp;#8217;s variable itself, is sometimes called &lt;strong&gt;call by sharing&lt;/strong&gt;. The term comes from the &lt;a href=&quot;https://csg.csail.mit.edu/pubs/memos/Memo-112/memo112-1a.pdf&quot;&gt;design document of CLU&lt;/a&gt;, a language created in the mid-1970s by a team led by Barbara Liskov. The object-argument behavior of Java, Kotlin, JavaScript, and Python and the passing of class instances in C#, all covered later, fall into this category. The term &quot;call by object reference&quot; used in the Python tutorial means the same thing. The same structure appears when you pass a map or a pointer in Go, or a pointer in C.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&quot;Call by value&quot; can mislead people into thinking the whole object is copied, and &quot;call by reference&quot; can mislead them into thinking that reassignment is reflected in the caller&amp;#8217;s variable. Call by sharing is a good term for avoiding both misconceptions. Some sources use it, such as the MDN JavaScript documentation quoted later, but it has not become a standard term in textbooks or language specifications. This article therefore continues with only the two categories of call by value and call by reference.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;java&quot;&gt;Java&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;result_of_the_swap_method&quot;&gt;Result of the swap Method&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The output of the following code shows how Java passes arguments.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Testing a swap method&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;public class ReferenceTest {
    public static void main(String[] args) {
        String x1 = &quot;Hello&quot;;
        String y1 = &quot;World&quot;;
        swap(x1, y1); // &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;
        System.out.println(x1); // Hello

        int x2 = 1;
        int y2 = 2;
        swap(x2, y2); // &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;
        System.out.println(x2); // 1
    }

    static void swap(Object x, Object y) {
        Object temp = x;
        x = y; // &lt;b class=&quot;conum&quot;&gt;(3)&lt;/b&gt;
        y = temp;
    }

    static void swap(int x, int y) {
        int temp = x;
        x = y;
        y = temp;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Copies of the reference values held by &lt;code&gt;x1&lt;/code&gt; and &lt;code&gt;y1&lt;/code&gt; are assigned to the parameters &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The int values are copied into the parameters of the &lt;code&gt;swap(int, int)&lt;/code&gt; overload. Without this overload, references to autoboxed Integer objects would be passed to the &lt;code&gt;Object&lt;/code&gt; parameters, with the same result.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Assigning each other&amp;#8217;s values to the parameters &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; inside the method does not change the caller&amp;#8217;s &lt;code&gt;x1&lt;/code&gt;, &lt;code&gt;y1&lt;/code&gt;, &lt;code&gt;x2&lt;/code&gt;, or &lt;code&gt;y2&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;By the test defined above, Java behaves as call by value for both primitive types and objects.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;why_reassigning_a_parameter_does_not_affect_the_callers_variable&quot;&gt;Why Reassigning a Parameter Does Not Affect the Caller&amp;#8217;s Variable&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Let&amp;#8217;s look more closely at passing an object as a method parameter.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Reassigning a parameter inside a method&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;class Tv {
    private String channel;

    void setChannel(String channel) { this.channel = channel; }
    String getChannel() { return channel; }
}

public class ReassignTest {
    public static void main(String[] args) {
        var myTv = new Tv();
        myTv.setChannel(&quot;Classic&quot;);

        reassign(myTv); // &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;
        System.out.println(myTv.getChannel()); // Classic
    }

    static void reassign(Tv targetTv) {
        targetTv = new Tv(); // &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;
        targetTv.setChannel(&quot;Rock&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The reference value held by &lt;code&gt;myTv&lt;/code&gt; in the main method is copied and assigned to the parameter &lt;code&gt;targetTv&lt;/code&gt; of the reassign method. The two variables point to the same object, but they are different variables.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A new object is assigned to the parameter &lt;code&gt;targetTv&lt;/code&gt;. Only the parameter &lt;code&gt;targetTv&lt;/code&gt; now points to the new object, and &lt;code&gt;myTv&lt;/code&gt; in the main method still points to the original object. That is why the output is &lt;code&gt;Classic&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;img/call-by-value/java-reassign.png&quot; alt=&quot;After the reference value is copied&quot; width=&quot;700&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;mutating_object_state_the_source_of_the_misconception&quot;&gt;Mutating Object State: The Source of the Misconception&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you change the internal state of the object the parameter points to, instead of reassigning the parameter, the changed state is visible through the caller&amp;#8217;s variable.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Changing object state inside a method without reassigning the parameter&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;public class ChangeStateTest {
    public static void main(String[] args) {
        var myTv = new Tv();
        myTv.setChannel(&quot;Classic&quot;);

        changeState(myTv); // &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;
        System.out.println(myTv.getChannel()); // &lt;b class=&quot;conum&quot;&gt;(3)&lt;/b&gt;
    }

    static void changeState(Tv targetTv) {
        targetTv.setChannel(&quot;Rock&quot;); // &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;As in the previous example, the reference value held by &lt;code&gt;myTv&lt;/code&gt; is copied and assigned to the parameter &lt;code&gt;targetTv&lt;/code&gt; of the changeState method. The two variables point to the same object.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Without reassigning the parameter &lt;code&gt;targetTv&lt;/code&gt;, the method changes the channel of the object the parameter points to &lt;code&gt;Rock&lt;/code&gt;. This is the very object that &lt;code&gt;myTv&lt;/code&gt; in the main method points to.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reading the channel through &lt;code&gt;myTv&lt;/code&gt; in the main method prints &lt;code&gt;Rock&lt;/code&gt;. The variable did not change, but the state of the object it points to did.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;img/call-by-value/java-mutation.png&quot; alt=&quot;Both variables point to the same object&quot; width=&quot;700&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Because the changed state is visible through the caller&amp;#8217;s variable, the claim that &quot;objects are passed by call by reference&quot; sounds plausible.
As explained above, however, the practical test for the two mechanisms is whether a value assigned to the parameter is reflected in the caller&amp;#8217;s variable.
The changed state is visible through the caller&amp;#8217;s variable only because both variables point to the same object, and what is passed is still a copy of the reference value. In other words, it is call by value.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;Head First Java&lt;/strong&gt; explains object references with the analogy of a TV remote control. Passing an object does not copy the TV (the object); it copies the remote control (the reference value) that operates the TV. If you change the channel with the copied remote (changing object state), the person holding the original remote sees the new channel too. If you pair the copied remote with a new TV (reassignment, &lt;code&gt;targetTv = new Tv()&lt;/code&gt;), that remote can no longer operate the original TV, and the original remote is still paired with the original TV.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;java_references_and_terminology_confusion&quot;&gt;Java References and Terminology Confusion&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;James Gosling, the creator of Java, addressed this misconception directly in &lt;strong&gt;The Java Programming Language&lt;/strong&gt;, which he coauthored.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Some people will say incorrectly that objects are passed &quot;by reference.&quot; &amp;#8230;&amp;#8203; The Java programming language does not pass objects by reference; it passes object references by value.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Ken Arnold, James Gosling, David Holmes&lt;br&gt;
&lt;cite&gt;The Java Programming Language, 4th edition&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;img/call-by-value/java-programming-language-4th.jpg&quot; alt=&quot;Cover of The Java Programming Language, 4th edition&quot; width=&quot;200&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;title&quot;&gt;Figure 2. Cover of The Java Programming Language, 4th edition (image source: &lt;a href=&quot;https://openlibrary.org/isbn/9780321349804&quot;&gt;Open Library&lt;/a&gt;)&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.java/learn/classes-objects/calling-methods-constructors/&quot;&gt;Dev.java&lt;/a&gt;, the current official Java learning site, says the same thing.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Reference data type parameters, such as objects, are also passed into methods by value. &amp;#8230;&amp;#8203; when the method returns, the passed-in reference still references the same object as before.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Dev.java&lt;br&gt;
&lt;cite&gt;Calling Methods and Constructors&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The Dragon Book quoted earlier also states in the same section that Java uses call by value exclusively. In the call-by-reference entry that follows, however, it writes that Java behaves as if it used call by reference for anything other than basic types.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Even though Java uses call-by-value exclusively, whenever we pass the name of an object to a called procedure, the value received by that procedure is in effect a pointer to the object. Thus, the called procedure is able to affect the value of the object itself. &amp;#8230;&amp;#8203;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;As we noted when discussing call-by-value, languages such as Java solve the problem of passing arrays, strings, or other objects by copying only a reference to those objects. The effect is that Java behaves as if it used call-by-reference for anything other than a basic type such as an integer or real.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Alfred V. Aho, Monica S. Lam, Ravi Sethi, Jeffrey D. Ullman&lt;br&gt;
&lt;cite&gt;Compilers: Principles, Techniques, and Tools, 2nd edition, Section 1.6.6&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&quot;Behaves as if it used call-by-reference&quot; refers to the effect: the object is not copied as a whole, and changes the called method makes to the object&amp;#8217;s state are visible to the caller. The phrase assumes what the book stated earlier, that the actual mechanism is not call by reference.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The word &quot;reference&quot; itself seems to have fed the misconception that Java passes objects by call by reference. Java calls a value that points to an object a &quot;reference,&quot; and that word overlaps with the &quot;reference&quot; in &quot;call by reference.&quot; Put more precisely, &lt;strong&gt;Java passes the reference value that points to an object by call by value.&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;A Java reference is a value that identifies and gives access to an object. You can think of it as a pointer.
The following Java object access&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Accessing a Java object&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;var myTv = new Tv();
myTv.setChannel(&quot;Rock&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;is similar to this C&amp;#43;&amp;#43; code that uses a pointer.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;C&amp;#43;&amp;#43; pointer&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;cpp&quot;&gt;Tv *myTv = new Tv();
myTv-&amp;gt;setChannel(&quot;Rock&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The Java variable &lt;code&gt;myTv&lt;/code&gt; holds not the object itself but a value that points to the object. When you pass &lt;code&gt;myTv&lt;/code&gt; as an argument to another method, this reference value is copied and passed. The C&amp;#43;&amp;#43; code above is an analogy for object access and pointer reassignment only; it does not mean that object lifetime management or the actual representation of references are the same.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Let&amp;#8217;s look at the relevant wording in the JLS (Java Language Specification). The JLS also uses the word &quot;pointer.&quot; Section &lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se26/html/jls-4.html#jls-4.3.1&quot;&gt;4.3.1. Objects&lt;/a&gt; defines reference values as pointers to objects.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Java Language Specification&lt;br&gt;
&lt;cite&gt;4.3.1. Objects&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This describes language-level semantics; it does not guarantee that a reference value has the same format as a raw memory address. &lt;a href=&quot;https://docs.oracle.com/en/java/javase/26/docs/specs/jvms/jvms-2.html#jvms-2.7&quot;&gt;JVMS (Java Virtual Machine Specification) 2.7&lt;/a&gt; does not mandate any particular internal structure for objects, and notes that in some implementations a reference may point to a handle rather than to the object data. A handle here is a small intermediate structure between the reference and the object data. The reference points to the handle, and pointers inside the handle in turn point to the object data and the method table. In other words, a reference value may not be the memory address where the object resides.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This description of handles originally described &quot;Sun&amp;#8217;s current implementation&quot; in the &lt;a href=&quot;https://web.archive.org/web/20001213164500/http://java.sun.com/docs/books/vmspec/html/Overview.doc.html&quot;&gt;first edition of the JVMS (1997)&lt;/a&gt;. The second edition changed the wording to &quot;some of Sun&amp;#8217;s implementations,&quot; and editions after the Oracle acquisition to &quot;some of Oracle&amp;#8217;s implementations,&quot; but the content stayed the same. At the time of the first edition, Sun&amp;#8217;s JVM was the Classic VM of JDK 1.0 and 1.1, and the &quot;Handleless Objects&quot; section of &lt;a href=&quot;https://www.oracle.com/java/technologies/whitepaper.html&quot;&gt;The Java HotSpot Performance Engine Architecture&lt;/a&gt; explains that the Classic VM used indirect handles. According to the same section, HotSpot, which today&amp;#8217;s Oracle JDK and OpenJDK share, does not use handles and implements object references as direct pointers to objects.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;How arguments are passed in a method call is described in Section &lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se26/html/jls-8.html#jls-8.4.1&quot;&gt;8.4.1. Formal Parameters&lt;/a&gt;.
It says only that the &quot;values&quot; of the argument expressions initialize newly created parameter variables. Nothing in it says that a parameter becomes an alias of the caller&amp;#8217;s variable, as call by reference was defined above.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When the method or constructor is invoked (§15.12), the values of the actual argument expressions initialize newly created parameter variables, each of the declared type, before execution of the body of the method or constructor.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Java Language Specification&lt;br&gt;
&lt;cite&gt;8.4.1. Formal Parameters&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;a_new_frame_for_every_method_invocation&quot;&gt;A New Frame for Every Method Invocation&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The JLS also describes, step by step, how a method invocation is executed. Section &lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se26/html/jls-15.html#jls-15.12.4.5&quot;&gt;15.12.4.5. Create Frame, Synchronize, Transfer Control&lt;/a&gt; says that once the method to invoke has been determined, a new activation frame is created and the argument values are placed in it.
An activation frame is a region of memory that holds the execution state of a method call, such as its parameters, local variables, and return address, and that disappears when the call ends.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Now a new activation frame is created, containing the target reference (if any) and the argument values (if any), as well as enough space for the local variables and stack for the method to be invoked &amp;#8230;&amp;#8203; The effect of this is to assign the argument values to corresponding freshly created parameter variables of the method, and to make the target reference available as this, if there is a target reference.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Java Language Specification&lt;br&gt;
&lt;cite&gt;15.12.4.5. Create Frame, Synchronize, Transfer Control&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The argument &quot;values&quot; are assigned to parameter variables freshly created in the new frame, not to the caller&amp;#8217;s variables. This reaches the same conclusion as Section 8.4.1. The reference to the object on which an instance method is invoked (the target reference) is placed in the new frame in the same way and becomes &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The JVMS defines this frame at the virtual machine level. Section &lt;a href=&quot;https://docs.oracle.com/en/java/javase/26/docs/specs/jvms/jvms-2.html#jvms-2.6&quot;&gt;2.6. Frames&lt;/a&gt; says that a new frame is created on the thread&amp;#8217;s JVM stack every time a method is invoked, and that each frame has its own array of local variables.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;A new frame is created each time a method is invoked. &amp;#8230;&amp;#8203; Frames are allocated from the Java Virtual Machine stack (§2.5.2) of the thread creating the frame. Each frame has its own array of local variables (§2.6.1), &amp;#8230;&amp;#8203;&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Java Virtual Machine Specification&lt;br&gt;
&lt;cite&gt;2.6. Frames&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Parameters are passed through this local variable array, as Section &lt;a href=&quot;https://docs.oracle.com/en/java/javase/26/docs/specs/jvms/jvms-2.html#jvms-2.6.1&quot;&gt;2.6.1. Local Variables&lt;/a&gt; explains.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The Java Virtual Machine uses local variables to pass parameters on method invocation. On class method invocation, any parameters are passed in consecutive local variables starting from local variable 0. On instance method invocation, local variable 0 is always used to pass a reference to the object on which the instance method is being invoked (this in the Java programming language). Any parameters are subsequently passed in consecutive local variables starting from local variable 1.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Java Virtual Machine Specification&lt;br&gt;
&lt;cite&gt;2.6.1. Local Variables&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Let&amp;#8217;s trace how the reference value of &lt;code&gt;myTv&lt;/code&gt; in the earlier &lt;code&gt;reassign&lt;/code&gt; example is copied into the parameter &lt;code&gt;targetTv&lt;/code&gt; within this structure. Two concepts are needed first.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Operand stack: a last-in, first-out stack, one per frame, that serves as the workspace where JVM instructions pop operands and push results. It is also used to prepare the arguments passed to a method.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Bytecode: the JVM instructions that javac compiles Java source into and stores in class files. Two instructions appear in this example&amp;#8217;s call sequence.&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/26/docs/specs/jvms/jvms-6.html#jvms-6.5.aload_n&quot;&gt;&lt;code&gt;aload_1&lt;/code&gt;&lt;/a&gt;: pushes the reference value held in local variable 1 of the current frame onto the operand stack.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/26/docs/specs/jvms/jvms-6.html#jvms-6.5.invokestatic&quot;&gt;&lt;code&gt;invokestatic&lt;/code&gt;&lt;/a&gt;: invokes a static method. It pops the argument values from the operand stack, creates a new frame, and stores those values in the new frame&amp;#8217;s local variables starting from local variable 0.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;With these concepts, the call to &lt;code&gt;reassign&lt;/code&gt; looks like the figure below. Because &lt;code&gt;reassign&lt;/code&gt; is a static method, the parameter &lt;code&gt;targetTv&lt;/code&gt; is local variable 0 of the &lt;code&gt;reassign&lt;/code&gt; frame. The reference value held by &lt;code&gt;myTv&lt;/code&gt;, which javac assigned to local variable 1 of the &lt;code&gt;main&lt;/code&gt; frame, is copied into this slot. The JVMS dictates that parameters start at slot 0, but the compiler decides which slot each non-parameter local variable occupies. At the bytecode level, the &lt;code&gt;aload_1&lt;/code&gt; instruction pushes this reference value onto the operand stack of the &lt;code&gt;main&lt;/code&gt; frame, and the &lt;code&gt;invokestatic&lt;/code&gt; instruction pops it and stores it in local variable 0 of the new frame. The two slots are separate storage belonging to different frames, so assigning a new object to &lt;code&gt;targetTv&lt;/code&gt; leaves &lt;code&gt;myTv&lt;/code&gt; in the &lt;code&gt;main&lt;/code&gt; frame pointing to the original object.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;img/call-by-value/java-stack-frames.png&quot; alt=&quot;The reassign frame and the main frame each have their own local variable array&quot; width=&quot;800&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Keep in mind that the JVMS defines an abstract machine. It is a model that specifies the rules for how instructions behave, not the memory layout of a particular implementation. The introduction to Chapter 2 of the JVMS leaves the memory layout of run-time data areas to the implementor&amp;#8217;s discretion. For example, machine code produced by a JIT compiler may keep parameters in CPU registers. Even so, the language semantics, in which the caller&amp;#8217;s variable and the parameter are separate variables, do not change.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;kotlin&quot;&gt;Kotlin&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Kotlin does not support call by reference either. In Kotlin, however, you cannot even run this article&amp;#8217;s test of whether assigning a new value to a parameter changes the caller&amp;#8217;s variable. The Function declaration section of the &lt;a href=&quot;https://kotlinlang.org/spec/declarations.html#function-declaration&quot;&gt;Kotlin Language Specification&lt;/a&gt; states that each parameter is introduced as the name of a value inside the function body, so parameters are final and cannot be changed inside the function.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Each parameter p&lt;sub&gt;i&lt;/sub&gt;: P&lt;sub&gt;i&lt;/sub&gt; = v&lt;sub&gt;i&lt;/sub&gt; introduces p&lt;sub&gt;i&lt;/sub&gt; as a name of value with type P&lt;sub&gt;i&lt;/sub&gt; available inside function body b; therefore, parameters are final and cannot be changed inside the function.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Kotlin Language Specification&lt;br&gt;
&lt;cite&gt;Function declaration&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Code that assigns a new value to a parameter is a compile error.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Reassigning a parameter and changing object state in Kotlin&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;class Tv(var channel: String)

fun main() {
    val myTv = Tv(&quot;Classic&quot;)
    changeState(myTv)
    println(myTv.channel) // Rock
}

fun reassign(targetTv: Tv) {
    targetTv = Tv(&quot;Rock&quot;) // &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;
}

fun changeState(targetTv: Tv) {
    targetTv.channel = &quot;Rock&quot; // &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;This is a compile error with the message &quot;&apos;val&apos; cannot be reassigned.&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Changing a property of the object the parameter points to makes the new value visible to the caller. If you delete the &lt;code&gt;reassign&lt;/code&gt; function that fails to compile and run the code, it prints &lt;code&gt;Rock&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Apart from reassignment being forbidden, Kotlin behaves like Java. On the JVM, what is passed is a copy of the reference value, as in Java. Because final parameters are part of the language specification, this does not change when you compile to non-JVM platforms such as Kotlin/JS or Kotlin/Native.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;javascript&quot;&gt;JavaScript&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;JavaScript behaves the same way as Java. Here is the swap example first.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;JavaScript swap function&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;function swap(x, y) {
  const temp = x;
  x = y;
  y = temp;
}

let x1 = &quot;Hello&quot;;
let y1 = &quot;World&quot;;
swap(x1, y1);
console.log(x1); // Hello&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When an object is passed, reassigning the parameter or changing the object&amp;#8217;s state inside the function also gives the same results as in Java.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Reassignment and state change when passing an object in JavaScript&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;const myTv = { channel: &quot;Classic&quot; };

reassign(myTv);
console.log(myTv.channel); // Classic

changeState(myTv);
console.log(myTv.channel); // Rock

function reassign(targetTv) {
  targetTv = { channel: &quot;Rock&quot; }; // &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;
}

function changeState(targetTv) {
  targetTv.channel = &quot;Rock&quot;; // &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Assigning a new object to the parameter &lt;code&gt;targetTv&lt;/code&gt; does not change the caller&amp;#8217;s &lt;code&gt;myTv&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Changing a property of the object the parameter points to makes the new value visible through the caller&amp;#8217;s &lt;code&gt;myTv&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Replace the &lt;code&gt;Tv&lt;/code&gt; object in the two Java figures above with an object literal, and they describe JavaScript&amp;#8217;s behavior.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#passing_arguments&quot;&gt;Functions page on MDN&lt;/a&gt; summarizes this behavior in the same terms: arguments are always passed by value, and object arguments are, more precisely, passed by sharing.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Arguments are always passed by value and never passed by reference. This means that if a function reassigns a parameter, the value won&amp;#8217;t change outside the function. More precisely, object arguments are passed by sharing, which means if the object&amp;#8217;s properties are mutated, the change will impact the outside of the function.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; MDN Web Docs&lt;br&gt;
&lt;cite&gt;Functions - Passing arguments&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The ECMAScript specification also describes this behavior as initializing new parameter bindings with argument values, not as creating aliases of the caller&amp;#8217;s variables. &lt;a href=&quot;https://tc39.es/ecma262/2026/multipage/ecmascript-language-expressions.html#sec-argument-lists-runtime-semantics-argumentlistevaluation&quot;&gt;ArgumentListEvaluation&lt;/a&gt; obtains values from the argument expressions with &lt;code&gt;GetValue&lt;/code&gt; and builds a list of ECMAScript language values, and &lt;a href=&quot;https://tc39.es/ecma262/2026/multipage/ordinary-and-exotic-objects-behaviours.html#sec-functiondeclarationinstantiation&quot;&gt;FunctionDeclarationInstantiation&lt;/a&gt; initializes the parameter bindings of the new function environment from that list.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Unlike Java, the ECMAScript specification does not call the value used to access an object a &quot;reference value.&quot; Object is one of the eight ECMAScript language types, the kinds of values a program manipulates directly, and an object itself is a value of that type. The &quot;copied reference value&quot; in this article is therefore best understood as a model for explaining the observed behavior.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;TypeScript passes arguments the same way as JavaScript. The &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html#runtime-behavior&quot;&gt;TypeScript Handbook&lt;/a&gt; states that, as a principle, TypeScript does not change the runtime behavior of JavaScript code.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;As a principle, TypeScript never changes the runtime behavior of JavaScript code.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; TypeScript Handbook&lt;br&gt;
&lt;cite&gt;TypeScript for the New Programmer - Runtime Behavior&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;After type checking, the TypeScript compiler erases the type information and emits plain JavaScript, so argument passing is identical to the JavaScript behavior described above. TypeScript also has no syntax for reference parameters like C#&apos;s &lt;code&gt;ref&lt;/code&gt; and &lt;code&gt;out&lt;/code&gt;, which are covered later.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;the_by_reference_wording_in_javascript_the_definitive_guide&quot;&gt;The By Reference Wording in JavaScript: The Definitive Guide&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Some sources make it easy to mistake JavaScript function calls for call by reference if you read only part of them. &lt;a href=&quot;https://docstore.mik.ua/orelly/webprog/jscript/ch11_02.htm&quot;&gt;&lt;strong&gt;JavaScript: The Definitive Guide&lt;/strong&gt;, 4th edition&lt;/a&gt;, written by David Flanagan and long regarded as the standard JavaScript book, has a summary table in Section 11.2, &quot;By Value Versus by Reference,&quot; stating that objects are copied, passed, and compared by reference. From that table alone, it looks as if you could describe JavaScript as call by reference.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Read the explanation of the behavior in the same section, however, and the book&amp;#8217;s &quot;by reference&quot; turns out to mean something different from the call by reference defined at the beginning of this article. The book uses the phrase to mean that a reference to the object is passed instead of the whole object value being copied, and it also explains that reassigning the parameter is not reflected in the caller&amp;#8217;s variable.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;A function can use the reference to modify properties of the object or elements of the array. But if the function overwrites the reference with a reference to a new object or array, that modification is not visible outside of the function. Readers familiar with the other meaning of this term may prefer to say that objects and arrays are passed by value, but the value that is passed is actually a reference rather than the object itself.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; David Flanagan&lt;br&gt;
&lt;cite&gt;JavaScript: The Definitive Guide, 4th edition, 11.2 By Value Versus by Reference&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The example that follows this explanation is titled &quot;References themselves are passed by value.&quot; The observed behavior the book describes is the same as in this article; only the name for what is passed differs.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;c&quot;&gt;C&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;C has no syntax like C&amp;#43;&amp;#43; reference parameters, so it has only call by value. Paragraph 4 of 6.5.2.2 Function calls in the C11 working draft &lt;a href=&quot;https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf&quot;&gt;N1570&lt;/a&gt; states that, in preparing a function call, the arguments are evaluated and each parameter is assigned the value of the corresponding argument.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In preparing for the call to a function, the arguments are evaluated, and each parameter is assigned the value of the corresponding argument.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; C11 Working Draft N1570&lt;br&gt;
&lt;cite&gt;6.5.2.2 Function calls, paragraph 4&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Footnote 93, attached to the same paragraph, explains that a function may change the values of its parameters, but these changes cannot affect the values of the arguments; on the other hand, it is possible to pass a pointer to an object, and the function may then change the value of the object pointed to.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;swap implemented with C pointer parameters&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;void swap(int* x, int* y) {
    int temp = *x;
    *x = *y; // &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;
    *y = temp;
}

int a = 1;
int b = 2;
swap(&amp;amp;a, &amp;amp;b); // &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Writing to the variable the pointer points to changes the caller&amp;#8217;s &lt;code&gt;a&lt;/code&gt;. Assigning a different address to &lt;code&gt;x&lt;/code&gt;, however, leaves the caller&amp;#8217;s &lt;code&gt;a&lt;/code&gt; unchanged.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;&amp;amp;&lt;/code&gt; is just the address-of operator, and copies of the pointer values are assigned to the parameters &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;. After the call, &lt;code&gt;a&lt;/code&gt; is 2 and &lt;code&gt;b&lt;/code&gt; is 1.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Applying the test from above, assigning a new value to a pointer parameter does not change the caller&amp;#8217;s variable, so this is call by value. It has the same structure as Java passing a reference value to an object.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;c_2&quot;&gt;C&amp;#43;&amp;#43;&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;C&amp;#43;&amp;#43; provides reference parameters, which are call by reference as this article defines it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;swap implemented with C&amp;#43;&amp;#43; reference parameters&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;cpp&quot;&gt;void swap(int&amp;amp; x, int&amp;amp; y) { // &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;
    int temp = x;
    x = y; // &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;
    y = temp;
}

int a = 1;
int b = 2;
swap(a, b); // &lt;b class=&quot;conum&quot;&gt;(3)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Declaring the parameter with type &lt;code&gt;int&amp;amp;&lt;/code&gt; makes &lt;code&gt;x&lt;/code&gt; an alias of the caller&amp;#8217;s variable &lt;code&gt;a&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An assignment inside the function actually changes the caller&amp;#8217;s variable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unlike C&amp;#8217;s &lt;code&gt;swap(&amp;amp;a, &amp;amp;b)&lt;/code&gt;, nothing at the call site indicates that an address is passed. After the call, &lt;code&gt;a&lt;/code&gt; is 2 and &lt;code&gt;b&lt;/code&gt; is 1.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;pointer_parameters_vs_reference_parameters&quot;&gt;Pointer Parameters vs. Reference Parameters&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;C&amp;#43;&amp;#43; also has the pointer parameters (for example, &lt;code&gt;int*&lt;/code&gt;) inherited from C, so the two need to be distinguished. Pointer parameters are call by value in C&amp;#43;&amp;#43; too. A function that takes pointers, such as &lt;code&gt;void swap(int* x, int* y)&lt;/code&gt;, receives copies of the pointer values in its parameters, exactly as in the C example. Assigning a different address to &lt;code&gt;x&lt;/code&gt; inside the function is not reflected in the caller&amp;#8217;s variable.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;An &lt;code&gt;int&amp;amp;&lt;/code&gt; reference parameter, on the other hand, has no syntax for reseating it at all. &lt;code&gt;x = y;&lt;/code&gt; is not a statement that rebinds the reference to another variable; it assigns a value to the object the reference refers to. Applying the test from above to the two cases gives different results. Assigning to a pointer parameter (&lt;code&gt;int*&lt;/code&gt;) leaves the caller&amp;#8217;s variable unchanged, while assigning to a reference parameter (&lt;code&gt;int&amp;amp;&lt;/code&gt;) changes it.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;references_in_the_c_standard&quot;&gt;References in the C&amp;#43;&amp;#43; Standard&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The C&amp;#43;&amp;#43; standard also describes a reference as an alias, not as a value that is passed. A note in &lt;a href=&quot;https://timsong-cpp.github.io/cppwp/n4950/dcl.ref&quot;&gt;[dcl.ref]&lt;/a&gt; says that a reference can be thought of as &quot;a name of an object.&quot; Paragraph 4 of the same section explicitly leaves unspecified whether a reference requires storage.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;It is unspecified whether or not a reference requires storage ([basic.stc]).&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; C&amp;#43;&amp;#43; Working Draft N4950&lt;br&gt;
&lt;cite&gt;[dcl.ref] paragraph 4&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Even though compilers usually implement references as addresses, the standard does not even specify whether a reference has storage.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you judge by what is copied at the machine level, no language would have call by reference at all. The two terms distinguish the semantics a language defines, not the compiled result.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;That said, the C&amp;#43;&amp;#43; standard does not call reference parameters call by reference. Searching the entire standard turns up no occurrence of &quot;call by value,&quot; &quot;call by reference,&quot; or &quot;pass by value,&quot; and only four occurrences of &quot;passed by reference,&quot; all in notes or library clauses rather than normative definitions. Instead of naming evaluation strategies, the standard specifies behavior. Paragraph 6 of &lt;a href=&quot;https://timsong-cpp.github.io/cppwp/n4950/expr.call&quot;&gt;[expr.call]&lt;/a&gt; says only that each parameter is initialized with its corresponding argument.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When a function is called, each parameter ([dcl.fct]) is initialized ([dcl.init], [class.copy.ctor]) with its corresponding argument.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; C&amp;#43;&amp;#43; Working Draft N4950&lt;br&gt;
&lt;cite&gt;[expr.call] paragraph 6&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;How a parameter of reference type is initialized is governed separately by the reference binding rules. Call by value and call by reference are not standard terms of any particular language; they are programming language theory terms for comparing argument-passing mechanisms across languages.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;c_3&quot;&gt;C#&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Like C&amp;#43;&amp;#43;, C# supports call by reference in its syntax. The &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/method-parameters&quot;&gt;Method parameters page of the C# reference&lt;/a&gt; states that C# passes arguments by value by default, and that a method receives a copy of the value for value types such as structs and a copy of the reference for reference types such as classes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;By default, C# passes arguments to functions by value. This approach passes a copy of the variable to the method. For value (struct) types, the method gets a copy of the value. For reference (class) types, the method gets a copy of the reference.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; C# reference&lt;br&gt;
&lt;cite&gt;Method parameters and modifiers&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The page goes on to explain that when a reference type is passed by value, reassigning the parameter inside the method does not change the caller&amp;#8217;s variable, while changing an instance member is visible through the caller&amp;#8217;s variable because both variables refer to the same instance. So far, this is the same as Java.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To pass a parameter by reference, you add the &lt;code&gt;ref&lt;/code&gt;, &lt;code&gt;out&lt;/code&gt;, &lt;code&gt;in&lt;/code&gt;, or &lt;code&gt;ref readonly&lt;/code&gt; modifier. Of these, only &lt;code&gt;ref&lt;/code&gt; and &lt;code&gt;out&lt;/code&gt; allow assigning to the caller&amp;#8217;s variable. The same page explains that a parameter passed by reference has no value of its own; it is a reference variable that refers to another variable, the referent. This is call by reference as defined in this article: the parameter becomes an alias of the caller&amp;#8217;s variable.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Swap implemented with C# ref parameters&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;void Swap(ref int x, ref int y) // &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;
{
    int temp = x;
    x = y; // &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;
    y = temp;
}

int a = 1;
int b = 2;
Swap(ref a, ref b); // &lt;b class=&quot;conum&quot;&gt;(3)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The parameter &lt;code&gt;x&lt;/code&gt;, declared with the &lt;code&gt;ref&lt;/code&gt; modifier, becomes an alias of the caller&amp;#8217;s variable &lt;code&gt;a&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Assigning to the parameter changes the caller&amp;#8217;s variable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unlike C&amp;#43;&amp;#43;, the call site also needs &lt;code&gt;ref&lt;/code&gt;. After the call, &lt;code&gt;a&lt;/code&gt; is 2 and &lt;code&gt;b&lt;/code&gt; is 1.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Because a &lt;code&gt;ref&lt;/code&gt; parameter requires &lt;code&gt;ref&lt;/code&gt; at both the method declaration and the call site, you can tell from the call expression alone that the caller&amp;#8217;s variable may change, unlike with C&amp;#43;&amp;#43; reference parameters. &lt;code&gt;out&lt;/code&gt; is a variant in which the caller passes an uninitialized variable and the method must assign a value to it, and &lt;code&gt;in&lt;/code&gt; and &lt;code&gt;ref readonly&lt;/code&gt; are read-only references that the method cannot modify.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;ref&lt;/code&gt; also works with variables of reference types. Passing a class instance as is copies the reference, as in Java, so reassigning the parameter is not reflected in the caller&amp;#8217;s variable. Passing it with &lt;code&gt;ref&lt;/code&gt; makes the parameter an alias of the caller&amp;#8217;s variable, so reassignment can change which object the caller&amp;#8217;s variable points to.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Replacing the caller&amp;#8217;s reference-type variable with a C# ref parameter&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;var myTv = new Tv();
myTv.Channel = &quot;Classic&quot;;

Replace(ref myTv); // &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;
Console.WriteLine(myTv.Channel); // Rock

void Replace(ref Tv targetTv)
{
    targetTv = new Tv(); // &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;
    targetTv.Channel = &quot;Rock&quot;;
}

class Tv
{
    public string Channel { get; set; } = &quot;&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;If you declare it as &lt;code&gt;Replace(Tv targetTv)&lt;/code&gt; without &lt;code&gt;ref&lt;/code&gt; and call &lt;code&gt;Replace(myTv)&lt;/code&gt;, it behaves like Java&amp;#8217;s &lt;code&gt;reassign(myTv)&lt;/code&gt; and prints &lt;code&gt;Classic&lt;/code&gt;. If the declaration has &lt;code&gt;ref&lt;/code&gt; and you omit only the &lt;code&gt;ref&lt;/code&gt; at the call site, it is a compile error.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Assigning a new object to the &lt;code&gt;ref&lt;/code&gt; parameter makes the caller&amp;#8217;s &lt;code&gt;myTv&lt;/code&gt; point to the new object. Running it on .NET 8 prints &lt;code&gt;Rock&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;C# distinguishes in its syntax between &quot;passing a reference type by value&quot; and &quot;passing a variable by reference with ref,&quot; two different behaviors.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;python&quot;&gt;Python&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Like Java, Python has only call by value. Section &lt;a href=&quot;https://docs.python.org/3/tutorial/controlflow.html#defining-functions&quot;&gt;4.8 Defining Functions&lt;/a&gt; of the official tutorial explains that arguments are introduced into the local symbol table of the called function, so they are passed using call by value, where the value is always an object reference.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The actual parameters (arguments) to a function call are introduced in the local symbol table of the called function when it is called; thus, arguments are passed using call by value (where the value is always an object reference, not the value of the object).&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; The Python Tutorial&lt;br&gt;
&lt;cite&gt;4.8 Defining Functions&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;A footnote on this sentence adds that &quot;call by object reference&quot; would be a better description, because when a mutable object is passed, the caller sees any changes made to it. The &lt;a href=&quot;https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference&quot;&gt;Python FAQ&lt;/a&gt; answers more directly: arguments are passed by assignment, and since assignment just creates references to objects, there is no alias between an argument name in the caller and in the callee, and so no call by reference.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Python swap function and passing an object&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;def swap(x, y):
    x, y = y, x  # &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;

a, b = 1, 2
swap(a, b)
print(a)  # 1


class Tv:
    def __init__(self, channel):
        self.channel = channel


def main():
    my_tv = Tv(&quot;Classic&quot;)
    reassign(my_tv)
    print(my_tv.channel)  # Classic
    changeState(my_tv)
    print(my_tv.channel)  # Rock


def reassign(targetTv):
    targetTv = Tv(&quot;Rock&quot;)  # &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;


def changeState(targetTv):
    targetTv.channel = &quot;Rock&quot;  # &lt;b class=&quot;conum&quot;&gt;(3)&lt;/b&gt;


main()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;This only rebinds the local names &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; to different objects, so the caller&amp;#8217;s &lt;code&gt;a&lt;/code&gt; stays 1.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rebinding the parameter &lt;code&gt;targetTv&lt;/code&gt; to a new object leaves the caller&amp;#8217;s &lt;code&gt;my_tv&lt;/code&gt; unchanged, and &lt;code&gt;Classic&lt;/code&gt; is printed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Changing an attribute of the object &lt;code&gt;targetTv&lt;/code&gt; points to makes the change visible through the caller&amp;#8217;s &lt;code&gt;my_tv&lt;/code&gt;, and &lt;code&gt;Rock&lt;/code&gt; is printed.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The results are the same as Java&amp;#8217;s &lt;code&gt;Tv&lt;/code&gt; example and JavaScript&amp;#8217;s &lt;code&gt;reassign&lt;/code&gt; and &lt;code&gt;changeState&lt;/code&gt; examples.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Because the caller&amp;#8217;s variable stays the same when you pass an integer or a string, but appears changed when you pass a list or a dictionary, it is easy to assume the passing rule depends on the type. The rule is the same. Integers and strings are immutable objects, so there is simply no way to change their internal state. Even with a list, rebinding the parameter inside the function, as in &lt;code&gt;x = [1]&lt;/code&gt;, leaves the caller unchanged.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;go&quot;&gt;Go&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Go does not make the caller&amp;#8217;s variable an implicit alias of a parameter in a function call either. The Calls section of the &lt;a href=&quot;https://go.dev/ref/spec#Calls&quot;&gt;Go specification&lt;/a&gt; says that after the function value and arguments are evaluated, new storage is allocated for the function&amp;#8217;s variables, including its parameters and results, and the arguments are assigned to the corresponding parameters. The &lt;a href=&quot;https://go.dev/doc/faq#pass_by_value&quot;&gt;Go FAQ&lt;/a&gt; answers more directly.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;As in all languages in the C family, everything in Go is passed by value. That is, a function always gets a copy of the thing being passed, as if there were an assignment statement assigning the value to the parameter.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Go FAQ&lt;br&gt;
&lt;cite&gt;When are function parameters passed by value?&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Taken literally, &quot;all languages in the C family&quot; is not accurate. C&amp;#43;&amp;#43; and C#, covered in this article, belong to the C family but support reference parameters in their syntax. Both languages still pass by value by default, though, and passing by reference is an exception that requires a modifier. The sentence is best read as &quot;the default in C-family languages is pass by value, and Go has only that default.&quot; As in C, Go requires you to pass a pointer explicitly to change the caller&amp;#8217;s variable.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;swap implemented with Go pointer parameters&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;func swap(x, y *int) {
    *x, *y = *y, *x // &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;
}

a, b := 1, 2
swap(&amp;amp;a, &amp;amp;b) // &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Writing to the variables the pointers point to changes the caller&amp;#8217;s &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;. Assigning a different pointer to &lt;code&gt;x&lt;/code&gt; inside the function does not change the caller&amp;#8217;s &lt;code&gt;a&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To change the caller&amp;#8217;s values with an ordinary function, you pass pointers as in C. Copies of the pointer values are assigned to the parameters &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;. After the call, &lt;code&gt;a&lt;/code&gt; is 2 and &lt;code&gt;b&lt;/code&gt; is 1.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Method calls, however, do not need &lt;code&gt;&amp;amp;&lt;/code&gt; at the call site. The &lt;a href=&quot;https://go.dev/ref/spec#Calls&quot;&gt;Calls section of the Go specification&lt;/a&gt; states that if &lt;code&gt;x&lt;/code&gt; is addressable and the method set of &lt;code&gt;&amp;amp;x&lt;/code&gt; contains &lt;code&gt;m&lt;/code&gt;, then &lt;code&gt;x.m()&lt;/code&gt; is shorthand for &lt;code&gt;(&amp;amp;x).m()&lt;/code&gt;. For methods with pointer receivers, the compiler takes the address for you under this rule, so you cannot tell from the shape of the call expression alone whether the caller&amp;#8217;s variable may change. Even then, what is passed is a pointer value.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;rust&quot;&gt;Rust&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Rust does not support call by reference in the sense defined in this article, a mechanism in which the parameter becomes an alias of the caller&amp;#8217;s variable. &lt;a href=&quot;https://doc.rust-lang.org/rust-by-example/scope/borrow.html&quot;&gt;Rust by Example&lt;/a&gt; calls passing a &lt;code&gt;&amp;amp;T&lt;/code&gt; &quot;passed by reference.&quot; A function call, however, does not create an alias of the caller&amp;#8217;s variable, and a reference such as &lt;code&gt;&amp;amp;mut T&lt;/code&gt; is itself a value that is passed by call by value. The &lt;a href=&quot;https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#ownership-and-functions&quot;&gt;Rust Book&lt;/a&gt; explains that passing a variable to a function moves or copies the value, just as assignment does.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Passing a variable to a function will move or copy, just as assignment does.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; The Rust Programming Language&lt;br&gt;
&lt;cite&gt;4.1 What is Ownership? - Ownership and Functions&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To understand &quot;move&quot; in this explanation, you need Rust&amp;#8217;s concept of ownership. The &lt;a href=&quot;https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#ownership-rules&quot;&gt;Ownership Rules section of the Rust Book&lt;/a&gt; summarizes ownership in three rules: each value has a variable that is its owner, there can be only one owner at a time, and when the owner goes out of scope, the value is dropped. An assignment such as &lt;code&gt;let s2 = s1;&lt;/code&gt; transfers ownership to &lt;code&gt;s2&lt;/code&gt;, which is called a move, and using &lt;code&gt;s1&lt;/code&gt; after the move is a compile error.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Which types are copied is determined by the &lt;code&gt;Copy&lt;/code&gt; trait.
A &lt;a href=&quot;https://doc.rust-lang.org/book/ch10-02-traits.html&quot;&gt;trait&lt;/a&gt; declares behavior shared by multiple types, similar to an interface in other languages. Some traits have no methods and only tell the compiler about a property of the type; &lt;code&gt;Copy&lt;/code&gt; is one of them.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;Copy&lt;/code&gt; is a trait for types whose original variable remains usable after its value is assigned to another variable. The &lt;a href=&quot;https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#stack-only-data-copy&quot;&gt;Stack-Only Data: Copy section of the Rust Book&lt;/a&gt; explains that variables of types implementing &lt;code&gt;Copy&lt;/code&gt; are not moved but trivially copied, so they remain valid after assignment to another variable. Integers, floating-point numbers, &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;char&lt;/code&gt;, and tuples containing only &lt;code&gt;Copy&lt;/code&gt; types belong to this group. Types that allocate heap memory or own resources, such as &lt;code&gt;String&lt;/code&gt; and &lt;code&gt;Vec&lt;/code&gt;, cannot be &lt;code&gt;Copy&lt;/code&gt;, and adding &lt;code&gt;Copy&lt;/code&gt; to a type that implements &lt;code&gt;Drop&lt;/code&gt;, which performs cleanup when a value goes out of scope, is a compile error. In effect, only types that are safe to duplicate bit for bit can be &lt;code&gt;Copy&lt;/code&gt;. So in &lt;code&gt;let t = s;&lt;/code&gt;, if &lt;code&gt;s&lt;/code&gt; is an &lt;code&gt;i32&lt;/code&gt; you can keep using &lt;code&gt;s&lt;/code&gt; afterward, but if it is a &lt;code&gt;String&lt;/code&gt; it is moved, as shown above.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;A value of a non-&lt;code&gt;Copy&lt;/code&gt; type such as &lt;code&gt;String&lt;/code&gt; has its ownership moved into the parameter, and a value of a &lt;code&gt;Copy&lt;/code&gt; type such as &lt;code&gt;i32&lt;/code&gt; is copied. When you pass a variable of type &lt;code&gt;&amp;amp;mut T&lt;/code&gt; to a parameter of type &lt;code&gt;&amp;amp;mut T&lt;/code&gt;, however, the compiler implicitly reborrows it, so you can use the variable again after the call even though &lt;code&gt;&amp;amp;mut T&lt;/code&gt; is not &lt;code&gt;Copy&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;From this article&amp;#8217;s perspective, moving and copying are the same mechanism, in which the parameter is initialized with the argument value, and neither creates an alias of the caller&amp;#8217;s variable. The only difference is whether the caller&amp;#8217;s variable remains usable after the call.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;swap implemented with Rust mutable references&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;fn swap(x: &amp;amp;mut i32, y: &amp;amp;mut i32) { // &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;
    let temp = *x;
    *x = *y; // &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;
    *y = temp;
}

let mut a = 1;
let mut b = 2;
swap(&amp;amp;mut a, &amp;amp;mut b); // &lt;b class=&quot;conum&quot;&gt;(3)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;What is passed to the parameter &lt;code&gt;x&lt;/code&gt; is a value of type &lt;code&gt;&amp;amp;mut i32&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Writing to the variable the mutable reference points to changes the caller&amp;#8217;s &lt;code&gt;a&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To change the caller&amp;#8217;s values with an ordinary function, you pass mutable references like this. After the call, &lt;code&gt;a&lt;/code&gt; is 2 and &lt;code&gt;b&lt;/code&gt; is 1.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The standard library function &lt;a href=&quot;https://doc.rust-lang.org/std/mem/fn.swap.html&quot;&gt;&lt;code&gt;std::mem::swap&lt;/code&gt;&lt;/a&gt; uses the same approach with the signature &lt;code&gt;fn swap&amp;lt;T&amp;gt;(x: &amp;amp;mut T, y: &amp;amp;mut T)&lt;/code&gt;. Passing mutable references is still not call by reference as defined in this article, in which reassigning the parameter itself changes the caller&amp;#8217;s variable binding. To reassign &lt;code&gt;x&lt;/code&gt;, you have to declare the parameter as &lt;code&gt;mut x: &amp;amp;mut i32&lt;/code&gt;, and even then the reassignment has no effect on the caller&amp;#8217;s &lt;code&gt;a&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Like Go&amp;#8217;s pointer-receiver methods, method calls in Rust do not need &lt;code&gt;&amp;amp;mut&lt;/code&gt; at the call site. Under the &lt;a href=&quot;https://doc.rust-lang.org/reference/expressions/method-call-expr.html#r-expr.method.autoref-deref&quot;&gt;method call rules in the Rust Reference&lt;/a&gt;, writing &lt;code&gt;v.push(1)&lt;/code&gt; makes the compiler automatically borrow the receiver and pass &lt;code&gt;&amp;amp;mut v&lt;/code&gt;, and what is passed is still a mutable reference value.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;ada_call_by_reference_vs_copy_restore&quot;&gt;Ada: Call by Reference vs. Copy-Restore&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The definitions section noted that the test &quot;when a function assigns a new value to its parameter, does the caller&amp;#8217;s variable change?&quot; cannot classify every passing mechanism by itself. Ada is an example. Ada declares parameters with the modes &lt;code&gt;in&lt;/code&gt;, &lt;code&gt;in out&lt;/code&gt;, and &lt;code&gt;out&lt;/code&gt;, and a value assigned to an &lt;code&gt;in out&lt;/code&gt; or &lt;code&gt;out&lt;/code&gt; parameter is reflected in the caller&amp;#8217;s variable. Judged only by the result the caller observes, this is the same as C&amp;#43;&amp;#43; reference parameters.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://ada-lang.io/docs/arm/AA-6/AA-6.2/&quot;&gt;Ada Reference Manual 6.2&lt;/a&gt;, however, decides whether a parameter is passed by copy or by reference mainly by its type, not its mode. Elementary types such as &lt;code&gt;Integer&lt;/code&gt; are by-copy types and are passed by copy; by-reference types such as tagged types are passed by reference; and a formal parameter declared &lt;code&gt;aliased&lt;/code&gt; is passed by reference regardless of its type. For parameters that fall into neither category, the manual does not specify whether they are passed by copy or by reference and leaves the choice to the implementation. For &lt;code&gt;in out&lt;/code&gt; and &lt;code&gt;out&lt;/code&gt; parameters passed by copy, &lt;a href=&quot;https://ada-lang.io/docs/arm/AA-6/AA-6.4&quot;&gt;6.4.1&lt;/a&gt; specifies that their values are copied back to the actual arguments when the subprogram completes normally. This is the call by copy-restore mentioned in the definitions section. The result, a changed caller variable, is the same as with call by reference, but the actual mechanism is copying.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The difference between passing by copy and passing by reference becomes visible when the procedure reads the variable passed as the actual argument directly. The example below passes the same variable &lt;code&gt;Original&lt;/code&gt; to a procedure that receives it by copy and to one that receives it as &lt;code&gt;aliased&lt;/code&gt;, and reads &lt;code&gt;Original&lt;/code&gt; right after assigning to the parameter.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;By-copy passing vs. aliased by-reference passing in Ada&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;ada&quot;&gt;with Ada.Text_IO; use Ada.Text_IO;

procedure Demo is
   Original : aliased Integer := 1;

   procedure By_Copy (Param : in out Integer) is
   begin
      Param := 2;
      Put_Line (&quot;inside By_Copy: Original =&quot; &amp;amp; Integer&apos;Image (Original)); -- &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;
   end By_Copy;

   procedure By_Reference (Param : aliased in out Integer) is
   begin
      Param := 2;
      Put_Line (&quot;inside By_Reference: Original =&quot; &amp;amp; Integer&apos;Image (Original)); -- &lt;b class=&quot;conum&quot;&gt;(3)&lt;/b&gt;
   end By_Reference;
begin
   By_Copy (Original);
   Put_Line (&quot;after By_Copy: Original =&quot; &amp;amp; Integer&apos;Image (Original)); -- &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;

   Original := 1;
   By_Reference (Original);
   Put_Line (&quot;after By_Reference: Original =&quot; &amp;amp; Integer&apos;Image (Original));
end Demo;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Integer&lt;/code&gt; is a by-copy type, so &lt;code&gt;Param&lt;/code&gt; is a copy of &lt;code&gt;Original&lt;/code&gt;. Right after 2 is assigned to &lt;code&gt;Param&lt;/code&gt;, &lt;code&gt;Original&lt;/code&gt; is still 1.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When the procedure completes normally, the value of &lt;code&gt;Param&lt;/code&gt; is copied back to &lt;code&gt;Original&lt;/code&gt;, and &lt;code&gt;Original&lt;/code&gt; becomes 2.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An &lt;code&gt;aliased&lt;/code&gt; parameter is passed by reference, so &lt;code&gt;Param&lt;/code&gt; is an alias of &lt;code&gt;Original&lt;/code&gt;. The moment 2 is assigned to &lt;code&gt;Param&lt;/code&gt;, &lt;code&gt;Original&lt;/code&gt; is 2 as well.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This is the output when compiled with GNAT 13.3, the Ada compiler included in GCC.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Output&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;inside By_Copy: Original = 1
after By_Copy: Original = 2
inside By_Reference: Original = 2
after By_Reference: Original = 2&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;After both calls return, the value is 2 in both cases, but reading &lt;code&gt;Original&lt;/code&gt; inside the procedures gives different results. A language supporting parameter modes in its syntax and those modes being implemented as call by reference are two separate facts.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The following table summarizes how the languages covered in this article pass arguments.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 11.1111%;&quot;&gt;
&lt;col style=&quot;width: 22.2222%;&quot;&gt;
&lt;col style=&quot;width: 33.3333%;&quot;&gt;
&lt;col style=&quot;width: 33.3334%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Language&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Supports call by reference&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Passing mechanism&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Changing the caller&amp;#8217;s variable through a parameter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Java&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Call by value. For objects, a copy of the reference value is passed&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Not possible. Only object state changes are shared&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Kotlin&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Call by value. Parameters are final, so reassignment is a compile error&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Not possible. Only object state changes are shared&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JavaScript&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Call by value. The Object value is assigned to a separate parameter binding&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Not possible. Only object state changes are shared&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;C&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Call by value&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Pass a pointer (&lt;code&gt;swap(&amp;amp;a, &amp;amp;b)&lt;/code&gt;)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;C&amp;#43;&amp;#43;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Yes, only for reference parameters (&lt;code&gt;int&amp;amp;&lt;/code&gt;)&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Call by value by default. Only reference parameters are call by reference&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Declare a reference parameter. You can also pass a pointer as in C&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;C#&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Yes, with &lt;code&gt;ref&lt;/code&gt;, &lt;code&gt;out&lt;/code&gt;, &lt;code&gt;in&lt;/code&gt;, and &lt;code&gt;ref readonly&lt;/code&gt; parameters. Of these, &lt;code&gt;ref&lt;/code&gt; and &lt;code&gt;out&lt;/code&gt; allow assigning to the parameter&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Call by value by default. For class instances, a copy of the reference is passed&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Declare a &lt;code&gt;ref&lt;/code&gt; or &lt;code&gt;out&lt;/code&gt; parameter. The call site also needs &lt;code&gt;ref&lt;/code&gt; or &lt;code&gt;out&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Python&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Call by value. The value is always an object reference&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Not possible. Only object state changes are shared&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Go&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Call by value. Map and slice values behave like pointers&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Pass a pointer (&lt;code&gt;swap(&amp;amp;a, &amp;amp;b)&lt;/code&gt;). Pointer-receiver methods take the address automatically&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Rust&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Call by value. Depending on the type, the value is moved or copied as in assignment, and a mutable reference &lt;code&gt;&amp;amp;mut T&lt;/code&gt; is also passed by value&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Pass a mutable reference (&lt;code&gt;swap(&amp;amp;mut a, &amp;amp;mut b)&lt;/code&gt;). Method calls borrow the receiver automatically&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Ada&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Partially. By-reference types and parameters declared &lt;code&gt;aliased&lt;/code&gt; are passed by reference regardless of mode. For other types, the manual does not specify. &lt;code&gt;in out&lt;/code&gt; and &lt;code&gt;out&lt;/code&gt; parameters of by-copy types are copied back on normal completion (copy-restore)&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;By copy or by reference, depending on the type&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Declare an &lt;code&gt;in out&lt;/code&gt; or &lt;code&gt;out&lt;/code&gt; mode&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Java, Kotlin, JavaScript, and Python initialize a separate parameter variable or binding with the argument value. They do not support call by reference, in which the caller&amp;#8217;s variable itself becomes an alias of the parameter.&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Passing an object neither clones the object nor passes the caller&amp;#8217;s variable itself. In Java, the reference value that points to the object, and in JavaScript, the Object value, is passed to a separate parameter, and the caller and callee can observe the same object.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reassigning a parameter inside a function does not change the caller&amp;#8217;s variable. Changing the internal state of the object the parameter points to, on the other hand, is visible through the caller&amp;#8217;s variable. This difference is the source of the misconception that &quot;objects are call by reference.&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;C and Go pass pointers, and Rust passes mutable references, to change the caller&amp;#8217;s values. A call that takes the address or reference of a variable on the spot shows &lt;code&gt;&amp;amp;&lt;/code&gt; or &lt;code&gt;&amp;amp;mut&lt;/code&gt;, but when you pass a variable that already holds a pointer or reference, or when the compiler takes the address or borrows for you as in Go and Rust method calls, you cannot tell from the shape of the call expression alone. The only languages here in which a parameter becomes an alias of the caller&amp;#8217;s variable are C&amp;#43;&amp;#43;, C#, and Ada, and Ada sometimes passes by copy and copies back depending on the type.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This object-sharing behavior is sometimes called call by sharing. Some sources, such as &lt;strong&gt;JavaScript: The Definitive Guide&lt;/strong&gt;, 4th edition, describe object sharing as &quot;by reference,&quot; but it should be distinguished from call by reference in the sense of passing an alias of the caller&amp;#8217;s variable.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Terminology&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Alfred V. Aho, Monica S. Lam, Ravi Sethi, Jeffrey D. Ullman, &lt;a href=&quot;https://www.pearson.com/en-us/subject-catalog/p/compilers-principles-techniques-and-tools/P200000003472/9780133002140&quot;&gt;&lt;strong&gt;Compilers: Principles, Techniques, and Tools&lt;/strong&gt;&lt;/a&gt;, 2nd edition, Addison-Wesley, 2007, 1.6.6 Parameter Passing Mechanisms&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Christopher Strachey, &lt;a href=&quot;https://www.cs.cmu.edu/~crary/819-f09/Strachey67.pdf&quot;&gt;&lt;strong&gt;Fundamental Concepts in Programming Languages&lt;/strong&gt;&lt;/a&gt;, Higher-Order and Symbolic Computation, Vol. 13, 2000, 3.4.2 Parameter calling modes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.masswerk.at/algol60/report.htm&quot;&gt;Revised Report on the Algorithmic Language Algol 60 - 4.7.3 Semantics&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://csg.csail.mit.edu/pubs/memos/Memo-112/memo112-1a.pdf&quot;&gt;CLU design document - Call by sharing&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Java&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se26/html/jls-8.html#jls-8.4.1&quot;&gt;Java Language Specification - 8.4.1. Formal Parameters&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se26/html/jls-4.html#jls-4.3.1&quot;&gt;Java Language Specification - 4.3.1. Objects&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se26/html/jls-15.html#jls-15.12.4.5&quot;&gt;Java Language Specification - 15.12.4.5. Create Frame, Synchronize, Transfer Control&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/26/docs/specs/jvms/jvms-2.html#jvms-2.6&quot;&gt;Java Virtual Machine Specification - 2.6. Frames&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/26/docs/specs/jvms/jvms-2.html#jvms-2.6.1&quot;&gt;Java Virtual Machine Specification - 2.6.1. Local Variables&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/26/docs/specs/jvms/jvms-2.html#jvms-2.7&quot;&gt;Java Virtual Machine Specification - 2.7. Representation of Objects&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jvms/se6/html/Overview.doc.html#16066&quot;&gt;Java Virtual Machine Specification (Java SE 6) - 3.7 Representation of Objects&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20001213164500/http://java.sun.com/docs/books/vmspec/html/Overview.doc.html&quot;&gt;The Java Virtual Machine Specification, first edition (1997) - 3.7 Representation of Objects (Wayback Machine)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.oracle.com/java/technologies/whitepaper.html&quot;&gt;The Java HotSpot Performance Engine Architecture - Handleless Objects&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/26/docs/specs/jvms/jvms-6.html#jvms-6.5.invokestatic&quot;&gt;Java Virtual Machine Specification - 6.5. invokestatic&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.java/learn/classes-objects/calling-methods-constructors/&quot;&gt;Dev.java - Calling Methods and Constructors&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ken Arnold, James Gosling, David Holmes, &lt;a href=&quot;https://www.informit.com/store/java-programming-language-9780321349804&quot;&gt;&lt;strong&gt;The Java Programming Language&lt;/strong&gt;&lt;/a&gt;, 4th edition, Addison-Wesley, 2006&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Kathy Sierra, Bert Bates, &lt;a href=&quot;https://www.oreilly.com/library/view/head-first-java/0596009208/&quot;&gt;&lt;strong&gt;Head First Java&lt;/strong&gt;&lt;/a&gt;, 2nd edition, O&amp;#8217;Reilly, 2005, Chapters 3 and 4&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Kotlin&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://kotlinlang.org/spec/declarations.html#function-declaration&quot;&gt;Kotlin Language Specification - Function declaration&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;JavaScript&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://tc39.es/ecma262/2026/multipage/ecmascript-language-expressions.html#sec-argument-lists-runtime-semantics-argumentlistevaluation&quot;&gt;ECMAScript Language Specification - ArgumentListEvaluation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://tc39.es/ecma262/2026/multipage/ordinary-and-exotic-objects-behaviours.html#sec-functiondeclarationinstantiation&quot;&gt;ECMAScript Language Specification - FunctionDeclarationInstantiation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#passing_arguments&quot;&gt;MDN Web Docs - Functions, Passing arguments&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html#runtime-behavior&quot;&gt;TypeScript Handbook - TypeScript for the New Programmer, Runtime Behavior&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;David Flanagan, &lt;a href=&quot;https://www.oreilly.com/library/view/javascript-the-definitive/0596000480/&quot;&gt;&lt;strong&gt;JavaScript: The Definitive Guide&lt;/strong&gt;&lt;/a&gt;, 4th edition, O&amp;#8217;Reilly, 2001, &lt;a href=&quot;https://docstore.mik.ua/orelly/webprog/jscript/ch11_02.htm&quot;&gt;11.2 By Value Versus by Reference&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;C&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf&quot;&gt;C11 Working Draft N1570 - 6.5.2.2 Function calls&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;C&amp;#43;&amp;#43;&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://timsong-cpp.github.io/cppwp/n4950/dcl.ref&quot;&gt;C&amp;#43;&amp;#43; Working Draft N4950 - [dcl.ref] References&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://timsong-cpp.github.io/cppwp/n4950/expr.call&quot;&gt;C&amp;#43;&amp;#43; Working Draft N4950 - [expr.call] Function call&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;C#&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/method-parameters#reference-parameters&quot;&gt;C# Reference - Reference parameters&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Python&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.python.org/3/tutorial/controlflow.html#defining-functions&quot;&gt;The Python Tutorial - 4.8 Defining Functions&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference&quot;&gt;Python Programming FAQ - How do I write a function with output parameters (call by reference)?&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://go.dev/ref/spec#Calls&quot;&gt;The Go Programming Language Specification - Calls&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://go.dev/doc/faq#pass_by_value&quot;&gt;Go FAQ - When are function parameters passed by value?&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rust&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#ownership-rules&quot;&gt;The Rust Programming Language - Ownership Rules&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#ownership-and-functions&quot;&gt;The Rust Programming Language - Ownership and Functions&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#stack-only-data-copy&quot;&gt;The Rust Programming Language - Stack-Only Data: Copy&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/book/ch10-02-traits.html&quot;&gt;The Rust Programming Language - Traits: Defining Shared Behavior&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/std/mem/fn.swap.html&quot;&gt;Rust Standard Library - std::mem::swap&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/reference/expressions/method-call-expr.html&quot;&gt;The Rust Reference - Method-call expressions&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ada&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://ada-lang.io/docs/arm/AA-6/AA-6.2/&quot;&gt;Ada Reference Manual - 6.2 Formal Parameter Modes&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://ada-lang.io/docs/arm/AA-6/AA-6.4&quot;&gt;Ada Reference Manual - 6.4 Subprogram Calls, 6.4.1 Parameter Associations&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
	</description>
    </item>
    <item>
      <title>VO vs. DTO: Definitions and the History of Conflating the Terms</title>
      <link>https://tech.benelog.net/vo-vs-dto.html</link>
      <pubDate>Sat, 3 Oct 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="false">vo-vs-dto.html</guid>
      	<description>
	&lt;div id=&quot;toc&quot; class=&quot;toc&quot;&gt;
&lt;div id=&quot;toctitle&quot;&gt;Table of Contents&lt;/div&gt;
&lt;ul class=&quot;sectlevel1&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#definition_of_value_object&quot;&gt;Definition of Value Object&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#the_status_of_immutability_definitional_requirement_vs_good_design&quot;&gt;The Status of Immutability: Definitional Requirement vs. Good Design&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#definition_of_dto_data_transfer_object&quot;&gt;Definition of DTO (Data Transfer Object)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#vo_in_the_first_edition_of_core_j2ee_patterns_and_to_in_the_second&quot;&gt;VO in the First Edition of Core J2EE Patterns and TO in the Second&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the_cost_of_calling_a_data_carrier_a_vo&quot;&gt;The Cost of Calling a Data Carrier a VO&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#references&quot;&gt;References&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#value_object&quot;&gt;Value Object&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#transfer_object_dto&quot;&gt;Transfer Object / DTO&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div id=&quot;preamble&quot;&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In practice, you often come across objects with nothing but getters and setters, whose job is to carry values, being called VOs (Value Objects). If the object&amp;#8217;s role is to carry data across a remote call between processes, calling it a DTO (Data Transfer Object) leaves less room for confusion. Value Object is a term that Martin Fowler&amp;#8217;s writing and Eric Evans&amp;#8217;s DDD (Domain-Driven Design) have already defined with a different meaning. This article lays out the definitions of the two patterns and the history of how the terms got mixed up.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;definition_of_value_object&quot;&gt;Definition of Value Object&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;A Value Object is an object that has no identity and whose equality is determined by the values it holds.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Identity is the property that distinguishes one object from another regardless of its attribute values. A member is still the same member after their name or address changes, and two members with the same name and address are still different people. Such objects are distinguished by an identifier like a member number, and DDD calls them ENTITIES.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Equality is the criterion for deciding whether two objects count as the same. A Value Object bases this criterion on the values it holds rather than on an identifier. Concepts like money, color, and date are typical examples. For instance, &lt;code&gt;Instant&lt;/code&gt; in Java, which represents a point on the timeline, compares equal with &lt;code&gt;equals()&lt;/code&gt; when two instances refer to the same instant, even if they were created separately by different means.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;Instant fromText = Instant.parse(&quot;2026-09-24T20:00:00Z&quot;);
Instant fromSeoulTime = ZonedDateTime.of(2026, 9, 25, 5, 0, 0, 0, ZoneId.of(&quot;Asia/Seoul&quot;))
    .toInstant();

assertThat(fromText).isEqualTo(fromSeoulTime); // 20:00 on the 24th UTC and 05:00 on the 25th in Seoul are the same instant&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This definition can be confirmed in the three sources below.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://martinfowler.com/bliki/ValueObject.html&quot;&gt;Martin Fowler&amp;#8217;s article&lt;/a&gt;: objects regarded as equal because their attribute values are equal. He explains this using a point made up of x and y coordinates as the example.&lt;/p&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Objects that are equal due to the value of their properties, in this case their x and y coordinates, are called value objects.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Value_object&quot;&gt;Wikipedia&lt;/a&gt;: an object whose equality is not based on identity, and which counts as the same when it holds the same values&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/implement-value-objects&quot;&gt;Microsoft&amp;#8217;s .NET architecture documentation&lt;/a&gt;: an object with no identity&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Eric Evans takes the same view. In the Value Objects entry of the &lt;a href=&quot;https://www.domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf&quot;&gt;Domain-Driven Design Reference&lt;/a&gt; (2015), which Evans published as a summary of the pattern definitions in &lt;strong&gt;Domain-Driven Design&lt;/strong&gt;, the problem statement presupposes that many objects have no conceptual identity, and it says to classify a model element as a value object when you care only about its attributes and logic.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Some objects describe or compute some characteristic of a thing. Many objects have no conceptual identity. &amp;#8230;&amp;#8203;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Therefore:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When you care only about the attributes and logic of an element of the model, classify it as a value object. Make it express the meaning of the attributes it conveys and give it related functionality.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Eric Evans&lt;br&gt;
&lt;cite&gt;Domain-Driven Design Reference: Value Objects&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In Java code, a class that expresses a value concept becomes a Value Object when it implements &lt;code&gt;equals()&lt;/code&gt; and &lt;code&gt;hashCode()&lt;/code&gt; on the attributes that serve as the criterion for equality. The contracts of these two methods that must be honored are laid out in Item 10 and Item 11 of Joshua Bloch&amp;#8217;s &lt;strong&gt;Effective Java&lt;/strong&gt;, 3rd ed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Item 10: the general contract of &lt;code&gt;equals()&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Reflexive: &lt;code&gt;x.equals(x)&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Symmetric: if &lt;code&gt;x.equals(y)&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt;, then &lt;code&gt;y.equals(x)&lt;/code&gt; also returns &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Transitive: if &lt;code&gt;x.equals(y)&lt;/code&gt; and &lt;code&gt;y.equals(z)&lt;/code&gt; return &lt;code&gt;true&lt;/code&gt;, then &lt;code&gt;x.equals(z)&lt;/code&gt; also returns &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Consistent: as long as the information used in the comparison does not change, &lt;code&gt;x.equals(y)&lt;/code&gt; returns the same result no matter how many times it is called.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Non-null: &lt;code&gt;x.equals(null)&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Item 11: when you override &lt;code&gt;equals()&lt;/code&gt;, also override &lt;code&gt;hashCode()&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;As long as the information used in &lt;code&gt;equals()&lt;/code&gt; comparisons does not change, &lt;code&gt;hashCode()&lt;/code&gt; returns the same value no matter how many times it is called.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Two objects that are equal according to &lt;code&gt;equals()&lt;/code&gt; return the same &lt;code&gt;hashCode()&lt;/code&gt; value.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Two objects that are unequal according to &lt;code&gt;equals()&lt;/code&gt; are not required to return different &lt;code&gt;hashCode()&lt;/code&gt; values, but returning different values improves the performance of hash tables.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;With records, which became a standard feature in Java 16, you no longer have to write these two methods yourself. &lt;a href=&quot;https://openjdk.org/jeps/395&quot;&gt;JEP 395&lt;/a&gt;, which introduced records, states that a record&amp;#8217;s &lt;code&gt;equals()&lt;/code&gt; and &lt;code&gt;hashCode()&lt;/code&gt; are generated automatically based on the values of all its components. Two record instances are equal when they have the same type and all of their component values are equal.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;public record Money(BigDecimal amount, Currency currency) {
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Two instances of this class are equal when the values they hold are the same.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;Currency krw = Currency.getInstance(&quot;KRW&quot;);
Money price1 = new Money(BigDecimal.valueOf(10000), krw);
Money price2 = new Money(BigDecimal.valueOf(10000), krw);

assertThat(price1).isEqualTo(price2); // equal when the values match, even with different references&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;However, because a record uses the &lt;code&gt;equals()&lt;/code&gt; of each component type as is, the automatically generated equality may differ from the equality the domain wants. For example, &lt;code&gt;BigDecimal&amp;#8217;s `equals()&lt;/code&gt; compares the scale as well, so &lt;code&gt;Money&lt;/code&gt; instances created from &lt;code&gt;new BigDecimal(&quot;10000&quot;)&lt;/code&gt; and &lt;code&gt;new BigDecimal(&quot;10000.0&quot;)&lt;/code&gt; end up as different values. In such cases, normalize the values in the constructor or define &lt;code&gt;equals()&lt;/code&gt; yourself.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The concept of an identity-free value object is also being introduced into the Java language and the JVM. It shares with the Value Object of DDD and Fowler the trait of being distinguished by value, without identity. Project Valhalla&amp;#8217;s &lt;a href=&quot;https://openjdk.org/jeps/401&quot;&gt;JEP 401: Value Objects (Preview)&lt;/a&gt; proposes value objects that are immutable, have no object identity, and are distinguished only by their field values. As of August 2026, this JEP has been integrated as a preview feature into JDK 28, scheduled for release in March 2027. &lt;a href=&quot;https://openjdk.org/jeps/169&quot;&gt;JEP 169: Larval State for Value Objects&lt;/a&gt; is a separate Draft proposal that deals with a temporary mutable state for these immutable value objects. That said, identity in DDD is a question of whether something needs to be conceptually distinguished in the domain, while the identity that Valhalla removes is a runtime property by which the JVM tells objects apart, so the two value objects are not the same concept. Even so, neither side uses value object to mean an object with nothing but getters and setters.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Meanwhile, in practice, a convention has also spread that interprets VO in the broad sense of a data holder, independent of the definition above, and calls carrier objects with nothing but getters and setters VOs. The main source that spread this convention is examined in the Core J2EE Patterns section below.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;the_status_of_immutability_definitional_requirement_vs_good_design&quot;&gt;The Status of Immutability: Definitional Requirement vs. Good Design&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Many books and articles recommend making Value Objects completely immutable.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;On p. 486 of &lt;strong&gt;Patterns of Enterprise Application Architecture&lt;/strong&gt;, Fowler recommends making Value Objects immutable, saying &quot;it&amp;#8217;s a very good idea to make them immutable&quot;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fowler&amp;#8217;s Value Object article says the same. The &lt;a href=&quot;https://web.archive.org/web/20110104080034/http://martinfowler.com/bliki/ValueObject.html&quot;&gt;version before the 2016 revision&lt;/a&gt; presented making value objects entirely immutable as a general heuristic, and the current revised version also presents &quot;value objects should be immutable&quot; as an important rule.&lt;/p&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;A general heuristic is that value objects should be entirely immutable.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the Value Objects entry of the DDD Reference quoted above, Eric Evans gives the design guideline to treat value objects as immutable.&lt;/p&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Treat the value object as immutable. Make all operations Side-effect-free Functions that don&amp;#8217;t depend on any mutable state. Don&amp;#8217;t give a value object any identity and avoid the design complexities necessary to maintain entities.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In Item 17 &quot;Minimize mutability&quot; of &lt;strong&gt;Effective Java&lt;/strong&gt;, 3rd ed., Joshua Bloch recommends, for classes in general and not just Value Objects, minimizing mutability and making them immutable where possible.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If a VO is immutable, no aliasing bug arises even when several objects share the same instance. An aliasing bug is the problem where changing a value on one side also changes the value on another side that references the same instance. Java&amp;#8217;s &lt;code&gt;java.util.Date&lt;/code&gt; and &lt;code&gt;Calendar&lt;/code&gt; are objects with the nature of values, yet they were designed to be mutable and became a source of such bugs. For example, when two objects share a single &lt;code&gt;Date&lt;/code&gt; instance holding a meeting&amp;#8217;s start time, calling &lt;code&gt;setTime()&lt;/code&gt; on one side changes the start time on the other side as well.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Side effect of the mutable Date class&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;record Meeting(String title, Date start) {
}

Date start = new Date();
Meeting review = new Meeting(&quot;Design review&quot;, start);
Meeting retro = new Meeting(&quot;Retrospective&quot;, start);

long oneHourLater = start.getTime() + Duration.ofHours(1).toMillis();
review.start().setTime(oneHourLater); // &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;

assertThat(retro.start().getTime()).isEqualTo(oneHourLater); // &lt;b class=&quot;conum&quot;&gt;(2)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Tries to postpone only the design review by one hour&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The retrospective&amp;#8217;s start time changes too&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Declaring &lt;code&gt;Meeting&lt;/code&gt; as a record is not enough on its own to prevent this problem. A record only prevents assigning a different instance to a field; the internal state of the &lt;code&gt;Date&lt;/code&gt; that the field references can still be changed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The fact that Java 8&amp;#8217;s &lt;code&gt;java.time&lt;/code&gt; package made all of its date and time classes, such as &lt;code&gt;LocalDate&lt;/code&gt; and &lt;code&gt;Instant&lt;/code&gt;, immutable also reflects this lesson. If the start time is represented as a &lt;code&gt;LocalDateTime&lt;/code&gt;, a date and time without a time zone, &lt;code&gt;plusHours()&lt;/code&gt; returns a new instance instead of modifying the existing one, so sharing the instance does not change the start time of the other meeting.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;record Meeting(String title, LocalDateTime start) {
}

LocalDateTime start = LocalDateTime.of(2026, 9, 25, 14, 0);
Meeting review = new Meeting(&quot;Design review&quot;, start);
Meeting retro = new Meeting(&quot;Retrospective&quot;, start);

Meeting delayedReview = new Meeting(review.title(), review.start().plusHours(1));

assertThat(delayedReview.start()).isEqualTo(LocalDateTime.of(2026, 9, 25, 15, 0));
assertThat(retro.start()).isEqualTo(start); // &lt;b class=&quot;conum&quot;&gt;(1)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;colist arabic&quot;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The retrospective&amp;#8217;s start time is unchanged&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;A close look at Fowler&amp;#8217;s and Evans&amp;#8217;s sentences, however, shows that immutability appears as a guideline, not as the definition of a VO. Fowler presents immutability as a rule for avoiding aliasing bugs, and Evans presents it as an imperative design guideline. The property Fowler presents as the definition of a Value Object is equality by value. The &quot;value objects should be immutable&quot; quoted above also appears, when you read the whole sentence, as a rule he follows in order to avoid aliasing bugs.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To avoid aliasing bugs I follow a simple but important rule: value objects should be immutable.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Martin Fowler&lt;br&gt;
&lt;cite&gt;Value Object&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In the same article, he even mentions an alternative: aliasing bugs can also be avoided when the language copies the value on every assignment, as with structs in C#.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;While immutability is my favorite technique to avoid aliasing bugs, it&amp;#8217;s also possible to avoid them by ensuring assignments always make a copy. Some languages provide this ability, such as structs in C#.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Martin Fowler&lt;br&gt;
&lt;cite&gt;Value Object&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Evans wrote about immutability in imperative sentences. In the solution part of the DDD Reference quoted above, the criterion for classifying something as a value object is whether you care only about the attributes and logic of the model element. The &quot;Treat the value object as immutable.&quot; that follows is an imperative sentence telling you to treat the objects so classified as immutable, and the sentence right after it is also an imperative, telling you to make all operations side-effect-free functions. I read these sentences not as classification criteria but as design guidance for handling the objects once they have been classified.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The comments Fowler left on the &lt;a href=&quot;https://wiki.c2.com/?ValueObjectsShouldBeImmutable&quot;&gt;ValueObjectsShouldBeImmutable&lt;/a&gt; page of Ward Cunningham&amp;#8217;s wiki show the distinction between definition and guideline even more clearly. He says not to give a newly designed object any methods that change its state.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;So if you design an object that should be a value object, don&amp;#8217;t provide any methods that change its state, ie make it immutable.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Martin Fowler&lt;br&gt;
&lt;cite&gt;c2 wiki: ValueObjectsShouldBeImmutable&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;And about Value Objects that have already been made mutable, he says the following.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you are using a ValueObject that is mutable, treat it like it is immutable. You may not realize why, but you will save a lot of time and money.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Martin Fowler&lt;br&gt;
&lt;cite&gt;c2 wiki: ValueObjectsShouldBeImmutable&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The very premise &quot;If you are using a ValueObject that is mutable&quot; presupposes the existence of Value Objects that are not immutable. The page is also named ShouldBeImmutable, not MustBeImmutable. The &lt;a href=&quot;https://dictionary.cambridge.org/dictionary/english/should&quot;&gt;Cambridge Dictionary&lt;/a&gt; gives the first meaning of should as follows.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;used to say or ask what is the correct or best thing to do&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Cambridge Dictionary&lt;br&gt;
&lt;cite&gt;should&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The same dictionary explains &lt;a href=&quot;https://dictionary.cambridge.org/dictionary/english/must&quot;&gt;must&lt;/a&gt; as used to show that it is necessary or very important that something happens. If must expresses a necessity that something has to be so, should expresses a recommendation that doing so is the right thing.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;On the other hand, some sources do describe immutability as part of the definition.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Microsoft&amp;#8217;s .NET architecture documentation mentioned above lists the absence of identity and immutability side by side as the two main characteristics of value objects, and states that immutability is an important requirement.&lt;/p&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;There are two main characteristics for value objects: They have no identity. They are immutable. The first characteristic was already discussed. Immutability is an important requirement.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Wikipedia writes that value objects should be immutable, and explains that immutability is required for the implicit contract that two value objects created with the same values must remain equal. Although it uses should, it treats immutability as the premise of a contract, which gives it a status close to that of a definition.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Chapter 6 of Vaughn Vernon&amp;#8217;s &lt;strong&gt;Implementing Domain-Driven Design&lt;/strong&gt; (2013) also includes immutability as one of the characteristics it lists for Value Objects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Kim Woo-geun&amp;#8217;s &lt;strong&gt;Pragmatic Programming for Java/Spring Developers&lt;/strong&gt; (2024, in Korean) also explains that &quot;A VO is an object that has this property of immutability&quot; (p. 43), and defines a VO as an object that satisfies three characteristics: immutability, equality, and self-validation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There are also concepts like the value object of JEP 401, where fields implicitly become final and shallow immutability is enforced at the language level. A different instance cannot be assigned to a field, but the internal state of the object a field references does not thereby become immutable.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;I see immutability as a desirable design norm rather than a definitional requirement of a VO. On the c2 wiki, Fowler too wrote that newly designed objects should be made immutable. Since a new Value Object is designed to be immutable whichever view you follow, cases where the difference between should and must shows up in practice are not common. Still, the distinction is not meaningless. When you meet a value-like object that was built mutable, like &lt;code&gt;java.util.Date&lt;/code&gt;, including immutability in the definition makes that object something other than a VO, while treating immutability as a guideline makes it a VO that fails to follow the immutability guideline. Fowler&amp;#8217;s sentence on the c2 wiki is practical advice from the latter viewpoint. It means that if you have already run into a VO that fails the immutability guideline, you should at least treat it as if it were immutable instead of excluding it as not a VO.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;It is similar to the difference between including &apos;not driving after drinking&apos; in the definition of a driver and seeing it as a norm a driver must follow. Either way, the conclusion that you must not drive after drinking is the same, but if the norm is folded into the definition, there is no longer a name for the violations that exist in reality, which makes such cases hard to discuss.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;definition_of_dto_data_transfer_object&quot;&gt;Definition of DTO (Data Transfer Object)&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In Fowler&amp;#8217;s original definition, a DTO is an object meant to reduce the cost of remote calls. &lt;a href=&quot;https://martinfowler.com/eaaCatalog/dataTransferObject.html&quot;&gt;The catalog for Martin Fowler&amp;#8217;s &lt;strong&gt;Patterns of Enterprise Application Architecture&lt;/strong&gt;&lt;/a&gt; defines a DTO as follows.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;An object that carries data between processes in order to reduce the number of method calls.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Martin Fowler&lt;br&gt;
&lt;cite&gt;Patterns of Enterprise Application Architecture catalog: Data Transfer Object&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The same definition appears on page 401 of the book. Every remote call costs a network round trip and serialization, so the pattern grew out of the intent to deliver all the needed data in a single call. For example, instead of fetching a customer&amp;#8217;s name, address, and order list with three remote getter calls, you receive a single DTO holding all three values in one call.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Objects that hold data crossing the network, such as HTTP API requests and responses, fit this definition in that they cross a process boundary and are serialized. Not every HTTP API is designed to reduce the number of remote calls, however, so calling these objects DTOs extends the original definition to modern API boundaries.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In practice the definition has been stretched further. A convention has emerged of calling any object that carries data across layer boundaries within the same process a DTO, with no remote call involved. Examples include objects holding DB query results, objects passed from the service layer to the view rendering layer, and response-only objects converted from JPA entities so that the entities are not exposed directly outside the service layer. This usage is far from the original definition of reducing the number of remote calls, so one could argue that such objects are hard to call DTOs.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Apart from the debate over the name, one can also ask whether having such objects in a local context is desirable from a design standpoint. In &lt;a href=&quot;https://martinfowler.com/bliki/LocalDTO.html&quot;&gt;LocalDTO&lt;/a&gt;, Fowler takes issue not with the name but with this usage. His starting point is that the DTO pattern exists to reduce the cost of remote calls, so in a local context with no remote calls that reason disappears. He therefore says that in a local context DTOs are not just unnecessary but actually harmful. The reasons are that a coarse-grained API that exchanges a lot of data in a single call is awkward to use, and that all the work of moving data from the domain layer or data source layer into DTOs is added cost.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Not just do you not need them in a local context, they are actually harmful both because a coarse-grained API is more difficult to use and because you have to do all the work moving data from your domain or data source layer into the DTOs.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Martin Fowler&lt;br&gt;
&lt;cite&gt;LocalDTO&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To the argument that DTOs should be placed in the service layer API so that the service layer&amp;#8217;s clients do not depend on the domain model, he replies that this may be convenient but is not worth all the cost of the data mapping.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In the same article, however, he acknowledges that something like a DTO is useful even locally when there is a large gap between the presentation layer&amp;#8217;s model and the domain model.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;One case where it is useful to use something like a DTO is when you have a significant mismatch between the model in your presentation layer and the underlying domain model.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Martin Fowler&lt;br&gt;
&lt;cite&gt;LocalDTO&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In such cases the mapping between the two models is needed anyway, so the DTO is not an added cost. Later in the same article he also adds the use of DTOs for exchanging data as messages between isolated areas in a multithreaded application. In short, Fowler&amp;#8217;s criticism is aimed at DTOs introduced for the sake of separation from the domain model itself. It does not reject cases where there is a real need, such as a mismatch between models or message passing between isolated areas.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The naming debate and the usage debate are different questions, but they start from the same premise. The fact that a local context has no remote calls serves, on one side, as grounds that the name DTO does not match the original definition, and on the other, as grounds that the reason to have a DTO grows weak. The role-based suffixes I propose later are an answer to the naming debate. Whether to have such an object at all is a matter to settle separately in the usage debate, and if you decide to have one, the proposal is to give it a name that reveals its role rather than &lt;code&gt;Dto&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In the end, the criterion for distinguishing a VO from a DTO is the object&amp;#8217;s characteristics and role. Representing a value concept and judging equality by value is a property of a VO; carrying data across a boundary is the role of a DTO. The two are not mutually exclusive categories. An immutable DTO with value equality is also a VO. Conversely, not every DTO needs value equality.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;vo_in_the_first_edition_of_core_j2ee_patterns_and_to_in_the_second&quot;&gt;VO in the First Edition of Core J2EE Patterns and TO in the Second&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In the Java/J2EE (now Jakarta EE) community, a prominent early source that spread the conflation of the two terms is &lt;strong&gt;Core J2EE Patterns: Best Practices and Design Strategies&lt;/strong&gt; by Deepak Alur and others. The first edition, published in 2001, defined an object that transfers data between tiers as a pattern named Value Object. The second edition in 2003 renamed the same pattern TO (Transfer Object). The rename appears intended to avoid confusion with the Value Object as a value-equality object. Martin Fowler calls the pattern with the same role a DTO (&lt;strong&gt;Patterns of Enterprise Application Architecture&lt;/strong&gt;, p. 401). In short, the following three refer to the same transfer pattern.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;VO in Core J2EE Patterns 1st ed. = TO in the 2nd ed. = Martin Fowler&amp;#8217;s DTO&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://www.oracle.com/java/technologies/transfer-object.html&quot;&gt;Oracle&amp;#8217;s Transfer Object documentation&lt;/a&gt; describes this pattern under the renamed name, Transfer Object.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Several other books also wrote that VO and DTO mean the same thing or are very close concepts.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Rod Johnson, &lt;strong&gt;Expert One-on-One J2EE Design and Development&lt;/strong&gt;, Wrox, 2002, p. 265&lt;/p&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Value objects are sometimes referred to as Data Transfer Objects (DTOs).&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rod Johnson and Juergen Hoeller, &lt;strong&gt;Expert One-on-One J2EE Development without EJB&lt;/strong&gt;, Wrox, 2004, p. 27&lt;/p&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Transfer objects, often referred to as Data Transfer Objects (DTOs) or Value Objects.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Murat Yener and Alex Theedom, &lt;strong&gt;Professional Java EE Design Patterns&lt;/strong&gt;, Wrox, 2014, Chapter 12&lt;/p&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The DTO is also referred to as the Value Object&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Derek C. Ashmore, &lt;strong&gt;The Java EE Architect&amp;#8217;s Handbook, Second Edition&lt;/strong&gt;, DVT Press, 2014, Chapter 5&lt;/p&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;My definition of &apos;value object&apos; is very close to a Data Transfer Object (DTO)&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;I cannot say for certain that all of these books were directly influenced by the first edition of Core J2EE Patterns. But given when they appeared and the phrasing repeated across the literature, I suspect the first edition&amp;#8217;s usage had some influence on the wording of later Java/J2EE literature. More than twenty years after the second edition changed the name, the practice remains.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;the_cost_of_calling_a_data_carrier_a_vo&quot;&gt;The Cost of Calling a Data Carrier a VO&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;You might wonder whether a name already in common use within a team really needs to change. But the practice of calling data carrier objects VOs has a real cost, because the Value Object as a value-equality object keeps appearing in three contexts: DDD, ORM, and Java language and JVM features.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;DDD (Domain-Driven Design): the VALUE OBJECT is, along with the ENTITY, a core building block of the domain model. An AGGREGATE is a consistency boundary for data changes with a single ENTITY as its root, and it can contain other ENTITIES and VALUE OBJECTS inside it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ORM: &lt;code&gt;@Embeddable&lt;/code&gt; in Hibernate and JPA maps an object that has no persistent identity of its own and belongs to the entity that owns it. It is the structure commonly used to map DDD VALUE OBJECTS, but JPA does not enforce value equality or immutability, so not every &lt;code&gt;@Embeddable&lt;/code&gt; is a DDD VALUE OBJECT.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Java language and JVM features: the Project Valhalla documents, including JEP 401, use the term value object to mean an object that is immutable, has no object identity, and is distinguished by its field values.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you understand a VO as a data holder with nothing but getters and setters, the terminology clashes when you read these materials, and confusion follows. For example, reading an explanation that says to map a VO as an &lt;code&gt;@Embeddable&lt;/code&gt;, you picture a request parameter object full of setters. Calling a data carrier object that crosses a remote process boundary a DTO connects naturally both with Fowler&amp;#8217;s definition and with the renamed name used since the second edition of Core J2EE Patterns.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;As seen in the DTO definition section above, recent convention also uses DTO in the broad sense of a data holder that crosses a layer boundary. But attaching the DTO suffix to every carrier object has a drawback. If objects holding HTTP request parameters and objects holding the results of statistics queries are all called DTOs, the name alone does not reveal the role. For example, from the name &lt;code&gt;IssueDto&lt;/code&gt; alone you cannot tell whether it is a request body, a lookup response, or the result of a statistics query.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;I recommend dividing suffixes by the object&amp;#8217;s role. The name then reveals the role on its own, and you also avoid the debate over departing from the strict definition when objects used in layers unrelated to remote calls are called DTOs. Here are some example names.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 50%;&quot;&gt;
&lt;col style=&quot;width: 50%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Role&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Example class names&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JSON response for an issue lookup&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;IssueResponse&lt;/code&gt;, &lt;code&gt;IssueDetailDto&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JSON request for issue creation&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;IssueCreationRequest&lt;/code&gt;, &lt;code&gt;IssueCreationCommand&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Issue lookup criteria&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;IssueQuery&lt;/code&gt;, &lt;code&gt;IssueCriteria&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Result of an issue statistics query against the DB&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;IssueStatsRow&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;value_object&quot;&gt;Value Object&lt;/h3&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://martinfowler.com/bliki/ValueObject.html&quot;&gt;Martin Fowler: Value Object&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20110104080034/http://martinfowler.com/bliki/ValueObject.html&quot;&gt;Martin Fowler: Value Object (version before the 2016 revision, Wayback Machine)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://wiki.c2.com/?ValueObjectsShouldBeImmutable&quot;&gt;C2 Wiki: Value Objects Should Be Immutable&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Value_object&quot;&gt;Wikipedia: Value object&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/implement-value-objects&quot;&gt;Microsoft: Implement value objects&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Martin Fowler, &lt;a href=&quot;https://www.informit.com/store/patterns-of-enterprise-application-architecture-9780321127426&quot;&gt;&lt;strong&gt;Patterns of Enterprise Application Architecture&lt;/strong&gt;&lt;/a&gt;, Addison-Wesley, 2002, p. 486&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Eric Evans, &lt;a href=&quot;https://www.informit.com/store/domain-driven-design-tackling-complexity-in-the-heart-9780321125217&quot;&gt;&lt;strong&gt;Domain-Driven Design&lt;/strong&gt;&lt;/a&gt;, Addison-Wesley, 2003&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf&quot;&gt;Eric Evans: Domain-Driven Design Reference (2015), Value Objects&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Kim Woo-geun, &lt;a href=&quot;https://wikibook.co.kr/pragmatic-programming/&quot;&gt;&lt;strong&gt;Pragmatic Programming for Java/Spring Developers&lt;/strong&gt; (in Korean)&lt;/a&gt;, WikiBooks, 2024, p. 43&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Joshua Bloch, &lt;a href=&quot;https://www.informit.com/store/effective-java-9780134685991&quot;&gt;&lt;strong&gt;Effective Java&lt;/strong&gt;&lt;/a&gt; 3rd ed., Addison-Wesley, 2018, Items 10, 11, and 17&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Vaughn Vernon, &lt;a href=&quot;https://www.informit.com/store/implementing-domain-driven-design-9780133039894&quot;&gt;&lt;strong&gt;Implementing Domain-Driven Design&lt;/strong&gt;&lt;/a&gt;, Addison-Wesley, 2013, Chapter 6&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://c2.com/ppr/checks.html&quot;&gt;Ward Cunningham: The CHECKS Pattern Language of Information Integrity - Whole Value&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://openjdk.org/jeps/395&quot;&gt;JEP 395: Records&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://openjdk.org/jeps/401&quot;&gt;JEP 401: Value Objects (Preview)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://openjdk.org/jeps/169&quot;&gt;JEP 169: Larval State for Value Objects&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;transfer_object_dto&quot;&gt;Transfer Object / DTO&lt;/h3&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://martinfowler.com/eaaCatalog/dataTransferObject.html&quot;&gt;Martin Fowler: Data Transfer Object&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://martinfowler.com/bliki/LocalDTO.html&quot;&gt;Martin Fowler: LocalDTO&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Martin Fowler, &lt;a href=&quot;https://www.informit.com/store/patterns-of-enterprise-application-architecture-9780321127426&quot;&gt;&lt;strong&gt;Patterns of Enterprise Application Architecture&lt;/strong&gt;&lt;/a&gt;, Addison-Wesley, 2002, p. 401&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Deepak Alur, John Crupi, and Dan Malks, &lt;a href=&quot;https://www.amazon.com/Core-J2EE-Patterns-Practices-Strategies/dp/0130648841&quot;&gt;&lt;strong&gt;Core J2EE Patterns: Best Practices and Design Strategies&lt;/strong&gt;&lt;/a&gt; 1st ed., Prentice Hall, 2001&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rod Johnson, &lt;a href=&quot;https://www.wiley.com/en-us/Expert+One+on+One+J2EE+Design+and+Development-p-9780764543852&quot;&gt;&lt;strong&gt;Expert One-on-One J2EE Design and Development&lt;/strong&gt;&lt;/a&gt;, Wrox, 2002, p. 265&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rod Johnson and Juergen Hoeller, &lt;a href=&quot;https://www.wiley.com/en-us/Expert+One+on+One+J2EE+Development+without+EJB-p-9780764573903&quot;&gt;&lt;strong&gt;Expert One-on-One J2EE Development without EJB&lt;/strong&gt;&lt;/a&gt;, Wrox, 2004, p. 27&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Murat Yener and Alex Theedom, &lt;a href=&quot;https://www.wiley.com/en-us/Professional+Java+EE+Design+Patterns-p-9781118843413&quot;&gt;&lt;strong&gt;Professional Java EE Design Patterns&lt;/strong&gt;&lt;/a&gt;, Wrox, 2014, Chapter 12&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Derek C. Ashmore, &lt;a href=&quot;https://www.amazon.com/Java-Architects-Handbook-Second-applications/dp/0972954880&quot;&gt;&lt;strong&gt;The Java EE Architect&amp;#8217;s Handbook, Second Edition&lt;/strong&gt;&lt;/a&gt;, DVT Press, 2014, Chapter 5&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.oracle.com/java/technologies/transfer-object.html&quot;&gt;Oracle: Core J2EE Patterns - Transfer Object&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;http://www.adam-bien.com/roller/abien/entry/value_object_vs_data_transfer&quot;&gt;Adam Bien: Value Object vs. Data Transfer Object (VO vs. DTO)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
	</description>
    </item>
    <item>
      <title>Documenting and Verifying Thread Safety: Javadoc, Annotations, Static Analysis, and ArchUnit</title>
      <link>https://tech.benelog.net/thread-safety-annotations.html</link>
      <pubDate>Sat, 3 Oct 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="false">thread-safety-annotations.html</guid>
      	<description>
	&lt;div id=&quot;toc&quot; class=&quot;toc&quot;&gt;
&lt;div id=&quot;toctitle&quot;&gt;Table of Contents&lt;/div&gt;
&lt;ul class=&quot;sectlevel1&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#thread_safety_in_javadoc&quot;&gt;Thread Safety in Javadoc&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#why_javadoc_hides_synchronized&quot;&gt;Why Javadoc Hides synchronized&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the_need_for_a_class_level_thread_safety_statement&quot;&gt;The Need for a Class-Level Thread Safety Statement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the_five_levels_of_thread_safety_in_effective_java&quot;&gt;The Five Levels of Thread Safety in Effective Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#examples_in_class_descriptions&quot;&gt;Examples in Class Descriptions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#thread_safety_with_annotations&quot;&gt;Thread Safety with Annotations&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#contract_in_apache_httpclient&quot;&gt;@Contract in Apache HttpClient&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#jcip_annotations&quot;&gt;JCIP Annotations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#annotations_spread_under_the_jcip_names&quot;&gt;Annotations Spread Under the JCIP Names&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#choosing_annotations_for_your_purpose&quot;&gt;Choosing Annotations for Your Purpose&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#what_static_analysis_tools_check&quot;&gt;What Static Analysis Tools Check&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#spotbugs&quot;&gt;SpotBugs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#error_prone&quot;&gt;Error Prone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#intellij_idea&quot;&gt;IntelliJ IDEA&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#project_specific_rules_verified_with_archunit&quot;&gt;Project-Specific Rules Verified with ArchUnit&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#example_code&quot;&gt;Example Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#rules_and_results&quot;&gt;Rules and Results&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#summary&quot;&gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#references&quot;&gt;References&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div id=&quot;preamble&quot;&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Whether instances of a class can be shared by multiple threads is information developers need to check carefully. Yet the standard Javadoc tags (&lt;code&gt;@param&lt;/code&gt;, &lt;code&gt;@return&lt;/code&gt;, &lt;code&gt;@throws&lt;/code&gt;, and so on) include no tag for thread safety. As a result, users of a class can easily miss what its provider intended.
This article first looks at a good classification scheme for stating thread safety in Javadoc. It then covers how to use annotations, static analysis tools, and ArchUnit tests to keep code that is unsafe under multiple threads from being deployed.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;thread_safety_in_javadoc&quot;&gt;Thread Safety in Javadoc&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This section covers four topics in order:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Why Javadoc does not show &lt;code&gt;synchronized&lt;/code&gt; on methods&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Why a class-level statement is still needed&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A suitable classification for documentation&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Examples from real library documentation&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;why_javadoc_hides_synchronized&quot;&gt;Why Javadoc Hides synchronized&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When an instance method carries the &lt;code&gt;synchronized&lt;/code&gt; keyword, the instance itself becomes the lock. Even if several threads call &lt;code&gt;synchronized&lt;/code&gt; methods on the same instance at the same time, only one thread can enter. The others wait until the first thread leaves the method. A &lt;code&gt;static synchronized&lt;/code&gt; method uses the &lt;code&gt;Class&lt;/code&gt; object of its class as the lock. So &lt;code&gt;synchronized&lt;/code&gt; is a clue to thread safety, and it would seem useful to show it in Javadoc.
Javadoc, however, does not print the &lt;code&gt;synchronized&lt;/code&gt; keyword in method declarations. Synchronization is considered an implementation detail, not a contract to expose in the API documentation. Item 82 of Effective Java, Third Edition, defends this policy on the same grounds.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;synchronized&lt;/code&gt; on a method declaration is equivalent to wrapping the whole method body in a &lt;code&gt;synchronized(this)&lt;/code&gt; block. That is, the following code&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;synchronized on the method declaration&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;synchronized void run() {
    // do something
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;does the same thing as this code:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;The whole method body in a synchronized block&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;void run() {
    synchronized (this) {
        // do something
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;As the implementation improves, the synchronized region may shrink to part of the method, or a separate lock object may replace &lt;code&gt;this&lt;/code&gt;. Such implementation details change more often than the external interface. It is also hard to show every lock-protected region in the method declaration. Some classes synchronize internally with &lt;code&gt;java.util.concurrent.locks.Lock&lt;/code&gt; or CAS (Compare And Swap) operations. The presence or absence of the &lt;code&gt;synchronized&lt;/code&gt; keyword therefore cannot be the only criterion for thread safety.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;the_need_for_a_class_level_thread_safety_statement&quot;&gt;The Need for a Class-Level Thread Safety Statement&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In a multithreaded environment, each method call may be safe on its own while a combination of calls is not.
For example, a &lt;code&gt;HashMap&lt;/code&gt; that is safely published after construction (for instance, through a &lt;code&gt;final&lt;/code&gt; field or a &lt;code&gt;volatile&lt;/code&gt; variable) and never modified afterward can be read with &lt;code&gt;get()&lt;/code&gt; from several threads without problems. But if one thread changes its structure with &lt;code&gt;put()&lt;/code&gt; while another calls &lt;code&gt;get()&lt;/code&gt;, the result is not guaranteed. The same applies to &lt;code&gt;Hashtable&lt;/code&gt;, whose public methods are all &lt;code&gt;synchronized&lt;/code&gt; or delegate to synchronized methods and views, and to a &lt;code&gt;Map&lt;/code&gt; wrapped with &lt;code&gt;Collections.synchronizedMap()&lt;/code&gt;. When two calls are combined, such as checking with &lt;code&gt;containsKey()&lt;/code&gt; and then calling &lt;code&gt;put()&lt;/code&gt;, a race condition occurs unless the caller holds a lock externally.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;What needs documenting, then, is not whether methods are synchronized but under what conditions the class is safe. Since Javadoc has no convention for this, developers have to write it in the class description themselves. The Effective Java classification in the next section provides the criteria.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;the_five_levels_of_thread_safety_in_effective_java&quot;&gt;The Five Levels of Thread Safety in Effective Java&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Item 82 of Effective Java, Third Edition, &quot;Document thread safety&quot; (Item 70 in the Second Edition), recommends documenting thread safety in five levels.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Immutable&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The state never changes, so no external synchronization is needed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Examples: &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Long&lt;/code&gt;, &lt;code&gt;BigInteger&lt;/code&gt; (the book&amp;#8217;s examples), &lt;code&gt;java.time.LocalDate&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unconditionally thread-safe&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The class has mutable state but synchronizes sufficiently inside.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Examples: &lt;code&gt;AtomicLong&lt;/code&gt;, &lt;code&gt;ConcurrentHashMap&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Conditionally thread-safe&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Some methods require external synchronization to be safe.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Example: a &lt;code&gt;List&lt;/code&gt; wrapped with &lt;code&gt;Collections.synchronizedList()&lt;/code&gt;. While iterating with an iterator, the caller must hold the &lt;code&gt;List&lt;/code&gt; object as a lock. Otherwise, the behavior is non-deterministic. A fail-fast iterator may throw &lt;code&gt;ConcurrentModificationException&lt;/code&gt;, but there is no guarantee that it will.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Not thread-safe&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The caller must synchronize externally.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Examples: &lt;code&gt;HashMap&lt;/code&gt;, &lt;code&gt;ArrayList&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Thread-hostile&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The class cannot be used by multiple threads even with external synchronization. Classes or methods that modify static data without synchronization fall into this category. Even if each caller locks a different instance, they all end up modifying the same static data concurrently.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Examples: The Third Edition explains that the &lt;code&gt;generateSerialNumber&lt;/code&gt; method in Item 78 would be thread-hostile if it incremented a static field without internal synchronization. &lt;code&gt;System.runFinalizersOnExit()&lt;/code&gt;, the example in the Second Edition, was deprecated in JDK 1.2 and removed in JDK 11.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;examples_in_class_descriptions&quot;&gt;Examples in Class Descriptions&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Here are three examples of where and how the Java standard library and Spring Batch describe thread safety.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;java_util_linkedlist&quot;&gt;java.util.LinkedList&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/LinkedList.html&quot;&gt;JDK 25 Javadoc for LinkedList&lt;/a&gt; states in bold, in the third paragraph of the class description, that it is &quot;not synchronized&quot;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;img/thread-safety-annotations/LinkedList-doc.png&quot; alt=&quot;JDK 25 Javadoc for LinkedList&quot; width=&quot;800&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;java_text_simpledateformat&quot;&gt;java.text.SimpleDateFormat&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/text/SimpleDateFormat.html&quot;&gt;JDK 25 Javadoc for SimpleDateFormat&lt;/a&gt; has a final section of the class description titled &quot;Synchronization&quot;, which says &quot;Date formats are not synchronized&quot;. Its &quot;API Note&quot; recommends &lt;code&gt;DateTimeFormatter&lt;/code&gt; as an &quot;immutable and thread-safe alternative&quot;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;img/thread-safety-annotations/SimpleDateFormat-doc.png&quot; alt=&quot;JDK 25 Javadoc for SimpleDateFormat&quot; width=&quot;800&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/format/DateTimeFormatter.html&quot;&gt;JDK 25 Javadoc for DateTimeFormatter&lt;/a&gt; states &quot;This class is immutable and thread-safe.&quot; under &quot;Implementation Requirements&quot; at the end of the class description.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;img/thread-safety-annotations/DateTimeFormatter-doc.png&quot; alt=&quot;JDK 25 Javadoc for DateTimeFormatter&quot; width=&quot;800&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;code&gt;java.time&lt;/code&gt; package, added in JDK 8, applies this format across the whole package. In the JDK 25 source, 31 of the 43 public classes in &lt;code&gt;java.time&lt;/code&gt; and its subpackages, including &lt;code&gt;LocalDate&lt;/code&gt;, &lt;code&gt;Instant&lt;/code&gt;, and &lt;code&gt;ZonedDateTime&lt;/code&gt;, state the same sentence in the same place with the Javadoc &lt;code&gt;@implSpec&lt;/code&gt; tag. All 12 public enums also use a sentence of the same shape, such as &quot;This is an immutable and thread-safe enum.&quot; Of the remaining 12 classes, all but the utility class &lt;code&gt;TemporalQueries&lt;/code&gt; state their immutability or threading conditions in the same section. Exception classes, for example, say &quot;This class is intended for use in a single thread.&quot;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;That said, this format has not become a standard across the whole JDK.
&lt;code&gt;@implSpec&lt;/code&gt; is not a standard Javadoc tag. It is a JDK-specific tag registered with the &lt;code&gt;-tag&lt;/code&gt; option when the JDK is built. If you use it as is in an ordinary project, the JDK 25 &lt;code&gt;javadoc&lt;/code&gt; command reports the error &quot;unknown tag. Unregistered custom tag?&quot;.
Within the JDK, new APIs also differ. &lt;code&gt;Arena&lt;/code&gt; and &lt;code&gt;Linker&lt;/code&gt; in &lt;code&gt;java.lang.foreign&lt;/code&gt;, which became a final API in JDK 22, and &lt;code&gt;ListFormat&lt;/code&gt;, added in JDK 22, state thread safety in &lt;code&gt;@implSpec&lt;/code&gt;. In contrast, &lt;code&gt;HexFormat&lt;/code&gt;, added in JDK 17, writes the same sentence &quot;This class is immutable and thread-safe.&quot; in the body without &lt;code&gt;@implSpec&lt;/code&gt;, and &lt;code&gt;HttpClient&lt;/code&gt;, added in JDK 11, also says in the body &quot;Once built, an HttpClient is immutable&quot;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;org_springframework_batch_item_file_flatfileitemwriter&quot;&gt;org.springframework.batch.item.file.FlatFileItemWriter&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://docs.spring.io/spring-batch/docs/current/api/org/springframework/batch/item/file/FlatFileItemWriter.html&quot;&gt;FlatFileItemWriter&lt;/a&gt; Javadoc in Spring Batch 5.2.6 states on the last line of the class description &quot;The implementation is &lt;strong&gt;not&lt;/strong&gt; thread-safe.&quot;, with only &quot;not&quot; in bold.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;img/thread-safety-annotations/FlatFileItemWriter-doc.png&quot; alt=&quot;Spring Batch 5.2.6 Javadoc for FlatFileItemWriter&quot; width=&quot;800&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;As these examples show, each class marks thread safety in a different place and a different way. &lt;code&gt;LinkedList&lt;/code&gt; and &lt;code&gt;FlatFileItemWriter&lt;/code&gt; use bold text, and &lt;code&gt;SimpleDateFormat&lt;/code&gt; uses a separate section heading. All four classes, however, put the statement in the middle or at the end of the class description rather than in the first paragraph, so anyone who does not read the API documentation to the end can easily miss it. I find myself wishing for a rule such as &quot;Put thread safety on the first line of the class description, and always make it stand out.&quot;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;thread_safety_with_annotations&quot;&gt;Thread Safety with Annotations&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Marking thread safety with annotations instead of prose gives it a consistent place and format in Javadoc. When an annotation&amp;#8217;s declaration carries the &lt;code&gt;@Documented&lt;/code&gt; meta-annotation, Javadoc prints that annotation as is in the class declaration.
We first look at a project that defined its own annotation for this purpose, and then at annotations that many projects can share.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;contract_in_apache_httpclient&quot;&gt;@Contract in Apache HttpClient&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://hc.apache.org/httpcomponents-client-5.5.x/&quot;&gt;Apache HttpClient&lt;/a&gt; 5 defines a single annotation for thread safety, &lt;code&gt;org.apache.hc.core5.annotation.Contract&lt;/code&gt;. Its &lt;code&gt;threading&lt;/code&gt; attribute takes a value of the &lt;a href=&quot;https://hc.apache.org/httpcomponents-core-5.3.x/current/httpcore5/apidocs/org/apache/hc/core5/annotation/ThreadingBehavior.html&quot;&gt;ThreadingBehavior&lt;/a&gt; enum.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 25%;&quot;&gt;
&lt;col style=&quot;width: 75%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;ThreadingBehavior&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;IMMUTABLE&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Fully immutable and thread-safe.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;IMMUTABLE_CONDITIONAL&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Immutable if the dependencies injected through the constructor are immutable, and thread-safe if they are thread-safe.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;STATELESS&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Stateless and thread-safe.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;SAFE&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Thread-safe.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;SAFE_CONDITIONAL&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Thread-safe only if the dependencies injected through the constructor are thread-safe.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;UNSAFE&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Not thread-safe. This is the default when the &lt;code&gt;threading&lt;/code&gt; attribute is omitted.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In the HttpClient 5.5 source, the main classes are declared as follows.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;HttpClient 5.5&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;@Contract(threading = ThreadingBehavior.SAFE)
public abstract class CloseableHttpClient implements HttpClient, ModalCloseable {

@Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
public class PoolingHttpClientConnectionManager
        implements HttpClientConnectionManager, ConnPoolControl&amp;lt;HttpRoute&amp;gt; {

@Contract(threading = ThreadingBehavior.SAFE)
public class BasicHttpClientConnectionManager implements HttpClientConnectionManager {&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;SAFE_CONDITIONAL&lt;/code&gt; has a name similar to &quot;conditionally thread-safe&quot; in Effective Java, but it means something different. The Effective Java classification is about which sequences of method calls need external synchronization, while the HttpClient value is about whether the dependencies received through the constructor are thread-safe.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;@Contract&lt;/code&gt; carries the &lt;code&gt;@Documented&lt;/code&gt; meta-annotation, so it appears in the class declaration in Javadoc. Below is the &lt;a href=&quot;https://hc.apache.org/httpcomponents-client-5.5.x/current/httpclient5/apidocs/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.html&quot;&gt;Javadoc for BasicHttpClientConnectionManager&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;img/thread-safety-annotations/BasicHttpClientConnectionManager-doc.png&quot; alt=&quot;HttpClient 5 Javadoc for BasicHttpClientConnectionManager&quot; width=&quot;800&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Because it always appears in the same place, thread safety is visible at a glance. The class description also says &quot;this class is fully thread-safe&quot;, but the annotation in the declaration catches the eye first.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;HttpClient did not start with its own annotation. Up to &lt;a href=&quot;https://github.com/apache/httpcomponents-client/tree/rel/v4.5.2/httpclient/src/main/java/org/apache/http&quot;&gt;HttpClient 4.5.2&lt;/a&gt;, &lt;code&gt;HttpGet&lt;/code&gt; and the classes from the 5.5 example above were declared with &lt;code&gt;@NotThreadSafe&lt;/code&gt; and &lt;code&gt;@ThreadSafe&lt;/code&gt;, as shown below.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;HttpClient 4.5.2&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;@NotThreadSafe
public class HttpGet extends HttpRequestBase {

@ThreadSafe
public abstract class CloseableHttpClient implements HttpClient, Closeable {

@ThreadSafe
public class PoolingHttpClientConnectionManager
    implements HttpClientConnectionManager, ConnPoolControl&amp;lt;HttpRoute&amp;gt;, Closeable {

@ThreadSafe
public class BasicHttpClientConnectionManager implements HttpClientConnectionManager, Closeable {&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;These annotations lived in the &lt;code&gt;org.apache.http.annotation&lt;/code&gt; package, and their Javadoc said they originated in the book &quot;Java Concurrency in Practice&quot;. Because of a &lt;a href=&quot;https://issues.apache.org/jira/browse/HTTPCLIENT-1743&quot;&gt;licensing issue&lt;/a&gt; with the original JCIP library, discussed below, HttpCore 4.4.5 removed these four annotations, and HttpClient has used &lt;code&gt;@Contract&lt;/code&gt; since 4.5.3.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;jcip_annotations&quot;&gt;JCIP Annotations&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Each project can define its own annotations, as HttpClient did. Using annotations from widely adopted open source, however, spares users from learning something new. SpotBugs and IntelliJ IDEA, covered later, include the JCIP package in their default lists of recognized annotations. The JCIP annotations that HttpClient 4.x borrowed are the starting point for such shared annotations.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://jcip.net/annotations/doc/net/jcip/annotations/package-summary.html&quot;&gt;JCIP annotations&lt;/a&gt; are the thread safety annotations proposed in Appendix A of &quot;Java Concurrency in Practice&quot;. There are four:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;@ThreadSafe&lt;/code&gt;: a thread-safe class&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;@NotThreadSafe&lt;/code&gt;: a class that is not thread-safe&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;@Immutable&lt;/code&gt;: an immutable class. An immutable class is thread-safe.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;@GuardedBy(&quot;lock&quot;)&lt;/code&gt;: placed on a field or method to indicate which lock must be held when accessing it. It can name either an intrinsic lock used by &lt;code&gt;synchronized&lt;/code&gt; or a &lt;code&gt;java.util.concurrent.locks.Lock&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The first three annotations tell users about the class&amp;#8217;s contract, and &lt;code&gt;@GuardedBy&lt;/code&gt; tells maintainers of the class which lock to respect.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Effective Java, Second Edition (2008) came out after JCIP (2006) and explains its five levels by comparing them with the JCIP annotations.
Lining up the two classifications, the four levels other than thread-hostile map onto the three class-level JCIP annotations. Unconditionally thread-safe and conditionally thread-safe are both covered by &lt;code&gt;@ThreadSafe&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 25%;&quot;&gt;
&lt;col style=&quot;width: 25%;&quot;&gt;
&lt;col style=&quot;width: 50%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Effective Java level&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;JCIP annotation&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Immutable&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;@Immutable&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JCIP considers immutable classes thread-safe, so &lt;code&gt;@ThreadSafe&lt;/code&gt; is not added on top.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Unconditionally thread-safe&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;@ThreadSafe&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Same meaning.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Conditionally thread-safe&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;@ThreadSafe&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JCIP has no separate name for this. Which lock to hold goes in the description. This is where the two classifications actually diverge.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Not thread-safe&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;@NotThreadSafe&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JCIP treats this annotation as optional.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Thread-hostile&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;None&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JCIP has no corresponding concept.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Not applicable&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;@GuardedBy(&quot;lock&quot;)&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Placed on fields and methods, for maintainers. The five levels of Effective Java are a class-level contract with users, so the basis of classification itself is different.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In short, Effective Java divides classes into five levels by how much external synchronization users need. JCIP uses a safe-or-not dichotomy with immutability added as a special case, and separates information for maintainers into &lt;code&gt;@GuardedBy&lt;/code&gt;. Section 4.5 of JCIP, &quot;Documenting synchronization policies&quot;, recommends documenting thread safety guarantees for users and synchronization policies for maintainers.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;annotations_spread_under_the_jcip_names&quot;&gt;Annotations Spread Under the JCIP Names&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The original library distributed with the JCIP book is &lt;code&gt;net.jcip:jcip-annotations:1.0&lt;/code&gt; on Maven Central. Its license, however, is Creative Commons Attribution, a license that the Creative Commons foundation itself does not recommend for software. That led to &lt;a href=&quot;https://github.com/stephenc/jcip-annotations&quot;&gt;com.github.stephenc.jcip:jcip-annotations:1.0-1&lt;/a&gt;, a reimplementation of the same API under the Apache License 2.0. The two libraries share the package name (&lt;code&gt;net.jcip.annotations&lt;/code&gt;) and the annotation names.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The JCIP annotations have also been copied into several other projects. The following libraries use the same annotation names in different packages.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;javax.annotation.concurrent&lt;/code&gt;: &lt;code&gt;com.google.code.findbugs:jsr305&lt;/code&gt;, the JSR-305 annotation implementation distributed by FindBugs, contains the same four annotations. &lt;a href=&quot;https://jcp.org/en/jsr/proposalDetails?id=305&quot;&gt;JSR-305 itself was abandoned without a final release and is dormant&lt;/a&gt;. On Java 9 and 10, the &lt;code&gt;javax.annotation&lt;/code&gt; package in this jar placed on the module path could conflict with the JDK&amp;#8217;s &lt;code&gt;java.xml.ws.annotation&lt;/code&gt; module. That JDK module was &lt;a href=&quot;https://docs.oracle.com/en/java/javase/26/migrate/removed-tools-components.html&quot;&gt;removed in JDK 11&lt;/a&gt;, however, so the problem does not apply to every version since Java 9.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;com.google.errorprone.annotations&lt;/code&gt;: &lt;code&gt;@Immutable&lt;/code&gt; and &lt;code&gt;@ThreadSafe&lt;/code&gt; used by Google&amp;#8217;s Error Prone, plus &lt;code&gt;@GuardedBy&lt;/code&gt; in the &lt;code&gt;concurrent&lt;/code&gt; subpackage.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;androidx.annotation.GuardedBy&lt;/code&gt;: for Android.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;org.apache.http.annotation&lt;/code&gt;: as seen above, the four annotations Apache HttpComponents copied and used up to 4.5.2. They were replaced by &lt;code&gt;@Contract&lt;/code&gt; in HttpCore 4.4.5 and HttpClient 4.5.3.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;choosing_annotations_for_your_purpose&quot;&gt;Choosing Annotations for Your Purpose&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The original JCIP library is hard to recommend for new projects because of its license, and JSR-305 because its standardization was abandoned.
Instead, considering the coverage of the static analysis tools in the next section, I recommend one of the following two libraries.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If compile-time verification comes first, &lt;code&gt;error_prone_annotations&lt;/code&gt; is the right fit. With Error Prone, &lt;code&gt;@Immutable&lt;/code&gt; and &lt;code&gt;@GuardedBy&lt;/code&gt; can be checked at compile time. Guava declares this library as a compile-scope dependency (as of 33.7.1-jre), so projects that use Guava get it on the compile classpath as a transitive dependency. If you use it directly in code, though, it is safer to declare the dependency explicitly rather than rely on the transitive one. Error Prone does not check &lt;code&gt;@ThreadSafe&lt;/code&gt;, and the library has no &lt;code&gt;@NotThreadSafe&lt;/code&gt;. You therefore also need a convention that unmarked classes are considered not thread-safe.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If expressing all four JCIP annotations in your public API, including &lt;code&gt;@NotThreadSafe&lt;/code&gt;, comes first, &lt;code&gt;com.github.stephenc.jcip:jcip-annotations&lt;/code&gt; is the right fit. IntelliJ and SpotBugs include this package (&lt;code&gt;net.jcip.annotations&lt;/code&gt;) in their default lists, so a project that uses both tools only needs to add one dependency.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;what_static_analysis_tools_check&quot;&gt;What Static Analysis Tools Check&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Annotations are valuable as documentation alone, but they become more useful when tools catch violations. This section describes how far SpotBugs, Error Prone, and IntelliJ IDEA each go. Tool coverage can change between versions, so here are the versions I ran or checked against documentation for this article.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;caption class=&quot;title&quot;&gt;Table 1. Verification baseline&lt;/caption&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 33.3333%;&quot;&gt;
&lt;col style=&quot;width: 66.6667%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Target&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Version and how it was checked&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Java&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JDK 25&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;SpotBugs&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;SpotBugs 4.10.4 with Gradle plugin 6.5.11, run on the examples&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Error Prone&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Error Prone 2.50.0 with Gradle plugin 5.1.1, run on the examples&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;IntelliJ IDEA&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Inspectopedia 2026.2 documentation and the inspection registrations in &lt;a href=&quot;https://github.com/JetBrains/intellij-community/blob/826413b22cfe5b5c573662f5d2442f454dbc0b23/java/java-backend/resources/META-INF/Inspections.xml&quot;&gt;IntelliJ Community source 826413b22cfe&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;SonarQube&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Built-in rules of the Java analyzer in &lt;a href=&quot;https://github.com/SonarSource/sonar-java/blob/9bf31b6e003782152b920fac0b1cfa5ba55e05a1/java-checks/src/main/java/org/sonar/java/checks/VolatileNonPrimitiveFieldCheck.java&quot;&gt;SonarJava source 9bf31b6e0037&lt;/a&gt; and its list of SpotBugs external rules&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Eclipse JDT&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;a href=&quot;https://help.eclipse.org/latest/topic/org.eclipse.jdt.doc.isv/guide/jdt_api_options.htm&quot;&gt;JDT Core Options&lt;/a&gt; and &lt;a href=&quot;https://github.com/eclipse-jdt/eclipse.jdt.core/tree/8c40c7d2ae12c0a32ab3cca1ab31b53956c65d51&quot;&gt;Eclipse JDT source 8c40c7d2ae12&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In the full list of JDT Core Options as of September 2026 and in Eclipse JDT source 8c40c7d2ae12, I found no compiler check that deals with thread safety annotations. Eclipse users can fill the gap with the &lt;a href=&quot;https://spotbugs.readthedocs.io/en/stable/eclipse.html&quot;&gt;SpotBugs Eclipse plugin&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;At the same point in time, I searched the built-in rule implementations in SonarJava source 9bf31b6e0037, the Java analyzer for SonarQube, for &lt;code&gt;ThreadSafe&lt;/code&gt;, &lt;code&gt;GuardedBy&lt;/code&gt;, and &lt;code&gt;javax.annotation.concurrent&lt;/code&gt;. S3077 was the only implementation that referred to thread safety annotations. I found no rule that directly verifies contracts declared with annotations. The &lt;a href=&quot;https://github.com/SonarSource/sonar-java/blob/9bf31b6e003782152b920fac0b1cfa5ba55e05a1/java-checks/src/main/java/org/sonar/java/checks/VolatileNonPrimitiveFieldCheck.java&quot;&gt;S3077 implementation&lt;/a&gt;, which flags &lt;code&gt;volatile&lt;/code&gt; on reference-type fields, makes an exception when the field&amp;#8217;s type carries &lt;code&gt;@Immutable&lt;/code&gt; or &lt;code&gt;@ThreadSafe&lt;/code&gt; from the JSR-305 package. It does not handle &lt;code&gt;@GuardedBy&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;SonarQube can &lt;a href=&quot;https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/importing-external-issues/external-analyzer-reports&quot;&gt;import SpotBugs reports as external issues&lt;/a&gt;. Its &lt;a href=&quot;https://github.com/SonarSource/sonar-java/blob/9bf31b6e003782152b920fac0b1cfa5ba55e05a1/external-reports/src/main/resources/org/sonar/l10n/java/rules/spotbugs/spotbugs-rules.json&quot;&gt;list of external rules&lt;/a&gt; includes all three bug patterns covered below. To see these results in SonarQube, then, you run SpotBugs in the build to produce an XML report and pass it with &lt;code&gt;sonar.java.spotbugs.reportPaths&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The SpotBugs and Error Prone examples are in &lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis&quot;&gt;examples/thread-safety-static-analysis&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;spotbugs&quot;&gt;SpotBugs&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://spotbugs.github.io/&quot;&gt;SpotBugs&lt;/a&gt;, the successor to FindBugs, has one bug pattern for the JCIP annotations, and it is for &lt;code&gt;@Immutable&lt;/code&gt;. &lt;code&gt;@GuardedBy&lt;/code&gt; and &lt;code&gt;@ThreadSafe&lt;/code&gt; have no dedicated checks. Instead, a detector that looks for inconsistently synchronized fields reads them as input to its judgment. SpotBugs recognizes three packages:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;net.jcip.annotations&lt;/code&gt; (original JCIP)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;javax.annotation.concurrent&lt;/code&gt; (JSR-305)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;jakarta.annotation.concurrent&lt;/code&gt; (recognized in advance as part of SpotBugs&apos; support for the jakarta namespace; Jakarta Annotations 3.0.0 has no such package)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html#jcip-fields-of-immutable-classes-should-be-final-jcip-field-isnt-final-in-immutable-class&quot;&gt;JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS&lt;/a&gt; bug pattern, which dates back to FindBugs 2.0, warns when a class annotated with &lt;code&gt;@Immutable&lt;/code&gt; has a field that is not &lt;code&gt;final&lt;/code&gt;. The SpotBugs 4.10.4 implementation, however, excludes &lt;code&gt;transient&lt;/code&gt; and &lt;code&gt;volatile&lt;/code&gt; fields from this check.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Create a class declared &lt;code&gt;@Immutable&lt;/code&gt; that nevertheless has a setter,&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/spotbugs/src/main/java/net/benelog/Memo.java&quot;&gt;Memo.java&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;package net.benelog;

import net.jcip.annotations.Immutable;

/**
 * Declared {@code @Immutable} but has a non-final field, so
 * SpotBugs reports JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS.
 */
@Immutable
public class Memo {
	private String content;

	public void setContent(String content) {
		this.content = content;
	}

	public String getContent() {
		return content;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;configure the &lt;a href=&quot;https://github.com/spotbugs/spotbugs-gradle-plugin&quot;&gt;SpotBugs plugin&lt;/a&gt; in Gradle,&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/spotbugs/build.gradle.kts&quot;&gt;spotbugs/build.gradle.kts&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;plugins {
	java
	id(&quot;com.github.spotbugs&quot;) version &quot;6.5.11&quot;
}

repositories {
	mavenCentral()
}

dependencies {
	implementation(&quot;com.github.stephenc.jcip:jcip-annotations:1.0-1&quot;)
}

java {
	toolchain {
		languageVersion.set(JavaLanguageVersion.of(25))
	}
}

spotbugs {
	toolVersion.set(&quot;4.10.4&quot;)
	ignoreFailures.set(true)
	if (project.hasProperty(&quot;reportLow&quot;)) {
		// Report low-priority warnings too. The default reports medium and above only.
		reportLevel.set(com.github.spotbugs.snom.Confidence.LOW)
	}
}

tasks.spotbugsMain {
	val report = layout.buildDirectory.file(&quot;reports/spotbugs/main.txt&quot;)
	reports.create(&quot;text&quot;) {
		required.set(true)
		outputLocation.set(report)
	}
	doLast {
		println(report.get().asFile.readText())
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;and run &lt;code&gt;./gradlew :spotbugs:spotbugsMain&lt;/code&gt;. The report contains this line:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code&gt;M B JCIP: Memo.content should be final since net.benelog.Memo is marked as Immutable.  In Memo.java&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The example sets &lt;code&gt;ignoreFailures&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; so that the report is printed in full even when there are warnings. To fail the build on violations in real CI, change this setting to &lt;code&gt;false&lt;/code&gt; or omit it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;@GuardedBy&lt;/code&gt; is handled by the &lt;a href=&quot;https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html#is-field-not-guarded-against-concurrent-access-is-field-not-guarded&quot;&gt;IS_FIELD_NOT_GUARDED&lt;/a&gt; bug pattern. This pattern, however, does not find violations by looking at the annotation. The &lt;a href=&quot;https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html#is-inconsistent-synchronization-is2-inconsistent-sync&quot;&gt;IS2_INCONSISTENT_SYNC&lt;/a&gt; detector estimates missing synchronization from the proportion of field accesses made while holding a lock. When a field carries &lt;code&gt;@GuardedBy(&quot;this&quot;)&lt;/code&gt;, the detector treats it differently in three ways:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;It renames the bug pattern to IS_FIELD_NOT_GUARDED.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It keeps fields as candidates even if no access holds the lock.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It raises the warning priority.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The only value it recognizes is &lt;code&gt;&quot;this&quot;&lt;/code&gt;. The detector treats fields where less than half of the accesses hold the lock as likely false positives and lowers their priority.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;That is why &lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/spotbugs/src/main/java/net/benelog/Counter.java&quot;&gt;Counter&lt;/a&gt; in the same project is not reported with the default settings, even though it modifies a &lt;code&gt;@GuardedBy(&quot;this&quot;)&lt;/code&gt; field without a lock. The read and write in &lt;code&gt;increment()&lt;/code&gt; happen without a lock, and only the read in the &lt;code&gt;synchronized&lt;/code&gt; method &lt;code&gt;get()&lt;/code&gt; holds it. One of three accesses, or 33%, holds the lock. Only when you make the example report low-priority warnings too with &lt;code&gt;./gradlew :spotbugs:spotbugsMain -PreportLow&lt;/code&gt; does it appear.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code&gt;L M IS: Counter.count not guarded against concurrent access; locked 33% of time  Unsynchronized access at Counter.java:[line 16]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The class below has the same violation plus two more &lt;code&gt;synchronized&lt;/code&gt; methods, which brings the locked accesses to 60%.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/spotbugs/src/main/java/net/benelog/LockedCounter.java&quot;&gt;LockedCounter.java&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;package net.benelog;

import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;

/**
 * Same violation as Counter, but more synchronized methods raise
 * the proportion of locked accesses. SpotBugs reports IS_FIELD_NOT_GUARDED.
 */
@ThreadSafe
public class LockedCounter {
	@GuardedBy(&quot;this&quot;)
	private int count;

	public void increment() {
		count++;
	}

	public synchronized int get() {
		return count;
	}

	public synchronized void reset() {
		count = 0;
	}

	public synchronized boolean isZero() {
		return count == 0;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This class is reported with high priority even with the default settings. The first letter of the output is the priority and the second is the category.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code&gt;H M IS: LockedCounter.count not guarded against concurrent access; locked 60% of time  Unsynchronized access at LockedCounter.java:[line 16]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The same detector also reads &lt;code&gt;@ThreadSafe&lt;/code&gt; and &lt;code&gt;@NotThreadSafe&lt;/code&gt;. It excludes fields of &lt;code&gt;@NotThreadSafe&lt;/code&gt; classes from the check and uses &lt;code&gt;@ThreadSafe&lt;/code&gt; on a class as grounds for raising the warning priority of its fields. It does not verify that the contract declared by the annotation is kept. A class declared &lt;code&gt;@ThreadSafe&lt;/code&gt; can hold a field of a &lt;code&gt;@NotThreadSafe&lt;/code&gt; type without a warning.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In short, the only thing SpotBugs checks deterministically from annotations alone is the &lt;code&gt;final&lt;/code&gt; rule for &lt;code&gt;@Immutable&lt;/code&gt; classes. &lt;code&gt;@GuardedBy&lt;/code&gt; violations are also reported as estimates based on the proportion of locked accesses; the annotation only changes the candidates and priority of that estimate.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;error_prone&quot;&gt;Error Prone&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Google&amp;#8217;s &lt;a href=&quot;https://errorprone.info/&quot;&gt;Error Prone&lt;/a&gt; plugs into &lt;code&gt;javac&lt;/code&gt; and checks rules that prevent errors at compile time.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;checking_guardedby&quot;&gt;Checking @GuardedBy&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Error Prone&amp;#8217;s &lt;a href=&quot;https://errorprone.info/bugpattern/GuardedBy&quot;&gt;GuardedBy check&lt;/a&gt; reports a compile error when a field or method annotated with &lt;code&gt;@GuardedBy(lock)&lt;/code&gt; is accessed without holding the specified lock. Unlike SpotBugs, whose reporting depends on access proportions, it decides for each access within its analysis scope whether the lock is held. It does not check accesses inside constructors and initializer blocks, or fields protected by a &lt;code&gt;ReadWriteLock&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The annotations this check recognizes include:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;com.google.errorprone.annotations.concurrent.GuardedBy&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;javax.annotation.concurrent.GuardedBy&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;androidx.annotation.GuardedBy&lt;/code&gt; for Android&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The original JCIP &lt;code&gt;net.jcip.annotations.GuardedBy&lt;/code&gt; is not on the list. The documentation states that the &lt;code&gt;@Immutable&lt;/code&gt; check targets only &lt;code&gt;com.google.errorprone.annotations.Immutable&lt;/code&gt; and excludes &lt;code&gt;javax.annotation.concurrent.Immutable&lt;/code&gt;. The next section confirms this.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In Gradle, the &lt;a href=&quot;https://github.com/tbroyer/gradle-errorprone-plugin&quot;&gt;net.ltgt.errorprone plugin&lt;/a&gt; attaches Error Prone to &lt;code&gt;javac&lt;/code&gt;. To compare annotations from three packages, the example includes all three libraries.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/errorprone/build.gradle.kts&quot;&gt;errorprone/build.gradle.kts&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;plugins {
	java
	id(&quot;net.ltgt.errorprone&quot;) version &quot;5.1.1&quot;
}

repositories {
	mavenCentral()
}

dependencies {
	implementation(&quot;com.github.stephenc.jcip:jcip-annotations:1.0-1&quot;)
	implementation(&quot;com.google.code.findbugs:jsr305:3.0.2&quot;)
	implementation(&quot;com.google.errorprone:error_prone_annotations:2.50.0&quot;)
	errorprone(&quot;com.google.errorprone:error_prone_core:2.50.0&quot;)
}

java {
	toolchain {
		languageVersion.set(JavaLanguageVersion.of(25))
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Compiling the following class, which uses the JSR-305 &lt;code&gt;@GuardedBy&lt;/code&gt;,&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/errorprone/src/main/java/net/benelog/JsrCounter.java&quot;&gt;JsrCounter.java&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;package net.benelog;

import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;

/**
 * JSR-305 package. Error Prone reports the @GuardedBy violation as a compile error.
 */
@ThreadSafe
public class JsrCounter {
	@GuardedBy(&quot;this&quot;)
	private int count;

	public void increment() {
		count++;
	}

	public synchronized int get() {
		return count;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;makes Error Prone 2.50.0 report this error:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code&gt;JsrCounter.java:15: error: [GuardedBy] This access should be guarded by &apos;this&apos;, which is not currently held
		count++;
		^
    (see https://errorprone.info/bugpattern/GuardedBy)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/errorprone/src/main/java/net/benelog/JcipCounter.java&quot;&gt;JcipCounter&lt;/a&gt;, the same code with only the import changed to &lt;code&gt;net.jcip.annotations.GuardedBy&lt;/code&gt;, compiles without any error. Conversely, &lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/errorprone/src/main/java/net/benelog/ErrorProneCounter.java&quot;&gt;ErrorProneCounter&lt;/a&gt;, which uses &lt;code&gt;@GuardedBy&lt;/code&gt; from Error Prone&amp;#8217;s own package, is caught with the same error as JsrCounter. The three classes are in the errorprone subproject of the example repository, and you can check them with &lt;code&gt;./gradlew :errorprone:compileJava&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;checking_immutable&quot;&gt;Checking @Immutable&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://errorprone.info/bugpattern/Immutable&quot;&gt;Immutable check&lt;/a&gt; verifies that a class annotated with &lt;code&gt;com.google.errorprone.annotations.Immutable&lt;/code&gt; is deeply immutable. The SpotBugs JCIP check only looks at whether fields are &lt;code&gt;final&lt;/code&gt;, while Error Prone also checks whether the types of reference fields are immutable. The conservative definition of immutability in the Error Prone documentation also requires that &lt;code&gt;this&lt;/code&gt; not escape from the constructor. The &lt;code&gt;ImmutableChecker&lt;/code&gt; in 2.50.0, however, does not analyze &lt;code&gt;this&lt;/code&gt; escaping from ordinary constructor bodies. The class below has one field that is not &lt;code&gt;final&lt;/code&gt; and one field that is &lt;code&gt;final&lt;/code&gt; but of a mutable type.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/errorprone/src/main/java/net/benelog/ErrorProneMemo.java&quot;&gt;ErrorProneMemo.java&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;package net.benelog;

import java.util.List;

import com.google.errorprone.annotations.Immutable;

/**
 * Error Prone&apos;s own @Immutable. Both the non-final field and
 * the final field of a mutable type are reported as compile errors.
 */
@Immutable
public class ErrorProneMemo {
	private String content;
	private final List&amp;lt;String&amp;gt; tags;

	public ErrorProneMemo(String content, List&amp;lt;String&amp;gt; tags) {
		this.content = content;
		this.tags = tags;
	}

	public String getContent() {
		return content;
	}

	public List&amp;lt;String&amp;gt; getTags() {
		return tags;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Both fields are reported as compile errors.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code&gt;ErrorProneMemo.java:13: error: [Immutable] type annotated with @Immutable could not be proven immutable: &apos;ErrorProneMemo&apos; has non-final field &apos;content&apos;
	private String content;
	               ^
    (see https://errorprone.info/bugpattern/Immutable)
  Did you mean &apos;private final String content;&apos;?
ErrorProneMemo.java:14: error: [Immutable] type annotated with @Immutable could not be proven immutable: &apos;ErrorProneMemo&apos; has field &apos;tags&apos; of type &apos;java.util.List&amp;lt;java.lang.String&amp;gt;&apos;, &apos;List&apos; is mutable
	private final List&amp;lt;String&amp;gt; tags;
	                           ^
    (see https://errorprone.info/bugpattern/Immutable)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;For &lt;code&gt;tags&lt;/code&gt; to pass, its type must be one that Error Prone knows to be immutable or one annotated with &lt;code&gt;@Immutable&lt;/code&gt;. A class that implements an interface annotated with &lt;code&gt;@Immutable&lt;/code&gt; is subject to the same check. For generic classes, the &lt;code&gt;containerOf&lt;/code&gt; attribute specifies which type parameters must be immutable.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/errorprone/src/main/java/net/benelog/JsrMemo.java&quot;&gt;JsrMemo&lt;/a&gt;, the same code with only the import changed to &lt;code&gt;javax.annotation.concurrent.Immutable&lt;/code&gt;, compiles without errors. &lt;code&gt;@GuardedBy&lt;/code&gt; is checked for the JSR-305 package too, but &lt;code&gt;@Immutable&lt;/code&gt; is checked only for Error Prone&amp;#8217;s own package.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;checking_threadsafe&quot;&gt;Checking @ThreadSafe&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;com.google.errorprone.annotations.ThreadSafe&lt;/code&gt; also has a &lt;a href=&quot;https://errorprone.info/bugpattern/ThreadSafe&quot;&gt;ThreadSafe check&lt;/a&gt; page. It sits in the &quot;Experimental&quot; group of the bug pattern list, so it looks as if you only need to turn it on, but in fact you cannot. Enabling it in the Gradle plugin with &lt;code&gt;options.errorprone.error(&quot;ThreadSafe&quot;)&lt;/code&gt; fails the build with the error &quot;ThreadSafe is not a valid checker name&quot;. &lt;code&gt;BuiltInCheckerSuppliers&lt;/code&gt;, the list of built-in checks in the Error Prone 2.50.0 source, contains &lt;code&gt;GuardedByChecker&lt;/code&gt; and &lt;code&gt;ImmutableChecker&lt;/code&gt; but not &lt;code&gt;ThreadSafeChecker&lt;/code&gt;. It was also missing from the earlier versions I checked, 2.3.0 through 2.45.0. The checker class itself is included in the &lt;code&gt;error_prone_core&lt;/code&gt; jar.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;So the class below compiles without any error under the default settings.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/errorprone/src/main/java/net/benelog/ErrorProneRegistry.java&quot;&gt;ErrorProneRegistry.java&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;package net.benelog;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.google.errorprone.annotations.ThreadSafe;

/**
 * Error Prone&apos;s own @ThreadSafe. Error Prone 2.50.0 does not check it by default.
 * Registering ThreadSafeChecker with -PthreadSafeCheck reports fields modified without a lock
 * and final fields of non-thread-safe types as compile errors.
 */
@ThreadSafe
public class ErrorProneRegistry {
	private int count;
	private final Map&amp;lt;String, String&amp;gt; entries = new HashMap&amp;lt;&amp;gt;();
	private final ConcurrentHashMap&amp;lt;String, String&amp;gt; safeEntries = new ConcurrentHashMap&amp;lt;&amp;gt;();

	public void register(String key, String value) {
		count++;
		entries.put(key, value);
		safeEntries.put(key, value);
	}

	public int getCount() {
		return count;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To see what this checker looks at, I created the &lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/threadsafe-check&quot;&gt;threadsafe-check&lt;/a&gt; subproject in the example repository. It is experimental code that registers a subclass of &lt;code&gt;ThreadSafeChecker&lt;/code&gt; in &lt;code&gt;META-INF/services&lt;/code&gt; so that Error Prone loads it as a plugin check. Error Prone uses ServiceLoader to find checks on the compiler&amp;#8217;s annotation processor path, with &lt;code&gt;com.google.errorprone.bugpatterns.BugChecker&lt;/code&gt; as the service interface. So all you need is a file with that name that lists the checker class name.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/threadsafe-check/src/main/resources/META-INF/services/com.google.errorprone.bugpatterns.BugChecker&quot;&gt;META-INF/services/com.google.errorprone.bugpatterns.BugChecker&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code&gt;com.google.errorprone.bugpatterns.threadsafety.ThreadSafeCheck&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The registered class is a wrapper that extends &lt;code&gt;ThreadSafeChecker&lt;/code&gt; and names the check with &lt;code&gt;@BugPattern&lt;/code&gt;. Because the constructor of &lt;code&gt;ThreadSafeChecker&lt;/code&gt; is package-private, the wrapper uses the same package name. It also has a nominal public no-argument constructor, which ServiceLoader requires. Since this code depends on the internals of 2.50.0, I do not recommend it for real projects.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/threadsafe-check/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeCheck.java&quot;&gt;ThreadSafeCheck.java&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;package com.google.errorprone.bugpatterns.threadsafety;

@BugPattern(
		name = &quot;ThreadSafe&quot;,
		summary = &quot;Type declaration annotated with @ThreadSafe is not thread safe&quot;,
		severity = ERROR)
public class ThreadSafeCheck extends ThreadSafeChecker {

	/** Public no-argument constructor required by ServiceLoader. Error Prone uses the @Inject constructor. */
	public ThreadSafeCheck() {
		super(null, null);
	}

	@Inject
	ThreadSafeCheck(WellKnownThreadSafety wellKnownThreadSafety,
			ThreadSafeAnalysis.Factory threadSafeAnalysisFactory) {
		super(wellKnownThreadSafety, threadSafeAnalysisFactory);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Adding this subproject as a dependency in the &lt;code&gt;errorprone&lt;/code&gt; configuration of the project under analysis registers it as a plugin check. The example adds it only when a Gradle property is present; this part was omitted from the &lt;code&gt;build.gradle.kts&lt;/code&gt; quoted earlier.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-static-analysis/errorprone/build.gradle.kts&quot;&gt;errorprone/build.gradle.kts&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;dependencies {
	errorprone(&quot;com.google.errorprone:error_prone_core:2.50.0&quot;)
	if (project.hasProperty(&quot;threadSafeCheck&quot;)) {
		// Register ThreadSafeChecker, which is not in the default check list, as a plugin.
		errorprone(project(&quot;:threadsafe-check&quot;))
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Running &lt;code&gt;./gradlew :errorprone:compileJava -PthreadSafeCheck&lt;/code&gt; reports the following:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code&gt;ErrorProneRegistry.java:16: error: [ThreadSafe] @ThreadSafe class fields should be final or annotated with @GuardedBy. See https://errorprone.info/bugpattern/ThreadSafe for details.
	private int count;
	            ^
    (see https://errorprone.info/bugpattern/ThreadSafe)
  Did you mean &apos;private final int count;&apos;?
ErrorProneRegistry.java:17: error: [ThreadSafe] @ThreadSafe class has non-thread-safe field, &apos;Map&apos; is not thread-safe
	private final Map&amp;lt;String, String&amp;gt; entries = new HashMap&amp;lt;&amp;gt;();
	                                  ^
    (see https://errorprone.info/bugpattern/ThreadSafe)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To pass this check, every instance field must be one of the following:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;final&lt;/code&gt;, with a declared type that Error Prone knows to be thread-safe&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;annotated with &lt;code&gt;@GuardedBy&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;static&lt;/code&gt; fields are not checked, and &lt;code&gt;@LazyInit&lt;/code&gt; fields are exempt from the &lt;code&gt;final&lt;/code&gt; requirement. &lt;code&gt;count&lt;/code&gt; is caught because it is neither &lt;code&gt;final&lt;/code&gt; nor &lt;code&gt;@GuardedBy&lt;/code&gt;. &lt;code&gt;entries&lt;/code&gt; is caught because its declared type &lt;code&gt;Map&lt;/code&gt; is not a thread-safe type. Even if the actual object is a &lt;code&gt;ConcurrentHashMap&lt;/code&gt;, a field declared as &lt;code&gt;Map&lt;/code&gt; is caught. &lt;code&gt;safeEntries&lt;/code&gt;, whose declared type is also &lt;code&gt;ConcurrentHashMap&lt;/code&gt;, passes. ErrorProneCounter, shown earlier, passes this check because &lt;code&gt;count&lt;/code&gt; has &lt;code&gt;@GuardedBy(&quot;this&quot;)&lt;/code&gt;, and is caught only by the &lt;code&gt;@GuardedBy&lt;/code&gt; check.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In short, as of September 2026, Error Prone checks &lt;code&gt;@Immutable&lt;/code&gt; and &lt;code&gt;@GuardedBy&lt;/code&gt;, but &lt;code&gt;@ThreadSafe&lt;/code&gt; serves only as documentation unless you register the checker yourself as a plugin, as shown above.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;intellij_idea&quot;&gt;IntelliJ IDEA&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;IntelliJ IDEA 2026.2 has six inspections in the &lt;a href=&quot;https://www.jetbrains.com/help/inspectopedia/Java-Concurrency-annotation-issues.html&quot;&gt;Concurrency annotation issues&lt;/a&gt; group. Five are for &lt;code&gt;@GuardedBy&lt;/code&gt; and one is for &lt;code&gt;@Immutable&lt;/code&gt;. No inspection verifies contracts declared with &lt;code&gt;@ThreadSafe&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;img/thread-safety-annotations/intellij-concurrency-inspections.png&quot; alt=&quot;Concurrency annotation issues inspections in IntelliJ IDEA 2026.2&quot; width=&quot;800&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.jetbrains.com/help/inspectopedia/FieldAccessNotGuarded.html&quot;&gt;Unguarded field access or method call&lt;/a&gt; inspection recognizes &lt;code&gt;@GuardedBy&lt;/code&gt; from all of these packages:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;net.jcip.annotations&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;javax.annotation.concurrent&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;org.apache.http.annotation&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;com.android.annotations.concurrency&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;androidx.annotation&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;com.google.errorprone.annotations.concurrent&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;SpotBugs also reads the original JCIP package, but it handles only &lt;code&gt;@GuardedBy(&quot;this&quot;)&lt;/code&gt; and stops at warnings guessed from access proportions. IntelliJ, by comparison, directly checks the guard expressions of the original JCIP annotation.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.jetbrains.com/help/inspectopedia/NonFinalFieldInImmutable.html&quot;&gt;Non-final field in @Immutable class&lt;/a&gt; inspection warns when an &lt;code&gt;@Immutable&lt;/code&gt; class has a field that is not &lt;code&gt;final&lt;/code&gt;. It does not check whether field types are mutable, so its depth is closer to SpotBugs than to Error Prone. Unlike SpotBugs, though, it also warns about &lt;code&gt;transient&lt;/code&gt; and &lt;code&gt;volatile&lt;/code&gt; fields. The markers this inspection recognizes are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;@Immutable&lt;/code&gt; in the original JCIP and JSR-305 packages&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Error Prone&amp;#8217;s &lt;code&gt;com.google.errorprone.annotations.Immutable&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;AutoValue&amp;#8217;s &lt;code&gt;@AutoValue&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The Javadoc &lt;code&gt;@Immutable&lt;/code&gt; tag&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;@ThreadSafe&lt;/code&gt; is read only as a hint by the &lt;a href=&quot;https://www.jetbrains.com/help/inspectopedia/AccessToStaticFieldLockedOnInstance.html&quot;&gt;Access to static field locked on instance&lt;/a&gt; inspection in the &quot;Threading issues&quot; group. This inspection warns when code holding an instance lock accesses a non-constant &lt;code&gt;static&lt;/code&gt; field. If the accessed field is &lt;code&gt;final&lt;/code&gt;, it &lt;a href=&quot;https://github.com/JetBrains/intellij-community/blob/826413b22cfe5b5c573662f5d2442f454dbc0b23/java/java-impl-inspections/src/com/siyeh/ig/threading/AccessToStaticFieldLockedOnInstanceInspection.java&quot;&gt;checks the annotations on the field&amp;#8217;s declared type&lt;/a&gt; and skips the warning if a registered thread safety annotation is present. It therefore does not verify the contract of the annotated class itself. The &lt;a href=&quot;https://github.com/JetBrains/intellij-community/blob/826413b22cfe5b5c573662f5d2442f454dbc0b23/java/java-psi-api/src/com/intellij/codeInsight/ConcurrencyAnnotationsManager.java&quot;&gt;default list&lt;/a&gt; contains these annotations:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;@ThreadSafe&lt;/code&gt; in the original JCIP, JSR-305, &lt;code&gt;org.apache.http.annotation&lt;/code&gt;, and &lt;code&gt;com.android.annotations.concurrency&lt;/code&gt; packages&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;@AnyThread&lt;/code&gt; in the &lt;code&gt;androidx.annotation&lt;/code&gt; and &lt;code&gt;android.support.annotation&lt;/code&gt; packages&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Error Prone&amp;#8217;s &lt;code&gt;@ThreadSafe&lt;/code&gt; is not in the default list.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;However, the &lt;a href=&quot;https://github.com/JetBrains/intellij-community/blob/826413b22cfe5b5c573662f5d2442f454dbc0b23/java/java-backend/resources/META-INF/Inspections.xml&quot;&gt;inspection registrations&lt;/a&gt; in IntelliJ IDEA 2026.2 declare every inspection in the &quot;Concurrency annotation issues&quot; group with &lt;code&gt;enabledByDefault=&quot;false&quot;&lt;/code&gt; and &lt;code&gt;level=&quot;WARNING&quot;&lt;/code&gt;. You have to turn them on yourself under Settings | Editor | Inspections | Java | Concurrency annotation issues. Even when enabled, they are editor warnings and cannot block &lt;code&gt;javac&lt;/code&gt; compilation the way Error Prone does. Enforcing them in CI requires a separate runner such as Qodana. &lt;a href=&quot;https://www.jetbrains.com/help/qodana/about-qodana.html&quot;&gt;Qodana&lt;/a&gt; is a code quality platform from JetBrains that runs the same inspections as IntelliJ IDEA in CI, as a Docker image or a command-line tool. You enable the checks in a &lt;a href=&quot;https://www.jetbrains.com/help/qodana/inspection-profiles.html&quot;&gt;Qodana profile&lt;/a&gt; and set a &lt;a href=&quot;https://www.jetbrains.com/help/qodana/quality-gate.html&quot;&gt;failure condition&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Across the three tools, the range checked deterministically from annotations alone is narrow.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;SpotBugs directly checks only the &lt;code&gt;final&lt;/code&gt; rule for &lt;code&gt;@Immutable&lt;/code&gt; classes. It guesses &lt;code&gt;@GuardedBy&lt;/code&gt; violations from the proportion of locked accesses, and reads &lt;code&gt;@ThreadSafe&lt;/code&gt; and &lt;code&gt;@NotThreadSafe&lt;/code&gt; only as input to that guess.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Error Prone catches missing locks for &lt;code&gt;@GuardedBy&lt;/code&gt; and deep immutability for &lt;code&gt;@Immutable&lt;/code&gt; as compile errors. It does not recognize the original JCIP package, however, and the &lt;code&gt;@ThreadSafe&lt;/code&gt; check works only if you register it separately.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;IntelliJ IDEA directly checks &lt;code&gt;@GuardedBy&lt;/code&gt; from many packages, including the original JCIP, and checks &lt;code&gt;@Immutable&lt;/code&gt; at about the same depth as SpotBugs. Its inspections are off by default, though, and are not tied to &lt;code&gt;javac&lt;/code&gt; compilation. Enforcing them requires a separate runner such as Qodana or a command-line inspection.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This is why I recommended either &lt;code&gt;error_prone_annotations&lt;/code&gt; or &lt;code&gt;jcip-annotations&lt;/code&gt; earlier. If you need checks that block compilation, use annotations that Error Prone recognizes. If you want to mark all four annotations and still get checks from your IDE and SpotBugs, use the &lt;code&gt;net.jcip.annotations&lt;/code&gt; package (the Apache-licensed reimplementation).&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;project_specific_rules_verified_with_archunit&quot;&gt;Project-Specific Rules Verified with ArchUnit&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The static analysis tools above partially check whether a class carrying an annotation is implemented as declared. Application code, however, has another kind of accident: an object shared by multiple threads holds, as a field and without synchronization, a class marked as not thread-safe.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;For example, Spring &lt;code&gt;@RestController&lt;/code&gt; and &lt;code&gt;@Service&lt;/code&gt; beans are singletons unless a separate scope is specified, so all request threads share their fields. You could use another scope such as request scope. Some projects, however, prevent the problem by making it a policy that controllers must not hold such types as fields at all.
Rules like this depend on the structure each project aims for, so general-purpose static analysis tools do not have them. With &lt;a href=&quot;https://www.archunit.org/&quot;&gt;ArchUnit&lt;/a&gt;, you can write such a rule as a JUnit test and check it on every build.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;example_code&quot;&gt;Example Code&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The example has a class annotated with &lt;code&gt;@NotThreadSafe&lt;/code&gt; and a controller that holds it as a field. The controller also has a &lt;code&gt;SimpleDateFormat&lt;/code&gt; field. The full project is in &lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-archunit&quot;&gt;examples/thread-safety-archunit&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-archunit/src/main/java/net/benelog/report/ReportFormatter.java&quot;&gt;ReportFormatter.java&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;package net.benelog.report;

import net.jcip.annotations.NotThreadSafe;

@NotThreadSafe
public class ReportFormatter {
	private final StringBuilder buffer = new StringBuilder();

	public String format(String title, String body) {
		buffer.setLength(0);
		return buffer.append(title).append(&apos;\n&apos;).append(body).toString();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-archunit/src/main/java/net/benelog/web/ReportController.java&quot;&gt;ReportController.java&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;package net.benelog.web;

import java.text.SimpleDateFormat;
import java.util.Date;

import net.benelog.report.ReportFormatter;
import net.benelog.report.ReportService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ReportController {
	private final ReportService reportService;
	private final ReportFormatter formatter = new ReportFormatter();
	private final SimpleDateFormat dateFormat = new SimpleDateFormat(&quot;yyyy-MM-dd&quot;);

	public ReportController(ReportService reportService) {
		this.reportService = reportService;
	}

	@GetMapping(&quot;/reports/{id}&quot;)
	public String report(@PathVariable long id) {
		return formatter.format(dateFormat.format(new Date()), reportService.find(id));
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;rules_and_results&quot;&gt;Rules and Results&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The ArchUnit test consists of two rules.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The first rule fails if the type of any field in a class annotated with &lt;code&gt;@RestController&lt;/code&gt; carries &lt;code&gt;@NotThreadSafe&lt;/code&gt;, without separately analyzing external synchronization or bean scope. ArchUnit reads annotations from bytecode. It can therefore check annotations with RUNTIME retention, such as the original JCIP annotations, as well as those with CLASS retention, such as the JSR-305 annotations and HttpClient&amp;#8217;s &lt;code&gt;@Contract&lt;/code&gt;. The example rule looks only at &lt;code&gt;net.jcip.annotations.NotThreadSafe&lt;/code&gt;, though, so checking annotations from other packages or the &lt;code&gt;threading&lt;/code&gt; value of &lt;code&gt;@Contract&lt;/code&gt; requires additional conditions. Library classes outside the analyzed packages are also read from the classpath under the default settings, so the same rule can check &lt;code&gt;@NotThreadSafe&lt;/code&gt; that a library has applied.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The second rule is for JDK classes that carry no thread safety annotation, such as &lt;code&gt;SimpleDateFormat&lt;/code&gt;. It lists &lt;code&gt;Format&lt;/code&gt;, &lt;code&gt;Calendar&lt;/code&gt;, and &lt;code&gt;StringBuilder&lt;/code&gt; explicitly.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-archunit/src/test/java/net/benelog/ThreadSafetyArchTest.java&quot;&gt;ThreadSafetyArchTest.java&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;package net.benelog;

import static com.tngtech.archunit.core.domain.JavaClass.Predicates.assignableTo;
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields;

import java.text.Format;
import java.util.Calendar;

import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;
import net.jcip.annotations.NotThreadSafe;
import org.springframework.web.bind.annotation.RestController;

@AnalyzeClasses(packages = &quot;net.benelog&quot;)
class ThreadSafetyArchTest {

	@ArchTest
	static final ArchRule controllers_should_not_hold_not_thread_safe_types =
			fields().that().areDeclaredInClassesThat().areAnnotatedWith(RestController.class)
					.should().notHaveRawType(annotatedWith(NotThreadSafe.class))
					.because(&quot;controllers are singletons by default, so all request threads share their fields&quot;);

	private static final DescribedPredicate&amp;lt;JavaClass&amp;gt; KNOWN_NOT_THREAD_SAFE_JDK_TYPES =
			assignableTo(Format.class)
					.or(assignableTo(Calendar.class))
					.or(assignableTo(StringBuilder.class))
					.as(&quot;JDK types that are not thread-safe (Format, Calendar, StringBuilder)&quot;);

	@ArchTest
	static final ArchRule controllers_should_not_hold_known_not_thread_safe_jdk_types =
			fields().that().areDeclaredInClassesThat().areAnnotatedWith(RestController.class)
					.should().notHaveRawType(KNOWN_NOT_THREAD_SAFE_JDK_TYPES)
					.because(&quot;JDK classes carry no thread safety annotations, so a list blocks them&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Running &lt;code&gt;./gradlew test&lt;/code&gt; with ArchUnit 1.5.0, Spring Web 7.0.9, JUnit 6.1.3, and JDK 25 fails both rules and reports which field broke each rule.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code&gt;Architecture Violation [Priority: MEDIUM] - Rule &apos;fields that are declared in classes that are annotated with @RestController should not have raw type annotated with @NotThreadSafe, because controllers are singletons by default, so all request threads share their fields&apos; was violated (1 times):
Field &amp;lt;net.benelog.web.ReportController.formatter&amp;gt; has raw type annotated with @NotThreadSafe in (ReportController.java:0)

Architecture Violation [Priority: MEDIUM] - Rule &apos;fields that are declared in classes that are annotated with @RestController should not have raw type JDK types that are not thread-safe (Format, Calendar, StringBuilder), because JDK classes carry no thread safety annotations, so a list blocks them&apos; was violated (1 times):
Field &amp;lt;net.benelog.web.ReportController.dateFormat&amp;gt; has raw type JDK types that are not thread-safe (Format, Calendar, StringBuilder) in (ReportController.java:0)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/thread-safety-archunit/src/main/java/net/benelog/web/HealthController.java&quot;&gt;HealthController&lt;/a&gt; in the same project, which holds only &lt;code&gt;Clock&lt;/code&gt; and &lt;code&gt;DateTimeFormatter&lt;/code&gt; as fields, passed both rules.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;ReportController&lt;/code&gt;, caught by the rules, can be fixed by holding the immutable &lt;code&gt;DateTimeFormatter&lt;/code&gt; as a field instead of &lt;code&gt;SimpleDateFormat&lt;/code&gt;, and by confining &lt;code&gt;ReportFormatter&lt;/code&gt; to a local variable of the request-handling method.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;ReportController after the fix&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(&quot;yyyy-MM-dd&quot;);

@GetMapping(&quot;/reports/{id}&quot;)
public String report(@PathVariable long id) {
	ReportFormatter formatter = new ReportFormatter();
	return formatter.format(DATE_FORMAT.format(LocalDate.now()), reportService.find(id));
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;After changing &lt;code&gt;ReportController&lt;/code&gt; this way and running the test again, both rules pass. &lt;code&gt;ReportFormatter&lt;/code&gt; is created anew on each call and is not shared with other request threads. Passing the rules, however, does not prove the thread safety of the whole controller. Other shared state and race conditions in sequences of method calls need separate review.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This approach also has limits.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In this rule, ArchUnit looks only at the raw declared type of a field. It cannot catch an &lt;code&gt;ArrayList&lt;/code&gt; assigned to a field declared as &lt;code&gt;List&lt;/code&gt;, or a non-thread-safe type used as a generic type argument.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It also misses cases where only a supertype carries the annotation and the actual declared type does not, unless you traverse the hierarchy separately.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The rule does not judge whether field access is protected by an external lock or whether the controller has a separate scope.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;JDK types without annotations require a manually maintained list, as in the second rule.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The rule looks only at fields declared directly in classes annotated with &lt;code&gt;@RestController&lt;/code&gt;. It therefore also misses fields inherited from an unannotated superclass.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When a Java class is used by multiple threads in a way it was not designed for, the side effects are serious and the source of the problem is hard to trace. That is why thread safety must be documented clearly. Writing it in Javadoc prose, however, leaves its place and wording different in every class, and it works only if people read carefully.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Marking thread safety with annotations gives it a consistent place and format and makes it readable by tools. I recommend Error Prone&amp;#8217;s annotations or the Apache-licensed reimplementation of the JCIP annotations. Error Prone blocks &lt;code&gt;@GuardedBy&lt;/code&gt; and &lt;code&gt;@Immutable&lt;/code&gt; violations with compile errors, IntelliJ IDEA reports some violations as editor warnings, and SpotBugs reports some in its analysis reports. None of the three tools, however, verifies contracts declared with &lt;code&gt;@ThreadSafe&lt;/code&gt; under default settings.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;For thread safety rules specific to your project, I recommend checking them with ArchUnit.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Books&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.oreilly.com/library/view/effective-java-3rd/9780134686097/&quot;&gt;Effective Java, Third Edition&lt;/a&gt;: Item 82. Document thread safety&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://jcip.net/&quot;&gt;Java Concurrency in Practice&lt;/a&gt;: 4.5 Documenting synchronization policies, Appendix A. Annotations for Concurrency&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Annotation libraries&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://jcip.net/annotations/doc/net/jcip/annotations/package-summary.html&quot;&gt;JCIP annotations Javadoc&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/stephenc/jcip-annotations&quot;&gt;JCIP Annotations under Apache License&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://errorprone.info/api/latest/com/google/errorprone/annotations/package-summary.html&quot;&gt;Error Prone annotations Javadoc&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google/guava/issues/2960&quot;&gt;Guava issue #2960: Migrate off of jsr305&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Javadoc examples&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se25/html/jls-8.html#jls-8.4.3.6&quot;&gt;JLS 25: synchronized Methods&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/LinkedList.html&quot;&gt;JDK 25 LinkedList Javadoc&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Collections.html#synchronizedList(java.util.List)&quot;&gt;JDK 25 Collections.synchronizedList Javadoc&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/ConcurrentModificationException.html&quot;&gt;JDK 25 ConcurrentModificationException Javadoc&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/text/SimpleDateFormat.html&quot;&gt;JDK 25 SimpleDateFormat Javadoc&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/format/DateTimeFormatter.html&quot;&gt;JDK 25 DateTimeFormatter Javadoc&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-8198249&quot;&gt;JDK-8198249: Remove deprecated Runtime::runFinalizersOnExit and System::runFinalizersOnExit&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.spring.io/spring-batch/docs/current/api/org/springframework/batch/item/file/FlatFileItemWriter.html&quot;&gt;Spring Batch FlatFileItemWriter Javadoc&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://hc.apache.org/httpcomponents-core-5.3.x/current/httpcore5/apidocs/org/apache/hc/core5/annotation/ThreadingBehavior.html&quot;&gt;HttpCore 5 ThreadingBehavior Javadoc&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/apache/httpcomponents-client/tree/rel/v5.5/httpclient5/src/main/java/org/apache/hc/client5/http/impl&quot;&gt;HttpClient 5.5 source&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/apache/httpcomponents-client/tree/rel/v4.5.2/httpclient/src/main/java/org/apache/http&quot;&gt;HttpClient 4.5.2 source&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://issues.apache.org/jira/browse/HTTPCLIENT-1743&quot;&gt;HTTPCLIENT-1743: Migrate from CC-BY licensed source&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/apache/httpcomponents-core/blob/4.4.x/RELEASE_NOTES.txt&quot;&gt;HttpCore 4.4.x Release Notes&lt;/a&gt;: Release 4.4.5&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Static analysis tools&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;SpotBugs&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html&quot;&gt;SpotBugs Bug descriptions&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://spotbugs.readthedocs.io/en/stable/eclipse.html&quot;&gt;SpotBugs Eclipse plugin&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Error Prone&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://errorprone.info/bugpattern/GuardedBy&quot;&gt;Error Prone: GuardedBy&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://errorprone.info/bugpattern/Immutable&quot;&gt;Error Prone: Immutable&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SonarQube&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/SonarSource/sonar-java/blob/9bf31b6e003782152b920fac0b1cfa5ba55e05a1/java-checks/src/main/java/org/sonar/java/checks/VolatileNonPrimitiveFieldCheck.java&quot;&gt;SonarJava: S3077 implementation (9bf31b6e0037)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/importing-external-issues/external-analyzer-reports&quot;&gt;SonarQube: External analyzer reports&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;IntelliJ&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jetbrains.com/help/inspectopedia/Java-Concurrency-annotation-issues.html&quot;&gt;IntelliJ IDEA Inspectopedia: Concurrency annotation issues&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jetbrains.com/help/inspectopedia/FieldAccessNotGuarded.html&quot;&gt;IntelliJ IDEA Inspectopedia: Unguarded field access or method call&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jetbrains.com/help/inspectopedia/NonFinalFieldInImmutable.html&quot;&gt;IntelliJ IDEA Inspectopedia: Non-final field in @Immutable class&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jetbrains.com/help/inspectopedia/AccessToStaticFieldLockedOnInstance.html&quot;&gt;IntelliJ IDEA Inspectopedia: Access to static field locked on instance&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/JetBrains/intellij-community/blob/826413b22cfe5b5c573662f5d2442f454dbc0b23/java/java-psi-api/src/com/intellij/codeInsight/ConcurrencyAnnotationsManager.java&quot;&gt;IntelliJ Community: ConcurrencyAnnotationsManager (826413b22cfe)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jetbrains.com/help/qodana/about-qodana.html&quot;&gt;About Qodana&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jetbrains.com/help/qodana/quality-gate.html&quot;&gt;Qodana Quality gate&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://help.eclipse.org/latest/topic/org.eclipse.jdt.doc.isv/guide/jdt_api_options.htm&quot;&gt;Eclipse JDT Core Options&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/reference/core/beans/factory-scopes.html&quot;&gt;Spring Framework Bean Scopes&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.archunit.org/userguide/html/000_Index.html&quot;&gt;ArchUnit User Guide&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
	</description>
    </item>
    <item>
      <title>MySQL JDBC Configuration for High-Performance Batch Jobs</title>
      <link>https://tech.benelog.net/mysql-jdbc-batch-options.html</link>
      <pubDate>Sat, 3 Oct 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="false">mysql-jdbc-batch-options.html</guid>
      	<description>
	&lt;div id=&quot;toc&quot; class=&quot;toc&quot;&gt;
&lt;div id=&quot;toctitle&quot;&gt;Table of Contents&lt;/div&gt;
&lt;ul class=&quot;sectlevel1&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#using_server_side_prepared_statements&quot;&gt;Using Server-Side Prepared Statements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#improving_batch_update_performance&quot;&gt;Improving Batch Update Performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#options_for_large_scale_data_retrieval&quot;&gt;Options for Large-Scale Data Retrieval&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#streaming_results_one_row_at_a_time&quot;&gt;Streaming Results One Row at a Time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#using_server_side_cursors&quot;&gt;Using Server-side Cursors&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#references&quot;&gt;References&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div id=&quot;preamble&quot;&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Batch jobs stress a JDBC driver differently from a web application. They run the same statement many times,
insert or update rows in bulk, and read result sets that do not fit in memory.
MySQL Connector/J has several connection options that change how it behaves in each of those situations,
and most of them are off by default. This article walks through the options that matter most for batch work,
what each one actually does on the wire, and the pitfalls to watch for when combining them.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;using_server_side_prepared_statements&quot;&gt;Using Server-Side Prepared Statements&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;PreparedStatement helps optimize repeated query execution.
Instead of declaring the entire SQL string such as &lt;code&gt;SELECT * FROM CITY WHERE COUNTRY = &apos;KOREA&apos; AND POPULATION &amp;gt; 10000&lt;/code&gt;,
it separates the static SQL structure &lt;code&gt;SELECT * FROM CITY WHERE COUNTRY = ? AND POPULATION &amp;gt; ?&lt;/code&gt; from the dynamic parameters.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When a query is executed repeatedly, the server reuses the parse tree built by parsing the SQL and executes it with different parameters.
This reduces CPU and other resource usage on the server.
If the JDBC driver sends only the varying parameters instead of transmitting the entire query for each execution, the amount of network traffic can also be reduced.
Whether such optimizations actually occur depends on the DBMS and configuration options.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;MySQL provides the &lt;code&gt;useServerPrepStmts&lt;/code&gt; option to control whether PreparedStatement optimization is performed on the server side.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Since the default value is &lt;code&gt;false&lt;/code&gt;, the JDBC driver sends the fully substituted SQL string to the server for every execution when no additional configuration is applied.
This is referred to as &lt;strong&gt;client-side PreparedStatement&lt;/strong&gt; or &lt;strong&gt;emulated PreparedStatement&lt;/strong&gt;.
This default behavior has the advantage of requiring only one network round-trip per query,
but server-side optimizations are not applied.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In environments such as typical web applications, where a variety of queries are executed,
it may be more efficient to keep the default behavior while enabling &lt;code&gt;cachePrepStmts=true&lt;/code&gt; and increasing related cache sizes. &lt;sup class=&quot;footnote&quot;&gt;[&lt;a id=&quot;_footnoteref_1&quot; class=&quot;footnote&quot; href=&quot;#_footnotedef_1&quot; title=&quot;View footnote.&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Setting &lt;code&gt;useServerPrepStmts=true&lt;/code&gt; enables PreparedStatement parsing on the server side.
When a &lt;code&gt;PreparedStatement&lt;/code&gt; object is created, the template query containing &lt;code&gt;?&lt;/code&gt; placeholders is first sent to the server, parsed, and prepared.
Each time &lt;code&gt;execute()&lt;/code&gt; is called, only the parameter values are transmitted, and the already-prepared query is executed.
If the same query is executed repeatedly, the parse tree built from SQL parsing is reused, improving performance and reducing network bandwidth.
However, MySQL clears the optimization state once execution finishes and optimizes again on the next execution, so you cannot assume the execution plan is built only once and reused indefinitely. &lt;sup class=&quot;footnote&quot;&gt;[&lt;a id=&quot;_footnoteref_2&quot; class=&quot;footnote&quot; href=&quot;#_footnotedef_2&quot; title=&quot;View footnote.&quot;&gt;2&lt;/a&gt;]&lt;/sup&gt;
On the other hand, since at least two network round-trips (prepare, execute) are required to execute a query, performance may actually degrade for queries that are executed only once.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If your application repeatedly executes the same query in MySQL, the &lt;code&gt;useServerPrepStmts=true&lt;/code&gt; option is worth trying out.
However, MySQL parses quickly and rebuilds the execution plan on every execution, so the gain from this option may be small.
In the two performance tests introduced in the footnote above, server-side prepared statements were not faster than client-side ones.
When enabling this option, also enable &lt;code&gt;cachePrepStmts=true&lt;/code&gt; so that the prepare request does not add a network round-trip on every execution, and measure performance with your actual batch job to decide whether to keep it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In MySQL versions prior to 5.1.17, using this option prevented queries from using the query cache, but from 5.1.17 onwards it can be used together with the query cache.
Note that the query cache itself was deprecated in MySQL 5.7.20 and removed in 8.0, so this constraint no longer matters on 8.0 and later. &lt;sup class=&quot;footnote&quot;&gt;[&lt;a id=&quot;_footnoteref_3&quot; class=&quot;footnote&quot; href=&quot;#_footnotedef_3&quot; title=&quot;View footnote.&quot;&gt;3&lt;/a&gt;]&lt;/sup&gt;
In MariaDB 10.6 and later, additional optimizations reduce metadata retransmission when using &lt;code&gt;useServerPrepStmts=true&lt;/code&gt;, resulting in even better performance. &lt;sup class=&quot;footnote&quot;&gt;[&lt;a id=&quot;_footnoteref_4&quot; class=&quot;footnote&quot; href=&quot;#_footnotedef_4&quot; title=&quot;View footnote.&quot;&gt;4&lt;/a&gt;]&lt;/sup&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;improving_batch_update_performance&quot;&gt;Improving Batch Update Performance&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When inserting or updating multiple rows, using the JDBC &lt;code&gt;Statement.executeBatch()&lt;/code&gt; method can execute them faster than repeatedly calling &lt;code&gt;executeUpdate()&lt;/code&gt; for single statements. MySQL can further optimize batch update performance by enabling the &lt;code&gt;rewriteBatchedStatements=true&lt;/code&gt; option in the JDBC connection URL.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Since the default value is &lt;code&gt;false&lt;/code&gt;, it must be explicitly enabled.
When enabled, the MySQL JDBC driver combines multiple individual queries into a single statement.
For example, let’s look at inserting three rows with a batch update using the following INSERT query:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Single INSERT form&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;INSERT INTO access_log(access_date_time, ip, username) VALUES (?, ?, ?);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Without &lt;code&gt;rewriteBatchedStatements=true&lt;/code&gt;, the driver executes the above statement three times.
With the option enabled, the driver merges them into a single statement:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Merged INSERT form&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;INSERT INTO access_log(access_date_time, ip, username) VALUES
  (?, ?, ?),
  (?, ?, ?),
  (?, ?, ?);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Since the server processes the merged INSERT as a single statement, one execution of that statement takes longer.
In replicated environments, this can increase the burden of replication lag.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Even when multi-value &lt;code&gt;VALUES&lt;/code&gt; clauses are not possible, the driver still combines multiple INSERT or UPDATE statements separated by &lt;code&gt;;&lt;/code&gt; into a single transmission when the batch contains four or more statements.
Batches of three or fewer statements are executed one by one.
The direct benefit of this mode is packing multiple SQL statements into one request to reduce network round-trips.
Unlike the multi-value INSERT, which the server processes as a single statement, the &lt;code&gt;;&lt;/code&gt;-combined form merely transmits several statements at once, so the parsing and execution of each statement are not merged into one.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Be aware that &lt;code&gt;rewriteBatchedStatements=true&lt;/code&gt; may conflict with other JDBC options or require additional tuning.
The Connector/J documentation once stated that &lt;code&gt;rewriteBatchedStatements&lt;/code&gt; was ignored when used together with &lt;code&gt;useServerPrepStmts=true&lt;/code&gt;, or with &lt;code&gt;useCursorFetch=true&lt;/code&gt; (introduced later in this document) which implicitly enables it.
The 8.0.30 release notes corrected this as a documentation error. &lt;sup class=&quot;footnote&quot;&gt;[&lt;a id=&quot;_footnoteref_5&quot; class=&quot;footnote&quot; href=&quot;#_footnotedef_5&quot; title=&quot;View footnote.&quot;&gt;5&lt;/a&gt;]&lt;/sup&gt;
Query rewriting now applies as-is even when combined with server-side PreparedStatements.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When combining queries, the driver calculates on its own how many statements to merge at once so that the combined statement stays within the &lt;code&gt;max_allowed_packet&lt;/code&gt; limit.
Therefore a small value usually does not cause an error; it just reduces how many statements are merged at once, shrinking the benefit of combining queries.
To insert or update large volumes at once, it is better to set this value generously.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;max_allowed_packet&lt;/code&gt; can be set in the MySQL configuration file or as a global system variable.
The session value is read-only and initialized from the global value when the connection is established, so if you change the global value dynamically, new connections use the changed value. &lt;sup class=&quot;footnote&quot;&gt;[&lt;a id=&quot;_footnoteref_6&quot; class=&quot;footnote&quot; href=&quot;#_footnotedef_6&quot; title=&quot;View footnote.&quot;&gt;6&lt;/a&gt;]&lt;/sup&gt;
You can check it with: &lt;code&gt;SHOW VARIABLES LIKE &apos;max%&apos;;&lt;/code&gt;
Note that the &lt;code&gt;bulk_insert_buffer_size&lt;/code&gt; setting is only referenced by the MyISAM engine—which is rarely used today—and can be ignored when using InnoDB.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Due to such conflicts and interactions among options,
it may be difficult to find a single DB configuration optimized for both reads and writes.
Another possible approach is to declare separate data sources in the application for large-scale read and write operations.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In Connector/J versions up to 8.0.28, inserting BLOB (Binary Large Object) values with a batch update could cause a &lt;code&gt;NullPointerException&lt;/code&gt;, but this has been fixed in newer versions.
&lt;sup class=&quot;footnote&quot;&gt;[&lt;a id=&quot;_footnoteref_7&quot; class=&quot;footnote&quot; href=&quot;#_footnotedef_7&quot; title=&quot;View footnote.&quot;&gt;7&lt;/a&gt;]&lt;/sup&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;options_for_large_scale_data_retrieval&quot;&gt;Options for Large-Scale Data Retrieval&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If an application using Spring Batch with MySQL executes queries through &lt;code&gt;JdbcCursorItemReader&lt;/code&gt; with default settings, the entire result set will be fetched at once and loaded into the application&amp;#8217;s memory.
When querying large datasets, this can cause Out Of Memory (OOM) errors.
To avoid this, you must use ResultSet streaming or server-side cursors.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;streaming_results_one_row_at_a_time&quot;&gt;Streaming Results One Row at a Time&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;ResultSet streaming retrieves query results gradually instead of receiving them all at once.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To use this approach, configure the PreparedStatement as follows:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Creating PreparedStatement for streaming&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;PreparedStatement statement = con.prepareStatement(
    sql,
    ResultSet.TYPE_FORWARD_ONLY,
    ResultSet.CONCUR_READ_ONLY
);

statement.setFetchSize(Integer.MIN_VALUE);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Note that calling &lt;code&gt;setFetchSize(Integer.MIN_VALUE)&lt;/code&gt; is not technically valid per JDBC specification.
The Javadoc for the java.sql.Statement interface states that if a value less than 0 is passed to the &lt;code&gt;setFetchSize(int)&lt;/code&gt; method, it should throw a SQLException.
However, since MySQL Connector/J enables streaming mode by passing &lt;code&gt;Integer.MIN_VALUE&lt;/code&gt; to this method, developers have no choice but to use it that way.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;While the streaming mode reduces memory usage, it is not always advantageous.
The query request is sent only once and the server pushes the entire result back as consecutive packets, so it does not incur one network round-trip per row. &lt;sup class=&quot;footnote&quot;&gt;[&lt;a id=&quot;_footnoteref_8&quot; class=&quot;footnote&quot; href=&quot;#_footnotedef_8&quot; title=&quot;View footnote.&quot;&gt;8&lt;/a&gt;]&lt;/sup&gt;
Instead, while the driver reads and processes the result one row at a time, server resources and locks are held until the query completes, so the slower the application consumes the rows, the longer this burden lasts.
Other drawbacks are that you cannot directly control the size of each transfer, and no other queries can be executed on the same connection until the ResultSet is closed. &lt;sup class=&quot;footnote&quot;&gt;[&lt;a id=&quot;_footnoteref_9&quot; class=&quot;footnote&quot; href=&quot;#_footnotedef_9&quot; title=&quot;View footnote.&quot;&gt;9&lt;/a&gt;]&lt;/sup&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In Spring Batch, calling &lt;code&gt;JdbcCursorItemReader.setFetchSize(Integer.MIN_VALUE)&lt;/code&gt; enables the streaming mode.
&lt;code&gt;Statement&lt;/code&gt; creation options such as &lt;code&gt;ResultSet.TYPE_FORWARD_ONLY&lt;/code&gt; are applied internally within &lt;code&gt;JdbcCursorItemReader&lt;/code&gt;.
If the &lt;code&gt;JdbcCursorItemReader.verifyCursorPosition&lt;/code&gt; property remains at its default &lt;code&gt;true&lt;/code&gt;, it conflicts with &lt;code&gt;TYPE_FORWARD_ONLY&lt;/code&gt; and produces the following error:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Error caused by verifyCursorPosition=true&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code&gt;org.springframework.dao.TransientDataAccessResourceException: Attempt to process next row failed; SQL [SELECT * FROM access_log]; Operation not allowed for a result set of type ResultSet.TYPE_FORWARD_ONLY.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Accordingly, a &lt;code&gt;JdbcCursorItemReader&lt;/code&gt; configured for per-row streaming should be created as follows:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;JdbcCursorItemReader for streaming queries in MySQL&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;return new JdbcCursorItemReaderBuilder&amp;lt;T&amp;gt;()
  .name(&quot;streamingDbReader&quot;)
  .dataSource(this.dataSource)
  .sql(sql)
  .rowMapper(rowMapper)
  .fetchSize(Integer.MIN_VALUE)
  .verifyCursorPosition(false)
  .build();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;using_server_side_cursors&quot;&gt;Using Server-side Cursors&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;MySQL server-side cursors work by storing results in a temporary table and allowing the client to fetch the data in configurable chunks.
Supported in server versions newer than MySQL 5.0.2, they can be enabled by adding &lt;code&gt;useCursorFetch=true&lt;/code&gt; to the JDBC URL. &lt;sup class=&quot;footnote&quot;&gt;[&lt;a id=&quot;_footnoteref_10&quot; class=&quot;footnote&quot; href=&quot;#_footnotedef_10&quot; title=&quot;View footnote.&quot;&gt;10&lt;/a&gt;]&lt;/sup&gt;
Since the default value is &lt;code&gt;false&lt;/code&gt;, this option must be explicitly enabled if client-side cursors are not desired.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Even with &lt;code&gt;useCursorFetch=true&lt;/code&gt;, server-side cursors will not be used unless a positive fetch size is specified.
This can be configured per query using the &lt;code&gt;Statement.setFetchSize(int)&lt;/code&gt; method in the JDBC API, or a default value can be set via the &lt;code&gt;defaultFetchSize&lt;/code&gt; property in the JDBC connection URL. &lt;sup class=&quot;footnote&quot;&gt;[&lt;a id=&quot;_footnoteref_11&quot; class=&quot;footnote&quot; href=&quot;#_footnotedef_11&quot; title=&quot;View footnote.&quot;&gt;11&lt;/a&gt;]&lt;/sup&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In Spring Batch, the fetch size can be specified using &lt;code&gt;JdbcCursorItemReader.setFetchSize(int)&lt;/code&gt;.
It is recommended to set this value equal to chunk size in your step configuration.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;As mentioned earlier, enabling the &lt;code&gt;useCursorFetch=true&lt;/code&gt; option also automatically enables &lt;code&gt;useServerPrepStmts=true&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.mysql.com/doc/connector-j/en/connector-j-connp-props-performance-extensions.html&quot;&gt;MySQL Connector/J Developer Guide: Performance Extensions&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.mysql.com/doc/connector-j/en/connector-j-reference-implementation-notes.html&quot;&gt;MySQL Connector/J Developer Guide: JDBC API Implementation Notes&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.4/en/statement-caching.html&quot;&gt;MySQL 8.4 Reference Manual: Caching of Prepared Statements and Stored Programs&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.4/en/server-system-variables.html#sysvar_max_allowed_packet&quot;&gt;MySQL 8.4 Reference Manual: max_allowed_packet&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://vladmihalcea.com/mysql-jdbc-statement-caching/&quot;&gt;Vlad Mihalcea: MySQL JDBC Statement Caching&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.spring.io/spring-batch/reference/readers-and-writers/database.html#JdbcCursorItemReader&quot;&gt;Spring Batch Reference: JdbcCursorItemReader&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://gist.github.com/benelog/e7560ccf29c4365d939e9c3d210f9086&quot;&gt;Original gist of this article&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;div class=&quot;footnote&quot; id=&quot;_footnotedef_1&quot;&gt;
&lt;a href=&quot;#_footnoteref_1&quot;&gt;1&lt;/a&gt;. For performance test cases combining &lt;code&gt;useServerPrepStmts&lt;/code&gt; and &lt;code&gt;cachePrepStmts&lt;/code&gt;, see the following articles: &lt;a href=&quot;https://vladmihalcea.com/mysql-jdbc-statement-caching/&quot; class=&quot;bare&quot;&gt;https://vladmihalcea.com/mysql-jdbc-statement-caching/&lt;/a&gt; , &lt;a href=&quot;https://tech.kakaopay.com/post/how-preparedstatement-works-in-our-apps/&quot; class=&quot;bare&quot;&gt;https://tech.kakaopay.com/post/how-preparedstatement-works-in-our-apps/&lt;/a&gt;
&lt;/div&gt;
&lt;div class=&quot;footnote&quot; id=&quot;_footnotedef_2&quot;&gt;
&lt;a href=&quot;#_footnoteref_2&quot;&gt;2&lt;/a&gt;. The MySQL 8.4 manual describes what is cached for prepared statements as an internal structure converted from the SQL; for example, &lt;code&gt;SELECT *&lt;/code&gt; is stored expanded into the actual column list. &lt;a href=&quot;https://dev.mysql.com/doc/refman/8.4/en/statement-caching.html&quot; class=&quot;bare&quot;&gt;https://dev.mysql.com/doc/refman/8.4/en/statement-caching.html&lt;/a&gt; &lt;code&gt;Query_expression.clear_execution()&lt;/code&gt; in the MySQL 8.4 source also resets the &lt;code&gt;optimized&lt;/code&gt; state to &lt;code&gt;false&lt;/code&gt; before a prepared statement is re-executed. &lt;a href=&quot;https://dev.mysql.com/doc/dev/mysql-server/8.4.9/sql__lex_8h_source.html&quot; class=&quot;bare&quot;&gt;https://dev.mysql.com/doc/dev/mysql-server/8.4.9/sql__lex_8h_source.html&lt;/a&gt;
&lt;/div&gt;
&lt;div class=&quot;footnote&quot; id=&quot;_footnotedef_3&quot;&gt;
&lt;a href=&quot;#_footnoteref_3&quot;&gt;3&lt;/a&gt;. The deprecation and removal of the query cache is documented in the &apos;How the Query Cache Operates&apos; section of the MySQL 5.7 Reference Manual: &lt;a href=&quot;https://dev.mysql.com/doc/refman/5.7/en/query-cache-operation.html&quot; class=&quot;bare&quot;&gt;https://dev.mysql.com/doc/refman/5.7/en/query-cache-operation.html&lt;/a&gt;
&lt;/div&gt;
&lt;div class=&quot;footnote&quot; id=&quot;_footnotedef_4&quot;&gt;
&lt;a href=&quot;#_footnoteref_4&quot;&gt;4&lt;/a&gt;. This improvement is tracked in &lt;a href=&quot;https://jira.mariadb.org/browse/MDEV-19237&quot; class=&quot;bare&quot;&gt;https://jira.mariadb.org/browse/MDEV-19237&lt;/a&gt;
&lt;/div&gt;
&lt;div class=&quot;footnote&quot; id=&quot;_footnotedef_5&quot;&gt;
&lt;a href=&quot;#_footnoteref_5&quot;&gt;5&lt;/a&gt;. The correction can be found in the following release notes: &lt;a href=&quot;https://dev.mysql.com/doc/relnotes/connector-j/en/news-8-0-30.html&quot; class=&quot;bare&quot;&gt;https://dev.mysql.com/doc/relnotes/connector-j/en/news-8-0-30.html&lt;/a&gt;
&lt;/div&gt;
&lt;div class=&quot;footnote&quot; id=&quot;_footnotedef_6&quot;&gt;
&lt;a href=&quot;#_footnoteref_6&quot;&gt;6&lt;/a&gt;. The &lt;code&gt;max_allowed_packet&lt;/code&gt; entry in the MySQL 8.4 Reference Manual states that the global value can be changed dynamically but the session value is read-only. &lt;a href=&quot;https://dev.mysql.com/doc/refman/8.4/en/server-system-variables.html#sysvar_max_allowed_packet&quot; class=&quot;bare&quot;&gt;https://dev.mysql.com/doc/refman/8.4/en/server-system-variables.html#sysvar_max_allowed_packet&lt;/a&gt;
&lt;/div&gt;
&lt;div class=&quot;footnote&quot; id=&quot;_footnotedef_7&quot;&gt;
&lt;a href=&quot;#_footnoteref_7&quot;&gt;7&lt;/a&gt;. See release notes: &lt;a href=&quot;https://dev.mysql.com/doc/relnotes/connector-j/en/news-8-0-29.html&quot; class=&quot;bare&quot;&gt;https://dev.mysql.com/doc/relnotes/connector-j/en/news-8-0-29.html&lt;/a&gt;
&lt;/div&gt;
&lt;div class=&quot;footnote&quot; id=&quot;_footnotedef_8&quot;&gt;
&lt;a href=&quot;#_footnoteref_8&quot;&gt;8&lt;/a&gt;. The response structure, in which the server answers a single query request with one packet per row sent in sequence, is described in the following MySQL protocol documentation: &lt;a href=&quot;https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_query_response_text_resultset.html&quot; class=&quot;bare&quot;&gt;https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_query_response_text_resultset.html&lt;/a&gt;
&lt;/div&gt;
&lt;div class=&quot;footnote&quot; id=&quot;_footnotedef_9&quot;&gt;
&lt;a href=&quot;#_footnoteref_9&quot;&gt;9&lt;/a&gt;. This is explained in the ResultSet section of the following Connector/J implementation notes. It describes the restriction that you must read all of the rows (or close the ResultSet) before issuing any other queries on the same connection, and introduces the &lt;code&gt;useCursorFetch&lt;/code&gt; option as an alternative that fetches a set number of rows at a time. &lt;a href=&quot;https://dev.mysql.com/doc/connector-j/en/connector-j-reference-implementation-notes.html&quot; class=&quot;bare&quot;&gt;https://dev.mysql.com/doc/connector-j/en/connector-j-reference-implementation-notes.html&lt;/a&gt;
&lt;/div&gt;
&lt;div class=&quot;footnote&quot; id=&quot;_footnotedef_10&quot;&gt;
&lt;a href=&quot;#_footnoteref_10&quot;&gt;10&lt;/a&gt;. The Connector/J documentation used to describe this property as determining whether cursor-based fetching should be used when the server version is newer than MySQL 5.0.2 and the fetch size is greater than 0. As the minimum supported server versions moved up, this version condition was dropped from recent documentation: &lt;a href=&quot;https://dev.mysql.com/doc/connector-j/en/connector-j-connp-props-performance-extensions.html&quot; class=&quot;bare&quot;&gt;https://dev.mysql.com/doc/connector-j/en/connector-j-connp-props-performance-extensions.html&lt;/a&gt;
&lt;/div&gt;
&lt;div class=&quot;footnote&quot; id=&quot;_footnotedef_11&quot;&gt;
&lt;a href=&quot;#_footnoteref_11&quot;&gt;11&lt;/a&gt;. See: &lt;a href=&quot;https://dev.mysql.com/doc/connector-j/en/connector-j-connp-props-performance-extensions.html&quot; class=&quot;bare&quot;&gt;https://dev.mysql.com/doc/connector-j/en/connector-j-connp-props-performance-extensions.html&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
	</description>
    </item>
    <item>
      <title>Running External Processes from Java: JDK 25 on Linux 6.x</title>
      <link>https://tech.benelog.net/java-external-process.html</link>
      <pubDate>Sat, 3 Oct 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="false">java-external-process.html</guid>
      	<description>
	&lt;div id=&quot;toc&quot; class=&quot;toc&quot;&gt;
&lt;div id=&quot;toctitle&quot;&gt;Table of Contents&lt;/div&gt;
&lt;ul class=&quot;sectlevel1&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#pitfalls_of_launching_external_processes_with_the_jdk_classes&quot;&gt;Pitfalls of Launching External Processes with the JDK Classes&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#processbuilder_vs_runtime_exec&quot;&gt;&lt;code&gt;ProcessBuilder&lt;/code&gt; vs. &lt;code&gt;Runtime.exec&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#limits_of_the_processbuilder_defaults&quot;&gt;Limits of the &lt;code&gt;ProcessBuilder&lt;/code&gt; Defaults&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#handling_the_stdout_and_stderr_pipes&quot;&gt;Handling the stdout and stderr Pipes&lt;/a&gt;
&lt;ul class=&quot;sectlevel3&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#deadlock_from_an_unread_pipe_buffer&quot;&gt;Deadlock from an Unread Pipe Buffer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#reading_both_streams_concurrently&quot;&gt;Reading Both Streams Concurrently&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#redirecting_output_the_java_process_will_not_read&quot;&gt;Redirecting Output the Java Process Will Not Read&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#closing_stdin_and_eof&quot;&gt;Closing stdin and EOF&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#timeouts_and_termination_policy&quot;&gt;Timeouts and Termination Policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#cleaning_up_descendant_processes&quot;&gt;Cleaning Up Descendant Processes&lt;/a&gt;
&lt;ul class=&quot;sectlevel3&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#cases_where_descendants_are_left_behind&quot;&gt;Cases Where Descendants Are Left Behind&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#terminating_by_process_group&quot;&gt;Terminating by Process Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#terminating_by_cgroup&quot;&gt;Terminating by cgroup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#using_zt_exec_and_apache_commons_exec&quot;&gt;Using zt-exec and Apache Commons Exec&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#zt_exec&quot;&gt;zt-exec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#apache_commons_exec&quot;&gt;Apache Commons Exec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#maintenance_status_and_recommendation&quot;&gt;Maintenance Status and Recommendation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#process_creation_on_linux&quot;&gt;Process Creation on Linux&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#three_ways_to_create_a_process_and_the_history_of_the_jdk_default&quot;&gt;Three Ways to Create a Process and the History of the JDK Default&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#how_posix_spawn_works&quot;&gt;How POSIX_SPAWN Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#memory_cost_and_launch_latency_of_fork&quot;&gt;Memory Cost and Launch Latency of FORK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#removing_the_vfork_setting_when_upgrading_the_jvm&quot;&gt;Removing the VFORK Setting When Upgrading the JVM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#references&quot;&gt;References&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div id=&quot;preamble&quot;&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This article, based on JDK 25 and Linux 6.x, covers what to watch for when writing Java code that launches external processes, and the libraries you can use for it.
It then looks at how the JVM creates child processes on Linux: the &lt;code&gt;posix_spawn()&lt;/code&gt; function, the behavior of &lt;code&gt;jspawnhelper&lt;/code&gt;, a small executable shipped with the JDK, the launch latency that appears with large heaps, and the setting that changes the launch mechanism.
The results in this article were verified in the environment below. The example code is in the &lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/java-external-process&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 33.3333%;&quot;&gt;
&lt;col style=&quot;width: 66.6667%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Item&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;OS&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Ubuntu 24.04.4 LTS, kernel 6.17&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Architecture&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;x86-64&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;glibc&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;2.39&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JDK&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Temurin 25+36&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;zt-exec&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;1.13.0&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Apache Commons Exec&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;1.6.0&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;pitfalls_of_launching_external_processes_with_the_jdk_classes&quot;&gt;Pitfalls of Launching External Processes with the JDK Classes&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;processbuilder_vs_runtime_exec&quot;&gt;&lt;code&gt;ProcessBuilder&lt;/code&gt; vs. &lt;code&gt;Runtime.exec&lt;/code&gt;&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;An external process launched from Java code is handled through a &lt;code&gt;java.lang.Process&lt;/code&gt; object. You get a &lt;code&gt;Process&lt;/code&gt; object from &lt;code&gt;ProcessBuilder.start()&lt;/code&gt; or &lt;code&gt;Runtime.exec()&lt;/code&gt;. &lt;code&gt;ProcessBuilder&lt;/code&gt; was &lt;a href=&quot;https://docs.oracle.com/javase/1.5.0/docs/guide/lang/enhancements.html&quot;&gt;added in JDK 5.0 (Java 1.5)&lt;/a&gt;, and from that release the Sun JDK&amp;#8217;s &lt;code&gt;Runtime.exec()&lt;/code&gt; was also changed to delegate internally to &lt;code&gt;ProcessBuilder&lt;/code&gt;. &lt;a href=&quot;https://www.javainthebox.net/laboratory/J2SE1.5/TinyTips/ProcessBuilder/ProcessBuilder.html&quot;&gt;A contemporary write-up that examined the JDK 1.5.0-beta2 source&lt;/a&gt; shows this implementation as well. The &lt;a href=&quot;https://github.com/openjdk/jdk/blob/jdk-25%2B36/src/java.base/share/classes/java/lang/Runtime.java&quot;&gt;Runtime source&lt;/a&gt; in OpenJDK 25 likewise creates a &lt;code&gt;ProcessBuilder&lt;/code&gt; and calls &lt;code&gt;start()&lt;/code&gt;. The two APIs therefore share the process creation implementation; the main difference is how they handle the launch configuration.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;I recommend &lt;code&gt;ProcessBuilder&lt;/code&gt; for new code.&lt;/strong&gt; It offers a more convenient API for specifying environment variables, the working directory, and output redirection. The output pipe handling discussed in this article can also be configured with &lt;code&gt;redirectOutput()&lt;/code&gt;, &lt;code&gt;redirectError()&lt;/code&gt;, &lt;code&gt;redirectErrorStream()&lt;/code&gt;, and similar methods.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 20%;&quot;&gt;
&lt;col style=&quot;width: 40%;&quot;&gt;
&lt;col style=&quot;width: 40%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Item&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Runtime.exec()&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;ProcessBuilder&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;When it runs&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Runs when &lt;code&gt;exec()&lt;/code&gt; is called&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Runs on &lt;code&gt;start()&lt;/code&gt; after the configuration is set up&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Command and arguments&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;A single string or an array of strings&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Varargs or a list with the arguments already separated&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Environment variables&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Passed as an array of &lt;code&gt;NAME=value&lt;/code&gt; strings&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Modified through the Map returned by &lt;code&gt;environment()&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Working directory&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Passed as an argument to an overload&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Set with &lt;code&gt;directory()&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;I/O configuration&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No API for redirection or for merging standard error&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Provides &lt;code&gt;redirectOutput()&lt;/code&gt;, &lt;code&gt;redirectError()&lt;/code&gt;, &lt;code&gt;redirectErrorStream()&lt;/code&gt;, &lt;code&gt;inheritIO()&lt;/code&gt;, and more&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In particular, the &lt;code&gt;Runtime.exec(String)&lt;/code&gt; family, which takes the command as a single string, has been deprecated since JDK 18. The &lt;a href=&quot;https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Runtime.html&quot;&gt;Runtime Javadoc&lt;/a&gt; explains that these methods split arguments on whitespace alone, so they can mishandle things like file names that contain spaces. Adding quotes does not group arguments the way a shell would.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;// Cannot pass a file name with a space as a single argument
Runtime.getRuntime().exec(&quot;cat \&quot;my file.txt\&quot;&quot;);

// Both forms pass the file name as a single argument
Runtime.getRuntime().exec(new String[]{&quot;cat&quot;, &quot;my file.txt&quot;});
new ProcessBuilder(&quot;cat&quot;, &quot;my file.txt&quot;).start();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;limits_of_the_processbuilder_defaults&quot;&gt;Limits of the &lt;code&gt;ProcessBuilder&lt;/code&gt; Defaults&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In Java, you can launch an external process with a few simple lines of code using the &lt;code&gt;ProcessBuilder&lt;/code&gt; class, as shown below.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Calling &lt;code&gt;ProcessBuilder&lt;/code&gt; with the defaults&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;Process process = new ProcessBuilder(&quot;echo&quot;, &quot;hello&quot;).start();
int exitCode = process.waitFor();
System.out.println(&quot;exit=&quot; + exitCode);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;With &lt;code&gt;echo hello&lt;/code&gt;, which produces little output and finishes quickly, this code works without problems.
But if you run a command that produces a lot of output, waits on standard input, or never finishes, this code can hang.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;handling_the_stdout_and_stderr_pipes&quot;&gt;Handling the stdout and stderr Pipes&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;If the child process fills the limited capacity of the standard output (stdout) or standard error (stderr) pipe while the parent process is not reading, the child blocks on its next write.&lt;/strong&gt;
To prevent that, you need code that keeps consuming standard output and standard error.
You must use one of the following two approaches.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Read the standard output and standard error streams on separate threads.&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If you need to process or refer to the two outputs separately, read each on its own thread.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you do not need to tell the two outputs apart, you can merge them into one and read it on a single thread.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you do not need to read a stream at all, redirect it to a file, to the parent&amp;#8217;s output, or to &lt;code&gt;/dev/null&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The earlier &apos;Calling &lt;code&gt;ProcessBuilder&lt;/code&gt; with the defaults&apos; code used neither of these approaches.
The following sections reproduce how this code hangs with examples and explain how to fix it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;deadlock_from_an_unread_pipe_buffer&quot;&gt;Deadlock from an Unread Pipe Buffer&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Even with &lt;code&gt;ProcessBuilder&lt;/code&gt;, the child process&amp;#8217;s standard output and standard error are delivered to the parent process through separate pipes by default. If the parent does not read a pipe, the pipe buffer fills up and the child process cannot finish writing.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;This problem occurs even if you never call &lt;code&gt;waitFor()&lt;/code&gt;.&lt;/strong&gt; The parent can go on executing the code that follows the child&amp;#8217;s launch, but the child may be unable to finish its work because its &lt;code&gt;write()&lt;/code&gt; call to the full pipe blocks. If on top of that the parent just waits for the child to exit with &lt;code&gt;waitFor()&lt;/code&gt;, the two end up in a deadlock, each waiting for the other. So removing &lt;code&gt;waitFor()&lt;/code&gt; does not solve it; you have to read or redirect the output pipes. The &lt;code&gt;Process&lt;/code&gt; Javadoc in JDK 25 warns about this blocking and the possibility of deadlock.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;From the Javadoc of the &lt;code&gt;Process&lt;/code&gt; class&lt;/div&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the process may cause the process to block, or even deadlock.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://man7.org/linux/man-pages/man7/pipe.7.html&quot;&gt;pipe(7)&lt;/a&gt; manual page tells how large the &quot;limited buffer size&quot; mentioned in the Javadoc is on Linux. Since kernel 2.6.11 the default pipe capacity has been 16 pages, that is, 64KiB with 4KiB pages. However, it can be smaller depending on the per-user pipe memory limit or system settings, and it can be changed with &lt;code&gt;F_SETPIPE_SZ&lt;/code&gt;, so you should not assume 64KiB as a fixed limit.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The code below has the &lt;code&gt;seq 1 100000&lt;/code&gt; command print 588,895 bytes and waits 3 seconds without reading them.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;DeadlockDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;Process process = new ProcessBuilder(&quot;seq&quot;, &quot;1&quot;, &quot;100000&quot;).start();
boolean finished = process.waitFor(3, TimeUnit.SECONDS);
System.out.println(&quot;finished within 3s: &quot; + finished + &quot;, alive: &quot; + process.isAlive());
long lines = process.inputReader().lines().count();
int exitCode = process.waitFor();
System.out.println(&quot;read &quot; + lines + &quot; lines, exit=&quot; + exitCode + &quot;, alive: &quot; + process.isAlive());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Output of DeadlockDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;finished within 3s: false, alive: true
read 100000 lines, exit=0, alive: false&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;After the parent process has waited 3 seconds, the child process is still alive. Once the pipe buffer filled up, the &lt;code&gt;write()&lt;/code&gt; call in &lt;code&gt;seq&lt;/code&gt; blocked and never returned. When the parent read the pipe to the end, the child wrote the rest of its output and finished with exit code 0 (&lt;code&gt;alive: false&lt;/code&gt;). Without the timeout, the parent would have kept waiting in &lt;code&gt;waitFor()&lt;/code&gt; as well.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;code&gt;inputReader()&lt;/code&gt; method used in the example was added in JDK 17. Before that, you had to wrap &lt;code&gt;getInputStream()&lt;/code&gt; in an &lt;code&gt;InputStreamReader&lt;/code&gt; and a &lt;code&gt;BufferedReader&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Using &lt;code&gt;inputReader()&lt;/code&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;// Before JDK 17
BufferedReader reader = new BufferedReader(
        new InputStreamReader(process.getInputStream()));
long lines = reader.lines().count();

// JDK 17 and later
long lines = process.inputReader().lines().count();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The two snippets choose their default encoding differently. &lt;code&gt;inputReader()&lt;/code&gt; decodes with the encoding named by the &lt;code&gt;native.encoding&lt;/code&gt; system property, while an &lt;code&gt;InputStreamReader&lt;/code&gt; created without arguments uses &lt;code&gt;Charset.defaultCharset()&lt;/code&gt;. &lt;a href=&quot;https://openjdk.org/jeps/400&quot;&gt;Since JDK 18 the default charset is UTF-8&lt;/a&gt;, but if you specify &lt;code&gt;-Dfile.encoding=COMPAT&lt;/code&gt;, the default encoding is determined by the operating system, the locale, and so on, as it was before.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;inputReader()&lt;/code&gt; does not detect the child process&amp;#8217;s output encoding automatically. If the child writes in the same encoding as the parent JVM&amp;#8217;s &lt;code&gt;native.encoding&lt;/code&gt;, this default is appropriate. If the child explicitly writes UTF-8 or runs under a different locale, you must specify the encoding yourself to match its output, for example &lt;code&gt;inputReader(StandardCharsets.UTF_8)&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;reading_both_streams_concurrently&quot;&gt;Reading Both Streams Concurrently&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you need to receive and process standard output and standard error separately, you have to drain both pipes at the same time. Reading one pipe to the end and then reading the other is not enough. If the standard error pipe fills first, the child process cannot write any more standard output, and the parent deadlocks waiting for EOF on standard output. In this case the program hangs in the read before it even reaches &lt;code&gt;waitFor()&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The code below runs a command that writes 588,895 bytes to standard error and then one line to standard output, and it reads standard output to the end before reading standard error.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;SequentialReadDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;Process process = new ProcessBuilder(&quot;sh&quot;, &quot;-c&quot;, &quot;seq 1 100000 &amp;gt;&amp;amp;2; echo done&quot;).start();
System.out.println(&quot;reading stdout...&quot;);
long outLines = process.inputReader().lines().count();
long errLines = process.errorReader().lines().count();
System.out.println(&quot;stdout &quot; + outLines + &quot;, stderr &quot; + errLines + &quot;, exit=&quot; + process.waitFor());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When you run this code, nothing follows the first line no matter how long you wait, and &lt;code&gt;ps&lt;/code&gt; shows that the child &lt;code&gt;seq&lt;/code&gt; process is still alive.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Output of SequentialReadDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;reading stdout...&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;seq&lt;/code&gt; is blocked because the standard error pipe is full, and the parent is waiting for an EOF on standard output that has not yet arrived. The next line, which reads standard error, never executes, so the deadlock is never broken. Giving each stream its own reader thread avoids neglecting one stream while reading the other. The &lt;code&gt;PlainJdkRunner&lt;/code&gt; example later in this article takes that approach.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you do not need to tell the two outputs apart, you can merge standard error into standard output with &lt;code&gt;redirectErrorStream(true)&lt;/code&gt;. That leaves only one pipe to read, so a single thread can handle it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;MergedReadDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;Process process = new ProcessBuilder(&quot;sh&quot;, &quot;-c&quot;, &quot;seq 1 100000 &amp;gt;&amp;amp;2; echo done&quot;)
        .redirectErrorStream(true)
        .start();
long lines = process.inputReader().lines().count();
System.out.println(&quot;read &quot; + lines + &quot; lines, exit=&quot; + process.waitFor());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;It is the same command, but this time it runs to completion. The 100,001 lines are the 100,000 lines written to standard error plus the single &lt;code&gt;done&lt;/code&gt; line on standard output.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Output of MergedReadDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;read 100001 lines, exit=0&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;redirecting_output_the_java_process_will_not_read&quot;&gt;Redirecting Output the Java Process Will Not Read&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If the parent process does not need to refer to or process the child&amp;#8217;s output, the simplest option is not to create a pipe that the Java code has to read. The alternatives to the default &lt;code&gt;Redirect.PIPE&lt;/code&gt; are as follows.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Redirect.INHERIT&lt;/code&gt;: the child process writes directly to the parent&amp;#8217;s standard output and standard error. (Since JDK 7)&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;inheritIO()&lt;/code&gt; method hands down all three of the parent process&amp;#8217;s streams (standard input, standard output, and standard error) at once. (Since JDK 7)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Redirect.DISCARD&lt;/code&gt;: sends the output to &lt;code&gt;/dev/null&lt;/code&gt;. (Since JDK 9)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Redirect.to(File)&lt;/code&gt;: sends the output to a file. (Since JDK 7)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Redirecting the child process&amp;#8217;s output&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;// Send directly to the parent&apos;s stdout and stderr
new ProcessBuilder(&quot;seq&quot;, &quot;1&quot;, &quot;100000&quot;)
        .redirectOutput(Redirect.INHERIT)
        .redirectError(Redirect.INHERIT)
        .start()
        .waitFor();

// Inherit all three streams at once, including stdin
new ProcessBuilder(&quot;seq&quot;, &quot;1&quot;, &quot;100000&quot;)
        .inheritIO()
        .start()
        .waitFor();

// Discard the output to /dev/null
new ProcessBuilder(&quot;seq&quot;, &quot;1&quot;, &quot;100000&quot;)
        .redirectOutput(Redirect.DISCARD)
        .redirectError(Redirect.DISCARD)
        .start()
        .waitFor();

// Send to files
new ProcessBuilder(&quot;seq&quot;, &quot;1&quot;, &quot;100000&quot;)
        .redirectOutput(Redirect.to(new File(&quot;seq-out.log&quot;)))
        .redirectError(Redirect.to(new File(&quot;seq-err.log&quot;)))
        .start()
        .waitFor();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In this environment, all four approaches printed the same 588,895 bytes as the earlier deadlock example and exited normally.
Because no child output pipe is created that the parent JVM has to read itself, there is no deadlock from an unread pipe.
However, if the output target inherited through &lt;code&gt;INHERIT&lt;/code&gt; is itself a pipe, for example if the parent JVM&amp;#8217;s standard output is piped to another program, the child&amp;#8217;s writes can block or slow down depending on how fast that program reads.
When discarding output, specify &lt;code&gt;DISCARD&lt;/code&gt; for both standard output and standard error. If you specify only one, the other remains at the default &lt;code&gt;PIPE&lt;/code&gt;, and the earlier deadlock can recur. &lt;code&gt;Redirect.to(File)&lt;/code&gt; truncates an existing file and overwrites it, so to append on each run use &lt;code&gt;Redirect.appendTo(File)&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;closing_stdin_and_eof&quot;&gt;Closing stdin and EOF&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Standard input (stdin), which the Javadoc quoted above warned about alongside the output streams, also defaults to a pipe. As long as the parent JVM keeps the write end of that pipe open, the child process never receives EOF. A command that reads stdin to the end, such as &lt;code&gt;cat&lt;/code&gt; run without arguments, keeps waiting even though no more input is coming, and the parent waits in &lt;code&gt;waitFor()&lt;/code&gt; for that command to finish. Draining the output pipes does not release this wait.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you have no input to send, close the write end with &lt;code&gt;process.getOutputStream().close()&lt;/code&gt; right after &lt;code&gt;start()&lt;/code&gt;, or specify empty input such as &lt;code&gt;redirectInput(new File(&quot;/dev/null&quot;))&lt;/code&gt;. If you do need to send input, close the stream once you have finished writing. zt-exec and Apache Commons Exec, covered later in this article, close the child&amp;#8217;s stdin immediately when no input is specified.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The example below runs &lt;code&gt;cat&lt;/code&gt; four times, changing only how stdin is handled.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;StdinEofDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;Process opened = new ProcessBuilder(&quot;cat&quot;).start();
System.out.println(&quot;stdin left open: finished within 1s=&quot; + opened.waitFor(1, TimeUnit.SECONDS)
        + &quot;, alive=&quot; + opened.isAlive());
opened.destroy();
opened.waitFor();

Process closed = new ProcessBuilder(&quot;cat&quot;).start();
closed.getOutputStream().close();
System.out.println(&quot;stdin closed: finished within 1s=&quot; + closed.waitFor(1, TimeUnit.SECONDS)
        + &quot;, exit=&quot; + closed.exitValue());

Process devNull = new ProcessBuilder(&quot;cat&quot;)
        .redirectInput(new File(&quot;/dev/null&quot;))
        .start();
System.out.println(&quot;/dev/null input: finished within 1s=&quot; + devNull.waitFor(1, TimeUnit.SECONDS)
        + &quot;, exit=&quot; + devNull.exitValue());

Process fed = new ProcessBuilder(&quot;cat&quot;).start();
try (Writer writer = fed.outputWriter()) {
    writer.write(&quot;hello\n&quot;);
}
String echoed = fed.inputReader().readLine();
System.out.println(&quot;closed after writing: echoed=&quot; + echoed + &quot;, exit=&quot; + fed.waitFor());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Here is the output. Only the first run, which left the stream writing to stdin open, failed to finish within one second; the other three received EOF and exited right away.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Output of StdinEofDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;stdin left open: finished within 1s=false, alive=true
stdin closed: finished within 1s=true, exit=0
/dev/null input: finished within 1s=true, exit=0
closed after writing: echoed=hello, exit=0&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The first &lt;code&gt;cat&lt;/code&gt; was ended with &lt;code&gt;destroy()&lt;/code&gt;. Without that cleanup it would stay alive while the remaining examples run.
Once the parent JVM exits, however, the write end of the pipe is closed with it, so at that point &lt;code&gt;cat&lt;/code&gt; also receives EOF and exits.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;code&gt;outputWriter()&lt;/code&gt; used in the last example was added in JDK 17 together with &lt;code&gt;inputReader()&lt;/code&gt;, and it replaces the code that wraps &lt;code&gt;getOutputStream()&lt;/code&gt; in an &lt;code&gt;OutputStreamWriter&lt;/code&gt; and a &lt;code&gt;BufferedWriter&lt;/code&gt;.
Called without arguments it uses the charset from the &lt;code&gt;native.encoding&lt;/code&gt; system property, and the &lt;code&gt;outputWriter(Charset)&lt;/code&gt; method lets you specify one directly.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Using &lt;code&gt;outputWriter()&lt;/code&gt;&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;// Before JDK 17
try (BufferedWriter writer = new BufferedWriter(
        new OutputStreamWriter(process.getOutputStream()))) {
    writer.write(&quot;hello\n&quot;);
}

// JDK 17 and later
try (BufferedWriter writer = process.outputWriter()) {
    writer.write(&quot;hello\n&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;timeouts_and_termination_policy&quot;&gt;Timeouts and Termination Policy&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Even when you handle all three pipes connected to the child process as described so far, an external command can still hang while waiting on the network or on a lock.
That is why a timeout and a termination policy are needed as well.
While the command hangs, the thread that called &lt;code&gt;waitFor()&lt;/code&gt; blocks until the external command finishes. The &lt;code&gt;waitFor(long, TimeUnit)&lt;/code&gt; method, available since JDK 8, merely returns &lt;code&gt;false&lt;/code&gt; when the time runs out; it does not terminate the process.
At that point you can request graceful termination with &lt;code&gt;destroy()&lt;/code&gt; and fall back to &lt;code&gt;destroyForcibly()&lt;/code&gt; if the process is still alive after a grace period, or you can kill it right away as the example below does.
In the OpenJDK implementation on Linux, &lt;code&gt;destroy()&lt;/code&gt; sends SIGTERM and &lt;code&gt;destroyForcibly()&lt;/code&gt; sends SIGKILL.
A process can catch SIGTERM with a handler and finish up before exiting: deleting temporary files, releasing locks, or cleaning up the child processes it created.
SIGKILL terminates the process with no chance to clean up through a handler, so files and descendant processes that were never cleaned up may be left behind. For commands that have something to clean up, it is therefore safer to send SIGTERM first with &lt;code&gt;destroy()&lt;/code&gt; and allow a grace period.
Note, however, that the process may still be alive briefly after &lt;code&gt;destroyForcibly()&lt;/code&gt; returns, so if you need the termination to be complete, confirm it with &lt;code&gt;waitFor()&lt;/code&gt;.
The example below only runs commands with nothing to clean up, such as &lt;code&gt;sleep&lt;/code&gt;, so it kills them right away.
It reads the two output streams on JDK 21 virtual threads and puts a one-second limit on waiting for exit.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;PlainJdkRunner.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;static void run(String... command) throws IOException, InterruptedException, TimeoutException {
    Process process = new ProcessBuilder(command).start();
    StringBuilder stdout = new StringBuilder();
    StringBuilder stderr = new StringBuilder();
    Thread outPump = Thread.ofVirtual().start(() -&amp;gt; process.inputReader().lines().forEach(l -&amp;gt; stdout.append(l).append(&apos;\n&apos;)));
    Thread errPump = Thread.ofVirtual().start(() -&amp;gt; process.errorReader().lines().forEach(l -&amp;gt; stderr.append(l).append(&apos;\n&apos;)));
    if (!process.waitFor(1, TimeUnit.SECONDS)) {
        process.destroyForcibly().waitFor();
        throw new TimeoutException(&quot;timed out: &quot; + String.join(&quot; &quot;, command));
    }
    outPump.join();
    errPump.join();
    System.out.println(command[0] + &quot;: exit=&quot; + process.exitValue()
            + &quot;, stdout chars=&quot; + stdout.length() + &quot;, stderr=&quot; + stderr.toString().trim());
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Here is the result of running four commands, &lt;code&gt;echo hello&lt;/code&gt;, &lt;code&gt;seq 1 100000&lt;/code&gt;, &lt;code&gt;ls /no-such-dir&lt;/code&gt;, and &lt;code&gt;sleep 60&lt;/code&gt;, through this method. The 588,895 bytes of output from &lt;code&gt;seq&lt;/code&gt; do not cause a hang, the stderr of &lt;code&gt;ls&lt;/code&gt; is collected separately, and &lt;code&gt;sleep&lt;/code&gt;, which exceeds one second, ends with an exception.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Output of PlainJdkRunner.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;echo: exit=0, stdout chars=6, stderr=
seq: exit=0, stdout chars=588895, stderr=
ls: exit=2, stdout chars=0, stderr=ls: cannot access &apos;/no-such-dir&apos;: No such file or directory
timed out: sleep 60&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This example targets commands that require no input and create no descendant processes.
To use it safely with other commands, you need to take care of the following as well.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For commands that read stdin, add the EOF handling from the previous section.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Propagate exceptions thrown in the reader threads to the calling thread, and clean up the process and streams on interrupt or timeout as well.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The one-second limit in the code above applies only to &lt;code&gt;waitFor()&lt;/code&gt;. If you need an overall timeout that also covers &lt;code&gt;start()&lt;/code&gt;, the subsequent wait for termination, and &lt;code&gt;join()&lt;/code&gt;, implement it separately.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the output is very large, do not collect all of it in a &lt;code&gt;StringBuilder&lt;/code&gt;; process it line by line or with a fixed-size buffer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For commands whose output encoding differs from the parent JVM&amp;#8217;s &lt;code&gt;native.encoding&lt;/code&gt;, specify the encoding with &lt;code&gt;inputReader(Charset)&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;cleaning_up_descendant_processes&quot;&gt;Cleaning Up Descendant Processes&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;destroy()&lt;/code&gt; and &lt;code&gt;destroyForcibly()&lt;/code&gt; send a signal only to the child you created directly.
If the command you ran created child processes of its own, those descendants can be left behind.
When the child exits first, the descendant processes it created are reparented to PID 1 or to the nearest subreaper process and keep running.
A subreaper is a process designated with &lt;code&gt;prctl(PR_SET_CHILD_SUBREAPER)&lt;/code&gt;; it takes over orphaned descendants in place of PID 1. systemd is the program that, on most Linux distributions, runs first as PID 1 at boot and starts and manages the other services, and it also runs a separate instance called &lt;code&gt;systemd --user&lt;/code&gt; for each logged-in user. The &lt;code&gt;systemd --user&lt;/code&gt; of a desktop session is one example of a subreaper.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;cases_where_descendants_are_left_behind&quot;&gt;Cases Where Descendants Are Left Behind&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;There are broadly four situations in which descendant processes are left behind.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Commands run through a shell&lt;/strong&gt;: this is the case when something like &lt;code&gt;sh -c &quot;a | b&quot;&lt;/code&gt; or a script such as &lt;code&gt;run.sh&lt;/code&gt; is terminated on timeout. The non-interactive shell running the script does not forward the SIGTERM it received to its children, so only the shell exits.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Programs run through a wrapper or launcher&lt;/strong&gt;: with &lt;code&gt;npm run&lt;/code&gt;, npm, which runs on Node.js, executes the script via &lt;code&gt;sh -c&lt;/code&gt;, so ending the npm process can leave the command the shell started running. LibreOffice&amp;#8217;s &lt;code&gt;soffice&lt;/code&gt; goes through &lt;code&gt;oosplash&lt;/code&gt; to run &lt;code&gt;soffice.bin&lt;/code&gt;, which does the actual work, and if you end &lt;code&gt;oosplash&lt;/code&gt; with SIGKILL, &lt;code&gt;soffice.bin&lt;/code&gt; is not terminated and stays behind. &lt;code&gt;git&lt;/code&gt; also runs &lt;code&gt;ssh&lt;/code&gt; or &lt;code&gt;git-remote-https&lt;/code&gt; as a child during fetch or clone.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Programs that turn themselves into daemons&lt;/strong&gt;: the Gradle daemon, the Kotlin compile daemon, &lt;code&gt;adb start-server&lt;/code&gt;, and &lt;code&gt;ssh&lt;/code&gt; ControlPersist connections are designed to outlive their caller. Such programs may also leave the original session and group with &lt;code&gt;setsid()&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scripts that do not wait for background jobs&lt;/strong&gt;: if &lt;code&gt;server &amp;amp;&lt;/code&gt; is not followed by &lt;code&gt;wait&lt;/code&gt;, &lt;code&gt;server&lt;/code&gt; keeps running even after the script exits normally. If this descendant keeps the inherited output pipe open, &lt;code&gt;waitFor()&lt;/code&gt; returns but the reader of the output never receives EOF.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Let&amp;#8217;s reproduce the first case. The code below runs &lt;code&gt;sleep 60&lt;/code&gt; through a shell, calls &lt;code&gt;destroy()&lt;/code&gt; after 0.3 seconds, and then prints which of the descendants it looked up before termination are still alive, along with their parent PIDs.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;DescendantLeakDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;public static void main(String[] args) throws Exception {
    run(&quot;sh&quot;, &quot;-c&quot;, &quot;sleep 60&quot;);
    run(&quot;sh&quot;, &quot;-c&quot;, &quot;sleep 60; echo done&quot;);
    run(&quot;sh&quot;, &quot;-c&quot;, &quot;sleep 60 | cat&quot;);
    run(&quot;bash&quot;, &quot;-c&quot;, &quot;sleep 60&quot;);
}

static void run(String... command) throws Exception {
    Process process = new ProcessBuilder(command).start();
    Thread.sleep(300);
    List&amp;lt;ProcessHandle&amp;gt; descendants = process.descendants().toList();
    process.destroy();
    process.waitFor(1, TimeUnit.SECONDS);
    Thread.sleep(100);

    List&amp;lt;String&amp;gt; left = descendants.stream()
            .filter(ProcessHandle::isAlive)
            .map(p -&amp;gt; name(p) + &quot;(ppid=&quot; + p.parent().map(ProcessHandle::pid).orElse(-1L) + &quot;)&quot;)
            .toList();
    System.out.println(String.join(&quot; &quot;, command) + &quot; -&amp;gt; shell alive=&quot; + process.isAlive()
            + &quot;, children=&quot; + descendants.size() + &quot;, left=&quot; + left);
    descendants.forEach(ProcessHandle::destroyForcibly);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Output of DescendantLeakDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;sh -c sleep 60 -&amp;gt; shell alive=false, children=1, left=[sleep(ppid=1693)]
sh -c sleep 60; echo done -&amp;gt; shell alive=false, children=1, left=[sleep(ppid=1693)]
sh -c sleep 60 | cat -&amp;gt; shell alive=false, children=2, left=[sleep(ppid=1693), cat(ppid=1693)]
bash -c sleep 60 -&amp;gt; shell alive=false, children=0, left=[]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In all three cases run with &lt;code&gt;sh -c&lt;/code&gt;, the shell exited but &lt;code&gt;sleep&lt;/code&gt; and &lt;code&gt;cat&lt;/code&gt; remained. PID 1693, the parent of the leftover processes, is the &lt;code&gt;systemd --user&lt;/code&gt; of this environment.
Note that a descendant was left behind even with &lt;code&gt;sh -c &quot;sleep 60&quot;&lt;/code&gt;, which has only a single command. dash 0.5.12, the &lt;code&gt;/bin/sh&lt;/code&gt; on Ubuntu 24.04, ran even the last command received through &lt;code&gt;-c&lt;/code&gt; as a child process. &lt;code&gt;bash -c&lt;/code&gt;, on the other hand, replaced the shell process with &lt;code&gt;sleep&lt;/code&gt; (&lt;code&gt;children=0&lt;/code&gt;), so no descendants were left.
Because this behavior varies with the kind and version of the shell, it is safer to assume that commands run through a shell leave descendants behind and to have a cleanup method ready.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If the leftover descendant is a command that will finish soon, the problem lasts only as long as that descendant runs. Even so, a task you have already marked as failed on timeout may keep writing files or calling external APIs in the background during that time.
The bigger problem is descendants that never finish on their own. The timeout itself means the command did not finish in time, so there is little reason to expect the leftover descendants to finish soon either. Descendants that are not meant to finish, such as servers or daemons, accumulate with each run and consume memory and the process count limit. If they hold on to a port or a file lock, the next run fails with an error like &lt;code&gt;Address already in use&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If a leftover descendant keeps the inherited output pipe open, a read in progress waits for the descendant to close the pipe, which can delay &lt;code&gt;join()&lt;/code&gt; as well. The &lt;code&gt;Process&lt;/code&gt; implementation in JDK 25 reclaims the output remaining in the pipe and closes the stream when the direct child exits, but whether a wait occurs depends on the ordering between a read already in progress and this exit handling.
As in the example above, you can also look up descendants with &lt;code&gt;ProcessHandle.descendants()&lt;/code&gt; from JDK 9 and terminate them one by one. But the result is a snapshot, so it cannot reliably clean up processes created between the lookup and the termination, or those reparented after the parent exited.
To end the descendants in one go, you have to manage them with OS-level process groups or cgroups.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;terminating_by_process_group&quot;&gt;Terminating by Process Group&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;For commands whose descendants stay in the same process group, you can run them under &lt;code&gt;setsid&lt;/code&gt; to create a new session and group, and then signal the whole group to clean up once the work is done.
The JDK API has no facility for creating a process group or signaling one, so this is put together by running the &lt;code&gt;setsid&lt;/code&gt; and &lt;code&gt;kill&lt;/code&gt; commands.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;ProcessGroupKillDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;public static void main(String[] args) throws Exception {
    run(&quot;sleep 60 | cat&quot;);
    run(&quot;(trap &apos;&apos; TERM; sleep 60) &amp;amp; sleep 60&quot;);
    run(&quot;setsid sleep 60 &amp;amp; sleep 60&quot;);
}

static void run(String script) throws Exception {
    Process process = new ProcessBuilder(&quot;setsid&quot;, &quot;sh&quot;, &quot;-c&quot;, script).start();
    List&amp;lt;ProcessHandle&amp;gt; descendants = List.of();
    try {
        if (!process.waitFor(1, TimeUnit.SECONDS)) {
            descendants = process.descendants().toList(); // snapshot for checking the result
        }
    } finally {
        killGroup(process.pid()); // the PID of the child, now a group leader via setsid, is the process group ID
        process.waitFor();
    }

    List&amp;lt;String&amp;gt; left = descendants.stream()
            .filter(ProcessHandle::isAlive)
            .map(p -&amp;gt; p.info().commandLine().orElse(&quot;?&quot;))
            .toList();
    System.out.println(&quot;[&quot; + script + &quot;] pgid=&quot; + process.pid() + &quot;, left=&quot; + left);
    descendants.forEach(ProcessHandle::destroyForcibly);
}

static void killGroup(long pgid) throws Exception {
    signalGroup(&quot;TERM&quot;, pgid);
    if (!awaitGroupExit(pgid, 5)) {
        signalGroup(&quot;KILL&quot;, pgid);
        awaitGroupExit(pgid, 5);
    }
}

static boolean awaitGroupExit(long pgid, long seconds) throws Exception {
    long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(seconds);
    while (signalGroup(&quot;0&quot;, pgid)) {
        if (System.nanoTime() &amp;gt; deadline) {
            return false;
        }
        Thread.sleep(100);
    }
    return true;
}

// an exit code of 0 from kill means at least one process in the group received the signal
static boolean signalGroup(String signal, long pgid) throws Exception {
    Process kill = new ProcessBuilder(&quot;kill&quot;, &quot;-&quot; + signal, &quot;--&quot;, &quot;-&quot; + pgid)
            .redirectErrorStream(true)
            .redirectOutput(Redirect.DISCARD)
            .start();
    return kill.waitFor() == 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The util-linux &lt;code&gt;setsid&lt;/code&gt; command only calls &lt;code&gt;fork()&lt;/code&gt; when the calling process is already a group leader; otherwise it calls &lt;code&gt;setsid()&lt;/code&gt; in its own process and then executes the command. A child created by the JVM is not a group leader, so its PID does not change and &lt;code&gt;process.pid()&lt;/code&gt; can be used as the ID of the new group.
Passing a negative PID argument to &lt;code&gt;kill&lt;/code&gt; sends the signal to the entire process group whose ID is the absolute value. The exception is &lt;code&gt;-1&lt;/code&gt;, which means every process you have permission to signal rather than a group. Because kill treats arguments starting with &lt;code&gt;-&lt;/code&gt;, such as &lt;code&gt;-9&lt;/code&gt; or &lt;code&gt;-KILL&lt;/code&gt;, as signals, &lt;code&gt;--&lt;/code&gt; is placed before the PID so that &lt;code&gt;-295952&lt;/code&gt; is read as a PID. Signal number 0 sends no actual signal and only checks that the target exists and that you have permission, so if &lt;code&gt;kill -0&lt;/code&gt; succeeds, processes remain in the group.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;waitFor()&lt;/code&gt; only waits for the shell, the direct child, so the shell exiting does not mean the whole group has exited. That is why &lt;code&gt;killGroup()&lt;/code&gt; sends SIGTERM, then waits up to 5 seconds while checking with &lt;code&gt;kill -0&lt;/code&gt; whether any processes remain in the group, and sends SIGKILL to the whole group if some are still there.
The cleanup is done in &lt;code&gt;finally&lt;/code&gt;, always, not only on timeout. Even when the direct child exits normally, descendants started in the background can remain. For example, with a script that only runs &lt;code&gt;sleep 60 &amp;amp;&lt;/code&gt;, the shell exits immediately and &lt;code&gt;waitFor()&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt;, but the &lt;code&gt;killGroup()&lt;/code&gt; in &lt;code&gt;finally&lt;/code&gt; cleans up the leftover &lt;code&gt;sleep&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Output of ProcessGroupKillDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;[sleep 60 | cat] pgid=295952, left=[]
[(trap &apos;&apos; TERM; sleep 60) &amp;amp; sleep 60] pgid=295959, left=[]
[setsid sleep 60 &amp;amp; sleep 60] pgid=296018, left=[/usr/bin/sleep 60]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;code&gt;sleep&lt;/code&gt; and &lt;code&gt;cat&lt;/code&gt; of the first command were in the same group as the shell, so SIGTERM ended them together.
The shell of the second command ended immediately on SIGTERM, but the &lt;code&gt;sleep&lt;/code&gt;, which was run to ignore SIGTERM, ended on SIGKILL after the 5-second grace period. Had SIGKILL been skipped on seeing only that the shell had exited, this &lt;code&gt;sleep&lt;/code&gt; would have remained.
In the third command, the &lt;code&gt;sleep&lt;/code&gt; run under &lt;code&gt;setsid&lt;/code&gt; moved to a new session and group, so it did not receive the signal sent to the group. When a descendant moves to a new session or a different group with &lt;code&gt;setsid()&lt;/code&gt; or &lt;code&gt;setpgid()&lt;/code&gt;, this method cannot clean it up. The daemon-style programs seen earlier fall into this category.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;terminating_by_cgroup&quot;&gt;Terminating by cgroup&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;cgroup (control group) is a Linux kernel feature that groups processes into a hierarchy to limit and measure resources such as CPU, memory, I/O, and the number of processes. The memory limits of Docker containers also work through cgroups.
A child created with &lt;code&gt;fork()&lt;/code&gt; starts out in its parent&amp;#8217;s cgroup, so grandchildren and all descendants below them end up in the same cgroup.
&lt;code&gt;setsid()&lt;/code&gt; changes only the session and the process group, not the cgroup. In cgroup v2, moving a process to another cgroup requires write permission on the &lt;code&gt;cgroup.procs&lt;/code&gt; file of the common ancestor of the source and destination cgroups. So a descendant without that permission cannot escape the cgroup.
The cgroup the current process belongs to can be seen in &lt;code&gt;/proc/self/cgroup&lt;/code&gt;, and the whole tree with the &lt;code&gt;systemd-cgls&lt;/code&gt; command.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;systemd creates a separate cgroup for each service, and when it stops a service it terminates every process left in that cgroup (&lt;code&gt;KillMode=control-group&lt;/code&gt; is the default).
With &lt;code&gt;systemd-run --scope&lt;/code&gt;, the same approach can be applied to a one-off command. The code below runs the command that escaped the process group earlier as a transient scope unit and stops that unit when the job is done.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;SystemdScopeDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;String unit = &quot;job-&quot; + UUID.randomUUID() + &quot;.scope&quot;;
Process process = new ProcessBuilder(
        &quot;systemd-run&quot;, &quot;--user&quot;, &quot;--scope&quot;, &quot;--quiet&quot;, &quot;--unit=&quot; + unit,
        &quot;sh&quot;, &quot;-c&quot;, &quot;setsid sleep 60 &amp;amp; sleep 60&quot;)
        .redirectError(Redirect.INHERIT)
        .start();
List&amp;lt;ProcessHandle&amp;gt; descendants = List.of();
try {
    if (process.waitFor(1, TimeUnit.SECONDS)) {
        System.out.println(&quot;exit=&quot; + process.exitValue()); // a failure of systemd-run also shows up here
    } else {
        System.out.println(&quot;cgroup: &quot; + Files.readString(Path.of(&quot;/proc/&quot; + process.pid() + &quot;/cgroup&quot;)).trim());
        descendants = process.descendants().toList(); // snapshot for checking the result
    }
} finally {
    int stopExit = new ProcessBuilder(&quot;systemctl&quot;, &quot;--user&quot;, &quot;stop&quot;, unit)
            .redirectError(Redirect.INHERIT)
            .start()
            .waitFor();
    System.out.println(&quot;systemctl stop exit=&quot; + stopExit);
    if (!process.waitFor(10, TimeUnit.SECONDS)) {
        process.destroyForcibly().waitFor();
    }
}

List&amp;lt;String&amp;gt; left = descendants.stream()
        .filter(ProcessHandle::isAlive)
        .map(p -&amp;gt; p.info().commandLine().orElse(&quot;?&quot;))
        .toList();
System.out.println(&quot;descendants=&quot; + descendants.size() + &quot;, left=&quot; + left);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Output of SystemdScopeDemo.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;cgroup: 0::/user.slice/user-1000.slice/user@1000.service/app.slice/job-0fbacb17-d0c0-47de-a65d-db30d90ecfb1.scope
systemctl stop exit=0
descendants=2, left=[]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When run with &lt;code&gt;--scope&lt;/code&gt;, &lt;code&gt;systemd-run&lt;/code&gt; waits for the scope unit to start and then replaces its own process with the command, so the PID does not change. The command is still a direct child of the JVM, so you can read its output and wait for it to exit through &lt;code&gt;Process&lt;/code&gt; just as in the earlier examples.
The cgroup path in the output ends with &lt;code&gt;job-&amp;#8230;&amp;#8203;scope&lt;/code&gt;, which confirms that the command ran in a dedicated cgroup. When &lt;code&gt;systemctl stop&lt;/code&gt; was called, both descendants ended, including the &lt;code&gt;sleep&lt;/code&gt; that had left the group with &lt;code&gt;setsid&lt;/code&gt;.
&lt;code&gt;systemctl stop&lt;/code&gt; sends SIGTERM first, then SIGKILL to any process that has not exited within the grace period (&lt;code&gt;TimeoutStopSec&lt;/code&gt;). The grace period can be set on &lt;code&gt;systemd-run&lt;/code&gt; with an option such as &lt;code&gt;-p TimeoutStopSec=5s&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;A scope is not tied to a specific process; it stays alive as long as at least one process remains in it. Even if the shell exits first, the scope remains active while a descendant launched in the background is still around, so the unit is always stopped in &lt;code&gt;finally&lt;/code&gt;, as in the earlier process group example.
If &lt;code&gt;systemd-run&lt;/code&gt; fails, for example because it cannot connect to the user bus, it prints an error to stderr and exits with a nonzero exit code, so the example passes stderr through to the parent and prints the exit code. It also checks the exit code of &lt;code&gt;systemctl stop&lt;/code&gt; and puts a time limit on &lt;code&gt;waitFor()&lt;/code&gt; so that it does not wait forever when cleanup fails. If the command finished first and the scope is already gone, &lt;code&gt;systemctl stop&lt;/code&gt; returns exit code 5, meaning the unit does not exist, so real code should treat that case as normal.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This approach has preconditions.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Using &lt;code&gt;--user&lt;/code&gt; requires the user&amp;#8217;s systemd instance to be running. For a service account on a server with no login session, either keep the user instance alive with &lt;code&gt;loginctl enable-linger&lt;/code&gt; or use the privileged system instance.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Most containers have no systemd inside, so this approach cannot be used there. In that case, consider running a separate container per job and cleaning up at the container level.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you have been delegated permission to manage cgroup v2 directly without systemd, you can create a cgroup for the job, start the command inside it, and then write &lt;code&gt;1&lt;/code&gt; to the &lt;code&gt;cgroup.kill&lt;/code&gt; file to send SIGKILL to every process, including those in child cgroups. This file is supported since Linux 5.14.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Whichever approach you take, the target job must be started inside that cgroup from the beginning, and the permission to move out of it must be managed together with the termination policy.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To sum up, if you control the command you run and it does not become a daemon, the process group approach is enough. If you run arbitrary scripts or commands received from outside, cleaning up by cgroup is the more reliable choice.
The libraries in the next section also cut down the code for output handling and timeouts, but they do not solve all of these conditions.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;using_zt_exec_and_apache_commons_exec&quot;&gt;Using zt-exec and Apache Commons Exec&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Writing code by hand that covers everything from the previous chapter, the output pipes, EOF on stdin, timeouts, and the termination policy, is not easy.
Even with a good understanding of the principles, reimplementing this code in every project is tedious.
In practice, using a library that already has this handling built in is the pragmatic choice.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The representative libraries in this area are zt-exec and Apache Commons Exec.
Both drain the output pipes on separate threads, and when no input is specified, a class named &lt;code&gt;PumpStreamHandler&lt;/code&gt; closes the child process&amp;#8217;s stdin so that the child receives EOF.
zt-exec&amp;#8217;s &lt;code&gt;PumpStreamHandler&lt;/code&gt; class is taken from Commons Exec, to the point that the top of its source file says &quot;This file originates from the Apache Commons Exec package&quot;. The main differences compared in this article are the shape of the API and the defaults.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;zt_exec&quot;&gt;zt-exec&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/zeroturnaround/zt-exec&quot;&gt;zt-exec&lt;/a&gt; is a library that ZeroTurnaround, the maker of JRebel, released in 2013 after consolidating the process execution code scattered across its internal projects.
Its official name is ZT Process Executor, as the title of the &lt;a href=&quot;https://github.com/zeroturnaround/zt-exec/blob/v1.13.0/README.md&quot;&gt;README&lt;/a&gt; says, but since the GitHub repository and the Maven artifactId are zt-exec, it is usually called zt-exec. This article uses zt-exec as well.
The README explains that the goal is to provide all the functionality of &lt;code&gt;ProcessBuilder&lt;/code&gt; and Commons Exec through a single &lt;code&gt;ProcessExecutor&lt;/code&gt; class.
Its only dependency is slf4j-api, and the minimum Java version is 8.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.zeroturnaround&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;zt-exec&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.13.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The advantages of zt-exec are as follows.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The defaults are safe.&lt;/strong&gt; Calling &lt;code&gt;execute()&lt;/code&gt; with no configuration merges stderr into stdout and discards that output. The deadlock caused by an unread pipe does not happen with the defaults. If you need the output, turn on &lt;code&gt;readOutput(true)&lt;/code&gt; and get a string from the result&amp;#8217;s &lt;code&gt;outputUTF8()&lt;/code&gt;, or pass a function that handles the output line by line to &lt;code&gt;redirectOutput()&lt;/code&gt; as a lambda expression.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Timeouts are distinguished by exception type.&lt;/strong&gt; When the wait in &lt;code&gt;execute()&lt;/code&gt; exceeds the time set with &lt;code&gt;timeout()&lt;/code&gt;, the caller receives a &lt;code&gt;TimeoutException&lt;/code&gt;, and the worker thread attempts termination with the stopper. An invalid exit code and a timeout can be told apart by exception type alone. However, there is no guarantee that the stopper has finished terminating the process at the moment the exception is caught. The default stopper only calls &lt;code&gt;destroy()&lt;/code&gt;, so on Linux it does not forcibly kill a process that ignores SIGTERM. The termination method can be changed with &lt;code&gt;stopper()&lt;/code&gt;. Implement the &lt;code&gt;ProcessStopper&lt;/code&gt; interface with a policy that calls &lt;code&gt;destroy()&lt;/code&gt;, waits for a grace period, and then calls &lt;code&gt;destroyForcibly()&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Exit code checking is declarative.&lt;/strong&gt; By default every exit code is allowed, and when you specify the allowed range with &lt;code&gt;exitValueNormal()&lt;/code&gt; or &lt;code&gt;exitValues(0, 1)&lt;/code&gt;, any other value raises an &lt;code&gt;InvalidExitValueException&lt;/code&gt;. The exception object&amp;#8217;s &lt;code&gt;getResult()&lt;/code&gt; gives access to the output up to that point, which is handy for logging the error message.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Auxiliary features are simple to configure.&lt;/strong&gt; &lt;code&gt;start().getFuture()&lt;/code&gt; runs the process asynchronously. However, &lt;code&gt;start()&lt;/code&gt; ignores the &lt;code&gt;timeout()&lt;/code&gt; setting, so you must limit the wait with something like &lt;code&gt;Future.get(timeout, unit)&lt;/code&gt; and handle cancellation and termination separately on timeout. A timeout from &lt;code&gt;get()&lt;/code&gt; alone does not terminate the process. &lt;code&gt;destroyOnExit()&lt;/code&gt; requests termination of the child process from a JVM shutdown hook. &lt;code&gt;redirectOutputAsInfo()&lt;/code&gt; can forward the output to an SLF4J logger, but this method is deprecated and &lt;code&gt;redirectOutput(Slf4jStream.of(logger).asInfo())&lt;/code&gt; is the recommended API.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Here is an example that runs the four commands from the previous chapter with zt-exec.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;ZtExecRunner.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;static void captureOutput() throws IOException, InterruptedException, TimeoutException {
    ProcessResult result = new ProcessExecutor()
            .command(&quot;echo&quot;, &quot;hello&quot;)
            .readOutput(true)
            .exitValueNormal()
            .execute();
    System.out.println(&quot;output=&quot; + result.outputUTF8().trim() + &quot;, exit=&quot; + result.getExitValue());
}

static void largeOutput() throws IOException, InterruptedException, TimeoutException {
    AtomicLong lines = new AtomicLong();
    ProcessResult result = new ProcessExecutor()
            .command(&quot;seq&quot;, &quot;1&quot;, &quot;100000&quot;)
            .redirectOutput(line -&amp;gt; lines.incrementAndGet())
            .timeout(3, TimeUnit.SECONDS)
            .execute();
    System.out.println(&quot;seq lines=&quot; + lines.get() + &quot;, exit=&quot; + result.getExitValue());
}

static void timeout() throws IOException, InterruptedException {
    try {
        new ProcessExecutor()
                .command(&quot;sleep&quot;, &quot;60&quot;)
                .timeout(1, TimeUnit.SECONDS)
                .execute();
    } catch (TimeoutException e) {
        System.out.println(&quot;timeout: &quot; + e.getMessage());
    }
}

static void exitValue() throws IOException, InterruptedException, TimeoutException {
    try {
        new ProcessExecutor()
                .command(&quot;ls&quot;, &quot;/no-such-dir&quot;)
                .readOutput(true)
                .exitValueNormal()
                .execute();
    } catch (InvalidExitValueException e) {
        System.out.println(&quot;exit=&quot; + e.getExitValue() + &quot;, output=&quot; + e.getResult().outputUTF8().trim());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Here is the output. The message of the &lt;code&gt;TimeoutException&lt;/code&gt; contains the executed command and the time limit, and the error message from &lt;code&gt;ls&lt;/code&gt; is read with &lt;code&gt;outputUTF8()&lt;/code&gt; thanks to the default that merges stderr into stdout. I also confirmed that no &lt;code&gt;sleep&lt;/code&gt; process was left behind after the timeout.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Output of ZtExecRunner.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;output=hello, exit=0
seq lines=100000, exit=0
timeout: Timed out waiting for Process[pid=294823, exitValue=&quot;not exited&quot;] to finish, timeout: 1 second, executed command [sleep, 60]
exit=2, output=ls: cannot access &apos;/no-such-dir&apos;: No such file or directory&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;There are caveats as well. With &lt;code&gt;readOutput(true)&lt;/code&gt;, the thread reading the output pipe stores every byte it reads in a &lt;code&gt;ByteArrayOutputStream&lt;/code&gt;. When the result is built, &lt;code&gt;toByteArray()&lt;/code&gt; copies it once more, and calling &lt;code&gt;outputUTF8()&lt;/code&gt; creates a string as well. Since &lt;code&gt;ByteArrayOutputStream&lt;/code&gt; doubles its size when the buffer fills up, the internal buffer alone can grow larger than the output, and a string containing characters outside Latin-1, such as Korean, uses 2 bytes per character. So peak memory usage can exceed twice the size of the output.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you do not need to keep the entire output, you can leave &lt;code&gt;readOutput&lt;/code&gt; off and pass a line-by-line handler to &lt;code&gt;redirectOutput()&lt;/code&gt;, as in &lt;code&gt;largeOutput()&lt;/code&gt; above. The buffer size in this approach depends on the length of the longest line rather than the total output. Huge output with no line breaks still takes a lot of memory, and if the handler is slow, the child process&amp;#8217;s output backs up as well. Such output is better sent to a file or to an &lt;code&gt;OutputStream&lt;/code&gt; that works with a fixed-size buffer.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This example used SLF4J API 2.0.17, so with no implementation present, three lines of warnings including &quot;No SLF4J providers were found&quot; appeared during initialization. The dependency declared in the POM of zt-exec 1.13.0 is SLF4J API 1.7.32, and the warning text in that version is different. If you do not need logging, you can add the &lt;code&gt;slf4j-nop&lt;/code&gt; that matches the SLF4J API you use to silence the warning.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;apache_commons_exec&quot;&gt;Apache Commons Exec&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://commons.apache.org/proper/commons-exec/&quot;&gt;Apache Commons Exec&lt;/a&gt; is a library that composes command execution, output stream handling, and timeouts from &lt;code&gt;DefaultExecutor&lt;/code&gt;, &lt;code&gt;PumpStreamHandler&lt;/code&gt;, and &lt;code&gt;ExecuteWatchdog&lt;/code&gt;, respectively. Version 1.6.0, used in this article, runs on Java 8 or later and has no external dependencies. &lt;code&gt;DefaultExecutor&lt;/code&gt; and &lt;code&gt;ExecuteWatchdog&lt;/code&gt; are created with a builder API, and the timeout is specified as a &lt;code&gt;Duration&lt;/code&gt;. The old constructors are marked deprecated.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.apache.commons&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;commons-exec&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.6.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Here is an example that runs the same four commands with 1.6.0. The &lt;code&gt;PumpStreamHandler&lt;/code&gt; class in charge of stream handling writes to &lt;code&gt;System.out&lt;/code&gt; and &lt;code&gt;System.err&lt;/code&gt; by default, so to collect the output you pass your own &lt;code&gt;OutputStream&lt;/code&gt; instances as below.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;CommonsExecRunner.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;static void run(String... command) throws IOException {
    CommandLine cmdLine = new CommandLine(command[0]);
    cmdLine.addArguments(Arrays.copyOfRange(command, 1, command.length), false);
    ByteArrayOutputStream stdout = new ByteArrayOutputStream();
    ByteArrayOutputStream stderr = new ByteArrayOutputStream();
    ExecuteWatchdog watchdog = ExecuteWatchdog.builder().setTimeout(Duration.ofSeconds(1)).get();
    DefaultExecutor executor = DefaultExecutor.builder().get();
    executor.setStreamHandler(new PumpStreamHandler(stdout, stderr));
    executor.setWatchdog(watchdog);
    try {
        int exitValue = executor.execute(cmdLine);
        System.out.println(command[0] + &quot;: exit=&quot; + exitValue + &quot;, stdout bytes=&quot; + stdout.size());
    } catch (ExecuteException e) {
        System.out.println(command[0] + &quot;: exit=&quot; + e.getExitValue()
                + &quot;, killed by watchdog=&quot; + watchdog.killedProcess()
                + &quot;, stderr=&quot; + stderr.toString().trim());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Output of CommonsExecRunner.java&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;echo: exit=0, stdout bytes=6
seq: exit=0, stdout bytes=588895
sleep: exit=143, killed by watchdog=true, stderr=
ls: exit=2, killed by watchdog=false, stderr=ls: cannot access &apos;/no-such-dir&apos;: No such file or directory&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;On Linux, &lt;code&gt;DefaultExecutor&lt;/code&gt; throws an &lt;code&gt;ExecuteException&lt;/code&gt; for a nonzero exit code by default.
The exit codes that are not treated as errors can be changed with &lt;code&gt;setExitValue()&lt;/code&gt; or &lt;code&gt;setExitValues()&lt;/code&gt;, and the check can be turned off with &lt;code&gt;setExitValues(null)&lt;/code&gt;.
The &lt;code&gt;sleep&lt;/code&gt; in the output above ended from the SIGTERM sent by the watchdog, so its exit code is 143.
In this example, after catching the exception, &lt;code&gt;watchdog.killedProcess()&lt;/code&gt; is used to tell whether the watchdog intervened.
However, this method indicates whether &lt;code&gt;destroy()&lt;/code&gt; was called and does not guarantee that the process actually terminated.
The &lt;code&gt;ExecuteWatchdog&lt;/code&gt; in 1.6.0 only calls &lt;code&gt;destroy()&lt;/code&gt; on timeout and has no API for escalating to a forcible kill.
If you need a forcible kill, you have to write separate code that finds the processes left after the timeout through &lt;code&gt;ProcessHandle&lt;/code&gt; and calls &lt;code&gt;destroyForcibly()&lt;/code&gt;.
If the command ignores SIGTERM, &lt;code&gt;execute()&lt;/code&gt; may keep waiting. Conversely, if a command that handles SIGTERM exits with an allowed code, &lt;code&gt;execute()&lt;/code&gt; may return without an exception even after the timeout. So &lt;code&gt;killedProcess()&lt;/code&gt; must be checked on the normal return path as well.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;maintenance_status_and_recommendation&quot;&gt;Maintenance Status and Recommendation&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Below is the status of the two libraries as of 2026-09-06, checked against the version list on Maven Central and each project&amp;#8217;s change log.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 20%;&quot;&gt;
&lt;col style=&quot;width: 40%;&quot;&gt;
&lt;col style=&quot;width: 40%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Item&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;zt-exec&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Apache Commons Exec&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Latest version&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;1.13.0 (2026-07-10)&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;1.6.0 (2025-11-25)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Previous releases&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;1.12 (2020-09-02), 1.11 (2019-07-05)&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;1.5.0 (2025-05-16), 1.4.0 (2024-01-01), 1.3 (2014-11-02)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Releases in the last 5 years&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;1&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;3&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Minimum Java version&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;8&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;8&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Dependencies&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;slf4j-api&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;None&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Default output handling&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Discarded (stderr merged into stdout)&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Written to &lt;code&gt;System.out&lt;/code&gt; and &lt;code&gt;System.err&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Timeout notification&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;TimeoutException&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Checked with &lt;code&gt;watchdog.killedProcess()&lt;/code&gt; (&lt;code&gt;ExecuteException&lt;/code&gt; if the exit code is a failure)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Nonzero exit code&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Allowed by default, &lt;code&gt;InvalidExitValueException&lt;/code&gt; when restricted with &lt;code&gt;exitValues()&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;ExecuteException&lt;/code&gt; by default&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Termination on timeout&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;destroy()&lt;/code&gt;, replaceable with &lt;code&gt;stopper()&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;destroy()&lt;/code&gt; only&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Of the two, I recommend zt-exec.
Its API design has three advantages.
It distinguishes timeouts from exit code errors by exception type, it takes a single method call to receive the output as a string, and its default settings avoid the deadlock caused by not reading the output pipe.
Both libraries use &lt;code&gt;destroy()&lt;/code&gt; as the default termination request. zt-exec lets you add a forcible termination policy with &lt;code&gt;stopper()&lt;/code&gt;, but Commons Exec requires separate code.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;process_creation_on_linux&quot;&gt;Process Creation on Linux&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;Java developers running JDK 25 on Linux today can leave the &lt;code&gt;jdk.lang.Process.launchMechanism&lt;/code&gt; option unset and keep the default, &lt;code&gt;POSIX_SPAWN&lt;/code&gt;.&lt;/strong&gt; It has been the default since JDK 13.
To see why, this chapter first reviews the three ways Linux offers to create a process and the history of the JDK default, then looks in turn at how &lt;code&gt;POSIX_SPAWN&lt;/code&gt; works, the memory cost and launch latency of the &lt;code&gt;FORK&lt;/code&gt; alternative, and finally the &lt;code&gt;VFORK&lt;/code&gt; setting to clean up when upgrading the JVM.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;three_ways_to_create_a_process_and_the_history_of_the_jdk_default&quot;&gt;Three Ways to Create a Process and the History of the JDK Default&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;An application&amp;#8217;s code for launching an external process passes through the JVM and ends up as a kernel system call or a C library function call.
Which function call creates the child process matters because this choice has led to real production failures in the past. The classic case is a JVM with a large heap failing to allocate memory while trying to run a single external command. The 2015 NAVER D2 article &lt;a href=&quot;https://d2.naver.com/helloworld/1113548&quot;&gt;Running external processes in Java (NAVER D2, 2015, in Korean)&lt;/a&gt; describes a Tomcat server configured with a large heap on which launching an external process failed with a &lt;code&gt;Cannot allocate memory&lt;/code&gt; exception. The OpenJDK issue tracker also records this history. The title of &lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-6868160&quot;&gt;JDK-6868160&lt;/a&gt;, which landed in JDK 7, is &quot;(process) Use vfork, not fork, on Linux to avoid swap exhaustion&quot;. In other words, changing the call that creates the child process was a measure to avoid swap exhaustion. That you can now simply leave the default alone is the result of these problems being solved one after another.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To run another program on Linux, you pair a call that creates a child process with &lt;code&gt;execve()&lt;/code&gt;, the system call that replaces that child with another program. The part where you have a choice is the first half, that is, how to create the child. There are three candidates.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;fork()&lt;/code&gt;: a system call that creates a child by duplicating the parent&amp;#8217;s address space.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;vfork()&lt;/code&gt;: a system call that creates a child that shares the parent&amp;#8217;s address space instead of duplicating it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;posix_spawn()&lt;/code&gt;: a C library function that bundles child creation and &lt;code&gt;execve()&lt;/code&gt; together.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The Linux kernel handles the &lt;code&gt;fork()&lt;/code&gt;, &lt;code&gt;vfork()&lt;/code&gt;, and &lt;code&gt;clone()&lt;/code&gt; system calls with the same function, &lt;code&gt;kernel_clone()&lt;/code&gt;, and flags determine what is shared with the parent. In glibc 2.39 on x86-64, the environment used for this article, the &lt;code&gt;fork()&lt;/code&gt; function invokes the &lt;code&gt;clone()&lt;/code&gt; system call, while the &lt;code&gt;vfork()&lt;/code&gt; function invokes the separate &lt;code&gt;vfork()&lt;/code&gt; system call directly. Even in the same glibc 2.39, the &lt;a href=&quot;https://github.com/bminor/glibc/blob/glibc-2.39/sysdeps/unix/sysv/linux/aarch64/vfork.S&quot;&gt;AArch64 vfork implementation&lt;/a&gt; uses the &lt;code&gt;clone()&lt;/code&gt; system call, so the call path differs by architecture. The &lt;a href=&quot;https://man7.org/linux/man-pages/man2/vfork.2.html&quot;&gt;vfork(2)&lt;/a&gt; manual describes &lt;code&gt;vfork()&lt;/code&gt; as equivalent to &lt;code&gt;clone()&lt;/code&gt; with the flags &lt;code&gt;CLONE_VM | CLONE_VFORK | SIGCHLD&lt;/code&gt;. These flags appear in the strace output later in this chapter.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Only &lt;code&gt;posix_spawn()&lt;/code&gt; sits at a different layer. It is a function specified by POSIX and implemented by the C library, so it has no corresponding system call; internally it picks one of the two methods above, or a &lt;code&gt;clone()&lt;/code&gt; with the same flags as &lt;code&gt;vfork()&lt;/code&gt;.
The JDK does not implement any of the three methods itself; all three call glibc functions.
You can confirm this in the dynamic symbols of &lt;code&gt;libjava.so&lt;/code&gt;, which contains the JVM&amp;#8217;s process creation code.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;$ nm -D $JAVA_HOME/lib/libjava.so | grep -E &apos;posix_spawn|fork&apos;
                 U fork@GLIBC_2.2.5
00000000000125c0 T Java_java_lang_ProcessImpl_forkAndExec
                 U posix_spawn@GLIBC_2.15
                 U vfork@GLIBC_2.2.5&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;T&lt;/code&gt; marks a symbol this file defines, and &lt;code&gt;U&lt;/code&gt; marks a symbol it does not define and looks up in another library at run time.
What OpenJDK built is the native method &lt;code&gt;forkAndExec&lt;/code&gt;; the functions corresponding to the three launch mechanisms are all linked to glibc symbols carrying &lt;code&gt;@GLIBC_&lt;/code&gt; version tags.
So even with the same &lt;code&gt;POSIX_SPAWN&lt;/code&gt; setting, the actual behavior depends on the libc and its version.
A JDK linked against musl rather than glibc gets musl&amp;#8217;s implementation in the same place.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The pros and cons of the three methods are as follows.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 20%;&quot;&gt;
&lt;col style=&quot;width: 40%;&quot;&gt;
&lt;col style=&quot;width: 40%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Method&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Advantages&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Disadvantages&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;fork()&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;The child&amp;#8217;s address space is separate, so it can do preparation work such as cleaning up file descriptors or changing the working directory without corrupting the parent&amp;#8217;s memory&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Copy-on-write defers copying the pages, but the page tables are copied, so the larger the heap the higher the creation cost, and the call is subject to the kernel&amp;#8217;s overcommit check&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;vfork()&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;The page tables are not copied, so the creation cost is nearly independent of the parent&amp;#8217;s heap size&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Behavior is undefined if the child does anything other than &lt;code&gt;execve()&lt;/code&gt; or &lt;code&gt;_exit()&lt;/code&gt;, and the parent&amp;#8217;s calling thread is blocked in the meantime&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;posix_spawn()&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Preparation work is handled in the way the library defines, and since glibc 2.24 it uses &lt;code&gt;CLONE_VM&lt;/code&gt; to avoid copying the page tables as well&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;The preparation work the child can do is limited to what the library supports, and the internal implementation varies with the libc and its version&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;That said, &lt;code&gt;fork()&lt;/code&gt; does not mean the child may call arbitrary functions before &lt;code&gt;execve()&lt;/code&gt; either. The &lt;a href=&quot;https://man7.org/linux/man-pages/man2/fork.2.html&quot;&gt;fork(2)&lt;/a&gt; manual restricts the functions a &lt;code&gt;fork()`ed child in a multithreaded program can safely call before `execve()&lt;/code&gt; to async-signal-safe functions. The other threads are not duplicated into the child, but the lock state those threads were holding can remain.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The vfork(2) manual describes the cost of &lt;code&gt;fork()&lt;/code&gt; as &quot;the time and memory required to duplicate the parent&amp;#8217;s page tables, and to create a unique task structure for the child&quot;.
This cost grows with the parent&amp;#8217;s heap size. In the measurements later in this chapter, running one command with the &lt;code&gt;FORK&lt;/code&gt; mechanism took 16 to 20 ms with a 256MB heap but 230 to 240 ms with an 8GB heap.
The constraints on &lt;code&gt;vfork()&lt;/code&gt; are stated even more strongly in the &lt;a href=&quot;https://man7.org/linux/man-pages/man2/vfork.2.html&quot;&gt;vfork(2)&lt;/a&gt; manual. Modifying any data other than the &lt;code&gt;pid_t&lt;/code&gt; variable that holds the return value, returning from the function that called &lt;code&gt;vfork()&lt;/code&gt;, or calling any function other than &lt;code&gt;_exit()&lt;/code&gt; and &lt;code&gt;execve()&lt;/code&gt; results in undefined behavior. Because the child modifies memory and a stack it shares with the parent, the outcome after the parent resumes cannot be predicted.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;OpenJDK went through these three methods in turn. It used &lt;code&gt;fork()&lt;/code&gt; at first, but when the creation cost became a problem with large heaps, JDK 7 switched the default to &lt;code&gt;vfork()&lt;/code&gt;. The JDK, however, did preparation work between &lt;code&gt;vfork()&lt;/code&gt; and &lt;code&gt;execve()&lt;/code&gt;, closing file descriptors and changing the working directory, which violated the &lt;code&gt;vfork()&lt;/code&gt; constraints we just saw. It was a choice that reduced the launch cost at the expense of safety. &lt;code&gt;POSIX_SPAWN&lt;/code&gt;, the default since JDK 13, moves this preparation work into a separate process that does not share memory with the parent, avoiding both the copying cost of &lt;code&gt;fork()&lt;/code&gt; and the constraint violations of &lt;code&gt;vfork()&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The three methods correspond to the values &lt;code&gt;FORK&lt;/code&gt;, &lt;code&gt;VFORK&lt;/code&gt;, and &lt;code&gt;POSIX_SPAWN&lt;/code&gt; of the implementation-specific system property &lt;code&gt;jdk.lang.Process.launchMechanism&lt;/code&gt;.
These three values are declared in &lt;a href=&quot;https://github.com/openjdk/jdk/blob/jdk-25%2B36/src/java.base/unix/classes/java/lang/ProcessImpl.java&quot;&gt;ProcessImpl.java&lt;/a&gt; in JDK 25, and on Linux all three can be specified, though &lt;code&gt;VFORK&lt;/code&gt; comes with a warning.
The history of the default is shown below. The JDK 27 entry is not a GA release result but what has been merged into the development build as of 2026-09-06.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 25%;&quot;&gt;
&lt;col style=&quot;width: 75%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;JDK&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Change on Linux&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;6&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;fork()&lt;/code&gt; + &lt;code&gt;execve()&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;7&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;vfork()&lt;/code&gt; becomes the default (&lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-6868160&quot;&gt;JDK-6868160&lt;/a&gt;)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;12&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;POSIX_SPAWN&lt;/code&gt; added as an optional mechanism (&lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-8212828&quot;&gt;JDK-8212828&lt;/a&gt;). Also backported to 11.0.4&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;13&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;POSIX_SPAWN&lt;/code&gt; becomes the default (&lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-8213192&quot;&gt;JDK-8213192&lt;/a&gt;)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;25&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Specifying &lt;code&gt;VFORK&lt;/code&gt; prints a deprecation warning (&lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-8357180&quot;&gt;JDK-8357180&lt;/a&gt;)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;27&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;VFORK&lt;/code&gt; removed in the development build. Specifying it prints a warning and falls back to &lt;code&gt;FORK&lt;/code&gt; (&lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-8357089&quot;&gt;JDK-8357089&lt;/a&gt;, &lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-8357090&quot;&gt;CSR&lt;/a&gt;)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Why &lt;code&gt;VFORK&lt;/code&gt; went away, and how to clean up any remaining setting, is covered in the last section of this chapter.
The next section looks at how &lt;code&gt;POSIX_SPAWN&lt;/code&gt;, the default since JDK 13, moves the preparation work into a separate process.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;how_posix_spawn_works&quot;&gt;How POSIX_SPAWN Works&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;posix_spawn()&lt;/code&gt; bundles child creation, &lt;code&gt;execve()&lt;/code&gt;, and the preparation work in between into a single call.
It is similar to &lt;code&gt;CreateProcess()&lt;/code&gt; on Windows.
The JDK uses this function to first launch a small executable called &lt;code&gt;jspawnhelper&lt;/code&gt;; &lt;code&gt;jspawnhelper&lt;/code&gt; cleans up file descriptors and the working directory according to the settings it receives over a pipe, then `execve()`s the actual command. This structure is described in the comment at the top of &lt;a href=&quot;https://github.com/openjdk/jdk/blob/jdk-25%2B36/src/java.base/unix/native/libjava/ProcessImpl_md.c&quot;&gt;ProcessImpl_md.c&lt;/a&gt; in the JDK 25 source.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The reason for the extra hop through the helper is the safety problem from the previous section.
Doing preparation work such as closing file descriptors and changing the working directory inside jspawnhelper, which was launched by the first &lt;code&gt;execve()&lt;/code&gt; and is therefore a separate process that does not share memory with the parent, does not violate the &lt;code&gt;vfork()&lt;/code&gt; constraints.
The same comment describes this as &quot;moving the preparation work after the first exec to narrow the vulnerable window&quot;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Viewed with strace, a tool that records the system calls a program makes in order, &lt;code&gt;execve&lt;/code&gt; appears twice.
Below is the process-creation-related portion of the system calls made when &lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/java-external-process/ProcessRunner.java&quot;&gt;ProcessRunner.java&lt;/a&gt;, which runs &lt;code&gt;echo hello&lt;/code&gt;, is executed with the default settings. Paths have been shortened.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;$ strace -f -e trace=clone,clone3,vfork,execve -o trace.txt java ProcessRunner
$ grep -v CLONE_THREAD trace.txt | grep -E &apos;clone|vfork|execve&apos;
285583 clone3({flags=CLONE_VM|CLONE_VFORK|CLONE_CLEAR_SIGHAND, exit_signal=SIGCHLD, stack=..., stack_size=0x9000}, 88 &amp;lt;unfinished ...&amp;gt;
285603 execve(&quot;$JAVA_HOME/lib/jspawnhelper&quot;, [&quot;$JAVA_HOME/lib/jspawnhelper&quot;, &quot;25+36-LTS&quot;, &quot;10:11:13&quot;], ...) = 0
285603 execve(&quot;/usr/bin/echo&quot;, [&quot;echo&quot;, &quot;hello&quot;], ...) = 0&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;No &lt;code&gt;vfork()&lt;/code&gt; system call appeared in this run.
&lt;code&gt;posix_spawn()&lt;/code&gt; in glibc 2.39 created the child with the &lt;code&gt;clone3&lt;/code&gt; system call, passing the &lt;code&gt;CLONE_VM&lt;/code&gt; and &lt;code&gt;CLONE_VFORK&lt;/code&gt; flags.
Depending on the environment, the &lt;code&gt;clone()&lt;/code&gt; system call may be used instead. The &lt;a href=&quot;https://github.com/bminor/glibc/blob/glibc-2.39/sysdeps/unix/sysv/linux/spawni.c&quot;&gt;posix_spawn implementation&lt;/a&gt; in glibc 2.39 falls back to &lt;code&gt;clone()&lt;/code&gt; if the &lt;code&gt;clone3&lt;/code&gt; call fails with &lt;code&gt;ENOSYS&lt;/code&gt; or &lt;code&gt;EINVAL&lt;/code&gt;.
&lt;code&gt;CLONE_VM&lt;/code&gt; means the child shares the parent&amp;#8217;s address space as is, so the heap mappings and page tables are not copied.
&lt;code&gt;CLONE_VFORK&lt;/code&gt; blocks the parent&amp;#8217;s calling thread until the child &lt;code&gt;execve()`s or exits.
Thanks to `CLONE_VM&lt;/code&gt;, the creation cost is nearly independent of heap size.
The measurements later in this chapter confirm this. Running the same program with &lt;code&gt;-Djdk.lang.Process.launchMechanism=FORK&lt;/code&gt; invokes the &lt;code&gt;clone&lt;/code&gt; system call without the &lt;code&gt;CLONE_VM&lt;/code&gt; flag to duplicate the address space, and &lt;code&gt;execve`s `/usr/bin/echo&lt;/code&gt; directly without &lt;code&gt;jspawnhelper&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;According to the &lt;a href=&quot;https://man7.org/linux/man-pages/man3/posix_spawn.3.html&quot;&gt;posix_spawn(3)&lt;/a&gt; manual, since glibc 2.24 &lt;code&gt;posix_spawn()&lt;/code&gt; calls &lt;code&gt;clone()&lt;/code&gt; with the &lt;code&gt;CLONE_VM&lt;/code&gt; and &lt;code&gt;CLONE_VFORK&lt;/code&gt; flags.
It gives the child process a separate stack, blocks signals during creation, and resets the child&amp;#8217;s handlers, reducing the risks related to the parent&amp;#8217;s stack and signal handling.
The comment in the JDK 25 source also describes the glibc 2.24 and later approach as the best choice for these reasons, and notes that musl has used this &lt;code&gt;clone()&lt;/code&gt; approach as well.
The &lt;code&gt;vfork()&lt;/code&gt; function itself has not been removed from glibc.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The fact that &lt;code&gt;jspawnhelper&lt;/code&gt; is a separate executable can cause trouble when updating the JDK.
If you overwrite the files in the JDK path used by a running JVM, the JVM code loaded in memory and the helper version on disk diverge, and launching external commands can fail.
&lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-8325621&quot;&gt;JDK-8325621&lt;/a&gt; strengthened the helper&amp;#8217;s version check against the background of such mismatches caused by automatic updates.
When updating the JDK, it is better not to overwrite the directory a running JVM uses, but to install into a new directory and switch to the new path when restarting the JVM.
Helper launch errors can also have other causes, such as permission or installation problems, so check the error message and the installation state first.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-8357090&quot;&gt;CSR for JDK-8357090&lt;/a&gt; explains that &lt;code&gt;FORK&lt;/code&gt; is kept as an alternative so that unexpected problems with &lt;code&gt;posix_spawn()&lt;/code&gt; can be worked around.
If &lt;code&gt;jspawnhelper&lt;/code&gt;-related errors keep recurring and the cause is hard to pin down right away, you can work around them temporarily by adding &lt;code&gt;-Djdk.lang.Process.launchMechanism=FORK&lt;/code&gt; to the JVM startup options.
This choice accepts the memory burden under the overcommit policy and the launch latency covered in the next section, and it is not a setting that takes effect immediately in a running JVM.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;memory_cost_and_launch_latency_of_fork&quot;&gt;Memory Cost and Launch Latency of FORK&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you choose &lt;code&gt;FORK&lt;/code&gt;, you have to take into account the cost of duplicating the parent JVM&amp;#8217;s address space.
&lt;code&gt;fork()&lt;/code&gt; creates a child process by duplicating the parent&amp;#8217;s virtual address space as is; Linux defers the actual copying of pages with copy-on-write, but it does copy the page tables.
Depending on the kernel&amp;#8217;s memory overcommit policy, the kernel may compute in advance the memory the child might use and refuse to create it.
This check applies even if the child is immediately replaced by a small program through &lt;code&gt;execve()&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The default value 0 of the Linux kernel setting &lt;code&gt;vm.overcommit_memory&lt;/code&gt;, exposed through the &lt;code&gt;/proc/sys/vm/overcommit_memory&lt;/code&gt; file, is a mode that &quot;refuses only obvious overcommits&quot;.
The kernel 5.1 code factored free memory, the page cache, free swap, and so on into this decision. If the size &lt;code&gt;fork()&lt;/code&gt; required to duplicate the heap mappings exceeded this computed value, the call could be refused with &lt;code&gt;ENOMEM&lt;/code&gt;, the error code meaning out of memory.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The kernel has several rules to keep this decision from becoming too strict, and they have been reinforced as versions progressed. Two examples follow.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Even on a server that strictly enforces the commit limit with &lt;code&gt;vm.overcommit_memory=2&lt;/code&gt;, the commit check at &lt;code&gt;fork()&lt;/code&gt; time does not unconditionally add the size of the parent&amp;#8217;s entire address space. It counts only the size of the mappings the kernel includes in the commit total, such as private writable mappings.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;a href=&quot;https://lkml.iu.edu/hypermail/linux/kernel/1904.1/05420.html&quot;&gt;mm: fix false-positive OVERCOMMIT_GUESS failures&lt;/a&gt; patch, merged in kernel 5.2, simplified the mode 0 decision to &quot;does the requested size exceed the sum of total RAM and swap&quot;. This relaxed the condition that refused to duplicate large mappings merely because free memory was low.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Even so, &lt;code&gt;FORK&lt;/code&gt; does not always succeed. In mode 2, large anonymous private writable mappings such as the Java heap count toward the cost, so process creation fails if the remaining commit limit is exceeded, and even in mode 0 the actual allocation of kernel memory such as page tables can fail.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Even when creation is not refused, the time cost remains.
Although the overcommit decision was relaxed, the fact that &lt;code&gt;fork()&lt;/code&gt; copies the page tables has not changed.
The copying cost varies with factors such as the heap area actually touched and the page size.
&lt;code&gt;POSIX_SPAWN&lt;/code&gt;, by contrast, shares the address space through &lt;code&gt;CLONE_VM&lt;/code&gt;, so there is no such copying.
I measured, for each launch mechanism, the time taken to run the &lt;code&gt;true&lt;/code&gt; command 30 times with the heap preallocated.
The measurement code is &lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/java-external-process/SpawnBench.java&quot;&gt;SpawnBench.java&lt;/a&gt;; I set &lt;code&gt;-Xms&lt;/code&gt; and &lt;code&gt;-Xmx&lt;/code&gt; to the same value and pre-touched the heap with the &lt;code&gt;-XX:+AlwaysPreTouch&lt;/code&gt; option before measuring.
Each run averaged 30 iterations of &lt;code&gt;start().waitFor()&lt;/code&gt; after 5 warm-up iterations, so the figures include running the command and waiting for it to exit, not just the pure creation time.
Below are the values from two runs at the time of writing.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 20%;&quot;&gt;
&lt;col style=&quot;width: 40%;&quot;&gt;
&lt;col style=&quot;width: 40%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Heap size&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;POSIX_SPAWN (ms/run)&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;FORK (ms/run)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;256MB&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;1.5, 1.2&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;15.9, 20.2&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;2GB&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;1.6, 1.9&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;120.8, 140.7&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;8GB&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;2.0, 2.1&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;243.5, 231.2&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In this measurement, &lt;code&gt;POSIX_SPAWN&lt;/code&gt; stayed in the range of about 1 to 2 ms, while &lt;code&gt;FORK&lt;/code&gt; slowed down as the heap grew, differing by more than 100 times at 8GB.
This does not mean the cost is exactly proportional to heap size or that this ratio appears in every environment.
A re-measurement on 2026-09-06 confirmed the same trend, but the absolute times differed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In the &lt;code&gt;FORK&lt;/code&gt; mechanism, &lt;code&gt;dup_mmap()&lt;/code&gt; in Linux 6.17, which duplicates the address space, copies the mappings and page tables while holding the parent process&amp;#8217;s address space lock (mmap_lock) in write mode.
Meanwhile, other threads attempting memory mapping changes that need this lock have to wait.
For a web application that runs external commands synchronously while handling requests, this cost can add to the response time.
Unless you are temporarily working around a &lt;code&gt;jspawnhelper&lt;/code&gt; problem, there is currently no reason to accept these drawbacks and change the default to &lt;code&gt;FORK&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;removing_the_vfork_setting_when_upgrading_the_jvm&quot;&gt;Removing the VFORK Setting When Upgrading the JVM&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When upgrading the JVM to version 25 or later, it is best to remove any remaining &lt;code&gt;-Djdk.lang.Process.launchMechanism=VFORK&lt;/code&gt; setting.
The places where this setting may still linger are JVM launch scripts, &lt;code&gt;JAVA_OPTS&lt;/code&gt; in Dockerfiles, and application server startup options.
JDK 25 prints a warning, and with the change merged into the JDK 27 development build the setting is replaced by &lt;code&gt;FORK&lt;/code&gt;, which can increase launch latency with large heaps.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Because &lt;code&gt;vfork()&lt;/code&gt; was the default on Linux from JDK 7 through 12, some projects wrote this property explicitly to preserve the previous behavior when the default changed to &lt;code&gt;POSIX_SPAWN&lt;/code&gt; in JDK 13.
Even in environments where glibc was older than 2.24 at the time, there was no need to specify this value.
According to the comment in the &lt;a href=&quot;https://github.com/openjdk/jdk/blob/jdk-13%2B33/src/java.base/unix/native/libjava/ProcessImpl_md.c&quot;&gt;JDK 13 source&lt;/a&gt;, &lt;code&gt;posix_spawn()&lt;/code&gt; in glibc 2.4 through 2.23 chooses between &lt;code&gt;fork()&lt;/code&gt; and &lt;code&gt;vfork()&lt;/code&gt; depending on the call arguments, and the way the JDK calls it meets the conditions for using &lt;code&gt;vfork()&lt;/code&gt;.
From glibc 2.24 onward, it changed to the &lt;code&gt;clone()&lt;/code&gt;-based implementation with the separate stack and signal handling described earlier.
There is no reason to specify this value today, so remove the property and return to the default.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;JDK 7&amp;#8217;s choice of &lt;code&gt;vfork()&lt;/code&gt; as the default was controversial even at the time.
&lt;code&gt;vfork()&lt;/code&gt; is not a standard function.
The STANDARDS section of the &lt;a href=&quot;https://man7.org/linux/man-pages/man2/vfork.2.html&quot;&gt;vfork(2)&lt;/a&gt; manual says &quot;None&quot;, and the HISTORY section notes that this function, which appeared in 3.0BSD, was marked OBSOLETE in POSIX.1-2001 and had its specification removed in POSIX.1-2008.
4.4BSD made &lt;code&gt;vfork()&lt;/code&gt; simply identical to &lt;code&gt;fork()&lt;/code&gt;, and Linux also behaved the same as &lt;code&gt;fork()&lt;/code&gt; until around 2.2.0-pre6, becoming an independent system call from 2.2.0-pre9.
Still, what went away is not the kernel&amp;#8217;s &lt;code&gt;vfork()&lt;/code&gt; but the JDK&amp;#8217;s &lt;code&gt;VFORK&lt;/code&gt; mode.
It was not because it was dropped from the standard, nor because the kernel changed, but because, as we saw earlier, the work the JDK itself did between &lt;code&gt;vfork()&lt;/code&gt; and &lt;code&gt;execve()&lt;/code&gt; was unsafe.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Specifying this option in JDK 25 prints the following warning to standard error.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;text&quot;&gt;$ java -Djdk.lang.Process.launchMechanism=VFORK ProcessRunner
VFORK MODE DEPRECATED
The VFORK launch mechanism has been deprecated for being dangerous.
It will be removed in a future java version. Either remove the
jdk.lang.Process.launchMechanism property (preferred) or use FORK mode
instead (-Djdk.lang.Process.launchMechanism=FORK).&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The examples and measurements in this article are based on JDK 25.
When applying them to older JDKs, check the launch mechanism and the range of API support separately.
For example, OpenJDK 11u can specify &lt;code&gt;POSIX_SPAWN&lt;/code&gt; as an option from 11.0.4 on, but its default is &lt;code&gt;vfork()&lt;/code&gt;, and the Linux implementation in OpenJDK 8u does not support this option as of 2026-09-06.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Code that launches external processes must be designed with output handling, timeouts, and a termination policy together. If you do not need to process the output, use &lt;code&gt;Redirect.DISCARD&lt;/code&gt; or &lt;code&gt;inheritIO()&lt;/code&gt;; if you need stdout and stderr separately, read both streams concurrently. For commands that read stdin, close the stream after writing all the input so the command receives EOF. After a timeout, take care not only of the termination request but also of whether to kill forcibly and of resource cleanup.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;zt-exec and Commons Exec reduce this code. Compare their defaults for output and exit codes, how they signal a timeout, and their dependencies, and pick the one that fits your project. Neither library guarantees forcible termination with the default &lt;code&gt;destroy()&lt;/code&gt; alone, and for cleaning up descendants you can send a signal to the processes remaining in the same group or terminate by cgroup.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;With JDK 25 on Linux, keeping the default &lt;code&gt;POSIX_SPAWN&lt;/code&gt; is the safe choice.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;JDK documentation&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Process.html&quot;&gt;Process (Java SE 25)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ProcessBuilder.html&quot;&gt;ProcessBuilder (Java SE 25)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Runtime.html&quot;&gt;Runtime (Java SE 25)&lt;/a&gt;: why the &lt;code&gt;exec(String)&lt;/code&gt; overloads are deprecated&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/openjdk/jdk/blob/jdk-25%2B36/src/java.base/share/classes/java/lang/Runtime.java&quot;&gt;Runtime.java (JDK 25+36)&lt;/a&gt;: the implementation of &lt;code&gt;exec()&lt;/code&gt; delegating to &lt;code&gt;ProcessBuilder&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/openjdk/jdk/blob/jdk-25%2B36/src/java.base/unix/native/libjava/ProcessImpl_md.c&quot;&gt;ProcessImpl_md.c (JDK 25+36)&lt;/a&gt;: comments explaining the pros and cons of each launch mechanism and the &lt;code&gt;posix_spawn()&lt;/code&gt; implementations in glibc and musl&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Libraries&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/zeroturnaround/zt-exec&quot;&gt;ZT Process Executor (zt-exec)&lt;/a&gt;: usage examples in the README&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/zeroturnaround/zt-exec/blob/main/CHANGELOG.md&quot;&gt;ZT Process Executor CHANGELOG&lt;/a&gt;: the changes in 1.13.0 and its release date&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://commons.apache.org/proper/commons-exec/&quot;&gt;Apache Commons Exec&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://commons.apache.org/proper/commons-exec/changes.html&quot;&gt;Apache Commons Exec Changes&lt;/a&gt;: the builder API and the introduction of &lt;code&gt;Duration&lt;/code&gt; since 1.4.0&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;OpenJDK issues&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-6868160&quot;&gt;JDK-6868160 (process) Use vfork, not fork, on Linux to avoid swap exhaustion&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-8212828&quot;&gt;JDK-8212828 (process) Provide a way for Runtime.exec to use posix_spawn on linux&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-8213192&quot;&gt;JDK-8213192 (process) Change the Process launch mechanism default on Linux to be posix_spawn&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-8357180&quot;&gt;JDK-8357180 Deprecate VFORK launch mechanism from Process implementation (linux)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://bugs.openjdk.org/browse/JDK-8357090&quot;&gt;JDK-8357090 Remove VFORK launch mechanism from Process implementation (linux)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/openjdk/jdk/commit/ca3fe721ba23a1304089b71c1b58940f16a0d053&quot;&gt;JDK-8357089 implementation commit&lt;/a&gt;: the removal of &lt;code&gt;VFORK&lt;/code&gt; and its replacement with &lt;code&gt;FORK&lt;/code&gt; in the JDK 27 development build&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://openjdk.org/jeps/486&quot;&gt;JEP 486: Permanently Disable the Security Manager&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Linux manuals and kernel&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://man7.org/linux/man-pages/man7/pipe.7.html&quot;&gt;pipe(7)&lt;/a&gt;: pipe capacity&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://man7.org/linux/man-pages/man1/setsid.1.html&quot;&gt;setsid(1)&lt;/a&gt;: running without forking when not a group leader&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://man7.org/linux/man-pages/man2/setsid.2.html&quot;&gt;setsid(2)&lt;/a&gt;, &lt;a href=&quot;https://manpages.ubuntu.com/manpages/noble/man1/kill.1.html&quot;&gt;kill(1) (procps-ng)&lt;/a&gt;, &lt;a href=&quot;https://man7.org/linux/man-pages/man2/kill.2.html&quot;&gt;kill(2)&lt;/a&gt;: creating a new session, signaling a group with a negative PID, checking existence with signal 0&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://man7.org/linux/man-pages/man2/PR_SET_CHILD_SUBREAPER.2const.html&quot;&gt;PR_SET_CHILD_SUBREAPER(2const)&lt;/a&gt;: reparenting to a subreaper&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.kernel.org/admin-guide/cgroup-v2.html&quot;&gt;Control Group v2&lt;/a&gt;: permission to move processes and &lt;code&gt;cgroup.kill&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/latest/systemd-run.html&quot;&gt;systemd-run(1)&lt;/a&gt;, &lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/latest/systemd.kill.html&quot;&gt;systemd.kill(5)&lt;/a&gt;: running as a scope unit and &lt;code&gt;KillMode&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://man7.org/linux/man-pages/man2/vfork.2.html&quot;&gt;vfork(2)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://man7.org/linux/man-pages/man3/posix_spawn.3.html&quot;&gt;posix_spawn(3)&lt;/a&gt;: the implementation since glibc 2.24&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.kernel.org/doc/html/latest/mm/overcommit-accounting.html&quot;&gt;Overcommit Accounting&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/torvalds/linux/blob/v5.2/mm/util.c&quot;&gt;Linux 5.2 mm/util.c&lt;/a&gt;: the mode 0 decision in &lt;code&gt;__vm_enough_memory()&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/torvalds/linux/blob/v6.17/mm/mmap.c&quot;&gt;Linux 6.17 mm/mmap.c&lt;/a&gt;: the locking and page table duplication in &lt;code&gt;dup_mmap()&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://lkml.iu.edu/hypermail/linux/kernel/1904.1/05420.html&quot;&gt;mm: fix false-positive OVERCOMMIT_GUESS failures&lt;/a&gt;: the kernel 5.2 patch that changed the heuristic&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.infoworld.com/article/2157336/when-runtime-exec-won-t.html&quot;&gt;When Runtime.exec() won&amp;#8217;t&lt;/a&gt;: Michael Daconta&amp;#8217;s article from 2000&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://d2.naver.com/helloworld/1113548&quot;&gt;Running external processes in Java (NAVER D2, 2015, in Korean)&lt;/a&gt;: the memory problem with &lt;code&gt;fork()&lt;/code&gt; and the background to the adoption of &lt;code&gt;vfork()&lt;/code&gt; in JDK 7&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/benelog/blog/tree/main/examples/java-external-process&quot;&gt;Example code for this article&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
	</description>
    </item>
    <item>
      <title>IEEE 754 Floating-Point Errors and Alternatives in Java</title>
      <link>https://tech.benelog.net/floating-point-java.html</link>
      <pubDate>Sat, 3 Oct 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="false">floating-point-java.html</guid>
      	<description>
	&lt;div id=&quot;toc&quot; class=&quot;toc&quot;&gt;
&lt;div id=&quot;toctitle&quot;&gt;Table of Contents&lt;/div&gt;
&lt;ul class=&quot;sectlevel1&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#floating_point_errors_in_code&quot;&gt;1. Floating-Point Errors in Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#bit_layout_of_double_ieee_754_binary64_and_how_0_1_is_stored&quot;&gt;2. Bit Layout of double (IEEE 754 binary64) and How 0.1 Is Stored&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#failures_caused_by_binary_representation_errors&quot;&gt;3. Failures Caused by Binary Representation Errors&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#the_2011_neis_grading_error&quot;&gt;3.1. The 2011 NEIS Grading Error&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the_1991_gulf_war_patriot_missile_failure&quot;&gt;3.2. The 1991 Gulf War Patriot Missile Failure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#alternatives_in_java_for_avoiding_decimal_arithmetic_errors&quot;&gt;4. Alternatives in Java for Avoiding Decimal Arithmetic Errors&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#calculating_with_bigdecimal&quot;&gt;4.1. Calculating with BigDecimal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#calculating_with_integers&quot;&gt;4.2. Calculating with Integers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#choosing_between_bigdecimal_and_integer_calculation&quot;&gt;5. Choosing Between BigDecimal and Integer Calculation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#domain_specific_libraries_for_money_and_units&quot;&gt;6. Domain-Specific Libraries for Money and Units&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#java_money_jsr_354&quot;&gt;6.1. Java Money (JSR 354)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#other_domain_specific_libraries&quot;&gt;6.2. Other Domain-Specific Libraries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#references&quot;&gt;7. References&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div id=&quot;preamble&quot;&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Java&amp;#8217;s &lt;code&gt;double&lt;/code&gt; and &lt;code&gt;float&lt;/code&gt; types store values in binary according to the IEEE 754 floating-point standard.
In this format, the decimal number 0.1 cannot be represented exactly.
In binary, 0.1 is an infinitely repeating fraction, so fitting it into a finite number of bits requires rounding it to the nearest representable value.
This article explains why such errors occur and when they become dangerous, using real failures as examples, and then summarizes the alternatives available in Java.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;floating_point_errors_in_code&quot;&gt;1. Floating-Point Errors in Code&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;You can see floating-point errors quickly in jshell, the interactive tool included with the JDK since JDK 9.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Adding decimals&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;jshell&amp;gt; 0.1 + 0.2
$1 ==&amp;gt; 0.30000000000000004

jshell&amp;gt; 0.1 + 0.2 == 0.3
$2 ==&amp;gt; false

jshell&amp;gt; 1.03 - 0.42
$3 ==&amp;gt; 0.6100000000000001&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Adding 0.1 ten times does not give 1.0.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Adding 0.1 ten times&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;jshell&amp;gt; double sum = 0;
sum ==&amp;gt; 0.0

jshell&amp;gt; for (int i = 0; i &amp;lt; 10; i++) { sum += 0.1; }

jshell&amp;gt; sum
sum ==&amp;gt; 0.9999999999999999&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;code&gt;BigDecimal&lt;/code&gt; constructor reveals the value actually stored for the &lt;code&gt;double&lt;/code&gt; literal 0.1.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;The actual value of the double literal 0.1&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;jshell&amp;gt; new BigDecimal(0.1)
$1 ==&amp;gt; 0.1000000000000000055511151231257827021181583404541015625&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;I typed 0.1 at the jshell prompt, but the stored value is greater than 0.1 by about 5.55 × 10&lt;sup&gt;-18&lt;/sup&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This error is not specific to Java.
JavaScript&amp;#8217;s &lt;code&gt;number&lt;/code&gt; type also uses the IEEE 754 binary64 format, so entering the same expressions in the Chrome DevTools console gives the same results.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;img/floating-point-java/chrome-console-float.png&quot; alt=&quot;Floating-point errors shown in the Chrome DevTools console&quot; width=&quot;800&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The next section follows the bit layout of &lt;code&gt;double&lt;/code&gt; to show why these values are stored.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;bit_layout_of_double_ieee_754_binary64_and_how_0_1_is_stored&quot;&gt;2. Bit Layout of double (IEEE 754 binary64) and How 0.1 Is Stored&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;IEEE 754 is a standard that defines several floating-point formats.
Java uses two of them.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 20%;&quot;&gt;
&lt;col style=&quot;width: 20%;&quot;&gt;
&lt;col style=&quot;width: 40%;&quot;&gt;
&lt;col style=&quot;width: 20%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Format&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;1985 name&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Size&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Java type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;binary32&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;single&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;32 bits (sign 1 / exponent 8 / fraction 23)&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;float&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;binary64&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;double&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;64 bits (sign 1 / exponent 11 / fraction 52)&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;double&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The first version of the standard, IEEE 754-1985, called these two formats single and double.
The 2008 revision renamed them binary32 and binary64, and these names carry over to the current standard, IEEE 754-2019.
The standard also defines binary16, binary128, and the decimal formats decimal32, decimal64, and decimal128.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The binary formats of IEEE 754 convert a real number to binary scientific notation and then store it as bits.
In decimal scientific notation, you shift the decimal point so that the integer part is a single digit from 1 to 9, and express the number of places shifted as a power of 10.
For example, 0.001 is written as 1.0 × 10&lt;sup&gt;-3&lt;/sup&gt;.
Binary works the same way: you multiply or divide by 2 until the integer part is a single digit, that is, until the value is at least 1 and less than 2.
The only nonzero digit in binary is 1, so the normalized result always takes the form ±1.fraction × 2&lt;sup&gt;exponent&lt;/sup&gt;.
For example, binary 0.011 (decimal 0.375) is written as 1.1 × 2&lt;sup&gt;-2&lt;/sup&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;A &lt;code&gt;double&lt;/code&gt; (binary64) stores this normalized value in 64 bits according to the following rules.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The sign (s) is stored in the first bit. 0 means positive and 1 means negative.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The exponent (e) is stored in 11 bits as the actual exponent plus 1023. This lets exponents from -1022 to 1023 be stored as unsigned integers from 1 to 2046; reading the value subtracts 1023 again. Of the values 0 to 2047 that 11 bits can hold, the two at either end are reserved: 0 represents zero and numbers very close to zero, and 2047 represents infinity and NaN.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The fraction (f) stores only the part after the binary point of 1.fraction, in 52 bits. The integer part of a normalized number is always 1, so that 1 is omitted.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the part after the binary point exceeds 52 bits, it is rounded to the nearest value.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The figure below shows 0.1 stored according to these rules.
The 64 bits are divided into a 1-bit sign, an 11-bit exponent, and a 52-bit fraction.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;img/floating-point-java/ieee754-double.png&quot; alt=&quot;Bit layout of IEEE 754 double and how 0.1 is stored&quot; width=&quot;800&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Let&amp;#8217;s follow the conversion in the lower half of the figure.
Multiplying 0.1 by 2 repeatedly gives 0.2, 0.4, and 0.8, and on the fourth step it becomes 1.6, which is the first value at least 1 and less than 2.
Since multiplying by 2 four times gave 1.6, 0.1 = 1.6 ÷ 2&lt;sup&gt;4&lt;/sup&gt; = 1.6 × 2&lt;sup&gt;-4&lt;/sup&gt;.
The sign bit is therefore 0, and the exponent field is -4 + 1023 = 1019.
The fraction is the problem.
Converting 0.6 to binary gives 0.1001 1001 1001…, with 1001 repeating forever.
Fitting it into 52 bits requires rounding.
The bits following the first 52 are 1001…, so the discarded part is more than half of the last bit&amp;#8217;s weight.
The value is therefore rounded up, and the last four bits of the fraction change from 1001 to 1010.
As a result, what gets stored is not 0.1 but a number very slightly greater than 0.1.
The value 0.1000000000000000055511151231257827021181583404541015625 seen earlier with &lt;code&gt;new BigDecimal(0.1)&lt;/code&gt; is exactly this rounded value.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;You can see the stored bits in hexadecimal in jshell.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;The 64 bits that store 0.1&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;jshell&amp;gt; Long.toHexString(Double.doubleToLongBits(0.1))
$1 ==&amp;gt; &quot;3fb999999999999a&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;One hexadecimal digit is 4 bits.
The first three digits, 3fb, are the 12 bits of the 1-bit sign and the 11-bit exponent combined: the sign bit is 0 and the exponent field is 0x3fb, or 1019 in decimal.
The remaining thirteen digits are the 52-bit fraction.
The digit 9 (1001) repeats twelve times, and only the last digit is rounded up to a (1010).&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Not every decimal fraction has this error.
Fractions whose denominator is a power of 2, such as 0.5 (1/2), 0.25 (1/4), and 0.75 (3/4), are finite binary fractions, so they are stored exactly as long as they need no more than 53 significant bits.
In contrast, a decimal fraction whose reduced denominator contains a factor of 5, such as 0.1 = 1/10, becomes an infinitely repeating binary fraction and cannot avoid rounding error.
For more detail on the standard, see the &lt;a href=&quot;https://en.wikipedia.org/wiki/IEEE_754&quot;&gt;Wikipedia article on IEEE 754&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;failures_caused_by_binary_representation_errors&quot;&gt;3. Failures Caused by Binary Representation Errors&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In fields that deal with approximations, such as data from scientific experiments, these errors often fall within an acceptable range.
But where values must be exactly equal, such as money and grades, or where errors accumulate across calculations, even a tiny error becomes a critical bug.
Let&amp;#8217;s look at two cases, one in Korea and one abroad, where such errors led to real failures.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;the_2011_neis_grading_error&quot;&gt;3.1. The 2011 NEIS Grading Error&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In July 2011, a grade-processing error in NEIS, the next-generation National Education Information System of South Korea, changed the end-of-semester class ranks of about 29,000 students at 823 high schools nationwide.
It happened just before the early-admission season for universities, so the impact was large.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;NEIS computed a semester total by adding written exam scores and performance assessment scores.
The ranks of students with the same total were decided by a tie-breaking rule that each school chose from 45 options (&lt;a href=&quot;https://m.yeongnam.com/view.php?key=20110726.010061333590001&quot;&gt;Yeongnam Ilbo article, in Korean&lt;/a&gt;).
For example, at a school that gives priority to written exam scores, student A with 65 on the written exam and 25 on performance assessments ranks ahead of student B with 60 and 30, even though both have a total of 90.
Detecting ties is an important step in computing ranks; schools even had to choose a tie-breaking rule in advance.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;NEIS was programmed to display totals to 16 decimal places, and some values had a stray &apos;1&apos; appearing irregularly in those decimal places.
This is the same pattern as 1.03 - 0.42 producing 0.6100000000000001 at the beginning of this article.
For example, if two students&apos; totals are computed as 90 and 90.00000000000001, the values are not equal, so a direct comparison treats the two students as not tied and sorts the one with the error as having a higher score.
As a result, students who should have had the same total were not detected as tied, or the ranks among tied students were reversed (&lt;a href=&quot;https://use.go.kr/news/user/bbs/BD_selectBbs.do?q_bbsSn=1005&amp;amp;q_bbsDocNo=13244&quot;&gt;notice from the Ulsan Metropolitan Office of Education, in Korean&lt;/a&gt;).
The Ministry of Education, Science and Technology explained that the bug was caused by not correcting this calculation error (&lt;a href=&quot;https://www.khan.co.kr/article/201107222144165&quot;&gt;Kyunghyang Shinmun article, in Korean&lt;/a&gt;).&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;jshell shows that adding the same numbers in a different order can produce a different sum.
The following is not NEIS&amp;#8217;s actual formula, only an illustration of the principle.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Sums that depend on the order of addition&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;jshell&amp;gt; 0.1 + 0.2 + 0.3
$1 ==&amp;gt; 0.6000000000000001

jshell&amp;gt; 0.3 + 0.2 + 0.1
$2 ==&amp;gt; 0.6

jshell&amp;gt; (0.1 + 0.2 + 0.3) == (0.3 + 0.2 + 0.1)
$3 ==&amp;gt; false&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Both sums add the same three numbers and should be equal, but with &lt;code&gt;double&lt;/code&gt; they produce different values and are not detected as a tie.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;According to the Ministry&amp;#8217;s count, the ranks of 29,007 students at 823 high schools changed, and for 2,416 students at 350 of those schools, the rank grade changed as well (&lt;a href=&quot;https://www.jejunews.com/news/articleView.html?idxno=957280&quot;&gt;Jeju Ilbo article, in Korean&lt;/a&gt;).
Schools had to recompute grades with an error-correction program and reissue report cards (Kyunghyang Shinmun article).&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The results of an inspection the Ministry&amp;#8217;s special task force announced in September of the same year identify the type in which the error occurred.
According to the report, while the old NEIS programs were being redeveloped for the next-generation NEIS, the team failed to anticipate arithmetic errors with floating-point (Double) data that arise from the characteristics of the newly installed database (DB2), and this caused the grading errors (&lt;a href=&quot;https://www.etnews.com/201109030016&quot;&gt;Electronic Times article, in Korean&lt;/a&gt;).
In other words, grades were computed with the DB2 DOUBLE type.
The DOUBLE type in &lt;a href=&quot;https://www.ibm.com/docs/en/db2/11.5?topic=list-numbers&quot;&gt;Db2 for Linux, UNIX, and Windows&lt;/a&gt; is 64 bits with the same range as IEEE 754 binary64, so it has the same kind of error as Java&amp;#8217;s &lt;code&gt;double&lt;/code&gt;.
Some programs were missing the &apos;error correction code&apos; that was supposed to fix that error, and the ranks were reversed in those programs (&lt;a href=&quot;https://www.yna.co.kr/view/AKR20110902079600004&quot;&gt;Yonhap News article, in Korean&lt;/a&gt;).&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;At the time, network developer Lee Kyung-moon pointed out in a &lt;a href=&quot;https://www.dailysecu.com/news/articleView.html?idxno=319&quot;&gt;DailySecu article (in Korean)&lt;/a&gt; that the problem was using floating point where exact numeric representation was required.
He argued that rather than correcting the error, the design should have switched to integer processing or a type that supports arbitrary precision.
Integer minor units and &lt;code&gt;BigDecimal&lt;/code&gt;, introduced later in this article, are exactly those two options.
The incident shows that handling values with decimal arithmetic, such as grades, in floating point can break comparisons such as tie detection.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;the_1991_gulf_war_patriot_missile_failure&quot;&gt;3.2. The 1991 Gulf War Patriot Missile Failure&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The fact that 0.1 cannot be represented as a finite binary fraction has also cost lives.
On February 25, 1991, during the Gulf War, an Iraqi Scud missile struck a US Army barracks in Dhahran, Saudi Arabia, killing 28 American soldiers and injuring 99 (&lt;a href=&quot;https://www.dvidshub.net/news/525747/scud-alert-after-blast&quot;&gt;US Army article&lt;/a&gt;).
A Patriot missile battery was deployed at the base to intercept such attacks, but it did not even attempt an interception.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Chapter 1 of the Korean book &lt;em&gt;Software Errors in History&lt;/em&gt; (Acorn Publishing, 2014), titled &quot;28 Lives Taken by an Error of 0.000000095,&quot; explains the cause of the incident based on the US GAO&amp;#8217;s &lt;a href=&quot;https://www.gao.gov/products/imtec-92-26&quot;&gt;investigation report&lt;/a&gt;.
The Patriot system counted time in tenths of a second to predict where a target would appear, and converted the count to seconds by multiplying it by 0.1 stored in a 24-bit fixed-point register.
Because 0.1 is an infinitely repeating binary fraction, everything beyond 24 bits was truncated, and an error of about 0.000000095 seconds accumulated every tenth of a second.
After 20 hours of continuous operation, the computed time was 0.0687 seconds behind the actual time; after about 100 hours, as with the battery at the time of the incident, it was 0.3433 seconds behind.
By the GAO report&amp;#8217;s calculation, this timing error shifted the range gate where the system looked for the target by 687 meters.
The tracking radar searched an area that far from the missile&amp;#8217;s actual position.
The radar found no target there, so no interceptor was launched, and the Scud hit the barracks.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This system used fixed-point arithmetic, not floating point like &lt;code&gt;double&lt;/code&gt;.
But the principle is the same as in this article: 0.1 could not fit into a finite number of binary digits, and the error accumulated.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;alternatives_in_java_for_avoiding_decimal_arithmetic_errors&quot;&gt;4. Alternatives in Java for Avoiding Decimal Arithmetic Errors&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This section introduces two ways to avoid the errors of &lt;code&gt;double&lt;/code&gt; arithmetic: using &lt;code&gt;BigDecimal&lt;/code&gt;, and handling amounts as integers in the smallest unit.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;calculating_with_bigdecimal&quot;&gt;4.1. Calculating with BigDecimal&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The standard way to handle decimal fractions exactly in Java is &lt;code&gt;java.math.BigDecimal&lt;/code&gt;.
&lt;a href=&quot;https://dzone.com/articles/never-use-float-and-double-for-monetary-calculatio&quot;&gt;Why You Should Never Use Float and Double for Monetary Calculations&lt;/a&gt; also recommends &lt;code&gt;BigDecimal&lt;/code&gt; instead of floating-point types for monetary calculations.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Addition and subtraction with BigDecimal&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;jshell&amp;gt; new BigDecimal(&quot;0.1&quot;).add(new BigDecimal(&quot;0.2&quot;))
$1 ==&amp;gt; 0.3

jshell&amp;gt; new BigDecimal(&quot;1.03&quot;).subtract(new BigDecimal(&quot;0.42&quot;))
$2 ==&amp;gt; 0.61&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;There is one catch. If you pass a &lt;code&gt;double&lt;/code&gt; to the constructor, the rounding error introduced when the decimal fraction was stored in the &lt;code&gt;double&lt;/code&gt; carries over into the &lt;code&gt;BigDecimal&lt;/code&gt;.
As shown earlier, &lt;code&gt;new BigDecimal(0.1)&lt;/code&gt; holds the approximation stored in the &lt;code&gt;double&lt;/code&gt;, not 0.1.
To avoid this error, use the constructor that takes a string, or &lt;code&gt;BigDecimal.valueOf()&lt;/code&gt;.
&lt;code&gt;BigDecimal.valueOf(0.1)&lt;/code&gt; builds the value from the string &quot;0.1&quot; returned by &lt;code&gt;Double.toString(0.1)&lt;/code&gt;, so it is 0.1.
However, &lt;code&gt;valueOf()&lt;/code&gt; cannot remove an error that already exists in a value computed as a &lt;code&gt;double&lt;/code&gt;.
&lt;code&gt;BigDecimal.valueOf(0.1 + 0.2)&lt;/code&gt; is 0.30000000000000004.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Using &lt;code&gt;BigDecimal&lt;/code&gt; does not decide the rounding policy for you.
A division whose decimal expansion does not terminate, such as 1 divided by 3, throws an exception unless you specify the scale and the rounding mode.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Non-terminating division with BigDecimal&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;jshell&amp;gt; new BigDecimal(&quot;1&quot;).divide(new BigDecimal(&quot;3&quot;))
|  Exception java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

jshell&amp;gt; new BigDecimal(&quot;1&quot;).divide(new BigDecimal(&quot;3&quot;), 10, RoundingMode.HALF_UP)
$1 ==&amp;gt; 0.3333333333&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;code&gt;java.math.RoundingMode&lt;/code&gt;, used here, is an enum for choosing how to round: whether to round 0.5 up (&lt;code&gt;HALF_UP&lt;/code&gt;), to the nearest even number (&lt;code&gt;HALF_EVEN&lt;/code&gt;), or to always discard the fraction (&lt;code&gt;DOWN&lt;/code&gt;).
For example, rounding 2.5 and 3.5 to integers gives 3 and 4 with &lt;code&gt;HALF_UP&lt;/code&gt;, 2 and 4 with &lt;code&gt;HALF_EVEN&lt;/code&gt;, and 2 and 3 with &lt;code&gt;DOWN&lt;/code&gt;.
What &lt;code&gt;BigDecimal&lt;/code&gt; eliminates is the error that comes from binary representation.
How many decimal places to keep and how to round them is a rule the team must decide and follow according to the needs of the domain.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Joshua Bloch makes the same recommendation in Item 60 of &lt;em&gt;Effective Java&lt;/em&gt;, 3rd edition, &quot;Avoid float and double if exact answers are required.&quot;
The item suggests &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;long&lt;/code&gt; as well as &lt;code&gt;BigDecimal&lt;/code&gt;; the section on choosing between them below covers which to pick.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;calculating_with_integers&quot;&gt;4.2. Calculating with Integers&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Handling amounts as integers in the smallest unit, such as won or cents, is also common.
The API of the payment service Stripe is an example.
The &lt;code&gt;amount&lt;/code&gt; attribute of the &lt;a href=&quot;https://docs.stripe.com/api/charges/object&quot;&gt;Charge object&lt;/a&gt; is an integer, described as follows.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;quoteblock&quot;&gt;
&lt;blockquote&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;A positive integer representing how much to charge in the smallest currency unit (e.g., 100 cents to charge $1.00 or 100 to charge ¥100, a zero-decimal currency).&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;attribution&quot;&gt;
&amp;#8212; Stripe API Reference&lt;br&gt;
&lt;cite&gt;The Charge object&lt;/cite&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This is especially practical for the Korean won, where amounts below 1 won have little value, as long as it does not conflict with business rules.
For the same reason, existing systems in Korea often define won amounts as integer columns in the database.
So even if the application calculates with &lt;code&gt;BigDecimal&lt;/code&gt;, there is at least one conversion to an integer right before the value is stored.
An example from the Woowa Brothers tech blog post &lt;a href=&quot;https://techblog.woowahan.com/2560/&quot;&gt;Writing Test Code with Spock (in Korean)&lt;/a&gt; shows this approach.
It uses &lt;code&gt;BigDecimal&lt;/code&gt; for rounding, but the amount parameter &lt;code&gt;amount&lt;/code&gt; and the return type are &lt;code&gt;long&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;calculate method from the Spock example&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;public static long calculate(long amount, float rate, RoundingMode roundingMode) {
    return BigDecimal.valueOf(amount * rate * 0.01)
            .setScale(0, roundingMode).longValue();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The amount itself stays an integer, and right after the rate calculation, which produces a fraction, the method returns to an integer through a &lt;code&gt;BigDecimal&lt;/code&gt; with an explicit rounding mode.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;However, once a floating-point type is mixed in, as in &lt;code&gt;amount * rate * 0.01&lt;/code&gt;, the calculation becomes floating-point arithmetic.
The &lt;code&gt;long&lt;/code&gt; value is converted to &lt;code&gt;float&lt;/code&gt; and the multiplication is done in &lt;code&gt;float&lt;/code&gt;, losing precision, so errors can occur even though the amount parameter and the return type are integers.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Limits of the calculate method&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;jshell&amp;gt; calculate(16_777_217L, 100f, RoundingMode.HALF_UP)
$1 ==&amp;gt; 16777216

jshell&amp;gt; calculate(671_089L, 50f, RoundingMode.HALF_UP)
$2 ==&amp;gt; 335544&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The first call multiplies by 100%, so it should return 16,777,217 unchanged, but the result is 1 won short.
A &lt;code&gt;float&lt;/code&gt; has 24 bits of precision (a 23-bit fraction plus the implicit leading bit), so it cannot represent every integer above 2&lt;sup&gt;24&lt;/sup&gt; (16,777,216); &lt;code&gt;16_777_217L&lt;/code&gt; becomes 16,777,216 the moment it is converted to &lt;code&gt;float&lt;/code&gt;.
The second call should return 335,545, which is 50% of 671,089 won (335,544.5 won) rounded, but it is 1 won short.
The amount is less than 2&lt;sup&gt;24&lt;/sup&gt;, but the product with the rate, 33,554,450, exceeds 2&lt;sup&gt;24&lt;/sup&gt; and is stored in a &lt;code&gt;float&lt;/code&gt; as 33,554,448.
Even if the inputs stay integers, a single floating-point step in the middle of the calculation brings the error back.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you modify the quoted example to move the rate into &lt;code&gt;BigDecimal&lt;/code&gt; as well, the result is 16,777,217 as expected.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Version with the multiplication moved into BigDecimal&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;public static long calculate(long amount, BigDecimal percent, RoundingMode roundingMode) {
    return BigDecimal.valueOf(amount)
            .multiply(percent)
            .movePointLeft(2)
            .setScale(0, roundingMode)
            .longValueExact();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Result of the fixed calculate method&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;jshell&amp;gt; calculate(16_777_217L, new BigDecimal(&quot;100&quot;), RoundingMode.HALF_UP)
$1 ==&amp;gt; 16777217&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;code&gt;longValueExact()&lt;/code&gt; call on the last line throws an &lt;code&gt;ArithmeticException&lt;/code&gt; if the result exceeds the range of &lt;code&gt;long&lt;/code&gt;.
In that case, &lt;code&gt;longValue()&lt;/code&gt; silently returns only the low-order 64 bits, so a wrong amount could be stored.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Even with integer minor units, eliminating errors requires keeping every intermediate value as an integer or a &lt;code&gt;BigDecimal&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;choosing_between_bigdecimal_and_integer_calculation&quot;&gt;5. Choosing Between BigDecimal and Integer Calculation&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;For a module where calculation precision matters, I recommend at least keeping &lt;code&gt;double&lt;/code&gt; and &lt;code&gt;float&lt;/code&gt; out of method parameters and return types.
The two alternatives are &lt;code&gt;BigDecimal&lt;/code&gt; and integer minor units (&lt;code&gt;long&lt;/code&gt;), both covered above.
&lt;code&gt;long&lt;/code&gt; is better for performance and concise code.
&lt;code&gt;BigDecimal&lt;/code&gt; is better at preventing developer mistakes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Item 60 of &lt;em&gt;Effective Java&lt;/em&gt;, mentioned earlier, frames the choice the same way.
Its conclusion is to use &lt;code&gt;BigDecimal&lt;/code&gt; if you want the system to keep track of the decimal point and want control over rounding, accepting that it is less convenient and slower than primitive types.
It also offers the alternative of using &lt;code&gt;int&lt;/code&gt; or &lt;code&gt;long&lt;/code&gt; if performance matters and you can keep track of the decimal point yourself, which is the integer approach from the previous section.
It adds a rule of thumb based on the number of digits: use &lt;code&gt;int&lt;/code&gt; if the values do not exceed nine decimal digits, &lt;code&gt;long&lt;/code&gt; if they do not exceed eighteen, and &lt;code&gt;BigDecimal&lt;/code&gt; beyond that.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Peter Lawrey of Chronicle Software lists the drawbacks of &lt;code&gt;BigDecimal&lt;/code&gt; in &lt;a href=&quot;https://blog.vanillajava.blog/2014/07/if-bigdecimal-is-answer-it-must-have.html&quot;&gt;If BigDecimal is the answer, it must have been a strange question&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The syntax is unnatural. You chain method calls instead of using operators, which makes formulas harder to read.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It uses more memory than &lt;code&gt;double&lt;/code&gt;. It keeps creating objects, which produces a lot of garbage.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It is much slower for most operations.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The post also includes a JMH benchmark measuring the speed difference.
The benchmark repeatedly computes the average of two values, rounded to six decimal places, over an array of 1,024 elements.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;caption class=&quot;title&quot;&gt;Table 1. Lawrey&amp;#8217;s JMH benchmark results&lt;/caption&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 50%;&quot;&gt;
&lt;col style=&quot;width: 25%;&quot;&gt;
&lt;col style=&quot;width: 25%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Benchmark&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Throughput (ops/s)&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Error&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;doubleMidPrice&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;123,208.083&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;±2,109.738&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;bigDecimalMidPrice&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;23,638.568&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;±590.094&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;code&gt;double&lt;/code&gt; implementation has about 5.2 times the throughput of the &lt;code&gt;BigDecimal&lt;/code&gt; one.
These numbers were measured in 2014 and may differ on current JVMs.
The comparison is against &lt;code&gt;double&lt;/code&gt;, not directly against &lt;code&gt;long&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Lawrey&amp;#8217;s conclusion is to use &lt;code&gt;BigDecimal&lt;/code&gt; if you do not know how to handle rounding with &lt;code&gt;double&lt;/code&gt; or if your project standards require it, but, if you have a choice, not to assume that &lt;code&gt;BigDecimal&lt;/code&gt; is automatically the right answer.
He cites his own experience: when he is brought in to tune the performance of a financial application, he eventually ends up removing &lt;code&gt;BigDecimal&lt;/code&gt;.
&lt;code&gt;BigDecimal&lt;/code&gt; is not the biggest source of latency at first, but as other bottlenecks get fixed, it eventually becomes the slowest part.
In the comments, he adds that trading and financial systems existed before &lt;code&gt;BigDecimal&lt;/code&gt;, and that many were built in C++ without a decimal type.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Integer minor units have the advantage of using few system resources.
A &lt;code&gt;long&lt;/code&gt; value requires no object creation, and its arithmetic is plain CPU integer arithmetic.
Memory size can be measured with &lt;a href=&quot;https://github.com/openjdk/jol&quot;&gt;JOL (Java Object Layout)&lt;/a&gt;.
With the default settings of the 64-bit JDK 25 HotSpot VM, a &lt;code&gt;new BigDecimal(&quot;1.03&quot;)&lt;/code&gt; object is 40 bytes.
When the value exceeds the range of &lt;code&gt;long&lt;/code&gt;, a &lt;code&gt;BigInteger&lt;/code&gt; and an &lt;code&gt;int&lt;/code&gt; array are added internally, so the 22-digit &lt;code&gt;new BigDecimal(&quot;12345678901234567890.12&quot;)&lt;/code&gt; takes 112 bytes in total.
A &lt;code&gt;long&lt;/code&gt; value is 8 bytes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;On the other hand, the &lt;code&gt;long&lt;/code&gt; approach requires developers to pay more attention to correctness when a calculation involves division or mixes in &lt;code&gt;double&lt;/code&gt; or &lt;code&gt;float&lt;/code&gt;.
&lt;code&gt;BigDecimal&lt;/code&gt; also requires a rounding rule, but with &lt;code&gt;long&lt;/code&gt;, an error caused by a missing rule is harder to notice.
When the rounding mode is missing from a division that does not terminate, &lt;code&gt;BigDecimal&lt;/code&gt; throws an exception as shown earlier, but &lt;code&gt;long&lt;/code&gt; division silently discards the fraction. For example, &lt;code&gt;10 / 3&lt;/code&gt; is 3.
And because &lt;code&gt;long&lt;/code&gt; uses the basic arithmetic operators, the compiler does not stop floating point from creeping in, as with &lt;code&gt;amount * rate * 0.01&lt;/code&gt; in the &lt;code&gt;calculate()&lt;/code&gt; example.
The whole system must also follow the same rule about what the smallest unit is, a burden that &lt;code&gt;BigDecimal&lt;/code&gt; does not have.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The following table summarizes the comparison.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all stretch&quot;&gt;
&lt;caption class=&quot;title&quot;&gt;Table 2. Comparison of BigDecimal and integer calculation&lt;/caption&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 16.6666%;&quot;&gt;
&lt;col style=&quot;width: 16.6666%;&quot;&gt;
&lt;col style=&quot;width: 33.3333%;&quot;&gt;
&lt;col style=&quot;width: 33.3335%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-center valign-top&quot;&gt;Type&lt;/th&gt;
&lt;th class=&quot;tableblock halign-center valign-top&quot;&gt;Representation&lt;/th&gt;
&lt;th class=&quot;tableblock halign-center valign-top&quot;&gt;Strengths&lt;/th&gt;
&lt;th class=&quot;tableblock halign-center valign-top&quot;&gt;Trade-offs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;BigDecimal&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Arbitrary-precision integer and a scale&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div class=&quot;content&quot;&gt;&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Represents decimal numbers exactly, with as many digits as needed&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;No need to decide separately how many places to scale to an integer&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div class=&quot;content&quot;&gt;&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Higher memory use (40 bytes or more per object in the measurement above)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Slower arithmetic&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Arithmetic through chained method calls instead of operators&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;long&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Integer converted to the smallest unit&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div class=&quot;content&quot;&gt;&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Low memory use with no object creation&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fast, using CPU integer arithmetic&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div class=&quot;content&quot;&gt;&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Errors occur if &lt;code&gt;double&lt;/code&gt; or &lt;code&gt;float&lt;/code&gt; slips into intermediate calculations&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The whole system must follow the smallest-unit rule&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Once you decide which type to use, write it down as a simple rule that everyone on the team can understand.
For example: &quot;Handle amounts as &lt;code&gt;long&lt;/code&gt; (in won) in every layer, compute only ratios with &lt;code&gt;BigDecimal&lt;/code&gt;, and truncate with &lt;code&gt;RoundingMode.DOWN&lt;/code&gt;.&quot;
The developers responsible for a system keep changing.
You cannot assume that everyone understands the principles behind the errors discussed in this article.
A clear and simple rule makes it more likely that newly assigned developers follow the same approach.
This keeps decimal arithmetic consistent across all modules and reduces the chance that code with critical errors gets in.
So I suggest also judging a candidate rule by whether you can write it down concisely and clearly.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;After setting a rule, you also need to fix existing code that violates it to keep things consistent from then on.
But code that already works and handles money directly is scary to change.
That is why an approach, once established, often stays unchanged.
Even if only to prepare for changing existing code someday, separate logic that handles sensitive numbers such as money into its own method, like &lt;code&gt;calculate()&lt;/code&gt; above.
Then you can test it with only inputs and expected results, without a database or external systems.
A structure that is easy to test catches not only decimal errors but also logic errors in newly added code early.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;domain_specific_libraries_for_money_and_units&quot;&gt;6. Domain-Specific Libraries for Money and Units&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The two approaches above are choices about how to hold a number.
On top of them, there are domain-specific libraries that wrap values with units, such as money, time, and physical quantities, in dedicated types.
Money libraries hold the number internally as either a &lt;code&gt;BigDecimal&lt;/code&gt; or integer minor units.
Java Money is a representative example.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;java_money_jsr_354&quot;&gt;6.1. Java Money (JSR 354)&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://javamoney.github.io/&quot;&gt;Java Money&lt;/a&gt; is an API for handling monetary amounts together with currencies, standardized as JSR 354.
It represents an amount and its currency together with the &lt;code&gt;MonetaryAmount&lt;/code&gt; interface.
The &lt;a href=&quot;https://jcp.org/en/jsr/detail?id=354&quot;&gt;JCP proposal&lt;/a&gt; considered including it in Java SE 9, but it was never added to the JDK.
To use it, you add the API (&lt;code&gt;javax.money&lt;/code&gt;) and its reference implementation, &lt;a href=&quot;https://github.com/JavaMoney/jsr354-ri&quot;&gt;Moneta&lt;/a&gt;, as separate dependencies.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;build.gradle&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;groovy&quot;&gt;implementation &apos;org.javamoney:moneta:1.4.5&apos;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;code&gt;Money&lt;/code&gt; implementation calculates with &lt;code&gt;BigDecimal&lt;/code&gt; internally, so 1.03 - 0.42, which gave 0.6100000000000001 with &lt;code&gt;double&lt;/code&gt; at the beginning of this article, comes out as exactly 0.61.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Subtraction with Money&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;MonetaryAmount price = Money.of(new BigDecimal(&quot;1.03&quot;), &quot;USD&quot;);
MonetaryAmount result = price.subtract(Money.of(new BigDecimal(&quot;0.42&quot;), &quot;USD&quot;));
System.out.println(result); // USD 0.61&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Arithmetic between amounts in different currencies throws &lt;code&gt;javax.money.MonetaryException&lt;/code&gt;.
The API stops the mistake of mixing amounts in different currencies with an exception at run time.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Adding amounts in different currencies&lt;/div&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;price.add(Money.of(100, &quot;KRW&quot;)); // MonetaryException: Currency mismatch: USD/KRW&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Java Money does not replace the two approaches above; it sits on top of them.
Two of the main &lt;code&gt;MonetaryAmount&lt;/code&gt; implementations in Moneta map directly to the two numeric representations discussed in this article.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Money&lt;/code&gt;: Stores the amount as a &lt;code&gt;BigDecimal&lt;/code&gt;, and so carries the costs of &lt;code&gt;BigDecimal&lt;/code&gt; summarized above.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/JavaMoney/jsr354-ri/blob/master/moneta-core/src/main/java/org/javamoney/moneta/FastMoney.java&quot;&gt;FastMoney&lt;/a&gt;: Stores the amount in a single &lt;code&gt;long&lt;/code&gt; in minor units. The scale is fixed at five decimal places, so the largest representable amount is about 92 trillion (&lt;code&gt;Long.MAX_VALUE&lt;/code&gt; / 10&lt;sup&gt;5&lt;/sup&gt;). In exchange for this limit it is fast; its Javadoc says it is 10 to 15 times faster than &lt;code&gt;Money&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Because they share an interface, you can swap &lt;code&gt;Money&lt;/code&gt; for &lt;code&gt;FastMoney&lt;/code&gt; if speed becomes a problem.
Even with a domain type, you still need to decide on the numeric representation, and the trade-offs from the previous section reappear as the choice of implementation.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In my view, whether to adopt Java Money depends on whether you handle multiple currencies.
With a single currency, plain &lt;code&gt;BigDecimal&lt;/code&gt; or &lt;code&gt;long&lt;/code&gt; values are enough; in a system that mixes currencies, having the API catch currency mismatches is worth the extra dependency.
For more usage examples, see &lt;a href=&quot;https://www.baeldung.com/java-money-and-currency&quot;&gt;Java Money and the Currency API on Baeldung&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;other_domain_specific_libraries&quot;&gt;6.2. Other Domain-Specific Libraries&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Java Money is not the only library that wraps numeric representations in domain types.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.joda.org/joda-money/&quot;&gt;Joda-Money&lt;/a&gt;: A money library that predates Java Money. The &lt;code&gt;Money&lt;/code&gt; class stores the amount as a &lt;code&gt;BigDecimal&lt;/code&gt; with the scale fixed to the currency&amp;#8217;s default number of decimal places (2 for the dollar, 0 for the yen), while the &lt;code&gt;BigMoney&lt;/code&gt; class allows any scale.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://unitsofmeasurement.github.io/&quot;&gt;Units of Measurement API (JSR 385)&lt;/a&gt;: Represents physical quantities such as length and mass, together with their units, as types. The API package is &lt;code&gt;javax.measure&lt;/code&gt;, and the reference implementation is &lt;a href=&quot;https://unitsofmeasurement.github.io/indriya/&quot;&gt;Indriya&lt;/a&gt;. Just as a currency mismatch throws an exception for money, it catches calculations with mismatched kinds of quantities, and when the generic types are fixed, it catches them at compile time. For example, code that adds a &lt;code&gt;Quantity&amp;lt;Mass&amp;gt;&lt;/code&gt; to a &lt;code&gt;Quantity&amp;lt;Length&amp;gt;&lt;/code&gt; does not compile because the generic types differ. Preventing unit errors is a separate matter from calculating values exactly, however. Values are accepted as &lt;code&gt;Number&lt;/code&gt;, so a quantity created from a &lt;code&gt;double&lt;/code&gt; keeps its floating-point error.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/Duration.html&quot;&gt;java.time.Duration&lt;/a&gt;: An example built into the JDK rather than an extra dependency. It stores a length of time in two fields, seconds (&lt;code&gt;long&lt;/code&gt;) and nanoseconds (&lt;code&gt;int&lt;/code&gt;). It applies the same technique as integer minor units, in that it does not hold fractional seconds in a single &lt;code&gt;double&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Even if none of these libraries fits, a type you build within your own system can play the same role.
In that case too, decide whether to hold the number as a &lt;code&gt;BigDecimal&lt;/code&gt; or an integer by the criteria described in this article.
And make sure &lt;code&gt;float&lt;/code&gt; or &lt;code&gt;double&lt;/code&gt; cannot slip into intermediate calculations.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;references&quot;&gt;7. References&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/IEEE_754&quot;&gt;IEEE 754 - Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Joshua Bloch, &lt;a href=&quot;https://www.informit.com/store/effective-java-9780134685991&quot;&gt;&lt;strong&gt;Effective Java&lt;/strong&gt;&lt;/a&gt;, 3rd edition, Addison-Wesley, 2018, Item 60&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://dzone.com/articles/never-use-float-and-double-for-monetary-calculatio&quot;&gt;Why You Should Never Use Float and Double for Monetary Calculations&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/bigdecimal-class-java/&quot;&gt;BigDecimal Class in Java - GeeksforGeeks&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.vanillajava.blog/2014/07/if-bigdecimal-is-answer-it-must-have.html&quot;&gt;If BigDecimal is the answer, it must have been a strange question - Peter Lawrey&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://techblog.woowahan.com/2560/&quot;&gt;Writing Test Code with Spock - Woowa Brothers Tech Blog (in Korean)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.stripe.com/api/charges/object&quot;&gt;The Charge object - Stripe API Reference&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/openjdk/jol&quot;&gt;JOL (Java Object Layout)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h3 id=&quot;failure_cases&quot; class=&quot;discrete&quot;&gt;Failure Cases&lt;/h3&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;NEIS grading error (all in Korean)&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.khan.co.kr/article/201107222144165&quot;&gt;How Did the NEIS Grading Error Happen? - Kyunghyang Shinmun (2011-07-22)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://m.yeongnam.com/view.php?key=20110726.010061333590001&quot;&gt;NEIS Program Failed to Handle &apos;Garbage Values&apos; - Yeongnam Ilbo (2011-07-26)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.dailysecu.com/news/articleView.html?idxno=319&quot;&gt;What Went Wrong with NEIS, the Next-Generation Headache - DailySecu (2011-07-26)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.yna.co.kr/view/AKR20110902079600004&quot;&gt;The NEIS Error Was a Foreseeable Accident: No Design Documents or Tests - Yonhap News (2011-09-02)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.etnews.com/201109030016&quot;&gt;Poor Development of the NEIS Education Administration System; Lawsuit Against Samsung SDS Considered - Electronic Times (2011-09-03)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Patriot missile failure&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Kim Jong-ha, &lt;a href=&quot;https://acornpub.co.kr/product/%EC%97%AD%EC%82%AC-%EC%86%8D%EC%9D%98-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EC%98%A4%EB%A5%98/4533/&quot;&gt;&lt;em&gt;Software Errors in History&lt;/em&gt;&lt;/a&gt; (in Korean), Acorn Publishing, 2014, pp. 25-35&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.gao.gov/products/imtec-92-26&quot;&gt;Patriot Missile Defense: Software Problem Led to System Failure at Dhahran, Saudi Arabia - GAO (1992-02)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.dvidshub.net/news/525747/scud-alert-after-blast&quot;&gt;Scud Alert: After the Blast - DVIDS&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h3 id=&quot;domain_specific_libraries&quot; class=&quot;discrete&quot;&gt;Domain-Specific Libraries&lt;/h3&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://javamoney.github.io/&quot;&gt;Java Money&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.baeldung.com/java-money-and-currency&quot;&gt;Java Money and the Currency API - Baeldung&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.joda.org/joda-money/&quot;&gt;Joda-Money&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://unitsofmeasurement.github.io/&quot;&gt;Units of Measurement API (JSR 385)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
	</description>
    </item>

  </channel> 
</rss>
