[{"data":1,"prerenderedAt":132},["ShallowReactive",2],{"content:\u002Fen\u002Ffiddling\u002Fthis-in-javac-string-concat":3,"surround:\u002Fen\u002Ffiddling\u002Fthis-in-javac-string-concat":121},{"id":4,"title":5,"authorship":6,"body":7,"categories":92,"date":94,"description":95,"draft":96,"extension":97,"image":98,"meta":99,"navigation":101,"path":102,"permalink":103,"published":103,"readingTime":104,"recommend":103,"references":103,"seo":109,"seoDescription":110,"seoTitle":103,"sitemap":111,"stem":112,"tags":113,"type":119,"__hash__":120},"content_en\u002Fposts\u002Ffiddling\u002Fthis-in-javac-string-concat.md","How Java’s this Keyword Can Prevent Compile-Time Constant Propagation","human-only",{"type":8,"value":9,"toc":89},"minimark",[10,14,25,39,47,50,56,59,65,80,83,86],[11,12,13],"p",{},"The title is a little long, but this really is an interesting problem. Consider this code:",[15,16,22],"pre",{"className":17,"code":19,"language":20,"meta":21},[18],"language-java","public class Test {\n    final static String s = \"a\";\n\n    public void test() {\n        String cmp = \"ab\";\n        String ab1 = s + \"b\";\n        String ab2 = this.s + \"b\";\n        System.out.println(ab1 == cmp);\n        System.out.println(ab2 == cmp);\n    }\n\n    public static void main(String[] args) {\n        new Test().test();\n    }\n}\n","java","",[23,24,19],"code",{"__ignoreMap":21},[11,26,27,28,31,32,35,36,38],{},"First, guess the output. The ",[23,29,30],{"code":30},"s"," on line 7 and ",[23,33,34],{"code":34},"this.s"," on line 8 refer to the same variable, the static final variable ",[23,37,30],{"code":30},". Even without knowing the answer, we would expect the two outputs to match. But the actual output is:",[15,40,45],{"className":41,"code":43,"language":44,"meta":21},[42],"language-shell","true\nfalse\n","shell",[23,46,43],{"__ignoreMap":21},[11,48,49],{},"Let's disassemble the generated bytecode with javap. Here is the test() portion:",[15,51,54],{"className":52,"code":53,"language":44,"meta":21},[42],"  public void test();\n    descriptor: ()V\n    flags: (0x0001) ACC_PUBLIC\n    Code:\n      stack=3, locals=4, args_size=1\n         0: ldc           #7                  \u002F\u002F String ab\n         2: astore_1\n         3: ldc           #7                  \u002F\u002F String ab\n         5: astore_2\n         6: aload_0\n         7: pop\n         8: ldc           #11                 \u002F\u002F String a\n        10: invokedynamic #13,  0             \u002F\u002F InvokeDynamic #0:makeConcatWithConstants:(Ljava\u002Flang\u002FString;)Ljava\u002Flang\u002FString;\n        15: astore_3\n        16: getstatic     #17                 \u002F\u002F Field java\u002Flang\u002FSystem.out:Ljava\u002Fio\u002FPrintStream;\n        19: aload_2\n        20: aload_1\n        21: if_acmpne     28\n        24: iconst_1\n        25: goto          29\n        28: iconst_0\n        29: invokevirtual #23                 \u002F\u002F Method java\u002Fio\u002FPrintStream.println:(Z)V\n        32: getstatic     #17                 \u002F\u002F Field java\u002Flang\u002FSystem.out:Ljava\u002Fio\u002FPrintStream;\n        35: aload_3\n        36: aload_1\n        37: if_acmpne     44\n        40: iconst_1\n        41: goto          45\n        44: iconst_0\n        45: invokevirtual #23                 \u002F\u002F Method java\u002Fio\u002FPrintStream.println:(Z)V\n        48: return\n",[23,55,53],{"__ignoreMap":21},[11,57,58],{},"Entry #7 in the constant pool is:",[15,60,63],{"className":61,"code":62,"language":44,"meta":21},[42],"   #7 = String             #8             \u002F\u002F ab\n   #8 = Utf8               ab\n",[23,64,62],{"__ignoreMap":21},[11,66,67,68,71,72,75,76,79],{},"The first true should be familiar. When compiling source into class bytecode, the compiler replaces final constants used in the current class's methods with literals. Thus ",[23,69,70],{"code":70},"String ab1 = s + \"b\"","; on Java source line 6 becomes ",[23,73,74],{"code":74},"String ab1 = \"a\" + \"b\"","; and, since this concatenates two literals, the compiler performs the concatenation itself. The statement ultimately becomes equivalent to ",[23,77,78],{"code":78},"String ab1 = \"ab\";",". Both cmp and ab1 therefore point to the string \"ab\" in the constant pool, so cmp == ab1. In the disassembled bytecode, lines 0 and 3 are identical: both ldc (Load Constant) instructions take #7 as their argument.",[11,81,82],{},"Bytecode lines 8–15 prepare ab2. They perform a dynamic method invocation of makeConcatWithConstants, a Java bootstrap method that handles String concatenation with “+”. This method creates a new string on the heap, which is why ab2 != cmp.",[11,84,85],{},"BTW, makeConcatWithConstants was introduced in JDK 9 to handle string “+” operations. Before JDK 8, javac had always used StringBuilder for this.",[11,87,88],{},"What causes the difference? Clearly, it is the this keyword. During compilation, Java implicitly adds a reference to the current instance, this, to every instance method. In bytecode, this is passed as a method argument. That is why, although void() has no parameters, args_size on bytecode line 5 is 1. For a variable accessed through an object reference, whether a class variable or an instance variable, the Java compiler simply disables this optimization wholesale. Change this.s here to Test.s and the output becomes true.",{"title":21,"searchDepth":90,"depth":90,"links":91},4,[],[93],"fiddling","2022-04-16 00:01:28","In Java, using `this` can prevent the compiler from optimizing constants. Although `ab1` and `ab2` in this example appear to refer to the same static final variable `s`, comparing them produces different results. `ab1` concatenates a direct reference to the static variable, while `ab2` accesses it through `this`, preventing the same constant propagation optimization. This illustrates how a small syntactic difference can change compilation behavior.",false,"md","https:\u002F\u002Fblog-img.774352199.xyz\u002FgKtkYe.webp",{"slots":100},{},true,"\u002Ffiddling\u002Fthis-in-javac-string-concat",null,{"text":105,"minutes":106,"time":107,"words":108},"3 min read",2.305,138300,461,{"title":5,"description":95},"Use javap to investigate why s and this.s produce different string comparisons, tracing constant folding, pool references, and runtime concatenation.",{"loc":102},"posts\u002Ffiddling\u002Fthis-in-javac-string-concat",[114,115,116,117,118],"Java","javac","String concatenation","Constant propagation","Bytecode","tech","p1XbMTgtC260r4RHHUbTpdtkqE1N0DRJHzcYrYAc5TE",[122,127],{"title":123,"path":124,"stem":125,"date":126,"type":119,"children":-1},"Setting Up the CSAPP Lab Environment","\u002Ffiddling\u002Fcsapplab0","posts\u002Ffiddling\u002Fcsapplab0","2021-12-27 00:09:00",{"title":128,"path":129,"stem":130,"date":131,"type":119,"children":-1},"Deep Copying Between Different Struct Types in Go","\u002Ffiddling\u002Fgolang-deepcopy-between-different-type","posts\u002Ffiddling\u002Fgolang-deepcopy-between-different-type","2022-08-15 01:05:01",1789914052313]