阿里巴巴面試題總結(java後端)-第一章

①,一面的面試題總結css

1,HashMap實現原理,ConcurrentHashMap實現原理。html

<1>HashMap,底層hash表,在jdk 1.7之前是數組與鏈表,jdk1.8之後是鏈表長度達到8時會演變成紅黑樹(維持數據的插入和查找的效率平衡)。 <2>ConcurrentHashMap,是hashMap的演變,在jdk 1.7之前是segement分段加鎖,爲了減小鎖競爭。 每次的數據查找經歷兩次hash,第一次hash先找到segement,第二次hash是查找segement段內的鏈表位置,在寫入數據時對segement加鎖. 每一個版本查找數據的特殊地方:1.6之前是找到節點數據才加鎖(外層調用不加鎖),1.7之後都是使用Unsafe.getObjectVolatile。 ConcurrentHashMap 1.8之後是數組和鏈表,再也不使用segement,直接對每一個鏈表進行加鎖,更進一步下降鎖競爭,put操做是使用了synchronize對當前鏈表加鎖,get是使用Unsafe.getObjectVolatile獲取最新的共享內存值(利用cas不加鎖),爲了保證獲取的數據是最新的(可見性)java

Segement的modCount變動條件:調用put或者remove操做,而且致使元素新增或者被刪除,才能引發變化,若是僅僅覆蓋或者刪除不成功,不會致使變化。 Size()方法:其實就是在每一個segement modcount相同狀況下(不然立刻再次發起重試),再校驗count的總數(查兩次),若是不一樣,重試兩次,若是仍是不一樣,就對每一個segement加鎖。mysql

補充:LinkedList、ArrayList,LinkedHashMap <1>LinkedList:底層是基於雙向鏈表(非環狀)實現,爲何使用雙向鏈表?爲了提升數據的查找效率(包括指定index位置插入),所以內部會根據index和size作比較,決定從頭部向後或者尾部向前開始遍歷,如當咱們調用get(int location)時,首先會比較「location」和「雙向鏈表長度的1/2」;若前者大,則從鏈表頭開始日後查找,直到location位置;不然,從鏈表末尾開始先前查找,直到location位置。nginx

特色:LinkedList查找效率低,增刪效率更高,能夠利用零碎的內存空間。web

<2>ArrayList:是基於動態數組實現的,若是空間不夠的時候,增長新元素時要動態擴容數組(期間還須要拷貝數據到新數組),刪除元素時也須要對後面的數據進行向前移動整合(由於後面還須要使用index搜索數據)。 特色:ArrayList的查找效率高,而增刪操做的效率低(只能使用連續的內存空間),使用建議:空間能夠申請大一些,儘可能不要刪除數據。面試

ArrayList中的modCount,繼承於AbstractList,這個成員變量記錄着集合的修改次數,也就每次add或者remove它的值都會加1,在使用迭代器遍歷集合的時候同時修改集合元素。由於ArrayList被設計成非同步的,所以會拋出異常,實現原理:獲取迭代器的時候,會記錄一個expectedModCount(不會被改變),在每次迭代過程當中會校驗expectedModCount和modCount是否相等,不然會拋出異常ConcurrentModificationExceptionredis

<3>LinkedHashMap:繼承了HashMap,所以底層仍是哈希表結構(數組+鏈表),可是另外多維護了一個雙向環狀鏈表(只有一個頭結點),提供了插入有序和訪問有序(lru)兩種模式,默認爲插入有序(打印的結果就是以前的插入順序),LinkedHashMap重寫了的內部addEntry(put調用)方法,重寫了Entry,包含before, after,爲了未來能構建一個雙向的鏈表,當向map裏面添加數據時,在createEntry時,把新建立的Entry加入到雙向鏈表中,之因此使用雙向環狀鏈表就是爲了實現訪問有序,每次訪問,都把節點調整到頭結點前面(實際上變成了尾節點)。 簡單總結:當put元素時,不但要把它加入到HashMap中去,還要加入到雙向鏈表中,因此能夠看出LinkedHashMap就是HashMap+雙向環狀鏈表算法

參考:www.jianshu.com/p/8f4f58b4b…spring

補充:爲何Hashtable ConcurrentHashmap不支持key或者value爲null ConcurrentHashmap和Hashtable都是支持併發的,這樣會有一個問題,當你經過get(k)獲取對應的value時,若是獲取到的是null時,你沒法判斷(除非全程在插入、查詢時加鎖判斷,此時性能就降低了),它是put(k,v)的時候value爲null,仍是這個key歷來沒有作過映射。HashMap是非併發的,能夠經過contains(key)來作這個判斷。而支持併發的Map在調用m.contains(key)和m.get(key),m可能已經不一樣了。 備註:實際存儲null,這種場景已經不大,若是真的要存儲,在查詢和插入都須要犧牲了在線程安全條件的併發性能。 參考:blog.csdn.net/gagewang1/a…

2,紅黑樹,爲何容許局部不平衡 徹底平衡二叉樹的左右兩個子樹的高度差的絕對值不超過1,所以每次的節點變動都須要保證樹的嚴格平衡。 紅黑樹只須要保證從一個節點到該節點的子孫節點的全部路徑上包含相同數目的黑節點,所以紅黑樹是一顆接近徹底平衡的二叉樹,減小了旋轉次數,提升數據的寫入效率。

