这篇文章也是个回帖,继续用来偷懒发blog
=====================================================
一位朋友给了下面这段代码(在他给的代码中略作修改,避免了引入Random类、Integer装箱类导致不必要的因素),提出了2个问题:1.for (int i = 0, n = list.size(); i < n; i++)的写法是否会比for (int i = 0; i < list.size(); i++)更快?2.为何这段代码在Server VM下测出来的速度比Client VM还慢?
public class Client1 {
public static void main(String[] args) {
List<Object> list = new ArrayList<Object>();
Object obj = new Object();
// 填充数据
for (int i = 0; i < 200000; i++) {
list.add(obj);
}
long start;
start = System.nanoTime();
// 初始化时已经计算好条件
for (int i = 0, n = list.size(); i < n; i++) {
}
System.out.println("判断条件中计算:" + (System.nanoTime() - start) + " ns");
start = System.nanoTime();
// 在判断条件中计算
for (int i = 0; i < list.size(); i++) {
}
System.out.println("判断条件中计算:" + (System.nanoTime() - start) + " ns");
}
}
首先来看看在代码最终执行时,for (int i = 0, n = list.size(); i < n; i++)的写法是否会比for (int i = 0; i < list.size(); i++)更快,用于测试的虚拟机是:
D:\_DevSpace\jdk1.7.0\fastdebug\bin>java -version
java version "1.7.0-ea-fastdebug"
Java(TM) SE Runtime Environment (build 1.7.0-ea-fastdebug-b127)
Java HotSpot(TM) Client VM (build 20.0-b06-fastdebug, mixed mode)
调试参数就加了一个-XX:+PrintAssembly,用于输出JIT后的汇编代码,虚拟机默认是Client。
for (int i = 0, n = list.size(); i < n; i++)的循环体是
0x01fcd554: inc %edx ; OopMap{[60]=Oop off=245}
;*if_icmplt
; - Client1::main@63 (line 17)
0x01fcd555: test %eax,0x1b0100 ; {poll}
0x01fcd55b: cmp %eax,%edx
;; 124 branch [LT] [B5]
0x01fcd55d: jl 0x01fcd554 ;*if_icmplt
; - Client1::main@63 (line 17)
变量i放在edx中,变量n放在eax中,inc指令对应i++(其实被优化成++i了),test指令是在回边处进行safepoint轮询(safepoint是用于进入GC,停顿线程),cmp是比较n和i的值,jl就是当i<n的时候进行跳转,跳转的地址是回到inc指令。
for (int i = 0; i < list.size(); i++)的循环体是:
0x01b6d610: inc %esi
;; block B7 [110, 118]
0x01b6d611: mov %esi,0x50(%esp)
0x01b6d615: mov 0x3c(%esp),%esi
0x01b6d619: mov %esi,%ecx ;*invokeinterface size
; - Client1::main@113 (line 23)
0x01b6d61b: mov %esi,0x3c(%esp)
0x01b6d61f: nop
0x01b6d620: nop
0x01b6d621: nop
0x01b6d622: mov $0xffffffff,%eax ; {oop(NULL)}
0x01b6d627: call 0x01b2b210 ; OopMap{[60]=Oop off=460}
;*invokeinterface size
; - Client1::main@113 (line 23)
; {virtual_call}
0x01b6d62c: nop ; OopMap{[60]=Oop off=461}
;*if_icmplt
; - Client1::main@118 (line 23)
0x01b6d62d: test %eax,0x160100 ; {poll}
0x01b6d633: mov 0x50(%esp),%esi
0x01b6d637: cmp %eax,%esi
;; 224 branch [LT] [B8]
0x01b6d639: jl 0x01b6d610 ;*if_icmplt
; - Client1::main@118 (line 23)
可以看到,除了上面原有的几条指令外,确实还多了一次invokeinterface方法调用(这里发生了方法调用,但是基本上没有分派的开销,因为inline cache是能起作用的),执行的方法是size,方法接收者是list对象,除此之外,其他指令都和上面的循环体一致。所以至少在HotSpot Client VM中,第一种循环的写法是能提高性能的。
但是这个结论并不是所有情况都能成立,譬如这里把list对象从ArrayList换成一个普通数组,把list.size()换成list.length。那将可以观察到两种写法输出的循环体是完全一样的(都和前面第一段汇编的循环一样),因为虚拟机不能保证ArrayList的size()方法调用一次和调用N次是否会产生不同的影响,但是对数组的length属性则可以保证这一点。也就是for (int i = 0, n = list.length; i < n; i++)和for (int i = 0; i < list.length; i++)的性能是没有什么差别的。
再来看看ServerVM和ClientVM中这2段代码JIT后的差别,本想直接对比汇编代码,但ServerVM经过Reordering后,代码就完全混乱了,很难和前面的比较,不过我们还是可以注意到两者编译过程的不同:
这是Client VM的编译过程
VM option '+PrintCompilation'
169 1 java.lang.String::hashCode (67 bytes)
172 2 java.lang.String::charAt (33 bytes)
174 3 java.lang.String::indexOf (87 bytes)
179 4 java.lang.Object::<init> (1 bytes)
185 5 java.util.ArrayList::add (29 bytes)
185 6 java.util.ArrayList::ensureCapacityInternal (26 bytes)
186 1% Client1::main @ 21 (79 bytes)
这是Server VM的编译过程
VM option '+PrintCompilation'
203 1 java.lang.String::charAt (33 bytes)
218 2 java.util.ArrayList::add (29 bytes)
218 3 java.util.ArrayList::ensureCapacityInternal (26 bytes)
221 1% Client1::main @ 21 (79 bytes)
230 1% made not entrant Client1::main @ -2 (79 bytes)
231 2% Client1::main @ 51 (79 bytes)
233 2% made not entrant Client1::main @ -2 (79 bytes)
233 3% Client1::main @ 65 (79 bytes)
ServerVM中OSR编译发生了3次,丢弃了其中2次(made not entrant的输出),换句话说,就是main()的每个循环JIT编译器都要折腾一下子。当然这并不是ServerVM看起来比ClientVM看起来慢的唯一原因。ServerVM的优化目的是为了长期执行生成尽可能高度优化的执行代码,为此它会进行各种努力:譬如丢弃以前的编译成果、在解释器或者低级编译器(如果开启多层编译的话)收集性能信息等等,这些手段在代码实际执行时是必要和有效的,但是在microbenchmark中就会显得很多余并且有副作用。因此写microbenchmark来测试Java代码的性能,经常会出现结果失真。
分享到:
相关推荐
while循环语句 sql server
这个 Java 循环语句练习题文档旨在帮助初学者熟悉 Java 语言的循环语句,包括 for 循环、while 循环、do-while 循环等。通过练习题的解答,读者可以熟悉 Java 语言的基本语法和循环语句的使用方法。 一、for 循环...
PB脚本中SQL语句写法与SQL中语句写法对照 PB脚本中SQL语句写法与SQL中语句写法对照是非常重要的...PB脚本中SQL语句写法与SQL中语句写法对照是非常重要的知识点,它们之间的差异和相似之处都需要我们认真研究和学习。
SQL Server Native Client 10.0 是微软推出的一款专门用于与SQL Server 2008及后续版本交互的客户端库,它集成了ODBC(Open Database Connectivity)和OLE DB(Object Linking and Embedding, Database)接口。...
1. **性能优化**:SQL Server Native Client 10.0 针对SQL Server进行了优化,提供了快速的数据传输和查询执行。 2. **支持新特性**:包括对SQL Server 2008引入的新特性如FILESTREAM、透明数据加密(TDE)、Change ...
SQL Server Native Client 11(简称sqlncli_11)是Microsoft开发的一款用于与SQL Server交互的客户端库,尤其适用于需要高性能数据访问的应用程序。它整合了ODBC(Open Database Connectivity)和OLE DB(Object ...
综上所述,Microsoft SQL Server 2005 Native Client是一个全面的数据访问组件,为开发者提供了多种方式来连接和操作SQL Server,同时提供了高级功能和性能优化,确保了应用程序与数据库之间的高效通信。无论是在...
SQL循环语句主要包括WHILE循环和FOR循环,它们允许我们在满足特定条件时重复执行一段代码块。在PL/SQL和T-SQL中,还有BEGIN-END结构来定义代码块。 1. WHILE循环:在满足特定条件时反复执行代码块,直到条件不再...
Microsoft SQL Server 2008 Native Client是在Windows 10操作系统上使用的一种数据库连接组件,它为应用程序提供了与SQL Server交互的能力。这个组件是专为SQL Server设计的,旨在提高性能、安全性和兼容性,特别是...
SQL Server Native Client 10.0 是微软针对SQL Server数据库管理系统推出的一款客户端接口,主要用于在Windows平台上连接和操作SQL Server 2008及其R2版本。这个压缩包"SQL Server Native Client 10.0.zip"包含了...
这个工具集提供了多种功能,使得用户能够有效地管理和操作SQL Server数据库,包括数据查询、数据导入导出、数据库维护以及性能监控等。在了解SQL Server 2008 Client之前,我们需要先知道SQL Server 2008的基本概念...
SSMS提供了一个图形界面,用于执行SQL语句、创建和修改数据库对象、监控服务器性能以及配置服务器和数据库设置。用户可以通过SSMS进行以下操作: - **连接到服务器**:输入服务器名称和身份验证信息,建立与SQL ...
SQL Server Native Client是微软开发的一款用于访问SQL Server数据库的客户端组件,它包含了ODBC(Open Database Connectivity)驱动程序和OLE DB提供程序。这个压缩包包含了9.0到11.0版本的32位和64位版本,适用于...
它的客户端组件包括一系列实用程序和接口,使得用户能够远程连接到SQL Server服务器,执行SQL语句,管理数据库对象,如表、视图、存储过程等,以及进行数据备份和恢复操作。 1. **SQL Server Management Studio ...
标题中的“win10可用的sql server 2008 navicat client”指的是在Windows 10操作系统中,能够与SQL Server 2008兼容的Navicat客户端工具。Navicat是一款强大的数据库管理和开发工具,它支持多种数据库系统,包括...
本压缩包文件“易语言源码循环语句的用法之速度比较.rar”主要探讨的是易语言中不同循环语句的性能差异,这对于优化代码和提高程序运行效率至关重要。 在易语言中,循环语句是控制程序流程的重要组成部分,用于重复...
本主题将深入探讨几种常见的循环语句,并对比它们在执行效率上的差异,这对于优化程序性能至关重要。易语言作为中国本土化的一种编程语言,同样支持多种循环结构,如“循环”,“直到”,“步进”等。以下我们将详细...
### SQL Server数据库性能优化之SQL语句篇 #### 一、引言 在现代企业级应用中,数据库性能直接影响到整个系统的响应时间和用户体验。对于基于SQL Server构建的应用来说,优化SQL语句是提升数据库性能的关键步骤之一...
7. **备份与恢复策略**:阐述了备份和恢复对性能的影响,包括不同类型的备份(完整、差异、日志)和恢复模式(简单、完整、大容量日志),以及如何平衡备份速度和数据恢复能力。 8. **索引维护与重建**:讨论了索引...
在IT行业中,数据库...总之,将SQL Server中的用户表数据导出为Insert语句是一项涉及数据库管理、编程和数据迁移的关键任务。通过MFC编写的应用程序,可以自动化这个过程,提高效率,同时确保数据的准确性和一致性。