设计优化

  1. 单例模式:延时加载(内部类)。反射和序列化会破坏单例
  2. 代理模式:延时加载
    • 静态代理:包括主题接口、真实主题、代理类、main。初始化时使用代理,真正使用时再通过代理加载真实主题。
    • 动态代理:jdk动态代理、CGLIB、javassist基于动态代码的代理、ASM。
    • 动态加载过程(以CGLIB为例):
      • 根据指定的回调类生成class字节码,并保存在byte数组中。
      • 通过反射,调用ClassLoader.defindClass将字节码定义为类。
      • 使用反射机制生成该类的实例。
    • 经典实现:AOP。AOP的实现方式有以下几种方式:
      • 静态织入:在编译期将切面织入到目标文件中。
      • jdk动态代理:运行期为接口生成代理类,再将切面织入代理类中。
      • CGLIB和javassist动态字节码:运行期,在目标加载后,动态构建字节码文件生成目标类的子类,将切面织入子类中。
  3. 享元模式:
    • 复用大对象,节省内存和创建的时间。
    • 和对象池的不同在于:享元对象都是不同的,各自有各自的含义和用途,而对象池的对象都是等价的,如数据库连接池中的连接。
  4. 装饰者模式:
    • 可以将功能组件和性能组件分开,需要再结合起来。
    • 设计原则:使用委托,少用继承。
    • 经典例子:OutputStream和InputStream。
  5. 观察者模式

常用优化组件和方法

  1. 缓冲:

    • 协调上层组件和下层组件的性能差异,最常用于提高I/O的速度。
    • 经典例子:写文件操作,FileWriter和BufferedWriter,使用缓冲区的writer性能会提升一倍。
  2. 缓存
  3. 对象复用—池
    • 只有对重量级对象使用对象池技术才能提高系统性能,对轻量级对象使用对象池,反而会降低系统性能。
    • 在Apache中,已经提供了一个Jakarta Commons Pool对象池组件可使用。
  4. 并行替代串行:数据迁移
  5. 负载均衡
  6. 时间换空间:CPU与内存
  7. 空间换时间:缓存