紅黑樹特色: (1)每一個節點或者是黑色,或者是紅色。 (2)根節點是黑色。 (3)每一個葉子節點(NIL)是黑色。 [注意:這裏葉子節點,是指爲空(NIL或NULL)的葉子節點!] (4)若是一個節點是紅色的,則它的子節點必須是黑色的。 (5)從一個節點到該節點的子孫節點的全部路徑上包含相同數目的黑節點。

補充:插入節點默認是紅色(第五點限制),紅黑樹具體的旋轉:www.cnblogs.com/skywang1234…

平衡二叉搜索樹:又被稱爲AVL樹(有別於AVL算法),且具備如下性質:它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,而且左右兩個子樹都是一棵平衡二叉樹。平衡二叉樹的經常使用實現方法有紅黑樹、AVL、替罪羊樹、Treap、伸展樹等。

普通的二叉查找樹:容易失去」平衡「,極端狀況下,二叉查找樹會退化成線性的鏈表,致使插入和查找的複雜度降低到 O(n) ,因此,這也是平衡二叉樹設計的初衷。那麼平衡二叉樹如何保持」平衡「呢?根據定義,有兩個重點,一是左右兩子樹的高度差的絕對值不能超過1,二是左右兩子樹也是一顆平衡二叉樹

樹的補充:blog.csdn.net/qq_25940921…

3,tcp三次握手,四次揮手,爲何不是2次握手?

<1>三次握手(Three-way Handshake),是指創建一個TCP鏈接時,須要客戶端和服務器總共發送3個包 TCP是一個雙向通訊協議,客戶端向服務端創建鏈接須要獲得服務端ack響應,意味着客戶端向服務端通訊正常,此時客戶端再給服務端回覆一個ack,意味着服務端向客戶端通訊正常,這樣才能正確創建起來雙工通訊通道,所以兩次握手不能保證服務端向客戶端通訊是否正常。

<2>TCP的鏈接的拆除須要發送四個包,所以稱爲四次揮手(four-way handshake)。客戶端或服務器都可主動發起揮手動做,在socket編程中,任何一方執行close()操做便可產生揮手操做。 客戶端或服務器能夠單獨關閉向另一端的通訊通道,即爲半關閉狀態,所以才進行四次揮手。 爲何須要半關閉?好比只須要向一方傳輸資源,傳輸完畢以EOF標記便可。

4,tcp和udp的區別,爲何是可靠和不可靠的? <1>TCP協議是有鏈接的,有鏈接的意思是開始傳輸實際數據以前TCP的客戶端和服務器端必須經過三次握手創建鏈接,會話結束以後也要結束鏈接。而UDP是無鏈接的 <2>TCP協議保證數據按序發送,按序到達,提供超時重傳來保證可靠性,可是UDP不保證按序到達,甚至不保證到達,只是努力交付,即使是按序發送的序列,也不保證按序送到。 <3>TCP有流量控制和擁塞控制,UDP沒有,網絡擁堵不會影響發送端的發送速率 <4>TCP是一對一的鏈接,而UDP則能夠支持一對一,多對多,一對多的通訊。 <5>TCP面向的是字節流的服務,UDP面向的是報文的服務

TCP的可靠性含義: 接收方收到的數據是完整, 有序, 無差錯的。 UDP不可靠性含義: 接收方接收到的數據可能存在部分丟失, 順序也不必定能保證。

備註:GET和POST還有一個重大區別,簡單的說:GET產生一個TCP數據包;POST產生兩個TCP數據包。

長的說: 對於GET方式的請求,瀏覽器會把http header和data一併發送出去,服務器響應200(返回數據); 而對於POST,瀏覽器先發送header,服務器響應100 continue,瀏覽器再發送data,服務器響應200 ok(返回數據)。

post請求的過程: (1)瀏覽器請求tcp鏈接(第一次握手) (2)服務器答應進行tcp鏈接(第二次握手) (3)瀏覽器確認,併發送post請求頭(第三次握手,這個報文比較小,因此http會在此時進行第一次數據發送) (4)服務器返回100 Continue響應 (5)瀏覽器發送數據 (6)服務器返回200 OK響應 get請求的過程: (1)瀏覽器請求tcp鏈接(第一次握手) (2)服務器答應進行tcp鏈接(第二次握手) (3)瀏覽器確認,併發送get請求頭和數據(第三次握手,這個報文比較小,因此http會在此時進行第一次數據發送) (4)服務器返回200 OK響應 也就是說,目測get的總耗是post的2/3左右,這個口說無憑,網上已經有網友進行過測試。

參考:blog.csdn.net/zzk220106/a…

5,TCP滑動窗口和socket緩衝區之間的關係 TCP的滑動窗口大小實際上就是socket的接收緩衝區大小的字節數,「窗口」對應的是一段能夠被髮送者發送的字節序列,其連續的範圍稱之爲「窗口」;每次成功發送數據以後,發送窗口就會在發送緩衝區中按順序移動,將新的數據包含到窗口中準備發送。(數據都往窗口寫入,所以窗口大小控制數據傳輸速率,起到一個緩衝的做用)

