为什么这个循环很多次的程序在循环后有一个`println`时会花费时间?
Why is this program which loops many times taking time when there is a `println` after the loops?
这是我正在尝试的小代码。该程序需要大量时间来执行。虽然 运行,但如果我尝试通过 eclipse 中的终止按钮终止它,它会 returns Terminate Failed
。我可以使用 kill -9 <PID>
.
从终端杀死它
但是,当我没有在程序的最后一行打印变量结果时(请检查代码的注释部分),程序立即退出。
我想知道:
为什么result的值在打印的时候执行起来很费时间?
请注意,如果我不打印 value
,同样的循环会立即结束。
为什么eclipse不能杀程序?
更新 1:
似乎 JVM 在运行时(而不是编译时)优化了代码。 This thread 很有帮助。
更新 2:
当我打印 value
的值时,jstack <PID>
不起作用。只有 jstack -F <PID>
有效。任何可能的原因?
public class TestClient {
private static void loop() {
long value =0;
for (int j = 0; j < 50000; j++) {
for (int i = 0; i < 100000000; i++) {
value += 1;
}
}
//When the value is being printed, the program
//is taking time to complete
System.out.println("Done "+ value);
//When the value is NOT being printed, the program
//completes immediately
//System.out.println("Done ");
}
public static void main(String[] args) {
loop();
}
}
因为compiler/JVM优化。当您打印结果时,计算和编译器将保持循环。
另一方面,compiler/JVM 将在您不使用循环中的任何内容时删除循环。
我怀疑当 aou 不打印结果时,编译器注意到 value
的值从未被使用,因此能够作为优化删除整个循环。
因此,如果没有 println
,您根本就不会循环,程序只会在打印值时立即退出,您正在执行所有 5,000,000,000,000 次迭代,这可能会有点冗长。
建议试试
public class TestClient {
public static long loop() {
long value =0;
for (int j = 0; j < 50000; j++) {
for (int i = 0; i < 100000000; i++) {
value += 1;
}
}
return value
}
public static void main(String[] args) {
// this might also take rather long
loop();
// as well as this
// System.out.println(loop());
}
}
此处编译器将无法优化掉 loop()
中的循环,因为它可能会被其他各种 类 调用,因此在所有情况下都会执行。
基本上,JVM 真的非常聪明。它可以感知您是否正在使用任何变量,并且基于此,它实际上可以删除与该变量相关的任何处理。由于您注释掉了打印 "value" 的代码行,它会感觉到该变量不会在任何地方使用,并且不会 运行 循环, 甚至一次都不会 .
但是当你做打印值时,它必须运行你的循环,这又是一个非常大的数字(50000 * 100000000)。现在,运行这个循环的时间取决于你的很多因素,包括但不限于你机器的处理器、分配给 JVM 的内存、处理器的负载等。
就你的eclipse无法终止的问题而言,我可以很容易地在我的机器上使用eclipse终止程序。也许你应该再检查一下。
这是 JIT 编译器优化(不是 java 编译器优化)。
如果比较 java 编译器为这两个版本生成的字节码,您会发现这两个版本都存在循环。
使用 println 的反编译方法是这样的:
private static void loop() {
long value = 0L;
for(int j = 0; j < '썐'; ++j) {
for(int i = 0; i < 100000000; ++i) {
++value;
}
}
System.out.println("Done " + value);
}
这是删除 println 后反编译方法的样子:
private static void loop() {
long value = 0L;
for(int j = 0; j < '썐'; ++j) {
for(int i = 0; i < 100000000; ++i) {
++value;
}
}
}
如您所见,循环仍然存在。
但是,您可以使用以下 JVM 选项启用 JIT 编译器日志记录和程序集打印:
-XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -XX:+PrintAssembly
您可能还需要下载 hsdis-amd64.dylib 并放入您的工作目录(MacOS、HotSpot Java 8)
在您 运行 TestClient 之后,您应该会在控制台中看到 JIT 编译器生成的代码。在这里我将post仅从输出中摘录。
没有 println 的版本:
# {method} 'loop' '()V' in 'test/TestClient'
0x000000010e3c2500: callq 0x000000010dc1c202 ; {runtime_call}
0x000000010e3c2505: data32 data32 nopw 0x0(%rax,%rax,1)
0x000000010e3c2510: sub [=13=]x18,%rsp
0x000000010e3c2517: mov %rbp,0x10(%rsp)
0x000000010e3c251c: mov %rsi,%rdi
0x000000010e3c251f: movabs [=13=]x10dc760ec,%r10
0x000000010e3c2529: callq *%r10 ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x000000010e3c252c: add [=13=]x10,%rsp
0x000000010e3c2530: pop %rbp
0x000000010e3c2531: test %eax,-0x1c18537(%rip) # 0x000000010c7aa000
; {poll_return}
0x000000010e3c2537: retq
带有 println 的版本:
# {method} 'loop' '()V' in 'test/TestClient'
0x00000001092c36c0: callq 0x0000000108c1c202 ; {runtime_call}
0x00000001092c36c5: data32 data32 nopw 0x0(%rax,%rax,1)
0x00000001092c36d0: mov %eax,-0x14000(%rsp)
0x00000001092c36d7: push %rbp
0x00000001092c36d8: sub [=14=]x10,%rsp
0x00000001092c36dc: mov 0x10(%rsi),%r13
0x00000001092c36e0: mov 0x8(%rsi),%ebp
0x00000001092c36e3: mov (%rsi),%ebx
0x00000001092c36e5: mov %rsi,%rdi
0x00000001092c36e8: movabs [=14=]x108c760ec,%r10
0x00000001092c36f2: callq *%r10
0x00000001092c36f5: jmp 0x00000001092c3740
0x00000001092c36f7: add [=14=]x1,%r13 ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x00000001092c36fb: inc %ebx ;*iinc
; - test.TestClient::loop@22 (line 9)
0x00000001092c36fd: cmp [=14=]x5f5e101,%ebx
0x00000001092c3703: jl 0x00000001092c36f7 ;*if_icmpge
; - test.TestClient::loop@15 (line 9)
0x00000001092c3705: jmp 0x00000001092c3734
0x00000001092c3707: nopw 0x0(%rax,%rax,1)
0x00000001092c3710: mov %r13,%r8 ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x00000001092c3713: mov %r8,%r13
0x00000001092c3716: add [=14=]x10,%r13 ;*ladd
; - test.TestClient::loop@20 (line 10)
0x00000001092c371a: add [=14=]x10,%ebx ;*iinc
; - test.TestClient::loop@22 (line 9)
0x00000001092c371d: cmp [=14=]x5f5e0f2,%ebx
0x00000001092c3723: jl 0x00000001092c3710 ;*if_icmpge
; - test.TestClient::loop@15 (line 9)
0x00000001092c3725: add [=14=]xf,%r8 ;*ladd
; - test.TestClient::loop@20 (line 10)
0x00000001092c3729: cmp [=14=]x5f5e101,%ebx
0x00000001092c372f: jl 0x00000001092c36fb
0x00000001092c3731: mov %r8,%r13 ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x00000001092c3734: inc %ebp ;*iinc
; - test.TestClient::loop@28 (line 8)
0x00000001092c3736: cmp [=14=]xc350,%ebp
0x00000001092c373c: jge 0x00000001092c376c ;*if_icmpge
; - test.TestClient::loop@7 (line 8)
0x00000001092c373e: xor %ebx,%ebx
0x00000001092c3740: mov %ebx,%r11d
0x00000001092c3743: inc %r11d ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x00000001092c3746: mov %r13,%r8
0x00000001092c3749: add [=14=]x1,%r8 ;*ladd
; - test.TestClient::loop@20 (line 10)
0x00000001092c374d: inc %ebx ;*iinc
; - test.TestClient::loop@22 (line 9)
0x00000001092c374f: cmp %r11d,%ebx
0x00000001092c3752: jge 0x00000001092c3759 ;*if_icmpge
; - test.TestClient::loop@15 (line 9)
0x00000001092c3754: mov %r8,%r13
0x00000001092c3757: jmp 0x00000001092c3746
0x00000001092c3759: cmp [=14=]x5f5e0f2,%ebx
0x00000001092c375f: jl 0x00000001092c3713
0x00000001092c3761: mov %r13,%r10
0x00000001092c3764: mov %r8,%r13
0x00000001092c3767: mov %r10,%r8
0x00000001092c376a: jmp 0x00000001092c3729 ;*if_icmpge
; - test.TestClient::loop@7 (line 8)
0x00000001092c376c: mov [=14=]x24,%esi
0x00000001092c3771: mov %r13,%rbp
0x00000001092c3774: data32 xchg %ax,%ax
0x00000001092c3777: callq 0x0000000109298f20 ; OopMap{off=188}
;*getstatic out
; - test.TestClient::loop@34 (line 13)
; {runtime_call}
0x00000001092c377c: callq 0x0000000108c1c202 ;*getstatic out
; - test.TestClient::loop@34 (line 13)
; {runtime_call}
您还应该有包含 JIT 编译器步骤的 hotspot.log 文件。以下是摘录:
<phase name='optimizer' nodes='114' live='77' stamp='0.100'>
<phase name='idealLoop' nodes='115' live='67' stamp='0.100'>
<loop_tree>
<loop idx='119' >
<loop idx='185' main_loop='185' >
</loop>
</loop>
</loop_tree>
<phase_done name='idealLoop' nodes='197' live='111' stamp='0.101'/>
</phase>
<phase name='idealLoop' nodes='197' live='111' stamp='0.101'>
<loop_tree>
<loop idx='202' >
<loop idx='159' inner_loop='1' pre_loop='131' >
</loop>
<loop idx='210' inner_loop='1' main_loop='210' >
</loop>
<loop idx='138' inner_loop='1' post_loop='131' >
</loop>
</loop>
</loop_tree>
<phase_done name='idealLoop' nodes='221' live='113' stamp='0.101'/>
</phase>
<phase name='idealLoop' nodes='221' live='113' stamp='0.101'>
<loop_tree>
<loop idx='202' >
<loop idx='159' inner_loop='1' pre_loop='131' >
</loop>
<loop idx='210' inner_loop='1' main_loop='210' >
</loop>
<loop idx='138' inner_loop='1' post_loop='131' >
</loop>
</loop>
</loop_tree>
<phase_done name='idealLoop' nodes='241' live='63' stamp='0.101'/>
</phase>
<phase name='ccp' nodes='241' live='63' stamp='0.101'>
<phase_done name='ccp' nodes='241' live='63' stamp='0.101'/>
</phase>
<phase name='idealLoop' nodes='241' live='63' stamp='0.101'>
<loop_tree>
<loop idx='202' inner_loop='1' >
</loop>
</loop_tree>
<phase_done name='idealLoop' nodes='253' live='56' stamp='0.101'/>
</phase>
<phase name='idealLoop' nodes='253' live='56' stamp='0.101'>
<phase_done name='idealLoop' nodes='253' live='33' stamp='0.101'/>
</phase>
<phase_done name='optimizer' nodes='253' live='33' stamp='0.101'/>
</phase>
您可以使用 JitWatch 工具进一步分析 JIT 编译器生成的 hotspot.log 文件 https://github.com/AdoptOpenJDK/jitwatch/wiki
要在全解释模式下禁用 JIT 编译器和 运行 Java 虚拟机,您可以使用 -Djava.compiler=NONE JVM 选项。
类似的问题在这个postWhy is my JVM doing some runtime loop optimization and making my code buggy?
为什么result的值在打印的时候执行起来很费时间?请注意,如果我不打印值,相同的循环会立即结束。
老实说,我在 eclipse(windows) 中 运行 你的代码,即使你评论 system.out.println 行,它也会保持 运行。我在调试模式下仔细检查(如果你打开调试透视图,你会在(默认情况下)左上角看到所有 运行 应用程序。)
但如果它对您来说运行得很快,那么最合理的答案是这是因为 java compiler/JVM 优化。我们都知道 java 尽管是一种解释性(主要)语言,但速度很快,因为它将源代码转换为字节码,使用 JIT 编译器,热点等
为什么eclipse不能杀程序?
我可以在eclipse中成功杀掉程序(windows)。也许特定的 Eclipse 版本或 linux.(不确定) 存在一些问题。当 eclipse 无法终止程序时,快速 google 搜索给出了多种情况。
这是我正在尝试的小代码。该程序需要大量时间来执行。虽然 运行,但如果我尝试通过 eclipse 中的终止按钮终止它,它会 returns Terminate Failed
。我可以使用 kill -9 <PID>
.
但是,当我没有在程序的最后一行打印变量结果时(请检查代码的注释部分),程序立即退出。
我想知道:
为什么result的值在打印的时候执行起来很费时间?
请注意,如果我不打印value
,同样的循环会立即结束。为什么eclipse不能杀程序?
更新 1: 似乎 JVM 在运行时(而不是编译时)优化了代码。 This thread 很有帮助。
更新 2:
当我打印 value
的值时,jstack <PID>
不起作用。只有 jstack -F <PID>
有效。任何可能的原因?
public class TestClient {
private static void loop() {
long value =0;
for (int j = 0; j < 50000; j++) {
for (int i = 0; i < 100000000; i++) {
value += 1;
}
}
//When the value is being printed, the program
//is taking time to complete
System.out.println("Done "+ value);
//When the value is NOT being printed, the program
//completes immediately
//System.out.println("Done ");
}
public static void main(String[] args) {
loop();
}
}
因为compiler/JVM优化。当您打印结果时,计算和编译器将保持循环。
另一方面,compiler/JVM 将在您不使用循环中的任何内容时删除循环。
我怀疑当 aou 不打印结果时,编译器注意到 value
的值从未被使用,因此能够作为优化删除整个循环。
因此,如果没有 println
,您根本就不会循环,程序只会在打印值时立即退出,您正在执行所有 5,000,000,000,000 次迭代,这可能会有点冗长。
建议试试
public class TestClient {
public static long loop() {
long value =0;
for (int j = 0; j < 50000; j++) {
for (int i = 0; i < 100000000; i++) {
value += 1;
}
}
return value
}
public static void main(String[] args) {
// this might also take rather long
loop();
// as well as this
// System.out.println(loop());
}
}
此处编译器将无法优化掉 loop()
中的循环,因为它可能会被其他各种 类 调用,因此在所有情况下都会执行。
基本上,JVM 真的非常聪明。它可以感知您是否正在使用任何变量,并且基于此,它实际上可以删除与该变量相关的任何处理。由于您注释掉了打印 "value" 的代码行,它会感觉到该变量不会在任何地方使用,并且不会 运行 循环, 甚至一次都不会 .
但是当你做打印值时,它必须运行你的循环,这又是一个非常大的数字(50000 * 100000000)。现在,运行这个循环的时间取决于你的很多因素,包括但不限于你机器的处理器、分配给 JVM 的内存、处理器的负载等。
就你的eclipse无法终止的问题而言,我可以很容易地在我的机器上使用eclipse终止程序。也许你应该再检查一下。
这是 JIT 编译器优化(不是 java 编译器优化)。
如果比较 java 编译器为这两个版本生成的字节码,您会发现这两个版本都存在循环。
使用 println 的反编译方法是这样的:
private static void loop() {
long value = 0L;
for(int j = 0; j < '썐'; ++j) {
for(int i = 0; i < 100000000; ++i) {
++value;
}
}
System.out.println("Done " + value);
}
这是删除 println 后反编译方法的样子:
private static void loop() {
long value = 0L;
for(int j = 0; j < '썐'; ++j) {
for(int i = 0; i < 100000000; ++i) {
++value;
}
}
}
如您所见,循环仍然存在。
但是,您可以使用以下 JVM 选项启用 JIT 编译器日志记录和程序集打印:
-XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -XX:+PrintAssembly
您可能还需要下载 hsdis-amd64.dylib 并放入您的工作目录(MacOS、HotSpot Java 8)
在您 运行 TestClient 之后,您应该会在控制台中看到 JIT 编译器生成的代码。在这里我将post仅从输出中摘录。
没有 println 的版本:
# {method} 'loop' '()V' in 'test/TestClient'
0x000000010e3c2500: callq 0x000000010dc1c202 ; {runtime_call}
0x000000010e3c2505: data32 data32 nopw 0x0(%rax,%rax,1)
0x000000010e3c2510: sub [=13=]x18,%rsp
0x000000010e3c2517: mov %rbp,0x10(%rsp)
0x000000010e3c251c: mov %rsi,%rdi
0x000000010e3c251f: movabs [=13=]x10dc760ec,%r10
0x000000010e3c2529: callq *%r10 ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x000000010e3c252c: add [=13=]x10,%rsp
0x000000010e3c2530: pop %rbp
0x000000010e3c2531: test %eax,-0x1c18537(%rip) # 0x000000010c7aa000
; {poll_return}
0x000000010e3c2537: retq
带有 println 的版本:
# {method} 'loop' '()V' in 'test/TestClient'
0x00000001092c36c0: callq 0x0000000108c1c202 ; {runtime_call}
0x00000001092c36c5: data32 data32 nopw 0x0(%rax,%rax,1)
0x00000001092c36d0: mov %eax,-0x14000(%rsp)
0x00000001092c36d7: push %rbp
0x00000001092c36d8: sub [=14=]x10,%rsp
0x00000001092c36dc: mov 0x10(%rsi),%r13
0x00000001092c36e0: mov 0x8(%rsi),%ebp
0x00000001092c36e3: mov (%rsi),%ebx
0x00000001092c36e5: mov %rsi,%rdi
0x00000001092c36e8: movabs [=14=]x108c760ec,%r10
0x00000001092c36f2: callq *%r10
0x00000001092c36f5: jmp 0x00000001092c3740
0x00000001092c36f7: add [=14=]x1,%r13 ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x00000001092c36fb: inc %ebx ;*iinc
; - test.TestClient::loop@22 (line 9)
0x00000001092c36fd: cmp [=14=]x5f5e101,%ebx
0x00000001092c3703: jl 0x00000001092c36f7 ;*if_icmpge
; - test.TestClient::loop@15 (line 9)
0x00000001092c3705: jmp 0x00000001092c3734
0x00000001092c3707: nopw 0x0(%rax,%rax,1)
0x00000001092c3710: mov %r13,%r8 ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x00000001092c3713: mov %r8,%r13
0x00000001092c3716: add [=14=]x10,%r13 ;*ladd
; - test.TestClient::loop@20 (line 10)
0x00000001092c371a: add [=14=]x10,%ebx ;*iinc
; - test.TestClient::loop@22 (line 9)
0x00000001092c371d: cmp [=14=]x5f5e0f2,%ebx
0x00000001092c3723: jl 0x00000001092c3710 ;*if_icmpge
; - test.TestClient::loop@15 (line 9)
0x00000001092c3725: add [=14=]xf,%r8 ;*ladd
; - test.TestClient::loop@20 (line 10)
0x00000001092c3729: cmp [=14=]x5f5e101,%ebx
0x00000001092c372f: jl 0x00000001092c36fb
0x00000001092c3731: mov %r8,%r13 ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x00000001092c3734: inc %ebp ;*iinc
; - test.TestClient::loop@28 (line 8)
0x00000001092c3736: cmp [=14=]xc350,%ebp
0x00000001092c373c: jge 0x00000001092c376c ;*if_icmpge
; - test.TestClient::loop@7 (line 8)
0x00000001092c373e: xor %ebx,%ebx
0x00000001092c3740: mov %ebx,%r11d
0x00000001092c3743: inc %r11d ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x00000001092c3746: mov %r13,%r8
0x00000001092c3749: add [=14=]x1,%r8 ;*ladd
; - test.TestClient::loop@20 (line 10)
0x00000001092c374d: inc %ebx ;*iinc
; - test.TestClient::loop@22 (line 9)
0x00000001092c374f: cmp %r11d,%ebx
0x00000001092c3752: jge 0x00000001092c3759 ;*if_icmpge
; - test.TestClient::loop@15 (line 9)
0x00000001092c3754: mov %r8,%r13
0x00000001092c3757: jmp 0x00000001092c3746
0x00000001092c3759: cmp [=14=]x5f5e0f2,%ebx
0x00000001092c375f: jl 0x00000001092c3713
0x00000001092c3761: mov %r13,%r10
0x00000001092c3764: mov %r8,%r13
0x00000001092c3767: mov %r10,%r8
0x00000001092c376a: jmp 0x00000001092c3729 ;*if_icmpge
; - test.TestClient::loop@7 (line 8)
0x00000001092c376c: mov [=14=]x24,%esi
0x00000001092c3771: mov %r13,%rbp
0x00000001092c3774: data32 xchg %ax,%ax
0x00000001092c3777: callq 0x0000000109298f20 ; OopMap{off=188}
;*getstatic out
; - test.TestClient::loop@34 (line 13)
; {runtime_call}
0x00000001092c377c: callq 0x0000000108c1c202 ;*getstatic out
; - test.TestClient::loop@34 (line 13)
; {runtime_call}
您还应该有包含 JIT 编译器步骤的 hotspot.log 文件。以下是摘录:
<phase name='optimizer' nodes='114' live='77' stamp='0.100'>
<phase name='idealLoop' nodes='115' live='67' stamp='0.100'>
<loop_tree>
<loop idx='119' >
<loop idx='185' main_loop='185' >
</loop>
</loop>
</loop_tree>
<phase_done name='idealLoop' nodes='197' live='111' stamp='0.101'/>
</phase>
<phase name='idealLoop' nodes='197' live='111' stamp='0.101'>
<loop_tree>
<loop idx='202' >
<loop idx='159' inner_loop='1' pre_loop='131' >
</loop>
<loop idx='210' inner_loop='1' main_loop='210' >
</loop>
<loop idx='138' inner_loop='1' post_loop='131' >
</loop>
</loop>
</loop_tree>
<phase_done name='idealLoop' nodes='221' live='113' stamp='0.101'/>
</phase>
<phase name='idealLoop' nodes='221' live='113' stamp='0.101'>
<loop_tree>
<loop idx='202' >
<loop idx='159' inner_loop='1' pre_loop='131' >
</loop>
<loop idx='210' inner_loop='1' main_loop='210' >
</loop>
<loop idx='138' inner_loop='1' post_loop='131' >
</loop>
</loop>
</loop_tree>
<phase_done name='idealLoop' nodes='241' live='63' stamp='0.101'/>
</phase>
<phase name='ccp' nodes='241' live='63' stamp='0.101'>
<phase_done name='ccp' nodes='241' live='63' stamp='0.101'/>
</phase>
<phase name='idealLoop' nodes='241' live='63' stamp='0.101'>
<loop_tree>
<loop idx='202' inner_loop='1' >
</loop>
</loop_tree>
<phase_done name='idealLoop' nodes='253' live='56' stamp='0.101'/>
</phase>
<phase name='idealLoop' nodes='253' live='56' stamp='0.101'>
<phase_done name='idealLoop' nodes='253' live='33' stamp='0.101'/>
</phase>
<phase_done name='optimizer' nodes='253' live='33' stamp='0.101'/>
</phase>
您可以使用 JitWatch 工具进一步分析 JIT 编译器生成的 hotspot.log 文件 https://github.com/AdoptOpenJDK/jitwatch/wiki
要在全解释模式下禁用 JIT 编译器和 运行 Java 虚拟机,您可以使用 -Djava.compiler=NONE JVM 选项。
类似的问题在这个postWhy is my JVM doing some runtime loop optimization and making my code buggy?
为什么result的值在打印的时候执行起来很费时间?请注意,如果我不打印值,相同的循环会立即结束。
老实说,我在 eclipse(windows) 中 运行 你的代码,即使你评论 system.out.println 行,它也会保持 运行。我在调试模式下仔细检查(如果你打开调试透视图,你会在(默认情况下)左上角看到所有 运行 应用程序。)
但如果它对您来说运行得很快,那么最合理的答案是这是因为 java compiler/JVM 优化。我们都知道 java 尽管是一种解释性(主要)语言,但速度很快,因为它将源代码转换为字节码,使用 JIT 编译器,热点等
为什么eclipse不能杀程序?
我可以在eclipse中成功杀掉程序(windows)。也许特定的 Eclipse 版本或 linux.(不确定) 存在一些问题。当 eclipse 无法终止程序时,快速 google 搜索给出了多种情况。