IT学习笔记(三)(持续更新)

一、Java程序性能优化

1. 程序的性能通过以下几个方面来表现:

1)执行速度:程序的反映是否迅速,响应时间是否足够短;

2)内存分配:内存分配是否合理,是否过多地消耗内存或者存在泄漏;

3)启动时间:程序从运行到可以正常处理业务需要花费多长时间;

4)负载承受能力:当系统压力上升时,系统的执行速度、响应时间的上升曲线是否平缓。

2.性能的参考指标:

1)执行时间 2)CPU时间  3)内存分配  4)磁盘吞吐量  5)网络吞吐量  6)响应时间

3.数据库调优:

       当使用JDBC进行查询时,对于大量的拥有相同结构的SQL查询,可以使用PreparedStatement代替Statement,以提高数据库的查询效率;在Select语句中,显示指定要查询的列名,避免使用星号“*”;为提高数据库查询效率,可以建立有效且合理的索引。 

4.设计优化:

1)单例模式

     它是一种对象创建模式,用于产生一个对象的具体实例,可以确保系统中一个类只产生一个实例。

     在Java中有两大好处:一是:对于频繁使用的对象,可以省略创建对象所花费的时间;二是:由于new操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻GC压力,缩短GC停顿时间。

    对于系统的关键组件和频繁使用的对象,使用单例模式便可以有效地改善系统的性能。

    单例类必须要有一个private访问级别的构造函数,能确保单例不会在系统中的其他代码内被实例化,同时,定义的成员变量和方法必须是static的。

    使用内部类的方式实现单例,既可以做到延迟加载,也不必使用同步关键字,是一种比较完善的实现。

    序列化和反序列化可能会破坏单例。一般来说,对单例进行序列化和反序列化的场景并不多见,但如果存在,就要多加注意。

2)代理模式

    它使用代理对象完成用户请求,屏蔽用户对真实对象的访问。

    在软件设计中,使用代理模式的意图很多,比如因为安全原因,需要屏蔽客户端直接访问真实对象;或者在远程调用中,需要使用代理类处理远程方法调用的技术细节;可能为了提高系统性能,对真实对象进行封装,从而达到延迟加载的目的。

    延迟加载的核心思想是:如果当前并没有使用这个组件,则不需要真正地初始化它,使用一个代理对象替代它的原有的位置,只要在真正需要使用的时候,才对它进行加载。在系统启动时,讲消耗资源最多的方法都是用代理模式分离,就可以加快系统的启动速度,减少用户的等待时间。

   动态代理是指在运行时,动态生成代理类,即代理类的字节码将在运行时生成并载入当前的ClassLoader。这样不必为每一个主题接口写一个代理方法(接口改动,则真实主题和代理类都要修改,不利于维护)。

3)享元模式

    核心思想:如果在一个系统中存在多个相同的对象,那么只需共享一份对象的拷贝,而不必为每一次使用都创建新的对象。

    享元模式对性能提升的主要两点:一是:可以节省重复创建对象的开销,因为被享元模式维护的相同对象只会被创建一次。二是:由于创建对象的数量减少,所以对系统内存的需求也减少。

    享元模式的主要角色有享元工厂(用以创建具体享元类,维护相同的享元对象。保证相同的享元对象可以被系统共享。)、抽象享元(定义需共享的对象的业务接口)、具体享元类(实现抽象享元类的接口,完成某一具体逻辑)和主函数(使用享元模式的组件,通过享元工厂取得享元对象)几部分组成。

    享元工厂是享元模式的核心,它需要确保系统可以共享相同的对象。一般情况下,享元工厂会维护一个对象列表,当任何组件尝试获取享元类时,如果请求的享元类已经被创建,则直接返回已有的享元类;若没有,则创建一个新的享元对象,并将它加入到维护队列中。

    享元工厂和对象池的重要区别:在一个对象池中,所有的对象都是等价的,任意两个对象在任何场景中都可以被对象池中的其他对象代替;而在享元模式中,享元工厂所维护的所有对象都是不同的,任何两个对象间不能相互代替。

