小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

一文了解 Java 應(yīng)用程序性能優(yōu)化指南

 Daigle 2018-01-13

關(guān)鍵時(shí)刻,第一時(shí)間送達(dá)!

在《2018 最具就業(yè)前景的 7 大編程語言》一文中,通過分析了來自 Indeed 的 25 門編程語言、棧和框架的數(shù)據(jù),我們盤點(diǎn)了18年最具就業(yè)前景的七大編程語言,其中,Java毫無懸念拔得頭籌。

那么對(duì)于開發(fā)者來說,如何讓 Java 應(yīng)用程序達(dá)到性能最佳?本文將一步步教你將 Java 應(yīng)用程序性能優(yōu)化到一流。

本文我們將探討一系列方法,用來提升 Java 應(yīng)用程序的性能。首先定義出可度量的性能指標(biāo),然后通過不同的工具來衡量和監(jiān)控應(yīng)用程序性能,并找到影響性能的瓶頸所在。

此外,我們還將展示一些常用的 Java 代碼級(jí)別優(yōu)化方法以及最佳的編碼實(shí)踐。最后,我們將深入 JVM 特定的調(diào)優(yōu)技巧和架構(gòu)改進(jìn)方法,以提升 Java 應(yīng)用程序的性能。

性能指標(biāo)

在開始動(dòng)手改進(jìn)應(yīng)用程序的性能之前,我們需要定義和理解非功能性需求的一些關(guān)鍵領(lǐng)域,比如可擴(kuò)展性、性能、可用性,等等。

以下是一些用來衡量 Web 應(yīng)用程序性能的常用指標(biāo): 

  • 平均應(yīng)用響應(yīng)時(shí)間;

  • 系統(tǒng)支持的平均并發(fā)用戶數(shù);

  • 在最大負(fù)載期間每秒支持的請(qǐng)求數(shù)。

借助不同的負(fù)載測(cè)試手段以及應(yīng)用程序監(jiān)測(cè)工具對(duì)這些指標(biāo)進(jìn)行量化,有助于找出性能瓶頸的關(guān)鍵點(diǎn)并對(duì)其進(jìn)行相應(yīng)的優(yōu)化,從而提升 Java 程序性能。

示例程序