java程序优化

  1. String:

    • 由char数组、偏移量和string长度组成。
    • 其真实长度由偏移量和长度在这个char数组中进行定位和截取。
    • substring容易导致内存泄漏。
    • 对于字符串相加,如果直接使用string相加,编译期会自动优化成StringBuilder,所以会有所优化。但若是在for循环中string相加,编译期没有那么只能,所以每次循环都会为string创建一个新的StringBuilder,所以相对而言,直接用string相加还是不如直接用StringBuilder来append。
  2. List(ArrayList、Vector、LinkedList):
    • ArrayList和Vector —> AbstractList —> List。
      LinkedList —> AbstractSequenceList —> AbstractList —> List。
    • ArrayList和Vector都是基于数组实现,封装了对内部数组的操作。
      LinkedList使用循环双向链表数据结构。前驱表项(最后一个元素) <— 当前元素(header) —> 后驱表项(第一个元素)。
    • 普通新增操作(即直接新增到最后)。
      //ArrayList
      public boolean add(E e){
      encureCapacity(size+1);//确保内部数组有足够空间,不够则进行扩容,1.5倍。性能取决于这个方法
      elementData[size++] = e;//将元素加入到数组的末尾,完成添加
      return true;
      }
      //LinkedList
      private Entry(E) addBefore(E e, Entry entry){
      //下面三行代码是性能消耗的关键点
      Entry newEntry = new Entry(e, entry, entry.previous);//创建新的元素
      newEntry.previous.next = newEntry;//将前驱表项的下一个元素指向当前新增元素
      newEntry.next.previous = newEntry;//将后驱表项的上一个元素指向当前新增元素
      size++;
      modCount++;
      return newEntry;
      }
    • 根据下标新增到指定位置
      ArrayList:每次插入操作,都会进行数组复制,下标越前,性能越差。
      LinkedList:在哪里插入性能都一样。
    • 删除指定位置
      ArrayList:同新增一样,都需要进行数组的复制,从尾到头性能逐渐提升。
      LinkedList:需要遍历,下标为中间是性能最差,需要遍历1/2的列表元素。
    • foreach运行时会被编译期解析成迭代器,反编译的代码看到还多了一步赋值的操作,所以三种循环的性能排序为for循环 > 迭代器 > foreach。
  3. Map:
    • properties —> HashTable —> Dictionary、Map。
      HashMap —> AbstractMap —> Map。
      TreeMap —> AbstractMap —> Map。
      LinkedHashMap —> HashMap —> AbstractMap —> Map。
    • HashTable不允许key或value使用null值,但HashMap可以。
    • HashMap原理:将key做hash算法,然后将hash值映射到内存地址,直接取得key所对应的数据。底层数据结构是数组,内存地址即数组的下标索引。
    • 为何HashMap是高性能的:
      • hash算法高效(多采用navive本地方法和位运算)。
      • hash值到内存地址(数组索引)的算法高效(根据hash值和数组长度 按位与计算)。
      • 根据内存地址(数组索引)可以直接取得对应的值。
    • hash算法和查找的源码:
      • hash算法:
        int hash = hash(key.hashCode());//计算key的hash值
        public native int hashCode();//可以重写,性能关键点,所以重写的hashCode方法是否冲突很重要
        static int hash(itn h){//基于位运算
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
        }
      • 查找内存地址的算法:
        int i = indexFor(hash, table.length);
        static int indexFor(int h, int length){
        return h & (length-1);
        }
    • HashMap高性能的注意条件:
      • hashCode()方法的实现,要尽可能减少冲突,这样子对HashMap的操作就近乎对数组的随机访问。若冲突多,则相当于退化成几个链表,等价于遍历链表,性能很差。一般hashCode的生成可以直接使用eclipse IDE提供的方法,或者引入第三方库如Apache commons来生成。
      • 容量参数,HashMap的扩容会遍历整个HashMap,对里面的数据进行重新计算在新数组的位置,所以应尽量避免扩容,在初始化的时候就估算好大概的容量。
      • 负载因子 = 元素个数 / 内部数组大小,默认为0.75,尽量不要大于1,这样子会带来冲突。
    • HashMap的表项结构,实际上是一个链表的数组。
      Entry1(每个Entry包括key、value、next、hash)
      Entry2


      Entryn —> Entryn1 —> Entryn2(hash冲突的链表结构)。
    • LinkedHashMap:维护了元素次序表的HashMap,在每个Entry对象中添加了before、after两个属性。有两种排序类型,按照元素进入集合的顺序或者被访问的先后顺序排序。
    • TreeMap:基于匀速的固有顺序排序(由Comparator或者Comparable确定)。其内部实现是基于红黑树,是一种平衡查找树,性能要优于平衡二叉树,可以在O(log n)时间内查找、插入和删除。
  4. Set:
    HashSet、LinkedHashSet、TreeSet都只是对应的Map的一种封装,所有操作都委托给HashMap对象完成。
  5. NIO:
    • 与流式I/O不同,它是基于块(Block)的。
    • 最重要的两个组件:缓冲Buffer和通道Channel。通道表示缓冲数据的源头或目的地,用于向缓冲读取或者写入数据,是访问缓冲的接口。应用程序不能直接对Channel进行读写操作,而必须通过Buffer来进行。
    • NIO进行文件复制的例子:
      public static void nioCopyFile(String resource, String destination){
      FileInputStream fis = new FileInputStream(resource);
      FileOutputStream fos = new FileOutputStream(destination);
      FileChannel readChannel = fis.getChannel();//读文件通道
      FileChannel writeChannel = fos.getChannel();//写文件通道
      ByteBuffer buffer = ByteBuffer.allocate(1024);//读入数据缓存
      while(true){
      buffer.clear();
      int len = readChannel.read(buffer);
      if(len == 1){
      break;
      }
      buffer.flip();
      writerChannel.write(buffer);
      }
      readChannel.close();
      writeChannel.close();
      }
    • Buffer3个重要的参数:position(位置)、capacity(容量)、limit(上限)。当执行flip()操作会将写模式转换为读模式,并将limit设置为当前position和将position置为0。
    • 三种文件流操作性能对比:基于Buffer的性能比普通的基于流的性能要高一倍,基于Buffer并将文件映射到内存的性能要高出一个数量级。

并行程序开发及优化

  1. Future模式(可用于一个大方法中,某些小方法比较耗时,则可以将这些小方法用这种模式处理)

    • 核心在于去除主函数等待时间,并使得原本需要等待的时间段可以用于处理其他业务,充分利用计算机资源。
    • jdk的并发包中已经内置了一种Future模式的实现,关键是callable接口的call()方法,重载定义业务逻辑。
  2. Master-Worker模式(可用于数据迁移前的准备数据,多进程收集数据并异步计算结果)
    • Master进程为主要进程,维护了一个worker进程队列、子任务队列和子结果集。Worker进程队列中的worker进程不停地从任务队列中提取要处理的子任务,并将子任务的处理结果写入结果集。
    • 可以使用ForkJoinPool框架。
  3. 生产者-消费者模式
    • 模式架构图:生产者 —> 内存缓冲区 —> 消费者。
    • 生产者Producer:用于提交用户请求,提取用户任务,并装入内存缓冲区。
      消费者Consumer:在内存缓冲区中提取并处理任务。
      内存缓冲区BlockingQueue:缓存生产者提交的任务或数据,供消费者使用。

