概述
随着Java的广泛应用,越来越多的关键企业系统也使用Java构建。作为Java核心运行环境的Java虚拟机JVM被广泛地部署在各种系统平台上。对Java应用的性能优化也越来越受到关注;谈到Java应用的性能问题就不得不涉及到两个方面:一是Java应用的构造是否是最优化的;二是对JVM的微调。本文将从一般意义上对Java性能的优化做一些总结。
Java性能优化的策略
一谈到性能优化,往往会被认为是应用开发和部署过程中或之后的事情,其实不然。如果想要构建一个最优化的系统,我们必须从该系统的需求分析和业务模型设计之初就要考虑到性能的最优化问题;当然对于一个已经构造好的系统来讲,我们能做的只是在不改变系统代码的前提下,尽量地在该系统的部署方案和运行环境上下功夫。由此,我们得出一个结论就是:所谓最优化是一个相对的概念,一个系统是否是最优化的,必须基于某个大前提来进行评判。因此,在进行优化分析之前一定要把握好前提条件是什么。
如上图所示,可以看出,对系统性能提高贡献最大、最明显的是从业务层面和架构层面所作的分析和优化;最不明显的是对系统平台和硬件层面以及网络层面的优化。因此在着手对目标系统进行优化分析之前,我们一定要从优化最明显、贡献最大的方面着手。这样有助于我们在最大程度上去提高系统性能。
以下我们将针对Java系统的性能优化,从代码编写和JVM两个角度着手,总结一下常见的方法和思路。
编写性能高效的Java代码
根据GC的工作原理,我们可以通过一些技巧和方式,让GC运行更加有效率,更加符合应用程序的要求。以下就是一些程序设计的几点建议:
1)避免对象创建和GC
只要有可能,应该避免创建对象,防止调用构造函数带来的相关性能成本,以及在对象结束其生命周期时进行垃圾收集所带来的成本。考虑以下这些准则:
- 只要有可能,就使用基本变量类型,而不使用对象类型。例如,使用 int,而不使用 Integer;
- 缓存那些频繁使用的寿命短的对象,避免一遍又一遍地重复创建相同的对象,并因此加重垃圾收集的负担;
- 在处理字符串时,使用 StringBuffer 而不使用字符串String进行连接操作,因为字符串对象具有不可变的特性,并且需要创建额外的字符串对象以完成相应的操作,而这些对象最终必须经历 GC;
- 避免过度地进行 Java 控制台的写操作,降低字符串对象处理、文本格式化和输出带来的成本;
- 实现数据库连接池,重用连接对象,而不是重复地打开和关闭连接;
- 使用线程池(thread pooling),避免不停地创建和删除线程对象,特别是在大量使用线程的时候;
- 避免在代码中调用GC。GC是一个“停止所有处理(stop the world)”的事件,它意味着除了 GC 线程自身外,其他所有执行线程都将处于挂起状态。如果必须调用 GC,那么可以在非紧急阶段或空闲阶段实现它;
- 避免在循环内分配对象。
- 尽早释放无用对象的引用。大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域(scope)后,自动设置为null。我们在使用这种方式时候,必须特别注意一些复杂的对象,例如数组,队列,树,图等,这些对象之间的相互引用关系较为复杂。对于这类对象,GC回收它们一般效率较低。如果程序允许,尽早将不再使用的引用对象赋为null。这样可以加速GC的工作。
- 如果有经常使用的图片,可以使用soft引用类型。它可以尽可能将图片保存在内存中,供程序调用,而不引起Out Of Memory。
- 注意一些全局的变量,以及一些静态变量。这些变量往往容易引起悬挂对象(dangling reference),造成内存浪费。
2)Java Native Interface(JNI)
使用本机代码编写应用程序的一部分,特别是频繁使用的部分,并将之与 Java 链接,这样做通常是为了提高性能。不过,JVM 与本机代码之间的通信通常很慢,因此,太多的 JNI 调用可能会降低性能。只要有可能就应该将本机操作集合在一起,以减少 JNI 调用的数量。使用 JNI 代码本地处理异常,尽管有时不可避免,但会导致性能下降。在这种情况下,应该使用 ExceptionCheck() 函数,因为与 ExceptionOccurred() 相比较,它带来的计算开销更少一些。后者必须创建一个将引用的对象,以及一个本地引用。
3)同步
为了减少 JVM 和操作系统中的争用,应该只在可行的情况下才使用同步方法。不要将同步方法放到循环结构中。
4)数据结构
作为一条通用规则,在更简单的数据结构能满足需要的地方,应该避免使用更复杂的数据结构。例如,在可以使用数组的地方不要使用向量。使用最有效的方法搜索元素,并将元素插入数据结构中,比如说,在向量的结尾处添加和删除元素,以便获得更好的性能。
5)尽可能使用堆栈变量
如果您频繁存取变量,就需要考虑从何处存取这些变量。变量是static变量,还是堆栈变量,或者是类的实例变量? 变量的存储位置对存取它的代码的性能有明显的影响。JVM 是一种基于堆栈的虚拟机,因此优化了对堆栈数据的存取和处理。所有局部变量都存储在一个局部变量表中,在 Java 操作数堆栈中进行处理,并可被高效地存取。存取 static 变量和实例变量成本更高,因为 JVM 必须使用代价更高的操作码,并从常数存储池中存取它们。(常数存储池保存一个类型所使用的所有类型、字段和方法的符号引用。)通常,在第一次从常数存储池中访问 static 变量或实例变量以后,JVM 将动态更改字节码以使用效率更高的操作码。尽管有这种优化,堆栈变量的存取仍然更快。
考虑到这些事实,在构建代码时就可以考虑通过存取堆栈变量而不是实例变量或 static 变量,使操作更高效。如果必须使用,可以考虑将实例变量或 static 变量复制到局部堆栈变量中。当变量的处理完成以后,其值又被复制回实例变量或 static 变量中。这并不表示您应该避免使用 static 变量或实例变量。您应该使用对您的设计有意义的存储机制。例如,如果您在一个循环中存取 static 变量或实例变量,则您可以临时将它们存储在一个局部堆栈变量中,这样就可以明显地提高代码的性能。这将提供最高效的字节码指令序列供 JVM 执行。
考虑到这些事实,在构建代码时就可以考虑通过存取堆栈变量而不是实例变量或 static 变量,使操作更高效。如果必须使用,可以考虑将实例变量或 static 变量复制到局部堆栈变量中。当变量的处理完成以后,其值又被复制回实例变量或 static 变量中。这并不表示您应该避免使用 static 变量或实例变量。您应该使用对您的设计有意义的存储机制。例如,如果您在一个循环中存取 static 变量或实例变量,则您可以临时将它们存储在一个局部堆栈变量中,这样就可以明显地提高代码的性能。这将提供最高效的字节码指令序列供 JVM 执行。
6)finalize函数
finalize是位于Object类的一个方法,该方法的访问修饰符为protected,由于所有类为Object的子类,因此用户类很容易访问到这个方法。由于,finalize函数没有自动实现链式调用,我们必须手动的实现,因此finalize函数的最后一个语句通常是super.finalize()。通过这种方式,我们可以从下到上实现finalize的调用,即先释放自己的资源,然后再释放父类的资源。
根据Java语言规范,JVM保证调用finalize函数之前,这个对象是不可达的,但是JVM不保证这个函数一定会被调用。另外,规范还保证finalize函数最多运行一次。
很多Java初学者会认为这个方法类似于C++中的析构函数,将很多对象、资源的释放都放在这一函数里面。其实,这不是一种很好的方式。原因有三,其一,GC为了能够支持finalize函数,要对覆盖这个函数的对象作很多附加的工作。其二,在finalize运行完成之后,该对象可能变成可达的,GC还要再检查一次该对象是否是可达的。因此,使用finalize会降低GC的运行性能。其三,由于GC调用finalize的时间是不确定的,因此通过这种方式释放资源也是不确定的。
通常,finalize用于一些不容易控制、并且非常重要的资源的释放,例如一些I/O的操作,数据的连接。这些资源的释放对整个应用程序是非常关键的。在这种情况下,程序员应该以通过程序本身管理(包括释放)这些资源为主,以finalize函数释放资源方式为辅,形成一种双保险的管理机制,而不应该仅仅依靠finalize来释放资源。
7)异常的开销很大
是的,异常开销很大。那么,这是不是就意味着您不该使用异常?当然不是。但是,何时应该使用异常,何时又不应该使用异常呢?不幸的是,答案不是一下子就说得清的。我们要说的是,您不必放弃已经学到的好的 try-catch 编程习惯,但是使用异常时可能会遇到麻烦,创建异常就是一个例子。当创建一个异常时,需要收集一个栈跟踪(stack track),这个栈跟踪用于描述异常是在何处创建的。构建这些栈跟踪时需要为运行时栈做一份快照,正是这一部分开销很大。运行时栈不是为有效的异常创建而设计的,而是设计用来让运行时尽可能快且没有任何不必要的延迟。但是,当需要创建一个 Exception 时,JVM 不得不说:“先别动,我想就您现在的样子存一份快照,所以暂时停止入栈和出栈操作。”栈跟踪不只包含运行时栈中的一两个元素,而是包含这个栈中的每一个元素,从栈顶到栈底,还有行号和一切应有的东西。如果在一个深度为20的栈中创建了异常,那么就别指望只记录顶部的几个栈元素了——得完完整整地记录下所有20个元素。从 main 或 Thread.run (在栈底)到栈顶,记录整个栈。
因此,创建异常这一部分开销很大。从技术上讲,栈跟踪快照是在本地方法 Throwable.fillInStackTrace() 中发生的,这个方法又是从 Throwable constructor 那里调用的。但是这并没有什么影响——如果您创建一个 Exception ,就得付出代价。好在捕获异常开销不大,因此可以使用 try-catch 将核心内容包起来。从技术上讲,您甚至可以随意地抛出异常,而不用花费很大的代价。招致性能损失的并不是 throw 操作——尽管在没有预先创建异常的情况下就抛出异常是有点不寻常。真正要花代价的是创建异常。幸运的是,好的编程习惯已教会我们,不应该不管三七二十一就抛出异常。异常是为异常的情况而设计的,使用时也应该牢记这一原则。
8)避免非常大的分配
有时候问题不是由当时的堆状态造成的,而是因为分配失败造成的。分配的内存块都必须是连续的,而随着堆越来越满,找到较大的连续块越来越困难。这不仅仅是 Java 的问题,使用 C 中的 malloc 也会遇到这个问题。JVM 在压缩阶段通过重新分配引用来减少碎片,但其代价是要冻结应用程序较长的时间。
分享到:
相关推荐
但根据标题《大话JAVA性能优化》和描述“虽然有些地方可能过时,但是还是可以一读”以及标签“java 优化”,可以推断出书籍内容可能围绕Java编程语言的性能优化相关知识。基于这些信息,我们可以构建关于Java性能...
Java性能优化是软件开发中的一个关键领域,尤其是在大型企业级应用和高并发系统中。《Java性能优化》一书深入探讨了如何通过各种技术提升Java应用程序的效率和响应速度。以下是一些基于书籍源码和相关文件名的关键...
《大话java性能优化》是周明耀先生的一本深入探讨Java性能调优的专业书籍,其主要内容涵盖了Java程序设计中的各种性能优化策略和技术。这本书旨在帮助开发者理解和掌握如何提升Java应用的运行效率,减少资源消耗,...
### Java性能优化实战知识点概述 #### 一、理论分析篇 **1.1 性能优化的衡量指标及注意事项** - **衡量指标**: 包括响应时间、吞吐量、资源利用率等。 - **注意事项**: 在进行性能优化时,需确保优化方案不会引入...
《Java程序性能优化》这本书主要探讨了如何通过各种技术和策略来提高Java应用程序的性能。 ### 性能瓶颈分析 - **CPU使用率高**:程序执行时,某些部分可能过度消耗CPU资源,导致性能下降。 - **内存泄漏**:对象...
总的来说,Java性能优化涉及多个层面,需要综合考虑程序设计、环境配置、代码实现和硬件资源等因素,通过精细化管理和技术手段,实现性能的提升。对于开发人员来说,理解和掌握这些优化技巧是提升Java应用效率的关键...
在Java开发过程中,性能优化是不可或缺...总结起来,Java性能优化是一个综合性的过程,涉及到代码编写、系统设计、资源管理等多个层面。通过对这些知识点的学习和实践,开发者能够构建出更加高效、稳定的Java应用程序。
总结,Java企业应用的性能优化是一项涉及广泛的技术任务,包括理解基本原理、选择合适的优化策略、使用有效的工具进行分析,并在整个软件栈中进行调优。通过综合运用这些方法,可以有效地提高Java应用程序的性能,...
《Java性能优化权威指南》是一本深度探讨Java应用程序性能提升的专业书籍。这本书旨在帮助开发者、系统管理员以及架构师掌握在实际开发过程中优化Java程序的关键技术和方法。书中涵盖了从基础概念到高级策略的全面...
【阿里JAVA性能优化实战】是面向Java开发人员和架构设计人员的专业课程,旨在提供系统优化的知识和技巧。本文将深入探讨Java性能优化的关键点,并基于给出的部分内容进行详细阐述。 性能调优对于任何互联网公司都是...
Java性能优化解析主要关注如何提升Java应用程序的效率...综上所述,Java性能优化是一个综合性的任务,涉及到程序设计、环境配置、代码实现等多个环节,需要系统性和针对性地进行改进,以实现更高效、更稳定的程序运行。
【Java 性能优化概述】 Java 作为一种跨平台的编程语言,自1990年代中期推出以来,凭借其“一次编写,到处运行”的特性获得了广泛赞誉,但也因其相对于C等语言的性能和运行效率问题受到诟病。尤其是在服务器端应用...
本文将深入探讨Java性能优化的方法和技术,帮助开发者通过减少时间和空间消耗来提升Java程序的整体性能。 #### 二、时间消耗的优化 ##### 2.1 标准代码优化 在编写Java程序时,开发者不应期望编译器(如`javac`或...
Java性能优化是一个复杂而深入的主题,它涉及到多个层面,包括操作系统、Java应用程序、垃圾收集机制、数据库以及缓存等。本文将围绕这些方面展开,详细阐述优化策略和实践方法。 首先,性能诊断是优化的第一步。在...
### Java性能优化关键知识点 #### 一、合理运用单例模式 **单例模式**是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点。在Java性能优化中,合理使用单例模式可以带来以下优势: 1. **...
JVM是Java性能优化的重要环节。理解JVM的工作原理,如垃圾收集机制、类加载机制,以及如何调整JVM参数,都是优化的关键。例如,设置合适的堆大小、新生代与老年代比例、GC策略等,可以显著改善应用性能。在容器化...
随着Java技术的不断发展,新的调优方法和工具将不断涌现,帮助开发者更有效地优化JVM性能。 JVM性能调优是一个多方面的任务,涉及JVM配置、垃圾回收策略、内存管理、线程使用等多个方面。通过采用合适的调优技巧和...
Java高性能系统的设计与优化是构建高效、稳定且可扩展的应用程序的关键环节。在这个主题中,我们将深入探讨几个关键的知识点,包括高性能系统的一般架构、优化原则以及JVM(Java虚拟机)的优化。 首先,让我们关注...
在这个领域,有多个层面和技术需要考虑,包括基础原理、方法论、具体的调优技术以及优化策略。以下是对这些内容的详细阐述: 1. 性能基础与方法论 性能优化首先要理解Little's定律,这是排队论中的一个基本概念。...