首先創(chuàng)建一個(gè)示例程序,下文將基于該示例程序探討性能優(yōu)化方法。我使用一個(gè)簡(jiǎn)單的 Spring Boot Web 應(yīng)用程序用作本文示例程序(可參考https:///spring-boot-level-up/)。該程序負(fù)責(zé)管理員工列表,通過暴露出 REST API 用來進(jìn)行員工新增和檢索。

在下文中我們將把它作為負(fù)載測(cè)試的參考,來監(jiān)控多種性能指標(biāo)。

找到性能瓶頸

負(fù)載測(cè)試工具和應(yīng)用程序性能管理(APM)解決方案通常用于跟蹤和優(yōu)化 Java 應(yīng)用程序的性能。圍繞不同的應(yīng)用場(chǎng)景運(yùn)行負(fù)載測(cè)試,同時(shí)使用 APM 工具監(jiān)控 CPU、IO、內(nèi)存占用等情況是識(shí)別瓶頸的關(guān)鍵。

Gatling 是負(fù)載測(cè)試的最佳工具之一,它提供了對(duì) HTTP 協(xié)議的極佳支持,這使得它成為對(duì)任何 HTTP 服務(wù)器進(jìn)行負(fù)載測(cè)試的絕佳選擇。

Stackify 的 Retrace 是一個(gè)非常成熟的 APM 解決方案,它具有非常豐富的功能,可以幫助你確定應(yīng)用程序的基準(zhǔn)。Retrace 的關(guān)鍵組件之一是其代碼性能分析模塊,它能夠在不減緩應(yīng)用程序的情況下收集運(yùn)行時(shí)信息。

Retrace 還提供了其他組件,用于監(jiān)控基于 JVM 運(yùn)行的應(yīng)用程序的內(nèi)存、線程和類。除了應(yīng)用程序指標(biāo)之外,它還能夠監(jiān)控托管應(yīng)用程序的服務(wù)器的 CPU 和 IO 使用情況。

因此,像 Retrace 這樣全面的監(jiān)控工具將解鎖應(yīng)用程序性能優(yōu)化的第一部分。第二部分則需要對(duì)應(yīng)用程序在真實(shí)世界的使用情況和負(fù)載進(jìn)行重現(xiàn)來優(yōu)化。

想要重現(xiàn)并不容易,并且了解應(yīng)用程序的當(dāng)前性能配置文件也非常重要。接下來我們將重點(diǎn)關(guān)注這個(gè)問題。

Gatling 負(fù)載測(cè)試

Gatling 模擬腳本采用 Scala 編寫,它附帶了一個(gè)功能強(qiáng)大的 GUI,提供場(chǎng)景記錄功能。GUI 自動(dòng)創(chuàng)建 Scala 腳本呈現(xiàn)模擬測(cè)試結(jié)果。模擬測(cè)試完成之后,Gatling 能夠生成有用的、即時(shí)分析的 HTML 報(bào)告。

定義一個(gè)場(chǎng)景

在啟動(dòng)記錄器之前,我們需要定義一個(gè)場(chǎng)景,用于呈現(xiàn)用戶在瀏覽 Web 應(yīng)用程序時(shí)發(fā)生的情況。

在我們的例子中,測(cè)試方法是:“模擬200個(gè)用戶,每個(gè)用戶發(fā)起10000個(gè)請(qǐng)求。”

配置記錄器

根據(jù) Gatling 的第一步,使用以下代碼創(chuàng)建一個(gè)名為 EmployeeSimulation 的 scala 新文件: 

class EmployeeSimulation extends Simulation {
   val scn = scenario('FetchEmployees').repeat(10000) {
       exec(
         http('GetEmployees-API')
           .get('http://localhost:8080/employees')
           .check(status.is(200))
       )
   }
   setUp(scn.users(200).ramp(100))
}

運(yùn)行負(fù)載測(cè)試

執(zhí)行以下命令啟動(dòng)負(fù)載測(cè)試:

$GATLING_HOME/bin/gatling.sh -s basic.EmployeeSimulation

對(duì)應(yīng)用程序的 API 進(jìn)行負(fù)載測(cè)試有助于發(fā)現(xiàn)微小且隱蔽的 bug,例如數(shù)據(jù)庫連接耗盡,請(qǐng)求在高負(fù)載期間超時(shí),由于內(nèi)存泄漏而導(dǎo)致的超高堆棧占用,等等。

監(jiān)控應(yīng)用程序

想要使用 Retrace 進(jìn)行 Java 應(yīng)用程序的開發(fā),首先需要在 Stackify 上注冊(cè)一個(gè)免費(fèi)的試用版。

接下來,我們需要將我們的 Spring Boot 應(yīng)用程序配置為 Linux 服務(wù)。我們還需要在托管應(yīng)用程序的服務(wù)器上安裝 Retrace 代理。

一旦啟動(dòng)了 Retrace 代理與要監(jiān)控的 Java 應(yīng)用程序,我們即可在 Retrace 儀表板上點(diǎn)擊 AddApp 鏈接。之后 Retrace 將開始監(jiān)控我們的應(yīng)用程序。

尋找堆中最慢的部分

Retrace 自動(dòng)監(jiān)測(cè)我們的應(yīng)用程序,并跟蹤多種常見框架和依賴項(xiàng)的使用情況,包括SQL、MongoDB、Redis、Elasticsearch 等。如果應(yīng)用程序包含下列性能問題,Retrace 能夠幫助我們快速找到原因: 

  • 是否有某條 SQL 語句減緩了程序速度? 

  • Redis 是否突然變慢? 

  • 是否有某個(gè) HTTP Web 服務(wù)變緩或者 down 掉?

例如,下圖展示了在給定時(shí)間內(nèi)系統(tǒng)中運(yùn)行最緩慢的部件。 

代碼級(jí)別優(yōu)化

負(fù)載測(cè)試和應(yīng)用程序監(jiān)控對(duì)于確定應(yīng)用程序中的一些關(guān)鍵瓶頸非常有用。但同時(shí),我們需要遵循良好的編碼實(shí)踐,盡量避免性能問題在開始應(yīng)用程序監(jiān)控之前出現(xiàn)。

使用 StringBuilder 進(jìn)行字符串連接

字符串連接是編程中非常普遍的操作,同時(shí)也是低效率的操作。簡(jiǎn)而言之,使用 = 來追加字符串的問題在于,每次操作都會(huì)分配新的字符串。

以一個(gè)簡(jiǎn)化的典型循環(huán)為例,我們分別用原始的字符串連接和 builder 方式來實(shí)現(xiàn)。

public String stringAppendLoop() {
   String s = '';
   for (int i = 0; i < 10000; i ) {
       if (s.length() > 0)
           s = ', ';
       s = 'bar';
   }
   return s;
}
public String stringAppendBuilderLoop() {
   StringBuilder sb = new StringBuilder();
   for (int i = 0; i < 10000; i ) {
       if (sb.length() > 0)
           sb.append(', ');
       sb.append('bar');
   }
   return sb.toString();
}

在上面的代碼中使用 StringBuilder 效率更高,尤其是對(duì)于頻繁進(jìn)行字符串操作的程序來說效率更加顯著。

需要說明的是,當(dāng)前版本的 JVM 自動(dòng)對(duì)字符串操作執(zhí)行了編譯和運(yùn)行時(shí)優(yōu)化。

避免遞歸

在 Java 應(yīng)用程序中,由于遞歸導(dǎo)致 StackOverFlowError 錯(cuò)誤是很常見的。

如果我們無法避免使用遞歸邏輯,那么盡量使用尾遞歸。

來看一個(gè)采用頭遞歸的例子:

public int factorial(int n) {
   if (n == 0) {
       return 1;
   } else {
       return n * factorial(n - 1);
   }
}

將其改為尾遞歸:

private int factorial(int n, int accum) {
   if (n == 0) {
       return accum;
   } else {
       return factorial(n - 1, accum * n);
   }
}
public int factorial(int n) {
   return factorial(n, 1);
}

其他一些 JVM 語言(如 Scala)已經(jīng)具有編譯器級(jí)別的支持來優(yōu)化尾遞歸代碼,并且正在設(shè)法將這種優(yōu)化類型引入到 Java 中。

謹(jǐn)慎使用正則表達(dá)式

正則表達(dá)式在很多應(yīng)用場(chǎng)景下確實(shí)作用明顯,但是它們往往需要非常高的性能成本。了解各種使用正則表達(dá)式的 JDK 字符串方法(如 String.replaceAll()或String.split())尤為重要。

如果你必須在計(jì)算密集型的代碼段中使用正則表達(dá)式,那么盡量使用 *Pattern *緩存模式,避免重復(fù)編譯:

static final Pattern HEAVY_REGEX = Pattern.compile('(((X)*Y)*Z)*');

對(duì)于操作字符串來說,使用像 Apache Commons Lang 這樣的流行庫也是一個(gè)很好的選擇。

避免創(chuàng)建和銷毀太多的線程

創(chuàng)建和處理線程是影響 JVM 性能的常見因素,因?yàn)榫€程對(duì)象的創(chuàng)建和銷毀開銷昂貴。