6,tcp擁塞控制 網絡中的鏈路容量和交換結點中的緩存和處理機都有着工做的極限,當網絡的需求超過它們的工做極限時,就出現了擁塞。擁塞控制就是防止過多的數據注入到網絡中,這樣可使網絡中的路由器或鏈路不致過載。經常使用的方法就是:

  1. 慢開始、擁塞控制
  2. 快重傳、快恢復

補充說明:慢開始是指發送方先設置cwnd=1,一次發送一個報文段,隨後每通過一個傳輸輪次,擁塞串口cwnd就加倍,其實增加並不慢,以指數形式增加。還要設定一個慢開始門限,當cwnd>門限值,改用擁塞避免算法。擁塞避免算法使cwnd按線性規律緩慢增加。當網絡發生延時,門限值減半,擁塞窗口執行慢開始算法。(先指數級別增長,再線性增長,延遲再衰減)

快重傳的機制是: 接收方創建這樣的機制,若是一個包丟失,則對後續的包繼續發送針對該包的重傳請求,一旦發送方接收到三個同樣的確認,就知道該包以後出現了錯誤,馬上重傳該包;

此時發送方開始執行「快恢復」算法: 慢開始門限減半; cwnd設爲慢開始門限減半後的數值; 執行擁塞避免算法(高起點,線性增加) 參考:www.cnblogs.com/woaiyy/p/35…

7,mysql事務是什麼?四大特性,四大隔離級別 事務:事務處理能夠用來維護數據庫的完整性,保證成批的 SQL 語句要麼所有執行,要麼所有不執行,是最小的不可再分的工做單元。 四大特性,原子性,隔離性,持久性,一致性。 四大隔離級別: 事務隔離級別 髒讀 不可重複讀 幻讀 讀未提交(read-uncommitted) 是 是 是 讀已提交(read-committed) 否 是 是 可重複讀(repeatable-read) 否 否 是 串行化(serializable) 否 否 否 serializable級別是最高的 mysql默認的事務隔離級別爲repeatable-read

① Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生。   ② Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。   ③ Read committed (讀已提交):可避免髒讀的發生。   ④ Read uncommitted (讀未提交):最低級別,任何狀況都沒法保證。

  以上四種隔離級別最高的是Serializable級別,最低的是Read uncommitted級別,固然級別越高,執行效率就越低。像Serializable這樣的級別,就是以鎖表的方式(相似於Java多線程中的鎖)使得其餘的線程只能在鎖外等待,因此平時選用何種隔離級別應該根據實際狀況。在MySQL數據庫中默認的隔離級別爲Repeatable read (可重複讀)。

  在MySQL數據庫中,支持上面四種隔離級別,默認的爲Repeatable read (可重複讀);而在Oracle數據庫中,只支持Serializable (串行化)級別和Read committed (讀已提交)這兩種級別,其中默認的爲Read committed級別。

8,spring ioc和aop,各自有什麼優勢?

ioc,依賴注入,在之前的軟件工程編碼過程當中,類的屬性須要硬編碼生成對象數據,耦合性較高,若是使用ioc,是在容器啓動過程當中,在bean對象實例化過程當中須要檢查其依賴數據,而且進行數據注入(setter,構造器注入),完成一個對象的實例化並實現瞭解耦合,而且可以對這些對象進行復用。

aop,主要分爲兩大類:一是採用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用靜態織入的方式,引入特定的語法建立「方面」,從而使得編譯器能夠在編譯期間織入有關「方面」的代碼。它利用一種稱爲"橫切"的技術,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,簡單理解是抽象出與業務邏輯無關的公共行爲邏輯。

9,java有哪幾種線程池 //線程池大小固定爲1 Executors.newSingleThreadExecutor();

//固定大小線程池由本身設定,即本身控制資源的固定分配
    Executors.newFixedThreadPool(10);

    //動態調整線程池的大小,最小爲0,最大爲int最大值,,newCachedThreadPool會大幅度提升大量短暫異步任務的性能,
    //若是執行業務邏輯比較慢,會致使持續建立線程,致使cpu資源消耗殆盡
    //爲何使用SynchronousQueue?最多隻能持有一個任務數據,當任務數據插入隊列失敗,會驅動建立新線程
    Executors.newCachedThreadPool();

    //基於延遲隊列實現的延時任務線程池,週期性的執行所提交的任務
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
    scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " run");
        }
    }, 1000,2000, TimeUnit.MILLISECONDS);
複製代碼

10,什麼狀況下使用Thread和Runnable建立線程,Runnable和Callable區別 兩個均可以實現多線程編程,可是基於java是單繼承和多實現,因此實現Runable更靈活,而且Runable能夠簡單的實現變量的線程間共享。 Runnable和Callable區別: 實現Callable接口的任務線程能返回執行結果;而實現Runnable接口的任務線程不能返回結果; Callable接口的call()方法容許拋出異常;而Runnable接口的run()方法的異常只能在內部消化,不能繼續上拋;

