Java虚拟机JVM性能优化(二):编译器

本文将是JVM 性能优化系列的第二篇文章(第一篇:传送门),Java 编译器将是本文讨论的核心内容。

本文中,作者(Eva Andreasson)首先介绍了不同种类的编译器,并对客户端编译,服务器端编译器和多层编译的运行性能进行了对比。然后,在文章的最后介绍了几种常见的JVM优化方法,如死代码消除,代码嵌入以及循环体优化。

Java最引以为豪的特性“平台独立性”正是源于Java编译器。软件开发人员尽其所能写出最好的java应用程序,紧接着后台运行的编译器产生高效的基于目标平台的可执行代码。不同的编译器适用于不同的应用需求,因而也就产生不同的优化结果。因此,如果你能更好的理解编译器的工作原理、了解更多种类的编译器,那么你就能更好的优化你的Java程序。

本篇文章突出强调和解释了各种Java虚拟机编译器之间的不同。同时,我也会探讨一些及时编译器(JIT)常用的优化方案。

什么是编译器?

简单来说,编译器就是以某种编程语言程序作为输入,然后以另一种可执行语言程序作为输出。Javac是最常见的一种编译器。它存在于所有的JDK里面。Javac 以java代码作为输出,将其转换成JVM可执行的代码—字节码。这些字节码存储在以.class结尾的文件中,并在java程序启动时装载到java运行时环境。

字节码并不能直接被CPU读取,它还需要被翻译成当前平台所能理解的机器指令语言。JVM中还有另一个编译器负责将字节码翻译成目标平台可执行的指令。一些JVM编译器需要经过几个等级的字节码代码阶段。例如,一个编译器在将字节码翻译成机器指令之前可能还需要经历几种不同形式的中间阶段。

从平台不可知论的角度出发,我们希望我们的代码能够尽可能的与平台无关。

为了达到这个目的,我们在最后一个等级的翻译—从最低的字节码表示到真正的机器代码—才真正将可执行代码与一个特定平台的体系结构绑定。从最高的等级来划分,我们可以将编译器分为静态编译器和动态编译器。 我们可以根据我们的目标执行环境、我们渴望的优化结果、以及我们需要满足的资源限制条件来选择合适的编译器。在上一篇文章中我们简单的讨论了一下静态编译器和动态编译器,在接下来的部分我们将更加深入的解释它们。

静态编译 VS 动态编译

我们前面提到的javac就是一个静态编译的例子。对于静态编译器,输入代码被解释一次,输出即为程序将来被执行的形式。除非你更新源代码并(通过编译器)重新编译,否则程序的执行结果将永远不会改变:这是因为输入是一个静态的输入并且编译器是一个静态的编译器。

通过静态编译,下面的程序:

代码如下:

staticint add7(int x ){      return x+7;}

将会转换成类似下面的字节码:

代码如下:

iload0 bipush 7 iadd ireturn

动态编译器动态的将一种语言编译成另外一种语言,所谓动态的是指在程序运行的时候进行编译—边运行边编译!动态编译和优化的好处就是可以处理应用程序加载时的一些变化。Java 运行时常常运行在不可预知甚至变化的环境上,因此动态编译非常适用于Java 运行时。大部分的JVM 使用动态编译器,如JIT编译器。值得注意的是,动态编译和代码优化需要使用一些额外的数据结构、线程以及CPU资源。越高级的优化器或字节码上下文分析器,消耗越多的资源。但是这些花销相对于显著的性能提升来说是微不足道的。

JVM种类以及Java的平台独立性

所有JVM的实现都有一个共同的特点就是将字节码编译成机器指令。一些JVM在加载应用程序时对代码进行解释,并通过性能计数器来找出“热”代码;另一些JVM则通过编译来实现。编译的主要问题是集中需要大量的资源,但是它也能带来更好的性能优化。

如果你是一个java新手,JVM的错综复杂肯定会搞得你晕头转向。但好消息是你并不需要将它搞得特别清楚!JVM将管理代码的编译和优化,你并不需要为机器指令以及采取什么样的方式写代码才能最佳的匹配程序运行平台的体系结构而操心。

从java字节码到可执行

一旦将你的java代码编译成字节码,接下来的一步就是将字节码指令翻译成机器代码。这一步可以通过解释器来实现,也可以通过编译器来实现。

解释