如果你的應(yīng)用程序需要使用大量的線程,那么線程池的使用將意義非凡,它可以讓這些昂貴的對(duì)象得到重復(fù)利用。

為此,Java 的 ExecutorService 提供了高級(jí) API 用來定義線程池并與之交互。

Java 7 中的 Fork/Join 框架也值得一提,它提供了一些工具來利用處理器多核優(yōu)勢(shì)從而加速并行處理。為了提供有效的并行執(zhí)行,框架使用了名為 ForkJoinPool 的線程池來管理工作線程。

JVM 調(diào)優(yōu)

優(yōu)化堆大小

為生產(chǎn)系統(tǒng)指定合適的 JVM 堆大小并非易事。首先需要回答下列問題來預(yù)測(cè)內(nèi)存需求:

  1. 我們計(jì)劃將多少個(gè)不同的應(yīng)用程序部署到單個(gè) JVM 進(jìn)程中?有多少個(gè) EAR、WAR、jar 這樣的文件? 

  2. 運(yùn)行時(shí)可能會(huì)加載多少個(gè) Java 類?是否包括第三方的 API?

  3. 估算內(nèi)存中緩存所需的空間,例如,由我們的應(yīng)用程序(和第三方 API)加載的內(nèi)部緩存數(shù)據(jù)結(jié)構(gòu)、來自數(shù)據(jù)庫的緩存數(shù)據(jù)以及從文件中讀取的數(shù)據(jù),等等。

  4. 估算應(yīng)用程序?qū)?chuàng)建的線程數(shù)量。

