日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

阿里一面:如何將重復性比較高的 String 類型的地址信息從 20GB 降到幾百兆?

來源: 責編: 時間:2024-04-15 18:17:16 226觀看
導讀這次應該是互聯網及軟件行業的第三次寒潮,大家在寒潮中一定要繼續保持學習,寒潮挺過去以后還是會迎來新的發展機遇。有粉絲去阿里面試,跟碼哥分享了其中一題面試問題「如何將重復性比較高的 String 類型的地址信息從 20G

這次應該是互聯網及軟件行業的第三次寒潮,大家在寒潮中一定要繼續保持學習,寒潮挺過去以后還是會迎來新的發展機遇。
gdQ28資訊網——每日最新資訊28at.com

有粉絲去阿里面試,跟碼哥分享了其中一題面試問題「如何將重復性比較高的 String 類型的地址信息從 20GB 降到幾百兆?」。gdQ28資訊網——每日最新資訊28at.com

今天,碼哥從多個角度帶你完全攻克這個知識點,讓面試官眼前一亮。gdQ28資訊網——每日最新資訊28at.com

切入正文......gdQ28資訊網——每日最新資訊28at.com

莫慌,今天給大家見識一下不一樣的 String,從根上拿捏直達 G 點。gdQ28資訊網——每日最新資訊28at.com

并且碼哥分享一個例子:通過性能調優我們能實現百兆內存輕松存儲幾十 G 數據。gdQ28資訊網——每日最新資訊28at.com

String對象是我們每天都「摸」的對象類型,但是她的性能問題我們卻總是忽略。gdQ28資訊網——每日最新資訊28at.com

愛她,不能只會簡單一起玩耍,要深入了解String 的內心深處,做一個「心有猛虎,細嗅薔薇」的暖男。gdQ28資訊網——每日最新資訊28at.com

通過以下幾點分析,我們一步步揭開她的衣裳,直達內心深處,提升一個 Level,讓 String 直接起飛。gdQ28資訊網——每日最新資訊28at.com

String 身體解密

想要深入了解,就先從基本組成開始……gdQ28資訊網——每日最新資訊28at.com

「String 締造者」對 String 對象做了大量優化來節省內存,從而提升 String 的性能:gdQ28資訊網——每日最新資訊28at.com

圖片圖片gdQ28資訊網——每日最新資訊28at.com

Java 6 及之前

數據存儲在 char[]數組中,String通過 offset 和 count兩個屬性定位 char[] 數據獲取字符串。gdQ28資訊網——每日最新資訊28at.com

這樣可以高效快速的定位并共享數組對象,并且節省內存,但是有可能導致內存泄漏。gdQ28資訊網——每日最新資訊28at.com

共享 char 數組為啥可能會導致內存泄漏呢?gdQ28資訊網——每日最新資訊28at.com

