Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java整数++;我不改变这个值_Java - Fatal编程技术网

Java整数++;我不改变这个值

Java整数++;我不改变这个值,java,Java,我刚刚做了一个简单的“程序”: 运行此程序后,我立即获得输出: (...) loop: 881452 loop: 881453 loop: 881454 loop: 881455 loop: 881456 loop: 881457 loop: 881458 (...) 好像i总是0 事实上,当我调试时,在挂起程序时,I将始终为零。当单步执行循环时,i将递增,但在恢复并挂起调试器时,i再次为0 当我将I更改为long时,在运行程序时,我需要等待很长时间才能看到第一个循环:1。在调试器中,在暂停程

我刚刚做了一个简单的“程序”:

运行此程序后,我立即获得输出:

(...)
loop: 881452
loop: 881453
loop: 881454
loop: 881455
loop: 881456
loop: 881457
loop: 881458
(...)
好像
i
总是0

事实上,当我调试时,在挂起程序时,
I
将始终为零。当单步执行循环时,
i
将递增,但在恢复并挂起调试器时,
i
再次为0

当我将
I
更改为long时,在运行程序时,我需要等待很长时间才能看到第一个
循环:1
。在调试器中,在暂停程序时,
i
会递增:它不是0,因此它可以正常工作


++i
作为int有什么问题?

您的循环溢出了
i
。您没有
break
,因此在一段时间后,
i
返回到0,这将打印语句并递增
k
。这也解释了为什么将
int
更改为
long
会导致打印速度减慢:一个
long
值溢出的时间要长得多。

如果继续递增整数类型,它最终会溢出,成为一个较大的负值。如果继续,它最终将再次变为0,并且循环将重复

有一些方便的方法可以帮助避免意外的溢出,例如,但这些方法通常不会在循环中使用



我知道它是溢出的。我只是觉得奇怪,它溢得那么快。我发现很奇怪,每次我挂起调试器时,我都是0

当你暂停运行线程时,考虑线程慢调用“代码> PROTLNE())/>代码的机会,它遍历java和原生OS代码的巨大堆栈,而不是在测试while循环时登陆的概率,这只是增加了局部变量。你必须有一个相当快的触发手指才能看到除了打印语句以外的任何东西。试着通过


当某件事连续发生40亿次时,很有可能下次还会发生。分支预测在任何情况下都会有帮助,优化运行时可能会完全删除增量操作和测试,因为
i
的中间值永远不会被读取。

它并不总是0,在循环(整数溢出)后变为0,所以它首先会变为integer.MAX\u值,然后是Integer.MIN_值,然后再向上移动到0。这就是为什么它看起来总是0,但实际上它在变成0之前需要所有可能的整数值。。。一次又一次。

首先让我们看看这个循环在逻辑上做了什么

i
将重复溢出。循环的每232次(约40亿次)迭代,输出将被打印,k将增加

这是合乎逻辑的观点。然而,编译器和运行时是允许优化的,如果您每秒获得的值超过一个值,那么很明显这种优化必须进行。即使使用现代分支预测、无序执行等,我也发现CPU不可能每时钟周期绕过一个紧循环(甚至我认为不太可能)。事实上,在调试器中,除了零之外,您永远不会看到任何东西,这强化了代码正在被优化的想法

您提到使用“long”时需要更长的时间,并且您确实看到了其他值。如果在未优化的循环中使用“长”计数器,则值之间的间隔将长达数十年。很明显,优化正在进行,但似乎优化者在完全优化无意义的迭代之前就放弃了。

因为,从0到
整数。MAX_值
(溢出后,从
-Integer.MAX_值
再到0)很快就不可能了

为了验证JIT在这里做了一些神奇优化的假设,我创建了一个稍加修改的程序,引入了一些方法,使其更容易识别部分代码:

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);
    }

}
javap-c IntOverflowTest发出并显示的字节码不会带来任何惊喜:

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
}
它显然会增加两个局部变量(
runLoop
,偏移量4和11)

但是,在热点反汇编器中使用
-XX:+UnlockDiagnosticVMOptions-XX:+LogCompilation-XX:+PrintAssembly
运行代码时,机器代码最终会如下所示:

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
可以清楚地看到,它不再增加外部变量
i
。它只调用
doPrint
方法,增加单个变量(
k
),然后立即跳回到
doPrint
调用之前的点

因此,JIT似乎确实检测到打印输出不涉及实际的“条件”,并且代码相当于只打印和递增单个变量的无限循环


对我来说,这似乎是一个相当复杂的优化。我想,发现这样一个案例绝非小事。但很明显,他们成功地做到了…

我是不是一次又一次地溢出了?我知道它是溢出的。我只是觉得奇怪,它溢得那么快。我觉得奇怪的是,每次我挂起调试器时,我都是0。我想看看我的更新是否解决了您的惊讶。一个
int
每秒无法溢出数十万次。很可能编译器正在优化整个条件分支,并以尽可能快的速度运行
println
。@JohannesD您的猜测是对的,请参阅:在这种情况下,JIT似乎确实优化了
i
的增量。@PeterLawrey抱歉,刚才注意到k只在打印时递增,不是每一个循环。即使是在现代处理器上,将一个整数增加40亿次也需要相当一小部分秒的时间才能达到c
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