- 浏览: 60779 次
- 性别:
- 来自: 北京
最新评论
-
suifeng214:
想问一下 楼主高并发是怎样测试的
java连接池性能测试报告 -
HeartArea:
这个不错,先留着
Tomcat jdbc-pool 与 commons DBCP 的参数对比【翻译全部属性】 -
chaodongyue:
求测试代码
java连接池性能测试报告 -
duzc2:
chgyan 写道 guangyan ?
服务端Mina线程关系和数据流动分析 -
chgyan:
服务端Mina线程关系和数据流动分析
为了研究javac对于String相关代码的字节码优化,我做了如下测试。
测试环境:
$ javac -version
javac 1.6.0_23
$ java -version
java version "1.6.0_23"
OpenJDK Runtime Environment (IcedTea6 1.11pre) (6b23~pre11-0ubuntu1.11.10.2)
OpenJDK 64-Bit Server VM (build 20.0-b11, mixed mode)
1.编写代码:
String name = "ab"+"cd"; String name1 = "ab" + new String("cd"); String name2 = "ab"+1+"cd"; String name3 = "ab"+"c" + new String("d"); StringBuffer name4 = new StringBuffer("e"); name4.append("f").append("g");
2.执行编译: javac StringAdd.java
生成字节码StringAdd.class
3.反编译:javap -c -l -verbose StringAdd > StringAdd.javap
生成反编译文件。
main方法如下(此处只列出主要部分,完整文件见附件):
0: ldc #2; //String abcd
#字符串常量连接,已经过常量折叠。
2: astore_1
3: new #3; //class java/lang/StringBuilder
6: dup
7: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
10: ldc #5; //String ab
12: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: new #7; //class java/lang/String
18: dup
19: ldc #8; //String cd 。
21: invokespecial #9; //Method java/lang/String."<init>":(Ljava/lang/String;)V
24: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokevirtual #10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
30: astore_2
# 通过构造方法建立的String常量没有折叠,但字符串连接操作已被StringBuilder取代
31: ldc #11; //String ab1cd
33: astore_3
# 有类型转换的常量字符串连接操作也被已常量折叠方式优化
34: new #3; //class java/lang/StringBuilder
37: dup
38: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
41: ldc #12; //String abc
43: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
46: new #7; //class java/lang/String
49: dup
50: ldc #13; //String d
52: invokespecial #9; //Method java/lang/String."<init>":(Ljava/lang/String;)V
55: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
58: invokevirtual #10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
61: astore 4
# 同时包含字面常量和通过构造方法构造的String的连接,字面常量部分被已常量折叠方式优化,剩余部分的字符串连接被StringBuilder取代。
63: new #14; //class java/lang/StringBuffer
66: dup
67: ldc #15; //String e
69: invokespecial #16; //Method java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
72: astore 5
74: aload 5
76: ldc #17; //String f
78: invokevirtual #18; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
81: ldc #19; //String g
83: invokevirtual #18; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
# 通过StringBuffer构造字符串没有被优化。
86: pop
87: return
分析:
通过上面对反编译字节码的分析,我们可以看到编译器在编译期对代码的优化特点:
1 常量折叠,将编译期可以计算出的静态结果提前得出,将运行时计算开销降低为0。
2 自动将字符串连接操作优化为StringBuilder类的append方法,以提高连接速度。
3 对于调用方法动态生成的对象无法以常量折叠方式优化
通过分析可以得知:
1 为了提高可读性,将一个字符串常量分割为多个用于排版,不会影响运行效率;
2 为了提高可读性,可以直接使用字符串连接符“+”,替换非线程同步的StringBuilder类,不会对运行时效率产生影响
3 使用单线程下使用StringBuffer反而会降低性能。猜测:1.5以前的版本可能没有区别,或使用StringBuffer更快,因为1.5才开始有StringBuilder类,也不知道1.5以前的编译器会不会使用StringBuffer替换字符串连接操作。
引申:
存在域定义
static final String a="a";
static String b="b";
static final String c=new String("c");
反编译方法内部代码
String name1 = "1"+a;
String name2 = "2"+b;
String name3 = "3"+c;
效果如下:
0: ldc #2; //String 1a
2: astore_1
# 静态常量被直接折叠保存。
3: new #3; //class java/lang/StringBuilder
6: dup
7: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
10: ldc #5; //String 2
12: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: getstatic #7; //Field b:Ljava/lang/String;
18: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_2
# 变量未被折叠
25: new #3; //class java/lang/StringBuilder
28: dup
29: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
32: ldc #9; //String 3
34: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
37: getstatic #10; //Field c:Ljava/lang/String;
40: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
43: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
46: astore_3
# 动态常量未被折叠保存
分析:
1 常量可以被认为运行时不可改变,所以编译时被以常量折叠方式优化。
2 变量和动态生成的常量必须在运行时确定值,所以不能在编译期折叠优化
结论:
如class1 中某个地方直接引用了 class2中的某个final常量,则在编译时会将常量值记入class1的常量池中,或被常量折叠优化。如果class2修改了这个常量并重新编译,运行时class1中的值不会随之变动,而是使用旧的class2的值,导致程序出现不可预期的效果。
所以建议 通过动态赋值方式给常量赋值,如:
final String str = new String(“str1”);
final int one = new Integer(1);
虽然增加了类初始化的时间,但可以保证final值所在class文件更新后其他class不用重新编译就可以使用新的值。(虽然随便修改final定义是不好的。。。)
附件:
用到以下2个类。
public class StringAdd{
public static void main(String[] arg){
String name = "ab"+"cd";
String name1 = "ab" + new String("cd");
String name2 = "ab"+1+"cd";
String name3 = "ab"+"c" + new String("d");
StringBuffer name4 = new StringBuffer("e");
name4.append("f").append("g");
}
}
public class StringAdd2{
static final String a="a";
static String b="b";
static final String c=new String("c");
public static void main(String[] args){
String name1 = "1"+a;
String name2 = "2"+b;
String name3 = "3"+c;
}
}
反编译结果如下:
Compiled from "StringAdd.java"
public class StringAdd extends java.lang.Object
SourceFile: "StringAdd.java"
minor version: 0
major version: 50
Constant pool:
const #1 = Method #21.#30; // java/lang/Object."<init>":()V
const #2 = String #31; // abcd
const #3 = class #32; // java/lang/StringBuilder
const #4 = Method #3.#30; // java/lang/StringBuilder."<init>":()V
const #5 = String #33; // ab
const #6 = Method #3.#34; // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
const #7 = class #35; // java/lang/String
const #8 = String #36; // cd
const #9 = Method #7.#37; // java/lang/String."<init>":(Ljava/lang/String;)V
const #10 = Method #3.#38; // java/lang/StringBuilder.toString:()Ljava/lang/String;
const #11 = String #39; // ab1cd
const #12 = String #40; // abc
const #13 = String #41; // d
const #14 = class #42; // java/lang/StringBuffer
const #15 = String #43; // e
const #16 = Method #14.#37; // java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
const #17 = String #44; // f
const #18 = Method #14.#45; // java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
const #19 = String #46; // g
const #20 = class #47; // StringAdd
const #21 = class #48; // java/lang/Object
const #22 = Asciz <init>;
const #23 = Asciz ()V;
const #24 = Asciz Code;
const #25 = Asciz LineNumberTable;
const #26 = Asciz main;
const #27 = Asciz ([Ljava/lang/String;)V;
const #28 = Asciz SourceFile;
const #29 = Asciz StringAdd.java;
const #30 = NameAndType #22:#23;// "<init>":()V
const #31 = Asciz abcd;
const #32 = Asciz java/lang/StringBuilder;
const #33 = Asciz ab;
const #34 = NameAndType #49:#50;// append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
const #35 = Asciz java/lang/String;
const #36 = Asciz cd;
const #37 = NameAndType #22:#51;// "<init>":(Ljava/lang/String;)V
const #38 = NameAndType #52:#53;// toString:()Ljava/lang/String;
const #39 = Asciz ab1cd;
const #40 = Asciz abc;
const #41 = Asciz d;
const #42 = Asciz java/lang/StringBuffer;
const #43 = Asciz e;
const #44 = Asciz f;
const #45 = NameAndType #49:#54;// append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
const #46 = Asciz g;
const #47 = Asciz StringAdd;
const #48 = Asciz java/lang/Object;
const #49 = Asciz append;
const #50 = Asciz (Ljava/lang/String;)Ljava/lang/StringBuilder;;
const #51 = Asciz (Ljava/lang/String;)V;
const #52 = Asciz toString;
const #53 = Asciz ()Ljava/lang/String;;
const #54 = Asciz (Ljava/lang/String;)Ljava/lang/StringBuffer;;
{
public StringAdd();
LineNumberTable:
line 1: 0
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
LineNumberTable:
line 3: 0
line 4: 3
line 5: 31
line 6: 34
line 7: 63
line 8: 74
line 9: 87
Code:
Stack=4, Locals=6, Args_size=1
0: ldc #2; //String abcd
2: astore_1
3: new #3; //class java/lang/StringBuilder
6: dup
7: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
10: ldc #5; //String ab
12: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: new #7; //class java/lang/String
18: dup
19: ldc #8; //String cd
21: invokespecial #9; //Method java/lang/String."<init>":(Ljava/lang/String;)V
24: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokevirtual #10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
30: astore_2
31: ldc #11; //String ab1cd
33: astore_3
34: new #3; //class java/lang/StringBuilder
37: dup
38: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
41: ldc #12; //String abc
43: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
46: new #7; //class java/lang/String
49: dup
50: ldc #13; //String d
52: invokespecial #9; //Method java/lang/String."<init>":(Ljava/lang/String;)V
55: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
58: invokevirtual #10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
61: astore 4
63: new #14; //class java/lang/StringBuffer
66: dup
67: ldc #15; //String e
69: invokespecial #16; //Method java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
72: astore 5
74: aload 5
76: ldc #17; //String f
78: invokevirtual #18; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
81: ldc #19; //String g
83: invokevirtual #18; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
86: pop
87: return
LineNumberTable:
line 3: 0
line 4: 3
line 5: 31
line 6: 34
line 7: 63
line 8: 74
line 9: 87
}
Compiled from "StringAdd2.java"
public class StringAdd2 extends java.lang.Object
SourceFile: "StringAdd2.java"
minor version: 0
major version: 50
Constant pool:
const #1 = Method #16.#32; // java/lang/Object."<init>":()V
const #2 = String #33; // 1a
const #3 = class #34; // java/lang/StringBuilder
const #4 = Method #3.#32; // java/lang/StringBuilder."<init>":()V
const #5 = String #35; // 2
const #6 = Method #3.#36; // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
const #7 = Field #15.#37; // StringAdd2.b:Ljava/lang/String;
const #8 = Method #3.#38; // java/lang/StringBuilder.toString:()Ljava/lang/String;
const #9 = String #39; // 3
const #10 = Field #15.#40; // StringAdd2.c:Ljava/lang/String;
const #11 = String #21; // b
const #12 = class #41; // java/lang/String
const #13 = String #22; // c
const #14 = Method #12.#42; // java/lang/String."<init>":(Ljava/lang/String;)V
const #15 = class #43; // StringAdd2
const #16 = class #44; // java/lang/Object
const #17 = Asciz a;
const #18 = Asciz Ljava/lang/String;;
const #19 = Asciz ConstantValue;
const #20 = String #17; // a
const #21 = Asciz b;
const #22 = Asciz c;
const #23 = Asciz <init>;
const #24 = Asciz ()V;
const #25 = Asciz Code;
const #26 = Asciz LineNumberTable;
const #27 = Asciz main;
const #28 = Asciz ([Ljava/lang/String;)V;
const #29 = Asciz <clinit>;
const #30 = Asciz SourceFile;
const #31 = Asciz StringAdd2.java;
const #32 = NameAndType #23:#24;// "<init>":()V
const #33 = Asciz 1a;
const #34 = Asciz java/lang/StringBuilder;
const #35 = Asciz 2;
const #36 = NameAndType #45:#46;// append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
const #37 = NameAndType #21:#18;// b:Ljava/lang/String;
const #38 = NameAndType #47:#48;// toString:()Ljava/lang/String;
const #39 = Asciz 3;
const #40 = NameAndType #22:#18;// c:Ljava/lang/String;
const #41 = Asciz java/lang/String;
const #42 = NameAndType #23:#49;// "<init>":(Ljava/lang/String;)V
const #43 = Asciz StringAdd2;
const #44 = Asciz java/lang/Object;
const #45 = Asciz append;
const #46 = Asciz (Ljava/lang/String;)Ljava/lang/StringBuilder;;
const #47 = Asciz toString;
const #48 = Asciz ()Ljava/lang/String;;
const #49 = Asciz (Ljava/lang/String;)V;
{
static final java.lang.String a;
Constant value: String a
static java.lang.String b;
static final java.lang.String c;
public StringAdd2();
LineNumberTable:
line 1: 0
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
LineNumberTable:
line 6: 0
line 7: 3
line 8: 25
line 9: 47
Code:
Stack=2, Locals=4, Args_size=1
0: ldc #2; //String 1a
2: astore_1
3: new #3; //class java/lang/StringBuilder
6: dup
7: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
10: ldc #5; //String 2
12: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: getstatic #7; //Field b:Ljava/lang/String;
18: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_2
25: new #3; //class java/lang/StringBuilder
28: dup
29: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
32: ldc #9; //String 3
34: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
37: getstatic #10; //Field c:Ljava/lang/String;
40: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
43: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
46: astore_3
47: return
LineNumberTable:
line 6: 0
line 7: 3
line 8: 25
line 9: 47
static {};
LineNumberTable:
line 3: 0
line 4: 5
Code:
Stack=3, Locals=0, Args_size=0
0: ldc #11; //String b
2: putstatic #7; //Field b:Ljava/lang/String;
5: new #12; //class java/lang/String
8: dup
9: ldc #13; //String c
11: invokespecial #14; //Method java/lang/String."<init>":(Ljava/lang/String;)V
14: putstatic #10; //Field c:Ljava/lang/String;
17: return
LineNumberTable:
line 3: 0
line 4: 5
}
发表评论
-
在 Eclipse 里使用 Java 6 注解处理器
2012-06-29 23:49 3572在 Eclipse 里使用 Java 6 注解处理器 ... -
JDK 7 特性
2012-06-29 00:09 1434JDK 7 特性 虚拟机 JSR 292:支持动 ... -
Java SE 7 和 JDK 7 兼容性
2012-06-29 00:08 5989Java SE 7 和 JDK 7 兼容性 兼 ... -
java的Integer缓冲
2012-05-28 15:14 2534java.lang.Integer.valueOf ... -
DBCP和Tomcat jdbc-pool 对比
2012-05-23 17:29 3497一 性能 低并发情况下DBCP略强于jdbc-pool,高 ... -
Tomcat jdbc-pool 与 commons DBCP 的参数对比【翻译全部属性】
2012-05-22 17:33 3901通用属性 属性名 描述(DBCP/To ... -
不同并发量下Tomcat jdbc-pool和DBCP连接池的性能和包依赖
2012-05-22 11:47 3081最小连接5,最大连接50,无延迟,排除预热,循环查询“sele ... -
[翻译]DBCP释放历史
2012-05-22 00:28 1492版本日期 描述 ... -
[翻译]Why another connection pool project?为什么还需要另外的连接池项目?
2012-05-22 00:01 1759英文原文: http://www.tomcatexpert.c ... -
(翻译)Tomcat JDBC 连接池
2012-05-21 17:52 4372介绍 org.apache.tomcat.jdbc. ... -
java连接池性能测试报告
2012-05-21 13:39 11356一 当前问题 1 ... -
JAVA NIO和MINA发送数据过程解析
2012-05-11 14:30 4398NIO发送数据过程: 1 将 ... -
开发环境Eclipse和GameServer的JVM调优
2012-05-11 10:03 1598杜天微 2012-3-29 系统信息: XP SP ... -
为了研究变量声明在for语句块前和for语句块内部的区别
2012-05-11 09:54 1230编译并反编译BeforeFor和InFor,对比如图《java ... -
服务端Mina线程关系和数据流动分析
2012-05-11 09:50 3657一 线程关系 NioSocketAcceptor类 线 ...
相关推荐
对于静态的、编译期可确定的字符串连接,使用`+`操作符可能足够;但对于运行时动态生成的字符串连接,特别是涉及大量连接或者在多线程环境中,`StringBuffer`或`StringBuilder`将是更好的选择。合理地使用这些工具,...
Java中字符串连接可以通过+操作符实现,但这种方式在循环中效率较低,因为它会产生大量的临时对象。intern()方法是一个本地方法,当调用str.intern()时,如果常量池中存在相同的字符串,则返回该字符串的引用;否则...
// 编译期自动优化为 s3 = "abcdef"; System.out.println(s1 == s2); // true System.out.println(s1 == s3); // true System.out.println(s2 == s3); // true ``` 这段代码显示了 `s1`、`s2` 和 `s3` 实际上指向...
对于 `str2` 的创建,虽然使用了字符串连接操作,但由于 `"a"` 和 `"bc"` 都是字符串常量,在编译器优化的过程中会被合并成 `"abc"` 并指向字符串池中的同一个对象。 **示例 2**: ```java public class Test { ...
这是一种三引号(````...```)包围的字符串,可以保留原始的缩进和换行,方便编写格式化的文本,如Markdown或SQL语句。 五、强引用的弱化(`java.lang.ref.Cleaner`) 在OpenJDK 11中,`java.lang.ref.Cleaner`作为`...
此外,JDK 11还增强了字符串处理能力,比如添加了isBlank()、strip()和stripIndent()方法,帮助开发者更加方便地处理字符串中的空白字符。同时,对于日期和时间API(java.time),增加了对ISO-8601日期时间格式的...
直接使用字符串字面量`"abcdf"`在编译期即可优化为单个字符串;而使用加法运算符连接的字符串在运行期逐个拼接,效率较低。 #### 29. classPath的作用 `classPath`环境变量指定了Java虚拟机查找用户类文件的路径,...
7. **字符串操作**:String类的不可变性,StringBuilder和StringBuffer的区别,常用字符串方法的理解和应用。 8. **泛型**:泛型的概念,通配符(?),泛型类和泛型方法的使用。 9. **多线程**:线程的创建...
静态绑定(早绑定)发生在编译期,如方法的重载;动态绑定(晚绑定)发生在运行期,如方法的重写。 36. **SQL优化**: 优化包括选择合适的数据类型、避免全表扫描、使用索引、避免在索引列上使用函数、减少JOIN...
- **面向切面编程(AOP)**:通过预编译方式或运行期动态代理实现横切关注点的方法织入。 - **声明式事务管理**:通过配置而非代码实现事务边界控制。 **模块介绍**: - **Spring Core**:提供依赖注入功能的核心...
反射允许程序在运行时动态访问类、方法和字段,而注解则提供了一种元数据方式,可以在编译期或运行期处理。面试中可能会探讨如何使用反射进行动态类型操作或利用注解实现元编程。 11. **设计模式** 常见的设计...
注解提供了一种标准的方法来标记代码,用于元数据的添加或某些编译期/运行期的行为改变。例如,@Override 注解用于标记方法覆盖。 **9. 枚举** 枚举类型提供了一种定义固定数量常量的有效方式,同时提供了多种实用...
- 泛型是通过编译期擦除实现的,即编译后的字节码不包含类型参数的信息。 6. **反射机制** - 反射机制允许程序在运行时获取类的信息并操作类的对象。 - 主要通过`Class`类来获取对象的类信息,然后通过`Field`、...
5. **字符串**:String类的常用方法,如concat()、substring()、indexOf()等。 然后,进一步深入,可能会包含: 6. **面向对象编程**:类的定义、构造器、封装、继承、多态等核心概念。 7. **异常处理**:try-catch...
也有分析认为,谷歌并不想做一个简单的手机终端制造商或者软件平台开发商,而意在一统传统互联网和 移 动互联网。----------------------------------- Android 编程基础 4 Android Android Android Android 手机新...