String(int offset, int count, char value[]) {    this.value = value;    this.offset = offset;    this.count = count;}public String substring(int beginIndex, int endIndex) {    //check boundary    return  new String(offset + beginIndex, endIndex - beginIndex, value);}

調用 substring() 的時候雖然創建了新的字符串,但字符串的值 value 仍然指向的是內存中的同一個數組,如下圖所示:gdQ28資訊網——每日最新資訊28at.com

圖片圖片gdQ28資訊網——每日最新資訊28at.com

如果我們僅僅是用 substring 獲取一小段字符,而原始 string字符串非常大的情況下,substring 的對象如果一直被引用。gdQ28資訊網——每日最新資訊28at.com

此時 String 字符串也無法回收,從而導致內存泄露。gdQ28資訊網——每日最新資訊28at.com

如果有大量這種通過 substring 獲取超大字符串中一小段字符串的操作,會因為內存泄露而導致內存溢出。gdQ28資訊網——每日最新資訊28at.com

JDK7、8

去掉了 offset 和 count兩個變量,減少了 String 對象占用的內存。gdQ28資訊網——每日最新資訊28at.com

substring 源碼:gdQ28資訊網——每日最新資訊28at.com

public String(char value[], int offset, int count) {    this.value = Arrays.copyOfRange(value, offset, offset + count);}public String substring(int beginIndex, int endIndex) {    int subLen = endIndex - beginIndex;    return new String(value, beginIndex, subLen);}

substring() 通過 new String() 返回了一個新的字符串對象,在創建新的對象時通過 Arrays.copyOfRange() 深度拷貝了一個新的字符數組。gdQ28資訊網——每日最新資訊28at.com

如下圖所示:gdQ28資訊網——每日最新資訊28at.com

圖片圖片gdQ28資訊網——每日最新資訊28at.com

String.substring 方法不再共享 char[]數組的數據,解決了可能內存泄漏的問題。gdQ28資訊網——每日最新資訊28at.com

Java 9

將 char[]字段改為 byte[],新增 coder屬性。gdQ28資訊網——每日最新資訊28at.com

碼哥,為什么這么改呢?gdQ28資訊網——每日最新資訊28at.com

一個 char 字符占 2 個字節,16 位。存儲單字節編碼內的字符(占一個字節的字符)就顯得非常浪費。gdQ28資訊網——每日最新資訊28at.com

為了節約內存空間,于是使用了 1 個字節占 8 位的 byte 數組來存放字符串。gdQ28資訊網——每日最新資訊28at.com

勤儉節約的女神,誰不愛……gdQ28資訊網——每日最新資訊28at.com

新屬性 coder 的作用是:在計算字符串長度或者使用 indexOf()方法時,我們需要根據編碼類型來計算字符串長度。gdQ28資訊網——每日最新資訊28at.com

coder 的值分別表示不同編碼類型:gdQ28資訊網——每日最新資訊28at.com

  • 0:表示使用 Latin-1 (單字節編碼);
  • 1:使用UTF-16。

String 的不可變性

了解了String 的基本組成之后,發現 String 還有一個比外在更性感的特性,她被 final關鍵字修飾,char 數組也是。gdQ28資訊網——每日最新資訊28at.com

圖片圖片gdQ28資訊網——每日最新資訊28at.com

我們知道類被 final 修飾代表該類不可繼承,而 char[]被 final+private 修飾,代表了 String 對象不可被更改。gdQ28資訊網——每日最新資訊28at.com

String 對象一旦創建成功,就不能再對它進行改變。gdQ28資訊網——每日最新資訊28at.com

Chaya:“String class 對象使用 final 修飾有什么好處?”gdQ28資訊網——每日最新資訊28at.com

安全性gdQ28資訊網——每日最新資訊28at.com

當你在調用其他方法時,比如調用一些系統級操作指令之前,可能會有一系列校驗。gdQ28資訊網——每日最新資訊28at.com

如果是可變類的話,可能在你校驗過后,它的內部的值又被改變了,這樣有可能會引起嚴重的系統崩潰問題。gdQ28資訊網——每日最新資訊28at.com

高性能緩存gdQ28資訊網——每日最新資訊28at.com

String不可變之后就能保證 hash值得唯一性,使得類似 HashMap容器才能實現相應的 key-value 緩存功能。gdQ28資訊網——每日最新資訊28at.com

實現字符串常量池gdQ28資訊網——每日最新資訊28at.com

由于不可變,才得以實現字符串常量池。gdQ28資訊網——每日最新資訊28at.com

字符串常量池指的是在創建字符串的時候,先去「常量池」查找是否創建過該「字符串」;gdQ28資訊網——每日最新資訊28at.com

如果有,則不會開辟新空間創建字符串,而是直接把常量池中該字符串的引用返回給此對象。gdQ28資訊網——每日最新資訊28at.com

創建字符串的兩種方式:gdQ28資訊網——每日最新資訊28at.com

  • String str1 = “碼哥字節”;
  • String str2 = new String(“碼哥字節”);

當代碼中使用第一種方式創建字符串對象時,JVM 首先會檢查該對象是否在字符串常量池中,如果在,就返回該對象引用。gdQ28資訊網——每日最新資訊28at.com

否則新的字符串將在常量池中被創建,并返回該引用。gdQ28資訊網——每日最新資訊28at.com

這樣可以減少同一個值的字符串對象的重復創建,節約內存。gdQ28資訊網——每日最新資訊28at.com

第二種方式創建,在編譯類文件時,"碼哥字節" 字符串將會放入到常量結構中,在類加載時,“碼哥字節" 將會在常量池中創建;gdQ28資訊網——每日最新資訊28at.com

在調用 new 時,JVM 命令將會調用 String 的構造函數,在堆內存中創建一個 String 對象,同時該對象指向「常量池」中的“碼哥字節”字符串,str 指向剛剛在堆上創建的 String 對象;gdQ28資訊網——每日最新資訊28at.com

如下圖:gdQ28資訊網——每日最新資訊28at.com

圖片圖片gdQ28資訊網——每日最新資訊28at.com

什么是對象和對象引用呀?gdQ28資訊網——每日最新資訊28at.com

str 屬于方法棧的字面量,它指向堆中的 String 對象,并不是對象本。gdQ28資訊網——每日最新資訊28at.com

對象在內存中是一塊內存地址,str 則是指向這個內存地址的引用。gdQ28資訊網——每日最新資訊28at.com

也就是說 str 并不是對象,而只是一個對象引用。gdQ28資訊網——每日最新資訊28at.com

碼哥,字符串的不可變到底指的是什么呀?gdQ28資訊網——每日最新資訊28at.com

String str = "Java";str = "Java,yyds"

第一次賦值 「Java」,第二次賦值「Java,yyds」,str 值確實改變了,為什么我還說 String 對象不可變呢?gdQ28資訊網——每日最新資訊28at.com

這是因為 str 只是 String 對象的引用,并不是對象本身。gdQ28資訊網——每日最新資訊28at.com

真正的對象依然還在內存中,沒有被改變。gdQ28資訊網——每日最新資訊28at.com

優化實戰

了解了 String 的對象實現原理和特性,是時候要深入女神內心,結合實際場景,如何更上一層樓優化 String 對象的使用。gdQ28資訊網——每日最新資訊28at.com

大量字符串拼接對象如何優化

既然 String 對象是不可變,所以我們在頻繁拼接字符串的時候是否意味著創建多個對象呢?gdQ28資訊網——每日最新資訊28at.com

String str = "癩蛤蟆撩青蛙" + "長的丑" + "玩的花";

上面你的代碼,你是不是以為先生成「癩蛤蟆撩青蛙」對象,再生成「癩蛤蟆撩青蛙長的丑」對象,最后生成「癩蛤蟆撩青蛙長得丑玩的花」對象。gdQ28資訊網——每日最新資訊28at.com

實際運行中,只有一個對象生成。gdQ28資訊網——每日最新資訊28at.com

Chaya:這是為什么呢?gdQ28資訊網——每日最新資訊28at.com

雖然代碼寫的丑陋,但是編譯器自動優化了代碼。再看下面例子:gdQ28資訊網——每日最新資訊28at.com

String str = "小青蛙";for(int i=0; i<1000; i++) {     str += i;}

上面的代碼編譯后,你可以看到編譯器同樣對這段代碼進行了優化。gdQ28資訊網——每日最新資訊28at.com

Java 在進行字符串的拼接時,JVM 編譯器會把上述代碼優化,偏向使用 StringBuilder,這樣可以提高程序的效率。優化后的代碼如下。gdQ28資訊網——每日最新資訊28at.com

String str = "小青蛙";for(int i=0; i<1000; i++) {            str = (new StringBuilder(String.valueOf(str))).append(i).toString();}

即使如此,還是循環內重復創建 StringBuilder對象。gdQ28資訊網——每日最新資訊28at.com

敲黑板gdQ28資訊網——每日最新資訊28at.com

所以做字符串拼接的時候,我建議你還是要顯示地使用 String Builder 來提升系統性能。gdQ28資訊網——每日最新資訊28at.com

如果在多線程編程中,String 對象的拼接涉及到線程安全,你可以使用 StringBuffer。gdQ28資訊網——每日最新資訊28at.com

重復性高的 String 信息優化

重點在于使用運用 intern 節省內存。直接看intern() 方法的定義與源碼:gdQ28資訊網——每日最新資訊28at.com

圖片圖片gdQ28資訊網——每日最新資訊28at.com

intern() 是一個本地方法,它的定義中說的是,當調用 intern 方法時,如果字符串常量池中已經包含此字符串,則直接返回此字符串的引用。gdQ28資訊網——每日最新資訊28at.com

否則將此字符串添加到常量池中,并返回字符串的引用。gdQ28資訊網——每日最新資訊28at.com

如果不包含此字符串,先將字符串添加到常量池中,再返回此對象的引用。gdQ28資訊網——每日最新資訊28at.com

Chaya:什么情況下適合使用 intern() 方法?gdQ28資訊網——每日最新資訊28at.com

Twitter 工程師曾分享過一個 String.intern() 的使用示例,Twitter 每次發布消息狀態的時候,都會產生一個地址信息,以當時 Twitter 用戶的規模預估,服務器需要 20G 的內存來存儲地址信息。gdQ28資訊網——每日最新資訊28at.com

public class Location {    private String city;    private String region;    private String countryCode;    private double longitude;    private double latitude;}

考慮到其中有很多用戶在地址信息上是有重合的,比如,國家、省份、城市等,這時就可以將這部分信息單獨列出一個類,以減少重復,代碼如下:gdQ28資訊網——每日最新資訊28at.com

public class SharedLocation {  private String city;  private String region;  private String countryCode;}public class Location {  private SharedLocation sharedLocation;  double longitude;  double latitude;}

通過優化,數據存儲大小減到了 20G 左右。gdQ28資訊網——每日最新資訊28at.com

但對于內存存儲這個數據來說,依然很大,怎么辦呢?gdQ28資訊網——每日最新資訊28at.com

Twitter 工程師使用 String.intern() 使重復性非常高的地址信息存儲大小從 20G 降到幾百兆,從而優化了 String 對象的存儲。gdQ28資訊網——每日最新資訊28at.com

核心代碼如下:gdQ28資訊網——每日最新資訊28at.com

SharedLocation sharedLocation = new SharedLocation();sharedLocation.setCity(messageInfo.getCity().intern());sharedLocation.setCountryCode(messageInfo.getRegion().intern());sharedLocation.setRegion(messageInfo.getCountryCode().intern());

弄個簡單例子方便理解:gdQ28資訊網——每日最新資訊28at.com

String a =new String("abc").intern();String b = new String("abc").intern();System.out.print(a==b);

輸出結果:true。gdQ28資訊網——每日最新資訊28at.com

在加載類的時候會在常量池中創建一個字符串對象,內容是「abc」。gdQ28資訊網——每日最新資訊28at.com

創建局部 a 變量時,調用 new Sting() 會在堆內存中創建一個 String 對象,String 對象中的 char 數組將會引用常量池中字符串。gdQ28資訊網——每日最新資訊28at.com

在調用 intern 方法之后,會去常量池中查找是否有等于該字符串對象的引用,有就返回引用。gdQ28資訊網——每日最新資訊28at.com

創建 b 變量時,調用 new Sting() 會在堆內存中創建一個 String 對象,String 對象中的 char 數組將會引用常量池中字符串。gdQ28資訊網——每日最新資訊28at.com

在調用 intern 方法之后,會去常量池中查找是否有等于該字符串對象的引用,有就返回引用給局部變量。gdQ28資訊網——每日最新資訊28at.com

而剛在堆內存中的兩個對象,由于沒有引用指向它,將會被垃圾回收。gdQ28資訊網——每日最新資訊28at.com

所以 a 和 b 引用的是同一個對象。gdQ28資訊網——每日最新資訊28at.com

字符串分割優化

split() 方法使用了正則表達式實現了其強大的分割功能,而正則表達式的性能是非常不穩定的。gdQ28資訊網——每日最新資訊28at.com

使用不恰當會引起回溯問題,很可能導致 CPU 居高不下。gdQ28資訊網——每日最新資訊28at.com

Java 正則表達式使用的引擎實現是 NFA(Non deterministic Finite Automaton,確定型有窮自動機)自動機,這種正則表達式引擎在進行字符匹配時會發生回溯(backtracking),而一旦發生回溯,那其消耗的時間就會變得很長,有可能是幾分鐘,也有可能是幾個小時,時間長短取決于回溯的次數和復雜度。gdQ28資訊網——每日最新資訊28at.com

所以我們應該慎重使用 split() 方法,我們可以用String.indexOf()方法代替 split() 方法完成字符串的分割。gdQ28資訊網——每日最新資訊28at.com

最后,出一個問題給大家,歡迎在評論區留言。gdQ28資訊網——每日最新資訊28at.com

通過三種不同的方式創建了三個對象,再依次兩兩匹配,每組被匹配的兩個對象是否相等?代碼如下:gdQ28資訊網——每日最新資訊28at.com

String str1 = "abc";String str2 = new String("abc");String str3 = str2.intern();assertSame(str1 == str2);assertSame(str2 == str3);assertSame(str1 == str3)

博主簡介

碼哥,9 年互聯網公司后端工作經驗,InfoQ 簽約作者、51CTO Top 紅人,阿里云開發者社區專家博主,目前擔任后端架構師主責,擅長 Redis、Spring、Kafka、MySQL技術和云原生微服務。gdQ28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-83641-0.html阿里一面:如何將重復性比較高的 String 類型的地址信息從 20GB 降到幾百兆?

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 首屏時間,你說你優化了,那你倒是計算出來給我看啊!

下一篇: 深入理解Transformer技術原理

標簽:
  • 熱門焦點
  • 6月安卓手機好評榜:魅族20 Pro蟬聯冠軍

    性能榜和性價比榜之后,我們來看最后的安卓手機好評榜,數據來源安兔兔評測,收集時間2023年6月1日至6月30日,僅限國內市場。第一名:魅族20 Pro好評率:95%5月份的時候魅族20 Pro就是
  • 帥氣純真少年!日本最帥初中生選美冠軍出爐

    日本第一帥哥初一生選美大賽冠軍現已正式出爐,冠軍是來自千葉縣的宗田悠良。日本一直熱衷于各種選美大賽,從&ldquo;最美JK&rdquo;起到&ldquo;最美女星&r
  • 摸魚心法第一章——和配置文件說拜拜

    為了能摸魚我們團隊做了容器化,但是帶來的問題是服務配置文件很麻煩,然后大家在群里進行了“親切友好”的溝通圖片圖片圖片圖片對比就對比,簡單對比下獨立配置中心和k8s作為配
  • 企業采用CRM系統的11個好處

    客戶關系管理(CRM)軟件可以為企業提供很多的好處,從客戶保留到提高生產力。  CRM軟件用于企業收集客戶互動,以改善客戶體驗和滿意度?! RM軟件市場規模如今超過580
  • 破圈是B站頭上的緊箍咒

    來源 | 光子星球撰文 | 吳坤諺編輯 | 吳先之每年的暑期檔都少不了瞄準追劇女孩們的古偶劇集,2021年有優酷的《山河令》,2022年有愛奇藝的《蒼蘭訣》,今年卻輪到小破站抓住了追
  • 10天營收超1億美元,《星鐵》比《原神》差在哪?

    來源:伯虎財經作者:陳平安即便你沒玩過《原神》,你一定聽說過的它的大名。恨它的人把《原神》開服那天稱作是中國游戲史上最黑暗的一天,有粉絲因為索尼在PS平臺上線《原神》,怒而
  • 網紅炒股不為了賺錢,那就是耍流氓!

    來源:首席商業評論6月26日高調宣布入市,網絡名嘴大v胡錫進居然進軍了股市。在一次財經媒體峰會上,幾個財經圈媒體大佬就&ldquo;胡錫進炒股是否知道認真報道&rdquo;展開討論。有
  • 微博大門常打開,迎接海外畫師漂洋東渡

    作者:互聯網那些事&ldquo;起猛了,我能看得懂日語了&rdquo;。&ldquo;為什么日本人說話我能聽懂?&rdquo;&ldquo;中文不像中文,日語不像日語,但是我竟然看懂了&rdquo;&hellip;&hell
  • 中關村論壇11月25日開幕,15位諾獎級大咖將發表演講

    11月18日,記者從2022中關村論壇新聞發布會上獲悉,中關村論壇將于11月25至30日在京舉行。本屆中關村論壇由科學技術部、國家發展改革委、工業和信息化部、國務
Top 主站蜘蛛池模板: 京山县| 微山县| 贡山| 元朗区| 麻栗坡县| 鄂温| 蓬溪县| 师宗县| 广汉市| 新宁县| 靖远县| 东莞市| 五峰| 区。| 兖州市| 澄城县| 图们市| 尖扎县| 嘉峪关市| 科技| 墨江| 古田县| 宁南县| 石台县| 阿拉善左旗| 通渭县| 崇义县| 扶绥县| 合肥市| 鄂尔多斯市| 霍城县| 克山县| 玉田县| 肃北| 疏勒县| 汉阴县| 贵德县| 格尔木市| 新泰市| 宜州市| 滦平县|