11,線程方法中的異常若是處理?父線程能夠捕獲到嗎? 在run方法體裏面必須主動捕獲check exception,若是是unchecked exception(RunntimeException)能夠給線程註冊一個UncaughtExceptionHandler,發生異常時執行回調。 經過Callable建立的線程,能夠在futureTask.get()獲取結果時捕獲異常。

12,synchronized和lock區別,什麼狀況下使用synchronized和Reentrantlock ? 1.首先synchronized是java內置關鍵字,在jvm層面,Lock是個java類; 2.synchronized沒法判斷是否獲取鎖的狀態,Lock能夠判斷是否獲取到鎖; 3.synchronized會自動釋放鎖(a 線程執行完同步代碼會釋放鎖 ;b 線程執行過程當中發生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),不然容易形成線程死鎖; 4.用synchronized關鍵字的兩個線程1和線程2,若是當前線程1得到鎖,線程2線程等待。若是線程1阻塞,線程2則會一直等待下去,而Lock鎖就不必定會等待下去,若是嘗試獲取不到鎖,線程能夠不用一直等待就結束了; 5.synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可中斷、可判斷、可公平(二者皆可) 6.Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少許的同步問題

備註:synchronized適合低併發的場景,鎖競爭發生的機率很小,此時鎖處於偏向鎖或者輕量級鎖狀態,所以性能更加好,好比jdk1.8concurrentHashMap爲何使用synchronized的緣由就是,每一個hash槽上的鎖競爭不多,用synchronized比lock更好

13,jvm的對象分配在哪一個區,Class對象分配在哪一個區? 線程共享的變量分配在堆(實例)和方法區(靜態變量等)裏面,非線程共享的變量在棧區 Class對象分配在堆裏面,Class的元數據是在方法區裏面

14,一次http請求的全過程,包括域名解析、主機定位等。 <1>域名解析,分爲以下幾個步驟(找不到逐步遞歸) A,查找瀏覽器的dns緩存(域名與ip的映射表) B,查找自身操做系統自身的DNS緩存, C,查找自身的host文件 D,請求打到運營商的dns服務器,訪問運營商的dns緩存。 E,訪問全球頂級域名解析器,得到將要尋址域名的權限域名服務器地址(用來保存該區中的全部主機域名到IP地址的映射,如baidu.com的域名ip管理機器) F,運營商dns訪問權限域名服務器獲取目標域名的ip。

<2>tcp三次握手 <3>創建TCP鏈接後發起http請求(get,post由發起者決定)   HTTP請求報文由三部分組成:請求行,請求頭和請求正文   請求行:用於描述客戶端的請求方式,請求的資源名稱以及使用的HTTP協議的版本號(例:GET/books/java.html HTTP/1.1)   請求頭:用於描述客戶端請求哪臺主機,以及客戶端的一些環境信息等   注:這裏提一個請求頭 Connection,Connection設置爲 keep-alive用於說明 客戶端這邊設置的是,本次HTTP請求以後並不須要關閉TCP鏈接,這樣可使下次HTTP請求使用相同的TCP通道,節省TCP創建鏈接的時間   請求正文:當使用POST, PUT等方法時,一般須要客戶端向服務器傳遞數據。這些數據就儲存在請求正文中(GET方式是保存在url地址後面,不會放到這裏) <4>服務器端響應http請求,瀏覽器獲得html代碼   HTTP響應也由三部分組成:狀態碼,響應頭和實體內容   狀態碼:狀態碼用於表示服務器對請求的處理結果   列舉幾種常見的:200(沒有問題) 302(要你去找別人) 304(要你去拿緩存) 307(要你去拿緩存) 403(有這個資源,可是沒有訪問權限) 404(服務器沒有這個資源) 500(服務器這邊有問題)   若干響應頭:響應頭用於描述服務器的基本信息,以及客戶端如何處理數據   實體內容:服務器返回給客戶端的數據   注:html資源文件應該不是經過 HTTP響應直接返回去的,應該是經過nginx經過io操做去拿到的吧

<5>瀏覽器解析html代碼,並請求html代碼中的資源 <6>斷開TCP鏈接(四次揮手) <7>頁面渲染給用戶

總結:域名解析 --> 發起TCP的3次握手 --> 創建TCP鏈接-->發起http請求 --> 服務器響應http請求,瀏覽器獲得html代碼 --> 瀏覽器解析html代碼,並請求html代碼中的資源(如js、css、圖片等)-->tcp四次揮手 --> 瀏覽器對頁面進行渲染呈現給用戶

②,二面的面試題總結 1,經常使用設計模式的介紹,單例模式,裝飾模式,及使用場景

單例模式: public class SingletonTest { private static volatile SingletonTest singletonTest;

public SingletonTest getInstance() {
    if (singletonTest == null) {
        synchronized (SingletonTest.class) {
            if (singletonTest == null) {
                singletonTest = new SingletonTest();
            }
        }
    }
    return singletonTest;
}
複製代碼

} 使用場景:全局只須要初始化一個實例對象,節省內存開銷(好比spring bean的單例配置)

