1)jvm垃圾回收機制
這個問題真的是老生常談了踏施;我是這么去聊的;java主要分為堆棧是晨,開發(fā)人員一般會關注堆區(qū)域躺坟,那么堆區(qū)域分為3個大區(qū)域;新生代熊痴,老年代他爸,永久代。而新生代再分為Eden,S0,S1區(qū)果善,老年代old诊笤,永久代p區(qū)域。
新生代為什么這么去設計主要是最大努力避免對象進入老年代巾陕,而導致老年代對象滿造成Full gc,而引發(fā)top the world的問題讨跟。
一個對象剛開始進入的是新生代eden,s0區(qū)域,當對象滿的時候出發(fā)ygc這個ygc速度很快鄙煤,基本沒有什么影響晾匠,然后把ygc存活的對象復制到s1區(qū)域,然后會把Eden,s0區(qū)域全部清理梯刚。這里采用復制清理算法凉馆,復制清理主要是解決垃圾碎片問題。
對象放入S1區(qū)域有兩種情況:
1)每次清理的時候亡资,都會把存活的對象做一個計數(shù)器澜共,當對象超過15次的時候,會把對象放到老年代(old)锥腻,
2)如果這個對象很大嗦董;大到S1區(qū)域存放不了,也會把對象直接放到(Old)區(qū)域瘦黑。
當老年代滿了的情況下就會觸發(fā)Full gc ,在cms垃圾回收器的情況展懈;觸發(fā)full gc會導致所有工作線程全部暫停销睁,等full gc完成 其他的線程才會繼續(xù)使用。
所以我們在設置垃圾回收內(nèi)存的時候存崖;是需要根據(jù)實際業(yè)務場景來考慮冻记,畢竟Old內(nèi)存設置太大,頻率會降低来惧,但是每次gc時間也會很長冗栗,如果設置太小供搀;雖然gc耗時縮短了隅居,但是頻率也會增加。
cms垃圾回收器主要問題在于葛虐,每次的回收時間不可控胎源,也就是我們沒辦法知道每次回收的耗時需要多久。
所以在jdk8以后增加了g1的垃圾回收器屿脐,這個垃圾回收器每次觸發(fā)full gc的耗時是可控的涕蚤,我們只要通過參數(shù)設置即可。
那它是怎么實現(xiàn)的呢的诵?這里引入了元空間的一個概念万栅。 g1的邏輯分代區(qū)域都沒有變;主要是老年代發(fā)生變化西疤。它把內(nèi)存分為多個region塊烦粒,大概是[1m~32m]之間,這么設計主要就是能夠通過代赁,用戶設置的full gc耗時來控制回收多少個內(nèi)存塊扰她。這樣設計控制粒度更精細了。 比如1毫秒芭碍,那么就大概可以預估我用回收多少個region塊了徒役。
這么設計就解決了cms觸發(fā)耗時不可控的問題!
2)在實際工作中有沒有解決gc的問題豁跑。
業(yè)務背景是這樣,某銀行系統(tǒng)每天都會假死一次泻云,影響千萬用戶轉賬艇拍。從物理內(nèi)存,jvm內(nèi)存宠纯,cpu,日志層面排查都很正常卸夕,加機器,重啟系統(tǒng)只能緩解問題婆瓜。
測試環(huán)境通過壓測快集;通過jstat 查看內(nèi)存情況贡羔,發(fā)現(xiàn)oid出現(xiàn)大量的full gc情況。 再通過jstack 排查堆棧日志个初,定位到full gc的日志塊乖寒,再通過日志塊定位到代碼行數(shù),發(fā)現(xiàn)代碼層面上院溺;在用戶登陸退出的時候調用system.gc來清理內(nèi)存楣嘁。 由于大量的gc調用堆積導致系統(tǒng)假死。
cms在調用gc的時候珍逸,實際上只是用很少的線程去回收逐虚,工作線程都會處于等待。所以在排查其他的指標上看不出異樣谆膳。
2)hashmap 和Hashtable的區(qū)別
這道題也是老生常談的叭爱,我并沒有去很細的去了解,就結合我自己經(jīng)驗去講了漱病。
我是這么來回答:
A) hashmap首先是我們常用的數(shù)據(jù)結構买雾,但是它也有很多缺點。
1)首先它是線程不安全的缨称,所以我們在并發(fā)的場景有update并不推線使用它凝果。我們可以通過ConcurrentHashMap保證線程安全。
2)還有對于大量數(shù)據(jù)存儲使用的時候也不推薦睦尽,因為它的底層是用鏈表來實現(xiàn)隨著它的量越大它的遍歷性能會越來越低器净。復雜度是屬于O(n)級別。
3)如果需要本地緩存也不推薦使用当凡,因為它沒有很好的內(nèi)存管理山害,如果我們需要一條數(shù)據(jù)在本地緩存使用1天清理一遍,這個就控制不了沿量。這種場景可以通過Guava Cache封裝的map實現(xiàn)更合適浪慌。
B) 而對于Hashtable這個是線程安全的,性能不沒有hashmap好朴则;工作中不常用沒有做太多了解权纤。
3)redis常用數(shù)據(jù)結構
string/list/hash/set/zset
4)redis為什么性能會比較高
redis為什么性能會高,首先它是個可以作為緩存數(shù)據(jù)庫乌妒,數(shù)據(jù)可不用落磁盤汹想。
當然它的底層處理鏈接的技術也有很大關系,主要通過reactor模式處理鏈接數(shù)撤蚊,而reactor模式底層使用的是io多路復用技術古掏,這個技術是操作系統(tǒng)級別的,目前操作系統(tǒng)io多路復用技術侦啸;主要還是使用epoll來實現(xiàn)槽唾,epoll能夠同時處理多少連接數(shù)丧枪,受限系統(tǒng)的句柄數(shù),而系統(tǒng)句柄數(shù)受限于物理內(nèi)存庞萍。
也就是說如果我們內(nèi)存夠用拧烦,系統(tǒng)句柄數(shù)就可以設置非常大,而redis通過io多路復用技術就能夠把處理并發(fā)連接數(shù)給提上來挂绰。這也就是官方redis說單機能實現(xiàn)10qps的勇氣屎篱。
目前io多路復用很多技術產(chǎn)品都有在使用,比如nginx ,netty,tomcat,kafka等葵蒂。
沒有展開來講select,poll,epoll 以及相關數(shù)據(jù)結構交播。
5)redis緩存穿透,緩存雪崩問題践付,如何優(yōu)化
對于redis緩存穿透秦士;我們是通過空key來解決,也就是說當一個緩存過期的時候永高,我們避免它直接去查詢mysql隧土,而是設置一個永久 null key返回,這樣保證了mysql數(shù)據(jù)庫的穩(wěn)定性提供有損服務命爬。
也可以通過分布式鎖來優(yōu)化曹傀,比如查詢redis沒有的情況下,再去獲鎖饲宛;獲得鎖的線程去查數(shù)據(jù)庫皆愉,查到數(shù)據(jù)再更新到redis,其他沒有獲得鎖的直接返回一個打底信息艇抠。
雪崩的的問題幕庐;就是大量數(shù)據(jù)同時過期,我們是可以通過把key耗時設置不同時段來避免家淤,也可以通過鎖异剥,null key來緩解。
當然我們對redis key的設計要嚴格管理絮重。經(jīng)常去做cr冤寿,很多團隊對redis key并沒有做太多的管理,而更容易引發(fā)問題青伤。
6)你有沒有使用過redis鎖
redis 分布式鎖有很多種方案督怜;常見的主要是通過nx px ,或者Redisson來實現(xiàn)潮模。
nx px主要的問題是設置鎖的時間是固定的亮蛔,如果任務沒有處理完痴施,但是鎖過期了沒辦法做時間續(xù)租擎厢,而Redisson解決了續(xù)租的這個問題究流,但是redis是屬于異步復制的架構,ap模型动遭;就是在極端的情況下可能會數(shù)據(jù)丟失芬探,比如redis集群;我們創(chuàng)建一個鎖在a節(jié)點厘惦,如果我們用的是rdb同步偷仿,設置5s進行rdb同步,在5s的間隔期間宵蕉,a節(jié)點掛了酝静,那么可能導致其他的用戶去獲得鎖。電商的情況下可能導致超賣的情況 羡玛。
所以如果使用分布式鎖别智,我們也要結合場景去選型,如果在并發(fā)并不高的情況稼稿;對一致性要求比較高薄榛,我們可以使用zookeeper來實現(xiàn)分布式鎖,zk是屬于ap模型让歼,對一致性要求非常高敞恋,在節(jié)點掛的情況下對外是不可用的,當對外可用的情況下一定是數(shù)據(jù)已經(jīng)同步好的谋右。
當然還有其他的分布式鎖框架硬猫,比如etcd等。
7)kafka為什么性能比較高
kafka的話我可以從4個點來講倚评,通過集群提高它的高可用浦徊。當然我們開發(fā)人員主要還是比較關注topic,那么topic下有個分區(qū)的概念天梧,這個分區(qū)特點就是提高性能盔性,可擴展性等。我們可以結合業(yè)務去合理的提高它的分區(qū)呢岗,當然消費者數(shù)量也要跟的上冕香,最好是1:1 。
至于怎么配置分區(qū)后豫,消費者需要結合實際場景去考慮悉尾,分區(qū)數(shù)太大,會占用很多內(nèi)存挫酿,而且分區(qū)的數(shù)據(jù)同步也是個問題构眯。
分區(qū)下面有個副本的概念,這個主要提供容災能力早龟,一個分區(qū)會有多個副本惫霸;每個副本數(shù)據(jù)都是一樣的猫缭,存儲在不同的節(jié)點中,如果某個分區(qū)掛了的情況下壹店,重新選舉最大能力可以保證數(shù)據(jù)不丟失猜丹,當然數(shù)據(jù)丟失的粒度在于我們是追求性能,還是追求數(shù)據(jù)可靠性硅卢,可以結合acks來調整平衡射窒。
8)kafka 零拷貝技術
講這個我們首先要了解兩個狀態(tài),為了考慮安全問題将塑,操作系統(tǒng)會分為兩個狀態(tài)就是內(nèi)核態(tài)脉顿,用戶態(tài)。所有用戶的操作都是在【用戶態(tài)】所有系統(tǒng)級別的調用都在【內(nèi)核態(tài)】点寥。
比如我們要去讀磁盤數(shù)據(jù)弊予;傳統(tǒng)的數(shù)據(jù)傳遞一般先是通過【內(nèi)核態(tài)】去從磁盤去讀數(shù)據(jù),切到【用戶態(tài)】去傳遞到用戶進程开财,用戶態(tài)可能會做一些處理然后再通過內(nèi)核態(tài)傳遞到系統(tǒng)socket再發(fā)送到網(wǎng)卡等汉柒。
【內(nèi)核拷貝到用戶態(tài)】
【用戶態(tài)拷貝到內(nèi)核態(tài)】
這里就是兩次拷貝了。那么在kafka中呢责鳍,它都是一些消息數(shù)據(jù)期間并不需要對消息做一些處理碾褂,所以它在內(nèi)核態(tài)的情況下讀取磁盤數(shù)據(jù)后然后直接發(fā)送到網(wǎng)卡,省去了兩次拷貝历葛。
9)mysql 樂觀鎖 悲觀鎖 正塌,mysql事物級別
這里我結合cap理論來講,性能恤溶,和數(shù)據(jù)一致性一定是互斥的乓诽。就看我們怎么從業(yè)務層面怎么去追求平衡。
mysql它提供了4種事物隔離級別咒程,不同級別有不同的方案鸠天,讀未提交,讀以提交帐姻,可重復讀稠集,串行化
如果我們需要數(shù)據(jù)完全可靠的話就用【串行化】級別就好了。 如果我們追求絕對性能的話我們可以用【讀未提交】饥瓷,當然這也完全是裸奔剥纷。mysql默認的隔離級別是【可重復讀】。