《JAVA程序性能优化》总结相关推荐

  1. STL源码剖析学习七:stack和queue

    STL源码剖析学习七:stack和queue stack是一种先进后出的数据结构,只有一个出口. 允许新增.删除.获取最顶端的元素,没有任何办法可以存取其他元素,不允许有遍历行为. 缺省情况下用deq ...

  2. 《STL源码剖析》学习-- 1.9-- 可能令你困惑的C++语法1

    最近在看侯捷的<STL源码剖析>,虽然感觉自己c++看得比较深一点,还是感觉还多东西不是那么明白,这里将一些细小的东西或者概念记录一下. 有些东西是根据<C++编程思想>理解的 ...

  3. 《STL源码剖析》学习--6章--_rotate算法分析

     最近在看侯捷的<STL源码剖析>,其中有许多不太明白之处,后经分析或查找资料有了些理解,现记录一下. <STL源码剖析>学习--6章--random access ite ...

  4. 《STL源码剖析》学习--6章--power算法分析

    最近在看侯捷的<STL源码剖析>,其中有许多不太明白之处,后经分析或查找资料有了些理解,现记录一下. 6章--power算法分析 书本中的算法如下所示: template <clas ...

  5. STL源码剖析——P142关于list::sort函数

    在list容器中,由于容器自身组织数据的特殊性,所以list提供了自己的排序函数list::sort, 并且实现得相当巧妙,不过<STL源码剖析>的原文中,我有些许疑问,对于该排序算法,侯 ...

  6. STL源码剖析---红黑树原理详解下

    转载请标明出处,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7760584       算法导论书上给出的红黑树的性质如下,跟STL源码 ...

  7. STL源码剖析面试问题

    当vector的内存用完了,它是如何动态扩展内存的?它是怎么释放内存的?用clear可以释放掉内存吗?是不是线程安全的? vector内存用完了,会以当前size大小重新申请2* size的内存,然后 ...

  8. STL源码剖析学习二:空间配置器(allocator)

    STL源码剖析学习二:空间配置器(allocator) 标准接口: vlaue_type pointer const_pointer reference const_reference size_ty ...

  9. STL源码剖析 数值算法 copy 算法

    copy复制操作,其操作通过使用assignment operator .针对使用trivial assignment operator的元素型别可以直接使用内存直接复制行为(使用C函数 memove ...

  10. STL源码剖析 算法开篇

    STL源码剖析 算法章节 算法总览_CHYabc123456hh的博客-CSDN博客 质变算法 质变算法 - 会改变操作对象的数值,比如互换.替换.填写.删除.排列组合.分隔.随机重排.排序等 #in ...

最新文章

  1. ZServer4D开源项目
  2. 区块链技术没那么复杂,别被大佬们忽悠晕了
  3. php sql注入 替换,通过替换单引号来防止SQL注入
  4. matlab中服从高斯分布的矩阵_推荐基础算法之矩阵分解PMF
  5. python 将元组解析为多个参数
  6. 微信小程序开发:学习笔记[8]——页面跳转及传参
  7. pytorch:固定部分层参数,固定单个模型
  8. 质量属性效用树例子_数百个 HTML5 例子学习 HT 图形组件 – 拓扑图篇
  9. 兰空图床Lsky Pro 免费开源图床网站程序v1.6.3
  10. tcp与udp的socket编程,udp代码实现客户端服务端,tcp代码实现客户端服务端(单线程、多线程、多进程分别实现),三次握手
  11. SSH2整合需要jar包解释
  12. ETH 5分钟内涨幅2.06%,现价1104.03usdt
  13. Java 方法的反射
  14. keil 之Vscode编辑器插件、格式化代码插件和最新注册机分享
  15. 前端知识:使用layui模板搭建页面,简单快捷
  16. 树上随机游走的期望距离
  17. android设备内部添加apn信息
  18. python编写五子棋小游戏 (电脑自走棋)
  19. 【OpenCV】纹理提取
  20. APP启动时白屏/黑屏 或者 Activity打开时白屏/黑屏

热门文章

  1. BUUCTF [NCTF2019]childRSA(费马小定理)
  2. 用python做股市数据分析一_用python做股票数据分析(兴趣篇)
  3. HTTP请求,出现Status Code: 405
  4. PXE配置中ks.cfg文件介绍
  5. matlab里面atn是什么意思,matlab教程第一章
  6. vscode 无法输入输出
  7. 关于各种职业的英文单词zz
  8. 高清视频会议终端市场现状及未来发展趋势分析
  9. HTTP和RTSP简介
  10. /usr/local