java常見問題概述

1、如何理解內存泄漏問題?有哪些情況會導致內存泄露?如何解決?

對於應用程序來說,當對象已經不再被使用,但是java的垃圾回收器不能回收他們的時候,就產生了內存泄漏。未引用對象將會被垃圾回收器回收,而引用對象卻不會,未引用對象顯然都是無用的對象,然而無用的對象並不都是未引用對象,有些無用對象也有可能是引用對象,這部分對象正是內存泄漏的來源。

如何阻止內存泄漏

使用List,Map等集合時,使用完成後賦值爲null

使用大對象時,在用完後賦值爲null

使用字符串處理,避免使用String,應大量使用StringBuffer,每一個String對象都得獨立佔用內存一塊區域

儘量少用靜態變量,因爲靜態變量存放在永久代

目前已知jdk1.6的substring()方法會導致內存泄漏

避免一些死循環等重複創建或對集合添加元素,稱爆內存

簡潔數據結構,少用靜態集合等

及時的關閉打開的文件,socker句柄

2、怎樣理解強一致性,弱一致性,最終一致性?

cap原理中,有三個要素:一致性,可用性,分區容忍性

cap原理三個要素中最多隻能同時實現倆點。對於分佈式數據系統,分區容錯性是最基本要求,大多數情況下會犧牲一致性而換取高可用性。

最終一致性從客戶端來看一致性要指的是多併發訪問時更新過的數據如何獲取的問題,從服務端來看,則是更新如何複製分佈到整個系統,以保證數據的最終一致。對於關係形數據庫,要求更新過的數據能被後續的訪問都能看到,是強一致性;如果能容忍後續的部分或者全部訪問不到,是弱一致性;如果經過一段時間後要求能訪問到更新的數據,則是最終一致性

3、分佈式鎖有哪些解決方案

1、利用數據庫實現方案,利用數據庫自身提供的鎖機制實現,要求數據庫支持行級鎖,實現簡單穩定可靠,性能差無法適應高併發的場景,容易出現死鎖的情況,無法優雅的實現阻塞式鎖

2、利用redis實現,使用sexnx和lua腳本機制實現,保證對緩存操作序列的原子性,性能好,實現相對交複雜,有出現死鎖的可能性,無法優雅的實現阻塞式鎖

3、利用zookeeper,基於zk的節點特性及watch機制實現,性能好,穩定可靠性高,能較好的實現阻塞式鎖,實現相對複雜

4、如何解決Redis緩存穿透的問題?

緩存衝突是指查詢一個一定不存在的數據,由於緩存不命中,並且出於容錯的考慮,請求全部都走數據庫。

請求的數據在緩存中大量不命中,導致請求走數據庫,緩存穿透如果發生,可能直接導致數據庫搞垮,導致整個服務癱瘓

方案:

1、由於我們請求的參數是不合法的,於是我們可以使用布隆過濾器提前攔截

2、當我們從數據庫中找不到的時候,我們也將這個空對象設置到緩存裏面去,下次再請求,就可以從緩存裏面獲取 

緩存擊穿:key對應的數據存在,但在redis中過期,此時如果有大量的併發請求過來,這些請求發現緩存過期一般都會從數據庫中加載數,大併發的情況下可能會擊垮數據庫

緩存雪崩:當緩存服務器重啓或者大量緩存集中在某一個時間端失效,這樣在失效的時候,也會給後端帶來很大的壓力

5、Redis hash算法用的是什麼?

6、redis集羣方案? 

7、樂觀鎖和悲觀鎖的原理及其應用?

樂觀鎖

樂觀鎖是一種併發控制的方法,假設多用戶併發的事務在處理時不會彼此互相影響,各事務能夠在不產生鎖的情況下處理各自影響的那部分數據,在提交數據更新之前,每個事務會先檢查在該事務讀取數據後,有沒有其他事務修改了該數據,如果其他事務有更新的話,正在提交的事務會進行回滾。

1、讀取:事務將數據讀入緩存,系統會給事務分派一個時間戳

2、校驗:事務執行完畢後,進行提交,這時同步校驗所有的事務,如果事務所讀取得數據之後又被其他事務修改,則產生衝突,進行回滾。

3、寫入:通過校驗階段後,將更新的數據寫入數據庫