在缺乏真實(shí)測(cè)試的情況下,這些數(shù)字很難估算。

獲得關(guān)于應(yīng)用程序需求最可靠的方法是對(duì)應(yīng)用程序進(jìn)行負(fù)載測(cè)試,并在運(yùn)行時(shí)跟蹤性能指標(biāo)。我們之前討論的基于 Gatling 的測(cè)試就是一個(gè)很好的方法。

選擇合適的垃圾收集器

對(duì)于大多數(shù)面向客戶端的 Java 應(yīng)用程序來說,Stop-the-world 垃圾收集器影響了程序的響應(yīng)能力和整體性能。

但是,新一代的垃圾收集器大多已經(jīng)解決了這個(gè)問題,并且通過適當(dāng)?shù)膬?yōu)化和調(diào)整,收集周期得到了弱化。但是想要做到這樣,你需要深入了解整個(gè) JVM 的垃圾收集機(jī)制以及應(yīng)用程序本身。

像分析器、heap dumps 以及 GC 日志記錄這樣的工具用處很大。同樣,它們都需要在真實(shí)的負(fù)載模式下才能派上用場(chǎng),正如前文討論的 Gatling 性能測(cè)試那樣。

有關(guān)不同垃圾收集器的更多信息,請(qǐng)參閱https:///what-is-java-garbage-collection/。

JDBC 性能

關(guān)系數(shù)據(jù)庫是影響 Java 應(yīng)用程序性能的另一個(gè)常見因素。為了獲得更快的請(qǐng)求響應(yīng)速度,我們必須關(guān)注應(yīng)用程序的每一層,并考慮代碼如何與底層 SQL DB 進(jìn)行交互。

連接池

眾所周知,數(shù)據(jù)庫連接代價(jià)是昂貴的。連接池是優(yōu)化該問題的重要機(jī)制。

強(qiáng)烈推薦 HikariCP JDBC ,它是一個(gè)輕量級(jí)(大約130Kb)且速度非??斓?JDBC 連接池框架。

JDBC 批量處理

在數(shù)據(jù)持久化過程中盡可能地批量操作。JDBC 批處理允許我們?cè)趩蝹€(gè)數(shù)據(jù)庫交互中發(fā)送多個(gè) SQL 語句。

批處理使得驅(qū)動(dòng)和數(shù)據(jù)庫本身的性能都得到提升。PreparedStatement 是批處理的絕佳選擇,一些數(shù)據(jù)庫系統(tǒng)(例如 Oracle)僅支持預(yù)處理語句的批處理。

Hibernate 則更加靈活,允許我們切換到單一配置的批處理。

語句緩存

