- 浏览: 3048937 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (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 16276以前要在Java里实现所谓“静态工具类”(static uti ... -
Java 8的default method与method resolution
2014-03-19 02:23 10453先看看下面这个代码例子, 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 22395(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 21874之前有同学在做龙书(第二版)题目,做到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 48366刚写了个学习JVM用的豆列跟大家分享。 豆列地址:http: ...
相关推荐
这个库提供了丰富的API,使得我们可以轻松地将Java对象转化为JSON字符串,以及将JSON字符串反序列化为Java对象。 ### JSON对象与Java对象的转换 1. **JSON字符串转Java对象** 使用`json-lib`,我们可以将JSON...
3. **性能考虑**:如果频繁进行日期和字符串之间的转换,可以考虑使用线程安全的`DateTimeFormatter`类(Java 8及以上版本提供)来替代`SimpleDateFormat`,以提高程序的性能。 4. **国际化支持**:如果应用程序需要...
在Java编程语言中,字符串是极其重要且常用...通过学习这些源代码,开发者可以加深对Java字符串的理解,并提高编写高效字符串处理代码的能力。在实践中,理解并熟练掌握这些概念对于任何Java开发者来说都是至关重要的。
通过以上分析,我们可以看到使用JDOM库在Java中解析和操作XML字符串的完整过程。这包括了如何将XML字符串转换为可读写的数据结构,以及如何遍历、读取和修改XML文档的元素和属性。这对于在Java应用程序中处理动态...
Java提供了多种方法来生成随机字符串,主要包括使用`java.util.Random`类和`java.security.SecureRandom`类。其中,`SecureRandom`类提供了一种更加安全的方式来生成随机数,它适合于安全性要求较高的应用场合。 ##...
本文将深入探讨如何使用Java将对象转换为JSON以及字符串数组。 首先,我们需要一个库来处理JSON转换,这里提到的"json-lib"是一个广泛使用的Java JSON库,提供了多种类型到JSON的转换功能。在Java中,有两种主要...
java 字符串转16进制 16进制转字符串 将两个ASCII字符合成一个字节; java 字符串转16进制 16进制转字符串 将两个ASCII字符合成一个字节; java 字符串转16进制 16进制转字符串 将两个ASCII字符合成一个字节; java ...
- `new String()`: 使用此构造函数创建一个新的字符串对象,可以传入字符数组或另一个字符串作为参数。 - `""`: 空字符串字面量,表示没有字符的字符串。 2. **获取字符串信息** - `length()`: 返回字符串的长度...
在Java中,我们可以使用`java.util.regex`包中的`Pattern`和`Matcher`类来匹配和操作符合特定模式的字符串。对于提取括号内的内容,一个简单的正则表达式可以是`\((.*?)\)`,它匹配一对括号,并用`.*?`非贪婪地捕获...
这篇博客“Java实现计算字符串表达式”可能讲解了如何利用Java来处理这种问题,虽然具体的实现细节没有提供,但我们可以基于一般的方法和常用的库来探讨这个主题。 首先,计算字符串表达式的基本步骤通常包括以下几...
- **字符串连接**:可以使用`+`运算符或`StringBuilder`/`StringBuffer`类来连接字符串。但`+`运算符在处理大量字符串连接时效率较低,因为它会在每次连接时创建新的`String`对象。 2. **StringBuffer与...
第4节: 揭秘JVM字符串常量池和Java堆-01第4节: 揭秘JVM字符串常量池和Java堆-01第4节: 揭秘JVM字符串常量池和Java堆-01第4节: 揭秘JVM字符串常量池和Java堆-01第4节: 揭秘JVM字符串常量池和Java堆-01第4节: ...
java 字符串工具类 java 字符串工具类java 字符串工具类 java 字符串工具类java 字符串工具类 java 字符串工具类java 字符串工具类 java 字符串工具类java 字符串工具类 java 字符串工具类java 字符串工具类 java ...
标题中的“实用小工具:Java实体类对比、Json对比、字符串动态拼接等功能”涉及到的是在软件开发过程中常用的几个技术点,主要集中在Java后端和前端Vue.js的应用上。这里我们将详细探讨这些知识点: 1. **Java实体...
### Java中截取带汉字的字符串 在Java编程语言中,处理包含中文字符的字符串时,经常遇到的一个问题是如何正确地截取...通过以上方法,我们可以在Java中有效地处理包含中文字符的字符串截取问题,避免出现乱码等情况。
java压缩字符串
当需要对字符串进行修改时,可以使用字符数组。字符数组`char[]`允许我们逐个访问和修改元素。例如,我们可以创建一个字符数组来模拟字符串,并通过更新数组中的元素来“移动”字符串。 3. **控制台输出**: 在...
在Java编程语言中,分解字符串是一项常见的任务,它涉及到对字符串进行分析,将字符串分割成多个子字符串。这个过程通常被称为字符串分割。在Java中,我们主要使用`String`类提供的`split()`方法来实现这一功能。...
在Java编程语言中,对字符串中的字符进行a到z排序是一项常见的操作,特别是在处理文本数据或需要对字母顺序排列的场景。本知识点将详细讲解如何实现这个功能。 首先,我们需要理解字符串在Java中的本质。在Java中,...