解释是编译字节码最简单的方式。解释器以查表的形式找到每条字节码指令对应的硬件指令,然后将它发送给CPU执行。

你可以将解释器想象成查字典:每一个特定的单词(字节码指令),都有一个具体的翻译(机器代码指令)与之对应。因为解释器每读一条指令就会马上执行该指令,所以该方式无法对一组指令集进行优化。同时每调用一个字节码都要马上对其进行解释,因此解释器运行速度是相当慢得。解释器以一种非常准确的方式来执行代码,但是由于没有对输出的指令集进行优化,因此它对目标平台的处理器来说可能不是最优的结果。

编译

编译器则是将所有将要执行的代码全部装载到运行时。这样当它翻译字节码时,就可以参考全部或部分的运行时上下文。它做出的决定都是基于对代码图分析的结果。如比较不同的执行分支以及参考运行时上下文数据。

在将字节码序列被翻译成机器代码指令集后,就可以基于这个机器代码指令集进行优化。优化过的指令集存储在一个叫代码缓冲区的结构中。当再次执行这些字节码时,就可以直接从这个代码缓冲区中取得优化过的代码并执行。在有些情况下编译器并不使用优化器来进行代码优化,而是使用一种新的优化序列—“性能计数”。

使用代码缓存器的优点是结果集指令可以被立即执行而不再需要重新解释或编译!

这可以大大的降低执行时间,尤其是对一个方法被多次调用的java应用程序。

优化

通过动态编译的引入,我们就有机会来插入性能计数器。例如,编译器插入性能计数器,每次字节码块(对应某个具体的方法)被调用时对应的计数器就加一。编译器通过这些计数器找到“热块”,从而就能确定哪些代码块的优化能对应用程序带来最大的性能提升。运行时性能分析数据能够帮助编译器在联机状态下得到更多的优化决策,从而更进一步提升代码执行效率。因为得到越多越精确的代码性能分析数据,我们就可以找到更多的可优化点从而做出更好的优化决定,例如:怎样更好的序列话指令、是否用更有效率的指令集来替代原有指令集,以及是否消除冗余的操作等。

例如

考虑下面的java代码

代码如下:

staticint add7(int x ){      return x+7;}

Javac 将静态的将它翻译成如下字节码:

代码如下:

iload0

bipush 7

iadd

ireturn

当该方法被调用时,该字节码将被动态的编译成机器指令。当性能计数器(如果存在)达到指定的阀值时,该方法就可能被优化。优化后的结果可能类似下面的机器指令集:

代码如下:

lea rax,[rdx+7]  ret

不同的编译器适用于不同的应用

不同的应用程序拥有不同的需求。企业服务器端应用通常需要长时间运行,所以通常希望对其进行更多的性能优化;而客户端小程序可能希望更快的响应时间和更少的资源消耗。下面让我们一起讨论三种不同的编译器以及他们的优缺点。

客户端编译器(Client-side compilers)

C1是一种大家熟知的优化编译器。当启动JVM时,添加-client参数即可启动该编译器。通过它的名字我们即可发现C1是一种客户端编译器。它非常适用于那种系统可用资源很少或要求能快速启动的客户端应用程序。C1通过使用性能计数器来进行代码优化。这是一种方式简单,且对源代码干预较少的优化方式。

服务器端编译器(Server-side compilers)

对于那种长时间运行的应用程序(例如服务器端企业级应用程序),使用客户端编译器可能远远不能够满足需求。这时我们应该选择类似C2这样的服务器端编译器。通过在JVM启动行中加入 –server 即可启动该优化器。因为大部分的服务器端应用程序通常都是长时间运行的,与那些短时间运行、轻量级的客户端应用相比,通过使用C2编译器,你将能够收集到更多的性能优化数据。因此你也将能够应用更高级的优化技术和算法。

提示:预热你的服务端编译器

对于服务器端的部署,编译器可能需要一些时间来优化那些“热点”代码。所以服务器端的部署常常需要一个“加热”阶段。所以当对服务器端的部署进行性能测量时,务必确保你的应用程序已经达到了稳定状态!给予编译器充足的时间进行编译将会给你的应用带来很多好处。

服务器端编译器相比客户端编译器来说能够得到更多的性能调优数据,这样就可以进行更复杂的分支分析,从而找到性能更优的优化路径。拥有越多的性能分析数据就能得到更优的应用程序分析结果。当然,进行大量的性能分析也就需要更多的编译器资源。如JVM若使用C2编译器,那么它将需要使用更多的CPU周期,更大的代码缓存区等等。

