问题
I have code like this:
public class TestGC {
private static final int _10MB = 10 * 1024 * 1024; // 10MB
public static void main(String[] args) {
test1();
// test2();
}
public static void test1() {
int i = 1;
if (i > 0) {
byte[] data = new byte[_10MB];
}
System.gc();
}
public static void test2() {
if (true) {
byte[] data = new byte[_10MB];
}
System.gc();
}
}
I run it with jvm option -verbose:gc
, My java env:
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)
CASE-1:
Run with method test1()
invoked, console output:
[GC 13312K->616K(116736K), 0.0014246 secs]
[Full GC 616K->554K(116736K), 0.0125266 secs]
data
var is collected by JVM.
CASE-2:
Run with method test2()
invoked, console output:
[GC 13312K->10936K(116736K), 0.0092033 secs]
[Full GC 10936K->10788K(116736K), 0.0155626 secs]
data
var is not collected.
I generate bytecode for methods by command javap
:
test1()
public static void test1();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=2, args_size=0
0: iconst_1
1: istore_0
2: iload_0
3: ifle 11
6: ldc #3 // int 10485760
8: newarray byte
10: astore_1
11: invokestatic #4 // Method java/lang/System.gc:()V
14: return
LineNumberTable:
line 11: 0
line 12: 2
line 13: 6
line 15: 11
line 16: 14
LocalVariableTable:
Start Length Slot Name Signature
11 0 1 data [B
2 13 0 i I
StackMapTable: number_of_entries = 1
frame_type = 252 /* append */
offset_delta = 11
locals = [ int ]
test2()
public static void test2();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=0
0: ldc #3 // int 10485760
2: newarray byte
4: astore_0
5: invokestatic #4 // Method java/lang/System.gc:()V
8: return
LineNumberTable:
line 20: 0
line 22: 5
line 23: 8
LocalVariableTable:
Start Length Slot Name Signature
5 0 0 data [B
My guess is: When method test1()
execute to stack map frame, the local variables is reset and lead to slot_1(data
located) is cleared.
Someone can give a detail explain?
回答1:
The scope of local variables is a compile-time thing. For the byte code, it only matters, which value was most recently written to a local variable index. For the garbage collector, it only matters, which value may subsequently get accessed.
But detecting that a value is not subsequently used, may depend on the compilation/optimization level of the code. In your simple test, the code will always run interpreted, so the JVM does not always detect that the created array is actually unused. When you run you test with -Xcomp
, it will always get collected immediately.
The behavior you have discovered depends on the conditional branches found in the byte code, but not on the presence of stack maps, which you can easily verify by compiling with -target 1.5
(also needs -source 1.5
), so that no stack maps are present in the compiled class file, but run on the same runtime environment; the behavior doesn’t change.
Note that your
if (true) {
byte[] data = new byte[_10MB];
}
System.gc();
is not different to
{
byte[] data = new byte[_10MB];
}
System.gc();
as you are branching over a compile-time constant. But since you are not overwriting the value, e.g. by creating and using another variable after the end of the scope, the byte code doesn’t differ from
byte[] data = new byte[_10MB];
System.gc();
All these variants exhibit the same behavior of not collecting the array still referenced by the stack frame, unless the code got compiled.
In contrast,
int i = 1;
if (i > 0) {
byte[] data = new byte[_10MB];
}
System.gc();
bears a conditional branch, so at the System.gc()
point, the array reference can’t be used, as the code point might get reached through a path where this variable is not initialized.
Likewise, the array is collected with
for(boolean b=true; b; b=!b) {
byte[] data = new byte[_10MB];
}
System.gc();
as the conditional branch may bypass the variable initialization, while with
do {
byte[] data = new byte[_10MB];
} while(false);
System.gc();
the array is not collected, as the variable is always initialized.
Also, with
public static void test1() {
int i = 1;
if (i > 0) {
byte[] data = new byte[_10MB];
}
else {
byte[] data = new byte[_10MB];
}
System.gc();
}
the array is not collected, as the variables is always initialized, regardless of which branch the code takes. As said, only in interpreted execution.
This is a sign that the stack map is not used here, as the stack map clearly declares, that there is no byte[]
variable at the branch merge point, like in your original test1()
variant.
来源:https://stackoverflow.com/questions/48960056/does-the-stackmaptable-affect-the-garbage-collection-behavior