4)装饰者模式

    装饰者模式通过委托机制,复用系统中的各个组件,在运行时,可以将这些功能组件进行叠加,从而构造一个“超级对象”,使其拥有所有这些组件的功能。装饰者模式可以有效分离性能组件和功能组件,从而提升模块的可维护性并增加模块的复用性。

    装饰者模式的主要角色有:组件接口(定义了被装饰者的核心功能和装饰者需要加强的功能点)、具体组件(实现了组件接口的核心方法,完成某一个具体的业务逻辑)、装饰者(实现组件接口,并持有一个具体的被装饰者对象)、具体装饰者(具体实现装饰的业务逻辑,即实现了被分离的各个增强功能点)。

5)观察者模式

    它可以在单线程中,使某一对象,及时得知自身所依赖的状态的变化。观察者模式可以用于事件监听、通知发布等场合。可以确保观察者在不使用轮训监控的情况下,及时收到相关消息和事件。

    观察者模式的主要角色有:主题接口(指被观察的对象。当其状态发生改变或者某事件发生时,它会将这个变化通知观察者。它维护了观察者所需要依赖的状态)、具体主题(实现了主题接口中的方法。其内部维护一个观察者列表。)、观察者接口(定义了观察者的基本方法。当依赖状态发生改变时,主题接口就会调用观察者的update()方法)、具体观察者(实现了观察者接口的update(),具体处理当被观察者状态改变或某一事件发生时的业务逻辑)。

6)Value Object模式

    它提倡将一个对象的各个属性进行封装,将封装后的对象在网络中传递,从而使系统拥有更好的交互模型,并且减少网络通信数据,从而提高系统性能。使用Value Object模式可以有效减少网络交互次数,提高远程调用方法的性能,也能使系统接口具有更好的可维护性。

7)业务代理模式

    它将一组由远程方法调用构成的业务流程,封装在一个位于展示层的代理类中。

    业务代理模式将一些业务流程封装在前台系统,为系统性能优化提供了基础平台。在业务代理中,不仅可以复用业务流程,还可以视情况为展示层组件提供缓存等功能,从而减少远程方法调用次数,降低系统压力。

5.常用优化组件和方法

1)缓冲(Buffer)

    缓冲区是一块特定的内存区域。开辟缓冲区的目的是通过缓解应用程序上下层之间的性能差异,提高系统的性能。 当上层组件性能优于下层组件时,可以有效减少上层组件对下层组件的等待时间。缓冲最常用的场景就是提高I/O的速度。

    一般来说,缓冲区不宜过小,过小的缓冲区无法起到真正的缓冲作用,缓冲区也不宜过大,过大的缓冲区会浪费系统内存,增加GC负担。

2)缓存(Cache)

    缓存是一块为提升系统性能而开辟的内存空间,他的主要作用是暂存数据处理结果,并提供下次访问使用。

    缓存可以保存一些来之不易的数据或者计算结果。当需要再次使用这些数据时,可以从缓存中低成本地获取,而不需要再占用宝贵的系统资源。 

3)池

    对象池化是目前非常常用的一种系统优化技术。它的核心思想是i,如果一个类被频繁请求使用,那么不必每次都生成一个实例,可以将这个类的一些实例保存在一个“池”中,待需要使用的时候从池中获取。在实现细节上,它可能是一个数组,一个链表或者任何集合类。最为熟悉的对象池有线程池和数据库池。

4)负载均衡

    并发数很多时,单台计算机无法承受,为保证应用程序的服务质量,使用多台计算机协同工作,将系统负载尽可能均匀地分配到各个计算机节点上。

    Terracotta是一款企业级的、开源的、JVM层的集群解决方案。它可以实现诸如分布式对象共享、分布式缓存、分布式Session等功能。可以作为负载均衡、高可用性的解决方案。

6.字符串优化处理

1)String对象及其特点

    String对象的3个基本特点:一是不变性:指String对象一旦生成,则不能再对它进行改变。这种不变模式的主要作用在于当一个对象需要被多线程共享,并且访问频繁时,可以省略同步和锁等待的时间,从而大幅度提高系统性能。二是针对常量池的优化:指当两个String对象拥有相同的值时,它们只引用常量池中的同一个拷贝。当同一个字符串反复出现时,这个技术可以大幅度节省内存空间。三是类的final定义:作为final类的String对象在系统中不可能有任何子类,这是对系统安全性的保护。

