当前位置: 首页 > 科技观察

打造高性能Java应用需掌握的5大知识

时间:2023-03-18 16:37:21 科技观察

构建高性能Java应用程序需要掌握的5个知识有时候我们很少关心它,但是在编写代码的过程中,时不时的对程序性能的影响是不可避免的,小到我们使用位操作来实现算术运算,大到我们JAVA代码的整体架构设计,性能其实和我们很接近。这篇文章主要讲了几点,主要是我们在性能领域比较关心的一些问题,有借鉴意义。如果学生对表演更感兴趣,那么我们可以一起深入研究每个点。对于性能调优,通常分为三个步骤:1.性能监控;2、性能分析;3.性能调优我们对操作系统性能的关注主要有以下几点:CPU利用率、CPU调度执行队列、内存利用率、网络I/O、磁盘I/O。1.CPU利用率对于一个应用程序来说,为了让应用程序达到最好的性能和扩展性,我们不仅要充分利用CPU周期中可用的部分,还要让这部分CPU的使用更有价值.而不是浪费。能够充分利用CPU周期对于在多处理器和多核系统上运行的多线程应用程序来说是非常具有挑战性的。另外,当CPU达到饱和状态时,并不意味着CPU的性能和扩展性已经达到最佳状态。为了区分应用程序如何使用CPU资源,我们必须在操作系统级别进行检测。在许多操作系统上,CPU利用率统计报告通常包括操作系统的用户和系统或内核使用情况。用户对CPU的使用率是指应用程序执行应用程序代码所需的时间。相反,内核和系统CPU使用率是指应用程序执行锁定在操作系统内核中的代码所花费的时间。高内核或系统CPU使用率可能表示共享资源紧张,或大量I/O设备交互。理想情况下,为了提高应用程序的性能和可扩展性,让内核或系统CPU时间为0%,因为执行内核或系统代码所花费的时间可以用来执行应用程序代码。因此,CPU使用率优化的正确方向是尽量减少CPU执行内核代码或系统代码的时间。对于计算密集型应用程序,性能监控比监控用户CPU使用率和内核或系统CPU使用率更深入。在计算密集型应用中,我们需要监控CPU时钟周期内的执行次数(Instructionsperclock;IPC)或者是每次CPU执行所使用的CPU周期(cyclesperinstruction;CPI)。对于计算密集型应用,我们从这两个维度来监控CPU是一个不错的选择,因为现代操作系统封装的CPU性能报告工具通常只打印CPU的使用率,而不会打印CPU在CPU周期中的使用情况。执行命令的时间。这意味着当CPU在内存中等待数据时,操作系统的CPU性能报告工具也会认为CPU在使用中。我们称这种场景为“Stall”,“Stall”场景经常发生,比如当CPU每当指令正在执行时,只要指令需要的数据还没有准备好,也就是不在register或CPUcache,就会出现“Stall”的情况。当“停顿”场景发生时,CPU会浪费时钟周期,因为CPU必须等待指令所需的数据到达寄存器或缓冲区。而在这种场景下,数百个CPU时钟周期被浪费是很正常的,所以在计算密集型应用中,提升性能的策略是减少“Stall”场景的发生或者提高CPU缓存的使用率,让更多的CPU周期被浪费在等待数据上。这种性能监控知识超出了本书的内容,需要性能专家的帮助。但是,性能分析工具OracleSolarisStudioPerformanceAnalyzer(稍后介绍)将包含此类数据。2.CPU调度队列除了监控CPU使用率,我们还可以通过监控CPU执行队列来判断系统是否满载。执行队列用于存储轻量级进程。这些进程通常准备好执行但正在等待CPU调度并在调度队列中等待。当当前处理器能够处理的轻量级进程数量较多时,就会创建调度队列。深度CPU调度队列表明系统已满载。系统的执行队列深度等于虚拟处理器无法执行的等待数,虚拟处理器数等于系统中的硬件线程数。我们可以使用javaapi获取虚拟处理器的数量,Runtime.avaliableProcessors()。当执行队列深度是虚拟处理器数量的四倍或更多时,操作系统会出现无响应。检测CPU调度队列的一般准则是,当我们发现队列深度高于虚拟进程数的两倍时就应该注意,但不需要立即采取行动。当大于三倍或四倍或更高时,就需要引起重视,急需解决。通常有两种可选的方式来观察队列的深度。第一种是增加CPU来分担负载或减少现有CPU的负载。这种方法实质上减少了每个执行单元的加载线程数,从而减少了执行队列深度。另一种方法是通过分析系统上运行的应用程序来增加CPU使用率。换句话说,找到一种方法来减少花在垃圾收集上的CPU周期,或者找到更好的算法来使用更少的CPU周期。执行CPU指令。性能专家通常关注后一种方法:减少代码执行路径长度和更好的CPU指令选择。Java程序员可以通过更好的执行算法和数据结构来提高代码执行效率。3.内存利用率除了CPU占用率外,还需要监控系统的内存属性。这些属性包括,例如:多线程引起的分页、交换、锁定和上下文切换。交换通常发生在应用程序所需的内存大于实际物理内存时。为了应对这种情况,操作系统通常会配置一个相应的区域,称为交换区。交换区通常位于物理磁盘上。当物理内存中的应用程序耗尽时,操作系统会暂时将部分内存数据交换到磁盘空间。这部分内存区域通常是访问频率最高的区域,不会影响相对“繁忙”的内存区域;当应用程序访问交换到磁盘区域的内存时,此时需要将磁盘交换区域的页面读入内存,而交换会影响应用程序的性能。虚拟机的垃圾收集器在交换时表现很差,因为垃圾收集器访问的大部分区域是不可达的,即垃圾收集器会导致交换活动发生。场面很戏剧化。如果垃圾回收的堆区已经交换到磁盘空间,此时会以页为单位进行交换,以便垃圾收集器扫描,交换过程中会大量产生垃圾。采集器采集时间延长。这时候,如果垃圾收集器是“StopTheWorld”(让应用响应停止),那么这个时间就会延长。4、网络I/O分布式JAVA应用的性能和可扩展性会受到网络带宽和网络性能的限制。例如,如果我们向网络接口发送的数据包超过了它的处理能力,这些数据包就会堆积在操作系统的缓冲区中,从而导致应用程序延迟,其他情况也会导致网络应用程序延迟。.用于区分和监控的工具通常很难在操作系统的打包工具中找到。虽然linux提供了netstat命令,但是linux和solaris都提供了网络使用情况的实现,都提供了包括每秒发送包、接收包、错误包、冲突等信息的统计信息。在以太网中,少量的数据包冲突是很正常的。如果错误包很多,可能是网卡有问题。同时,netstat虽然可以统计网络接口发送和接收的数据,但很难判断网卡是否被充分利用。比如netstat-i显示当前网卡每秒发送2500个数据包,但我们仍然无法判断当前网络利用率是100%还是1%,只能知道有流量。这只是在不知道网络数据包大小的情况下可以得出的结论。简单的说,我们无法通过linux和solaris提供的netstat来判断当前网络是否影响性能。在我们的JAVA应用程序运行时,我们需要一些其他工具来监控网络。5.磁盘I/O如果应用程序对磁盘进行操作,我们需要对磁盘进行监控,以监控可能出现的磁盘性能问题。一些应用程序是I/O密集型的,例如数据库。磁盘使用情况通常也存在于应用日志系统中,日志通常用于记录系统运行过程中的重要信息。