JVM/编译器优化对象未使用的属性
optimization of unused properties of object by JVM / Compiler
我的 class 包含一些从未在任何地方使用过的属性(这是我真实场景的 DEMO)。我听说 JVM 优化了我们的 Java 代码。
JVM/编译器是否优化/删除对象未使用的属性?
public class A {
private int unused1 = 100;// never called anywhere inside object
public int unused2 = 999;// never called anywhere in the application
}
我知道我需要好好学习JVM、Compiler和优化。但是需要答案,因为在短时间内我必须决定是从大型代码库(大约 10,000 java 文件)中手动删除所有(尽可能多的)未使用的变量,还是仅依赖于 JVM 优化。
期待一些有趣且富有成效的建议。
TL;DR:不,JVM 编译器 (javac) 不会优化掉未使用的变量。
让我们看一下 javac 编译器生成的字节码。
将其用作测试class:
public class Test {
private int test = 5;
private int test2 = 10;
private String aString = "HelloWorld";
}
产生:
Classfile /C:/Users/Huw/Desktop/Test.class
Last modified 19-Apr-2016; size 331 bytes
MD5 checksum 1c49b13d1d5d8a2c52924b20753122af
Compiled from "Test.java"
public class Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #7.#19 // java/lang/Object."<init>":()V
#2 = Fieldref #6.#20 // Test.test:I
#3 = Fieldref #6.#21 // Test.test2:I
#4 = String #22 // HelloWorld
#5 = Fieldref #6.#23 // Test.aString:Ljava/lang/String;
#6 = Class #24 // Test
#7 = Class #25 // java/lang/Object
#8 = Utf8 test
#9 = Utf8 I
#10 = Utf8 test2
#11 = Utf8 aString
#12 = Utf8 Ljava/lang/String;
#13 = Utf8 <init>
#14 = Utf8 ()V
#15 = Utf8 Code
#16 = Utf8 LineNumberTable
#17 = Utf8 SourceFile
#18 = Utf8 Test.java
#19 = NameAndType #13:#14 // "<init>":()V
#20 = NameAndType #8:#9 // test:I
#21 = NameAndType #10:#9 // test2:I
#22 = Utf8 HelloWorld
#23 = NameAndType #11:#12 // aString:Ljava/lang/String;
#24 = Utf8 Test
#25 = Utf8 java/lang/Object
{
public Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_5
6: putfield #2 // Field test:I
9: aload_0
10: bipush 10
12: putfield #3 // Field test2:I
15: aload_0
16: ldc #4 // String HelloWorld
18: putfield #5 // Field aString:Ljava/lang/String;
21: return
LineNumberTable:
line 1: 0
line 3: 4
line 4: 9
line 5: 15
}
SourceFile: "Test.java"
如您所见,编译器仍然分配字段属性。
所以不,未使用的变量仍将分配指针(对于对象)和内存(对于基元)。
不幸的是,他们没有这种魔力,希望您别无选择,只能使用 PMD
or FindBugs
等工具清理代码,这将帮助您检测此类问题以及更多问题。
我使用 OpenJDK 中全新的 Java 对象布局工具 (http://openjdk.java.net/projects/code-tools/jol/) 进行了一些测试。
测试表明,至少在我的 2 台机器上,JVM、月相等。JIT 无法确定某些字段未使用并且不会对其进行优化。
但即使 JIT 可以,它实际上 将 进行特定优化也没有必要。
这是测试:
public static long devNull;
public static void main(String[] args) {
out.println(VM.current().details());
Wasty wasty = new Wasty();
Clean clean = new Clean();
PrintWriter pw = new PrintWriter(out);
for (int i = 0; i < 10_000_000; i++) {
devNull += wasty.doSomething();
devNull += clean.doSomething();
if (i == 0 || i == 9_999_999) {
pw.println(GraphLayout.parseInstance(wasty).toFootprint());
pw.println(GraphLayout.parseInstance(clean).toFootprint());
}
}
pw.close();
}
public class Wasty {
private long _long;
private double _double;
private String _string;
private long used;
private int _int;
private boolean _boolean;
public long doSomething() {
return used++;
}
}
public class Clean {
private long used;
public long doSomething() {
return used++;
}
}
结果是:
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# WARNING | Compressed references base/shifts are guessed by the experiment!
# WARNING | Therefore, computed addresses are just guesses, and ARE NOT RELIABLE.
# WARNING | Make sure to attach Serviceability Agent to get the reliable addresses.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
layout.Wasty@5f205aad footprint:
COUNT AVG SUM DESCRIPTION
1 48 48 layout.Wasty
1 48 (total)
layout.Clean@2f410acfd footprint:
COUNT AVG SUM DESCRIPTION
1 24 24 layout.Clean
1 24 (total)
layout.Wasty@5f205aad footprint:
COUNT AVG SUM DESCRIPTION
1 48 48 layout.Wasty
1 48 (total)
layout.Clean@2f410acfd footprint:
COUNT AVG SUM DESCRIPTION
1 24 24 layout.Clean
1 24 (total)
因此,就 占用空间(内存性能)而言,重要 未使用的字段 因为它们实际上会消耗内存并且可能会破坏对象的布局。针对未使用字段的第二点是 Joop Eggen 已经提到的代码味道。
我的 class 包含一些从未在任何地方使用过的属性(这是我真实场景的 DEMO)。我听说 JVM 优化了我们的 Java 代码。
JVM/编译器是否优化/删除对象未使用的属性?
public class A {
private int unused1 = 100;// never called anywhere inside object
public int unused2 = 999;// never called anywhere in the application
}
我知道我需要好好学习JVM、Compiler和优化。但是需要答案,因为在短时间内我必须决定是从大型代码库(大约 10,000 java 文件)中手动删除所有(尽可能多的)未使用的变量,还是仅依赖于 JVM 优化。
期待一些有趣且富有成效的建议。
TL;DR:不,JVM 编译器 (javac) 不会优化掉未使用的变量。
让我们看一下 javac 编译器生成的字节码。
将其用作测试class:
public class Test {
private int test = 5;
private int test2 = 10;
private String aString = "HelloWorld";
}
产生:
Classfile /C:/Users/Huw/Desktop/Test.class
Last modified 19-Apr-2016; size 331 bytes
MD5 checksum 1c49b13d1d5d8a2c52924b20753122af
Compiled from "Test.java"
public class Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #7.#19 // java/lang/Object."<init>":()V
#2 = Fieldref #6.#20 // Test.test:I
#3 = Fieldref #6.#21 // Test.test2:I
#4 = String #22 // HelloWorld
#5 = Fieldref #6.#23 // Test.aString:Ljava/lang/String;
#6 = Class #24 // Test
#7 = Class #25 // java/lang/Object
#8 = Utf8 test
#9 = Utf8 I
#10 = Utf8 test2
#11 = Utf8 aString
#12 = Utf8 Ljava/lang/String;
#13 = Utf8 <init>
#14 = Utf8 ()V
#15 = Utf8 Code
#16 = Utf8 LineNumberTable
#17 = Utf8 SourceFile
#18 = Utf8 Test.java
#19 = NameAndType #13:#14 // "<init>":()V
#20 = NameAndType #8:#9 // test:I
#21 = NameAndType #10:#9 // test2:I
#22 = Utf8 HelloWorld
#23 = NameAndType #11:#12 // aString:Ljava/lang/String;
#24 = Utf8 Test
#25 = Utf8 java/lang/Object
{
public Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_5
6: putfield #2 // Field test:I
9: aload_0
10: bipush 10
12: putfield #3 // Field test2:I
15: aload_0
16: ldc #4 // String HelloWorld
18: putfield #5 // Field aString:Ljava/lang/String;
21: return
LineNumberTable:
line 1: 0
line 3: 4
line 4: 9
line 5: 15
}
SourceFile: "Test.java"
如您所见,编译器仍然分配字段属性。
所以不,未使用的变量仍将分配指针(对于对象)和内存(对于基元)。
不幸的是,他们没有这种魔力,希望您别无选择,只能使用 PMD
or FindBugs
等工具清理代码,这将帮助您检测此类问题以及更多问题。
我使用 OpenJDK 中全新的 Java 对象布局工具 (http://openjdk.java.net/projects/code-tools/jol/) 进行了一些测试。
测试表明,至少在我的 2 台机器上,JVM、月相等。JIT 无法确定某些字段未使用并且不会对其进行优化。
但即使 JIT 可以,它实际上 将 进行特定优化也没有必要。
这是测试:
public static long devNull;
public static void main(String[] args) {
out.println(VM.current().details());
Wasty wasty = new Wasty();
Clean clean = new Clean();
PrintWriter pw = new PrintWriter(out);
for (int i = 0; i < 10_000_000; i++) {
devNull += wasty.doSomething();
devNull += clean.doSomething();
if (i == 0 || i == 9_999_999) {
pw.println(GraphLayout.parseInstance(wasty).toFootprint());
pw.println(GraphLayout.parseInstance(clean).toFootprint());
}
}
pw.close();
}
public class Wasty {
private long _long;
private double _double;
private String _string;
private long used;
private int _int;
private boolean _boolean;
public long doSomething() {
return used++;
}
}
public class Clean {
private long used;
public long doSomething() {
return used++;
}
}
结果是:
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# WARNING | Compressed references base/shifts are guessed by the experiment!
# WARNING | Therefore, computed addresses are just guesses, and ARE NOT RELIABLE.
# WARNING | Make sure to attach Serviceability Agent to get the reliable addresses.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
layout.Wasty@5f205aad footprint:
COUNT AVG SUM DESCRIPTION
1 48 48 layout.Wasty
1 48 (total)
layout.Clean@2f410acfd footprint:
COUNT AVG SUM DESCRIPTION
1 24 24 layout.Clean
1 24 (total)
layout.Wasty@5f205aad footprint:
COUNT AVG SUM DESCRIPTION
1 48 48 layout.Wasty
1 48 (total)
layout.Clean@2f410acfd footprint:
COUNT AVG SUM DESCRIPTION
1 24 24 layout.Clean
1 24 (total)
因此,就 占用空间(内存性能)而言,重要 未使用的字段 因为它们实际上会消耗内存并且可能会破坏对象的布局。针对未使用字段的第二点是 Joop Eggen 已经提到的代码味道。