多层编译

多层编译混合了客户端编译和服务器端编译。Azul第一个在他的Zing JVM中实现了多层编译。最近,这项技术已经被Oracle Java Hotspot JVM采用(Java SE7 之后)。多层编译综合了客户端和服务器端编译器的优点。客户端编译器在以下两种情况表现得比较活跃:应用启动时;当性能计数器达到较低级别的阈值时进行性能优化。客户端编译器也会插入性能计数器以及准备指令集以备接下来的高级优化—服务器端编译器—使用。多层编译是一种资源利用率很高的性能分析方式。因为它可以在低影响编译器活动时收集数据,而这些数据可以在后面更高级的优化中继续使用。这种方式与使用解释性代码分析计数器相比可以提供更多的信息。

图1所描述的是解释器、客户端编译、服务器端编译、多层编译的性能比较。X轴是执行时间(时间单位),Y轴是性能(单位时间内的操作数)

图1.编译器性能比较

相对于纯解释性代码,使用客户端编译器可以带来5到10倍的性能提升。获得性能提升的多少取决于编译器的效率、可用的优化器种类以及应用程序的设计与目标平台的吻合程度。但对应程序开发人员来讲最后一条往往可以忽略。

相对于客户端编译器,服务器端编译器往往能带来30%到50%的性能提升。在大多数情况下,性能的提升往往是以资源的损耗为代价的。

多层编译综合了两种编译器的优点。客户端编译有更短的启动时间以及可以进行快速优化;服务器端编译则可以在接下来的执行过程中进行更高级的优化操作。

一些常见的编译器优化

到目前为止,我们已经讨论了优化代码的意义以及怎样、何时JVM会进行代码优化。接下来我将以介绍一些编译器实际用到的优化方式来结束本文。JVM优化实际发生在字节码阶段(或者更底层的语言表示阶段),但是这里将使用java语言来说明这些优化方式。我们不可能在本节覆盖所有的JVM优化方式;当然啦,我希望通过这些介绍能激发你去学习数以百计的更高级的优化方式的兴趣并在编译器技术方面有所创新。

死代码消除

死代码消除,顾名思义就是消除那些永远不会被执行到的代码—即“死”代码。

如果编译器在运行过程中发现一些多余指令,它将会将这些指令从执行指令集里面移除。例如,在列表1里面,其中一个变量在对其进行赋值操作后永远不会被用到,所有在执行阶段可以完全地忽略该赋值语句。对应到字节码级别的操作即是,永远不需要将该变量值加载到寄存器中。不用加载意味着消耗更少的cpu时间,因此也就能加快代码执行,最终导致应用程序加快—如果该加载代码每秒被调用好多次,那优化效果将更明显。

列表1 用java 代码列举了一个对永远不会被使用的变量赋值的例子。

列表1. 死代码

代码如下:

int timeToScaleMyApp(boolean endlessOfResources){  
int reArchitect =24;  
int patchByClustering =15;  
int useZing =2;  
if(endlessOfResources)      
return reArchitect + useZing;  
else      
return useZing;
}

在字节码阶段,如果一个变量被加载但是永远不会被使用,编译器可以检测到并消除掉这些死代码,如列表2所示。如果永远不执行该加载操作则可以节约cpu时间从而改进程序的执行速度。

列表2. 优化后的代码

代码如下:

int timeToScaleMyApp(boolean endlessOfResources){  
int reArchitect =24;   //unnecessary operation removed here…  
int useZing =2;  
if(endlessOfResources)      
return reArchitect + useZing;  
else      
return useZing;
}

冗余消除是一种类似移除重复指令来改进应用性能的优化方式。

很多优化尝试着消除机器指令级别的跳转指令(如 x86体系结构中得JMP). 跳转指令将改变指令指针寄存器,从而转移程序执行流。这种跳转指令相对其他ASSEMBLY指令来说是一种很耗资源的命令。这就是为什么我们要减少或消除这种指令。代码嵌入就是一种很实用、很有名的消除转移指令的优化方式。因为执行跳转指令代价很高,所以将一些被频繁调用的小方法嵌入到函数体内将会带来很多益处。列表3-5证明了内嵌的好处。

列表3. 调用方法

代码如下:

int whenToEvaluateZing(int y){   return daysLeft(y)+ daysLeft(0)+ daysLeft(y+1);}

列表4. 被调用方法

代码如下:

int daysLeft(int x){   if(x ==0)      return0;   else      return x -1;}

列表5. 内嵌方法

代码如下:

int whenToEvaluateZing(int y){  
int temp =0;     
if(y ==0)
temp +=0;
else
temp += y -1;  
if(0==0)
temp +=0;
else
temp +=0-1;  
if(y+1==0)
temp +=0;
else
temp +=(y +1)-1;     
return temp;
}

在列表3-5中我们可以看到,一个小方法在另一个方法体内被调用了三次,而我们想说明的是:将被调用方法直接内嵌到代码中所花费的代价将小于执行三次跳转指令所花费的代价。

内嵌一个不常被调用的方法可能并不会带来太大的不同,但是如果内嵌一个所谓的“热”方法(经常被调用的方法)则可以带来很多的性能提升。内嵌后的代码常常还可以进行更进一步的优化,如列表6所示。

列表6. 代码内嵌后,更进一步的优化实现

代码如下:

int whenToEvaluateZing(int y){   if(y ==0)return y;   elseif(y ==-1)return y -1;   elsereturn y + y -1;}

循环优化

循环优化在降低执行循环体所带来的额外消耗方面起着很重要的作用。这里的额外消耗指的是昂贵的跳转、大量的条件检测,非优化管道(即,一系列无实际操作、消耗额外cpu周期的指令集)。这里有很多种循环优化,接下来列举一些比较流行的循环优化:

循环体合并:当两个相邻的循环体执行相同次数的循环时,编译器将试图合并这两个循环体。如果两个循环体相互之间是完全独立的,则它们还可以被同时执行(并行)。

反演循环: 最基本的,你用一个do-while循环来替代一个while循环。这个do-while循环被放置在一个if语句中。这个替换将减少两次跳转操作;但增加了条件判断,因此增加了代码量。这种优化是以适当的增加资源消耗换来更有效的代码的很棒的例子—编译器对花费和收益进行衡量,在运行时动态的做出决定。

重组循环体: 重组循环体,使整个循环体能全部的存储在缓存器中。

展开循环体: 减少循环条件的检测次数和跳转次数。你可以把这想象成将几次迭代“内嵌”执行,而不必进行条件检测。循环体展开也会带来一定的风险,因为它可能因为影响流水线和大量的冗余指令提取而降低性能。再一次,是否展开循环体由编译器在运行时决定,如果能带来更大的性能提升则值得展开。

以上就是对编译器在字节码级别(或更低级别)如何改进应用程序在目标平台执行性能的一个概述。我们所讨论的都是些常见、流行的优化方式。由于篇幅有限我们只举了一些简单的例子。我们的目的是希望通过上面简单的讨论来激起你深入研究优化的兴趣。

结论:反思点和重点

根据不同的目的,选择不同的编译器。

1.解释器是将字节码翻译成机器指令的最简单形式。它的实现基于一个指令查询表。
2.编译器可以基于性能计数器进行优化,但是需要消耗一些额外的资源(代码缓存,优化线程等)。
3.客户端编译器相对于解释器可以带来5到10倍的性能提升。
4.服务器端编译器相对于客户端编译器来说可以带来30%到50%的性能提升,但需要消耗更多的资源。
5.多层编译则综合了两者的优点。使用客户端编译来获取更快的响应速度,接着使用服务器端编译器来优化那些被频繁调用的代码。

这里有很多种可能的代码优化方式。编译器的一个重要工作就是分析所有可能的优化方式,然后对各种优化方式所付出的代价与最终得到的机器指令带来的性能提升进行权衡。

(0)