裝飾模式 public class DecoratorTest { public interface Greeting { void sayHello(); }

public static class GreetingImpl implements Greeting {
    @Override
    public void sayHello() {
        System.out.println("GreetingImpl run");
    }
}

public static abstract class GreetingDecorator implements Greeting {
    private Greeting greeting;

    public GreetingDecorator(Greeting greeting) {
        this.greeting = greeting;
    }

    @Override
    public void sayHello() {
        greeting.sayHello();
    }
}

public static class GreetingBefore extends GreetingDecorator {
    public GreetingBefore(Greeting greeting) {
        super(greeting);
    }

    @Override
    public void sayHello() {
        before();
        super.sayHello();
    }

    private void before() {
        System.out.println("Before");
    }
}

public static void main(String[] args) throws FileNotFoundException {
    Greeting greeting = new GreetingBefore(new GreetingImpl());
    greeting.sayHello();

}
複製代碼

}

代理模式重在於對方法的控制,添加行爲對於用戶是被動的;裝飾模式重在於裝飾方法,增長方法的功能(功能加強),添加裝飾對於用戶是主動的(代理模式和裝飾模式看起來很相像)

輸入輸出流使用裝飾模式的地方:BufferedInputStream就是一個裝飾者,它能爲一個本來沒有緩衝功能的InputStream添加上緩衝的功能。

2,Java會出現內存溢出嗎?什麼狀況下會出現? 內存溢出(Out Of Memory,OOM),就是內存不夠用了,內存泄漏(Memory Leak),指的是申請的內存空間,本身沒有去主動釋放,gc也沒法釋放(如強引用),屢次內存泄漏,就會致使內存溢。

3,雙親委派模型,爲何這樣作? 當一個類加載和初始化的時候,類僅在有須要加載的時候被加載。假設你有一個應用須要的類叫做Abc.class,首先加載這個類的請求由Application類加載器委託給它的父類加載器Extension類加載器,而後再委託給Bootstrap類加載器。Bootstrap類加載器會先看看rt.jar中有沒有這個類,由於並無這個類,因此這個請求由回到Extension類加載器,它會查看jre/lib/ext目錄下有沒有這個類,若是這個類被Extension類加載器找到了,那麼它將被加載,而Application類加載器不會加載這個類;而若是這個類沒有被Extension類加載器找到,那麼再由Application類加載器從classpath中尋找。

雙親委託能夠避免重複加載,當父親已經加載了該類的時候,就沒有必要子ClassLoader再加載一次(單一性)。

4,對象什麼狀況下進入老年代? <1>申請大對象,young區沒法存放這麼大的對象,此時須要在老年代申請空間。 <2>young區中的eden區對象通過屢次Minor GC晉升到Survivor區,若是還存活,就會移動到老年代。

補充: <1>full gc發生條件:System.gc()調用,老年代空間不足,perm(方法區,即永生代)空間不足。 <2>young區爲何要有Survivor區?只要進行一次MinorGC,存活的對象就進入了old區,致使old區很快寫滿,就會頻繁出發full gc,進而引起99線波動很大。 <3>young區爲何要有2個Survivor區?若是使用單個Survivor,eden區和Survivor通過Minor GC以後存活的對象存放在單個Survivor,可能出現碎片化,使用兩個就不會(每次gc後s0和s1之間交換數據,保證有一個無碎片,一個爲空) <4>young和old區比例通常是1:2,eden和s0,s1是8:1:1 參考:blog.csdn.net/towads/arti…

5,快速排序說一下(todo)

6,Spring AOP原理說下 AOP是切面編程,所謂切面編程是把與業務無關的公共邏輯抽象成一個公共方法執行。AOP的實現是經過動態代理,動態代理有兩種實現方式,jdk自帶的動態代理(經過類加載器,運行時生成一個新的代理類,代理類經過反射機制獲取被代理類的方法,完成被代理方法的執行),cglib在類加載時經過探針技術(javaAgent和ASM,均是字節碼修改工具)修改被代理類的字節碼,生成新的class,完成被代理類的方法調用。 二者區別:Jdk動態代理針對接口,cglib針對類。 補充:Jdk動態代理針對接口爲何只能代理接口?考慮到被代理對象的通用性,因爲一個接口可能有多個實現類,不須要針對每一個實現類寫一個代理類,這樣提升了通用性。

7,BIO、NIO(如何實現的)、AIO 先簡介一下同步、異步、阻塞、非阻塞的概念 同步:調用者(用戶線程,如selector線程)調用一個服務,必須本身主動等待結果,中間能夠作別的事情,可是得要輪詢結果。 異步:調用者(用戶線程)調用一個服務,不須要等待業務邏輯執行完,就能夠去別的事情,以前業務邏輯處理完,會通知調用者。 關注點:執行結果獲取結果方式(主動、被動(回調))

阻塞:調用結果沒有返回以前,會持續等待結果(調用者線程被阻塞,cpu沒有交出去) 非阻塞:調用結果沒有返回以前,調用者線程不會被阻塞。 關注點:用戶線程是否被阻塞

BIO:同步阻塞io NIO:同步非阻塞io,須要多路複用器Selector進行調度,執行結果須要多路複用器詢問。 AIO:異步非阻塞io,執行結果由os進行回調通知

