PpLK: |Practical Java| Chapter 4 Performance (性能) (一)

来源:互联网 发布:windows10怎么安装软件 编辑:程序博客网 时间:2024/05/01 18:07
|Practical Java| Chapter 4   Performance (性能)    (一)
 
         Java正变得越来越快, 这得益于 JIT 代码生成器 (code generators) / garabage collection
         以及其他运行期优化技术 (Runtime optimizations)的进步. 然后程序适当的设计一直
         是不容忽视的.
       
         优化的事项:
         A> 除非证实必须优化,否则不要优化你的代码。验明这一点的惟一方法
                    用你自己的计时技术或运用某个执行评测器(execution profiler)。
         B> 如果优化工作进行得不够小心细致,可能会将臭虫引入代码中。记住
                    活的慢郎总好过不干活的急惊风。
         C>  实施优化之后,应该再次进行评测,以验证是否达到了预期效果。
         D> [适用于所有侧s J的成本模型[c。d咖deI)是不存在的。因此在某个JVM
                    中令代码更快捷的技术,未必在另一个JVM中也能得到相同效果。
         代码性能特征(performance characteristics )计时技术:
         long start = System.currentTimeMillis();
         //code to time
         long end = System.currentTimeMillis();
         long Time = end -start;

28> 先把焦点放在设计 / 数据结构 和算法上.
 
       We should forget about small efficiencies, about 97% of the Time.
       Premature optimization is the root of all evil.
       产生快捷代码的一个规则是,只优化必要的部分。花费时间将代码优化,却未能给
       程序带来实质性的性能影响,就是在浪费时间. 典型情况下 80% ~90% 的程序执行
       时间花费在10% ~ 20% 的代码上面(译注:80 - 20法则)。称最好通过性能评测器
       (performance profilers)找出这需要改善10% ~ 20% 的代码。
       

29> 不要依赖编译期 (compile - Time )优化技术
      
       Java编译器在优化方面着墨甚少. Sun的 Javac 及其他大部分complier只支持少量
             优化技术: 简单的常量合并(constant folding) / 无用码删除(dead code elimination)
       (文件宣称[实际毫无意义]: javac -o 可以为运行期产生出优化代码. || javap -c 反汇编)

30>  理解运行期 (Runtime) 代码优化技术
  
        JIT的目的是将 bytecode 在运行期转换为本机二进制码 (native binary code).
        JIT的运行也需要时间的, 如果JIT 的运行时间多, 即意味着程序启动的时间也长.

31>  如果进行字符串结合, StringBuffer 优于 String
  
        String 用来表示那些建立后就不再改变的字符串 (Character strings), 即String 对象
                 是只读且不可变的.(immutable)
        StringBuffer 用来表示内容可变的字符序列, 提供一些函数用来修改底部(underlying)
                 字符序列的内容.
        String 和StringBuffer 之间的关键差异在于, 执行单纯的字符串合并(concatenation)时,
                 后者比之前者快很多.
Ex:    Javap -c 查看一下代码的bytecode:
    public  class PP{
    public static void main(String []args){
    String str = new String ("Practical ");
    str +="Java";
    //StringBuffer str2 = new StringBuffer("Practical ");
                  //str2.append("Java");
   }
  }
          0  new #7 <Class java lang.String>
          3  dup
          4  ldc #2 <String "Practical ">
          6  invokespecial #12 <Method java.lang.String (java.lang.String>
          9  astore_1            
          10  new #8 <Class java.lang.StringBuffer>                
          13  dup
          14  aload_1
          15  invokestatic #23 <Method java.lang.String valueof(java.lang.Object)>
          18  invokespecial #13 <Method java.lang.StringBuffer(java.lang.String)>
          21  ldc  #1 <String "Java">
          23  invokevirtual #15 <Method java.lang.StringBuffer append(java.lang.String)>
          26  invoidvirtual #22 <Method java.lang.String toString()>
          29 astore_1
// 0~9  String str = new String ("Practical ");
// 10    因为String 是不可变的,为了实现合并必须建立一个StringBuffer对象;
// 26    合并之后必须将其转换回String, 此处调用toString() 完成. 这个方法根据
//        [StringBuffer 临时对象]建立一个新的String对象.  ----  这里临时对象的
//        建立及转型为String, 代价不菲.
           上述程序main 产生了5个对象:  
                     位置 0 / 4 产生2个String 对象;
                     位置 10 产生一个StringBuffer 对象;
                     位置 21 / 26 产生2个String 对象;
//StringBuffer str2 = new StringBuffer("Practical ");
//str2.append("Java");
         采用StringBuffer的 bytecode:
         0  new #7 <Class java lang.StringBuffer>
         3  dup
         4  ldc #2 <String "Practical ">
         6  invokespecial #12 <Method java.lang.StringBuffer (java.lang.String>
         9  astore_1    
         10  aload_1
         11  ldc #1 <String "Java">
         13  invokevirtual #15 <Method java.lang.StringBuffer append(java.lang.String)>
         16  pop
          这里的两句代码产生3个对象; ( 位置0 --- StringBuffer, 4 / 11 --- String)

32>   将对象的创建成本 (creation cost) 降至最小
        
         对象的构建 (construction) 不仅仅是[分配内存 + 初始化一些 值域(fields)]这么简单!!
        
         对象构建过程中,一定会以固定顺序发生的东东:
         A> 从Heap 中分配内存, 用以存放全部的instance 变量以及这个对象连同其
               superclasses的实现专属数据(implementation-specific data).  -- 即包括指向
               "class and method data" 的指针.
         B> 对象的instance 变量被初始化为其相应的缺省值.
         C> 调用 most derived class(最深层派生类)的 Constructor  ---
//事实上, 构造函数被.class 文件中的 initialization method 替换了. im函数是名为
//<init> 的特殊函数, 由Java 编译期安放在.class文件里. 其中包含 [构造函数代码]
//[instance 变量的初始化代码] / [调用superclass im]的代码.
              构造函数做的第一件事就是调用superclass的 construct .这个程序一直反
              复持续到java.lang.Object构造函数被调用为止.(jlo是一切o的base class);
        D> 在构造函数本身执行之前,所有instance 变量的initializers 和初始化区段
              (initialization blocks)先获得执行, 然后才执行构造函数本身. 于是base
              class 的construct 最先执行, most derived class 的construct最后执行.这使
              得任何class的construct都放心大胆的使用其任何superclasses的instance变量.
Ex:  [ (lightweight)对象的建立比一个重型(heavyweight)对象快很多]
       Light lgt = new Light(5);
       1> 从heap 分配内存, 用来存放Light class的instance变量;
       2> class的instance变量被初始化为相应缺省值;
       3> 调用Light construct Method;
       4> LS 调用其superclass construct;
       5> 第4>部返回后, LS 对instance 变量设定初值;
       6> LS 结束, Object reference lgt指向heap 中刚建立完成的Light 对象;
       以下情况会增加对象创建的成本: (creation cost)
                A> construct 有大量代码;
                B> 内含数量众多或体积庞大的对象 ----她们的初始化将是construct的一部分;
                C> 太深的继承层次(inheritance hierarchy);
                // 记住, 每一个被创建的对象,也是垃圾回收器跟踪和可能释放的目标,
                // 所以garbage collention正确管理她们的存储也需要代价;

33>  谨慎未用上的对象 (unused objects)
        对象的创建是需要代价的, 确保所create 对象在使用上的质量.

34>  将同步化 (synchronization) 降至最低
synchronized 函数比之 non-synchronized 函数慢很多: (两者的bytecode是一样的)
------>
        当函数被提供了synchronized 修饰符后, lock的获取(acquistion) 和随后的释放
        (release)就不再通过monitorenter 和monitorexit 操作码(opcodes)来进行. 取而代
        之的是, 当JVM调用一个函数时, 会检验 ACC_SYNCHRONIZED属性标记.如
        果存在这个标记(property flag), 当前线程就取得一个lock 并调用之, 且在函数
 返回时释放lock .  
        synchronized 不仅降低代码执行速度,视其使用方式的不通, 还可能增加代码体积.
比较下面两个功能相当的函数:
 public synchronized int top1(){ return intarr[0] }
        pubic int top top2(){ synchronized(this){ return intarr[0];} }
//top1() 并不会生成额外的代码, top2()则是在函数体中使用synchronized.
//后者将会产生操作码 monitorenter 和 monitorexit 的bytecode , 以及为了
//处理异常而附加的代码.如此top1() 比top2() 性能略高.
//对于 Vector 类就是因为绝大多数的函数都是synchronized, 所以现在不推荐使用,
//现在取而代之的有 ArrayList class. 当然这只是在不需要同步化的情况下.
原创粉丝点击