相关推荐

  • Java虚拟机最多支持多少个线程的探讨

    McGovernTheory在StackOverflow提了这样一个问题: Java虚拟机最多支持多少个线程?跟虚拟机开发商有关么?跟操作系统呢?还有其他的因素吗? Eddie的回答: 这取决于你使用的CPU,操作系统,其他进程正在做的事情,你使用的Java的版本,还有其他的因素.我曾经见过一台Windows服务器在宕机之前有超过6500个线程.当然,大多数线程什么事情也没有做.一旦一台机器上有差不多6500个线程(Java里面),机器就会开始出问题,并变得不稳定. 以我的经验来看,JVM容纳的

  • 了解Java虚拟机JVM的基本结构及JVM的内存溢出方式

    JVM内部结构图 Java虚拟机主要分为五个区域:方法区.堆.Java栈.PC寄存器.本地方法栈.下面 来看一些关于JVM结构的重要问题. 1.哪些区域是共享的?哪些是私有的? Java栈.本地方法栈.程序计数器是随用户线程的启动和结束而建立和销毁的, 每个线程都有独立的这些区域.而方法区.堆是被整个JVM进程中的所有线程共享的. 2.方法区保存什么?会被回收吗? 方法区不是只保存的方法信息和代码,同时在一块叫做运行时常量池的子区域还 保存了Class文件中常量表中的各种符号引用,以及翻译出来的

  • 浅谈Java的虚拟机结构以及虚拟机内存的优化

    工作以来,代码越写越多,程序也越来越臃肿,效率越来越低,对于我这样一个追求完美的程序员来说,这是绝对不被允许的,于是除了不断优化程序结构外,内存优化和性能调优就成了我惯用的"伎俩". 要对Java程序进行内存优化和性能调优,不了解虚拟机的内部原理(或者叫规范更严谨一点)是肯定不行的,这里推荐一本好书<深入Java虚拟机(第二版)>(Bill Venners著,曹晓刚 蒋靖 译,实际上本文正是作者阅读本书之后,对Java虚拟机的个人理解阐述).当然了,了解Java虚拟机的好处

  • Java虚拟机JVM性能优化(三):垃圾收集详解

    Java平台的垃圾收集机制显著提高了开发者的效率,但是一个实现糟糕的垃圾收集器可能过多地消耗应用程序的资源.在Java虚拟机性能优化系列的第三部分,Eva Andreasson向Java初学者介绍了Java平台的内存模型和垃圾收集机制.她解释了为什么碎片化(而不是垃圾收集)是Java应用程序性能的主要问题所在,以及为什么分代垃圾收集和压缩是目前处理Java应用程序碎片化的主要办法(但不是最有新意的). 垃圾收集(GC)的目的是释放那些不再被任何活动对象引用的Java对象所占用的内存,它是Java

  • Java虚拟机JVM性能优化(一):JVM知识总结

    Java应用程序是运行在JVM上的,但是你对JVM技术了解吗?这篇文章(这个系列的第一部分)讲述了经典Java虚拟机是怎么样工作的,例如:Java一次编写的利弊,跨平台引擎,垃圾回收基础知识,经典的GC算法和编译优化.之后的文章会讲JVM性能优化,包括最新的JVM设计--支持当今高并发Java应用的性能和扩展. 如果你是一个开发人员,你肯定遇到过这样的特殊感觉,你突然灵光一现,所有的思路连接起来了,你能以一个新的视角来回想起你以前的想法.我个人很喜欢学习新知识带来的这种感觉.我已经有过很多次这样

  • Java虚拟机装载和初始化一个class类代码解析

    在 java 应用程序开发中,只有被 java 虚拟机装载的 Class 类型才能在程序中使用.只要生成的字节码符合 java 虚拟机的指令集和文件格式,就可以在 JVM 上运行,这为 java 的跨平台性提供条件.下面,我们来看看虚拟机是如何装载和初始化一个 class 类的. 装载一个类 学习过C/C++语言的读者知道,C/C++源代码必须首先别编译成本地的机器代码,然后还需要一个链接代码过程.该链接过程的主要任务就是:合并不同的源码文件产出的中间代码,并最终获得一个可直接执行的应用程序.然

  • Java虚拟机JVM性能优化(二):编译器

    本文将是JVM 性能优化系列的第二篇文章(第一篇:传送门),Java 编译器将是本文讨论的核心内容. 本文中,作者(Eva Andreasson)首先介绍了不同种类的编译器,并对客户端编译,服务器端编译器和多层编译的运行性能进行了对比.然后,在文章的最后介绍了几种常见的JVM优化方法,如死代码消除,代码嵌入以及循环体优化. Java最引以为豪的特性"平台独立性"正是源于Java编译器.软件开发人员尽其所能写出最好的java应用程序,紧接着后台运行的编译器产生高效的基于目标平台的可执行代

  • java性能优化之编译器版本与平台对应关系

    目录 JIT编译器版本 默认情况JVM如何选择编译器? 如何判断当前环境jvm使用的编译器? 小节 本章节更加具体化的学习编译器还有哪些可以优化的方便,让你的应用展现出更好的性能. JIT编译器版本 JIT编译器有不同的版本,而最终你使用哪种,取决于你所使用的系统平台.前面的文章我们说到编译器有-client和-server,具体划分应该是如下所示: -client 32位client编译器 -server 32位server编译器 -d64 64位server编译器 如果你的系统是32位,那么

  • 深入理解Java虚拟机 JVM 内存结构

    目录 前言 JVM是什么 JVM内存结构概览 运行时数据区 程序计数器 Java虚拟机栈 本地方法栈 方法区 运行时常量池 Java堆 直接内存 前言 JVM是Java中比较难理解和掌握的一部分,也是面试中被问的比较多的,掌握好JVM底层原理有助于我们在开发中写出效率更高的代码,可以让我们面对OutOfMemoryError时不再一脸懵逼,可以用掌握的JVM知识去查找分析问题.去进行JVM的调优.去让我们的应用程序可以支持更高的并发量等......总之一句话,学好JVM很重要! JVM是什么 J

  • Java 虚拟机(JVM)之基本概念详解

    1.类加载子系统:负责从文件系统或者网络中加载Class信息,加载的信息存放在一块称之为方法区的内存空间. 2.方法区:就是存放类信息.常量信息.常量池信息.包括字符串字面量和数字常量等.方法区是辅助堆栈的块永久区,解决堆栈信息的产生,是先决条件. 3.Java堆:再java虚拟机启动的时候建立Java堆,它是java程序最主要的内存工作区域,几乎所有的对象实例都存放到Java堆中,堆空间是所有线程共享的.堆解决的是数据存储问题,即数据怎么放.放在哪儿. 4.直接内存:Java的NIO库允许Ja

  • 详解Java虚拟机(JVM)运行时

    JVM(Java虚拟机)是一个抽象的计算模型.就如同一台真实的机器,它有自己的指令集和执行引擎,可以在运行时操控内存区域.目的是为构建在其上运行的应用程序提供一个运行环境.JVM可以解读指令代码并与底层进行交互:包括操作系统平台和执行指令并管理资源的硬件体系结构.本文主要介绍Java虚拟机(JVM)运行时详解. 我们知道的JVM内存区域有:堆和栈,这是一种泛的分法,也是按运行时区域的一种分法,堆是所有线程共享的一块区域,而栈是线程隔离的,每个线程互不共享. 线程不共享区域 每个线程的数据区域包括

  • java虚拟机jvm方法区实例讲解

    和java堆一样,方法区是一块所有线程共享的内存区域,用于保存系统的类信息,类的信息有哪些呢.字段.方法.常量池.方法区也有一块内存区域所以方法区的内存大小,决定了系统可以包含多少个类,如果系统类太多,方法区内存不够肯定会导致方法区溢出,虚拟机同样会抛出内存溢出信息.(内存溢出后面相关文章给大家总结) jdk6和jdk7中,方法区可以理解为永久区(Perm).永久区可以使用参数-XX:PermSize和-XX:MaxPermSize制定.默认情况下-XX:MaxPermSize为64MB.如果你

  • Java虚拟机JVM优化实战的过程全记录

    前言 Java虚拟机是运行所有Java程序的抽象计算机,是Java语言的运行环境,它是Java 最具吸引力的特性之一.Java虚拟机是通过在实际的计算机上仿真模拟各种计算机功能模拟来实现的,通过Java虚拟机,您只要根据JVM规格描述将解释器移植到特定的计算机上,就能保证经过编译的任何Java代码能够在该系统上运行. 最近在看JVM群里有人发了一个GC情况,让人帮忙看优化的,于是我也凑热闹发了出来想让群里的大神们指导优化一下,以下是优化过程记录. 一开始我贴了下面的两张图 jstat看GC记录

  • Java编程代码性能优化

    一.咱们之所以这么干的目的: 1.效率(最重要) 2.可读性,便于后期维护.(同样很重要) 二.代码优化的要求: 1.减小代码的体积. 2.提高代码的运行效率. 三.常用的代码的优化: 1.尽量重用对象 : 特别是String对象的重用.最常用的就是字符串的拼接: 当遇到频繁擦拼接String时.记住一定用StringBuilder/StringBuffer 例如: ArrayList<String> list; //省去list初始化. StringBuilder builder = new

随机推荐