阿里java高级工程师面试100题(建议收藏)
1,java堆,分新生代老年代,新生代有Eden,from surviver,to surviver三個(gè)空間,堆被所有線程共。eden內(nèi)存不足時(shí),發(fā)生一次minor GC,會(huì)把from survivor和eden的對(duì)象復(fù)制到to survivor,這次的to?survivor就變成了下次的from survivor,經(jīng)過(guò)多次minor GC,默認(rèn)15次,達(dá)到次數(shù)的對(duì)象會(huì)從survivor進(jìn)行老年代。1次new如果新生代裝不下,則直接進(jìn)入老年代。
2,HashMap和HashTable是使用數(shù)組+鏈表結(jié)構(gòu)實(shí)現(xiàn),根據(jù)Hash和table長(zhǎng)度計(jì)算數(shù)組的下標(biāo)index做操作,hashMap默認(rèn)數(shù)組長(zhǎng)度為16,hashMap對(duì)null值的key都放在table[0]的位置,table[index]形成1個(gè)鏈表,當(dāng)然在新版jdk中鏈表節(jié)點(diǎn)數(shù)>8會(huì)變成紅黑樹(shù)結(jié)構(gòu)。hashMap達(dá)到最大數(shù)量會(huì)擴(kuò)容,擴(kuò)容table長(zhǎng)度變?yōu)?倍,每個(gè)元素(table中)但重新計(jì)算index放到新的table中。
3,堆的年輕代和老年代。
堆的年輕代大則老年代小,GC少,但是每次時(shí)間會(huì)比較長(zhǎng)。年輕代小則老年代大,會(huì)縮短每次GC的時(shí)間,但是次數(shù)頻繁。可以讓老年代盡量緩存常用對(duì)象,JVM默認(rèn)年輕代和老年代的大小比例為1:2,。觀察峰值老年代內(nèi)存,不影響full GC,加大老年代可調(diào)1:1,但是要給老年代預(yù)留三分之一的空間。減少使用全局變量和大對(duì)象 ,調(diào)整新生代,老年代到最合適。
4,字節(jié)流不會(huì)用到內(nèi)存緩沖區(qū),文件本身直接操作。字符流操作使用內(nèi)存緩存區(qū),用緩存存操作文件。字符流在輸出前將所有內(nèi)容暫時(shí)保存到內(nèi)存中,即緩存區(qū)暫時(shí)存儲(chǔ),如果想不關(guān)閉也將字符流輸出則可以使用flush方法強(qiáng)制刷出。字節(jié)字符轉(zhuǎn)化可能存在系統(tǒng)編碼lang,要制定編碼。getbyte字節(jié)流使用更加廣泛。
5,中文占用2個(gè)字節(jié),read()函數(shù)讀1個(gè)字節(jié)把A會(huì)讀入的原因。ASCII碼是8位,A在ASCII碼中有對(duì)應(yīng)碼,A只要8位就能表示,但是unicode是支持ASCII碼的,在unicode中表示A是使用低8位的ASCII碼,補(bǔ)上高8位的0,read()1分字節(jié)就已經(jīng)讀入A的ASCII碼,打印時(shí)會(huì)給其高8位補(bǔ)上0,所以顯示正常A。
6,喚醒一個(gè)阻塞的線程
如因?yàn)镾leep,wait,join等阻塞,可以使用interrupted exception異常喚醒。
7,內(nèi)存溢出可能原因和解決。
原因可能是A,數(shù)據(jù)加載過(guò)多,如1次從數(shù)據(jù)庫(kù)中取出過(guò)多數(shù)據(jù) ? B,集合類中有對(duì)對(duì)象的引用,用完后沒(méi)有清空或者集合對(duì)象未置空導(dǎo)致引用存在等,是的JVM無(wú)法回收 ?C,死循環(huán),過(guò)多重復(fù)對(duì)象 D,第三方軟件的bug ? ? ? E,啟動(dòng)參數(shù)內(nèi)存值設(shè)定的過(guò)小。
例如方法:修改JVM啟動(dòng)參數(shù),加內(nèi)存(-Xms,-Xmx);錯(cuò)誤日志,是否還有其他錯(cuò)誤;代碼走查
8,redis使用單線程模型,數(shù)據(jù)順序提交,redis支持主從模式,mencache只支持一致性hash做分布式;redis支持?jǐn)?shù)據(jù)落地,rdb定時(shí)快照和aof實(shí)時(shí)記錄操作命令的日志備份,memcache不支持;redis數(shù)據(jù)類型豐富,有string,hash,set,list, sort set,而memcache只支持簡(jiǎn)單數(shù)據(jù)類型;memcache使用cas樂(lè)觀鎖做一致性。
jedis操作Hash:hmset, hmget, hdel, hkeys
jedis操作List: lpush,lrange按照范圍取出,rpush, del, sort等keyjedis操作Set:sadd,srem移除noname,smembers, sismember, scard等。
使用場(chǎng)景例如
Hash:存儲(chǔ)讀取更新用戶多個(gè)屬性
List:微博TimeLine,消息列表
Set:共同好友,二度好友,用唯一性可以統(tǒng)計(jì)網(wǎng)站所有獨(dú)立IP,好友推薦根據(jù)tag求交集,大于threshold就可以推薦。
sortset:set增加1個(gè)權(quán)重score參數(shù)
其他場(chǎng)景:A訂閱發(fā)布系統(tǒng),redis對(duì)某個(gè)key消息發(fā)布及訂閱,當(dāng)1個(gè)key消息發(fā)布后,所有訂閱它的客戶端都會(huì)收到相應(yīng)消息,例如實(shí)時(shí)消息系統(tǒng),即時(shí)聊天,群聊等。
事務(wù)-常用EX,EC提交執(zhí)行的命令,在server不出問(wèn)題,可以保證一連串的命令是順序執(zhí)行額;提供1個(gè)watch功能,對(duì)1個(gè)key作watch,然后再執(zhí)行transation。
?
?
9,Class.forName()將類加載到JVM,還會(huì)對(duì)類解釋,執(zhí)行static塊,而ClassLoader也加載到JVM,但是不會(huì)執(zhí)行static塊,并且只有調(diào)用了new Instance方法才會(huì)調(diào)用構(gòu)造函數(shù)。
10,java反射機(jī)制。
可以在運(yùn)行時(shí)判斷一個(gè)對(duì)象所屬的類,構(gòu)造一個(gè)類的對(duì)象,判斷類具有的成員變量和方法,調(diào)用1個(gè)對(duì)象的方法。4個(gè)關(guān)鍵的類:Class,Constructor,Field,Method。 ? ?getConstructor獲得構(gòu)造函數(shù)/getDeclardConstructor; getField/getFields/getDeclardFields獲得類所生命的所有字段;getMethod/getMethods/getDeclardMethod獲得類聲明的所有方法,正常方法是一個(gè)類創(chuàng)建對(duì)象,而反射是1個(gè)對(duì)象找到1個(gè)類。
11,Object類中的方法:clone(),但是使用該方法必須實(shí)現(xiàn)Java.lang.Cloneable接口,equal()方法判斷引用是否一致,指向同一對(duì)象,即相等于==,只有覆寫(xiě)了equals()方法之后,才可以說(shuō)不同。hashcode(),對(duì)象的地址, toString(), finalize()。
12,序列化和反序列化
序列化和反序列化即對(duì)象和字節(jié)序列間的轉(zhuǎn)化,進(jìn)程間傳送文本圖片音頻等以二進(jìn)制傳送。JDK中ObjectOuputStream和ObjectInputStream為輸出輸入流,只有實(shí)現(xiàn)SeriaLizable/Externalizable接口的類才能被序列化。如Person對(duì)象傳遞給內(nèi)存流使用DataConstractJsonSeralizer, MemoryStream stream = new MemoryStream();?DataConstractJsonSeralizer SER = new?DataConstractJsonSeralizer(typeof(person)); ?ser.writeObjectStream(stream, person);顯示json輸出,StramReader sr = new StreamReader(stream1); sr.ReadToEnd()。
13,講講分布式唯一ID。
確定ID存儲(chǔ)用64位,1個(gè)64位二進(jìn)制1是這樣的00000000.....1100......0101,切割64位,某段二進(jìn)制表示成1個(gè)約束條件,前41位為毫秒時(shí)間,后緊接9位為IP,IP之后為自增的二進(jìn)制,記錄當(dāng)前面位數(shù)相同情況下是第幾個(gè)id,如現(xiàn)在有10臺(tái)機(jī)器,這個(gè)id生成器生成id極限是同臺(tái)機(jī)器1ms內(nèi)生成2的14次方個(gè)ID。
分布式唯一ID = 時(shí)間戳 << 41位, int類型服務(wù)器編號(hào) << 10,序列自增sequence。每個(gè)時(shí)間戳內(nèi)只能生成固定數(shù)量如(10萬(wàn))個(gè)自增號(hào),達(dá)到最大值則同步等待下個(gè)時(shí)間戳,自增從0開(kāi)始。將毫秒數(shù)放在最高位,保證生成的ID是趨勢(shì)遞增的,每個(gè)業(yè)務(wù)線、每個(gè)機(jī)房、每個(gè)機(jī)器生成的ID都是不同的。如39bit毫秒數(shù)|4bit業(yè)務(wù)線|2bit機(jī)房|預(yù)留|7bit序列號(hào)。高位取2016年1月1日1到現(xiàn)在的毫秒數(shù),系統(tǒng)運(yùn)行10年,至少需要10年x365天x24小時(shí)x3600秒x1000毫秒=320x10~9,差不多39bit給毫秒數(shù),每秒單機(jī)高峰并發(fā)小于100,差不多7bit給每毫秒的自增號(hào),5年內(nèi)機(jī)房小于100臺(tái)機(jī)器,預(yù)留2bit給機(jī)房,每個(gè)機(jī)房小于100臺(tái)機(jī)器,預(yù)留7bit給每個(gè)機(jī)房,業(yè)務(wù)線小于10個(gè),預(yù)留4bit給業(yè)務(wù)線標(biāo)識(shí)。
64bit分布式ID(42bit毫秒+5bit機(jī)器ID+12位自增)等
生成分布式ID的方式:A,2個(gè)自增表,步長(zhǎng)相互隔開(kāi) ? B,時(shí)間的毫秒或者納秒 ?C,UUID ? ? ? ? D,64位約束條件(如上)
14,NIO和IO的區(qū)別
第一點(diǎn),NIO少了1次從內(nèi)核空間到用戶空間的拷貝。
ByteBuffer.allocateDirect()分配的內(nèi)存使用的是本機(jī)內(nèi)存而不是Java堆上的內(nèi)存,和網(wǎng)絡(luò)或者磁盤交互都在操作系統(tǒng)的內(nèi)核空間中發(fā)生。allocateDirect()的區(qū)別在于這塊內(nèi)存不由java堆管理, 但仍然在同一用戶進(jìn)程內(nèi)。
第二點(diǎn),NIO以塊處理數(shù)據(jù),IO以流處理數(shù)據(jù)
第三點(diǎn),非阻塞,NIO1個(gè)線程可以管理多個(gè)輸入輸出通道
15,內(nèi)存泄漏
未對(duì)作廢數(shù)據(jù)內(nèi)存單元置為null,盡早釋放無(wú)用對(duì)象的引用,使用臨時(shí)變量時(shí),讓引用變量在推出活動(dòng)域后自動(dòng)設(shè)置為null,暗示垃圾收集器收集;程序避免用String拼接,用StringBuffer,因?yàn)槊總€(gè)String會(huì)占用內(nèi)存一塊區(qū)域;盡量少用靜態(tài)變量(全局不會(huì)回收);不要集中創(chuàng)建對(duì)象尤其大對(duì)象,可以使用流操作;盡量使用對(duì)象池,不再循環(huán)中創(chuàng)建對(duì)象,優(yōu)化配置;創(chuàng)建對(duì)象到單例getInstance中,對(duì)象無(wú)法回收被單例引用;服務(wù)器session時(shí)間設(shè)置過(guò)長(zhǎng)也會(huì)引起內(nèi)存泄漏。
16,對(duì)象克隆和實(shí)現(xiàn)方式
克隆的對(duì)象可能包含一些已經(jīng)修改過(guò)的屬性,而new1個(gè)對(duì)象屬性都還是初始化時(shí)候的值,被復(fù)制克隆的類要實(shí)現(xiàn)Clonable接口,覆蓋clone()方法,訪問(wèn)修飾符為public,方法中調(diào)用super.clone()得到所需要的復(fù)制方法,類中的屬性類也需要實(shí)現(xiàn)Clonable接口,覆寫(xiě)clone()方法,并在super中也調(diào)用子屬性類的clone()復(fù)制,才可以實(shí)現(xiàn)深拷貝。
或者寫(xiě)到流中序列化的方式來(lái)實(shí)現(xiàn),不必考慮引用類型中還包含引用類型,直接用序列化來(lái)實(shí)現(xiàn)對(duì)象的深復(fù)制拷貝,即將對(duì)象寫(xiě)到流,再?gòu)牧髦凶x出來(lái),需要實(shí)現(xiàn)seriazation接口。
17,redis內(nèi)存數(shù)據(jù)上升到一定大小會(huì)執(zhí)行數(shù)據(jù)淘汰策略,redis提供了6種數(shù)據(jù)淘汰策略。
LRU:從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集合中挑選最近最少使用的數(shù)據(jù)淘汰
random:從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)中挑選任意數(shù)據(jù)淘汰
ttl:從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集合中挑選將要過(guò)期的數(shù)據(jù)淘汰。
notenvision:禁止驅(qū)逐數(shù)據(jù)
如mysql中有2千萬(wàn)數(shù)據(jù),redis只存儲(chǔ)20萬(wàn)的熱門數(shù)據(jù)。LRU或者TTL都滿足熱點(diǎn)數(shù)據(jù)讀取較多,不太可能超時(shí)特點(diǎn)。
redis特點(diǎn):速度塊,O(1),豐富的數(shù)據(jù)類型,支持事物原子性,可用于緩存,比memecache速度塊,可以持久化數(shù)據(jù)。
常見(jiàn)問(wèn)題和解決:Master最好不做持久化如RDB快照和AOF日志文件;如果數(shù)據(jù)比較重要,某分slave開(kāi)啟AOF備份數(shù)據(jù),策略為每秒1次,為了主從復(fù)制速度及穩(wěn)定,MS主從在同一局域網(wǎng)內(nèi);主從復(fù)制不要用圖狀結(jié)構(gòu),用單向鏈表更為穩(wěn)定 M-S-S-S-S。。。。;redis過(guò)期采用懶漢+定期,懶漢即get/set時(shí)候檢查key是否過(guò)期,過(guò)期則刪除key,定期遍歷每個(gè)DB,檢查制定個(gè)數(shù)個(gè)key;結(jié)合服務(wù)器性能調(diào)節(jié)并發(fā)情況。
過(guò)期淘汰,數(shù)據(jù)寫(xiě)入redis會(huì)附帶1個(gè)有效時(shí)間,這個(gè)有效時(shí)間內(nèi)該數(shù)據(jù)被認(rèn)為是正確的并不關(guān)心真實(shí)情況,例如對(duì)支付等業(yè)務(wù)采用版本號(hào)實(shí)現(xiàn),redis中每一份數(shù)據(jù)都維持1個(gè)版本號(hào),DB中也維持1份,只有當(dāng)redis的與DB中的版本一致時(shí),才會(huì)認(rèn)為redis為有效的,不過(guò)仍然每次都要訪問(wèn)DB,只需要查詢version版本字段即可。
18,異步化,生產(chǎn)接口每秒鐘10萬(wàn)并發(fā),消費(fèi)者用異步慢慢消費(fèi)。緩存模式空間換時(shí)間,把1兩億的數(shù)據(jù)名單打到緩存。服務(wù)降級(jí),把不重要的任務(wù)放棄;靜態(tài)資源離線包下載機(jī)制,在wify下會(huì)主動(dòng)提前把靜態(tài)下載前端層保護(hù)可請(qǐng)將用戶請(qǐng)求延長(zhǎng),點(diǎn)擊后主動(dòng)給它隨機(jī)等待2s的時(shí)間/2分鐘之內(nèi)不能請(qǐng)求;后端做部分接口的開(kāi)關(guān),設(shè)置超短耗時(shí)時(shí)間,原來(lái)只用5ms的只給20ms。
系統(tǒng)一段時(shí)間內(nèi)會(huì)自動(dòng)重試,重試多次后就認(rèn)為是失敗了,檢查支付接口返回該訂單的錢,支付操作如果回復(fù)錯(cuò)誤則回滾扣庫(kù)存的事務(wù),沒(méi)返回則會(huì)記錄進(jìn)行中pendding狀態(tài),結(jié)束整個(gè)過(guò)程,等通知失敗/成功,AB系統(tǒng)之間會(huì)出現(xiàn)死循環(huán)補(bǔ)償,如B退單不成功,一般就是記錄錯(cuò)誤日志了。超時(shí)每隔一段時(shí)間去定時(shí)回調(diào)服務(wù)定時(shí)回滾,一定次數(shù)還是超時(shí)則提示用戶聯(lián)系客服,訂單庫(kù)存可以不會(huì)滾,記錄狀態(tài),如果一直調(diào)用支付不成功,則讓用戶自己去處理聯(lián)系客服,可以不回滾用戶的數(shù)據(jù),金額扣了才算真正完成,是一種簡(jiǎn)單粗暴的做法。
公共配置抽象成存儲(chǔ)到zookeeper配置中心或者redis等,DB也存儲(chǔ)一份,各應(yīng)用監(jiān)聽(tīng)ZK的配置變化,可以建一個(gè)配置web管理頁(yè)面。
19,dubbo用ProxyFactoty代理工廠將HelloServiceImpl封裝成1個(gè)Inoke執(zhí)行,即ProxyFactory.getInvoke(ref, (Class)接口,注冊(cè)URL,解碼參數(shù)),并將Invoke導(dǎo)出成1個(gè)Exporter,包括去注冊(cè)中心ZK注冊(cè)服務(wù)。Invoke有本地執(zhí)行的Invoke,遠(yuǎn)程通信執(zhí)行的Invoke。
20,每次扣減庫(kù)存時(shí)加上1個(gè)請(qǐng)求流水編號(hào),上層請(qǐng)求扣減庫(kù)存沒(méi)拿到結(jié)果的話,重新查詢1次做重試操作,量不大都是加鎖處理。減少鎖的時(shí)間,犧牲冪等性,扣減為DB下地操作,查詢扣減和設(shè)置合成1步,中間沒(méi)有網(wǎng)絡(luò)請(qǐng)求。利用緩存,通過(guò)寫(xiě)log記錄操作,異步合并日志及更新,重啟時(shí)cache失效,讀log恢復(fù),避免重復(fù)提交,寫(xiě)操作不建議重試快速失敗。多個(gè)商品同時(shí)增減庫(kù)存,可使用訂單號(hào)做冪等處理,應(yīng)用層對(duì)單個(gè)商品減庫(kù)存,操作排隊(duì),商品消息ID路由在1個(gè)應(yīng)用server處理,讀本地緩存,失效再redis,DB采用樂(lè)觀鎖,組提交,1次減庫(kù)存多個(gè)訂單的購(gòu)買量。可將同一個(gè)key下庫(kù)存m分為n組k1......kn,每組數(shù)為m/n,扣減依次在各組扣減,減少并發(fā)沖突。隊(duì)列裝滿后關(guān)閉隊(duì)列進(jìn)入,然后用戶輪訓(xùn)自己是否搶到了異步ajax,用戶資源隊(duì)列固定長(zhǎng)度。2個(gè)隊(duì)列,1個(gè)銷售的資源隊(duì)列放入redis,有另外1個(gè)隊(duì)列用來(lái)裝搶購(gòu)的會(huì)員的uid。
紅包狀態(tài)正常,并成功將狀態(tài)改為“已領(lǐng)取”,且消息發(fā)送成功,用戶端開(kāi)始消費(fèi)該消息,如果消費(fèi)失敗/超時(shí),用MQ做重試做冪等,直到成功,每條消息有唯一編號(hào)且保證消息處理成功與去重表的日志同時(shí)出現(xiàn)。
熱點(diǎn)將hot data拆分,分在不同庫(kù)和不同表,分散熱點(diǎn)Data,減輕DB并發(fā)更新熱點(diǎn)帶來(lái)RT升高和應(yīng)用連接超時(shí)。SQL在mysql層加以限制,SQL超時(shí)/thradrunning到1定值則拒絕SQL執(zhí)行,一定時(shí)間異步將結(jié)果寫(xiě)入DB,nginx對(duì)IP做限制,可能誤殺。
21,SpringAOP,XML配置<aop:config>,切面<aop:aspect>切點(diǎn)<aop:pointcut>,連接切點(diǎn)和通知方法<aop:before>和<aop:after>等,注解可以直接使用@before執(zhí)行方法@after ,@before(“pointcut()”) ,@after("pointcut"), @Aroud("excutete()),@AfteReturning,@AfterThrowing,可作日志事務(wù),權(quán)限等待,AOP即通過(guò)把具體的類創(chuàng)建對(duì)應(yīng)的 代理類,從代理類來(lái)對(duì)具體進(jìn)行操作。 ? ? ? ? ? ? ? ? ? ? ?
目標(biāo)實(shí)現(xiàn)了接口,默認(rèn)采用JDK實(shí)現(xiàn)AOP,也可以強(qiáng)制使用CGlib來(lái)實(shí)現(xiàn)AOP,目標(biāo)沒(méi)有實(shí)現(xiàn)接口的話,則必須采用CGlib,Spring自動(dòng)在JDK和CGlib切換。如果要求spring強(qiáng)制使用CGlib實(shí)現(xiàn)AOP,則可以配置,添加Cglib庫(kù)。。。jar, Spring配置文件中加入<aop:aspecj-autoproxy proxy-target-Class=true> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
22,MyISM采用表級(jí)鎖,對(duì)Myism表讀不會(huì)阻塞讀,會(huì)阻塞同表寫(xiě),對(duì)Myism寫(xiě)則會(huì)阻塞讀和寫(xiě),即一個(gè)線程獲得1個(gè)表的寫(xiě)鎖后,只有持有鎖的線程可以對(duì)表更新操作,其他線程的讀和寫(xiě)都會(huì)等待。
InnoDB,采用行級(jí)鎖,支持事務(wù),例如只對(duì)a列加索引,如果update ...where a=1 and b=2其實(shí)也會(huì)鎖整個(gè)表, select 使用共享鎖,update insert delete采用排它鎖,commit會(huì)把鎖取消,當(dāng)然select by id for update也可以制定排它鎖。
23,實(shí)時(shí)隊(duì)列采用雙隊(duì)列模式,生產(chǎn)者將行為記錄寫(xiě)入Queue1,worker服務(wù)從Queue1消費(fèi)新鮮數(shù)據(jù),如果異常則寫(xiě)入Queue2(主要保存異常數(shù)據(jù)),RetryWorker會(huì)監(jiān)聽(tīng)Queue2,消費(fèi)異常數(shù)據(jù),如果還未處理成功按照一定的策略等待或者將異常數(shù)據(jù)再寫(xiě)入Queue2,如果數(shù)據(jù)發(fā)生積壓可以調(diào)整worker的消費(fèi)游標(biāo),從最新數(shù)據(jù)重新開(kāi)始消費(fèi),保證了最新data得到處理,中間未處理的一段則可以啟動(dòng)backupWorker指定起止游標(biāo)在消費(fèi)完指定區(qū)間的數(shù)據(jù)后,backupWorker會(huì)自動(dòng)停止。
DB降級(jí)開(kāi)關(guān)后,可直接寫(xiě)入redis(storm),同時(shí)將數(shù)據(jù)寫(xiě)入一份到Retry隊(duì)列,在開(kāi)啟DB降級(jí)開(kāi)關(guān)后消費(fèi)Retry隊(duì)列中的數(shù)據(jù),從而把數(shù)據(jù)寫(xiě)入到mysql中,達(dá)到最終一致性。MYSQL切分為分片為2的N次方,例如原來(lái)分為兩個(gè)庫(kù)d0和d1均放在s0服務(wù)器上,s0同時(shí)有備機(jī)s1,擴(kuò)容只要幾步驟:確保s0到s1服務(wù)器同步順利,沒(méi)有明顯延遲;s0暫時(shí)關(guān)閉讀寫(xiě)權(quán)限;確保s1已經(jīng)完全同步到s0更新;s1開(kāi)放讀寫(xiě)權(quán)限;d1的dns由s0切換到s1;s0開(kāi)放讀寫(xiě)權(quán)限。
24,DB的特性和隔離級(jí)別
4大特性:原子性,一致性,分離性,持久性
隔離級(jí)別:
讀提交:寫(xiě)事務(wù)禁止讀
讀未提交:寫(xiě)事務(wù)允許讀
可重復(fù)讀:寫(xiě)事務(wù)禁止讀事務(wù),讀禁止寫(xiě)
序列化:全部禁止
詳細(xì)說(shuō)明:讀提交1個(gè)事務(wù)開(kāi)始寫(xiě)則全部禁止其他事務(wù)訪問(wèn)該行。讀未提交1個(gè)事務(wù)開(kāi)始寫(xiě)則不允許其他事務(wù)同時(shí)寫(xiě),但可以讀。可重復(fù)讀 讀事務(wù)會(huì)禁止寫(xiě)事務(wù),寫(xiě)事物則禁止其他任何事務(wù)。序列化性能最低,全部禁止,串行執(zhí)行。 MYSQL默認(rèn)的是可重復(fù)讀。
25,帖子服務(wù)、元數(shù)據(jù)服務(wù)、帖子搜索服務(wù),提供索引數(shù)據(jù)存儲(chǔ),tid和uid查詢直接從帖子服務(wù)從元數(shù)據(jù)返回,其他檢索查詢有帖子搜索服務(wù)從索引數(shù)據(jù)檢索并返回,帖子服務(wù)增刪改查用MQ同步到帖子搜索服務(wù),搜索服務(wù)修改索引的數(shù)據(jù)(索引樹(shù),倒排表),索引表t_mapping(tid,uid)。
300億數(shù)據(jù)在全量索引庫(kù)中,數(shù)百萬(wàn)一天內(nèi)修改過(guò)的數(shù)據(jù)在一天庫(kù)中,50萬(wàn)小時(shí)內(nèi)修改過(guò)的數(shù)據(jù)在小時(shí)庫(kù)中,在update請(qǐng)求時(shí),只會(huì)操作最低級(jí)別的索引例如小時(shí)庫(kù)。小時(shí)庫(kù),1小時(shí)合并一次,合并到天庫(kù),天庫(kù)一天合并1次,合并到全量庫(kù)中。
26,講一下NIO和網(wǎng)絡(luò)傳輸
NIO Reactor反應(yīng)器模式,例如汽車是乘客訪問(wèn)的實(shí)體reactor,乘客上車后到售票員處Acceptor登記,之后乘客便可休息睡覺(jué)了,到達(dá)乘客目的地后,售票員Aceptor將其喚醒即可。持久TCP長(zhǎng)鏈接每個(gè)client和server之間有存在一個(gè)持久連接,當(dāng)CCU(用戶并發(fā)數(shù)量)上升,阻塞server無(wú)法為每個(gè)連接運(yùn)行1個(gè)線程,自己開(kāi)發(fā)1個(gè)二進(jìn)制協(xié)議,將message壓縮至3-6倍,傳輸雙向且消息頻率高,假設(shè)server鏈接了2000個(gè)client,每個(gè)client平均每分鐘傳輸1-10個(gè)message,1個(gè)messaged的大小為幾百字節(jié)/幾千字節(jié),而server也要向client廣播其他玩家的當(dāng)前信息,需要高速處理消息的能力。Buffer,網(wǎng)絡(luò)字節(jié)存放傳輸?shù)牡胤?#xff0c;從channel中讀寫(xiě),從buffer作為中間存儲(chǔ)格式,channel是網(wǎng)絡(luò)連接與buffer間數(shù)據(jù)通道,像之前的socket的stream。
27,緩存擊透
預(yù)加載;
加載DB時(shí)同步,其他則等待;
DB端做SQL合并,Queue合并排隊(duì)處理;
部分緩存設(shè)置為永不過(guò)期;
先清除緩存,讀取數(shù)據(jù)時(shí)候則等待500ms,500ms緩存應(yīng)該已經(jīng)加載完成;
采用雙key緩存,A1為原始緩存,A2為拷貝緩存;
如果DB為空null則g給redis設(shè)置1個(gè)NFC空nei容。
28,Dubbo源碼使用了哪些設(shè)計(jì)模式
A,工廠模式,ExtenstionLoader.getExtenstionLoader(Protocol.class).getAdaptiveExtenstion()
B,裝飾器模式+責(zé)任鏈,以provider的調(diào)用鏈為例,具體調(diào)用鏈代碼是在protocolFilterWrapper的buildInvokeChain完成的,將注解中含有g(shù)roup=provider的Filter實(shí)現(xiàn),調(diào)用順序?yàn)镋choFilter -> ClassLoaderFilter -> GenericFilter -> ContextFilter -> ExceptionFilter -> TimeoutFilter -> MonitorFilter -> TraceFilter。裝飾器模式和責(zé)任鏈混合使用,Echo是回聲測(cè)試請(qǐng)求,ClassLoaderFilter則只是在其主功能上添加了功能。
C,觀察者模式,provider啟動(dòng)時(shí)需要與注冊(cè)中心交互,先注冊(cè)自己的服務(wù),再訂閱自己的服務(wù),訂閱時(shí)采用了觀察者模式,注冊(cè)中心每5s定時(shí)檢查是否有服務(wù)更新,有更新則向服務(wù)提供者發(fā)送1個(gè)notify消息后即可運(yùn)行NotifyListener的notity方法,執(zhí)行監(jiān)聽(tīng)器方法。
D,動(dòng)態(tài)代理模式。 ?擴(kuò)展JDK的ExtensionLoaderdeAdaptive實(shí)現(xiàn),根據(jù)調(diào)用階段動(dòng)態(tài)參數(shù)決定調(diào)用哪個(gè)類,生成代理類的代碼是ExtensionLoader的createAdaptiveExtenstionClassLoader方法。
29,平衡二叉樹(shù),左右高度之差不超過(guò)1,Add/delete可能造成高度>1,此時(shí)要旋轉(zhuǎn),維持平衡狀態(tài),避免二叉樹(shù)退化為鏈表,讓Add/Delete時(shí)間復(fù)雜度但控制在O(log2N),旋轉(zhuǎn)算法2個(gè)方法,1是求樹(shù)的高度,2是求2個(gè)高度最大值,1個(gè)空樹(shù)高度為-1,只有1個(gè)根節(jié)點(diǎn)的樹(shù)的高度為0,以后每一層+1,平衡樹(shù)任意節(jié)點(diǎn)最多有2個(gè)兒子,因此高度不平衡時(shí),此節(jié)點(diǎn)的2棵子樹(shù)高度差為2。例如單旋轉(zhuǎn),雙旋轉(zhuǎn),插入等。
紅黑樹(shù)放棄完全平衡,追求大致平衡,保證每次插入最多要3次旋轉(zhuǎn)就能平衡。
30,多線程同步鎖
A,RentrantLock,可重入的互斥鎖,可中斷可限時(shí),公平鎖,必須在finally釋放鎖,而synchronize由JVM釋放。可重入但是要重復(fù)退出,普通的lock()不能響應(yīng)中斷,lock.lockInterruptbly()可響應(yīng)中斷,可以限時(shí)tryLock(),超時(shí)返回false,不會(huì)永久等待構(gòu)成死鎖。
B,Confition條件變量,signal喚醒其中1個(gè)在等待的線程,signalall喚醒所有在等待的線程await()等待并釋放鎖,與lock結(jié)合使用。
C,semaphore信號(hào)量,多個(gè)線程比(額度=10)進(jìn)入臨界區(qū),其他則阻塞在臨界區(qū)外。
D,ReadWriteLock,讀讀不互斥,讀寫(xiě)互斥,寫(xiě)寫(xiě)互斥。
E,CountDownLantch倒數(shù)計(jì)時(shí)器,countdown()和await()
F,CyCliBarrier
G,LockSupport,方法park和unpark
31,棧溢出的原因
是否遞歸的調(diào)用;大量循環(huán);全局變量是否過(guò)多;數(shù)組,List,Map數(shù)據(jù)是否過(guò)大;用DDMS工具檢查地方。
內(nèi)存溢出的原因
過(guò)多使用了static;static最好只用int和string等基本類型;大量的遞歸或者死循環(huán);大數(shù)據(jù)項(xiàng)的查詢,如返回表的所有記錄,應(yīng)該采用分頁(yè)查詢。檢查是否有數(shù)組、List、map中存放的是對(duì)象的引用而不是對(duì)象,這些引用會(huì)讓對(duì)應(yīng)對(duì)象不能被釋放。
棧過(guò)大會(huì)導(dǎo)致內(nèi)存占用過(guò)多,頻繁頁(yè)交換阻礙效率。
32,說(shuō)一下http/2
Http/2采用二進(jìn)制格式而不是文本
Http/2是完全多路復(fù)用的,而非有序并阻塞的。
Http/2使用報(bào)頭壓縮
Http/2讓服務(wù)器可以將響應(yīng)主動(dòng)推送到客戶端緩存中。
33,說(shuō)一下內(nèi)存泄露
A,HashMap,vector等容易(靜態(tài)集合類), 和應(yīng)用程序生命周期一樣,所引用的所有對(duì)象Object也不能釋放。
B,當(dāng)集合類里面的對(duì)象屬性被修改后,再調(diào)用remove()不起作用,hashcode值發(fā)生了改變
C,其對(duì)象add監(jiān)聽(tīng)器,但是往往釋放對(duì)象時(shí)忘記去刪除這些監(jiān)聽(tīng)器
D,各種連接記得關(guān)閉
E,內(nèi)部類的引用
F,調(diào)用其他模塊,對(duì)象作用參數(shù)
G,單例模式,持有外部對(duì)象引用無(wú)法收回。
內(nèi)存泄露例子
Vector<String> A = new?Vector<String>();
for(int i = 0; i < 100; i++){
Object o = new Object ();
A.add(o);
o = null;
}
?........
內(nèi)存溢出的例子
StringBuffer b = new?StringBuffer ();
for(int i =0; i < 100; i++){
for(int j =0; i < 100; j++){
b.append(*);
}
}
34,SpirngMVC的生命周期 和 SpringBean的生命周期
SpirngMVC的生命周期 :
A,DispatcherSerlvet(前端控制器)
B,-》 HandlerMapping(處理器映射器),根據(jù)xml注解查找對(duì)應(yīng)的Hander -》 返回Handler
C,-》處理器適配器去執(zhí)行Handler
D,-》Handler執(zhí)行完成后給處理器適配器返回ModelAndView
E,-》前端控制器請(qǐng)求視圖解析器去執(zhí)行視圖解析,根據(jù)邏輯視圖名解析成真正的視圖JSP,向前端控制器返回view
F,-》前端控制器進(jìn)行視圖渲染,將模型數(shù)據(jù)放到request-》返回給用戶
SpringBean的生命周期:
Instance實(shí)例化-》設(shè)置屬性值-》調(diào)用BeanNameAware的setBeanName方法-》調(diào)用BeanPostProsessor的預(yù)初始化方法-》調(diào)用InitializationBean的afterPropertiesSet()的方法-》調(diào)用定制的初始化方法callCustom的init-method-》調(diào)用BeanPostProsessor的后初始化方法-》Bean可以使用了 -》 容器關(guān)閉-》 調(diào)用DisposableBean的destroy方法-》調(diào)用定制的銷毀方法CallCustom的destroy-method。
35,AQS,抽象隊(duì)列同步器
AQS定義2種資源共享方式:獨(dú)占與share共享
獨(dú)占:只能有1個(gè)線程運(yùn)行
share共享:多個(gè)線程可以同p執(zhí)行如samphore/countdownlanch
AQS負(fù)責(zé)獲取共享state的入隊(duì)和/喚醒出隊(duì)等,AQS在頂層已經(jīng)實(shí)現(xiàn)好了,AQS有幾種方法:acquire()是獨(dú)占模式下線程共享資源的頂層入口,如獲取到資源,線程直接返回,否則進(jìn)入等待隊(duì)列,直到獲取到資源為止。tryAcquire()將線程加入等待隊(duì)列的尾部,并標(biāo)志為獨(dú)占。acquireQueued()使線程在等待隊(duì)列中獲取資源,一直到獲取資源后不返回,如果過(guò)程被中斷也返回true,否則false。
線程在等待過(guò)程中被中斷是不響應(yīng)的,獲取資源才補(bǔ)上中斷。將線程添加到隊(duì)列尾部用了CAS自旋(死循環(huán)直到成功),類似于AutomicInteger的CAS自旋volatile變量。
start->tryAcquire -> 入隊(duì) -> 找安全點(diǎn) -> park等待狀態(tài) -> 當(dāng)前節(jié)點(diǎn)成對(duì)頭 -> End
36,單例模式的7種寫(xiě)法
懶漢2種,枚舉,餓漢2種,靜態(tài)內(nèi)部類,雙重校驗(yàn)鎖(推薦)。
37,lucence倒排索引
三個(gè)文件:字典文件,頻率文件,位置文件。詞典文件不僅保存有每個(gè)關(guān)鍵詞,還保留了指向頻率文件和位置文件的指針,通過(guò)指針可以找到該關(guān)鍵字的頻率信息和位置信息。
field的概念,用于表達(dá)信息所在位置(如標(biāo)題中,文章中,url中),在建索引中,該field信息也記錄在詞典文件中,每個(gè)關(guān)鍵詞都有一個(gè)field信息(因?yàn)槊總€(gè)關(guān)鍵字一定屬于一個(gè)或多個(gè)field)。
關(guān)鍵字是按字符順序排列的(lucene沒(méi)有使用B樹(shù)結(jié)構(gòu)),因此lucene可以用二元搜索算法快速定位關(guān)鍵詞。
假設(shè)要查詢單詞 “l(fā)ive”,lucene先對(duì)詞典二元查找、找到該詞,通過(guò)指向頻率文件的指針讀出所有文章號(hào),然后返回結(jié)果。詞典通常非常小,因而,整個(gè)過(guò)程的時(shí)間是毫秒級(jí)的。
對(duì)詞典文件中的關(guān)鍵詞進(jìn)行了壓縮,關(guān)鍵詞壓縮為<前綴長(zhǎng)度,后綴>,例如:當(dāng)前詞為“阿拉伯語(yǔ)”,上一個(gè)詞為“阿拉伯”,那么“阿拉伯語(yǔ)”壓縮為<3,語(yǔ)>。對(duì)數(shù)字的壓縮,數(shù)字只保存與上一個(gè)值的差值。
38,ZooKeeper分布式高可用
ZooKeeper 運(yùn)行期間,集群中至少有過(guò)半的機(jī)器保存了最新數(shù)據(jù)。集群超過(guò)半數(shù)的機(jī)器能夠正常工作,集群就能夠?qū)ν馓峁┓?wù)。
zookeeper可以選出N臺(tái)機(jī)器作主機(jī),它可以實(shí)現(xiàn)M:N的備份;keepalive只能選出1臺(tái)機(jī)器作主機(jī),所以keepalive只能實(shí)現(xiàn)M:1的備份。
通常有以下兩種部署方案:雙機(jī)房部署(一個(gè)穩(wěn)定性更好、設(shè)備更可靠的機(jī)房,這個(gè)機(jī)房就是主要機(jī)房,而另外一個(gè)機(jī)房則更加廉價(jià)一些,例如,對(duì)于一個(gè)由 7 臺(tái)機(jī)器組成的 ZooKeeper 集群,通常在主要機(jī)房中部署 4 臺(tái)機(jī)器,剩下的 3 臺(tái)機(jī)器部署到另外一個(gè)機(jī)房中);三機(jī)房部署(無(wú)論哪個(gè)機(jī)房發(fā)生了故障,剩下兩個(gè)機(jī)房的機(jī)器數(shù)量都超過(guò)半數(shù)。在三個(gè)機(jī)房中都部署若干個(gè)機(jī)器來(lái)組成一個(gè) ZooKeeper 集群。假設(shè)機(jī)器總數(shù)為 N,各機(jī)房機(jī)器數(shù):N1 = (N-1)/2?,N2=1~(N-N1)/2?,N3 = N - N1 - N2?)。
水平擴(kuò)容就是向集群中添加更多機(jī)器,Zookeeper2種方式(不完美),一種是集群整體重啟,另外一種是逐臺(tái)進(jìn)行服務(wù)器的重啟。
?
39,如何將數(shù)據(jù)分布在redis第幾個(gè)庫(kù)?
答:redis 本身支持16個(gè)數(shù)據(jù)庫(kù),通過(guò) 數(shù)據(jù)庫(kù)id 設(shè)置,默認(rèn)為0。
例如jedis客戶端設(shè)置。一:JedisPool(org.apache.commons.pool.impl.GenericObjectPool.Config poolConfig, String host, int port, int timeout, String password, int database);
第一種通過(guò)指定構(gòu)造函數(shù)database字段選擇庫(kù),不設(shè)置則默認(rèn)0庫(kù)。二:jedis.select(index);調(diào)用jedis的select方法指定。
40,類加載器的雙親委派加載機(jī)制?
答:當(dāng)一個(gè)類收到了類加載請(qǐng)求,他首先不會(huì)嘗試自己去加載這個(gè)類,而是把這個(gè)請(qǐng)求委派給父類去完成,每一個(gè)層次類加載器都是如此,因此所有的加載請(qǐng)求都應(yīng)該傳送到啟動(dòng)類加載其中,只有當(dāng)父類加載器反饋?zhàn)约簾o(wú)法完成這個(gè)請(qǐng)求的時(shí)候(在它的加載路徑下沒(méi)有找到所需加載的Class),子類加載器才會(huì)嘗試自己去加載。
41,kafka高性能的原因?
答:
A,Broker NIO異步消息處理,實(shí)現(xiàn)了IO線程與業(yè)務(wù)線程分離;
B,磁盤順序?qū)?#xff1b;
C, 零拷貝(跳過(guò)用戶緩沖區(qū)的拷貝,建立一個(gè)磁盤空間和內(nèi)存的直接映射,數(shù)據(jù)不再?gòu)?fù)制到用戶態(tài)緩沖區(qū));
D,分區(qū)/分段(每次文件操作都是對(duì)一個(gè)小文件的操作,非常輕便,同時(shí)也增加了并行處理能力);
F,批量發(fā)送 (可以指定緩存的消息達(dá)到某個(gè)量的時(shí)候就發(fā)出去,或者緩存了固定的時(shí)間后就發(fā)送出去,大大減少服務(wù)端的I/O次數(shù))
E,數(shù)據(jù)壓縮
42,冪等的處理方式?
答:一、查詢與刪除操作是天然冪等
二、唯一索引,防止新增臟數(shù)據(jù)
三、token機(jī)制,防止頁(yè)面重復(fù)提交
四、悲觀鎖 ?for update
五、樂(lè)觀鎖(通過(guò)版本號(hào)/時(shí)間戳實(shí)現(xiàn),?通過(guò)條件限制where avai_amount-#subAmount# >= 0)
六、分布式鎖
七、狀態(tài)機(jī)冪等(如果狀態(tài)機(jī)已經(jīng)處于下一個(gè)狀態(tài),這時(shí)候來(lái)了一個(gè)上一個(gè)狀態(tài)的變更,理論上是不能夠變更的,這樣的話,保證了有限狀態(tài)機(jī)的冪等。)
八、select + insert(并發(fā)不高的后臺(tái)系統(tǒng),或者一些任務(wù)JOB,為了支持冪等,支持重復(fù)執(zhí)行)
43,HTTPS工作流程?
a、客戶端發(fā)送自己支持的加密規(guī)則給服務(wù)器,代表告訴服務(wù)器要進(jìn)行連接了
b、服務(wù)器從中選出一套加密算法和hash算法以及自己的身份信息(地址等)以證書(shū)的形式發(fā)送給瀏覽器,證書(shū)中包含服務(wù)器信息,加密公鑰,證書(shū)的辦法機(jī)構(gòu)
c、客戶端收到網(wǎng)站的證書(shū)之后要做下面的事情:?
? ? ? ? c1、驗(yàn)證證書(shū)的合法性
? ? ? ? c2、如果驗(yàn)證通過(guò)證書(shū),瀏覽器會(huì)生成一串隨機(jī)數(shù)作為密鑰K,并用證書(shū)中的公鑰進(jìn)行加密
? ? ? ? c3、用約定好的hash算法計(jì)算握手消息,然后用生成的密鑰K進(jìn)行加密,然后一起發(fā)送給服務(wù)器
d、服務(wù)器接收到客戶端傳送來(lái)的信息,要求下面的事情:?
? ? ? ?d1、用私鑰解析出密碼,用密碼解析握手消息,驗(yàn)證hash值是否和瀏覽器發(fā)來(lái)的一致
? ? ? ?d2、使用密鑰加密消息,回送
如果計(jì)算法hash值一致,握手成功
?
44,RabbitMQ消息堆積怎么處理?
答:
- 增加消費(fèi)者的處理能力(例如優(yōu)化代碼),或減少發(fā)布頻率
- 單純升級(jí)硬件不是辦法,只能起到一時(shí)的作用
- 考慮使用隊(duì)列最大長(zhǎng)度限制,RabbitMQ 3.1支持
- 給消息設(shè)置年齡,超時(shí)就丟棄
- 默認(rèn)情況下,rabbitmq消費(fèi)者為單線程串行消費(fèi),設(shè)置并發(fā)消費(fèi)兩個(gè)關(guān)鍵屬性concurrentConsumers和prefetchCount,concurrentConsumers設(shè)置的是對(duì)每個(gè)listener在初始化的時(shí)候設(shè)置的并發(fā)消費(fèi)者的個(gè)數(shù),prefetchCount是每次一次性從broker里面取的待消費(fèi)的消息的個(gè)數(shù)
- 建立新的queue,消費(fèi)者同時(shí)訂閱新舊queue
- 生產(chǎn)者端緩存數(shù)據(jù),在mq被消費(fèi)完后再發(fā)送到mq
- 打破發(fā)送循環(huán)條件,設(shè)置合適的qos值,當(dāng)qos值被用光,而新的ack沒(méi)有被mq接收時(shí),就可以跳出發(fā)送循環(huán),去接收新的消息;消費(fèi)者主動(dòng)block接收進(jìn)程,消費(fèi)者感受到接收消息過(guò)快時(shí)主動(dòng)block,利用block和unblock方法調(diào)節(jié)接收速率,當(dāng)接收線程被block時(shí),跳出發(fā)送循環(huán)。
- 新建一個(gè)topic,partition是原來(lái)的10倍;然后寫(xiě)一個(gè)臨時(shí)的分發(fā)數(shù)據(jù)的consumer程序,這個(gè)程序部署上去消費(fèi)積壓的數(shù)據(jù),消費(fèi)之后不做耗時(shí)的處理,直接均勻輪詢寫(xiě)入臨時(shí)建立好的10倍數(shù)量的queue;接著臨時(shí)征用10倍的機(jī)器來(lái)部署consumer,每一批consumer消費(fèi)一個(gè)臨時(shí)queue的數(shù)據(jù);等快速消費(fèi)完積壓數(shù)據(jù)之后,得恢復(fù)原先部署架構(gòu),重新用原先的consumer機(jī)器來(lái)消費(fèi)消息;
45,RabbitMQ的消息丟失解決方案?
答:
- 消息持久化:Exchange 設(shè)置持久化:durable:true;Queue 設(shè)置持久化;Message持久化發(fā)送。
- ACK確認(rèn)機(jī)制:消息發(fā)送確認(rèn);消息接收確認(rèn)。
46,負(fù)載均衡算法?
常見(jiàn)6種負(fù)載均衡算法:輪詢,隨機(jī),源地址哈希,加權(quán)輪詢,加權(quán)隨機(jī),最小連接數(shù)。
nginx5種負(fù)載均衡算法:輪詢,weight,ip_hash,fair(響應(yīng)時(shí)間),url_hash
dubbo負(fù)載均衡算法:隨機(jī),輪詢,最少活躍調(diào)用數(shù),一致性Hash
47,JVM內(nèi)存區(qū)域劃分?
答:
- 堆:Java中的堆是用來(lái)存儲(chǔ)對(duì)象本身的以及數(shù)組(當(dāng)然,數(shù)組引用是存放在Java棧中的),是Java垃圾收集器管理的主要區(qū)域。堆是被所有線程共享的,在JVM中只有一個(gè)堆。
- 虛擬機(jī)棧:虛擬機(jī)棧中存放的是一個(gè)個(gè)的棧幀,每個(gè)棧幀對(duì)應(yīng)一個(gè)被調(diào)用的方法,在棧幀中包括局部變量表、操作數(shù)棧、指向當(dāng)前方法所屬的類的運(yùn)行時(shí)常量池的引用、方法返回地址和一些額外的附加信息。當(dāng)線程執(zhí)行一個(gè)方法時(shí),就會(huì)隨之創(chuàng)建一個(gè)對(duì)應(yīng)的棧幀,并將建立的棧幀壓棧。當(dāng)方法執(zhí)行完畢之后,便會(huì)將棧幀出棧。
- 本地方法棧:本地方法棧則是為執(zhí)行本地方法(Native Method)服務(wù)的,在HotSopt虛擬機(jī)中直接就把本地方法棧和Java棧合二為一
- 方法區(qū):方法區(qū)與堆一樣,是被線程共享的區(qū)域。方法區(qū)存儲(chǔ)了類的信息(包括類的名稱、方法信息、字段信息)、靜態(tài)變量、常量以及編譯器編譯后的代碼等。在方法區(qū)中有一個(gè)非常重要的部分就是運(yùn)行時(shí)常量池,它是每一個(gè)類或接口的常量池的運(yùn)行時(shí)表示形式,在類和接口被加載到JVM后,對(duì)應(yīng)的運(yùn)行時(shí)常量池就被創(chuàng)建出來(lái)。當(dāng)然并非Class文件常量池中的內(nèi)容才能進(jìn)入運(yùn)行時(shí)常量池,在運(yùn)行期間也可將新的常量放入運(yùn)行時(shí)常量池中,比如String的intern方法。當(dāng)方法區(qū)無(wú)法滿足內(nèi)存分配需求時(shí),則拋出OutOfMemoryError異常。在HotSpot虛擬機(jī)中,用永久代來(lái)實(shí)現(xiàn)方法區(qū),將GC分代收集擴(kuò)展至方法區(qū),但是這樣容易遇到內(nèi)存溢出的問(wèn)題。JDK1.7中,已經(jīng)把放在永久代的字符串常量池移到堆中。JDK1.8撤銷永久代,引入元空間。
- 程序計(jì)數(shù)器(線程私有):是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器,每條線程都要有一個(gè)獨(dú)立的程序計(jì)數(shù)器,這類內(nèi)存也稱為“線程私有”的內(nèi)存。正在執(zhí)行java方法的話,計(jì)數(shù)器記錄的是虛擬機(jī)字節(jié)碼指令的地址(當(dāng)前指令的地址)。如果還是Native方法,則為空。
- 直接內(nèi)存:在JDK1.4中新加入的NOI類,引入了一種基于通道與緩沖區(qū)的I/O方式,它可以使用Native函數(shù)直接分配堆外內(nèi)存,然后通過(guò)一個(gè)存儲(chǔ)在Java堆中的DirectByteBuffer對(duì)象作為這塊內(nèi)存的引用進(jìn)行操作。
48,jvm YGC和FGC發(fā)生的具體場(chǎng)景?
答:
正在處理的實(shí)現(xiàn)事務(wù)功能,下次自動(dòng)回滾。
隊(duì)列實(shí)現(xiàn)持久化儲(chǔ)存,下次啟動(dòng)自動(dòng)載入。
添加標(biāo)志位,未處理 0,處理中 1,已處理 2。每次啟動(dòng)的時(shí)候,把所有狀態(tài)為 1 的,置為 0。
關(guān)鍵性的應(yīng)用就給電腦配個(gè) UPS。
?
YGC :對(duì)新生代堆進(jìn)行g(shù)c。頻率比較高,因?yàn)榇蟛糠謱?duì)象的存活壽命較短,在新生代里被回收。性能耗費(fèi)較小。
FGC :全堆范圍的gc。默認(rèn)堆空間使用到達(dá)80%(可調(diào)整)的時(shí)候會(huì)觸發(fā)fgc。以我們生產(chǎn)環(huán)境為例,一般比較少會(huì)觸發(fā)fgc,有時(shí)10天或一周左右會(huì)有一次。
YGC發(fā)生場(chǎng)景:edn空間不足
FGC發(fā)生場(chǎng)景:old空間不足,perm空間不足,調(diào)用方法System.gc() ,ygc時(shí)的悲觀策略, dump live的內(nèi)存信息時(shí)(jmap –dump:live)
49,一個(gè)線程池正在處理服務(wù)如果忽然斷電該怎么辦?
答:
隊(duì)列實(shí)現(xiàn)持久化儲(chǔ)存,下次啟動(dòng)自動(dòng)載入。
但是實(shí)際需要看情況,大體思路是這樣。
添加標(biāo)志位,未處理 0,處理中 1,已處理 2。每次啟動(dòng)的時(shí)候,把所有狀態(tài)為 1 的,置為 0。或者定時(shí)器處理
關(guān)鍵性的應(yīng)用就給電腦配個(gè) UPS。
50,SpringBoot的優(yōu)點(diǎn)?
答:
快速構(gòu)建項(xiàng)目,極大的提高了開(kāi)發(fā)、部署效率。
對(duì)主流開(kāi)發(fā)框架的無(wú)配置集成。
項(xiàng)目可獨(dú)立運(yùn)行,無(wú)須外部依賴Servlet容器。
提供運(yùn)行時(shí)的應(yīng)用監(jiān)控。
51,DoS,DDoS,DRDoS攻擊分別是什么?
答:DoS是Denial of Service的簡(jiǎn)寫(xiě)就是拒絕服務(wù)。
DDoS就是Distributed Denial of Service的簡(jiǎn)寫(xiě)就是分布式拒絕服務(wù)。
DRDoS就是Distributed Reflection Denial of Service的簡(jiǎn)寫(xiě),分布反射式拒絕服務(wù)。
DoS、DDos以及DRDoS攻擊手段和防范措施
52,服務(wù)限流的方式?
答:
- 漏桶:水(請(qǐng)求)先進(jìn)入到漏桶里,漏桶以一定的速度出水(接口有響應(yīng)速率),當(dāng)水流入速度過(guò)大會(huì)直接溢出(訪問(wèn)頻率超過(guò)接口響應(yīng)速率),然后就拒絕請(qǐng)求。
- 令牌桶算法:系統(tǒng)會(huì)按恒定1/QPS時(shí)間間隔(如果QPS=100,則間隔是10ms)往桶里加入Token,如果桶已經(jīng)滿了就不再加了.新請(qǐng)求來(lái)臨時(shí),會(huì)各自拿走一個(gè)Token,如果沒(méi)有Token就拒絕服務(wù)。
- 基于redis實(shí)現(xiàn)的限流:假設(shè)每分鐘訪問(wèn)次數(shù)不能超過(guò)10次,在Redis中創(chuàng)建一個(gè)鍵,過(guò)期60秒,對(duì)此服務(wù)接口的訪問(wèn)就把鍵值加1,在60秒內(nèi)增加到10的時(shí)候,禁止訪問(wèn)服務(wù)接口。
- 計(jì)數(shù)器,滑動(dòng)窗口
53,Quartz實(shí)現(xiàn)原理?
答:A、scheduler是一個(gè)計(jì)劃調(diào)度器容器(總部),容器里面可以盛放眾多的JobDetail和trigger,當(dāng)容器啟動(dòng)后,里面的每個(gè)JobDetail都會(huì)根據(jù)trigger按部就班自動(dòng)去執(zhí)行。
B、JobDetail是一個(gè)可執(zhí)行的工作,它本身可能是有狀態(tài)的。
C、Trigger代表一個(gè)調(diào)度參數(shù)的配置,什么時(shí)候去調(diào)。
D、當(dāng)JobDetail和Trigger在scheduler容器上注冊(cè)后,形成了裝配好的作業(yè)(JobDetail和Trigger所組成的一對(duì)兒),就可以伴隨容器啟動(dòng)而調(diào)度執(zhí)行了。
E、scheduler是個(gè)容器,容器中有一個(gè)線程池,用來(lái)并行調(diào)度執(zhí)行每個(gè)作業(yè),這樣可以提高容器效率。
54,數(shù)據(jù)庫(kù)的鎖?
答:行鎖(共享鎖和排他鎖),表鎖,頁(yè)級(jí)鎖,頁(yè)級(jí)鎖,意向鎖,讀鎖,寫(xiě)鎖,悲觀鎖,樂(lè)觀鎖等
55,簡(jiǎn)述ThreadPoolExecutor內(nèi)部工作原理?
答:
先查看當(dāng)前運(yùn)行狀態(tài),如果不是RUNNING 狀態(tài)會(huì)拒絕執(zhí)行任務(wù),如果是RUNNING狀態(tài),就會(huì)查看當(dāng)前運(yùn)行的線程數(shù)量,如果小于核心線程數(shù),會(huì)創(chuàng)建新的線程來(lái)執(zhí)行這個(gè)任務(wù),如果不小于核心線程,會(huì)將這個(gè)任務(wù)放到阻塞隊(duì)列去等代執(zhí)行,直到上一個(gè)任務(wù)執(zhí)行完再來(lái)執(zhí)行這個(gè)任務(wù)。如果失敗會(huì)創(chuàng)建一個(gè)非核心線程來(lái)執(zhí)行這個(gè)任務(wù)如果當(dāng)前線程數(shù)大于最大線程數(shù),會(huì)直接拒絕該任務(wù)。
56,聚集索引和非聚集索引的區(qū)別?
答:
聚集索引:
索引中鍵值的邏輯順序決定了表中相應(yīng)行的物理順序(索引中的數(shù)據(jù)物理存放地址和索引的順序是一致的),可以這么理解:只要是索引是連續(xù)的,那么數(shù)據(jù)在存儲(chǔ)介質(zhì)上的存儲(chǔ)位置也是連續(xù)的。
比方說(shuō):想要到字典上查找一個(gè)字,我們可以根據(jù)字典前面的拼音找到該字,注意拼音的排列時(shí)有順序的。
聚集索引就像我們根據(jù)拼音的順序查字典一樣,可以大大的提高效率。在經(jīng)常搜索一定范圍的值時(shí),通過(guò)索引找到第一條數(shù)據(jù),根據(jù)物理地址連續(xù)存儲(chǔ)的特點(diǎn),然后檢索相鄰的數(shù)據(jù),直到到達(dá)條件截至項(xiàng)。
非聚集索引
索引的邏輯順序與磁盤上的物理存儲(chǔ)順序不同。非聚集索引的鍵值在邏輯上也是連續(xù)的,但是表中的數(shù)據(jù)在存儲(chǔ)介質(zhì)上的物理順序是不一致的,即記錄的邏輯順序和實(shí)際存儲(chǔ)的物理順序沒(méi)有任何聯(lián)系。索引的記錄節(jié)點(diǎn)有一個(gè)數(shù)據(jù)指針指向真正的數(shù)據(jù)存儲(chǔ)位置。
總結(jié)如下:
如果一個(gè)主鍵被定義了,那么這個(gè)主鍵就是作為聚集索引
如果沒(méi)有主鍵被定義,那么該表的第一個(gè)唯一非空索引被作為聚集索引
如果沒(méi)有主鍵也沒(méi)有合適的唯一索引,那么innodb內(nèi)部會(huì)生成一個(gè)隱藏的主鍵作為聚集索引,這個(gè)隱藏的主鍵是一個(gè)6個(gè)字節(jié)的列,改列的值會(huì)隨著數(shù)據(jù)的插入自增。
InnoDB引擎會(huì)為每張表都加一個(gè)聚集索引,而聚集索引指向的的數(shù)據(jù)又是以物理磁盤順序來(lái)存儲(chǔ)的,自增的主鍵會(huì)把數(shù)據(jù)自動(dòng)向后插入,避免了插入過(guò)程中的聚集索引排序問(wèn)題。如果對(duì)聚集索引進(jìn)行排序,這會(huì)帶來(lái)磁盤IO性能損耗是非常大的。
57,java并發(fā)包下有哪些類?
答:ConcurrentHashMap,ConcurrentSkipListMap,ConcurrentNavigableMap
CopyOnWriteArrayList
BlockingQueue,BlockingDeque (ArrayBlockingQueue,LinkedBlockingDeque,LinkedBlockingQueue,DelayQueue,PriorityBlockingQueue,SynchronousQueue)
ConcurrentLinkedDeque,ConcurrentLinkedQueue,TransferQueue,LinkedTransferQueue
CopyOnWriteArraySet,ConcurrentSkipListSet
CyclicBarrier,CountDownLatch
Lock(ReetrantLock,ReetrantReadWriteLock)
Atomic包
58,threadlocal為什么會(huì)出現(xiàn)oom?
答:ThreadLocal里面使用了一個(gè)存在弱引用的map, map的類型是ThreadLocal.ThreadLocalMap. Map中的key為一個(gè)threadlocal實(shí)例。這個(gè)Map的確使用了弱引用,不過(guò)弱引用只是針對(duì)key。每個(gè)key都弱引用指向threadlocal。 當(dāng)把threadlocal實(shí)例置為null以后,沒(méi)有任何強(qiáng)引用指向threadlocal實(shí)例,所以threadlocal將會(huì)被gc回收。?
但是,我們的value卻不能回收,而這塊value永遠(yuǎn)不會(huì)被訪問(wèn)到了,所以存在著內(nèi)存泄露。因?yàn)榇嬖谝粭l從current thread連接過(guò)來(lái)的強(qiáng)引用。只有當(dāng)前thread結(jié)束以后,current thread就不會(huì)存在棧中,強(qiáng)引用斷開(kāi),Current Thread、Map value將全部被GC回收。最好的做法是將調(diào)用threadlocal的remove方法。
在ThreadLocal的get(),set(),remove()的時(shí)候都會(huì)清除線程ThreadLocalMap里所有key為null的value,但是這些被動(dòng)的預(yù)防措施并不能保證不會(huì)內(nèi)存泄漏:
(1)使用static的ThreadLocal,延長(zhǎng)了ThreadLocal的生命周期,可能導(dǎo)致內(nèi)存泄漏。?
(2)分配使用了ThreadLocal又不再調(diào)用get(),set(),remove()方法,那么就會(huì)導(dǎo)致內(nèi)存泄漏,因?yàn)檫@塊內(nèi)存一直存在。
59,mysql數(shù)據(jù)庫(kù)鎖表怎么解決?
答:查詢鎖表信息
當(dāng)前運(yùn)行的所有事務(wù)
select * from information_schema.innodb_trx
當(dāng)前出現(xiàn)的鎖
select * from information_schema.innodb_locks
鎖等待的對(duì)應(yīng)關(guān)系
select * from information_schema.innodb_lock_waits ?
通過(guò) select * from information_schema.innodb_trx 查詢 trx_mysql_thread_id然后執(zhí)行 kill 線程ID
KILL ? 8807;//后面的數(shù)字即時(shí)進(jìn)程的ID
60,java 判斷對(duì)象是否是某個(gè)類的類型方法?
- instanceof 運(yùn)算符是用來(lái)在運(yùn)行時(shí)指出對(duì)象是否是特定類的一個(gè)實(shí)例。instanceof通過(guò)返回一個(gè)布爾值來(lái)指出,這個(gè)對(duì)象是否是這個(gè)特定類或者是它的子類的一個(gè)實(shí)例。
- getClass判斷,如o.getClass().equals(ClassA.class)。(使用instanceof來(lái)判斷一個(gè)對(duì)象是不是屬于某個(gè)類,但是有時(shí)候這個(gè)類是繼承于一個(gè)父類的,所以,不能嚴(yán)格判斷出是不是自己的類,而不是自己的父類。)?
61,Spring+MyBatis實(shí)現(xiàn)讀寫(xiě)分離簡(jiǎn)述?
答:
- 方案一:通過(guò)MyBatis配置文件創(chuàng)建讀寫(xiě)分離兩個(gè)DataSource,每個(gè)SqlSessionFactoryBean對(duì)象的mapperLocations屬性制定兩個(gè)讀寫(xiě)數(shù)據(jù)源的配置文件。將所有讀的操作配置在讀文件中,所有寫(xiě)的操作配置在寫(xiě)文件中。
- 方案二:通過(guò)Spring AOP在業(yè)務(wù)層實(shí)現(xiàn)讀寫(xiě)分離,在DAO層調(diào)用前定義切面,利用Spring的AbstractRoutingDataSource解決多數(shù)據(jù)源的問(wèn)題,實(shí)現(xiàn)動(dòng)態(tài)選擇數(shù)據(jù)源
- 方案三:通過(guò)Mybatis的Plugin在業(yè)務(wù)層實(shí)現(xiàn)數(shù)據(jù)庫(kù)讀寫(xiě)分離,在MyBatis創(chuàng)建Statement對(duì)象前通過(guò)攔截器選擇真正的數(shù)據(jù)源,在攔截器中根據(jù)方法名稱不同(select、update、insert、delete)選擇數(shù)據(jù)源。
- 方案四:通過(guò)spring的AbstractRoutingDataSource和mybatis Plugin攔截器實(shí)現(xiàn)非常友好的讀寫(xiě)分離,原有代碼不需要任何改變。推薦第四種方案
62,紅黑樹(shù)的特點(diǎn)?
答:(1)每個(gè)節(jié)點(diǎn)或者是黑色,或者是紅色。
(2)根節(jié)點(diǎn)是黑色。
(3)每個(gè)葉子節(jié)點(diǎn)(NIL)是黑色。?[注意:這里葉子節(jié)點(diǎn),是指為空(NIL或NULL)的葉子節(jié)點(diǎn)!]
(4)如果一個(gè)節(jié)點(diǎn)是紅色的,則它的子節(jié)點(diǎn)必須是黑色的。
(5)從一個(gè)節(jié)點(diǎn)到該節(jié)點(diǎn)的子孫節(jié)點(diǎn)的所有路徑上包含相同數(shù)目的黑節(jié)點(diǎn)。[這里指到葉子節(jié)點(diǎn)的路徑]
63,kafka消息會(huì)不會(huì)丟失?
答:Kafka消息發(fā)送分同步(sync)、異步(async)兩種方式。默認(rèn)是使用同步方式,可通過(guò)producer.type屬性進(jìn)行配置;Kafka保證消息被安全生產(chǎn),有三個(gè)選項(xiàng)分別是0,1,-1。
通過(guò)request.required.acks屬性進(jìn)行配置:
0代表:不進(jìn)行消息接收是否成功的確認(rèn)(默認(rèn)值);
1代表:當(dāng)Leader副本接收成功后,返回接收成功確認(rèn)信息;
-1代表:當(dāng)Leader和Follower副本都接收成功后,返回接收成功確認(rèn)信息;
- 網(wǎng)絡(luò)異常
acks設(shè)置為0時(shí),不和Kafka集群進(jìn)行消息接受確認(rèn),當(dāng)網(wǎng)絡(luò)發(fā)生異常等情況時(shí),存在消息丟失的可能; - 客戶端異常
異步發(fā)送時(shí),消息并沒(méi)有直接發(fā)送至Kafka集群,而是在Client端按一定規(guī)則緩存并批量發(fā)送。在這期間,如果客戶端發(fā)生死機(jī)等情況,都會(huì)導(dǎo)致消息的丟失; - 緩沖區(qū)滿了
異步發(fā)送時(shí),Client端緩存的消息超出了緩沖池的大小,也存在消息丟失的可能; - Leader副本異常
acks設(shè)置為1時(shí),Leader副本接收成功,Kafka集群就返回成功確認(rèn)信息,而Follower副本可能還在同步。這時(shí)Leader副本突然出現(xiàn)異常,新Leader副本(原Follower副本)未能和其保持一致,就會(huì)出現(xiàn)消息丟失的情況;
以上就是消息丟失的幾種情況,在日常應(yīng)用中,我們需要結(jié)合自身的應(yīng)用場(chǎng)景來(lái)選擇不同的配置。
想要更高的吞吐量就設(shè)置:異步、ack=0;想要不丟失消息數(shù)據(jù)就選:同步、ack=-1策略
64,kafka的leader副本選舉?
答:如果某個(gè)分區(qū)patition的Leader掛了,那么其它跟隨者將會(huì)進(jìn)行選舉產(chǎn)生一個(gè)新的leader,之后所有的讀寫(xiě)就會(huì)轉(zhuǎn)移到這個(gè)新的Leader上,在kafka中,其不是采用常見(jiàn)的多數(shù)選舉的方式進(jìn)行副本的Leader選舉,而是會(huì)在Zookeeper上針對(duì)每個(gè)Topic維護(hù)一個(gè)稱為ISR(in-sync replica,已同步的副本)的集合,顯然還有一些副本沒(méi)有來(lái)得及同步。只有這個(gè)ISR列表里面的才有資格成為leader(先使用ISR里面的第一個(gè),如果不行依次類推,因?yàn)镮SR里面的是同步副本,消息是最完整且各個(gè)節(jié)點(diǎn)都是一樣的)。
??通過(guò)ISR,kafka需要的冗余度較低,可以容忍的失敗數(shù)比較高。假設(shè)某個(gè)topic有f+1個(gè)副本,kafka可以容忍f個(gè)不可用,當(dāng)然,如果全部ISR里面的副本都不可用,也可以選擇其他可用的副本,只是存在數(shù)據(jù)的不一致。
65,kafka消息的檢索?
答:其實(shí)很簡(jiǎn)單主要是用二分查找算法,比如我們要查找一條offest=10000的文件,kafka首先會(huì)在對(duì)應(yīng)分區(qū)下的log文件里采用二分查看定位到某個(gè)記錄該offest
=10000這條消息的log,然后從相應(yīng)的index文件定位其偏移量,然后拿著偏移量到log里面直接獲取。這樣就完成了一個(gè)消息的檢索過(guò)程。
66,RabbitMQ 集群方式?
答:
1)普通集群:
以兩個(gè)節(jié)點(diǎn)(rabbit01、rabbit02)為例來(lái)進(jìn)行說(shuō)明。
????rabbit01和rabbit02兩個(gè)節(jié)點(diǎn)僅有相同的元數(shù)據(jù),即隊(duì)列的結(jié)構(gòu),但消息實(shí)體只存在于其中一個(gè)節(jié)點(diǎn)rabbit01(或者rabbit02)中。
????當(dāng)消息進(jìn)入rabbit01節(jié)點(diǎn)的Queue后,consumer從rabbit02節(jié)點(diǎn)消費(fèi)時(shí),RabbitMQ會(huì)臨時(shí)在rabbit01、rabbit02間進(jìn)行消息傳輸,把A中的消息實(shí)體取出并經(jīng)過(guò)B發(fā)送給consumer。所以consumer應(yīng)盡量連接每一個(gè)節(jié)點(diǎn),從中取消息。即對(duì)于同一個(gè)邏輯隊(duì)列,要在多個(gè)節(jié)點(diǎn)建立物理Queue。否則無(wú)論consumer連rabbit01或rabbit02,出口總在rabbit01,會(huì)產(chǎn)生瓶頸。當(dāng)rabbit01節(jié)點(diǎn)故障后,rabbit02節(jié)點(diǎn)無(wú)法取到rabbit01節(jié)點(diǎn)中還未消費(fèi)的消息實(shí)體。如果做了消息持久化,那么得等rabbit01節(jié)點(diǎn)恢復(fù),然后才可被消費(fèi);如果沒(méi)有持久化的話,就會(huì)產(chǎn)生消息丟失的現(xiàn)象。
2)鏡像集群:
?在普通集群的基礎(chǔ)上,把需要的隊(duì)列做成鏡像隊(duì)列,消息實(shí)體會(huì)主動(dòng)在鏡像節(jié)點(diǎn)間同步,而不是在客戶端取數(shù)據(jù)時(shí)臨時(shí)拉取,也就是說(shuō)多少節(jié)點(diǎn)消息就會(huì)備份多少份。該模式帶來(lái)的副作用也很明顯,除了降低系統(tǒng)性能外,如果鏡像隊(duì)列數(shù)量過(guò)多,加之大量的消息進(jìn)入,集群內(nèi)部的網(wǎng)絡(luò)帶寬將會(huì)被這種同步通訊大大消耗掉。所以在對(duì)可靠性要求較高的場(chǎng)合中適用
? ? 由于鏡像隊(duì)列之間消息自動(dòng)同步,且內(nèi)部有選舉master機(jī)制,即使master節(jié)點(diǎn)宕機(jī)也不會(huì)影響整個(gè)集群的使用,達(dá)到去中心化的目的,從而有效的防止消息丟失及服務(wù)不可用等問(wèn)題
67,ElasticSearch如何解決深度分頁(yè)的問(wèn)題?
答:使用scroll(有狀態(tài))和search after(無(wú)狀態(tài))的游標(biāo)方式。
68,java代碼優(yōu)化(一)
答:盡量指定類、方法的final修飾符。
盡量重用對(duì)象。
盡可能使用局部變量。
及時(shí)關(guān)閉流。
盡量減少對(duì)變量的重復(fù)計(jì)算。
盡量采用懶加載的策略,即在需要的時(shí)候才創(chuàng)建。
慎用異常。
不要在循環(huán)中使用try...catch...,應(yīng)該把其放在最外層。
如果能估計(jì)到待添加的內(nèi)容長(zhǎng)度,為底層以數(shù)組方式實(shí)現(xiàn)的集合、工具類指定初始長(zhǎng)度。
當(dāng)復(fù)制大量數(shù)據(jù)時(shí),使用System.arraycopy()命令。
乘法和除法使用移位操作。
循環(huán)內(nèi)不要不斷創(chuàng)建對(duì)象引用。
基于效率和類型檢查的考慮,應(yīng)該盡可能使用array,無(wú)法確定數(shù)組大小時(shí)才使用ArrayList。
盡量使用HashMap、ArrayList、StringBuilder,除非線程安全需要,否則不推薦使用Hashtable、Vector、StringBuffer,后三者由于使用同步機(jī)制而導(dǎo)致了性能開(kāi)銷。
不要將數(shù)組聲明為public static final。
69,java代碼優(yōu)化(二)
答:盡量在合適的場(chǎng)合使用單例。
盡量避免隨意使用靜態(tài)變量。
及時(shí)清除不再需要的會(huì)話。
實(shí)現(xiàn)RandomAccess接口的集合比如ArrayList,應(yīng)當(dāng)使用最普通的for循環(huán)而不是foreach循環(huán)來(lái)遍歷
使用同步代碼塊替代同步方法。
將常量聲明為static final,并以大寫(xiě)命名。
不要?jiǎng)?chuàng)建一些不使用的對(duì)象,不要導(dǎo)入一些不使用的類。
程序運(yùn)行過(guò)程中避免使用反射。
使用數(shù)據(jù)庫(kù)連接池和線程池。
使用帶緩沖的輸入輸出流進(jìn)行IO操作。
順序插入和隨機(jī)訪問(wèn)比較多的場(chǎng)景使用ArrayList,元素刪除和中間插入比較多的場(chǎng)景使用LinkedList。
不要讓public方法中有太多的形參。
字符串變量和字符串常量equals的時(shí)候?qū)⒆址A繉?xiě)在前面。
請(qǐng)知道,在java中if (i == 1)和if (1 == i)是沒(méi)有區(qū)別的,但從閱讀習(xí)慣上講,建議使用前者。
不要對(duì)數(shù)組使用toString()方法。
不要對(duì)超出范圍的基本數(shù)據(jù)類型做向下強(qiáng)制轉(zhuǎn)型。
70,java代碼優(yōu)化(三)
答:公用的集合類中不使用的數(shù)據(jù)一定要及時(shí)remove掉。
把一個(gè)基本數(shù)據(jù)類型轉(zhuǎn)為字符串,基本數(shù)據(jù)類型.toString()是最快的方式、String.valueOf(數(shù)據(jù))次之、數(shù)據(jù)+""最慢
使用最有效率的方式去遍歷Map。
對(duì)資源的close()建議分開(kāi)操作。
對(duì)于ThreadLocal使用前或者使用后一定要先remove。
切記以常量定義的方式替代魔鬼數(shù)字,魔鬼數(shù)字的存在將極大地降低代碼可讀性,字符串常量是否使用常量定義可以視情況而定。
long或者Long初始賦值時(shí),使用大寫(xiě)的L而不是小寫(xiě)的l,因?yàn)樽帜竘極易與數(shù)字1混淆,這個(gè)點(diǎn)非常細(xì)節(jié),值得注意。
所有重寫(xiě)的方法必須保留@Override注解。
推薦使用JDK7中新引入的Objects工具類來(lái)進(jìn)行對(duì)象的equals比較,直接a.equals(b),有空指針異常的風(fēng)險(xiǎn)。
循環(huán)體內(nèi)不要使用"+"進(jìn)行字符串拼接,而直接使用StringBuilder不斷append。
不捕獲Java類庫(kù)中定義的繼承自RuntimeException的運(yùn)行時(shí)異常類。
避免Random實(shí)例被多線程使用,雖然共享該實(shí)例是線程安全的,但會(huì)因競(jìng)爭(zhēng)同一seed導(dǎo)致的性能下降,JDK7之后,可以使用ThreadLocalRandom來(lái)獲取隨機(jī)數(shù)。
靜態(tài)類、單例類、工廠類將它們的構(gòu)造函數(shù)置為private。
71,單點(diǎn)登錄原理與簡(jiǎn)單實(shí)現(xiàn)?
答:相比于單系統(tǒng)登錄,sso需要一個(gè)獨(dú)立的認(rèn)證中心,只有認(rèn)證中心能接受用戶的用戶名密碼等安全信息,其他系統(tǒng)不提供登錄入口,只接受認(rèn)證中心的間接授權(quán)。間接授權(quán)通過(guò)令牌實(shí)現(xiàn),sso認(rèn)證中心驗(yàn)證用戶的用戶名密碼沒(méi)問(wèn)題,創(chuàng)建授權(quán)令牌,在接下來(lái)的跳轉(zhuǎn)過(guò)程中,授權(quán)令牌作為參數(shù)發(fā)送給各個(gè)子系統(tǒng),子系統(tǒng)拿到令牌,即得到了授權(quán),可以借此創(chuàng)建局部會(huì)話,局部會(huì)話登錄方式與單系統(tǒng)的登錄方式相同。這個(gè)過(guò)程,也就是單點(diǎn)登錄的原理,用下圖說(shuō)明
單點(diǎn)登錄自然也要單點(diǎn)注銷,在一個(gè)子系統(tǒng)中注銷,所有子系統(tǒng)的會(huì)話都將被銷毀,用下面的圖來(lái)說(shuō)明
72,MQ做數(shù)據(jù)同步也會(huì)造成不一致,又需要引入監(jiān)控,實(shí)時(shí)計(jì)算2個(gè)集群的數(shù)據(jù)同步,做一致性同步。大部分來(lái)說(shuō),同步es和solr不要在代碼中去同步,同步失敗無(wú)法保證事務(wù),而且業(yè)務(wù)耦合。可以使用Databug和cancel等工具去做代碼解耦,MQ支持重試,存儲(chǔ)失敗后拋出異常下次再處理。數(shù)據(jù)做異構(gòu),對(duì)外服務(wù)時(shí)任意拼裝,MYSQL在半同步復(fù)制上做了一些優(yōu)化,保證了一致性,引入了諸如paxos等主流算法保證強(qiáng)一致性問(wèn)題。
當(dāng)DB(監(jiān)聽(tīng)從庫(kù)),binlog有變化,cancel監(jiān)聽(tīng)到時(shí)候解析過(guò)濾發(fā)送MQ(表名字,主鍵等)到變化的實(shí)時(shí)從庫(kù)中查詢數(shù)據(jù)同步到ES聚合表,MQ可以重試,系統(tǒng)解耦。事務(wù)log挖掘縣城會(huì)對(duì)DB的事務(wù)log監(jiān)聽(tīng),并把這些事件發(fā)布到消息代理。
73,分布式服務(wù)調(diào)用可以實(shí)現(xiàn)跟蹤系統(tǒng),可以在業(yè)務(wù)日志中添加調(diào)用鏈ID,各個(gè)環(huán)節(jié)RPC均添加調(diào)用時(shí)延,QPS等。
非業(yè)務(wù)組件應(yīng)該少加入業(yè)務(wù)代碼,服務(wù)調(diào)用采用買點(diǎn),也會(huì)采用配置采樣率方式,買點(diǎn)即當(dāng)前節(jié)點(diǎn)的上下文信息,包含TraceId,RPCId,開(kāi)始結(jié)束時(shí)間,類型,協(xié)議,調(diào)用方IP,端口,服務(wù)名等,以及其他異常信息,報(bào)文等擴(kuò)展,日志采用離線+實(shí)時(shí)的如flume結(jié)合kafka等,應(yīng)按照TraceId匯總?cè)罩竞蟀碦PCId順序整理。
74,Sentinel 工作原理?
答:(1)每個(gè) Sentinel 以每秒鐘一次的頻率向它所知的 Master,Slave 以及其他 Sentinel 實(shí)例發(fā)送一個(gè) PING 命令;
(2)如果一個(gè)實(shí)例(instance)距離最后一次有效回復(fù) PING 命令的時(shí)間超過(guò) down-after-milliseconds 選項(xiàng)所指定的值, 則這個(gè)實(shí)例會(huì)被 Sentinel 標(biāo)記為主觀下線;
(3)如果一個(gè) Master 被標(biāo)記為主觀下線,則正在監(jiān)視這個(gè) Master 的所有 Sentinel 要以每秒一次的頻率確認(rèn) Master 的確進(jìn)入了主觀下線狀態(tài);
(4)當(dāng)有足夠數(shù)量的 Sentinel(大于等于配置文件指定的值)在指定的時(shí)間范圍內(nèi)確認(rèn) Master 的確進(jìn)入了主觀下線狀態(tài),則 Master 會(huì)被標(biāo)記為客觀下線;
(5)在一般情況下, 每個(gè) Sentinel 會(huì)以每 10 秒一次的頻率向它已知的所有 Master,Slave 發(fā)送 INFO 命令;
當(dāng) Master 被 Sentinel 標(biāo)記為客觀下線時(shí),Sentinel 向下線的 Master 的所有 Slave 發(fā)送 INFO 命令的頻率會(huì)從 10 秒一次改為每秒一次;
(6)若沒(méi)有足夠數(shù)量的 Sentinel 同意 Master 已經(jīng)下線, Master 的客觀下線狀態(tài)就會(huì)被移除;
(7)若 Master 重新向 Sentinel 的 PING 命令返回有效回復(fù), Master 的主觀下線狀態(tài)就會(huì)被移除。
監(jiān)控( Monitoring ): Redis Sentinel 實(shí)時(shí)監(jiān)控主服務(wù)器和從服務(wù)器運(yùn)行狀態(tài);
自動(dòng)故障轉(zhuǎn)移:如果一個(gè) master 不正常運(yùn)行了,哨兵可以啟動(dòng)一個(gè)故障轉(zhuǎn)移進(jìn)程,將一個(gè) slave 升級(jí)成為 master,其他的 slave 被重新配置使用新的 master,并且應(yīng)用程序使用 Redis 服務(wù)端通知的新地址;
75,高性能統(tǒng)計(jì)UV的方式?
(1)使用redis的set集合
(2)使用redis的bitmap(注意內(nèi)存消耗)
76,Hbase二級(jí)索引,索引海量數(shù)據(jù)實(shí)現(xiàn)方案?
答:
(1) 方案1:使用開(kāi)源的hbase-indexer,是借助于hbase的WAL實(shí)現(xiàn),不會(huì)影響hbase性能
? ?https://blog.csdn.net/xiahoujie_90/article/details/53400044
(2)? 方案2:基于ES自己實(shí)現(xiàn),利用habse的協(xié)處理器實(shí)現(xiàn),會(huì)影響hbase性能
關(guān)鍵注意點(diǎn):因?yàn)閿?shù)據(jù)是存在Hbase中,ES充當(dāng)?shù)氖撬饕巧?#xff0c;所以在創(chuàng)建ES的mapping時(shí),
應(yīng)指定_source為enabled:false。關(guān)閉存儲(chǔ)原始文檔。
https://wenku.baidu.com/view/422722fdd0f34693daef5ef7ba0d4a7303766c71.html
77,Elasticsearch分片使用優(yōu)化?
答:(1)拆分集群
對(duì)于存在明顯分界線的業(yè)務(wù),可以按照業(yè)務(wù)、地域使用不同集群,這種拆分集群的思路是非常靠譜的。對(duì)于我們的場(chǎng)景,已經(jīng)按照地域拆分了集群,且同一地域的子業(yè)務(wù)間分界線不明顯,拆分過(guò)多的集群維護(hù)成本較高。
(2)調(diào)整滾動(dòng)周期
根據(jù)保留時(shí)長(zhǎng)調(diào)整index滾動(dòng)周期是最簡(jiǎn)單有效的思路。例如保留3天的數(shù)據(jù)按天滾動(dòng),保留31天的數(shù)據(jù)按周滾動(dòng),保留一年的數(shù)據(jù)按月滾動(dòng)。合理的滾動(dòng)周期,可以在存儲(chǔ)成本增加不大的情況下,大幅降低分片數(shù)量。
對(duì)于我們的場(chǎng)景,大部分?jǐn)?shù)據(jù)保留31天,在按周滾動(dòng)的情況下,集群的總分片數(shù)可以下降到6.5w~個(gè)。
(3)合理設(shè)置分片數(shù)和副本數(shù)
除個(gè)別子業(yè)務(wù)壓力較高外,大部分業(yè)務(wù)壓力較小,合理設(shè)置單Index的分片數(shù)效果也不錯(cuò)。我們的經(jīng)驗(yàn)是單個(gè)分片的大小在10GB~30GB之間比較合適,對(duì)于壓力非常小的業(yè)務(wù)可以直接分配1個(gè)分片。其他用戶可結(jié)合具體場(chǎng)景考慮,同時(shí)注意單分片的記錄條數(shù)不要超過(guò)上限2,147,483,519。
在平衡我們的業(yè)務(wù)場(chǎng)景對(duì)數(shù)據(jù)可靠性的要求 及 不同副本數(shù)對(duì)存儲(chǔ)成本的開(kāi)銷 兩個(gè)因素之后,我們選擇使用一主一從的副本策略。
目前我們集群?jiǎn)蜪ndex的平均分配數(shù)為3,集群的總分片數(shù)下降到3w~個(gè)。
(4)分片分配流程優(yōu)化
默認(rèn)情況下,ES在分配分片時(shí)會(huì)考慮分片relocation對(duì)磁盤空間的影響。在分片數(shù)較少時(shí),這個(gè)優(yōu)化處理的副作用不明顯。但隨著單機(jī)分片數(shù)量的上升,這個(gè)優(yōu)化處理涉及的多層循環(huán)嵌套過(guò)程耗時(shí)愈發(fā)明顯。可通過(guò)cluster.routing.allocation.disk.include_relocations: false關(guān)閉此功能,這對(duì)磁盤均衡程度影響不明顯。
(5)預(yù)創(chuàng)建Index
對(duì)于單集群3w分片的場(chǎng)景,集中在每周某天0點(diǎn)創(chuàng)建Index,對(duì)集群的壓力還是較大,且存儲(chǔ)空間存在波動(dòng)。考慮到集群的持續(xù)擴(kuò)展能力和可靠性,我們采用預(yù)創(chuàng)建方式提前創(chuàng)建分片,并把按Index的創(chuàng)建時(shí)間均勻打散到每周的每一天。
(6)持續(xù)調(diào)整分片數(shù)
對(duì)于集群分片的調(diào)整,通常不是一蹴而就的。隨著業(yè)務(wù)的發(fā)展,不斷新增的子業(yè)務(wù) 或 原有子業(yè)務(wù)規(guī)模發(fā)生突變,都需要持續(xù)調(diào)整分片數(shù)量。
默認(rèn)情況下,新增的子業(yè)務(wù)會(huì)有默認(rèn)的分片數(shù)量,如果不足,會(huì)在測(cè)試階段及上線初期及時(shí)發(fā)現(xiàn)。隨著業(yè)務(wù)發(fā)展,系統(tǒng)會(huì)考慮Index近期的數(shù)據(jù)量、寫(xiě)入速度、集群規(guī)模等因素,動(dòng)態(tài)調(diào)整分片數(shù)量。
78,如何編寫(xiě)高質(zhì)量代碼151建議?
答:
一、Java開(kāi)發(fā)中通用的方法和準(zhǔn)則
不要在常量和變量中出現(xiàn)易混淆的字母;
莫讓常量蛻變成變量;
三元操作符的類型務(wù)必一致;
避免帶有變長(zhǎng)參數(shù)的方法重載;
別讓null值和空值威脅到變長(zhǎng)方法;
覆寫(xiě)變長(zhǎng)方法也要循規(guī)蹈矩;
警惕字增的陷阱;
不要讓舊語(yǔ)法困擾你;
少用靜態(tài)導(dǎo)入;
不要在本類中覆蓋靜態(tài)導(dǎo)入的變量和方法;
養(yǎng)成良好習(xí)慣,顯示聲明UID;
避免用序列化類在構(gòu)造函數(shù)中為不變量賦值;
避免為final變量復(fù)雜賦值;
使用序列化類的私有方法巧妙解決部分屬性持久化問(wèn)題;
break萬(wàn)萬(wàn)不可忘;
易變業(yè)務(wù)使用腳本語(yǔ)言編寫(xiě);
慎用動(dòng)態(tài)編譯;
避免instantceof非預(yù)期結(jié)果;
斷言對(duì)決不是雞肋;
不要只替換一個(gè)類;
二、基本類型
使用偶判斷,不用奇判斷;
用整數(shù)類型處理貨幣;
不要讓類型默默轉(zhuǎn)換;
邊界,邊界,還是邊界;
不要讓四舍五入虧了一方;
提防包裝類型的null值;
謹(jǐn)慎包裝類型的大小比較;
優(yōu)先使用整型池;
優(yōu)先選擇基本類型;
不要隨便設(shè)置隨機(jī)種子;
三、類、對(duì)象及方法
在接口中不要存在實(shí)現(xiàn)代碼;
靜態(tài)變量一定要先聲明后賦值;
不要覆寫(xiě)靜態(tài)方法;
構(gòu)造函數(shù)盡量簡(jiǎn)化;
避免在構(gòu)造函數(shù)中初始化其他類;
使用構(gòu)造代碼塊精煉程序;
使用靜態(tài)內(nèi)部類提供封裝性;
使用匿名類的構(gòu)造函數(shù);
匿名類的構(gòu)造函數(shù)很特殊;
讓多重繼承成為現(xiàn)實(shí);
讓工具類不可實(shí)例化;
避免對(duì)象的淺拷貝;
推薦使用序列化實(shí)現(xiàn)對(duì)象的拷貝;
覆寫(xiě)equals方法時(shí)不要識(shí)別不出自己;
equals應(yīng)該考慮null值情景;
在equals中使用getClass進(jìn)行類型判斷;
覆寫(xiě)equals方法必須覆寫(xiě)hashCode方法;
推薦覆寫(xiě)toString方法;
使用package-info類為包服務(wù);
不要主動(dòng)進(jìn)行垃圾回收;
四、字符串
推薦使用String直接量賦值;
注意方法中傳遞的參數(shù)要求;
正確使用String、StringBuffer、StringBuilder;
注意字符串的位置;
自由選擇字符串拼接方法;
推薦在復(fù)雜字符串操作中使用正則表達(dá)式;
強(qiáng)烈建議使用UTF編碼;
對(duì)字符串排序持一種寬容的心態(tài);
五、數(shù)組和集合
性能考慮,數(shù)組是首選;
若有必要,使用變長(zhǎng)數(shù)組;
警惕數(shù)組的淺拷貝;
在明確的場(chǎng)景下,為集合指定初始容量;
多種最值方法,適時(shí)選擇;
避開(kāi)基本類型數(shù)組轉(zhuǎn)換列表陷阱;
asList方法產(chǎn)生的List對(duì)象不可更改;
不同的列表選擇不同的遍歷方法;
頻繁插入和刪除時(shí)使用LinkedList;
列表相等只需關(guān)心元素?cái)?shù)據(jù);
推薦使用subList處理局部列表;
生成子表后不要再操作原列表;
使用Comparator進(jìn)行排序;
不推薦使用binarySearch對(duì)列表進(jìn)行檢索;
集合中的元素必須做到compareTo和equals同步;
集合運(yùn)算時(shí)使用更優(yōu)雅的方式;
使用shuffle大亂列表;
減少HashMap中元素的數(shù)量;
集合中的哈希碼不要重復(fù);
多線程使用Vector或者HashTable;
非穩(wěn)定排序推薦使用List;
六、枚舉和注解
推薦使用枚舉定義常量;
使用構(gòu)造函數(shù)協(xié)助描述枚舉項(xiàng);
小心switch帶來(lái)的空值異常;
在switch的default代碼塊中增加AssertionError錯(cuò)誤;
使用valueOf前必須進(jìn)行校驗(yàn);
用枚舉實(shí)現(xiàn)工廠方法模式更簡(jiǎn)潔;
枚舉項(xiàng)的數(shù)量限制在64個(gè)以內(nèi);
小心注解繼承;
枚舉和注解結(jié)合使用威力更大;
注意@Override不同版本的區(qū)別;
七、枚舉和注解
Java的泛型是類型擦除的;
不能初始化泛型參數(shù)和數(shù)組;
強(qiáng)制聲明泛型的實(shí)際類型;
不同的場(chǎng)景使用不同的泛型通配符;
警惕泛型是不能協(xié)變和逆變的;
建議采用的順序是List<T>、List<?>、List<Object>;
嚴(yán)格限定泛型類型采用多重界限;
數(shù)組的真實(shí)類型必須是泛型類型的子類型;
注意Class類的特殊性;
適時(shí)選擇getDeclaredXXX和getXXX;
反射訪問(wèn)屬性或方法時(shí)將Accessible設(shè)置為true;
使用forName動(dòng)態(tài)加載類文件;
動(dòng)態(tài)加載不合適數(shù)組;
動(dòng)態(tài)代理可以使代理模式更加靈活;
反射讓模板方法模式更強(qiáng)大;
不需要太多關(guān)注反射效率;
八、異常
提倡異常封裝;
采用異常鏈傳遞異常;
受檢異常盡可能轉(zhuǎn)化為非受檢異常;
不要在finally塊中處理返回值;
使用Throwable獲取棧信息;
異常只為異常服務(wù);
多使用異常,把性能問(wèn)題放一邊;
九、多線程和并發(fā)
不推薦覆寫(xiě)start方法;
啟動(dòng)線程前stop方法是不可靠的;
不使用stop方法停止線程;
線程優(yōu)先級(jí)只使用三個(gè)等級(jí);
使用線程異常處理器提升系統(tǒng)可靠性;
volatile不能保證數(shù)據(jù)同步;
異步運(yùn)算多考慮使用Callable接口;
優(yōu)先選擇線程池;
適時(shí)選擇不同的線程池來(lái)實(shí)現(xiàn);
Lock與synchronized是不一樣的;
預(yù)防線程死鎖;
適當(dāng)設(shè)置阻塞隊(duì)列長(zhǎng)度;
使用CountDownLatch協(xié)調(diào)子線程;
CyclicBarrier讓多線程齊步走;
?
?
?
與50位技術(shù)專家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的阿里java高级工程师面试100题(建议收藏)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java代码实现负载均衡六种算法(强烈建
- 下一篇: ThreadLocal为什么会内存泄漏(