使用場景: BIO:使用鏈接數較少,服務器壓力很低的架構。 NIO方式適用於鏈接數目多且鏈接比較短(輕操做)的架構,好比聊天服務器,併發侷限於應用中,編程比較複雜,JDK1.4開始支持。 AIO方式使用於鏈接數目多且鏈接比較長(重操做)的架構,好比相冊服務器,充分調用OS參與併發操做,編程比較複雜,JDK7開始支持。

AIO在性能上相對於NIO沒有本質的提高。 AIO只是幫助你從內核中將數據複製到用戶空間中,並調用你傳入的回調方法(內核完成回調)。 NIO是須要程序本身從內核中將數據複製到用戶空間中,並須要程序本身調用相應的處理邏輯。

補充:在微服務架構裏面,服務響應都比較快,aio並不比nio響應快多少,所以netty實現上也就不使用aio。

8,消息中間件有哪些?它們之間的優劣勢? ActiveMQ:單機吞吐量在萬級,比RocketMQ和Kafka低一個數量級,傳統中小企業用的多一些,沒有大規模吞吐場景驗證,沒有活躍社區,有較低的機率丟失數據(不是百分百可靠),支持事務。 RabbitMQ:單機吞吐量在萬級,比RocketMQ和Kafka低一個數量級,基於erlang開發,不利於java開發者深刻研究,其可靠性很是高,有活躍社區,不支持事務。 RocketMQ:吞吐量10萬級(高吞吐,低延遲),單機支持topic可達到好幾百,基於java開發(客戶端只支持java),有活躍社區,支持事務。(高吞吐量實現上參考了不少kafka的原理,好比內存映射,多個broker) Kafka:吞吐量10萬級(高吞吐,低延遲),客戶端支持多種語言,單機topic過多時,會致使性能降低,此時須要增長機器,有活躍社區,不支持事務。

參考:www.jianshu.com/p/eaafb1581…

9,redis的持久化方式 RDB:在指定的時間間隔能對數據進行快照存儲。 優勢:使用單獨子進程來進行持久化,主進程不會進行任何IO操做,保證了redis的高性能 缺點:RDB是間隔一段時間進行持久化,若是持久化之間redis發生故障,會發生數據丟失,數據的準確性不高。 AOF:AOF持久化方式記錄每次對服務器寫的操做,當服務器重啓的時候會從新執行這些命令來恢復原始的數據,AOF命令以redis協議追加保存每次寫的操做到文件末尾,Redis還能對AOF文件進行後臺重寫,使得AOF文件的體積不至於過大。 優勢:能夠保持更高的數據完整性,所以已成爲主流的持久化方案 缺點:AOF文件比RDB文件大,且恢復速度慢。

補充:redis如何壓縮AOF文件,具體過程以下: redis調用fork ,如今有父子兩個進程 <1>,子進程根據內存中的數據庫快照,往臨時文件中寫入重建數據庫狀態的命令 <2>,父進程繼續處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證若是子進程重寫失敗的話並不會出問題。 <3>,當子進程把快照內容寫入已命令方式寫到臨時文件中後,子進程發信號通知父進程。而後父進程把緩存的寫命令也寫入到臨時文件。 <4>,如今父進程可使用臨時文件替換老的aof文件,並重命名,後面收到的寫命令也開始往新的aof文件中追加。 <5>,須要注意到是重寫aof文件的操做,並無讀取舊的aof文件,而是將整個內存中的數據庫內容用命令的方式重寫了一個新的aof文件,這點和快照有點相似。

簡單總結:如何縮小AOF文件大小:文件重寫是指按期重寫AOF文件(產生新的AOF文件),減少AOF文件的體積。須要注意的是,AOF重寫是把Redis進程內的數據轉化爲寫命令,同步到新的AOF文件(爲了壓縮aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis將使用與快照相似的方式將內存中的數據 以命令的方式保存到臨時文件中,最後替換原來的文件)

10,棧和隊列(todo)

參考:www.cnblogs.com/smyhvae/p/4…

11,jvm垃圾回收算法 <1>標記-清除算法 這種垃圾回收一次回收分爲兩個階段:標記、清除。首先標記全部須要回收的對象,在標記完成後回收全部被標記的對象。這種回收算法會產生大量不連續的內存碎片,當要頻繁分配一個大對象時,jvm在新生代中找不到足夠大的連續的內存塊,會致使jvm頻繁進行內存回收(目前有機制,對大對象,直接分配到老年代中) 場景:若是沒有Survivor,eden區gc後會產生不少碎片。 <2>複製算法 這種算法會將內存劃分爲兩個相等的塊,每次只使用其中一塊。當這塊內存不夠使用時,就將還存活的對象複製到另外一塊內存中,而後把這塊內存一次清理掉。這樣作的效率比較高,也避免了內存碎片。可是這樣內存的可以使用空間減半,是個不小的損失。 場景:在發生young gc的時候,Survivor0和Survivor1之間的數據相互複製遷移。

<3>標記-整理算法(標記壓縮法) 這是標記-清除算法和複製算法的綜合版,在完成標記階段後,不是直接對可回收對象進行清理,而是讓存活對象向着一端移動,而後清理掉邊界之外的內存,避免產生內存碎片。

<4>分代收集算法 這是對上面三種算法的綜合應用,而且採起分代處理,當前商業虛擬機都採用這種算法。首先根據對象存活週期的不一樣將內存分爲幾塊即新生代、老年代,而後根據不一樣年代的特色,採用不一樣的收集算法。

