Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java integer ++i not changing the value

Tags:

java

I just made this simple "program":

public static void main(String[] args) {
        int i = 1;
        int k = 0;
        while (true) {
            if(++i==0) System.out.println("loop: " + ++k);
        }
    }

Upon running this program, I immediately get the output:

(...)
loop: 881452
loop: 881453
loop: 881454
loop: 881455
loop: 881456
loop: 881457
loop: 881458
(...)

as if i would be always 0.

And in fact, when I debug in Eclipse, upon suspending the program, i would be always zero. When stepping through the loop, i would increment, but upon resuming and suspending the debugger, i is 0 again.

When I change i to long, upon running the program I need to wait quite a while before seeing the first loop: 1. In the debugger, upon pausing the program, i does increment: it's not 0, so it works as it should.

What's the problem with ++i as an int?

like image 917
SlumpA Avatar asked Dec 14 '15 20:12

SlumpA


People also ask

Can an integer change value?

Simply put: You can't, because Integer is immutable and you only get the object address by value, so swapping the whole object is not possible because after the method finished, the old object gets reassigned.

Why is my method not returning a value JAVA?

If a method does not return a value, it must be declared to return void . However, the pop() method in the Stack class returns a reference data type: an object. Methods use the return operator to return a value. Any method that is not declared void must contain a return statement.

How do you change the value of an integer object?

Integer objects are immutable, so you cannot modify the value once they have been created. You will need to create a new Integer and replace the existing one.

How to get the value of an integer in Java?

The java.lang.Integer.intValue () is an inbuilt method in java that returns the value of this integer as an int. Parameters: The method does not accept any parameters. Return Value : The method returns the numeric value which is represented by the object after conversion to the integer type. Program 1: For a positive integer.

How to convert integer to int primitive type in Java?

To convert integer object to int primitive type in java we can use intValue () method of Integer Class . This method does not take any argument but Returns the value of this Integer as an int. There we will convert Integer to int using intValue () method as below example .

Why can't I change the value of an array of integers?

In fact, you're not changing the value of arr, either - it's a reference to the same array as before, but the value in the array has been changed. And that's what you can't do with Integer, because Integer is an immutable type. If you want a mutable class like Integer, you could use AtomicInteger instead:

What is the use of int i in Java?

It is a type of String which will be parsed into an integer object. This is of integer type and used in converting the string object. Returns an Integer instance holding the value of the specified parameter int i. Returns an Integer instance holding the value represented by the string argument.


3 Answers

If you keep incrementing an integer type, it will eventually overflow, becoming a large negative value. If you keep going, it will eventually become 0 again, and the cycle will repeat.

There are convenience methods to help avoid inadvertent overflows, like Math.addExact(), but these wouldn't normally be used in a loop.


I know that it is overflowing. I'm just puzzled that it's overflowing THAT fast. And I find it strange that each time I suspend the debugger, i is 0.

When you suspend a running thread, consider the chance of the thread being in a slow call to println() that traversing a huge stack of Java and native OS code, versus the probability of landing in the test of your while loop, which is just incrementing a local variable. You'd have to have a pretty quick trigger finger to see anything other than the print statement. Try stepping through instead.

When something happens 4 billion times in a row, it's a pretty good guess it will happen next time. Branch prediction will help here in any case, and it's possible that your optimizing runtime removes the increment operation and test entirely, since the intervening values of i are never read.

like image 150
erickson Avatar answered Sep 25 '22 01:09

erickson


As JohannesD suggested in a comment, it's hardly possible to count from 0 to Integer.MAX_VALUE (and, after the overflow, from -Integer.MAX_VALUE to 0 again) so quickly.

In order to verify the assumption that the JIT does some magic optimization here, I created a slightly modified program, introducing some methods make it easier to identify parts of the code:

class IntOverflowTest
{
    public static void main(String[] args) {
        runLoop();
    }

    public static void runLoop()
    {
        int i = 1;
        int k = 0;
        while (true) {
            if(++i==0) doPrint(++k);
        }
    }

    public static void doPrint(int k)
    {
        System.out.println("loop: " + k);
    }

}

The bytecode emitted and shown with javap -c IntOverflowTest brings no surprises:

class IntOverflowTest {
  IntOverflowTest();
    Code:
       0: aload_0
       1: invokespecial #1                  
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #2                  
       3: return

  public static void runLoop();
    Code:
       0: iconst_1
       1: istore_0
       2: iconst_0
       3: istore_1
       4: iinc          0, 1
       7: iload_0
       8: ifne          4
      11: iinc          1, 1
      14: iload_1
      15: invokestatic  #3                  
      18: goto          4

  public static void doPrint(int);
    Code:
       0: getstatic     #4                  
       3: new           #5                  
       6: dup
       7: invokespecial #6                  
      10: ldc           #7                  
      12: invokevirtual #8                  
      15: iload_0
      16: invokevirtual #9                  
      19: invokevirtual #10                 
      22: invokevirtual #11                 
      25: return
}

It clearly does increment both local variables (runLoop, offsets 4 and 11).

However, when running the code with -XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -XX:+PrintAssembly in a Hotspot Disassembler, the machine code eventually ends up to be the following:

Decoding compiled method 0x00000000025c2c50:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
  # {method} {0x000000001bb40408} 'runLoop' '()V' in 'IntOverflowTest'
  #           [sp+0x20]  (sp of caller)
  0x00000000025c2da0: mov    %eax,-0x6000(%rsp)
  0x00000000025c2da7: push   %rbp
  0x00000000025c2da8: sub    $0x10,%rsp         ;*synchronization entry
                                                ; - IntOverflowTest::runLoop@-1 (line 10)

  0x00000000025c2dac: mov    $0x1,%ebp          ;*iinc
                                                ; - IntOverflowTest::runLoop@11 (line 13)

  0x00000000025c2db1: mov    %ebp,%edx
  0x00000000025c2db3: callq  0x00000000024f6360  ; OopMap{off=24}
                                                ;*invokestatic doPrint
                                                ; - IntOverflowTest::runLoop@15 (line 13)
                                                ;   {static_call}
  0x00000000025c2db8: inc    %ebp               ;*iinc
                                                ; - IntOverflowTest::runLoop@11 (line 13)

  0x00000000025c2dba: jmp    0x00000000025c2db1  ;*invokestatic doPrint
                                                ; - IntOverflowTest::runLoop@15 (line 13)

  0x00000000025c2dbc: mov    %rax,%rdx
  0x00000000025c2dbf: add    $0x10,%rsp
  0x00000000025c2dc3: pop    %rbp
  0x00000000025c2dc4: jmpq   0x00000000025b0d20  ;   {runtime_call}
  0x00000000025c2dc9: hlt

One can clearly see that it does not increment the outer variable i any more. It only calls the doPrint method, increments a single variable (k in the code), and then and immediately jumps back to the point before the doPrint call.

So the JIT indeed seems to detect that there is no real "condition" involved for printing the output, and that the code is equivalent to an infinite loop that only prints and increments a single variable.

This seems like a quite sophisticated optimization for me. I would expect that it's far from trivial to detect a case like this. But obviously, they managed to do so...

like image 30
Marco13 Avatar answered Sep 23 '22 01:09

Marco13


Your loop is overflowing i. You have no break, so after a period of time, i wraps back to 0, and this prints the statement and increments k. This also explains why changing the int to a long causes the printing to slow down: it takes much longer for a long value to overflow.

like image 26
mk. Avatar answered Sep 27 '22 01:09

mk.