語句緩存是另一種能夠提高持久層性能的方法 - 一種你可以輕松利用但鮮為人知的性能優(yōu)化手段。

基于 JDBC 驅(qū)動(dòng)程序,你可以在客戶端(Driver)或數(shù)據(jù)庫端(語法樹甚至執(zhí)行計(jì)劃)上緩存 PreparedStatement。

縱向擴(kuò)展與橫向擴(kuò)展

數(shù)據(jù)庫復(fù)制和分片是提高吞吐量的重要手段,我們應(yīng)該充分利用這些久經(jīng)沙場(chǎng)的體系結(jié)構(gòu)來擴(kuò)展企業(yè)應(yīng)用程序的持久層。

架構(gòu)改進(jìn)

高速緩存

如今內(nèi)存價(jià)格不再昂貴而且會(huì)變得越來越低,但是從磁盤或者網(wǎng)絡(luò)檢索數(shù)據(jù)的代價(jià)依然很高。顯然,緩存是我們?cè)谔嵘绦蛐阅軙r(shí)不容忽視的環(huán)節(jié)。

當(dāng)然,將獨(dú)立緩存系統(tǒng)引入到應(yīng)用程序的拓?fù)浣Y(jié)構(gòu)中會(huì)增加架構(gòu)的復(fù)雜性,因此想要利用緩存,最直接的方式是充分利用已經(jīng)使用的庫和框架中的現(xiàn)有緩存功能。

例如,大多數(shù)持久性框架都有很好的緩存支持。Spring MVC 等 Web 框架還可以利用Spring 中內(nèi)置的緩存支持以及基于 ETags 的強(qiáng)大的 HTTP 級(jí)緩存。

簡(jiǎn)單使用了緩存之后,便能頻繁訪問應(yīng)用程序。如果想要更進(jìn)一步,那么諸如 Redis、Ehcache 或 Memcache 這樣的獨(dú)立緩存服務(wù)器是很好的選擇,它們能夠減少數(shù)據(jù)庫負(fù)載并提升應(yīng)用程序性能。

橫向擴(kuò)展

無論我們?yōu)槌绦蚨哑隽硕嗌儆布傆心硞€(gè)時(shí)刻會(huì)顯得依然不夠。雖然橫向擴(kuò)展天生存在局限性,但是當(dāng)系統(tǒng)遇到問題時(shí),橫向擴(kuò)展依然是支撐更多負(fù)載的唯一途徑。

橫向擴(kuò)展實(shí)施起來實(shí)屬不易,但它是在系統(tǒng)遭遇某些瓶頸時(shí)的唯一解決方法。

而且,大多數(shù)的現(xiàn)代框架和庫都支持橫向擴(kuò)展。Spring 生態(tài)系統(tǒng)有項(xiàng)目組專門用于解決該領(lǐng)域的應(yīng)用程序體系結(jié)構(gòu)問題,其他大多數(shù)項(xiàng)目都有類似的支持。

最后,除了純粹的 Java 性能之外,在集群的幫助下進(jìn)行擴(kuò)展的另外一個(gè)好處是,添加新節(jié)點(diǎn)還會(huì)導(dǎo)致冗余和更好的處理故障的技術(shù),從而提高整個(gè)系統(tǒng)的可用性。

總結(jié)

我們探討了許多不同的方法來提高 Java 應(yīng)用程序的性能。我們首先介紹了負(fù)載測(cè)試、基于 APM 工具的應(yīng)用程序和服務(wù)器監(jiān)控,隨后介紹了編寫高性能 Java 代碼的一些最佳實(shí)踐。

最后,我們研究了 JVM 特定的調(diào)優(yōu)技巧、數(shù)據(jù)庫優(yōu)化和架構(gòu)改進(jìn)方案,以擴(kuò)展我們的應(yīng)用程序。

原文:How to Improve the Performance of a Java Application

鏈接:https:///articles/how-to-improve-the-performance-of-a-java-applicati

作者:Eugen Paraschiv  

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多