補充:

1,Java GC如何判斷對象是否爲垃圾(www.cnblogs.com/hzzjj/p/626… <1>引用計數法,引用計數法就是若是一個對象沒有被任何引用指向,則可視之爲垃圾。這種方法的缺點就是不能檢測到環的存在。 <2>根搜索算法,主流的虛擬機都是採用GC Roots Tracing算法,好比Sun的Hotspot虛擬機即是採用該算法。 該算法的核心算法是從GC Roots對象做爲起始點,利用數學中圖論知識,圖中可達對象即是存活對象,而不可達對象則是須要回收的垃圾內存。 那麼能夠做爲GC Roots的對象: A,虛擬機棧的棧幀的局部變量表所引用的對象; B,本地方法棧的JNI所引用的對象; C,方法區的靜態變量和常量所引用的對象;

垃圾回收器

Cms垃圾回收器:CMS(Concurrent Mark-Sweep)是以犧牲吞吐量爲代價來得到最短回收停頓時間的垃圾回收器,老年代的回收採用CMS,使用算法:標記清除法(參考:blog.csdn.net/hutongling/…

工做過程: <1>初始標記:在這個階段,須要虛擬機停頓正在執行的任務,官方的叫法STW(Stop The Word),因此這個過程雖然暫停了整個JVM,可是很快就完成了。初始標記也就是標記一下GC roots 關聯到的對象。(並非全部活動對象)。 <2>併發標記:這個階段緊隨初始標記階段,在初始標記的基礎上繼續向下追溯標記。併發標記階段,應用程序的線程和併發標記的線程併發執行,因此用戶不會感覺到停頓。併發標記就須要標記出GC roots關聯到的對象的引用對象有哪些。好比說 A -> B (A 引用 B,假設 A是GC Roots關聯到的對象),那麼這個階段就是標記出B對象,A對象會在初始標記中標記出來。 <3>從新標記(也會stw),以前在併發標記時,由於是GC和用戶程序是併發執行的,可能致使一部分已經標記爲 從GC Roots不可達 的對象,由於用戶程序的(併發)運行,又可達了(對象被複活了),Remark的做用就是將這部分對象又標記爲 可達對象。 <4>併發清理:清理垃圾對象,這個階段收集器線程和應用程序線程併發執行。 初始標記和從新標記都要stop the world

從新標記(Remark) 的做用在於: 以前在併發標記時,由於是 GC 和用戶程序是併發執行的,可能致使一部分已經標記爲 從 GC Roots 不可達 的對象,由於用戶程序的(併發)運行,又可達 了,Remark 的做用就是將這部分對象又標記爲 可達對象。 至於 「浮動垃圾」,由於 CMS 在 併發標記 時是併發的,GC 線程和用戶線程併發執行,這個過程固然可能會由於線程的交替執行而致使新產生的垃圾(即浮動垃圾)沒有被標記到;而 從新標記 的做用只是修改以前 併發標記 所得到的不可達對象,因此是沒有辦法處理 「浮動垃圾」 的。

Cms的缺點: <1>CMS不會整理和壓縮堆空間,致使產生問題:通過CMS收集的堆會產生空間碎片(不壓縮空間是爲了響應更快),典型的空間換時間(實際能夠配置通過多少次old gc以後整理old區,減小內存碎片) <2>須要更多的CPU資源,不直接使用多線程,直接利用多核cpu。 <3>CMS的另外一個缺點是它須要更大的堆空間,爲了保證在CMS回收完堆以前還有空間分配給正在運行的應用程序,CMS不會在老年代滿的時候纔開始收集,在空間到了68%就開始回收。 <4>cms會產生浮動垃圾,因爲在應用運行的同時進行垃圾回收,因此有些垃圾可能在垃圾回收進行完成時產生,這樣就形成了「Floating Garbage」,這些垃圾須要在下次垃圾回收週期時才能回收掉。因此,併發收集器通常須要20%的預留空間用於這些浮動垃圾。

G1垃圾回收器,回收算法是:標記整理,減小內存碎片化

G1是在JDK 7u4版本以後發佈的垃圾收集器,並在jdk9中成爲默認垃圾收集器,G1也是利用多CPU來縮短stop the world時間(弱化了分代的概念,g1能同時做用於新生代和老年代),而且是高效的併發垃圾收集器。可是G1再也不像上文所述的垃圾收集器,須要分代配合不一樣的垃圾收集器,由於G1中的垃圾收集區域是「分區」(Region)的。G1的分代收集和以上垃圾收集器不一樣的就是除了有年輕代的ygc,全堆掃描的fullgc外,還有包含全部年輕代以及部分老年代Region的MixedGC。G1的優點還有能夠經過調整參數,指定垃圾收集的最大容許pause time。下面會詳細闡述下G1分區以及分代的概念,以及G1 GC的幾種收集過程的分類。

G1的優勢: 0. 弱化young區和old區概念,把堆區分爲若干小的區域region,這些region 可分爲eden,surival,old,humergus,默認分爲2048個區,每塊1~32M 1,同時對多個regoin進行併發標記,每次GC不須要清理所有的堆區域,只須要清理垃圾最多的分區,這樣能夠減小stw的時間,提高了jvm的響應性能。 2,不一樣區域region不須要連續。活躍數據在不一樣region複製和移動。 3,單個大對象能夠存放多個humergus之間,能夠有效利用空間。 4,G1根據回收時間的可預計性(時間可配置),一次回收必定數量的region,減小stw時間。

參考:www.cnblogs.com/niejunlei/p…

備註:G1的收集都是STW的,但年輕代和老年代的收集界限比較模糊,採用了混合(mixed)收集的方式。即每次收集既可能只收集年輕代分區(年輕代收集),也可能在收集年輕代的同時,包含部分老年代分區(混合收集),這樣即便堆內存很大時,也能夠限制收集範圍,從而下降停頓。G1採用內存分區(Region)的思路,將內存劃分爲一個個相等大小的內存分區,回收時則以分區爲單位進行回收,存活的對象複製到另外一個空閒分區中。因爲都是以相等大小的分區爲單位進行操做,所以G1自然就是一種壓縮方案(局部壓縮)。

G1的工做流程: 1:初始標記,STW。基於yong GC,標記survivor中可能引用老年代對象的對象,做爲Root Region,並掃描 2:併發標記:貫穿整個堆內存,標記活躍對象,並當即清除,同時收集活躍對象統計信息。 3:從新標記:使用snapshot-at-the-beginning(SATB),移除,回收標記的空region。STW 4:清理/複製,G1選擇最不活躍的region,以便最快收集。這些區域能夠和yong GC同時收集,STW 清理:統計活躍對象,活躍區域(STW)=》清理RSet(STW)=》重置空的region=》歸還到free list(併發)。 複製:移動活躍對象到未應用的區域(STW)

發生full gc條件:當收集垃圾,從一個區域複製數據到另外一個區域時,找不到可用區域。

12,MySql索引

<1>InnoDB(b+樹,聚簇索引):支持事務處理,支持外鍵,支持崩潰修復能力和併發控制。若是須要對事務的完整性要求比較高(好比銀行),要求實現併發控制(好比售票),那選擇InnoDB有很大的優點。若是須要頻繁的更新、刪除操做的數據庫,也能夠選擇InnoDB,由於支持事務的提交(commit)和回滾(rollback)(用於事務處理應用程序,具備衆多特性,包括ACID事務支持。(提供行級鎖))

<2>MyISAM(b+樹,非聚簇索引):插入數據快,空間和內存使用比較低。若是表主要是用於插入新記錄和讀出記錄,那麼選擇MyISAM能實現處理高效率。若是應用的完整性、併發性要求比較低,也可使用(MyISAM類型不支持事務處理等高級處理,所以也不支持數據的回滾修復)。

<3>MEMORY(hash結構):全部的數據都在內存中,數據的處理速度快,可是安全性不高。若是須要很快的讀寫速度,對數據的安全性要求較低,能夠選擇MEMOEY。它對錶的大小有要求,不能創建太大的表。因此,這類數據庫只使用在相對較小的數據庫表。

爲何使用b+樹? 在範圍查詢方面,B+樹的優點更加明顯,B樹的範圍查找須要不斷進行遍歷查找每一個數據(查一次,進行一次io)。首先二分查找到範圍下限,在不斷經過遍歷,知道查找到範圍的上限便可。整個過程比較耗時,而B+樹的範圍查找則簡單了許多。首先經過二分查找,找到範圍下限,而後同過葉子結點的鏈表順序遍歷,直至找到上限便可,整個過程簡單許多,效率也比較高。(b+樹的數據都分佈在子節點上)。B+樹的空間相對比較大,典型的空間換時間。

13,tomcat類加載器 在tomcat7版本下,Tomcat本身定義了兩個核心類加載器,JasperLoader負責加載jsp文件通過編譯生成的jsp類,該類加載器打破了雙親委託機制(爲何打破雙親委託機制?實現jsp文件的熱部署,若是不打破雙親委託,以前的jsp部署一次,就會被持續運行,因爲父類加載器一直存在,沒法再次熱部署了)。webappclassLoader只負責加載web項目下的lib類(分析源碼並無打破雙親委託機制)

14,內存泄漏,什麼狀況下會發生,如何排查? 指的是申請的內存空間,本身沒有去主動釋放,gc也沒法釋放(如強引用),屢次內存泄漏,就會致使內存溢出,在生成環境中對某些大對象的不合理使用(沒有用的對象沒被釋放),致使gc回收不了。 如何排查:用jmap生成堆內存信息文件(常說的內存dump), MAT(Memory Analyzer Tool),Eclipse提供的一款用於Heap Dump文件的工具,MAT分析堆信息文件,包括整個堆內存的大小、類(Class)的數量、對象(Object)的數量、類加載器(Class Loader)的數量、以及所佔的空間大小。 補充:jstack查看線程堆棧信息,排查死鎖用的比較多。

16,spi,service provice interface,實際上是一套協議,使用者不用關注其實現,各個功能實現者針對標準進行實現就能夠了,好比jdbc的數據庫驅動,各個廠商有本身的具體實現

相關文章
相關標籤/搜索