34: 垃圾回收的原理
優勢:a.不須要考慮內存管理, b.能夠有效的防止內存泄漏,有效的利用可以使用的內存, c.因爲有垃圾回收機制,Java中的對象再也不有"做用域"的概念,只有對象的引用纔有"做用域"
原理:垃圾回收器是做爲一個單獨的低級別的線程運行,在不可知的狀況下對內存堆中已死亡的或者長期沒有使用的對象回收,可是不能實時的對某一對象或者全部對象進行垃圾回收。
垃圾回收機制:分代複製垃圾回收、標記垃圾回收、增量垃圾回收
35: 你寫過Java的Web系統
36: 簡單介紹一下你的項目
37: 兩個有序的數組,合成一個有序的數組,怎麼合併效率高
歸併排序
38: 淘寶的登錄頁面,怎麼保證他安全
使用哈希加鹽法來爲密碼加密
解決的辦法是將密碼加密後再存儲進數據庫,比較經常使用的加密方法是使用哈希函數(Hash Function)。哈希函數的具體定義,你們能夠在網上或者相關書籍中查閱到,簡單地說,它的特性以下:
(1)原始密碼經哈希函數計算後獲得一個哈希值
(2)改變原始密碼,哈希函數計算出的哈希值也會相應改變
(3) 一樣的密碼,哈希值也是相同的
(4) 哈希函數是單向、不可逆的。也就是說從哈希值,你沒法推算出原始的密碼是多少
最簡單、常見的破解方式當屬字典破解(Dictionary Attack)和暴力破解(Brute Force Attack)方式。這兩種方法說白了就是猜密碼。
字典破解和暴力破解都是效率比較低的破解方式。若是你知道了數據庫中密碼的哈希值,你就能夠採用一種更高效的破解方式,查表法(Lookup Tables)。還有一些方法,好比逆向查表法(Reverse Look up Tables)、彩虹表(Rainbow Tables)等,都和查表法大同小異。如今咱們來看一下查表法的原理。java
查表法不像字典破解和暴力破解那樣猜密碼,它首先將一些比較經常使用的密碼的哈希值算好,而後創建一張表,固然密碼越多,這張表就越大。當你知道某個密碼的哈希值時,你只須要在你創建好的表中查找 該哈希值,若是找到了,你就知道對應的密碼了。node
從上面的查表法能夠看出,即使是將原始密碼加密後的哈希值存儲在數據庫中依然是不夠安全的。那麼有什麼好的辦法來解決這個問題呢?答案是加鹽。mysql
鹽(Salt)是什麼?就是一個隨機生成的字符串。咱們將鹽與原始密碼鏈接(concat)在一塊兒(放在前面或後面均可以),而後將concat後的字符串加密。採用這種方式加密密碼,查表法就不靈了(由於鹽是隨機生成的)。 nginx
單單使用哈希函數來爲密碼加密是不夠的,須要爲密碼加鹽來提升安全性,鹽的長度不能太短,而且鹽的產生應該是隨機的。git
39: 你有最新半年用戶的訂單,天天的用戶訂單量有上億,預測下將來一週哪些商品最容易被購買
數據建模-分析
40: 你有啥問題
「入職後有沒有培訓活動?」
「公司對個人指望是什麼?」
「這個部門或團隊有多少人?主要是負責哪方面的?」
一、synchronized關鍵字原理?
原理:synchronized底層是經過一個monitor的對象阻塞和獲取。
對代碼同步:指令執行時,monitor的進入數減1,若是減1後進入數爲0,那線程退出monitor,再也不是這個monitor的全部者。其餘被這個monitor阻塞的線程能夠嘗試去獲取這個 monitor 的全部權。
對方法同步:常量池中多了ACC_SYNCHRONIZED標示符。JVM就是根據該標示符來實現方法的同步的:當方法調用時,調用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標誌是否被設置,若是設置了,執行線程將先獲取monitor,獲取成功以後才能執行方法體,方法執行完後再釋放monitor。在方法執行期間,其餘任何線程都沒法再得到同一個monitor對象。
重量級鎖:Mutex Lock 監視器鎖monitor本質就是依賴於底層的操做系統的Mutex Lock來實現的。
二、hashMap底層實現。
首先,HashMap 是 Map 的一個實現類,它表明的是一種鍵值對的數據存儲形式。Key 不容許重複出現,Value 隨意。jdk 8 以前,其內部是由數組+鏈表來實現的,而 jdk 8 對於鏈表長度超過 8 的鏈表將轉儲爲紅黑樹。大體的數據存儲形式以下:
從上圖中能夠看出,HashMap底層就是一個數組結構,數組中的每一項又是一個鏈表。當新建一個HashMap的時候,就會初始化一個數組。
源碼以下:
Java代碼
- /**
- * The table, resized as necessary. Length MUST Always be a power of two.
- */
- transient Entry[] table;
-
- static class Entry<K,V> implements Map.Entry<K,V> {
- final K key;
- V value;
- Entry<K,V> next;
- final int hash;
- ……
- }
能夠看出,Entry就是數組中的元素,每一個 Map.Entry 其實就是一個key-value對,它持有一個指向下一個元素的引用,這就構成了鏈表。
Java代碼
- public V put(K key, V value) {
- // HashMap容許存放null鍵和null值。
- // 當key爲null時,調用putForNullKey方法,將value放置在數組第一個位置。
- if (key == null)
- return putForNullKey(value);
- // 根據key的keyCode從新計算hash值。
- int hash = hash(key.hashCode());
- // 搜索指定hash值在對應table中的索引。
- int i = indexFor(hash, table.length);
- // 若是 i 索引處的 Entry 不爲 null,經過循環不斷遍歷 e 元素的下一個元素。
- for (Entry<K,V> e = table[i]; e != null; e = e.next) {
- Object k;
- if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
- V oldValue = e.value;
- e.value = value;
- e.recordAccess(this);
- return oldValue;
- }
- }
- // 若是i索引處的Entry爲null,代表此處尚未Entry。
- modCount++;
- // 將key、value添加到i索引處。
- addEntry(hash, key, value, i);
- return null;
- }
當咱們往HashMap中put元素的時候,先根據key的hashCode從新計算hash值,根據hash值獲得這個元素在數組中的位置(即下標),若是數組該位置上已經存放有其餘元素了,那麼在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最早加入的放在鏈尾。若是數組該位置上沒有元素,就直接將該元素放到此數組中的該位置上。
採用迭代器遍歷,不只適用於HashMap,對其它類型的容器一樣適用。
採用這種方法的遍歷,能夠用下文說起的方式安全地對HashMap內的元素進行修改,並不會對後續的刪除操做形成影響。
若是使用foreach遍歷方法刪除HashMap中的元素,Java頗有可能會在運行時拋出異常。
爲何呢?
一、使用iterator迭代刪除時沒有問題的,在每一次迭代時都會調用hasNext()方法判斷是否有下一個,是容許集合中數據增長和減小的。
二、使用forEach刪除時,會報錯ConcurrentModificationException,由於在forEach遍歷時,是不容許map元素進行刪除和增長。
因此,遍歷刪除map集合中元素時,必須使用迭代iterator
若是不是2的2次冪,空間浪費至關大,更糟的是這種狀況中,數組能夠使用的位置比數組長度小了不少,這意味着進一步增長了碰撞的概率,減慢了查詢的效率!而當數組長度爲16時,即爲2的n次方時,2n-1獲得的二進制數的每一個位上的值都爲1,這使得在低位上&時,獲得的和原hash的低位相同,加之hash(int h)方法對key的hashCode的進一步優化,加入了高位計算,就使得只有相同的hash值的兩個值纔會被放到數組中的同一個位置上造成鏈表。
HashMap 包含以下幾個構造器:
HashMap():構建一個初始容量爲 16,負載因子爲 0.75 的 HashMap。
HashMap(int initialCapacity):構建一個初始容量爲 initialCapacity,負載因子爲 0.75 的 HashMap。
HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的負載因子建立一個 HashMap。
HashMap的基礎構造器HashMap(int initialCapacity, float loadFactor)帶有兩個參數,它們是初始容量initialCapacity和加載因子loadFactor。
initialCapacity:HashMap的最大容量,即爲底層數組的長度。
loadFactor:負載因子loadFactor定義爲:散列表的實際元素數目(n)/ 散列表的容量(m)。
紅黑樹本質上是一種二叉查找樹,但它在二叉查找樹的基礎上額外添加了一個標記(顏色),同時具備必定的規則。這些規則使紅黑樹保證了一種平衡,插入、刪除、查找的最壞時間複雜度都爲 O(logn)。
三、TCP與UDP的區別
一、基於鏈接與無鏈接
二、TCP要求系統資源較多,UDP較少;
三、UDP程序結構較簡單
四、流模式(TCP)與數據報模式(UDP);
五、TCP保證數據正確性,UDP可能丟包
六、TCP保證數據順序,UDP不保證
四、TCP三次握手說一下。
簡單說,讓雙方都證明對方能發收。
知道對方能收是由於收到對方的由於收到而發的迴應。
具體:
1:A發,B收, B知道A能發
2:B發,A收, A知道B能發收
3:A發,B收, B知道A能收
五、看你項目用到線程池,說一下線程池工做原理,任務拒接策略有哪幾種?
一個線程從被提交(submit)到執行共經歷如下流程:
- 線程池判斷核心線程池裏是的線程是否都在執行任務,若是不是,則建立一個新的工做線程來執行任務。若是核心線程池裏的線程都在執行任務,則進入下一個流程
- 線程池判斷工做隊列是否已滿。若是工做隊列沒有滿,則將新提交的任務儲存在這個工做隊列裏。若是工做隊列滿了,則進入下一個流程。
- 線程池判斷其內部線程是否都處於工做狀態。若是沒有,則建立一個新的工做線程來執行任務。若是已滿了,則交給飽和策略來處理這個任務。
任務拒接策略?
有4種內置的實現策略和一個用戶自定義拒絕策略。
AbortPolicy 爲java線程池默認的阻塞策略,不執行此任務,並且直接拋出一個運行時異常,切記ThreadPoolExecutor.execute須要try catch,不然程序會直接退出。
DiscardPolicy 直接拋棄,任務不執行,空方法 。
DiscardOldestPolicy 從隊 列裏面拋棄head的一個任務,並再次execute 此task。
CallerRunsPolicy 在調用execute的線程裏面執行此command,會阻塞入口 。
用戶自定義拒絕策略 實現RejectedExecutionHandler,並本身定義策略模式。
再次須要注意的是,ThreadPoolExecutor.submit() 函數,此方法內部調用的execute方法,並把execute執行完後的結果給返回,但若是任務並無執行的話(被拒絕了),則submit返回的future.get()會一直等到。
future 內部其實仍是一個runnable,並把command給封裝了下,當command執行完後,future會返回一個值。
六、進程和線程的區別?
進程和線程的主要差異在於它們是不一樣的操做系統資源管理方式。進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不一樣執行路徑。線程有本身的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,因此多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對於一些要求同時進行而且又要共享某些變量的併發操做,只能用線程,不能用進程。
1) 簡而言之,一個程序至少有一個進程,一個進程至少有一個線程.
2) 線程的劃分尺度小於進程,使得多線程程序的併發性高。
3) 另外,進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存,從而極大地提升了程序的運行效率。
4) 線程在執行過程當中與進程仍是有區別的。每一個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口。可是線程不可以獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。
5) 從邏輯角度來看,多線程的意義在於一個應用程序中,有多個執行部分能夠同時執行。但操做系統並無將多個線程看作多個獨立的應用,來實現進程的調度和管理以及資源分配。這就是進程和線程的重要區別。
七、ArrayList與LinkedList的區別?
一、ArrayList是實現了基於動態數組的數據結構,LinkedList基於鏈表的數據結構。 (LinkedList是雙向鏈表,有next也有previous)
二、對於隨機訪問get和set,ArrayList以爲優於LinkedList,由於LinkedList要移動指針。
三、對於新增和刪除操做add和remove,LinedList比較佔優點,由於ArrayList要移動數據。
八、線程安全與非線程安全集合說一下,底層怎麼實現的(hashmap,concurrenthashmap)?
Hashmap本質是數組加鏈表。根據key取得hash值,而後計算出數組下標,若是多個key對應到同一個下標,就用鏈表串起來,新插入的在前面。
ConcurrentHashMap:在hashMap的基礎上,ConcurrentHashMap將數據分爲多個segment,默認16個(concurrency level),而後每次操做對一個segment加鎖,避免多線程鎖的概率,提升併發效率。
九、Hashtable、ConcurrentHashMap、TreeMap、HashMap的key,value都是不爲空的嗎?
HashMap的key和value都容許爲空,treeMap的value容許爲空。
十、單例模式
5種
1.餓漢模式(調用效率高,可是不能延時加載):
2.懶漢模式(調用效率不高,可是能延時加載):
3.雙重檢測鎖模式(因爲JVM底層模型緣由,偶爾會出問題,不建議使用):
4.靜態內部類式(線程安全,調用效率高,能夠延時加載):
5.枚舉類(線程安全,調用效率高,不能延時加載,能夠自然的防止反射和反序列化調用):
如何選用:
-單例對象 佔用資源少,不須要延時加載,枚舉 好於 餓漢
-單例對象 佔用資源多,須要延時加載,靜態內部類 好於 懶漢式
不安全 。
double-check 雙重檢查鎖定 。
能保證被它修飾的成員變量能夠被多個線程正確的處理。Volatile是輕量級的synchronized,它在多處理器開發中保證了共享變量的「可見性」。可見性的意思是當一個線程修改一個共享變量時,另一個線程能讀到這個修改的值。它在某些狀況下比synchronized的開銷更小
十一、 判斷一個字符在一個字符串中出現的次數 ?
StringUtils.countMatches(str, t);
十二、HashMap是否是有序的?
不是有序的.
有TreeMap和LinkedHashMap。
- TreeMap和LinkedHashMap是如何保證它的順序的?
LinkedHashMap 是根據元素增長或者訪問的前後順序進行排序,而 TreeMap是基於元素的固有順序 (由 Comparator 或者 Comparable 肯定)。
TreeMap TreeMap則實現了 SortedMap 接口。
參照TreeMap的value排序,咱們同樣的也能夠實現HashMap的排序。
1三、實現全部的線程一塊兒等待某個事件的發生,當某個事件發生時,全部線程一塊兒開始往下執行的話,有什麼好的辦法嗎?
柵欄(Java的併發包中的CyclicBarrier) CountDownLatch CyclicBarrier Semaphore
- CountDownLatch (N個線程數量count減爲0 主程序或一組程序開始執行)
CountDownLatch是一個計數器閉鎖,主要的功能就是經過await()方法來阻塞住當前線程,而後等待計數器減小到0了,再喚起這些線程繼續執行。
這個類裏主要有兩個方法,一個是向下減計數器的方法:countdown(),若是取得當前的狀態爲0,說明這個鎖已經結束,直接返回false;
若是沒有結束,而後去設置計數器減1,若是compareAndSetState不成功,則繼續循環執行。 而其中的一直等待計數器歸零的方法是await()。
- CyclicBarrier(N個線程,他們之間任何一個沒有完成,全部的線程都必須等待)
CyclicBarrier是加計數方式,計數達到構造方法中參數指定的值時釋放全部等待的線程。
- Semaphore(Semaphore 是隻容許必定數量的線程同時執行一段任務。)
Semaphore,每次semaphore.acquire(),獲取一個資源,每次semaphore.acquire(n),獲取n個資源,
當達到semaphore 指定資源數量時就不能再訪問線程處於阻塞,必須等其它線程釋放資源,semaphore.relase()每次資源一個資源,
semaphore.relase(n)每次資源n個資源。
你知道它的實現原理嗎?
繼續問,你還知道其它的實現方式嗎?
繼續問,你以爲這些方式裏哪一個方式更好?
若是讓你來寫的話,你以爲還有比它更好的實現方式嗎?
1四、IO包和NIO包 熟悉嗎?
- NIO模型 其中的selector 職責和實現原理
傳統的socket IO中,須要爲每一個鏈接建立一個線程,當併發的鏈接數量很是巨大時,線程所佔用的棧內存和CPU線程切換的開銷將很是巨大。使用NIO,再也不須要爲每一個線程建立單獨的線程,能夠用一個含有限數量線程的線程池,甚至一個線程來爲任意數量的鏈接服務。因爲線程數量小於鏈接數量,因此每一個線程進行IO操做時就不能阻塞,若是阻塞的話,有些鏈接就得不處處理,NIO提供了這種非阻塞的能力。
一、增長了一個角色,要有一個專門負責收集客人需求的人。NIO裏對應的就是Selector。
二、由阻塞服務方式改成非阻塞服務了,客人吃着的時候服務員不用一直侯在客人旁邊了。傳統的IO操做,好比read(),當沒有數據可讀的時候,線程一直阻塞被佔用,直到數據到來。NIO中沒有數據可讀時,read()會當即返回0,線程不會阻塞。
NIO中,客戶端建立一個鏈接後,先要將鏈接註冊到Selector,至關於客人進入餐廳後,告訴前臺你要用餐,前臺會告訴你你的桌號是幾號,而後你就可能到那張桌子坐下了,SelectionKey就是桌號。當某一桌須要服務時,前臺就記錄哪一桌須要什麼服務,好比1號桌要點菜,2號桌要結賬,服務員從前臺取一條記錄,根據記錄提供服務,完了再來取下一條。這樣服務的時間就被最有效的利用起來了。
Selector類是NIO的核心類,Selector可以檢測多個註冊的通道上是否有事件發生,若是有事件發生,便獲取事件而後針對每一個事件進行相應的響應處理。這樣一來,只是用一個單線程就能夠管理多個通道,也就是管理多個鏈接。這樣使得只有在鏈接真正有讀寫事件發生時,纔會調用函數來進行讀寫,就大大地減小了系統開銷,而且沒必要爲每一個鏈接都建立一個線程,不用去維護多個線程,而且避免了多線程之間的上下文切換致使的開銷。
與Selector有關的一個關鍵類是SelectionKey,一個SelectionKey表示一個到達的事件,這2個類構成了服務端處理業務的關鍵邏輯。
1五、虛擬機JVM 組成部分
程序計數器
|
指示當前程序執行到了哪一行,執行JAVA方法時紀錄正在執行的虛擬機字節碼指令地址;執行本地方法時,計數器值爲undefined
|
虛擬機棧
|
用於執行JAVA方法。棧幀存儲局部變量表、操做數棧、動態連接、方法返回地址和一些額外的附加信息。程序執行時棧幀入棧;執行完成後棧幀出棧
|
本地方法棧
|
用於執行本地方法,其它和虛擬機棧相似
|
着重說一下虛擬機棧中的局部變量表,裏面存放了三個信息:
- 各類基本數據類型(boolean、byte、char、short、int、float、long、double)
- 對象引用(reference)
- returnAddress地址
這個returnAddress和程序計數器有什麼區別?前者是指示JVM的指令執行到哪一行,後者則是你的代碼執行到哪一行。
私有內存區伴隨着線程的產生而產生,一旦線程停止,私有內存區也會自動消除,所以討論的內存回收主要是針對共享內存區。
JAVA堆
既然GC主要發生在堆內存中,這部分咱們會對堆內存進行比較詳細的描述。
堆內存是由存活和死亡的對象組成的。存活的對象是應用能夠訪問的,不會被垃圾回收。死亡的對象是應用不可訪問尚且尚未被垃圾收集器回收掉的對象。一直到垃圾收集器把這些對象回收掉以前,他們會一直佔據堆內存空間。堆是應用程序在運行期請求操做系統分配給本身的向高地址擴展的數據結構,是不連續的內存區域。用一句話總結堆的做用:程序運行時動態申請某個大小的內存空間。
新生代:剛剛新建的對象在Eden中,經歷一次Minor GC,Eden中的存活對象就會被移動到第一塊survivor space S0,Eden被清空;等Eden區再滿了,就再觸發一次Minor GC,Eden和S0中的存活對象又會被複制送入第二塊survivor space S1。S0和Eden被清空,而後下一輪S0與S1交換角色,如此循環往復。若是對象的複製次數達到16次,該對象就會被送到老年代中。
設置兩個Survivor區最大的好處就是解決了碎片化
老年代:若是某個對象經歷了幾回垃圾回收以後還存活,就會被存放到老年代中。老年代的空間通常比新生代大。
GC名稱
|
介紹
|
Minor GC
|
發生在新生代,頻率高,速度快(大部分對象活不過一次Minor GC)
|
Major GC
|
發生在老年代,速度慢
|
Full GC
|
清理整個堆空間
|
JAVA 並無給咱們提供明確的代碼來標註一塊內存並將其回收。或許你會說,咱們能夠將相關對象設爲 null 或者用 System.gc()。然而,後者將會嚴重影響代碼的性能,由於通常每一次顯式的調用 system.gc() 都會中止全部響應,去檢查內存中是否有可回收的對象。這會對程序的正常運行形成極大的威脅。另外,調用該方法並不能保證 JVM 當即進行垃圾回收,僅僅是通知 JVM 要進行垃圾回收了,具體回收與否徹底由 JVM 決定。這樣作是費力不討好。
一、追蹤回收算法(tracing collector)
從根結點開始遍歷對象的應用圖。同時標記遍歷到的對象。遍歷完成後,沒有被標記的對象就是目前未被引用,能夠被回收。
二、壓縮回收算法(Compacting Collector)
把堆中活動的對象集中移動到堆的一端,就會在堆的另外一端流出很大的空閒區域。這種處理簡化了消除碎片的工做,但可能帶來性能的損失。
三、複製回收算法(Coping Collector)
把堆均分紅兩個大小相同的區域,只使用其中的一個區域,直到該區域消耗完。此時垃圾回收器終端程序的執行,經過遍歷把全部活動的對象複製到另外一個區域,複製過程當中它們是緊挨着佈置的,這樣也能夠達到消除內存碎片的目的。複製結束後程序會繼續運行,直到該區域被用完。
可是,這種方法有兩個缺陷:
對於指定大小的堆,須要兩倍大小的內存空間,
須要中斷正在執行的程序,下降了執行效率
四、按代回收算法(Generational Collector)
爲何要按代進行回收?這是由於不一樣對象生命週期不一樣,每次回收都要遍歷全部存活對象,對於整個堆內存進行回收無疑浪費了大量時間,對症下藥能夠提升垃圾回收的效率。主要思路是:把堆分紅若搞個子堆,每一個子堆視爲一代,算法在運行的過程當中優先收集「年幼」的對象,若是某個對象通過屢次回收仍然「存活」,就移動到高一級的堆,減小對其掃描次數。
串行回收器(serial collector)
並行回收器
CMS回收器
G1回收器
JAVA性能優化
真正影響JAVA程序性能的,就是碎片化。碎片是JAVA堆內存中的空閒空間,多是TLAB剩餘空間,也多是被釋放掉的具備較長生命週期的小對象佔用的空間。
- 減小new對象。每次new對象以後,都要開闢新的內存空間。這些對象不被引用以後,還要回收掉。所以,若是最大限度地合理重用對象,或者使用基本數據類型替代對象,都有助於節省內存;
- 多使用局部變量,減小使用靜態變量。局部變量被建立在棧中,存取速度快。靜態變量則是在堆內存;
- 避免使用finalize,該方法會給GC增添很大的負擔;
- 若是是單線程,儘可能使用非多線程安全的,由於線程安全來自於同步機制,同步機制會下降性能。例如,單線程程序,能使用HashMap,就不要用HashTable。同理,儘可能減小使用synchronized
- 用移位符號替代乘除號。eg:a*8應該寫做a<<3
- 對於常常反覆使用的對象使用緩存;
- 儘可能使用基本類型而不是包裝類型,儘可能使用一維數組而不是二維數組;
- 儘可能使用final修飾符,final表示不可修改,訪問效率高
- 單線程狀況下(或者是針對於局部變量),字符串儘可能使用StringBuilder,比StringBuffer要快;
- String爲何慢?由於String 是不可變的對象, 所以在每次對 String 類型進行改變的時候其實都等同於生成了一個新的 String 對象,而後將指針指向新的 String 對象。若是不能保證線程安全,儘可能使用StringBuffer來鏈接字符串。這裏須要注意的是,StringBuffer的默認緩存容量是16個字符,若是超過16,apend方法調用私有的expandCapacity()方法,來保證足夠的緩存容量。所以,若是能夠預設StringBuffer的容量,避免append再去擴展容量。若是能夠保證線程安全,就是用StringBuilder。
1六、ArrayList遍歷時正確刪除元素?
刪除元素請使用Iterator方式,若是併發操做,須要對Iterator對象加鎖。
1七、對一個List的進行subList後,原有list進行增、刪、改,再操做subList會怎樣?
子 List 的元素和原 List 中的後一部分是重合的, 而子 List 還在遍歷過程當中時, 向原 List 中新增元素, 這樣給子 List 的遍歷過程形成了干擾甚至困擾, 因而就拋出了併發修改異常將會拋出java.util.ConcurrentModificationException
1八、web應用安全問題?
一、跨站腳本攻擊(CSS or XSS, Cross Site Scripting)
方案:輸入或輸出時對其進行字符過濾或轉義處理。
2、SQL注入攻擊(SQL injection)
方案:輸入輸出都是過濾、合法性檢查和長度限制等通用方法。
3、遠程命令執行(Code execution,我的以爲譯成代碼執行並不確切)
方案:嚴格限制運行Web服務的用戶權限。
4、目錄遍歷(Directory traversal)
方案:一、一樣是限制Web應用在服務器上的運行 2、進行嚴格的輸入驗證,控制用戶輸入非法路徑。
5、文件包含(File inclusion)
方案:對文件來源進行審查
6、腳本代碼暴露(Script source code disclosure)
7、Http請求頭的額外的回車換行符注入(CRLF injection/HTTP response splitting)
8、跨幀腳本攻擊(Cross Frame Scripting)
9、PHP代碼注入(PHP code injection)
十、XPath injection
十一、Cookie篡改(Cookie manipulation)
十二、URL重定向(URL redirection)
1三、Blind SQL/XPath injection for numeric/String inputs
1四、Google Hacking
1五、表單、AJAX提交必須執行CSRF安全過濾。
1六、URL外部重定向傳入的目標地址必須執行白名單過濾。
1九、簡單介紹下spring的ioc和aop?
- 控制反轉(Inversion of Control,英文縮寫爲IOC);
ioc就是典型的工廠模式,經過sessionfactory去注入實例。依賴注入 。
本身實現用什麼方式? 反射原理
其實就是經過解析xml文件,經過反射建立出咱們所須要的bean,再將這些bean挨個放到集合中,而後對外提供一個getBean()方法,以便咱們得到這bean。
通俗來說就如同婚姻介紹所,只須要告訴它找個什麼樣的女友,而後婚介就會按照咱們的要求,提供一個mm,若是婚介給咱們的人選不符合要求,咱們就會拋出異常。
- 面向切面編程(Aspect Oriented Programming,英文縮寫爲AOP)
AOP就是典型的代理模式的體現。 實現攔截器 日誌 統一權限校驗 。
spring的IoC容器是spring的核心,spring AOP是spring框架的重要組成部分。
實現AOP的技術,主要分爲兩大類:一是採用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用靜態織入的方式,引入特定的語法建立「方面」,從而使得編譯器能夠在編譯期間織入有關「方面」的代碼.簡單點解釋,比方說你想在你的biz層全部類中都加上一個打印‘你好’的功能,這時就能夠用aop思想來作.你先寫個類寫個類方法,方法經實現打印‘你好’,而後Ioc這個類 ref=「biz.*」讓每一個類都注入便可實現。
20、併發問題
丟失更新 用戶A把6改爲2 用戶B把2改爲6 則用戶A丟失了更新
髒讀問題 用戶A,B 看到的都是6 用戶B把6改成2 則用戶A讀的值仍然是6
2一、樂觀鎖 悲觀鎖
悲觀 屏蔽一切違反數據操做完整性
樂觀 只是在提交的時候檢查是否違反數據完整性
2二、sql優化
- 複雜sql避免模糊匹配
- 索引問題 惟一索引 和普通索引
- 複雜操做
- 在能夠使用UNION ALL的語句裏,使用了UNION
- 字段長度小於5000用varchar,超過用TEXT,獨立一張表,用主鍵來對應。
- 在varchar字段上創建索引時,必須指定索引長度,不必對全字段創建索引。
2四、介紹下使用的持久層框架? 爲何要選擇這個(有什麼好處)?
MyBatis 是支持普通 SQL查詢,存儲過程和高級映射的優秀持久層框架。MyBatis 消除了幾乎全部的JDBC代碼和參數的手工設置以及結果集的檢索。
MyBatis 使用簡單的 XML或註解用於配置和原始映射,將接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄。
2五、mybatis $和#的區別
一、效果來看:SELECT * FROM USER WHERE ID = #{id} /${id} ,從效果上來看均可以替換成功,只不過前置默認會把傳入的參數當成字符串處理(它還能夠經過,jdbcType=Number等指明類型),後者只是簡單的替換(不加‘’號)。
二、概念上:${}是一個動態SQL編譯,#{}是預編譯
三、功能上:#{}由於是預編譯,因此能夠防止SQL注入,但${}不能。
四、使用上:#{}經常使用來處理參數傳入,${}用來處理動態SQL構造,如select * from ${}
五、原則:儘量的使用#{}
PS:
1. #將傳入的數據都當成一個字符串,會對自動傳入的數據加一個雙引號。如:order by #user_id#,若是傳入的值是111,那麼解析成sql時的值爲order by "111", 若是傳入的值是id,則解析成的sql爲order by "id".
2. $將傳入的數據直接顯示生成在sql中。如:order by $user_id$,若是傳入的值是111,那麼解析成sql時的值爲order by user_id, 若是傳入的值是id,則解析成的sql爲order by id.
3. #方式可以很大程度防止sql注入。
4.$方式沒法防止Sql注入。
5.$方式通常用於傳入數據庫對象,例如傳入表名.
6.通常能用#的就別用$.
2六、HashMap和Hashtable有什麼區別?
HashMap和Hashtable都實現了Map接口,所以不少特性很是類似。可是,他們有如下不一樣點:
HashMap容許鍵和值是null,而Hashtable不容許鍵或者值是null。
Hashtable是同步的,而HashMap不是。所以,HashMap更適合於單線程環境,而Hashtable適合於多線程環境。
HashMap提供了可供應用迭代的鍵的集合,所以,HashMap是快速失敗的。另外一方面,Hashtable提供了對鍵的列舉(Enumeration)。
通常認爲Hashtable是一個遺留的類。
2七、hashCode()和equals()方法的重要性體如今什麼地方?
經過hashCode和equals方法保證元素的惟一性,當重寫equals方法時,必須重寫hashCode方法,由於若是不重寫這兩個方法,就會默認使用Object的方法,通常是不相同的,因此就會致使存儲了重複值,與hashset、hashmap等性質衝突。
2八、Vector、ArrayList和LinkedList有什麼區別?
ArrayList和LinkedList都實現了List接口,他們有如下的不一樣點:
ArrayList是基於索引的數據接口,它的底層是數組。它能夠以O(1)時間複雜度對元素進行隨機訪問。與此對應,LinkedList是以元素列表的形式存儲它的數據,每個元素都和它的前一個和後一個元素連接在一塊兒,在這種狀況下,查找某個元素的時間複雜度是O(n)。
相對於ArrayList,LinkedList的插入,添加,刪除操做速度更快,由於當元素被添加到集合任意位置的時候,不須要像數組那樣從新計算大小或者是更新索引。
LinkedList比ArrayList更佔內存,由於LinkedList爲每個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。
也能夠參考ArrayList vs. LinkedList。
2九、數據庫事務及隔離級別說一下。
原子性(Atomicity )、一致性( Consistency )、隔離性或獨立性( Isolation)和持久性(Durabilily),簡稱就是ACID。
數據庫進行任何寫入操做的時候都是要先寫日誌的,一樣的道理,咱們在執行事務的時候數據庫首先會記錄下這個事務的redo操做日誌,而後纔開始真正操做數據庫,
在操做以前,首先會把日誌文件寫入磁盤,那麼當忽然斷電的時候,即便操做沒有完成,在從新啓動數據庫時候,數據庫會根據當前數據的狀況進行undo回滾或者是redo前滾,
這樣就保證了數據的強一致性。
① Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生。
② Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。
③ Read committed (讀已提交):可避免髒讀的發生。
④ Read uncommitted (讀未提交):最低級別,任何狀況都沒法保證。
30、synchronized和lock區別,可重入鎖與非可重入鎖的區別
- Lock是一個接口,而synchronized是Java中的關鍵字,synchronized是內置的語言實現;
- synchronized在發生異常時,會自動釋放線程佔有的鎖,所以不會致使死鎖現象發生;而Lock在發生異常時,若是沒有主動經過unLock()去釋放鎖,則極可能形成死鎖現象,所以使用Lock時須要在finally塊中釋放鎖;
- Lock可讓等待鎖的線程響應中斷,而synchronized卻不行,使用synchronized時,等待的線程會一直等待下去,不可以響應中斷;
- 經過Lock能夠知道有沒有成功獲取鎖,而synchronized卻沒法辦到。
- Lock能夠提升多個線程進行讀操做的效率。
- 在性能上來講,若是競爭資源不激烈,二者的性能是差很少的,而當競爭資源很是激烈時(即有大量線程同時競爭),此時Lock的性能要遠遠優於synchronized。因此說,在具體使用時要根據適當狀況選擇。
可重入鎖,也叫作遞歸鎖,指的是同一線程 外層函數得到鎖以後 ,內層遞歸函數仍然有獲取該鎖的代碼,但不受影響。
在JAVA環境下 ReentrantLock 和synchronized 都是 可重入鎖。可重入鎖最大的做用是避免死鎖。
3一、aop代理模式
AOP 全稱 Aspect Oriented Programming,面向切面編程,和 OOP 同樣也是一種編程思想。AOP 出現的緣由是爲了解決 OOP 在處理 侵入性業務上的不足。
代理模式分爲靜態代理和動態代理兩種。
靜態代理:一般用於對原有業務邏輯的擴充。建立一個代理類實現和方法相同的方法,經過讓代理類持有真實對象,而後在原代碼中調用代理類方法,來達到添加咱們須要業務邏輯的目的。
動態代理:動態代理底層是使用反射實現的,是在程序運行期間動態的建立接口的實現。
3二、jdk1.8新特性
1. 速度更快 – 紅黑樹
HashMap中的紅黑樹
HashMap中鏈長度大於8時採起紅黑樹的結構存儲。
紅黑樹,除了添加,效率高於鏈表結構。
2. 代碼更少 – Lambda
- Lambda表達式的基礎語法:Java8引入了一個新的操做符「->」,該操做符成爲箭頭操做符或者Lambda操做符,箭頭操做符將Lambda表達式拆分紅兩部分
- 左側:Lambda表達式的參數列表
- 右側:Lambda表達式中所需執行的功能,即Lambda體。
3. 強大的Stream API – Stream
一系列流水線式的中間操做。
流是數據渠道,用於操做數據源(集合、數組等)所生成的元素序列。
注意:
①Stream本身不會存儲元素。
②Stream不會改變源對象。相反,會返回持有新結果的新Stream。
③Stream操做是延遲執行的。這意味着他們會等到須要結果的時候才執行。
4. 便於並行 – Parallel
在必要的狀況下,將一個大任務進行必要的拆分Fork成若干個小任務,再將小任務的運算結果進行Join彙總。
5. 最大化減小空指針異常 – Optional
是一個容器類,表明一個值存在或不存在,原來用null 表示一個值不存在,如今Optional 能夠更好的表達這個概念。而且能夠避免空指針異常。
六、ConcurrentHashMap
- Jdk1.7時隔壁級別CocnurrentLevel(鎖分段機制)默認爲16。
- JDK1.8採起了CAS算法
- Jdk1.8沒有永久區,取而代之的是MetaSpace元空間,用的是物理內存。
3三、java的4種引用 強軟弱虛
強引用
a、new一個對象,強引用不會被GC回收。
b、默認的就是強引用
軟引用(SoftReference)、
一、內存不足的時候會被回收
二、會影響對象的回收
弱引用(WeakReference)
一、當引用的對象被回收時,reference的引用爲null
二、它不影響對象的回收
弱引用與軟引用的區別在於:
只具備弱引用的對象擁有更短暫的生命週期。
在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。
虛引用(PhantomReference)
虛引用主要用來跟蹤對象被垃圾回收器回收的活動。
虛引用與軟引用和弱引用的一個區別在於:
虛引用必須和引用隊列 (ReferenceQueue)聯合使用。
當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象的內存以前,把這個虛引用加入到與之關聯的引用隊列中
PS:
一、在對應引用A設置成null調用gc時並不會立刻回收,如有對A對象的引用則更不會回收了
二、這些引用會保存着對象設置時的值,因此並非只持有引用。
三、threadlocal中的entity就是使用了weakreference
3四、分佈式服務 解決了哪些問題,本身設計一個分佈式框架 會用到哪些技術?
須要拆分應用進行服務化,以提升開發效率,調優性能,節省關鍵競爭資源
當服務愈來愈多時,服務的URL地址信息就會爆炸式增加,配置管理變得很是困難,F5硬件負載均衡器的單點壓力也愈來愈大。
當進一步發展,服務間依賴關係變得錯蹤複雜,甚至分不清哪一個應用要在哪一個應用以前啓動,架構師都不能完整的描述應用的架構關係。
接着,服務的調用量愈來愈大,服務的容量問題就暴露出來,這個服務須要多少機器支撐?何時該加機器?等等…
用到哪些技術: dubbo(分佈式框架), zookeeper(開源的分佈式協調服務) ,redis(緩存), ssdb, nsq, nginx(負載均衡), Kafka,hessian ,RPC,netty。
3五、Zookeeper服務的註冊和發現?
1. init獲取Zookeeper的服務註冊信息,並緩存在service_repos
2. get_service_repos方法獲取實例變量service_repos
3. get_service_endpoint根據init構建好的service_repos,以及lb_strategy提供的負載均衡策略返回某個服務的URL地址
4. update_service_repos經過Zookeeper的Watcher機制來實時更新本地緩存service_repos
5. heartbeat_monitor是一個心跳檢測線程,用來進行服務提供者的健康存活檢測,若是出現問題,將該服務提供者從該服務的提供者列表中移除;
反之,則加入到服務的提供者列表中LoadBalanceStrategy定義了根據服務提供者的信息返回對應的服務Host和IP,即決定由那臺主機+端口來提供服務。
3六、主流的分佈式框架?
3七、redis的原理 和存儲結構 持久化和非持久
Redis存儲機制分紅兩種Snapshot和AOF。不管是那種機制,Redis都是將數據存儲在內存中。
Snapshot工做原理: 是將數據先存儲在內存,而後當數據累計達到某些設定的伐值的時候,就會觸發一次DUMP操做,將變化的數據一次性寫入數據文件(RDB文件)。
AOF 工做原理: 是將數據也是先存在內存,可是在存儲的時候會使用調用fsync來完成對本次寫操做的日誌記錄,這個日誌揭露文件實際上是一個基於Redis網絡交互協議的文本文件。
3八、類加載過程
JVM類加載機制分爲五個部分:加載,驗證,準備,解析,初始化,下面咱們就分別來看一下這五個過程。
1. 虛擬機在首次加載Java類時,會對靜態代碼塊、靜態成員變量、靜態方法進行一次初始化(靜態間按順序執行)。
2. 只有在調用new方法時纔會建立類的實例。
3. 類實例建立過程:父子繼承關係,先父類再子類。父類的靜態->子類的靜態->父類的初始化塊->父類的構造方法->子類的初始化塊->子類的構造方法
4. 類實例銷燬時候:首先銷燬子類部分,再銷燬父類部分。
PS:
一、加載:類加載器加載類文件
二、驗證:確保class文件結構沒有被串改
三、準備:爲類的講臺變量分配內存,將其初始化默認值。
四、解析:常量池內的符號引用轉爲直接引用,將法中對其餘方法的應用緩存方法區中對內存地址的引用。
3九、String,StringBuffer,StringBuilder有什麼不一樣?
一、線程安全方面:stringbuffer是線程安全的,其餘都不是。
二、速度:stringbuilder是最快的。
三、內存空間:string每次操做都會建立一個新對象(另string常量池優化),其餘都是可變類(有緩存會自增容量)
40、String和StringBuffer的實現?
4一、Volatile關鍵字做用?除了保證數據可見性,還有其餘什麼使用方式?
一、做用主要有兩個:保持內存的可見性,防止指令重排序
二、保持內存可見性:經過集中原子操做完成工做內存和主內存的交互
a、unlock:做用於主內存,解除獨佔狀態。
b、read:做用主內存,把一個變量的值從主內存傳輸到線程的工做內存。
c、load:做用於工做內存,把read操做傳過來的變量值放入工做內存的變量副本中。
d、use:做用工做內存,把工做內存當中的一個變量值傳給執行引擎。
e、assign:做用工做內存,把一個從執行引擎接收到的值賦值給工做內存的變量。
f、store:做用於工做內存的變量,把工做內存的一個變量的值傳送到主內存中。
g、write:做用於主內存的變量,把store操做傳來的變量的值放入主內存的變量中。
h、lock:做用於主內存,把變量標識爲線程獨佔狀態。
valatile的特殊規則:
a、read、load、use動做必須連續出現 :每次讀取前必須先從主內存刷新最新的值。
b、assign、store、write動做必須連續出現 : 每次寫入後必須當即同步回主內存當中。
三、防止指令重排序:編譯器在生成字節碼時,會在指令序列中插入內存屏障來禁止特定類型的處理重排序。
a、valitile寫操做:storestore 寫操做 storeload
b、volatile讀操做: loadload 讀操做 loadstore
PS:
一、內存屏障:解決指令重排序問題,多CPU的情形下能夠強制同步cpu中緩存不一致的狀況。
二、緩存一致性原則:
三、鎖、內存屏障與一致性
a、只有一個cpu是,cup只會從本身的緩存中讀取數據,加入緩存丟失,從主內存讀取數據到緩存中,最終內存數據都是一致的。
b、多核狀況下,每一個CPU都有本身的緩存,須要讀到最新數據就要解決某CPU更新了緩存端但未寫回內存,其餘cpu看不到的問題。
c、此時就引入了緩存一致性協議(做用域緩存行):
modifiled(該cup已經獨佔了該緩存段,並作了修改,其餘核心讀取時,此數據必須刷入主存),
exclusive(數據已經讀入cacheline,而且只有該CPU擁有它,能夠直接修改數據)
shared(多個cpu共享某內存的數據,當cpu須要修改數據時,須要提交RFO請求獲取數據的獨佔全,即進入exclusive狀態才能進行修改)
invalid(share 狀態下,當前cpu贊成了其餘cpu申請寫的時候,變成該狀態)
即:當前cpu起始狀態(shared),發起rfo,(被接收後當前cpu變成exclusive,其餘接收的cpu變成invalid),當前cpu修改了數據就變成modified(這個狀態要求讀取當前緩存段前,當前數據必須同步到主存)
d、問題:若是當前cpu忙,沒接收別的cpu的RFO,則那個發起請求的cpu就無事可作了,這就下降了性能。
e、爲了解決上述問題,添加了兩個相似緩存的東西,Store buffer和invalidate queue。
這樣當cpu須要寫緩存行時就將寫指令丟入storebuff,去幹別的事,等RFO受到迴應時,該指令才執行。
cup收到其餘核心的RFO指令後,會當即迴應但相應的失效操做指令(較忙時)會放入到invalidate queue中
f、這套機制實現了異步,帶了性能提高的同時也帶來了問題,在發去RFO請求的cpu執行寫時(先讀store buffer,再讀緩存)只對當前CPU可見(其餘cpu可能沒有執行本身invalidate queue中的失效指令)。
g、爲了解決這個問題,引入了讀寫屏障。寫屏障保證寫屏障前在全部store buffer的指令都真正的寫入到緩存,讀屏障保證在讀屏障前全部invalidate queue中全部的無效化指令都執行。這保證了不一樣核心上,緩存的強同步。
h、在鎖的實現上,通常lock都會加入讀屏障,保證後續代碼能夠讀到別的cpu未回寫的緩存數據(
應該是作緩存失效吧?),而unlock會加入寫屏障,將全部未回寫的緩存回寫(
確保到modified狀態吧)。
四、內存屏障保證緩存的一致性
詳參:http://gocode.cc/project/9/article/128
4二、ThreadLocal作什麼的?如何使用?源碼是如何實現的?get()方法?
一、併發背景下,經過將相關對象封閉到執行線程中來解決併發問題,是一種以空間換時間的作法。
二、具體實現就是爲當前執行線程維護一個ThreadLocalMap對象(維護到執行線程中,每一個線程建立一個),其中維護一個entity對象數組(繼承weakReference),其中threadlocal對象爲key,設置的對象爲value,以實現thread中維護多個threadLocal對象
三、通常在類中使用final static 修飾
四、get方法使用當前threadlocal對象實例從當前執行線程的threadlocalmap中獲取存放的對象。
五、線程的threadLocalMap中之索引維護一個entity數組,是由於一個線程能夠持有多個threadlocal
PS:
一、threadLocalMap中維護的entity繼承了weakReference,功能就是當threadlocal被回收後就變成null->value
二、threadLocal在get時會清除key爲null的entity
4三、ConcurrentHashMap源碼?JDK1.6,1.7,1.8中分別有什麼不一樣?
一、負載因子0.75,默認容量16,當大於16*0.75時擴容一倍。
二、1.6和1.7無太大差異,只是在new hashmap時1.6確實開闢了內存空間,1.7採用的是懶漢式,在put時才構造。
三、1.7對待併發採用的是基於分片+鏈表數組結構,要通過兩次hash碰撞,分片是用的是可重入鎖;1.8則摒棄了分片直接使用synchronized加到桶中第一個元素上,cas用於交換元素。
四、1.8對待增加,當鏈表長度大於8時但桶大小小於64時擴充容量,大於64時將桶轉爲紅黑樹操做。
PS:
一、node中的value和next都用volatile修飾。
二、hash碰撞就是兩個對象的key的hashcode同樣,這時候如何獲取他的value。
三、1.8中的spread方法對hash作了擴展,將高16位和當前hash作異或操做,解決選擇桶的下標時老是與低4位運算,形成的表的長度較小問題,從而減小系統的開銷(hash碰撞狀況)
四、桶的大小超過64時,使用紅黑樹也是當發生較大碰撞時下降衝突的考慮。
4四、分佈式程序調用鏈
全鏈路性能監控從總體維度到局部維度展現各項指標,將跨應用的全部調用鏈性能信息集中展示,可方便度量總體和局部性能,而且方便找到故障產生的源頭,生產上可極大縮短故障排除時間。
Google Dapper
一、背景:隨着微服務的應用,業務調用鏈愈來愈複雜,一個請求可能涉及到幾十個服務的系統服務,涉及到多個團隊的業務系統。當遇到問題須要定位時,也會產生一系列麻煩。
二、解決方案:經過調用鏈,把一次請求調用過程串聯起來,實現對請求路徑的監控,便於快速定位。
三、調用鏈顯示內容:各個調用環節的性能分析(如各API使用時間、使用堆棧)、調用個環節依賴關係還原、SQL打印、IP顯示。
四、通用框架:google的Dapper,淘寶的鷹眼,京東的九頭蛇。
五、調用鏈原理:
a、請求生成一個全局TranceId,經過TraceId能夠串聯起整個調用鏈,一個tranceId表明一次請求。
b、除了TranceId,還須要SpanId記錄調用的父子關係,span是本身生成,透傳子調用成爲parentId
c、一個沒有parentId的span是調用鏈入口
e、這個調用過程當中每一個請求都要透傳tranceId和spanId
f、要查看某次完整的調用鏈只要根據TranceId查出全部調用記錄,而後經過parentId和spanId組織起整個調用父子關係。
PS:具體參見
https://blog.csdn.net/Damon__Wang/article/details/81782911
https://blog.csdn.net/Damon__Wang/article/details/82051631
4五、生產環境如何定位內存溢出?CPU使用率太高?Linux命令?
一、內存溢出的常見狀況分幾種:堆溢出(java heap space),PermGen space(方法區),不能建立本地線程(unable to create new native thread),回收執行了太長時間、超過限制(GC overhead limit exceeded)。初步判斷區域
二、關鍵還在於分析dump文件。這個能夠提早設置+HeapDumpOnOutOfMemeryError,或jmp
三、使用Jprofile打開dump文件,這裏能夠看到大對象和具體的引用
四、固然結合gc的日誌更好(-XX:+PrintGC)
4六、Netty
Netty 是一個基於NIO的客戶、服務器端編程框架。
Netty是什麼?
1)本質:JBoss作的一個Jar包
2)目的:快速開發高性能、高可靠性的網絡服務器和客戶端程序
3)優勢:提供異步的、事件驅動的網絡應用程序框架和工具
4)特色:
a、併發高
b、傳輸快:領拷貝,使用直接緩衝區
c、封裝好:比較簡潔,使用鏈式調用
通俗的說:一個好使的處理Socket的東東
4七、kafka 事務 性能
4八、內存屏障
Java內存模型中volatile變量在寫操做以後會插入一個store屏障,在讀操做以前會插入一個load屏障。一個類的final字段會在初始化後插入一個store屏障,來確保final字段在構造函數初始化完成並可被使用時可見。
PS:
一、爲了提升性能,處理器設計了多級緩存,cpu的緩存和共享的緩存。cpu把處理結果發到緩存後就能夠作其餘處理了,但這也形成了可見性問題。
二、緩存操做是分紅緩存行,緩存一致性原則(MESI)經過定義獨佔、共享、修改、失效等緩存行的狀態來協調多個處理器對其的操做。(內存級別)當共享的同一緩存端的數據發生變化時其餘cpu都會獲得通知。
三、爲了杜絕內存不一致的狀況(如指令重排序,cpu和編譯階段都會),又引入了內存屏障來確保一致性。內存屏障分爲LoadLoad屏障,LoadStore屏障,StoreStore屏障,StoreLoad屏障,都是確保後一個操做前前一個操做必須完成(指令在中間,分割對應的操做指令,肯定先後的一個關係)。(指令級別)
四、lock前綴指令具有內存屏障功能(load&store)的的CUP指令,執行時鎖住子系統來確保執行順序,甚至跨多個CPU。
五、JVM中,除了內存屏障,還使用先行發生原則來確保指令的先後關係。如對象鎖釋放必須先於加鎖發生,start先於thread內全部指令執行前發生。
六、cas操做能夠理解爲是lock指令(鎖着內存)+系統cas指令來實現的。
七、
4九、redis面試題
一、Redis有哪些數據結構?程序員
a、基本類型:字符串,數值
b、集合:字典Hash、列表List、集合Set、有序集合SortedSet,鏈表
c、其餘:若是你是Redis中高級用戶,還須要加上下面幾種數據結構HyperLogLog、Geo、 Pub/Sub。Redis Module,像BloomFilter,RedisSearch,Redis-ML。
二、使用過Redis分佈式鎖麼,它是什麼回事?github
a、先拿setnx來爭搶鎖,搶到以後,再用expire給鎖加一個過時時間防止鎖忘記了釋放。
三、若是在setnx以後執行expire以前進程意外crash或者要重啓維護了,那會怎麼樣?web
a、這個鎖就永遠得不到釋放了。
b、set指令有很是複雜的參數,這個應該是能夠同時把setnx和expire合成一條指令來用的! 如redis的lua腳本
四、假如Redis裏面有1億個key,其中有10w個key是以某個固定的已知的前綴開頭的,若是將它們所有找出來?
a、使用keys指令能夠掃出指定模式的key列表。
五、若是這個redis正在給線上的業務提供服務,那使用keys指令會有什麼問題?
a、redis關鍵的一個特性:redis的單線程的。keys指令會致使線程阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。
b、這個時候能夠使用scan指令,scan指令能夠無阻塞的提取出指定模式的key列表,可是會有必定的重複機率,
c、在客戶端作一次去重就能夠了,可是總體所花費的時間會比直接用keys指令長。
六、使用過Redis作異步隊列麼,你是怎麼用的?
a、通常使用list結構做爲隊列,rpush生產消息,lpop消費消息。當lpop沒有消息的時候,要適當sleep一會再重試。
七、可不能夠不用sleep呢?
a、list還有個指令叫blpop,在沒有消息的時候,它會阻塞住直到消息到來。
八、能不能生產一次消費屢次呢?
a、使用pub/sub主題訂閱者模式,能夠實現1:N的消息隊列。publish/subscribe
九、pub/sub有什麼缺點?
a、在消費者下線的狀況下,生產的消息會丟失,得使用專業的消息隊列如rabbitmq等。(消息是即時的,不作存儲)
十、redis如何實現延時隊列?
a、使用sortedset和string,數據做爲拿時間戳做爲score,消息內容做爲key調用zadd來生產消息,消費者用zrangebyscore指令獲取N秒以前的數據輪詢進行處理。
b、string中uuid做爲key,data做爲value存放數據。
c、sortedset中,string中的uuid做爲key,時間戳做爲value存儲。獲取的時候使用zrangebyscore排序回去最先的數據。
d、應對的場景就是:併發對數據庫更新時,鎖表會致使效率低,使用這種延遲操做能夠解決這種效率低的問題。
十一、若是有大量的key須要設置同一時間過時,通常須要注意什麼?
a、若是大量的key過時時間設置的過於集中,到過時的那個時間點,redis可能會出現短暫的卡頓現象。
b、通常須要在時間上加一個隨機值,使得過時時間分散一些。(過時時間=固定時間+隨機值)
十二、Redis如何作持久化的?
a、bgsave作鏡像全量持久化,aof作增量持久化。
b、由於bgsave會耗費較長時間,不夠實時,在停機的時候會致使大量丟失數據,因此須要aof來配合使用。
c、 在redis實例重啓時,會使用bgsave持久化文件從新構建內存,再使用aof重放近期的操做指令來實現完整恢復重啓以前的狀態。
1三、若是忽然機器掉電會怎樣?
a、取決於aof日誌sync屬性的配置,若是不要求性能,在每條寫指令時都sync一下磁盤,就不會丟失數據。
b、可是在高性能的要求下每次都sync是不現實的,通常都使用定時sync,好比1s1次,這個時候最多就會丟失1s的數據。
1四、bgsave的原理是什麼?
a、fork和cow。fork是指redis經過建立子進程來進行bgsave操做,cow指的是copy on write,
b、子進程建立後,父子進程共享數據段,父進程繼續提供讀寫服務,寫髒的頁面數據會逐漸和子進程分離開來。
1五、Pipeline有什麼好處,爲何要用pipeline?
a、能夠將屢次IO往返的時間縮減爲一次,前提是pipeline執行的指令之間沒有因果相關性。
b、使用redis-benchmark進行壓測的時候能夠發現影響redis的QPS峯值的一個重要因素是pipeline批次指令的數目。
1六、Redis的同步機制瞭解麼?
a、Redis能夠使用主從同步,從從同步。
b、第一次同步時,主節點作一次bgsave,並同時將後續修改操做記錄到內存buffer,待完成後將rdb文件全量同步到複製節點, 複製節點接受完成後將rdb鏡像加載到內存。
c、加載完成後,再通知主節點將期間修改的操做記錄同步到複製節點進行重放就完成了同步過程
1七、是否使用過Redis集羣,集羣的原理是什麼?
a、Redis Sentinal着眼於高可用,在master宕機時會自動將slave提高爲master,繼續提供服務。
b、 Redis Cluster着眼於擴展性,在單個redis內存不足時,使用Cluster進行分片存儲。
50、動態代理有幾種,Jdk與Cglib區別
一、實現方式:
a、jdk經過反射機制生成一個實現代理接口(參數中interfaces裏全部接口且繼承了Proxy的代理類)的匿名類,在調用具體方法前調用invokeHandler的invoke處理
a.1,Proxy.newProxyInstance(classloader,target.getclass.getInterfaces(),target(implemants InvocationHandler)
a.二、生成一個實現了參數中interfaces裏全部接口且繼承了Proxy的代理類的字節碼,而後用參數中的classloader加載這個代理類。
a.三、使用代理類父類的構造函數Proxy(invocationHandler)來建立一個代理類實例,將咱們自定義的InvocationHandler的子類傳入。
a.四、返回這個代理類的實例。
b、cglib利用ASM開源包,直接修改代理類class的字節碼生成子類來重寫其方法。
二、目標類的限制:
a、jdk只能正對實現了接口的類
b、cglib針對有誤實現接口的都行
c、cglib不能重寫final類或方法
三、性能:jdk是愈來愈快
ps:
一、Java 字節碼操控框架。它能被用來動態生成類或者加強既有類的功能
二、spring中的應用:<aop:aspectj-autoproxy proxy-target-class="true"/>:cglib
5一、數據庫三大範式
一、字段不能夠再分:也就是說一個列名下的值只能是一種類型,如號碼中既有手機號、座機號等都不行
保證字段的原子性,也是關係型數據庫的標準(面向對象)
二、有主鍵,非主鍵字段依賴主鍵(有一個主題):一個主鍵表明一條記錄,只能有一個主題;也就是說一條記錄中不能即存在學生的記錄信息也存在課程的主題信息
惟一性
三、非主鍵字段不能相互依賴(都是平級的):每列都與主鍵有直接關係,不存在傳遞依賴。
5二、左鏈接和右鏈接說一下,內鏈接呢
一、左鏈接:使用left join on,匹配時匹配表中沒能匹配上的也顯示,驅動表中只顯示匹配的上的
二、右鏈接:使用right join on,效果和左鏈接相反
三、內連接:使用(inner) join on ,匹配表和驅動表都只顯示匹配的上的。
四、優化:都須要優化驅動表
五、性能:左右關聯比內關聯要好一點。
5三、數據庫索引有幾種
5四、數據庫引擎你認識幾種,innodb 和myisam 區別,你的項目用到哪一個引擎
PS:
一、指數據庫事務正確執行的四個基本要素:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)
二、數據庫引擎是用於存儲、處理和保護數據的核心服務。利用數據庫引擎可控制訪問權限並快速處理事務,從而知足企業內大多數須要處理大量數據的應用程序的要求。
三、查詢數據庫支持的引擎:show engines
四、查詢數據庫使用的引擎:show variables like '%storage_engine%';
5五、若hashcode方法永遠返回1會產生什麼結果
一、首先、編程時就堅持修改了equals就要修改hashchode要求
二、hashcode在對象的對比和一些集合類中會被用到,如hashmap
三、hashcode的存在也是一種優化程序的體現,如hashmap中的桶,若是全部的hashcode相同,那hashcode會構成一個線性表,致使性能降級
PS:爲何選擇31作乘數
一、不大不小:爲了不hash重複須要選擇一個大一點的質數作乘數,不然就會致使hash值重複的較多,100以上的數乘下來容易超過int的範圍。
二、31,33,37,39,41中31能夠被jvm優化,做爲位移計算,這種很高效。
5六、Error與RuntimeException的區別
兩者的不一樣之處:
Exception:
1.能夠是可被控制(checked) 或不可控制的(unchecked)
2.表示一個由程序員致使的錯誤
3.應該在應用程序級被處理
Error:
1.老是不可控制的(unchecked)
2.常常用來用於表示系統錯誤或低層資源的錯誤
3.如何可能的話,應該在系統級被捕捉
5七、引用計數法與GC Root可達性分析法區別
一、引用計數:相似給對象添加一個計數器,當對象被引用的時候就在本身的計數器上加一,當某個引用的對象被回收後。引用爲零的對象就會被回收
優勢:簡單、高效
缺點:相互引用不能被識別(a.instance=c,b.instance=a,這兩項都再也不被使用,但卻沒法回收)
二、可達性分析:從根開始遍歷他的引用,當某個對象到根不可達時則對象該引用能夠回收
根節點:方法區的常量、靜態變量
虛擬機棧區的變量列表的引用
本地方法棧中的引用
本地方法棧中引用的對象
PS:
一、五大分區:
a、程序計數器:記錄當前執行程序的位置,改變計數器的值來肯定執行下一條指令,如循環、分支、方法跳轉
b、虛擬機棧:每一個線程都會建立一個虛擬機棧,經過壓棧出棧的方式執行方法調用。分局部變量表、操做數棧、動態連接、方法出口等。
c、本地方法棧:native方法
d、堆:存放對象實例
e、方法區:用於存放已被虛擬機加載的類信息,常量,靜態變量等數據。
f、直接內存:並非虛擬機運行時數據區的一部分,也不是虛擬機規範中定義的內存區域。
二、各區域的使用
5八、雙親委派機制說一下
一、某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次遞歸,若是父類加載器能夠完成類加載任務,就成功返回;只有父類加載器沒法完成此加載任務時,才本身去加載。
二、全盤責任委託:一個類若是被某個類加載器加載,那麼除非指定別的加載器,不然這個類關聯的類也有這個類加載器加載。
三、由下向上詢問是否加載,由上向下嘗試加載。
四、線程上下文加載器:針對java的spi狀況,也就是引導類加載的類型須要使用二方包的狀況,如jdk
5九、算法題:找出一個數組中第100個小的數字(堆思想解決)
60、看你項目用到策略模式和工廠模式,說一下區別
一、比喻:去必勝客吃披薩,工廠模式關注的是最終能吃到披薩,策略模式關注在關注的是披薩是如何作的。
a、用途不一樣
工廠是建立型模型,他的做用是建立對象
策略是行爲型模型,他的做用是讓一個對象在許多行爲中選擇一種行爲。
b、關注點不一樣
一個關注對象的建立
一個關注行爲的封裝
c、解決不一樣的問題
工廠模式,它接受指令,建立出符合要求的實例。它主要解決的是資源的統一分配,將對象的建立獨立出來,讓對象的建立和具體的使用客戶無關。
策略模式,它爲了解決策略的切換與擴展,讓策略模式的變化獨立於使用策略模式的用戶。
d、工廠至關於黑盒子,策略至關於白盒子
PS:
一、
設計模式有三種類型:建立型(解決:對象的建立和具體的使用解耦),行爲型(描述了對象和類的模式,以及它們之間的通訊模式)
,組合型(解決怎樣組裝現有的類,設計他們的交互方式,從而達到實現必定的功能的目的)。
二、項目中:緩存的使用,定義一個公用的緩存操做頁面,在緩存工廠中經過配置緩存類別獲取具體的緩存實現,redis,tair
三、簡述:
四、工廠模式有三種:簡單工廠,工廠方法,抽象工廠。
a、簡單工廠(靜態工廠):將類的實例化轉交一個工廠,具體的行爲由子類決定(即如何構造該實例);解決的問題:怎麼構造一個對象有工廠內部決定,如是否設置某個屬性等,用於隱藏實現細節。如計算器,獲取單例等
b、工廠方法:定義一個用於建立對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。不一樣的對象使用不一樣的工廠
c、抽象工廠:提供一個建立一系列相關或相互依賴對象的接口,而無需指定他們具體的類。解決的問題:切換數據庫
五、策略模式:它定義了算法家族,分別封裝起來,讓他們之間能夠相互替換,此模式讓算法的變化,不會影響到使用算法的客戶
6一、模板方法模式
定義:一個模板結構,將具體內容延遲到子類去實現。
解決的問題:
一、將複用性高的代碼抽取到抽象父類中,具體的操做在繼承的子類中定義。
二、父類調用子類操做,子類擴展不一樣的行爲,這樣即實現了控制反轉也符合開閉原則。
使用場景
試卷,apache velocity
6二、開閉原則懂嗎,說一下
一、設計模式中提到的一個概念。
二、目的指導咱們如何創建一個穩定的、靈活的系統。
三、開閉原則的定義是:軟件中的對象(類、模塊、函數等)應該對於擴展是開放的,可是,對於修改是封閉的。(能夠新增、儘量少的修改)
6三、NIO說一下你的理解
一、NIO也就是非阻塞IO,相對於BIO而言,固然也有AIO
二、打個比喻水工接水的比方:BIO就是多個多個水籠統,每一個水龍頭都有一個接水工,每一個接水工只有接到水才纔會作後續處理;NIO是爲每一個水龍頭添加了一個水缸且只有一個水工負責查看接到水剛,當某個水缸接滿水,水工就先處理這個水缸的後續處理。
三、這個水缸就緩衝區buffer,水工就是選擇器Select,水工就看查看某個水龍頭時就等於鏈接了通道channel。
四、具體的實現時,全部的通道都註冊到選擇器中,選擇器輪循查看通道中的數據是否準備就緒,而讀出和寫入都是直接到了緩衝區。
五、這個地方提供了一個新的概念:直接緩衝區,繞過內核地址空間(系統空間),直接將數據的物理地址映射到用戶地址空間(JVM)。
ps:以前的數據拷貝都是先寫到物理內存,而後再拷貝到jvm中。
6四、AtomicInteger底層原理
一、AtomicInteger是一個提供原子操做的Integer類,經過線程安全的方式操做加減。
二、其中存儲的value使用了volatile修飾,操做時使用cas無鎖算法。
ps:
一、具體實現就是使用Unsafe。
二、它有以下功能:內存管理(分配、釋放內存),很是規的對象實例化(無需調用構造器),操做類、對象、變量(指針偏移獲取),數組操做(指針偏移),多線程同步(對象鎖機制,cas操做),線程掛起與恢復,內存屏障(loadFence、storeFence、fullFence)
6五、CAS機制是什麼?有什麼缺點,會出現什麼問題?
CAS是英文單詞Compare And Swap的縮寫,翻譯過來就是比較並替換。
CAS機制當中使用了3個基本操做數:內存地址V,舊的預期值A,要修改的新值B,若預期值A和內存值V相同就把內存值修改爲新值B
CAS的缺點:
1.CPU開銷較大
在併發量比較高的狀況下,若是許多線程反覆嘗試更新某一個變量,卻又一直更新不成功,循環往復,會給CPU帶來很大的壓力。
2.不能保證代碼塊的原子性
CAS機制所保證的只是一個變量的原子性操做,而不能保證整個代碼塊的原子性。好比須要保證3個變量共同進行原子性的更新,就不得不使用Synchronized了。
3.ABA問題
這是CAS機制最大的問題所在。
PS:
什麼是ABA問題?
引用原書的話:若是在算法中的節點能夠被循環使用,那麼在使用「比較並交換」指令就可能出現這種問題,在CAS操做中將判斷「V的值是否仍然爲A?」,而且若是是的話就繼續執行更新操做,在某些算法中,若是V的值首先由A變爲B,再由B變爲A,那麼CAS將會操做成功。
Java中提供了
java.util.concurrent.atomic中AtomicStampedReference和AtomicMarkableReference來解決ABA問題。
多CPU的狀況下的cas操做是CPU提供的支持。
一、這和volatile的底層實現是相同的
二、底層:這個讀取、對比以及設置的操做私用lock前綴保證惟一性。
6六、本地緩存過時策略怎麼設置,一致性怎麼保證?
1、一致性(以下是主動的狀況)
一、當數據時效性要求比較高時,須要保證緩存與數據庫保存一致,並且須要保證緩存節點和副本中的數據也要保存一致,不能出現差別現象。
二、這就比較依賴緩存過時和更新策略,通常會在數據發生更改時,主動更新緩存中的數據或者移除對應的緩存。
三、通常的緩存使用
方案一(先更新緩存,再更新數據庫):
一、是不可用的
二、首先庫存是以數據庫爲準的,若是緩存更新完成但數據庫未更新完成且庫存少於緩存則會形成負庫存。
三、若緩存更新成功,數據庫更新失敗則緩存一直都是髒數據。
方案二(新更新數據庫,再更新緩存)
一、不可取
二、若是兩個線程併發執行,會存在A更新數據庫,B更新數據庫,B更新緩存,A更新緩存(網絡緣由),則數據庫中的就是髒數據。
三、針對那種依賴前值計算後更新的場景,無疑是浪費性能。
方案三(先刪除緩存,再更新數據庫:更新數據庫失敗對業務也沒什麼影響)
一、待優化
二、存在A刪除了緩存,B發現緩存不存在從數據庫查詢到舊值寫到了緩存,A將新值寫入到數據庫:此時緩存和數據庫不一致。
三、能夠採用延時雙刪策略:先淘汰緩存,再寫入數據庫,休眠1秒,再次淘汰緩存。此時能夠確保上述B的寫入被刪除。(休眠是爲了確保讀請求結束,寫請求能夠刪除對請求形成的髒數據)
四、第二次刪除能夠採用新線程來作以免下降吞吐。
五、第二次刪除失敗了的解決方案詳見方案四
方案四(先更新數據庫,再刪除緩存)
一、待優化
二、緩存恰好失效,A查詢數據庫獲得舊值,b將新值寫入數據庫並刪除緩存,A將舊值寫入緩存(若b的寫入數據庫操做要足夠端以致於B刪除緩存早於A寫入緩存發生:機率低)
三、2的解決方案:異步延時刪除,
緩存設置有效時間。
四、針對緩存更新失敗的解決方案:
刪除失敗後,將刪除key的消息發送到消息隊列,重試刪除直到成功(這對業務代碼有侵入)
使用mysql的中間件如Canal,單啓一個獨立的程序去處理。
2、過時策略
一、緩存過時策略大體分兩種:能夠經過過時時間來控制內容過時的狀況和沒法經過過時時間來控制內容過時的狀況。
二、能夠經過過時時間來控制內容過時的狀況
a、設置絕對過時時間(秒殺商品)。
三、沒法經過過時時間來控制過時的狀況
a、設置滑動過時(針對時效性不強的):在讀取緩存的時候將該緩存項的過時時間在當前時間的基礎上延後指定長度的事件。(如文章有評論就延長過時時間的狀況)
ps:
一、緩存滿了,從緩存中移除數據的策略
a、新進先出算法
b、最久未使用算法
c、最少使用算法
二、緩存併發問題
a、緩存過時後將嘗試從後端數據庫獲取數據,當數據獲取到更新完成這段會有多個線程到數據庫後去數據,對數據庫形成極大的衝擊,甚至致使血崩。
b、此時就要加鎖,到後臺數據庫請求數據要先嚐試獲取鎖,未獲取鎖的線程只能等待。
c、針對緩存過時也要選擇一個範圍內隨機過時,不能所有集中到某個時間段。
三、緩存穿透(不存在的key,緩存不起做用)
a、查詢一個不存在的數據,因爲緩存是不被命中被動寫的(若是沒有就查數據庫),而且出於容錯考慮,若是存儲層查不到數據就不寫入緩存,這將致使每次都要查詢存儲層查詢,失去了緩存的意義。在流量大的時候,可能db就掛掉了。
b、緩存空對象,過時時間段,不超過五分鐘。
c、單獨過濾處理:將對應數據爲空的key進行統一存放
d、布隆過濾器:將全部可能存在的數據哈希到一個足夠大的bitmap中,一個必定不存在的數據會被 這個bitmap攔截掉
四、緩存顛簸:緩存節點故障致使,使用hash算法解決
五、緩存雪崩
a、緩存採用了相同的過時時間,致使緩存再同一時刻同時失效,DB瞬間壓力過大崩潰。
b、在緩存失效的基礎上添加1-5分鐘的隨機值
c、從應用架構角度,咱們能夠經過限流、降級、熔斷等手段來下降影響,也能夠經過多級緩存來避免這種災難
六、緩存擊穿(一個存在的key,在緩存過時的一刻,同時有大量的請求)
a、使用互斥鎖:使用setnx設置值,成功消息返回的纔去查詢數據庫(成功後設置有效期)
七、緩存無底洞現象
八、其餘
a、目前主流的數據庫、緩存、Nosql、搜索中間件等技術棧中,都支持「分片」技術,來知足「高性能、高併發、高可用、可擴展」等要求
b、命中:能夠直接經過緩存獲取到須要的數據。
3、cache的使用
4、掌醫的實現
5、商品秒殺邏輯