2)StringBuffer 和 StringBuilder

    String对象具有不变性,为了能高效地动态生成和构建字符串对象,就需要使用StringBuffer和StringBuilder类。

    它们都实现了AbstractStringBuilder抽象类,拥有几乎相同的对外接口,两者的最大不同在于StringBuffer对几乎所有方法都做了同步,而StringBuilder并没有做任何同步。同步需要消耗一定的系统资源,所以StringBuffer的效率要低于StringBuilder。但是在多线程中,StringBuilder无法保证线程安全,不能使用。

    扩容策略是将原来的容量大小翻倍,以新的容量申请内存空间,建立新的char数组,然后将原数组中的内容复制到这个新的数组中。因此大对象的扩容会涉及大量的内存复制操作,所哟,如果能够与西安评估StringBuilder的大小,将有效地节省这些操作,从而提高系统的性能。

7.核心数据结构

1)List接口

    3种List重要实现,即ArraryList、Vector和LinkedList。

    ArraryList和Vector使用了数组实现,封装了对内部数组的操作,因此,对 ArraryList或Vector的操作,等价于对内部对象数据的操作。两者的区别是, ArraryList没有对任何一个方法做线程同步,因此不是线程安全的;Vector中绝大部分方法都做了线程同步,是一种线程安全的实现。

    LinkedList使用了循环双向链表数据结构,其链表由一系列表项连接而成,一个表项总包含3个部分:元素内容、前驱表项和后驱表项。

   ArraryList是基于数组实现的,而数组是一块连续的内存空间,如果在数组的任意位置插入或删元素,必然导致在该位置后的所有元素需要重新排序,因此,其效率相对会比较低。而对LinkedList来说,在任意位置插入或者删除元素,效率要比ArraryList高。

    对ArraryList这些基于数组的实现来说,随机访问的速度是很快的。但对于LinkedList等基于链表的实现,随机访问性能是非常差的,应避免使用。

2)Map接口

    Map接口主要实现的类有Hashtable、HashMap、LinkedHashMap和TreeMap。

    Hashtable和HashMap两者的差别是,首先,Hashtable的大部分做了同步,而HashMap没有,因此HashMap不是线程安全的;其次,Hashtable不允许key或者value使用null值,而HashMap可以。第三,在内部算法上,它们对key的hash算法和hash值到内存索引的映射算法不同。

    存入到HashMap中的元素,在遍历时,其输出是无序的,若想保存元素输入时的顺序,则需要使用LinkedHashMap。LinkedHashMap继承自HashMap,具备HashMap的高效性,而在HashMap的基础上,LinkedHashMap又在内部增加了一个链表,用以存放元素的顺序,因此,LinkedHashMap是一个维护了元素次序表的HashMap。

    TreeMap实现了SortedMap接口,可以对元素进行排序,但它的性能略微低于HashMap。与LinkedHashMap不同,LinkedHashMap是根据元素增加或者访问的先后顺序进行排序,而TreeMap则根据元素的key进行排序。要正常使用TreeMap,一定要通过两种方式(即在TreeMap的构造函数中注入一个Comparator或者使用一个实现了Comparable接口的key)中的一种将排序规则传递给TreeMap。如果既不指定Comparator,又不去实现Comparable接口,那么在put()操作时,就会抛出异常。

3)Set接口

    Set集合中的元素是不能重复的。Set接口的重要实现包括HashSet、LinkedHashSet和TreeSet。

    HashSet,基于hash的快速元素插入,元素间无顺序;LinkedHashSet,基于hash的快速元素插入,同时维护着元素插入集合时的先后顺序,遍历集合时,总是按照先进先出的顺序排序。TreeSet,基于红黑树的实现,有着高效的基于元素key的排序算法。

猜你喜欢

转载自blog.csdn.net/xudasong123/article/details/79773556