- MVCC,binlog,redolog,undolog都是什么,起什么作用?
(1)undolog 也就是我們常說的回滾日志檔案 主要用于事務中執行失敗,進行回滾,以及MVCC中對于資料歷史版本的查看,由引擎層的InnoDB引擎實作,是邏輯日志,記錄資料修改被修改前的值,比如"把id='B' 修改為id = 'B2' ,那么undo日志就會用來存放id ='B'的記錄”,當一條資料需要更新前,會先把修改前的記錄存盤在undolog中,如果這個修改出現例外,則會使用undo日志來實作回滾操作,保證事務的一致性,當事務提交之后,undo log并不能立馬被洗掉,而是會被放到待清理鏈表中,待判斷沒有事物用到該版本的資訊時才可以清理相應undolog,它保存了事務發生之前的資料的一個版本,用于回滾,同時可以提供多版本并發控制下的讀(MVCC),也即非鎖定讀, (2)redoLog 是重做日志檔案是記錄資料修改之后的值,用于持久化到磁盤中,redo log包括兩部分:一是記憶體中的日志緩沖(redo log buffer),該部分日志是易失性的;二是磁盤上的重做日志檔案(redo log file),該部分日志是持久的,由引擎層的InnoDB引擎實作,是物理日志,記錄的是物理資料頁修改的資訊,比如“某個資料頁上內容發生了哪些改動”,當一條資料需要更新時,InnoDB會先將資料更新,然后記錄redoLog 在記憶體中,然后找個時間將redoLog的操作執行到磁盤上的檔案上,不管是否提交成功我都記錄,你要是回滾了,那我連回滾的修改也記錄,它確保了事務的持久性, (3)MVCC多版本并發控制是MySQL中基于樂觀鎖理論實作隔離級別的方式,用于讀已提交和可重復讀取隔離級別的實作,在MySQL中,會在表中每一條資料后面添加兩個欄位:最近修改該行資料的事務ID,指向該行(undolog表中)回滾段的指標,Read View判斷行的可見性,創建一個新事務時,copy一份當前系統中的活躍事務串列,意思是,當前不應該被本事務看到的其他事務id串列, (4)binlog由Mysql的Server層實作,是邏輯日志,記錄的是sql陳述句的原始邏輯,比如"把id='B' 修改為id = ‘B2’,binlog會寫入指定大小的物理檔案中,是追加寫入的,當前檔案寫滿則會創建新的檔案寫入, 產生:事務提交的時候,一次性將事務中的sql陳述句,按照一定的格式記錄到binlog中,用于復制和恢復在主從復制中,從庫利用主庫上的binlog進行重播(執行日志中記錄的修改邏輯),實作主從同步,業務資料不一致或者錯了,用binlog恢復
- Kafka是如何實作高吞吐率的?
1)順序讀寫:kafka的訊息是不斷追加到檔案中的,這個特性使kafka可以(2)充分利用磁盤的順序讀寫性能 (3)零拷貝:跳過“用戶緩沖區”的拷貝,建立一個磁盤空間和記憶體的直接映射,資料不再復制到“用戶態緩沖區” (4)檔案分段:kafka的佇列topic被分為了多個區partition,每個partition又分為多個段segment,所以一個佇列中的訊息實際上是保存在N多個片段檔案中 (5)批量發送:Kafka允許進行批量發送訊息,先將訊息快取在記憶體中,然后一次請求批量發送出去 (6)資料壓縮:Kafka還支持對訊息集合進行壓縮,Producer可以通過GZIP或Snappy格式對訊息集合進行壓縮
- Http請求的完全程序
(1)瀏覽器根據域名決議IP地址(DNS),并查DNS快取 (2)瀏覽器與WEB服務器建立一個TCP連接 (3)瀏覽器給WEB服務器發送一個HTTP請求(GET/POST):一個HTTP請求報文由請求行(request line)、請求頭部(headers)、空行(blank line)和請求資料(request body)4個部分組成, (4)服務端回應HTTP回應報文,報文由狀態行(status line)、相應頭部(headers)、空行(blank line)和回應資料(response body)4個部分組成, (5)瀏覽器決議渲染
- Spring的@Transactional如何實作的?
(1)組態檔開啟注解驅動,在相關的類和方法上通過注解@Transactional標識, (2)spring 在啟動的時候會去決議生成相關的bean,這時候會查看擁有相關注解的類和方法,并且為這些類和方法生成代理,并根據@Transaction的相關引數進行相關配置注入,這樣就在代理中為我們把相關的事務處理掉了(開啟正常提交事務,例外回滾事務), (3)真正的資料庫層的事務提交和回滾是通過binlog或者redo log實作的
- 為什么要使用執行緒池?
(1)降低資源消耗,通過重復利用已創建的執行緒降低執行緒創建和銷毀造成的消耗, (2)提高回應速度,當任務到達時,任務可以不需要的等到執行緒創建就能立即執行, (3)提高執行緒的可管理性,執行緒是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用執行緒池可以進行統一的分配,調優和監控,
- rpc框架實作原理?
主要有以下幾個步驟: (1)建立通信: 首先要解決通訊的問題:即A機器想要呼叫B機器,首先得建立起通信連接,主要是通過在客戶端和服務器之間建立TCP連接,遠程程序呼叫的所有相關的資料都在這個連接里面進行傳輸交換, 通常這個連接可以是按需連接(需要呼叫的時候就先建立連接,呼叫結束后就立馬斷掉),也可以是長連接(客戶端和服務器建立起連接之后保持長期持有,不管此時有無資料包的發送,可以配合心跳檢測機制定期檢測建立的連接是否存活有效),多個遠程程序呼叫共享同一個連接, (2)服務尋址: 解決尋址的問題:即A機器上的應用A要呼叫B機器上的應用B,那么此時對于A來說如何告知底層的RPC框架所要呼叫的服務具體在哪里呢? 通常情況下我們需要提供B機器(主機名或IP地址)以及特定的埠,然后指定呼叫的方法或者函式的名稱以及入參出參等資訊,這樣才能完成服務的一個呼叫,比如基于Web服務協議堆疊的RPC,就需要提供一個endpoint URI,或者是從UDDI服務上進行查找,如果是RMI呼叫的話,還需要一個RMI Registry來注冊服務的地址, (3)網路傳輸: ①序列化 當A機器上的應用發起一個RPC呼叫時,呼叫方法和其入參等資訊需要通過底層的網路協議如TCP傳輸到B機器,由于網路協議是基于二進制的,所有我們傳輸的引數資料都需要先進行序列化(Serialize)或者編組(marshal)成二進制的形式才能在網路中進行傳輸,然后通過尋址操作和網路傳輸將序列化或者編組之后的二進制資料發送給B機器, ②反序列化 當B機器接收到A機器的應用發來的請求之后,又需要對接收到的引數等資訊進行反序列化操作(序列化的逆操作),即將二進制資訊恢復為記憶體中的表達方式,然后再找到對應的方法(尋址的一部分)進行本地呼叫(一般是通過生成代理Proxy去呼叫, 通常會有JDK動態代理、CGLIB動態代理、Javassist生成位元組碼技術等),之后得到呼叫的回傳值, (4)服務呼叫: B機器進行本地呼叫(通過代理Proxy)之后得到了回傳值,此時還需要再把回傳值發送回A機器,同樣也需要經過序列化操作,然后再經過網路傳輸將二進制資料發送回A機器,而當A機器接收到這些回傳值之后,則再次進行反序列化操作,恢復為記憶體中的表達方式,最后再交給A機器上的應用進行相關處理(一般是業務邏輯處理操作),
- redis熱key怎么解決?
(1)利用二級快取: 比如利用ehcache,或者一個HashMap都可以,在你發現熱key以后,把熱key加載到系統的JVM中,針對這種熱key請求,會直接從jvm中取,而不會走到redis層,假設此時有十萬個針對同一個key的請求過來,如果沒有本地快取,這十萬個請求就直接懟到同一臺redis上了, 現在假設,你的應用層有50臺機器,OK,你也有jvm快取了,這十萬個請求平均分散開來,每個機器有2000個請求,會從JVM中取到value值,然后回傳資料,避免了十萬個請求懟到同一臺redis上的情形, (2)備份熱key: 這個方案也很簡單,不要讓key走到同一臺redis上不就行了,我們把這個key,在多個redis上都存一份不就好了,接下來,有熱key請求進來的時候,我們就在有備份的redis上隨機選取一臺,進行訪問取值,回傳資料,
- 不同年代GC收集器有哪些?
(1)serial收集器:單執行緒,作業時必須暫停其他作業執行緒,多用于client機器上,使用復制演算法 (2)ParNew收集器:serial收集器的多執行緒版本,server模式下虛擬機首選的新生代收集器,復制演算法 (3)Parallel Scavenge收集器:復制演算法,可控制吞吐量的收集器,吞吐量即有效運行時間, (4)Serial Old收集器:serial的老年代版本,使用整理演算法, (5)Parallel Old收集器:第三種收集器的老年代版本,多執行緒,標記整理 (6)CMS收集器:目標是最短回收停頓時間,標記清除演算法實作,分四個階段: ?初始標記:GC Roots直連的物件做標記 ?并發標記:多執行緒方式GC Roots Tracing ?重新標記:修正第二階段標記的記錄 ?并發清除, 缺點:標記清除演算法的缺點,產生碎片,CPU資源敏感,
- ES腦裂問題分析及優化
(1)腦裂問題可能的成因 ?網路問題:集群間的網路延遲導致一些節點訪問不到master,認為master掛掉了從而選舉出新的master,并對master上的分片和副本標紅,分配新的主分片 ?節點負載:主節點的角色既為master又為data,訪問量較大時可能會導致ES停止回應造成大面積延遲,此時其他節點得不到主節點的回應認為主節點掛掉了,會重新選取主節點, ?記憶體回收:data節點上的ES行程占用的記憶體較大,引發JVM的大規模記憶體回收,造成ES行程失去回應, (2)腦裂問題解決方案: ?減少誤判:discovery.zen.ping_timeout節點狀態的回應時間,默認為3s,可以適當調大,如果master在該回應時間的范圍內沒有做出回應應答,判斷該節點已經掛掉了,調大引數(如6s,discovery.zen.ping_timeout:6),可適當減少誤判, ?選舉觸發 discovery.zen.minimum_master_nodes:1 該引數是用于控制選舉行為發生的最小集群主節點數量, 當備選主節點的個數大于等于該引數的值,且備選主節點中有該引數個節點認為主節點掛了,進行選舉,官方建議為(n/2)+1,n為主節點個數(即有資格成為主節點的節點個數) 增大該引數,當該值為2時,我們可以設定master的數量為3,這樣,掛掉一臺,其他兩臺都認為主節點掛掉了,才進行主節點選舉, ?角色分離:即master節點與data節點分離,限制角色
- 講一講類加載的程序
一般來說,我們把 Java 的類加載程序分為三個主要步驟:加載,連接,初始化,具體行為在 Java 虛擬機規范里有非常詳細的定義, (1)首先是加載程序(Loading),它是 Java 將位元組碼資料從不同的資料源讀取到 JVM 中,并映射為 JVM 認可的資料結構(Class 物件),這里的資料源可能是各種各樣的形態,比如 jar 檔案,class 檔案,甚至是網路資料源等;如果輸入資料不是 ClassFile 的結構,則會拋出 ClassFormatError,加載階段是用戶參與的階段,我們可以自定義類加載器,去實作自己的類加載程序, (2)第二階段是連接(Linking),這是核心的步驟,簡單說是把原始的類定義資訊平滑地轉入 JVM 運行的程序中,這里可進一步細分成三個步驟:1,驗證(Verification),這是虛擬機安全的重要保障,JVM 需要核驗位元組資訊是符合 Java 虛擬機規范的,否則就被認為是 VerifyError,這樣就防止了惡意資訊或者不合規資訊危害 JVM 的運行,驗證階段有可能觸發更多 class 的加載,2,準備(Pereparation),創建類或者介面中的靜態變數,并初始化靜態變數的初始值,但這里的“初始化”和下面的顯示初始化階段是有區別的,側重點在于分配所需要的記憶體空間,不會去執行更進一步的 JVM 指令,3,決議(Resolution),在這一步會將常量池中的符號參考(symbolic reference)替換為直接參考,在 Java 虛擬機規范中,詳細介紹了類,介面,方法和欄位等各方面的決議, (3)最后是初始化階段(initialization),這一步真正去執行類初始化的代碼邏輯,包括靜態欄位賦值的動作,以及執行類定義中的靜態初始化塊內的邏輯,編譯器在編譯階段就會把這部分邏輯整理好,父型別的初始化邏輯優先于當前型別的邏輯,再來談談雙親委派模型,簡單說就是當加載器(Class-Loader)試圖加載某個型別的時候,除非父類加載器找不到相應型別,否則盡量將這個任務代理給當前加載器的父加載器去做,使用委派模型的目的是避免重復加載 Java 型別,
- 不可重復讀和幻讀區別
(1)"不可重復讀" 是指在一個事務內,多次讀同一資料,在這個事務還沒有結束時,bai另外一個事務也訪問該同一資料,那么,在第一個事務中的兩次讀資料之間,由于第二個事務的修改,那么第一個事務兩次讀到的的資料可能是不一樣的,這樣就發生了在一個事務內兩次讀到的資料是不一樣的,因此稱為是不可重復讀, (2)幻覺讀是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的資料進行了修改,這種修改涉及到表中的全部資料行,同時,第二個事務也修改這個表中的資料,這種修改是向表中插入一行新資料,那么,以后就會發生操作第一個事務的用戶發現表中還有沒有修改的資料行,就好象發生了幻覺一樣,
- 單例物件會被jvm的gc時回收嗎
(1)jvm卸載類的判定條件如下: ①該類所有的實體都已經被回收,也就是java堆中不存在該類的任何實體, ②加載該類的ClassLoader已經被回收, ③該類對應的java.lang.Class物件沒有任何地方被參考,無法在任何地方通過反射訪問該類的方法, (2)只有上面三個條件都滿足,jvm才會在垃圾收集的時候卸載類,顯然,單例的類不滿足條件一,因此單例類也不會被卸載,也就是說,只要單例類中的靜態參考指向jvm堆中的單例物件,那么單例類和單例物件都不會被垃圾收集,依據根搜索演算法,物件是否會被垃圾收集與未被使用時間長短無關,僅僅在于這個物件是不是不可回收的,
- Get和Post區別
(1)Get是不安全的,因為在傳輸程序,資料被放在請求的URL中;Post的所有操作對用戶來說都是不可見的, (2)Get傳送的資料量較小,這主要是因為受URL長度限制;Post傳送的資料量較大,一般被默認為不受限制, (3)Get限制Form表單的資料集的值必須為ASCII字符;而Post支持整個ISO10646字符集, (4)Get執行效率卻比Post方法好,Get是form提交的默認方法, GET產生一個TCP資料包;POST產生兩個TCP資料包,(非必然,客戶端可靈活決定)
- 死鎖的4個必要條件
(1)互斥條件:一個資源每次只能被一個執行緒使用; (2)請求與保持條件:一個執行緒因請求資源而阻塞時,對已獲得的資源保持不放; (3)不剝奪條件:行程已經獲得的資源,在未使用完之前,不能強行剝奪; (4)回圈等待條件:若干執行緒之間形成一種頭尾相接的回圈等待資源關系
- Redis 的資料結構及使用場景
1)String字串:字串型別是 Redis 最基礎的資料結構,首先鍵都是字串型別,而且 其他幾種資料結構都是在字串型別基礎上構建的,我們常使用的 set key value 命令就是字串,常用在快取、計數、共享Session、限速等, (2)Hash哈希:在Redis中,哈希型別是指鍵值本身又是一個鍵值對 結構,添加命令:hset key field value,哈希可以用來存放用戶資訊,比如實作購物車, (3)List串列(雙向鏈表):串列(list)型別是用來存盤多個有序的字串,可以做簡單的訊息佇列的功能, (4)Set集合:集合(set)型別也是用來保存多個的字串元素,但和串列型別不一 樣的是,集合中不允許有重復元素,并且集合中的元素是無序的,不能通過 索引下標獲取元素,利用 Set 的交集、并集、差集等操作,可以計算共同喜好,全部的喜好,自己獨有的喜好等功能, (5)Sorted Set有序集合(跳表實作):Sorted Set 多了一個權重引數 Score,集合中的元素能夠按 Score 進行排列,可以做排行榜應用,取 TOP N 操作
- ZAB協議
ZAB協議包括兩種基本的模式:崩潰恢復和訊息廣播,當整個 Zookeeper 集群剛剛啟動或者Leader服務器宕機、重啟或者網路故障導致不存在過半的服務器與 Leader 服務器保持正常通信時,所有服務器進入崩潰恢復模式,首先選舉產生新的 Leader 服務器,然后集群中 Follower 服務器開始與新的 Leader 服務器進行資料同步, 當集群中超過半數機器與該 Leader 服務器完成資料同步之后,退出恢復模式進入訊息廣播模式,Leader 服務器開始接收客戶端的事務請求生成事物提案來進行事務請求處理,
- volatile作用
(1)volatile在多處理器開發中保證了共享變數的“ 可見性”,可見性的意思是當一個執行緒修改一個共享變數時,另外一個執行緒能讀到這個修改的值,(共享記憶體,私有記憶體) (2)volatile關鍵字通過“記憶體屏障”來防止指令被重排序,
- 什么是值傳遞和參考傳遞
(1)值傳遞是對基本型變數而言的,傳遞的是該變數的一個副本,改變副本不影響原變數. (2)參考傳遞一般是對于物件型變數而言的,傳遞的是該物件地址的一個副本, 并不是原物件本身 ,
- Java記憶體模型
Java虛擬機規范中將Java運行時資料分為六種: (1)程式計數器:是一個資料結構,用于保存當前正常執行的程式的記憶體地址,Java虛擬機的多執行緒就是通過執行緒輪流切換并分配處理器時間來實作的,為了執行緒切換后能恢復到正確的位置,每條執行緒都需要一個獨立的程式計數器,互不影響,該區域為“執行緒私有”, (2)Java虛擬機堆疊:執行緒私有的,與執行緒生命周期相同,用于存盤區域變數表,操作堆疊,方法回傳值,區域變數表放著基本資料型別,還有物件的參考, (3)本地方法堆疊:跟虛擬機堆疊很像,不過它是為虛擬機使用到的Native方法服務, (4)Java堆:所有執行緒共享的一塊記憶體區域,物件實體幾乎都在這分配記憶體, (5)方法區:各個執行緒共享的區域,儲存虛擬機加載的類資訊,常量,靜態變數,編譯后的代碼, (6)運行時常量池:代表運行時每個class檔案中的常量表,包括幾種常量:編譯時的數字常量、方法或者域的參考,
- string.stringbuilder.stringbuffer的區別,為什么string不可變
(1)區別 ①String是字串常量,而StringBuffer和StringBuilder是字串變數,由String創建的字符內容是不可改變的,而由StringBuffer和StringBuidler創建的字符內容是可以改變的, ②StringBuffer是執行緒安全的,而StringBuilder是非執行緒安全的,StringBuilder是從JDK 5開始,為StringBuffer類補充的一個單執行緒的等價類,我們在使用時應優先考慮使用StringBuilder,因為它支持StringBuffer的所有操作,但是因為它不執行同步,不會有執行緒安全帶來額外的系統消耗,所以速度更快, (2)String為什么不可變: 雖然String、StringBuffer和StringBuilder都是final類,它們生成的物件都是不可變的,而且它們內部也都是靠char陣列實作的,但是不同之處在于,String類中定義的char陣列是final的,而StringBuffer和StringBuilder都是繼承自AbstractStringBuilder類,它們的內部實作都是靠這個父類完成的,而這個父類中定義的char陣列只是一個普通是私有變數,可以用append追加,因為AbstractStringBuilder實作了Appendable介面
- 為什么在重寫equals方法的時候要重寫hashcode的方法?
(1)我們知道判斷的時候先根據hashcode進行的判斷,相同的情況下再根據equals()方法進行判斷,如果只重寫了equals方法,而不重寫hashcode的方法,造成hashcode的值不同,而equals()方法判斷出來的結果為true, (2)在Java中的一些容器中,不允許有兩個完全相同的物件,插入的時候,如果判斷相同則會進行覆寫,這時候如果只重寫了equals()的方法,而不重寫hashcode的方法,Object中hashcode是根據物件的存盤地址轉換而形成的一個哈希值,這時候就有可能因為沒有重寫hashcode方法,造成相同的物件散列到不同的位置而造成物件的不能覆寫的問題,
- 反射的作用是什么?
(1)反射的主要作用是用來擴展系統和動態呼叫程式集, (2)所謂擴展系統就是先把系統寫好,系統里面定義介面,后面開發的人去寫介面的代碼, (3)動態呼叫程式集就是利用反射去呼叫編譯好的dll,當然此時的dll沒有被參考到你所建的工程里面,
- 同步與異步區別?
(1)同步,可以理解為在執行完一個函式或方法之后,一直等待系統回傳值或訊息,這時程式是出于阻塞的,只有接收到回傳的值或訊息后才往下執行其他的命令, (2)異步,執行完函式或方法后,不必阻塞性地等待回傳值或訊息,只需要向系統委托一個異步程序,那么當系統接收到回傳值或訊息時,系統會自動觸發委托的異步程序,從而完成一個完整的流程,
- Java中overload override的區別
(1)Overload是多載的意思,Override是覆寫的意思,也就是重寫, (2)重寫Override表示子類中的方法可以與父類中的某個方法的名稱和引數完全相同,通過子類創建的實體物件呼叫這個方法時,將呼叫子類中定義的方法,這相當于把父類中的方法給覆寫了,這也是多型性的一種表現, (3)多載overload的特點就是與回傳值無關,只看引數串列,所以多載的方法可以改變回傳值型別,所以,如果兩個方法的引數串列完全一樣,是不能通過讓它們的回傳值型別不同來實作多載的, (4)override是覆寫一個方法并且對其重寫,以求達到不同的作用,對我們來說最熟悉的覆寫就是對介面方法的實作,在介面中一般只是對方法進行了宣告,而我們在實作時,就需要實作介面宣告的所有方法,除了這個典型的用法以外,我們在繼承中也可能會在子類覆寫父類中的方法, (5)overload對我們來說可能比較熟悉,可以翻譯為多載,它是指我們可以定義一些名稱相同的方法,通過定義不同型別的輸入引數來區分這些方法,然后再呼叫時,JVM就會根據不同的引數樣式,來選擇合適的方法執行, (6)方法的重寫和多載是Java多型性的不同表現,重寫是父類與子類之間多型性的一種表現,而多載是一個類中多型性的一種表現,
- 執行緒的創建方式
(1)繼承Thread ①定義Thread類的子類,并重寫該類的run方法,該run方法的方法體就代表了執行緒要完成的任務,因此把run()方法稱為執行體, ②創建Thread子類的實體,即創建了執行緒物件, ③呼叫執行緒物件的start()方法來啟動該執行緒, (2)實作Runnable介面 ①定義runnable介面的實作類,并重寫該介面的run()方法,該run()方法的方法體同樣是該執行緒的執行緒執行體, ②創建 Runnable實作類的實體,并依此實體作為Thread的target來創建Thread物件,該Thread物件才是真正的執行緒物件, ③呼叫執行緒物件的start()方法來啟動該執行緒, (3)實作Callable介面 ①創建Callable介面的實作類,并實作call()方法,該call()方法將作為執行緒執行體,并且有回傳值, ②創建Callable實作類的實體,使用FutureTask類來包裝Callable物件,該FutureTask物件封裝了該Callable物件的call()方法的回傳值, ③使用FutureTask物件作為Thread物件的target創建并啟動新執行緒, ④呼叫FutureTask物件的get()方法來獲得子執行緒執行結束后的回傳值
- LinkList和ArrayList的區別
(1)ArrayList的底層實作就是陣列,且ArrayList實作了RandomAccess,表示它能快速隨機訪問存盤的元素,通過下標 index 訪問,只是我們需要用 get() 方法的形式,陣列支持隨機訪問,查詢速度快,增刪元素慢; (2)LinkedList的底層實作是鏈表,LinkedList沒有實作RandomAccess 介面,鏈表支持順序訪問,查詢速度慢,增刪元素快;
- 垃圾回收演算法
(1)標記—清除演算法 標記—清除演算法是最基礎的收集演算法,它分為“標記”和“清除”兩個階段:首先標記出所需回收的物件,在標記完成后統一回收掉所有被標記的物件,它的標記程序其實就是前面的可達性分析演算法中判定垃圾物件的標記程序, (2)復制演算法 復制演算法是針對標記—清除演算法的缺點,在其基礎上進行改進而得到的,它將可用記憶體按容量分為大小相等的兩塊,每次只使用其中的一塊,當這一塊的記憶體用完了,就將還存活著的物件復制到另外一塊記憶體上面,然后再把已使用過的記憶體空間一次清理掉, (3)標記—整理演算法 復制演算法比較適合于新生代,在老年代中,物件存活率比較高,如果執行較多的復制操作,效率將會變低,所以老年代一般會選用其他演算法,如標記—整理演算法,該演算法標記的程序與標記—清除演算法中的標記程序一樣,但對標記后出的垃圾物件的處理情況有所不同,它不是直接對可回收物件進行清理,而是讓所有的物件都向一端移動,然后直接清理掉端邊界以外的記憶體
- Hashtable 和 HashMap的區別?
( 1)主要區別在于 HashMap允許將 null作為一個 entry的 key或者 value,而 Hashtable不允許,由于非執行緒安全,多執行緒情況下,效率上可能高于 Hashtable, Hashtable和 HashMap采用的 hash/rehash 演算法都大概一樣,所以單執行緒性能不會有很大的差異, ( 2) Hashtable的方法是 Synchronize的,而 HashMap不是,在多個執行緒訪問 Hashtable時,不需要自己為它的方法實作同步,而 HashMap 就必須為之提供外同步 (Collections.synchronizedMap), ( 3) HashMap是 Hashtable的輕量級實作(非執行緒安全的實作),他們都完成了 Map介面, Hashtable繼承自 Dictionary類,而 HashMap是 Java1.2引進的 Map interface的一個實作,
- servlet的執行緒安全問題?
答:如果代碼所在的行程中有多個執行緒在同時運行,而這些執行緒可能會同時運行這段代碼,如果每次運行結果和單執行緒運行的結果是一樣的,而且其他的變數的值也和預期的是一樣的,就是執行緒安全的,那么我們都知道servlet是多執行緒的,同時一個servlet實作類只會有一個實體物件,也就是它是Singleton的,所以多個執行緒是可能會訪問同一個servlet實體物件的,同一個實體物件被多個執行緒訪問,如果沒有做同步處理,那么servlet就是非執行緒安全的,如果做了同步處理,就是執行緒安全,所以,servlet是否執行緒安全是由它的實作來決定的,如果它內部的屬性或方***被多個執行緒改變,它就是執行緒不安全的,反之,就是執行緒安全的,
- 為什么要用volatile關鍵字?
答:在高并發時會出現并發模式例外,volatile可以防止指令重排,創建物件操作并不是一個原子操作,分為三個步驟 (1)構建物件:根據Person類元資訊確定物件的大小,向JVM堆中申請一塊記憶體區域并構建物件的默認資訊(加載Person物件成員變數資訊并賦默認值如 int型別為0,參考型別為null), (2)初始化物件:然后執行物件內部生成的init方法,初始化成員變數值,同時執行搜集到的{}代碼塊邏輯,最后執行物件構造方法, (3)參考物件:物件實體化完畢后,再把堆疊中的Person物件參考地址指向Person物件在堆記憶體中的地址......
- java并發鎖機制,
(1)偏向鎖:JDK1.6提出來的一種鎖優化的機制,其核心的思想是,如果程式沒有競爭,則取消之前已經取得鎖的執行緒同步操作,也就是說,若某一鎖被執行緒獲取后,便進入偏向模式,當執行緒再次請求這個鎖時,就無需再進行相關的同步操作了,從而節約了操作時間,如果在此之間有其他的執行緒進行了鎖請求,則鎖退出偏向模式 (2)輕量級鎖:如果偏向鎖失敗,Java虛擬機就會讓執行緒申請輕量級鎖,輕量級鎖在虛擬機內部,使用一個成為BasicObjectLock的物件實作的,這個物件內部由一個BasicLock物件和一個持有該鎖的Java物件指標組成,BasicObjectLock物件放置在Java堆疊幀中,在BasicLock物件內部還維護著displaced_header欄位,用于備份物件頭部的Mark Word, (3)重量級鎖:當輕量級鎖失敗,虛擬機就會使用重量級鎖,重量級鎖在操作程序中,執行緒可能會被作業系統層面掛起,如果是這樣,執行緒間的切換和呼叫成本就會大大提高, (4)自旋鎖:它可以使執行緒在沒有取得鎖的時候,不被掛起,而轉去執行一個慷訓圈,若在若干個慷訓圈后,執行緒如果可以獲得鎖,則繼續執行,若執行緒依然不能獲得鎖,才會被掛起,使用自旋鎖后,執行緒被掛起的幾率相對減少,執行緒執行的連貫性相對加強,因此,對于那些鎖競爭不是很激烈,鎖占用時間很短的并發執行緒,具有一定的積極意義,但對于鎖競爭激烈,單執行緒鎖占用很長時間的并發程式,自旋鎖在自旋等待后,往往毅然無法獲得對應的鎖,不僅僅白白浪費了CPU時間,最侄訓是免不了被掛起的操作 ,反而浪費了系統的資源,
- 了解zookeeper的leader選取演算法嗎,講一下它的流程,
(1)自增選舉輪次,Zookeeper規定所有有效的投票都必須在同一輪次中,在開始新一輪投票時,會首先對logicalclock進行自增操作, (2)初始化選票,在開始進行新一輪投票之前,每個服務器都會初始化自身的選票,并且在初始化階段,每臺服務器都會將自己推舉為Leader, (3)發送初始化選票,完成選票的初始化后,服務器就會發起第一次投票,Zookeeper會將剛剛初始化好的選票放入sendqueue中,由發送器WorkerSender負責發送出去, (4)接收外部投票,每臺服務器會不斷地從recvqueue佇列中獲取外部選票,如果服務器發現無法獲取到任何外部投票,那么就會立即確認自己是否和集群中其他服務器保持著有效的連接,如果沒有連接,則馬上建立連接,如果已經建立了連接,則再次發送自己當前的內部投票, (5)判斷選舉輪次,在發送完初始化選票之后,接著開始處理外部投票,在處理外部投票時,會根據選舉輪次來進行不同的處理, ·外部投票的選舉輪次大于內部投票,若服務器自身的選舉輪次落后于該外部投票對應服務器的選舉輪次,那么就會立即更新自己的選舉輪次(logicalclock),并且清空所有已經收到的投票,然后使用初始化的投票來進行PK以確定是否變更內部投票,最終再將內部投票發送出去, ·外部投票的選舉輪次小于內部投票,若服務器接收的外選票的選舉輪次落后于自身的選舉輪次,那么Zookeeper就會直接忽略該外部投票,不做任何處理,并回傳步驟4, ·外部投票的選舉輪次等于內部投票,此時可以開始進行選票PK, (6)選票PK,在進行選票PK時,符合任意一個條件就需要變更投票, · 若外部投票中推舉的Leader服務器的選舉輪次大于內部投票,那么需要變更投票, · 若選舉輪次一致,那么就對比兩者的ZXID,若外部投票的ZXID大,那么需要變更投票, · 若兩者的ZXID一致,那么就對比兩者的SID,若外部投票的SID大,那么就需要變更投票, (7)變更投票,經過PK后,若確定了外部投票優于內部投票,那么就變更投票,即使用外部投票的選票資訊來覆寫內部投票,變更完成后,再次將這個變更后的內部投票發送出去, (8)選票歸檔,無論是否變更了投票,都會將剛剛收到的那份外部投票放入選票集合recvset中進行歸檔,recvset用于記錄當前服務器在本輪次的Leader選舉中收到的所有外部投票, (9)統計投票,完成選票歸檔后,就可以開始統計投票,統計投票是為了統計集群中是否已經有過半的服務器認可了當前的內部投票,如果確定已經有過半服務器認可了該投票,則終止投票,否則回傳步驟(4), (10)更新服務器狀態,若已經確定可以終止投票,那么就開始更新服務器狀態,服務器首選判斷當前被過半服務器認可的投票所對應的Leader服務器是否是自己,若是自己,則將自己的服務器狀態更新為LEADING,若不是,則根據具體情況來確定自己是FOLLOWING或是OBSERVING,
- 雙親委派機制及其使用原因?
(1)當某個特定的類加載器它在接到需要加載類的請求時,這個類會首先查看自己已加載完的類中是否包含這個類,如果有就回傳,沒有的話就會把加載的任務交給父類加載器加載,以此遞回,父類加載器如果可以完成類加載任務,就回傳它,當父類加載器無法完成這個加載任務時,才會不得已自己去加載,這種機制就叫做雙親委派機制, (2)原因: 雙親委派機制能夠保證多加載器加載某個類時,最終都是由一個加載器加載,確保最終加載結果相同,
- treemap和HashMap的區別?
(1)HashMap是通過hashcode()對其內容進行快速查找的;HashMap中的元素是沒有順序的;TreeMap中所有的元素都是有某一固定順序的,如果需要得到一個有序的結果,就應該使用TreeMap; (2)HashMap繼承AbstractMap類;覆寫了hashcode() 和equals() 方法,以確保兩個相等的映射回傳相同的哈希值;TreeMap繼承SortedMap類;他保持鍵的有序順序; (3)HashMap:基于hash表實作的;使用HashMap要求添加的鍵類明確定義了hashcode() 和equals();為了優化HashMap的空間使用,可以調優初始容量和負載因子;TreeMap:基于紅黑樹實作的;TreeMap就沒有調優選項,因為紅黑樹總是處于平衡的狀態;
- java記憶體泄露解決
堆的dump檔案,通過jmx的mbean生產當前 的heap資訊, 用eclipse自帶的靜態分析工具Mat(windDBG)打開 分析記憶體泄露:那些被懷疑為記憶體泄露,哪些占用空間大,物件呼叫關系
- 分布式鎖的實作原理和有多少種實作方式?
目前主流的分布式鎖的實作方式有三種: ①借助資料庫來實作,新建一張鎖表; 操作前向表添加一條鎖記錄(鎖id建立唯一索引),成功添加者獲得鎖權限,處理完后洗掉鎖記錄來釋放鎖, ②基于快取實作,如memcache 和 redis; memcache的add操作具有原子性,可以保證同一個key add操作只有一個成功,來獲取鎖權限,利用快取的失效時間來解決死鎖問題,相對于第一種方案,這種方案性能更好,而且操作更方便, ③通過zookeeper實作; 客戶端會在zookeeper生成一個臨時的目錄節點,存盤在一個序列中,每次節點序號最小的節點對應的客戶端獲得鎖,處理完成后洗掉最小節點,而且可重復獲取鎖(通過判斷序號是否和最小的節點相同),這種方式可以實作阻塞分布式鎖,和鎖的重復獲取問題,
- HashMap和Hashtable的區別
主要的區別有:執行緒安全性,同步,以及速度, (1)HashMap幾乎可以等價于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受為null的鍵值(key)和值(value),而Hashtable則不行), HashMap是非synchronized,而Hashtable是synchronized,這意味著Hashtable是執行緒安全的,多個執行緒可以共享一個Hashtable;而如果沒有正確的同步的話,多個執行緒是不能共享HashMap的,Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴展性更好, (2)另一個區別是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的,所以當有其它執行緒改變了HashMap的結構(增加或者移除元素),將會拋出ConcurrentModificationException,但迭代器本身的remove()方法移除元素則不會拋出ConcurrentModificationException例外,但這并不是一個一定發生的行為,要看JVM,這條同樣也是Enumeration和Iterator的區別, 由于Hashtable是執行緒安全的也是synchronized,所以在單執行緒環境下它比HashMap要慢,如果你不需要同步,只需要單一執行緒,那么使用HashMap性能要好過Hashtable, HashMap不能保證隨著時間的推移Map中的元素次序是不變的,
- HashMap中是否任何物件都可以做為key,用戶自定義物件做為key有沒有什么要求?
用自定義類作為key,必須重寫equals()和hashCode()方法, 自定義類中的equals() 和 hashCode()都繼承自Object類, Object類的hashCode()方法回傳這個物件存盤的記憶體地址的編號, 而equals()比較的是記憶體地址是否相等,
- 對sql進行優化的原則有哪些?
(1)減少回傳不必要的資料 (2)減少物理和邏輯讀次數 (3)減少計算次數
- String和StringBuffer的區別
1)運行速度:StringBuilder >StringBuffer >String String是字串常量,不可變,每次改變只是創建一個新的物件,然后GC回收掉老的那個,所以執行速度最慢,另外兩個是字串物件,可變, (2)執行緒安全: StringBuilder是執行緒不安全的,StringBuffer是執行緒安全的,看是否帶synchronized關鍵字,多執行緒則采用StringBuffer,單執行緒則要建議用速度較快的StringBuilder, (3)String:適用于少量的字串操作的情況,String是final類,無法被繼承,StringBuilder:適用于單執行緒下在字符緩沖區進行大量操作的情況, StringBuffer:適用多執行緒下在字符緩沖區進行大量操作的情況,
- 如果A和B物件回圈參考,是否可以被GC?
答:這個回圈參考是否被回收,就看這個回圈參考是否掛在根上,A參考B,B參考A,A和B并沒有掛在某個記憶體元和根上,當他們的生命周期結束的時候,這兩個物件都有可能被回收,
- Error、Exception和RuntimeException的區別,作用又是什么?
Error是Throwable 的子類,用于指示合理的應用程式不應該試圖捕獲的嚴重問題,大多數這樣的錯誤都是例外條件,雖然 ThreadDeath 錯誤是一個“正規”的條件,但它也是 Error 的子類,因為大多數應用程式都不應該試圖捕獲它,在執行該方法期間,無需在其 throws 子句中宣告可能拋出但是未能捕獲的 Error的任何子類,因為這些錯誤可能是再也不會發生的例外條件, Exception類及其子類是 Throwable 的一種形式,它指出了合理的應用程式想要捕獲的條件, RuntimeException是那些可能在 Java 虛擬機正常運行期間拋出的例外的超類,可能在執行方法期間拋出但未被捕獲的RuntimeException 的任何子類都無需在 throws 子句中進行宣告,它是Exception的子類,
- reader和inputstream區別
(1)InputStream是表示位元組輸入流的所有類的超類;Reader是用于讀取字符流的抽象類 (2)InputStream提供的是位元組流的讀取,而非文本讀取,這是和Reader類的根本區別, 即用Reader讀取出來的是char陣列或者String ,使用InputStream讀取出來的是byte陣列,
- hashCode的作用;
hashCode方法的主要作用是為了配合基于散列的集合一起正常運行,這樣的散列集合包括HashSet、HashMap以及HashTable, Java中的hashCode方法就是根據一定的規則將與物件相關的資訊(比如物件的存盤地址,物件的欄位等)映射成一個數值,這個數值稱作為散列值,
- Java中的記憶體溢位是如何造成的?
(1)記憶體中加載的資料量過于龐大,如一次從資料庫取出過多資料; (2)集合類中有對物件的參考,使用完后未清空,使得JVM不能回收; (3)代碼中存在死回圈或回圈產生過多重復的物件物體; (4)使用的第三方軟體中的BUG; (5)啟動引數記憶體值設定的過小;
- springMVC的作業原理圖
(1)客戶端發出一個http請求給web服務器,web服務器對http請求進行決議,如果匹配 DispatcherServlet的請求映射路徑(在web.xml中指定), web容器將請求轉交給DispatcherServlet; (2)DipatcherServlet接收到這個請求之后將根據請求的資訊(包括URL、Http方法、請求報文頭和請 求引數Cookie等) 以及HandlerMapping的配置找到處理請求的處理器(Handler); (3)DispatcherServlet根據HandlerMapping找到對應的Handler,將處理權交給Handler(Handler將 具體的處理進行封裝), 再由具體的HandlerAdapter對Handler進行具體的呼叫, (4)Handler對資料處理完成以后將回傳一個ModelAndView()物件給DispatcherServlet; (5)Handler回傳的ModelAndView()只是一個邏輯視圖并不是一個正式的視圖,DispatcherSevlet通過 ViewResolver將邏輯視圖轉化為真正的視圖View; (6)Dispatcher通過model決議出ModelAndView()中的引數進行決議最終展現出完整的view并回傳給客戶端;
- RPC框架和普通http有什么區別和優勢?
基于Tcp封裝還是http封裝的? (1)1、RPC是一種API,HTTP是一種無狀態的網路協議,RPC可以基于HTTP協議實作,也可以直接在TCP協議上實作, (2)RPC主要是用在大型網站里面,因為大型網站里面系統繁多,業務線復雜,而且效率優勢非常重要的一塊,這個時候RPC的優勢就比較明顯了, (3)HTTP主要是用在中小型企業里面,業務線沒那么繁多的情況下, (4)HTTP開發方便簡單、直接,開發一個完善的RPC框架難度比較大, (5)HTTP發明的初衷是為了傳送超文本的資源,協議設計的比較復雜,引數傳遞的方式效率也不高,開源的RPC框架針對遠程呼叫協議上的效率會比HTTP快很多, (6)HTTP需要事先通知,修改Nginx/HAProxy配置,RPC能做到自動通知,不影響上游, (7)HTTP大部分是通過Json來實作的,位元組大小和序列化耗時都比Thrift要更消耗性能,RPC,可以基于Thrift實作高效的二進制傳輸,
- GC的基本原理?什么時候需要GC?為什么需要GC?
GC (Garbage Collection)的基本原理:將記憶體中不再被使用的物件進行回收,GC中用于回收的方法稱為收集器,由于GC需要消耗一些資源和時間,Java在對物件的生命周期特征進行分析后,按照新生代、舊生代的方式來對物件進行收集,以盡可能的縮短GC對應用造成的暫停 (1)對新生代的物件的收集稱為minor GC; (2)對舊生代的物件的收集稱為Full GC; (3)程式中主動呼叫System.gc()強制執行的GC為Full GC, 不同的物件參考型別, GC會采用不同的方法進行回收,JVM物件的參考分為了四種型別: (1)強參考:默認情況下,物件采用的均為強參考(這個物件的實體沒有其他物件參考,GC時才會被回收) (2)軟參考:軟參考是Java中提供的一種比較適合于快取場景的應用(只有在記憶體不夠用的情況下才會被GC) (3)弱參考:在GC時一定會被GC回收 (4)虛參考:由于虛參考只是用來得知物件是否被GC
- 怎樣避免死鎖?
(1)破壞“不可剝奪”條件:一個行程不能獲得所需要的全部資源時便處于等待狀態,等待期間他占有的資源將被隱式的釋放重新加入到 系統的資源串列中,可以被其他的行程使用,而等待的行程只有重新獲得自己原有的資源以及新申請的資源才可以重新啟動,執行, (2)破壞”請求與保持條件”:第一種方法靜態分配即每個行程在開始執行時就申請他所需要的全部資源,第二種是動態分配即每個行程在申請所需要的資源時他本身不占用系統資源, (3)破壞“回圈等待”條件:采用資源有序分配其基本思想是將系統中的所有資源順序編號,將緊缺的,稀少的采用較大的編號,在申請資源時必須按照編號的順序進行,一個行程只有獲得較小編號的行程才能申請較大編號的行程
- BEAN的生命周期
(1)應用啟動的時候檢查加載需要被Spring管理的bean. (2)根據實作的介面,依次設定beanName,BeanFactory,ApplicationContext應用背景關系, (3)根據實作的介面,依次呼叫加載前,設定值,自定義初始化方法,加載完成后, (4)bean已經可以用了,存活直到背景關系也被銷毀, (5)銷毀的時候呼叫destroy方法和自定義的銷毀方法,
歡迎搜索關注本人與朋友共同開發的微信面經小程式【大廠面試助手】和公眾號【微瞰技術】


轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/239429.html
標籤:其他
上一篇:大廠面試系列一些問題的答案
