22

Are there any practical uses of the Java Virtual Machine's NOP opcode in today's JVM? If so, what are some scenarios in which NOPs would be generated in bytecode?

I would even be interested to see an example of Java code that compiles into bytecode with NOPs.


Update

BCEL's MethodGen class says,

While generating code it may be necessary to insert NOP operations.

I am guessing other Bytecode generation libraries are in the same boat, as was pointed out in the accepted answer.

jbranchaud
  • 5,909
  • 9
  • 45
  • 70
  • Usually it is used in debug code to allow breakpoints on something that doesn't translate into bytecode, like `{`. – vcsjones May 02 '12 at 21:40
  • Do you mean this would appear in bytecode when a Java file is compiled with `javac -g`? – jbranchaud May 02 '12 at 21:45
  • I don't believe `javac` will do that. But other compilers and debuggers could make use of that functionality. – vcsjones May 02 '12 at 21:56
  • As an example, using soot to generate Jimple code with some if statements results in NOPs. – Jus12 Jul 10 '12 at 19:15

3 Answers3

12

Some NOP bytecode use cases are for class file transformations, optimizations and static analysis performed by tools such as Apache BCEL, ASM, FindBugs, PMD, etc. The Apache BCEL manual touches on some uses of NOP for analysis and optimization purposes.

A JVM may use NOP bytecodes for JIT optimizations to ensure code blocks that are at synchronization safepoints are properly aligned to avoid false sharing.

As for some sample code compiled using the JDK javac compiler that contains NOP bytecodes, that's an interesting challenge. However, I doubt the compiler will generate any class file containing NOP bytecodes since the bytecode instruction stream is only single-byte aligned. I would be curious to see such an example, but I can't think of any myself.

Go Dan
  • 15,194
  • 6
  • 41
  • 65
1

Here is an example from some code I've been working on where nop instructions where placed into the byte code (as viewed by Bytecode Visualizer for Eclipse)

The original code

public abstract class Wrapper<T extends Wrapper<T,E>,E>
  implements Supplier<Optional<E>>, Consumer<E>
{
  /** The wrapped object. */
  protected Optional<E> inner;

  /*
   * (non-Javadoc)
   * @see java.lang.Object#equals(java.lang.Object)
   */
  /**
   * A basic equals method that will compare the wrapped object to
   * whatever you throw at it, whether it is wrapped or not.
   */
  @Override
  public boolean equals(final Object that)
  {
    return this==that
        ||LambdaUtils.castAndMap(that,Wrapper.class,afterCast
            -> inner.equals(afterCast.inner))
        .orElseGet(()
            -> LambdaUtils.castAndMap(that,Optional.class,afterCast
                -> inner.equals(afterCast))
            .orElseGet(()
                -> Optional.ofNullable(that).map(thatobj
                    -> that.equals(inner.get()))
                .orElseGet(()
                    -> false)));
  }
}

The translated byte code for the equals(Object) method

public boolean equals(java.lang.Object arg0) {
    /* L27 */
    0 aload_0;                /* this */
    1 aload_1;                /* that */
    2 if_acmpeq 36;
    /* L28 */
    5 aload_1;                /* that */
    6 ldc 1;
    8 aload_0;                /* this */
    9 invokedynamic 29;       /* java.util.function.Function apply(ext.cat.wcutils.collections.Wrapper arg0) */
    12 nop;
    13 nop;
    14 invokestatic 30;       /* java.util.Optional ext.cat.wcutils.util.LambdaUtils.castAndMap(java.lang.Object arg0, java.lang.Class arg1, java.util.function.Function arg2) */
    /* L30 */
    17 aload_0;               /* this */
    18 aload_1;               /* that */
    19 invokedynamic 39;      /* java.util.function.Supplier get(ext.cat.wcutils.collections.Wrapper arg0, java.lang.Object arg1) */
    22 nop;
    23 nop;
    24 invokevirtual 40;      /* java.lang.Object orElseGet(java.util.function.Supplier arg0) */
    27 checkcast 46;          /* java.lang.Boolean */
    30 invokevirtual 48;      /* boolean booleanValue() */
    /* L37 */
    33 ifne 5;
    /* L27 */
    36 iconst_0;
    37 ireturn;
    38 iconst_1;
    39 ireturn;
}

I am not sure why those would be inserted. I just hope they don't adversely affect performance.

HesNotTheStig
  • 549
  • 1
  • 4
  • 9
  • 7
    There are no `nop`s, it’s just a bug in the Bytecode Visualizer you have used. The `invokedynamic` instruction consists of five bytes, the last two being zero per specification. Apparently, the Bytecode Visualizer doesn’t know that and assumes the `invokedynamic` instruction to have three bytes only and misinterprets the two zero bytes as `nop` instructions. See [JVM Spec invokedynamic](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokedynamic). – Holger May 19 '16 at 15:12
  • I guess that makes sense. I'm waiting for an update to Bytecode Visualizer for Eclipse Neon. Maybe we should tell the developer. – HesNotTheStig May 20 '16 at 11:52
-1

No ops are often added for processor pipeline optimizations. I'm not sure of what extent Java currently uses them.

From Wikipedia:

A NOP is most commonly used for timing purposes, to force memory alignment, to prevent hazards, to occupy a branch delay slot, or as a place-holder to be replaced by active instructions later on in program development (or to replace removed instructions when refactoring would be problematic or time-consuming). In some cases, a NOP can have minor side effects; for example, on the Motorola 68000 series of processors, the NOP opcode causes a synchronization of the pipeline.

Nate
  • 557
  • 3
  • 8