- 浏览: 3015661 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (430)
- Programming Languages (23)
- Compiler (20)
- Virtual Machine (57)
- Garbage Collection (4)
- HotSpot VM (26)
- Mono (2)
- SSCLI Rotor (1)
- Harmony (0)
- DLR (19)
- Ruby (28)
- C# (38)
- F# (3)
- Haskell (0)
- Scheme (1)
- Regular Expression (5)
- Python (4)
- ECMAScript (2)
- JavaScript (18)
- ActionScript (7)
- Squirrel (2)
- C (6)
- C++ (10)
- D (2)
- .NET (13)
- Java (86)
- Scala (1)
- Groovy (3)
- Optimization (6)
- Data Structure and Algorithm (3)
- Books (4)
- WPF (1)
- Game Engines (7)
- 吉里吉里 (12)
- UML (1)
- Reverse Engineering (11)
- NSIS (4)
- Utilities (3)
- Design Patterns (1)
- Visual Studio (9)
- Windows 7 (3)
- x86 Assembler (1)
- Android (2)
- School Assignment / Test (6)
- Anti-virus (1)
- REST (1)
- Profiling (1)
- misc (39)
- NetOA (12)
- rant (6)
- anime (5)
- Links (12)
- CLR (7)
- GC (1)
- OpenJDK (2)
- JVM (4)
- KVM (0)
- Rhino (1)
- LINQ (2)
- JScript (0)
- Nashorn (0)
- Dalvik (1)
- DTrace (0)
- LLVM (0)
- MSIL (0)
最新评论
-
mldxs:
虽然很多还是看不懂,写的很好!
虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩 -
HanyuKing:
Java的多维数组 -
funnyone:
Java 8的default method与method resolution -
ljs_nogard:
Xamarin workbook - .Net Core 中不 ...
LINQ的恶搞…… -
txm119161336:
allocatestlye1 顺序为 // Fields o ...
最近做的两次Java/JVM分享的概要
把先前在论坛回复的一些帖打捞进来。这篇的原帖是:JAVA6可以使用字符串累加
下面是回复内容,带补充。
=================================================================
<< 我肯定没有这样说过…这帖里说的东西都跟HotSpot(或者其它JVM实现)一点关系都没有,还在Java源码级编译器层次上…
<< 这个也不对…要分情况说。前面也有说了,String的+本来就是用StringBuilder(JDK 5或以上)或StringBuffer(JDK 1.4或以下)实现的。
关键问题是:是不是同一个StringBuilder/StringBuffer。在循环里用+=之类的方式来拼接字符串的问题就出在每轮循环里都new了一个StringBuilder/StringBuffer来做拼接,然后toString()完就抛弃了,等下轮循环进来又再new一个。
嗯,从Java字节码一层能看到的情况看,确实是如此的。从StringBuffer转换到StringBuilder也是JDK5的时候做的事情了,而不是JDK6才开始的。
让我演示一下某代码在JDK 1.0.2上编译出来的样子:
<< 留意一下Class文件的版本号,这个是用JDK 1.0.2里的javac编译的,如假包换。
看到了么,main()方法里的字节码根本就没有字符串拼接的动作发生,对吧?
然后用JDK 1.0.2的javac -O来编译:
加了-O参数之后main()方法里啥也不剩了,只有个return。
这才是Java层的“优化”…
然则从Sun的JDK 1.3开始javac就忽略-O参数了。现在大家用JDK6里的javac就看不到这种效果。
================================================
换个例子,
还是用JDK 1.0.2里的javac,不带-O参数来编译:
当时的Java就已经是用StringBuffer来实现字符串拼接(+)的了。
这也毫无出奇之处,在Java语言规范里就有这么写:
顺带一提现在在JDK6里用的是Java语言规范第三版,而JDK7即将使用的是Java语言规范第四版(还没出)。
================================================
JVM的实现内部也有一些针对字符串拼接的优化的。但那些看Java字节码是看不出来的——在这个抽象层之下,屏蔽了“优化”这种细节。
例如说JRockit里有特制的jrockit.vm.StringMaker类专门用来优化字符串拼接,JRockit的JIT编译器会识别出StringBuilder的使用模式,发现符合条件的时候就把一些StringBuilder“悄悄的”转换为这种特别的StringMaker类来处理。
大家用得比较多的Oracle/Sun的HotSpot VM的比较新的版本里也有类似的优化,可以把符合条件的一些相邻的StringBuilder合并为一个,用于优化这种场景:
(假定变量a在后面就没有再被使用过了)
本来这段代码会被javac编译为等价于下面的代码:
而被HotSpot的JIT编译器优化后,会变成类似下面的样子:
也就是把相邻的StringBuilder合并掉了。再次留意这个例子的前提是变量a在后面就没有被用过了,只有变量b在后面还有被用到。
控制这个优化的启动参数是OptimizeStringConcat,如果用Oracle JDK 6 update 21以上的版本的话可以试试对比开启它和关闭它的效果。
这才是“JVM做的优化”。字节码层面就能看出来的所谓“优化”根本还没到JVM那层。
下面是回复内容,带补充。
=================================================================
javabkb 写道
听过sajia老师讲的视频 记得1.4后的hotspot就有这个编译优化效果了
<< 我肯定没有这样说过…这帖里说的东西都跟HotSpot(或者其它JVM实现)一点关系都没有,还在Java源码级编译器层次上…
javabkb 写道
但是一直有个问题,即使是编译优化了,用加号拼接的效率还是不如用stringbuilder的
<< 这个也不对…要分情况说。前面也有说了,String的+本来就是用StringBuilder(JDK 5或以上)或StringBuffer(JDK 1.4或以下)实现的。
关键问题是:是不是同一个StringBuilder/StringBuffer。在循环里用+=之类的方式来拼接字符串的问题就出在每轮循环里都new了一个StringBuilder/StringBuffer来做拼接,然后toString()完就抛弃了,等下轮循环进来又再new一个。
sswh 写道
JDK6并没有特别优化。
嗯,从Java字节码一层能看到的情况看,确实是如此的。从StringBuffer转换到StringBuilder也是JDK5的时候做的事情了,而不是JDK6才开始的。
让我演示一下某代码在JDK 1.0.2上编译出来的样子:
public class X { public static void main(String[] args) { String a = "alpha"; String b = "beta"; String c = "charlie"; String d = a + b + c; } }
Compiled from "X.java" public class X extends java.lang.Object SourceFile: "X.java" minor version: 3 major version: 45 Constant pool: const #1 = String #16; // alpha const #2 = String #8; // beta const #3 = String #22; // charlie const #4 = class #17; // java/lang/Object const #5 = class #10; // X const #6 = Method #4.#7; // java/lang/Object."<init>":()V const #7 = NameAndType #20:#23;// "<init>":()V const #8 = Asciz beta; const #9 = Asciz ConstantValue; const #10 = Asciz X; const #11 = Asciz Exceptions; const #12 = Asciz LineNumberTable; const #13 = Asciz SourceFile; const #14 = Asciz LocalVariables; const #15 = Asciz Code; const #16 = Asciz alpha; const #17 = Asciz java/lang/Object; const #18 = Asciz main; const #19 = Asciz ([Ljava/lang/String;)V; const #20 = Asciz <init>; const #21 = Asciz X.java; const #22 = Asciz charlie; const #23 = Asciz ()V; { public static void main(java.lang.String[]); Code: Stack=1, Locals=4, Args_size=1 0: ldc #1; //String alpha 2: astore_1 3: ldc #2; //String beta 5: astore_2 6: ldc #3; //String charlie 8: astore_3 9: return LineNumberTable: line 3: 0 line 4: 3 line 5: 6 line 2: 9 public X(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #6; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 }
<< 留意一下Class文件的版本号,这个是用JDK 1.0.2里的javac编译的,如假包换。
看到了么,main()方法里的字节码根本就没有字符串拼接的动作发生,对吧?
然后用JDK 1.0.2的javac -O来编译:
Compiled from "X.java" public class X extends java.lang.Object SourceFile: "X.java" minor version: 3 major version: 45 Constant pool: const #1 = class #11; // java/lang/Object const #2 = class #6; // X const #3 = Method #1.#4; // java/lang/Object."<init>":()V const #4 = NameAndType #14:#16;// "<init>":()V const #5 = Asciz ConstantValue; const #6 = Asciz X; const #7 = Asciz Exceptions; const #8 = Asciz SourceFile; const #9 = Asciz LocalVariables; const #10 = Asciz Code; const #11 = Asciz java/lang/Object; const #12 = Asciz main; const #13 = Asciz ([Ljava/lang/String;)V; const #14 = Asciz <init>; const #15 = Asciz X.java; const #16 = Asciz ()V; { public static void main(java.lang.String[]); Code: Stack=0, Locals=1, Args_size=1 0: return public X(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #3; //Method java/lang/Object."<init>":()V 4: return }
加了-O参数之后main()方法里啥也不剩了,只有个return。
这才是Java层的“优化”…
然则从Sun的JDK 1.3开始javac就忽略-O参数了。现在大家用JDK6里的javac就看不到这种效果。
================================================
换个例子,
public class Y { public static void main(String[] args) { String a = "alpha"; String b = "beta"; String c = "charlie"; String d = a + b + c; System.out.println(d); } }
还是用JDK 1.0.2里的javac,不带-O参数来编译:
Compiled from "Y.java" public class Y extends java.lang.Object SourceFile: "Y.java" minor version: 3 major version: 45 Constant pool: const #1 = String #32; // alpha const #2 = String #22; // beta const #3 = String #45; // charlie const #4 = class #35; // java/lang/StringBuffer const #5 = class #36; // java/lang/Object const #6 = class #25; // java/io/PrintStream const #7 = class #24; // Y const #8 = class #42; // java/lang/System const #9 = Method #5.#18; // java/lang/Object."<init>":()V const #10 = Method #4.#17; // java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; const #11 = Field #8.#15; // java/lang/System.out:Ljava/io/PrintStream; const #12 = Method #4.#18; // java/lang/StringBuffer."<init>":()V const #13 = Method #6.#16; // java/io/PrintStream.println:(Ljava/lang/String;)V const #14 = Method #4.#19; // java/lang/StringBuffer.toString:()Ljava/lang/String; const #15 = NameAndType #33:#41;// out:Ljava/io/PrintStream; const #16 = NameAndType #20:#34;// println:(Ljava/lang/String;)V const #17 = NameAndType #44:#39;// append:(Ljava/lang/String;)Ljava/lang/StringBuffer; const #18 = NameAndType #40:#46;// "<init>":()V const #19 = NameAndType #31:#21;// toString:()Ljava/lang/String; const #20 = Asciz println; const #21 = Asciz ()Ljava/lang/String;; const #22 = Asciz beta; const #23 = Asciz ConstantValue; const #24 = Asciz Y; const #25 = Asciz java/io/PrintStream; const #26 = Asciz Exceptions; const #27 = Asciz LineNumberTable; const #28 = Asciz SourceFile; const #29 = Asciz LocalVariables; const #30 = Asciz Code; const #31 = Asciz toString; const #32 = Asciz alpha; const #33 = Asciz out; const #34 = Asciz (Ljava/lang/String;)V; const #35 = Asciz java/lang/StringBuffer; const #36 = Asciz java/lang/Object; const #37 = Asciz main; const #38 = Asciz ([Ljava/lang/String;)V; const #39 = Asciz (Ljava/lang/String;)Ljava/lang/StringBuffer;; const #40 = Asciz <init>; const #41 = Asciz Ljava/io/PrintStream;; const #42 = Asciz java/lang/System; const #43 = Asciz Y.java; const #44 = Asciz append; const #45 = Asciz charlie; const #46 = Asciz ()V; { public static void main(java.lang.String[]); Code: Stack=2, Locals=5, Args_size=1 0: ldc #1; //String alpha 2: astore_1 3: ldc #2; //String beta 5: astore_2 6: ldc #3; //String charlie 8: astore_3 9: new #4; //class java/lang/StringBuffer 12: dup 13: invokespecial #12; //Method java/lang/StringBuffer."<init>":()V 16: aload_1 17: invokevirtual #10; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; 20: aload_2 21: invokevirtual #10; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; 24: aload_3 25: invokevirtual #10; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; 28: invokevirtual #14; //Method java/lang/StringBuffer.toString:()Ljava/lang/String; 31: astore 4 33: getstatic #11; //Field java/lang/System.out:Ljava/io/PrintStream; 36: aload 4 38: invokevirtual #13; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 41: return LineNumberTable: line 3: 0 line 4: 3 line 5: 6 line 6: 9 line 7: 33 line 2: 41 public Y(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #9; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 }
当时的Java就已经是用StringBuffer来实现字符串拼接(+)的了。
这也毫无出奇之处,在Java语言规范里就有这么写:
Java语言规范第一版 写道
15.17.1.2 Optimization of String Concatenation
An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class (§20.13) or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.
For primitive objects, an implementation may also optimize away the creation of a wrapper object by converting directly from a primitive type to a string.
An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class (§20.13) or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.
For primitive objects, an implementation may also optimize away the creation of a wrapper object by converting directly from a primitive type to a string.
顺带一提现在在JDK6里用的是Java语言规范第三版,而JDK7即将使用的是Java语言规范第四版(还没出)。
================================================
JVM的实现内部也有一些针对字符串拼接的优化的。但那些看Java字节码是看不出来的——在这个抽象层之下,屏蔽了“优化”这种细节。
例如说JRockit里有特制的jrockit.vm.StringMaker类专门用来优化字符串拼接,JRockit的JIT编译器会识别出StringBuilder的使用模式,发现符合条件的时候就把一些StringBuilder“悄悄的”转换为这种特别的StringMaker类来处理。
大家用得比较多的Oracle/Sun的HotSpot VM的比较新的版本里也有类似的优化,可以把符合条件的一些相邻的StringBuilder合并为一个,用于优化这种场景:
String a = x + y + z; String b = a + x + y;
(假定变量a在后面就没有再被使用过了)
本来这段代码会被javac编译为等价于下面的代码:
String a = new StringBuilder().append(x).append(y).append(z).toString(); String b = new StringBuilder().append(a).append(x).append(y).toString();
而被HotSpot的JIT编译器优化后,会变成类似下面的样子:
String b = new StringBuilder().append(x).append(y).append(z).append(x).append(y).toString();
也就是把相邻的StringBuilder合并掉了。再次留意这个例子的前提是变量a在后面就没有被用过了,只有变量b在后面还有被用到。
控制这个优化的启动参数是OptimizeStringConcat,如果用Oracle JDK 6 update 21以上的版本的话可以试试对比开启它和关闭它的效果。
这才是“JVM做的优化”。字节码层面就能看出来的所谓“优化”根本还没到JVM那层。
发表评论
-
The Prehistory of Java, HotSpot and Train
2014-06-02 08:18 0http://cs.gmu.edu/cne/itcore/vi ... -
MSJVM and Sun 1.0.x/1.1.x
2014-05-20 18:50 0当年的survey paper: http://www.sym ... -
Sun JDK1.4.2_28有TieredCompilation
2014-05-12 08:48 0原来以前Sun的JDK 1.4.2 update 28就已经有 ... -
IBM JVM notes (2014 ver)
2014-05-11 07:16 0Sovereign JIT http://publib.bou ... -
class data sharing by Apple
2014-03-28 05:17 0class data sharing is implement ... -
Java 8与静态工具类
2014-03-19 08:43 16139以前要在Java里实现所谓“静态工具类”(static uti ... -
Java 8的default method与method resolution
2014-03-19 02:23 10332先看看下面这个代码例子, interface IFoo { ... -
HotSpot Server VM与Server Class Machine
2014-02-18 13:21 0HotSpot VM历来有Client VM与Server V ... -
Java 8的lambda表达式在OpenJDK8中的实现
2014-02-04 12:08 0三月份JDK8就要发布首发了,现在JDK8 release c ... -
GC stack map与deopt stack map的异同
2014-01-08 09:56 0两者之间不并存在包含关系。它们有交集,但也各自有特别的地方。 ... -
HotSpot Server Compiler与data-flow analysis
2014-01-07 17:41 0http://en.wikipedia.org/wiki/Da ... -
字符串的一般封装方式的内存布局 (1): 元数据与字符串内容,整体还是分离?
2013-11-07 17:44 22250(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局
2013-11-01 12:55 0(Disclaimer:未经许可请 ... -
关于string,内存布局,C++ std::string,CoW
2013-10-30 20:45 0(Disclaimer:未经许可请 ... -
对C语义的for循环的基本代码生成模式
2013-10-19 23:12 21740之前有同学在做龙书(第二版)题目,做到8.4的练习,跟我对答案 ... -
Java的instanceof是如何实现的
2013-09-22 16:57 0Java语言规范,Java SE 7版 http://docs ... -
oop、klass、handle的关系
2013-07-30 17:34 0oopDesc及其子类的实例 oop : oopDesc* ... -
Nashorn各种笔记
2013-07-15 17:03 0http://bits.netbeans.org/netbea ... -
《深入理解Java虚拟机(第二版)》书评
2013-07-08 19:19 0值得推荐的中文Java虚拟机入门书 感谢作者赠与的样书,以下 ... -
豆列:从表到里学习JVM实现
2013-06-13 14:13 48112刚写了个学习JVM用的豆列跟大家分享。 豆列地址:http: ...
相关推荐
里面有详细的json字符串和java对象(JavaBean,HashMap,ArrayList,Array...)的互相转化
实验9:Java数组与字符串.doc
java 用递归实现字符串反转 java 用递归实现字符串反转
Java第6章 字符串 含源代码 Java第6章 字符串 含源代码 Java第6章 字符串 含源代码 Java第6章 字符串 含源代码 Java第6章 字符串 含源代码
在JAVA中拼接两个字符串的最简便的方式就是使用操作符”+”了。如果你用”+”来连接固定长度的字符串,可能性能上会稍受影响,但是如果你是在 循环中来”+”多个串的话,性能将指数倍的下降。假设有一个字符串,我们...
java 字符串转16进制 16进制转字符串 将两个ASCII字符合成一个字节; java 字符串转16进制 16进制转字符串 将两个ASCII字符合成一个字节; java 字符串转16进制 16进制转字符串 将两个ASCII字符合成一个字节; java ...
Java获取随机字符串Java获取随机字符串Java获取随机字符串
在Java中对于字符串数组的排序,我们可以使用Arrays.sort(String[])方法很便捷的进行排序。例如: String[] arrays = new String[] { gyu, sdf, zf, 大同, 收到, 地方, 三等分, 的人, 反对高铁, 泛代数, 上的投入...
java压缩字符串
Java分割字符串
JAVA日期与字符串的转换 1) 将一个Date类型的变量转换为字符串 2) 将一个字符串转换Date类型的变量
第6章 字符串和包装类;本章目标;字符串String;Strings are Imm;字符串对象的创建方法;Interned String;Interned String;Interned String;取得字符串的长度;按照位置取得指定字符;遍历字符串;练习;下标越界异常;获取...
java解析xml字符串,添加属性,更新属性等
java字符串处理取出括号内的字符串 都是我自己试过可以用的j
使用Java操作JSON字符串对象,使用Java操作JSON字符串对象
java代码-使用java解决从字符串中解析二维数组的源代码 ——学习参考资料:仅用于个人学习使用!
主要用于将java对象转换为json对象字符串,或字符串数组。
Java中如何使用字符串来实例化类以及调用类
java 插入新的字符串 java 插入新的字符串 java 插入新的字符串
4、该工具类采用了Java的开发语言,代码清晰易懂,可以快速实现字符串转换的功能,无论是初学者还是有经验的Java开发人员都可以轻松掌握。 5、使用方法: 5.1、可以单独复制一个单子,粘贴到对应的参数上,执行...