zoukankan      html  css  js  c++  java
  • 笔记《Java程序性能优化 让你的Java程序更快、更稳定》 第二章 设计调优

    2.1 善用设计模式 23 (1)

    1. 设计模式好处;

    clip_image001[6]

    2.1.1 单例模式 23 (6)

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

    2. 两大好处:a、对于频繁创建的对象,可以省略创建对象所花费的时间;b、new操作减少,因而对系统内存的使用频率也会降低,降低GC压力,缩短GC停顿时间;

    3. 单例模式的参与者:单例类和使用者;

    4. 第一种实现方式:私有默认构造器,静态getInstance方法;这种实现方式简单、可靠,但不能延迟加载;

    5. 第二种实现方式:私有默认构造器,创建时判断对象是否为空,提供同步的getInstance方法;这种实现方式虽然能够延迟加载,但引入了同步,多线程时时耗远远大于第一种实现;

    6. 第三种实现方式:私有默认构造器,私有静态内部类里边创建私有静态的实例,静态getInstance方法;当getInstance被调用时,才会加载SingletonHolder,从而初始化instance,同时,由于实例的建立是在类加载时完成的,故天生对多线程友好;

    clip_image003

    clip_image005[6]

    第一种实现:

    clip_image007[6]

    这种单例的实现方式简单、有效,但是不能延迟加载;

    clip_image009[6]

    clip_image011[6]

    clip_image013

    第二种实现方式,引入延迟加载:

    clip_image015

    clip_image017

    clip_image019

    第三种实现方式:

    clip_image021

    clip_image023

    clip_image025

    clip_image027

    clip_image029

    clip_image031

    clip_image033

    clip_image035

    2.1.2 代理模式 28 (17)

    1. 使用代理对象完成用户请求,屏蔽用户对真实对象的访问;

    2. 使用代理模式的意图:安全原因、远程调用、延迟加载(提升系统的性能和反应速度);

    3. 代理模式的主要参与者:主题接口、真实主题、代理类、客户端;

    4. 延迟加载的常用案例:数据库连接、系统启动,hibernate框架;

    5. 延迟加载的核心思想:如果当前并没有使用该组件,则不需要真正地初始化它,使用一个代理对象替代它原有的位置,只要在真正需要使用的时候,才对它进行加载;

    6. 动态代理是指在运行时,动态生成代理类;

    7. 和静态代理相比,动态代理的好处:1、不需要为真实主题写一个形式上完全一样的封装类,2、使用一些动态代理的生成方法甚至可以在运行时指定代理类的执行逻辑;

    8. 生成动态代理类的方法有4种:JDK自带的动态代理、CGLIB、javassist、ASM库;

    9. JDK自带的动态代理使用简单,但功能较弱;

    10. CGLIB和javassist是高级的字节码生成库,性能比JDK自带的好,且功能强大;

    11. ASM库低级的字节码生成工具,对开发人员要求较高,性能没有量级的提升,可维护性差,只在性能要求苛刻的地方使用;

    12. JDK的动态代理实现:实现InvocationHandler接口,通过Proxy.newProxyInstance方法创建代理;

    13. CGLIB的实现(javassist类似):实现MethodInterceptor接口,new Enhancer-->setCallback-->setInterfaces-->create;

    14. javassist有两种方式创建:1、使用代理工厂;2、通过使用动态代码创建;

    15. javassist代理工厂方式实现:实现MethodInterceptor接口,new ProxyFactory-->setInterfaces-->createClass-->newInstance;

    16. javassist使用动态代码方式创建,相当灵活,甚至可以在运行时生成业务逻辑;

    17. javassist动态代码方式实现:ClassPool,CtClass;

    clip_image037

    clip_image039

    clip_image041

    clip_image043

    clip_image045

    clip_image047

    clip_image049

    clip_image051

    jdk的动态代理实现:

    clip_image053

    clip_image055

    clip_image057

    CGLIB的实现:

    clip_image059

    clip_image061

    javassist的代理工厂实现:

    clip_image063

    clip_image065

    javassist动态java代码方式实现:

    clip_image067

    clip_image069

    几种实现方式性能测试:

    clip_image071

    clip_image073

    clip_image075

    clip_image077

    2.1.3 亨元模式 37 (8)

    1. 亨元模式以提高系统性能为目的,核心思想是如果一个系统中存在多个相同的对象,那么只需共享一份对象的拷贝,而不必每次都创建新的对象。

    2. 亨元模式对性能提升的主要帮助有两点:(1)、可以节省重复创建对象的开销,(2)、由于创建对象的数量减少,所以对内存的需求也减少,GC压力降低;

    3. 亨元模式的主要角色有4个:亨元工厂、抽象亨元、具体亨元类和主函数;

    4. 一般情况下,亨元工厂会维护一个对象列表,如果请求的对象已经创建,则直接返回,如果没有,则创建对象加入到维护队列中;

    5. 亨元模式的典型应用:SAAS系统(Software As A Service,目前比较流行的一种软件应用模式);

    6. 以一个人事管理系统的SAAS系统为例,公司甲、乙、丙都为这个SAAS系统的用户,则3个亨元实例,就足以应付300个员工的查询请求了;

    7. 亨元模式和对象池的最大不同是亨元模式的对象都是不可以相互替代的,对象池的对象都是等价的,如数据库连接池中的数据库连接;

    8. 报表实例;

    clip_image079

    clip_image081

    clip_image083

    clip_image085

    clip_image087

    clip_image089

    实例:

    clip_image091

    clip_image093

    clip_image095

    clip_image097

    clip_image099

    clip_image101

    clip_image103

    2.1.4 装饰者模式 40 (7)

    1. 装饰者模式可以动态添加对象功能;

    2. 有一条重要的设计准则叫做合成/聚合复用原则:代码复用应该尽可能使用委托,而不是使用继承;因为继承是一种紧密耦合,任何父类的改动都会影响其子类,不利于系统维护。而委托则是松散耦合,只要接口不变,委托类的改动并不会影响其上层对象;

    3. 装饰者模式通过委托机制,复用系统中的各个组件,在运行时,可以将这些功能组件进行叠加,从而构造一个“超级对象”,使其拥有所有这些组件的功能;

    4. 装饰者模式的好处:可以有效地分离性能组件和功能组件,从而提升模块的可维护性并增加模块的复用性;

    5. 装饰者模式的主要角色有4个:组件接口、具体组件、装饰者、具体装饰者;

    6. 装饰者的典型案例是对输出结果进行增强,对输出内容转化为HTML,增加HTTP头;

    7. JDK中不少组件用装饰者模式实现,比如OutputStream和InputStream类族的实现;

    clip_image105

    clip_image107

    clip_image109

    案例:

    clip_image111

    clip_image113

    clip_image115

    clip_image117

    clip_image119

    clip_image121

    clip_image123

    clip_image125

    clip_image127

    clip_image129

    jdk中的不少组件用装饰者模实现;

    clip_image131

    clip_image133

    clip_image135

    clip_image137

    clip_image139

    clip_image141

    clip_image143

    2.1.5 观察者模式 46 (5)

    1. 当一个对象的行为依赖于另一个对象的状态时,适用于观察者模式,如果不用,则一般需要在另一个线程中不停监听对象所依赖的状态,增加系统的负担;

    2. 观察者模式的意义在于:可以在单线程中,使某一个对象,及时得知自身所依赖的状态变化。

    3. 观察者模式可以用于事件监听、通知发布等场合;

    4. 观察者模式的主要角色有4个:主题接口、具体主题、观察者接口、具体观察者;

    5. JDK有一套观察者模式的实现,java.util.Observable类和java.util.Observer接口,

    clip_image145

    clip_image147

    clip_image149

    实例:

    clip_image151

    clip_image153

    clip_image155

    clip_image157

    clip_image159

    clip_image161

    clip_image163

    2.1.6 Value Object模式 50 (2)

    1. 在J2EE软件开发中,通常会对系统模块进行分层;

    2. Value Object模式提倡将一个对象的各个属性进行封装,将封装后的对象在网络中传递,从而拥有更好的交互模型,并且减少网络通信数据,从而提高系统性能;

    clip_image165

    clip_image167

    clip_image169

    2.1.7 业务代理模式 53 (2)

    1. 业务代理模式是将一组由远程方法调用构成的业务流程,封装在一个位于展示层的代理中;

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

    clip_image171

    clip_image173

    clip_image175

    clip_image177

    clip_image179

    clip_image181

    clip_image183

    clip_image185

    2.2 常用优化组件和方法 56 (2)

    1. 组件:缓冲和缓存;

    2. 常用的5个优化思想:池化对象、并行代替串行、负载均衡、时间换空间、空间换时间;

    2.2.1 缓冲(Buffer) 56 (6)

    1. 缓冲区是一块特定的内存区域;

    2. 开辟缓冲区的目的是缓解应用程序上下层之间的性能差异,提高系统的性能。

    3. 日常生活中,缓冲区的一个典型应用是漏斗;

    4. 缓冲可以协调上层组件和下层组件的性能差,当上层组件性能优于下层组件时,可以有效较少上层组件对下层组件的等待时间;

    5. 缓冲最常用的场景就是提高I/O的速度;

    6. 案例:使用缓冲区提升动画显示效果(先在内存中画圆,然后一次性显示出来);

    clip_image187

    clip_image189

    clip_image191

    clip_image193

    clip_image195

    clip_image197

    clip_image199

    案例:使用缓冲区提升动画显示效果

    clip_image201

    clip_image203

    clip_image205

    clip_image207

    clip_image209

    2.2.2 缓存(Cache) 59 ()

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

    2. 案例:(1)、目前流行的几种浏览器都会在本地缓存远程的页面,从而减少远程HTTP访问次数;(2)、在服务器端的系统开发中,设计人员可以为一些核心API加上缓存,从而提供系统的整体性能;

    3. 最为简单的缓存实现是使用HashMap实现,会有很多问题:何时清理无效的数据、如何防止缓存数据过多而导致内存溢出等;

    4. 稍好的解决方案是直接使用WeakHashMap,它使用弱引用维护一张哈希表,从而避免了潜在的内存溢出问题,但作为专业的缓存,功能略有不足;

    5. Java的3种缓存框架:EHCache、OSCache和JBOSSCache等;

    6. EHCache缓存出自Hibernate,是Hibernate框架默认的数据缓存解决方案;

    7. OSCache缓存是由OpenSymphony设计的,它可以用于缓存任何对象,甚至缓存部分JSP或HTTP请求;

    8. JBOSSCache是由JBOSS开发,可用于JBOSS集群间数据共享的缓存框架;

    9. EHCache缓存配置实例:定义了一个默认的Cache模板,定义了两个缓存,名字分别是cache1和cache2;

    10. 什么时候使用缓存:在频繁使用且重负载的函数实现中,加入缓存,以提高它在频繁调用时的性能;

    11. 为方法加入缓存有两种方式:(1)、硬编码方式,在方法中加入缓存的处理逻辑,缺点是缓存组件和业务代码紧密结合,依赖性强;(2)、动态代理,好处是在业务层,无需关注对缓存的操作,缓存操作代码被完全独立并隔离,并且对一个新的函数方法加入缓存不会影响原有方法的实现,是一种比较灵活的软件结构;

    12. 实例:整数做因式分解;

    clip_image211

    clip_image213

    clip_image215

    clip_image217

    clip_image219

    clip_image221

    clip_image223

    实例:对整数做因式分解;

    clip_image225

    clip_image227

    clip_image229

    clip_image231

    clip_image233

    clip_image235

    2.2.3 对象复用--“池” 63 (8)

    1. 对象池化:如果一个类被频繁请求使用,那么不必每次都生成一个实例,可以将这个类的一些实例保存在“池”中,待需要使用的时候直接从池中获取;

    2. 实现上可能是数组、链表、任何集合类;常见的案例有:线程池和数据库连接池;

    3. 目前应用较为广泛的数据库连接池组件有C3P0、dbcp、Proxool;C3P0是伴随Hibernate一起发布的,与Hibernate联系紧密的数据库连接池;

    4. 在JDK中,new操作的效率是相当高的,不需要担心频繁的new操作对系统有性能影响。但是new操作时所调用的类构造函数可能是非常费时的,对于这些对象,可以考虑池化;

    5. 实际开发中,不用自己实现对象池,在Apache中,已经提供了一个Jakarta Commons Pool对象池组件,可以直接使用,主要有:接口ObjectPool、接口PoolableObjectFactory;

    6. Jakarta Commons Pool中内置定义了3个对象池,分别是StackObjectPool、GenericObjectPool、SoftReferenceObjectPool。

    7. 实例:对象池工厂;

    8. 只有对重量级对象使用对象池技术才能提供系统性能,对轻量级的对象使用对象池,可能反而降低系统性能;

    clip_image237

    clip_image239

    clip_image241

    clip_image243

    clip_image245

    clip_image247

    clip_image249

    clip_image251

    clip_image253

    clip_image255

    clip_image257

    clip_image259

    clip_image261

    clip_image263

    clip_image265

    clip_image267

    实例:对象池工厂

    clip_image269

    clip_image271

    clip_image273

    clip_image275

    2.2.4 并行替代串行 69 (1)

    1. java对多线程的支持为多核计算提供了强有力的保障。

    2.2.5 负载均衡 69 (8)

    1. 对大型应用来说,系统负载可能非常重,以网站应用为例,如果并发数很多,则单台计算机就无法承受,此时,为保障应用程序的服务质量,需要使用多台计算机协同工作,将系统负载尽可能均匀分配到各个计算机节点上;

    2. 典型实现是Tomcat集群:使用Apache作为负载分配器,将请求转向各个Tomcat服务器;

    3. 在使用tomcat集群时,有两种基本的session共享模式:黏性Session模式和复制Session模式;

    4. 黏性session模式:所有的session信息被平均分配到各个tomcat节点上,以实现负载均衡,但一旦一个节点宕机,它所维护的session将丢失,不具备高可用性,且同一个用户只能与一台tomcat交互,因为其他tomcat节点不保存这个用户信息;

    5. 复制session模式:所有的session在所有的节点上保持一致。当一个节点上的session信息被修改,这个session会被广播到其他tomcat节点上,以保持session同步。缺点是容易引起网络繁忙,影响系统效率;

    6. 成熟的高可用性系统解决方案:Terracotta,它是java开源软件中,一款跨JVM虚拟机专门用于分布式缓存的框架,使用它也可以实现Tomcat的session共享;

    7. Terracotta是一款企业级的、开源的、JVM层的集群解决方案,它可以实现分布式对象共享、分布式缓存、分布式session等功能,可以作为负载均衡、高可用性的解决方案;官网:http://terracotta.org;

    8. terracotta实例:分布式对象共享、session共享;

    clip_image277

    clip_image279

    clip_image280

    clip_image282

    terracotta应用案例:

    clip_image284

    clip_image286

    clip_image288

    clip_image290

    clip_image292

    clip_image294

    clip_image295

    clip_image297

    clip_image299

    clip_image301

    clip_image303

    clip_image305

    clip_image306

    clip_image308

    2.2.6 时间换空间 75 (4)

    1. 由于系统资源是有限的,为了在有限的系统资源内,达成某些特定的性能目标,就需要使用时间换空间或空间换时间的方法。

    2. 时间换空间常用用于嵌入式设备,或者内存、硬盘空间不足等情况,通过使用牺牲CPU的方式,获得原本需要更多内存或者硬盘空间才能完成的工作;

    3. 一个非常简单的时间换空间的算法:实现了a、b两个变量的值交换。常用的算法是采用中间变量,而引入额外的变量意味着要使用更多的空间,以下算法,则可以免去中间变量,而达到变量交换的目的,代价是引入更多的CPU运算;
    a=a+b;
    b=a-b;
    a=a-b;

    4. 另一个比较有用的例子是对无符号数整数的支持。在java语言中,不支持无符号整数,这意味着当需要无符号的byte时,需要使用short代替,这也意味着空间上的浪费。使用位运算符模拟无符号byte;

    clip_image310

    clip_image312

    clip_image314

    2.2.7 空间换时间 76 (3)

    1. 与时间换空间相反,空间换时间是尝试使用更多的内存或者磁盘空间换取CPU资源或者网络资源等,通过增加系统的内存消耗,来加快程序的运行速度;

    2. 典型案例是缓存。空间换时间是一种设计思路,除了缓存外,在一些算法中,也可以使用这样的技术。

    3. 空间换时间排序方法;

    clip_image316

    clip_image318

    clip_image320

    clip_image322

    clip_image324

  • 相关阅读:
    Python入门-函数进阶
    Python入门-初始函数
    Leetcode300. Longest Increasing Subsequence最长上升子序列
    Leetcode139. Word Break单词拆分
    Leetcode279. Perfect Squares完全平方数
    Leetcode319. Bulb Switcher灯泡开关
    Leetcode322. Coin Change零钱兑换
    二叉树三种遍历两种方法(递归和迭代)
    Leetcode145. Binary Tree Postorder Traversal二叉树的后序遍历
    Leetcode515. Find Largest Value in Each Tree Row在每个树行中找最大值
  • 原文地址:https://www.cnblogs.com/yejq/p/4437820.html
Copyright © 2011-2022 走看看