樂觀併發控制多數用於共享數據競爭不大,衝突較少的情況下,回滾事務的成本會低於讀取數據加鎖的成本,能夠獲得更高的吞吐量。

使用樂觀鎖的時候,會未數據增加一個版本標識,當讀取數據時,將版本標識的值一同讀出,數據每更新一下,同時對版本標識進行更新,當提交數據的時候,會判斷當前版本號是不是該數據最新的版本號,如果不是則直接回滾,是的話正常更新。

悲觀鎖

 如果一個事務執行的操作讀取某行數據應用了鎖, 那只有當這個事務把鎖釋放,其他事務才能夠執行與該鎖衝突的操作

使用場景

樂觀鎖使用於寫比較少的情況下多讀場景,即衝突很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量,但如果時多寫的情況下,一般會經常產生衝突,着就會導致上層應用會不斷的進行retry,降低性能,一般多寫的場景用悲觀鎖就比較適合了。

樂觀鎖的倆種實現方式:

版本號機制:在數據表中加上一個數據版本號version字段,表示數據被修改的次數,當數據被修改時,version值會加1,當線程A要更新數據值時,在讀取數據的同時也會讀取version的值,在提交更新時, 若剛纔讀取到的version值爲當前數據庫中的version值相等時菜更新,否則重試更新操作

CAS算法:比較與交換,不使用鎖的情況下實現多線程之間的變量同步,也就是在沒有線程被阻塞的情況下實現變量的同步,所以也叫非阻塞同步,其中三個操作數

需要讀寫的內存值V

進行比較的值A

寫入的新值B

當且僅當V的值等於A時,CAS通過原子方式用新值B來更新V的值,否則不會執行任何操作,一般情況下,如果時一個自旋的操作,將會不斷的重試

 

8、受檢異常和非受檢異常的區別?

異常體系分類圖

Error:和底層硬件有關的錯誤。不是程序本身的異常,即使捕捉這種異常,我們也解決不了

Exception:程序本身所拋出的異常。

        受檢異常(CheckedException):可能是由一些外部的因素所引起的,是必須捕獲的異常,否則無法通過編譯(如:NullPointerException,ClassCastException,ArrayIndexsOutOfBoundsException,ArithmeticException)

        非受檢異常(RuntimeException):可能是由於開發的時候邏輯錯誤,可以捕獲也可以不捕獲,不捕獲的話,由jvm來處理(如:FileNotFoundException,IOException,SQLException)

異常通過trc - catch 或者throws來解決

9、瞭解軟引用,弱引用,虛引用,強引用?

 

強引用:平日裏面的用到的new了一個對象就是強引用,例如 Object obj = new Object();

當JVM的內存空間不足時,寧願拋出OutOfMemoryError使得程序異常終止也不願意回收具有強引用的存活着的對象!記住是存活着,不可能是你new一個對象就永遠不會被GC回收。

當一個普通對象沒有其他引用關係,只要超過了引用的作用域或者顯示的將引用賦值爲null時,你的對象就表明不是存活着,這樣就會可以被GC回收了。當然回收的時間是不一定的具體得看GC回收策略

軟引用:軟引用的生命週期比強引用短一些。軟引用是通過SoftReference類實現的。這樣就是一個簡單的軟引用使用方法。通過get()方法獲取對象。當JVM認爲內存空間不足時,就回去試圖回收軟引用指向的對象,也就是說在JVM拋出OutOfMemoryError之前,會去清理軟引用對象。軟引用可以與引用隊列(ReferenceQueue)聯合使用。

弱引用:弱引用是通過WeakReference類實現的,它的生命週期比軟引用還要短,也是通過get()方法獲取對象。在GC的時候,不管內存空間足不足都會回收這個對象,同樣也可以配合ReferenceQueue 使用,也同樣適用於內存敏感的緩存。ThreadLocal中的key就用到了弱引用

虛引用:通過PhantomReference類實現的。任何時候可能被GC回收,就像沒有引用一樣。無法通過虛引用訪問對象的任何屬性或者函數。那就要問了要它有什麼用?虛引用僅僅只是提供了一種確保對象被finalize以後來做某些事情的機制。比如說這個對象被回收之後發一個系統通知啊啥的

虛引用是必須配合ReferenceQueue 使用的,具體使用方法和上面提到軟引用的一樣。主要用來跟蹤對象被垃圾回收的活動。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章