java性能优化笔记 - 01

来源:互联网 发布:windows 适合的Ps 编辑:程序博客网 时间:2024/05/29 17:49

★为了提高系统的速度,仅增加CPU处理器的数量并不一定能起到有效地作用,需要从

根本上修改程序的串行行为,提高系统内科并行化的模块比重。在此基础上,合理增加并

行处理器的数量,才能从最小的投入得到最大的加速比。

1. 设计模式

使用设计模式可以有效的避免时间和空间上的开销,但不能过渡设计。

★代理模式

因为静态内部类在调用时才会被加载,故使用代理模式可以延迟加载,提高系统性能。

如Hibernate中取得对象的load()方法就采取了这种策略。

 

★享元模式

通过同一个工厂创造出不同的产品,从而在一定程度上节省了内存空间。与连接池等区

别是,连接池创建的是同一对象。

 

★装饰器模式

通过装饰器,可以提高类的聚合能力。如BufferedOutputStream类,在OutputStream的

基础上增加了缓冲区,减少了IO的访问次数,提高了系统性能。

 

★观察者模式

形如JDK中的JButton监听事件,只要设置触发动作和响应事件。一旦动作触发即可并监

听到。

 

★Value Object

网络传输对象,减少因为属性获取而导致的网络传输次数。

 

★业务代理

将一些业务流程封装在前台系统,为系统性能优化提供了基础平台。在业务代理中,不仅

可以复用业务流程,还可以视情况为展示层组件提供缓存等功能,从而减少远程方法调用

次数,降低系统压力。

 

2.缓冲

缓冲可以协调上层组件和下层组件的性能差。当上层组件性能优于下层组件时,可以有效

减少上层组件对下层组件的等待时间。基于这样的结构,上层应用不需要等待下层组件真

实地接受全部数据,即可返回操作,加快了上层组件的处理速度,从而提高系统的整体性

能。

★由于I/O操作很容易成为性能的瓶颈,尽可能在I/O读写中加入缓冲组件以提高系统的性

能。使用动态代理无需修改一个逻辑方法的代码,便可以为它加上缓存功能,提高其性能。

 

3.对象复用“池”

在程序中使用数据库连接池和线程池,可以有效的改善系统在高并发下的性能。只有对重

量级对象使用对象池技术才能提高系统性能。对轻量级的对象使用对象池,可能反而会降

低系统性能。

 

4.负载均衡

 在使用tomcat集群时,有两种基本的session共享模式。

★黏性session模式:所有的session信息被平均分配到各个tomcat节点。但一个节点宕机,

它所维护的session信息将丢失,不具备高可用性。且一台用户只能与一台tomcat交互,因

为其它tomcat节点上不保存这个用户信息。

 

★复制session模式:将所有session在所有的tomcat节点上保持一致。当一个节点的session

信息被修改,这个session会被广播到其它tomcat节点上以保持session同步。很容易引起网路

繁忙,影响系统效率。

 

5.String对象

不变性:不变模式的主要作用在于当一个对象需要被多线程共享,并且访问频繁时,可以省略同

步和锁等待的时间,从而大幅度提高系统性能。

 

针对常量池的优化:当两个String对象拥有相同的值时,它们只引用常量池中的同一个拷贝。当

同一个字符串反复出现时,可以大幅度节省内存空间。

 

subString()方法的内存泄露:在使用该函数时,采用空间换时间的手段,会重新new一个string

对象。应用程序无法使用它,因此在实际应用中,不用担心它所带来的麻烦。但是,java.lang包

内的对象对它的调用会引起内存泄露。

 

对于静态字符串的连接操作,java在编译时会进行彻底的优化,将多个连接操作的字符串在编译

时合成一个单独的长字符串。因此一些看起来很慢的代码,可能实际上并不会太慢。

例如:

String s = str1 + str2 + str3;

在编译时会转为 new StringBuilder(String.valueOf(str1)).append(str2).append(str3);

 

StringBuffer --------> 线程同步,需消耗大量资源。

StringBuilder-------> 线程不同步,效率更高。

 

