




免费预览已结束,剩余39页可下载查看
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1.用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实例非常简单。例如,下面是Factory模式的一个典型实现:public static Credit getNewCredit() return new Credit(); 改进后的代码使用clone()方法,如下所示:private static Credit BaseCredit = new Credit();public static Credit getNewCredit() return (Credit) BaseCredit.clone();面的思路对于数组处理同样很有用。 2. 使用非阻塞I/O 版本较低的JDK不支持非阻塞I/O API。为避免I/O阻塞,一些应用采用了创建大量线程的办法(在较好的情况下,会使用一个缓冲池)。这种技术可以在许多必须支持并发I/O流的应用中见到,如Web服务器、报价和拍卖应用等。然而,创建Java线程需要相当可观的开销。 3. 慎用异常 异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。 4. 不要重复初始化变量 默认情况下,调用类的构造函数时, Java会把变量初始化成确定的值:所有的对象被设置成null,整数变量(byte、short、int、long)设置成0,float和double变量设置成0.0,逻辑值设置成false。当一个类从另一个类派生时,这一点尤其应该注意,因为用new关键词创建一个对象时,构造函数链中的所有构造函数都会被自动调用。 5. 尽量指定类的final修饰符 带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String。为String类指定final防止了人们覆盖length()方法。另外,如果指定一个类为final,则该类所有的方法都是final。Java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50%。 6. 尽量使用局部变量 调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。另外,依赖于具体的编译器/JVM,局部变量还可能得到进一步优化。 7. 乘法和除法 考虑下面的代码: for (val = 0; val 100000; val +=5) alterX = val * 8; myResult = val * 2; 用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码:for (val = 0; val 100000; val += 5) alterX = val 3; myResult = val 1; 修改后的代码不再做乘以8的操作,而是改用等价的左移3位操作,每左移1位相当于乘以2。相应地,右移1位操作相当于除以2。值得一提的是,虽然移位操作速度快,但可能使代码比较难于理解,所以最好加上一些注释。3. 选择合适的引用机制在典型的JSP应用系统中,页头、页脚部分往往被抽取出来,然后根据需要引入页头、页脚。当前,在JSP页面中引入外部资源的方法主要有两种:include指令,以及include动作。include指令:例如该指令在编译时引入指定的资源。在编译之前,带有include指令的页面和指定的资源被合并成一个文件。被引用的外部资源在编译时就确定,比运行时才确定资源更高效。 include动作:例如 该动作引入指定页面执行后生成的结果。由于它在运行时完成,因此对输出结果的控制更加灵活。但时,只有当被引用的内容频繁地改变时,或者在对主页面的请求没有出现之前,被引用的页面无法确定时,使用include动作才合算。 4. 在spring中对orm层的动作设置只读属性 将 (只对数据库进行读取的操作) 设置只读属性10. Servlet与内存使用 许多开发者随意地把大量信息保存到用户会话之中。一些时候,保存在会话中的对象没有及时地被垃圾回收机制回收。从性能上看,典型的症状是用户感到系统周期性地变慢,却又不能把原因归于任何一个具体的组件。如果监视JVM的堆空间,它的表现是内存占用不正常地大起大落。解决这类内存问题主要有二种办法。第一种办法是,在所有作用范围为会话的Bean中实现HttpSessionBindingListener接口。这样,只要实现valueUnbound()方法,就可以显式地释放Bean使用的资源。 另外一种办法就是尽快地把会话作废。大多数应用服务器都有设置会话作废间隔时间的选项。另外,也可以用编程的方式调用会话的setMaxInactiveInterval()方法,该方法用来设定在作废会话之前,Servlet容器允许的客户请求的最大间隔时间,以秒计算。 11. HTTP Keep-Alive Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。市场上的大部分Web服务器,包括iPlanet、IIS和Apache,都支持HTTP Keep-Alive。对于提供静态内容的网站来说,这个功能通常很有用。但是,对于负担较重的网站来说,这里存在另外一个问题:虽然为客户保留打开的连接有一定的好处,但它同样影响了性能,因为在处理暂停期间,本来可以释放的资源仍旧被占用。当Web服务器和应用服务器在同一台机器上运行时,Keep-Alive功能对资源利用的影响尤其突出。 12. JDBC与Unicode 想必你已经了解一些使用JDBC时提高性能的措施,比如利用连接池、正确地选择存储过程和直接执行的SQL、从结果集删除多余的列、预先编译SQL语句,等等。除了这些显而易见的选择之外,另一个提高性能的好选择可能就是把所有的字符数据都保存为Unicode(代码页13488)。Java以Unicode形式处理所有数据,因此,数据库驱动程序不必再执行转换过程。但应该记住:如果采用这种方式,数据库会变得更大,因为每个Unicode字符需要2个字节存储空间。另外,如果有其他非Unicode的程序访问数据库,性能问题仍旧会出现,因为这时数据库驱动程序仍旧必须执行转换过程。13. JDBC与I/O如果应用程序需要访问一个规模很大的数据集,则应当考虑使用块提取方式。默认情况下,JDBC每次提取32行数据。举例来说,假设我们要遍历一个5000行的记录集,JDBC必须调用数据库157次才能提取到全部数据。如果把块大小改成512,则调用数据库的次数将减少到10次。在一些情形下这种技术无效。例如,如果使用可滚动的记录集,或者在查询中指定了FOR UPDATE,则块操作方式不再有效。Java代码优化尽可能地使用stack(栈)变量(方法内部的局部变量) Java程序包含了大量的对象,我们需要了解它们是从哪里被访问的,变量存储于何处对程序的性能有显著的影响尤其是某些需要被频繁访问的变量。我们写一个Java类,在其内部方法中定义的局部变量或对象是存储在stack(堆栈)中的,且JVM是一种stack-based的,因此访问和操纵stack中的数据时性能最佳。而Java类的instance变量(这个类的field)和static变量是在constant pool(常量池)中存储和得到访问的。constant pool中保存了所有的符号引用(symbolic references),指向所有型别(types)、值域(field),以及每个型别所使用的所有函数(mothods)。访问instance和static变量时,由于它们存放于constant pool中,所以JVM需要使用更多更耗时的操作码(分析程序生成的bytecode可以看出来)来访问它们。下面给出一段代码示例,对比后说明怎么尽可能地使用stack变量:package test;public class StackVars private int x; / instance变量 private static int staticX; /static 变量 public void stackAccess(int val) /访问和操作stack变量j int j = 0; for (int i = 0; i val; i+) j += 1; public void instanceAccess(int val) /访问和操作instance变量x for (int i = 0; i val; i+) x += 1; public void staticAccess(int val) /访问和操作static变量staticX for (int i = 0; i val; i+) staticX += 1; 经测试,发现运行instanceAccess()和staticAccess()方法的时间大约相同,但却比运行stackAccess()方法慢了23倍。因此我们对instanceAccess()、staticAccess()两个方法的代码作以下调整,以得到更快的性能:public void instanceAccess(int val) /访问和操作instance变量x int tempX=x; for (int i = 0; i val; i+) tempX += 1; x=tempX; public void staticAccess(int val) /访问和操作static变量staticX int tempStaticX=staticX; for (int i = 0; i val; i+) tempStaticX += 1; staticX=tempStaticX; 改善之处就是将instance和static变量放到循环之外,而用一个stack变量来完成多次局部运算,最后再将这个stack变量的值传回instance或static变量,从而提高了代码的性能。Sun JDK自带JVM内存使用分析工具HProf 使用Sun JDK自带JVM内存使用分析工具HProf可以分析JVM堆栈,从而找到占用内存较大的对象。这对应经常出现内存泄漏(OOM)的JAVA系统进行调优很有帮助。HProf使用方法 在WeblogicServer启动脚本中增加-Xrunhprof:heap=sites,重新启动WeblogicServer。 使用kill -3 或退出WeblogicServer均会生成java.hprof.txt文件,直接打开此文件便可分析JVM的具体运行情况。从java.hprof.txt记录的JVM堆栈结果中可以发现JVM占用内存较大的对象: percent live alloced stack class rank self accum bytes objs bytes objs trace name 1 4.57% 4.57% 2289696 47702 8392224 174838 4251 C 2 3.99% 8.57% 2000016 1 2000016 1 12308 C 3 3.65% 12.22% 1827552 9622 1852672 10082 43265 C 4 2.58% 14.80% 1293912 53913 3929424 163726 4258 java.lang.String 5 2.05% 16.85% 1028664 7585 3207272 24923 4252 C 6 2.03% 18.88% 1015816 159 1015816 159 18694 B 7 1.88% 20.77% 942080 230 2740224 669 20416 B 8 1.61% 22.37% 805752 2142 2150856 4635 45318 B 9 1.60% 23.98% 802880 772 802880 772 24710 weblogic.servlet.utils.URLMatchMap$URLMatchNode 10 1.60% 25.57% 799400 19985 2781400 69535 45073 cnc.util.Field 11 1.36% 26.93% 679360 3805 679360 3805 494 B 12 1.35% 28.28% 674856 28119 5181240 215885 2985 java.util.HashMap$Entry 96 0.19% 63.73% 94776 3112 94776 3112 9146 C 97 0.19% 63.92% 93456 3894 123936 5164 23631 java.lang.String 98 0.19% 64.10% 93224 3884 123968 5165 23644 java.lang.String 99 0.19% 64.29% 93192 3883 123936 5164 23636 java.lang.String 100 0.18% 64.47% 89528 238 240264 520 33227 B 101 0.17% 64.64% 86448 1901 103472 2255 18715 java.lang.Object 102 0.17% 64.81% 85464 676 85768 695 18715 S 103 0.17% 64.98% 85184 1331 85184 1331 28266 ernal.MethodDescriptor 104 0.17% 65.15% 84224 752 84224 752 24148 ernal.dd.ServletDescriptor 105 0.17% 65.32% 84136 528 50471136 348769 63 C 106 0.16% 65.48% 79968 1428 388976 6946 5503 java.lang.reflect.Method 107 0.15% 65.63% 77520 1615 77520 1615 27967 weblogic.ejb20.deployer.mbimpl.MethodInfoImpl 108 0.15% 65.79% 77056 4816 469808 29363 20250 java.lang.Object 109 0.15% 65.94% 76960 74 76960 74 23695 B 110 0.15% 66.09% 76104 3171 215040 8960 45071 cnc.util.FyCol 111 0.15% 66.24% 74688 3112 74688 3112 9152 java.util.Hashtable$Entry 112 0.15% 66.39% 74688 3112 74688 3112 9147 java.lang.String 113 0.15% 66.54% 74280 61 794328 788 45313 C 114 0.14% 66.68% 72480 1510 436032 9084 45353 C 115 0.14% 66.82% 70720 68 70720 68 25869 B 116 0.14% 66.97% 70720 68 70720 68 27448 B 117 0.14% 67.11% 70272 1279 142672 2439 5503 C 118 0.14% 67.24% 69256 86 69256 86 6584 S 119 0.13% 67.38% 67056 66 67056 66 28882 java.lang.Object 120 0.13% 67.51% 66176 752 66176 752 24170 ernal.dd.UIDescriptor 121 0.13% 67.64% 65688 715 65688 715 25389 C 122 0.13% 67.77% 65600 4 885600 54 23939 C 123 0.13% 67.90% 65600 4 623200 38 40639 C 124 0.13% 68.03% 65576 367 65576 367 51686 C 125 0.13% 68.17% 65568 2 65568 2 30610 java.util.HashMap$Entry 126 0.13% 68.30% 65568 2 130816 16 43271 java.util.HashMap$Entry 127 0.13% 68.43% 65552 1 65552 1 16617 B 128 0.13% 68.56% 64600 1615 64600 1615 27969 java.util.HashMap 129 0.13% 68.68% 63888 2662 64032 2668 16951 java.util.HashMap$Entry 130 0.13% 68.81% 63888 2662 64032 2668 16997 java.util.HashMap$Entry 131 0.13% 68.94% 63888 2662 64032 2668 16996 ernal.ClientMethodDescriptor 132 0.13% 69.07% 63888 2662 99120 4130 16949 java.lang.String 133 0.13% 69.19% 63888 2662 64032 2668 16976 java.lang.String 134 0.13% 69.32% 63232 152 63232 152 9655 weblogic.utils.collections.ConcurrentHashMap$Entry 135 0.13% 69.45% 63232 152 63232 152 9704 weblogic.utils.collections.ConcurrentHashMap$Entry 136 0.12% 69.57% 62168 3885 82632 5164 23628 B 137 0.12% 69.69% 61680 406 66904 468 1 C 138 0.12% 69.82% 61504 4 246016 16 47372 B 139 0.12% 69.94% 61144 36 91019160 23904 92 B 140 0.12% 70.06% 61040 763 61040 763 24194 ernal.dd.ServletMappingDescriptor 141 0.12% 70.18% 60400 1510 363360 9084 45338 java.util.Hashtable 142 0.12% 70.30% 59544 827 59544 827 24746 ernal.ServletRuntimeMBeanImpl 143 0.12% 70.42% 59248 1058 484984 8664 33236 oracle.jdbc.ttc7.TTCItem 144 0.12% 70.53% 58152 232 187176 764 748 C 145 0.12% 70.65% 57888 2412 161904 6746 16621 java.lang.String 146 0.11% 70.77% 57400 1435 57400 1435 16855 java.util.HashMap根据以上的结果,在java.hprof.txt中定位到导致分配大内存的操作如下:TRACE 63:java.lang.StringBuffer.expandCapacity(StringBuffer.java:202)java.lang.StringBuffer.append(StringBuffer.java:401)java.util.zip.ZipFile.getEntry(ZipFile.java:148)java.util.jar.JarFile.getEntry(JarFile.java:198)TRACE 92:java.util.zip.InflaterInputStream.(InflaterInputStream.java:71)java.util.zip.ZipFile$1.(ZipFile.java:240)java.util.zip.ZipFile.getInputStream(ZipFile.java:212)java.util.zip.ZipFile.getInputStream(ZipFile.java:183)再进一步分析则需要应用开发人员对应用代码做相应的分析定位。注意:使用HProf非常消耗资源,切记不要在生产系统使用。Java优化编程(第二版) 2.1 垃圾回收堆内存 内存管理的话题在C或C+程序设计中讨论得相对较多,因为在C与C+程序设计中需要开发人员自己申请并管理内存,开发人员可以申请/借用(Apply)系统内存并且负责释放/归还(Release)系统内存,如果“只借不还”就会造成系统内存泄露的问题。在Java程序设计中,这些工作由Java虚拟机(JVM)负责处理。所有内存的申请、分配、释放都由JVM负责完成。因此,开发人员就省去了这部分工作,不过这并不意味着开发人员可以完全依赖于JVM的内存管理功能,如果你这样想并在实际的应用开发中也这样做,你所开发的应用的性能,就有可能不是最优的。这是因为,无论配置多么优良的硬件环境其自身资源都是有限的,由于没有合理、科学的使用内存资源,即使是Java应用也会出现内存枯竭的现象。例如,我们经常会遇到的OutOfMemoryException。再者Java语言(其实不只Java语言)的性能极大程度上依赖于其运行的硬件环境资源,而内存又是硬件环境资源中重要的一部分,因此说,如果开发人员开发的Java应用没能有效、合理地使用系统内存,那么这个应用就不可能具备较高的性能,甚至会导致整个系统在运行一段时间后崩溃。本章将对Java应用开发中与内存管理相关的技术做详细的讲解。2.1 垃圾回收谈到Java内存管理的话题,就必然会提到垃圾回收的概念,垃圾回收的英文名称为Garbage Collection,简称GC,它是Java程序设计中有关内存管理的核心概念,Java虚拟机(JVM)的内存管理机制被称为垃圾回收机制。因此,要想掌握在开发Java应用时怎样才能合理地管理内存,首先应该了解Java虚拟机的内存管理机制垃圾回收机制。否则,在不了解垃圾回收具体实现机制的情况下讨论Java程序设计中的内存管理,优化Java应用性能,就有些纸上谈兵,舍本逐末了。上面我们提到Java程序设计中的内存管理机制是通过垃圾回收来完成的,那么在JVM运行环境中什么样的对象是垃圾呢?下面我们给出了在JVM运行环境中垃圾对象的定义:一个对象创建后被放置在JVM的堆内存(heap)中,当永远不再引用这个对象时,它将被JVM在堆内存(heap)中回收。被创建的对象不能再生,同时也没有办法通过程序语句释放它们。我们也可以这样给JVM中的垃圾对象下定义:当对象在JVM运行空间中无法通过根集合(root set)到达(找到)时,这个对象就被称为垃圾对象。根集合是由类中的静态引用域与本地引用域组成的。JVM中通过根集合索引对象如图2-1所示。图2-1 JVM中通过根集合索引对象&注意 图2-1中打了X标记的对象就是不可到达的对象,这些对象就被JVM视为垃圾对象并被JVM回收。JVM将给这些对象打上相应的标记,然后清扫回收这些对象,并将散碎的内存单元收集整合。这里提到了堆内存的概念,它是JVM管理的一种内存类型,在做Java应用开发时经常会用到由JVM管理的两种类型的内存:堆内存(heap)与栈内存(stack)。有关堆内存的概念,在前面的相关章节中,已经做过相应的介绍。简单地讲,堆内存主要用来存储程序在运行时创建或实例化的对象与变量,例如:我们通过new MyClass()创建的类MyClass的对象。而栈内存(stack)则是用来存储程序代码中声明为静态(static)(或非静态)的方法,JVM、堆内存(heap)与栈内存(stack)三者的关系如图2-2所示。图2-2 JVM、堆内存与栈内存的关系下面通过一个实例来看一下堆内存(heap)与栈内存(stack)中所存储对象的类型有哪些不同。 public class BirdTest static Vector birdList = new Vector(); static void makeBird () Object bird= new Bird (); birdList.addElement(bird); public static void main(String arg) makeBird (); 在上面的代码中声明了一些静态的变量与方法,同时也通过关键字new创建了一些对象实例,下面给出这个简单的类在运行时JVM中堆内存(heap)与栈内存(stack)中所存储的对象情况,如图2-3所示。图2-3 JVM中堆内存与栈内存中所存储的对象情况在图2-3中,可以看到我们在类BirdTest中声明的Vector 类的birdList对象,以及在运行时创建的Bird对象都被放在了堆内存(heap)中,而把两个静态方法main()与makeBird()放在了栈内存(stack)中。这说明birdList对象占用了堆内存,静态方法main()与makeBird()则占用了栈内存。在对Java程序设计中内存管理技术做更为深入的讨论之前,有必要再详细地讲一下堆内存(heap)的相关知识。堆内存堆内存(heap)在JVM启动的时候就被创建,它是JVM中非常关键的一个内存管理区域。堆内存中所存储的对象可以被JVM自动回收,不能通过其他外部手段回收,也就是说开发人员无法通过添加相关代码的手段,回收位于堆内存中的对象。堆内存(heap)通常情况下被分为两个区域:新对象(new object)区域与老对象(old object)区域。这里又引入了两个有关JVM内存管理的新概念:新对象(new object)区域与老对象(old object)区域。下面分别对这两个概念做一下介绍。新对象(new object)区域。又可以细分为三个小区域:伊甸园(Eden)区域、From区域与To区域。伊甸园区域用来保存新创建的对象,它就像一个堆栈,新的对象被创建,就像指向该栈的指针(如果你熟悉C语言,应该非常熟悉指针的概念)在不断增长一样,当伊甸园区域中的对象满了之后,JVM系统将要做可到达性测试,主要任务是检测有哪些对象由根集合出发是不可到达的,这些对象就可以被JVM回收,并且将所有的活动对象从伊甸园区域拷到To区域,此时一些对象将发生状态交换,有的对象就从To区域被转移到From区域,此时From区域就有了对象。上面对象迁移的整个过程,都是由JVM控制完成的。当我们在使用一些Java应用服务器软件时,通过其所提供的内存与性能监控界面,会看到这一过程引起的系统内存的变化。在这个过程执行期间,Java虚拟机的性能是非常低下的,这个过程会严重影响正在运行的应用的性能。老对象(old object)区域。在老对象区域中的对象仍然会有一个较长的生命周期,大多数JVM系统中的垃圾对象,都来源于“短命”对象,经过一段时间后,被转入老对象区域的对象,就变成了垃圾对象。此时,它们都被打上相应的标记,JVM系统将会自动回收这些垃圾对象,建议你不要频繁地强制系统做垃圾回收,这是因为JVM会利用有限的系统资源,优先完成垃圾回收工作,致使应用无法快速地响应来自用户端的请求,这样会影响系统的整体性能,这也正是我们不建议读者自己频繁强制做垃圾回收的原因。为了使读者能够更清楚地了解垃圾回收的过程,根据上面的讲解,给出了JVM做垃圾回收的过程示意图,如图2-4所示。图2-4 JVM做垃圾回收的过程示意通过上面的学习,我们知道垃圾回收与对象的生命周期是紧紧联系在一起的,那么JVM中的对象生命周期是怎样的呢?下面就讲解一下JVM中对象的生命周期的相关知识。2.2 JVM中对象的生命周期在JVM运行空间中,对象的整个生命周期大致可以分为7个阶段:创建阶段(Creation)、应用阶段(Using)、不可视阶段(Invisible)、不可到达阶段(Unreachable)、可收集阶段(Collected)、终结阶段(Finalized)与释放阶段(Free)。上面的这7个阶段,构成了JVM中对象的完整的生命周期。下面分别介绍对象在处于这7个阶段时的不同情形。2.2.1 创建阶段在对象创建阶段,系统要通过下面的步骤,完成对象的创建过程:(1)为对象分配存储空间。(2)开始构造对象。(3)递归调用其超类的构造方法。(4)进行对象实例初始化与变量初始化。(5)执行构造方法体。上面的5个步骤中的第3步就是指递归地调用该类所扩展的所有父类的构造方法,一个Java类(除Object类外)至少有一个父类(Object),这个规则既是强制的,也是隐式的。你可能已经注意到在创建一个Java类的时候,并没有显式地声明扩展(extends)一个Object父类。实际上,在Java程序设计中,任何一个Java类都直接或间接的是Object类的子类。例如下面的代码:public class A 这个声明等同于下面的声明:public class A extends java.lang.Object 上面讲解了对象处于创建阶段时,系统所做的一些处理工作,其中有些过程与应用的性能密切相关,因此在创建对象时,我们应该遵循一些基本的规则,以提高应用的性能。下面是在创建对象时的几个关键应用规则:(1)避免在循环体中创建对象,即使该对象占用内存空间不大。(2)尽量及时使对象符合垃圾回收标准。(3)不要采用过深的继承层次。(4)访问本地变量优于访问类中的变量。关于规则(1)避免在循环体中创建对象,即使该对象占用内存空间不大,需要提示一下,这种情况在我们的实际应用中经常遇到,而且我们很容易犯类似的错误,例如下面的代码: for (int i = 0; i 10000; +i) Object obj = new Object(); System.out.println(obj= + obj); 上面代码的书写方式相信对你来说不会陌生,也许在以前的应用开发中你也这样做过,尤其是在枚举一个Vector对象中的对象元素的操作中经常会这样书写,但这却违反了上述规则(1),因为这样会浪费较大的内存空间,正确的方法如下所示: Object obj = null;for (int i = 0; i 10000; +i) obj = new Object(); System.out.println(obj= + obj); 采用上面的第二种编写方式,仅在内存中保存一份对该对象的引用,而不像上面的第一种编写方式中代码会在内存中产生大量的对象应用,浪费大量的内存空间,而且增大了系统做垃圾回收的负荷。因此在循环体中声明创建对象的编写方式应该尽量避免。另外,不要对一个对象进行多次初始化,这同样会带来较大的内存开销,降低系统性能,如:public class A private Hashtable table = new Hashtable (); public A() / 将Hashtable对象table初始化了两次 table = new Hashtable(); 正确的方式为:public class B private Hashtable table = new Hashtable (); public B() 不要小看这个差别,它却使应用软件的性能相差甚远,如图2-5所示。图2-5 初始化对象多次所带来的性能差别看来在程序设计中也应该遵从“勿以恶小而为之”的古训,否则我们开发出来的应用也是低效的应用,有时应用软件中的一个极小的失误,就会大幅度地降低整个系统的性能。因此,我们在日常的应用开发中,应该认真对待每一行代码,采用最优化的编写方式,不要忽视细节,不要忽视潜在的问题。2.2.2 应用阶段当对象的创建阶段结束之后,该对象通常就会进入对象的应用阶段。这个阶段是对象得以表现自身能力的阶段。也就是说对象的应用阶段是对象整个生命周期中证明自身“存在价值”的时期。在对象的应用阶段,对象具备下列特征: 系统至少维护着对象的一个强引用(Strong Reference); 所有对该对象的引用全部是强引用(除非我们显式地使用了:软引用(Soft Reference)、弱引用(Weak Reference)或虚引用(Phantom Reference)。上面提到了几种不同的引用类型。可能一些读者对这几种引用的概念还不是很清楚,下面分别对之加以介绍。在讲解这几种不同类型的引用之前,我们必须先了解一下Java中对象引用的结构层次。Java对象引用的结构层次示意如图2-6所示。图2-6 对象引用的结构层次示意由图2-6我们不难看出,上面所提到的几种引用的层次关系,其中强引用处于顶端,而虚引用则处于底端。下面分别予以介绍。1强引用强引用(Strong Reference)是指JVM内存管理器从根引用集合(Root Set)出发遍寻堆中所有到达对象的路径。当到达某对象的任意路径都不含有引用对象时,对这个对象的引用就被称为强引用。2软引用软引用(Soft Reference)的主要特点是具有较强的引用功能。只有当内存不够的时候,才回收这类内存,因此在内存足够的时候,它们通常不被回收。另外,这些引用对象还能保证在Java抛出OutOfMemory 异常之前,被设置为null。它可以用于实现一些常用资源的缓存,实现Cache的功能,保证最大限度的使用内存而不引起OutOfMemory。再者,软可到达对象的所有软引用都要保证在虚拟机抛出OutOfMemoryError之前已经被清除。否则,清除软引用的时间或者清除不同对象的一组此类引用的顺序将不受任何约束。然而,虚拟机实现不鼓励清除最近访问或使用过的软引用。下面是软引用的实现代码: import java.lang.ref.SoftReference; A a = new A();/ 使用 a/ 使用完了a,将它设置为soft 引用类型,并且释放强引用;SoftReference sr = new SoftReference(a);a = null;/ 下次使用时if (sr!=null) a = sr.get(); else / GC由于内存资源不足,可能系统已回收了a的软引用, / 因此需要重新装载。 a = new A(); sr=new SoftReference(a); 软引用技术的引进,使Java应用可以更好地管理内存,稳定系统,防止系统内存溢出,避免系统崩溃(crash)。因此在处理一些占用内存较大而且声明周期较长,但使用并不频繁的对象时应尽量应用该技术。正像上面的代码一样,我们可以在对象被回收之后重新创建(这里是指那些没有保留运行过程中状态的对象),提高应用对内存的使用效率,提高系统稳定性。但事物总是带有两面性的,有利亦有弊。在某些时候对软引用的使用会降低应用的运行效率与性能,例如:应用软引用的对象的初始化过程较为耗时,或者对象的状态在程序的运行过程中发生了变化,都会给重新创建对象与初始化对象带来不同程度的麻烦,有些时候我们要权衡利弊择时应用。3弱引用弱引用(Weak Reference)对象与Soft引用对象的最大不同就在于:GC在进行回收时,需要通过算法检查是否回收Soft引用对象,而对于Weak引用对象,GC总是进行回收。因此Weak引用对象会更容易、更快被GC回收。虽然,GC在运行时一定回收Weak引用对象,但是复杂关系的Weak对象群常常需要好几次GC的运行才能完成。Weak引用对象常常用于Map数据结构中,引用占用内存空间较大的对象,一旦该对象的强引用为null时,对这个对象引用就不存在了,GC能够快速地回收该对象空间。与软引用类似我们也可以给出相应的应用代码: import java.lang.ref.WeakReference; A a = new A();/ 使用 a/ 使用完了a,将它设置为weak 引用类型,并且释放强引用;WeakReference wr = new WeakReference (a);a = null;/ 下次使用时if (wr!=null) a = wr.get(); else a = new A(); wr = new WeakReference (a); 弱引用技术主要适用于实现无法防止其键(或值)被回收的规范化映射。另外,弱引用分为“短弱引用(Short Week Reference)”和“长弱引用(Long Week Reference)”,其区别是长弱引用在对象的Finalize方法被GC调用后依然追踪对象。基于安全考虑,不推荐使用长弱引用。因此建议使用下面的方式创建对象的弱引用。 WeakReference wr = new WeakReference(obj);或WeakReference wr = new WeakReference(obj, false); 4虚引用虚引用(Phantom
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 电子支付与网络安全问题:高中信息技术课程教案
- 时装品牌干货知识培训课件
- 自由飞翔的小鸟450字7篇
- 纪检宣传课件
- 山东省济宁市(五四学制)2024-2025学年六年级下学期期末历史试题(含答案)
- 描写春天的中考作文(14篇)
- 品牌宣传与推广执行方案
- 农业种植资源共享和流通合作协议
- 品牌宣传与市场拓展方案
- 2025年嵌入式系统设计师考试嵌入式系统大数据试题
- 《高处作业安全培训》课件
- 垃圾清运合同范本模板建筑
- 合伙开公司必签的五份协议
- Module9 Unit2 Wishing You Happiness Every Day(说课稿)-2023-2024学年外研版(三起)英语六年级下册
- 2024LNG储罐焊缝X射线数字成像检测规范
- DB5117T 22-2020 地理标志产品 米城大米
- 粤沪版物理九年级上册第十二章第1节《认识内能》同步练习
- 采油工培训教学计划
- 设计概论讲课课件(第三版杨晓琪)
- 小学数学分数四则混合运算200题带答案
- 《血管活性药物静脉输注护理》团体标准解读
评论
0/150
提交评论