如果能预先评估以上的大小,将能够有效地节省扩容等操作,从而提高系统的性能。在集合

ArrayList的扩容中同样适用。

当转化成字符串的时候,应当避免使用""串进行转化。使用合适的String.valueOf方法或者包装类的toString(value)方法。

尽量使用StringBuilder进行字符串拼接。

使用Java 6 update 20引入的-XX:+OptimizeStringConcat选项来提高字符串拼接的性能。

 

6.ArrayList与LinkedList

①ArrayList存在扩容,LinkedList不存在扩容等性能开销。

②ArrayList适合在结尾处添加删除元素,不适合任意位置。因为其每次添加删除元素时需重

新调整整个宿主,性能开销较大。

③在LinkedList的实现中,首先要通过循环找到要删除的元素。如果要删除的位置位于List的

前半段,则从前进行查找,反之亦然。但要移除List中间的元素,却几乎要遍历完半个List,在

List拥有大量元素的情况下,效率很低。

 

对ArrayList这些基于数组的实现来说,随机访问的速度是很快的。在遍历这些List对象时,可以

优先考虑随机访问。但对于LinkedList等基于链表的实现,随机访问的性能是非常差的,应避免

使用。

 

编译器将Foreach循环作为迭代器处理,二者是完全等价的。而且在foreach循环的迭代操作中,

又存在一步多余的赋值操作,从而导致Foreach循环的性能比直接使用迭代器略差一点。

 

7.HashMap

HashMap的高性能需要保证以下两点:

①Hash算法必须是高效的。

②Hash值到内存地址(数组索引)算法是快速的,可根据内存地址直接取得。

 

HashMap的扩容操作会遍历整个HashMap,应该尽量避免该操作的发生。设置合理的初始大小和

负载因子,可以有效地减少HashMap扩容的次数。

 

HashMap的性能在一定程度上取决于hashCode()的实现,一个好的hashCode()算法,可以尽可能

减少冲突,从而提高HashMap访问速度。

 

LinkedHashMap可以简单的理解为一个维护了元素次序表的HashMap

 

不要再迭代器模式中修改被迭代的集合。如果这么做,就会抛出ConcurrentModificationException异常,

这个特性适用于所有的集合类,包括HashMap,Vector,ArrayList等。

 

虽然一般认为get()方法是只读的,但是当前的LinkedHashMap却工作在按照元素访问顺序排序的模式中,

get()方法会修改LinkedHashMap中的链表结构,以便将最近访问的元素放置到链表的末层。当LinkedHashMap

工作在这种模式时,不能在迭代器中使用get()操作。

 

TreeMap:TreeMap排序方式和LinkedHashMap是不同的,LinkedHashMap是基于元素进入集合的顺序或被访问

的先后顺序排序。而TreeMap则是基于元素的固定顺序(由Comparator或者Comparable确定)

 

为了确定Key的排序算法,可以使用两种方式

①在TreeMap的构造函数中注入一个Comparator

public TreeMap(Comparator <? super K >  comparator)

②使用一个实现Comparable接口的Key。

 

8.Set

所有Set的实现,都只是对应的Map的一种封装而已。

HashSet             ----------->  HashMap               输出顺序毫无规律

LinkedHashSet ----------> LinkedHashMap     输出顺序与输入顺序完全一致

TreeSet               ----------> TreeMap                   输出顺序从小到大或者从大到小

 

9.优化集合访问代码

①分离循环中被重复调用的代码。

②省略相同的操作。

③如果可以,则尽量直接访问内部元素而不要调用对应的接口。函数调用是需要消耗系统资源的,直接访问会更

高效。

 

10.RandomAccess接口

主要是标示那些可支持快速随机访问的List实现。在JDK中,任何一个基于数组的List实现都实现了RandomAccess

接口,而基于链表的实现则都没有。因为只有数组能够进行快速的随机访问,而对链表的随机访问需要进行链表的遍历。

通过RandomAccess可以知道List是否支持快速随机访问。如果应用程序需要通过索引下标对List做随机访问,尽量

不要使用LinkedList。

0 0
原创粉丝点击