JAVA开发面试题_网络_操作系统_JAVA基础_JVM虚拟机
?
目錄
?
網絡篇
OSI七層模型與TCP/IP 五層模型
常見應用層協議和運輸層、網絡層協議,以及硬件如路由器之類在哪一層
TCP與UDP區別和應用場景,基于TCP的協議有哪些,基于UDP的有哪些
TCP可靠傳輸的保證,擁塞控制目的和過程
TCP粘包現象原因和解決方法
TCP三次握手過程以及每次握手后的狀態改變,為什么三次? 為什么兩次不行?
TCP四次揮手過程以及狀態改變,為什么四次?CLOSE-WAIT和TIME-WAIT存在的意義?如何查看TIME-WAIT狀態的鏈接數量?為什么會TIME-WAIT過多?解決方法是怎樣的?
瀏覽器輸入URL并回車的過程以及相關協議,DNS查詢過程。
HTTP1.0、1.1、2.0之間的區別
HTTP與HTTPS之間的區別,HTTPS鏈接建立的過程,了解對稱加密算法和非對稱加密算法不?? HTTP與HTTPS之間的區別
HTTP請求有哪些。get和Post區別
HTTP常見響應狀態碼,從1xx到5xx
重定向和轉發區別
操作系統
進程和線程的區別
協程
進程間通信方式IPC
用戶態和核心態
操作系統分配的進程空間是怎樣的?線程能共享哪些?
操作系統內存管理方式,分頁分段以及段頁式的優缺點
頁面置換算法有哪些,FIFO為什么不好?如何改進?LRU思想,手寫LRU
死鎖條件,解決方式
Java基礎篇
Java面向對象特性介紹、與C++區別
多態實現原理
抽象類和接口區別,以及各自的使用場景
泛型以及泛型擦除。List類型的list,可以加入無繼承關系的B類型對象嗎?如何加入?
Java異常體系
反射原理以及使用場景
ThreadLocal原理,如何使用?
ThreadLocal內存泄漏的場景
static關鍵字和final關鍵字使用情況,一個類不能被繼承,除了final關鍵字之外,還有什么方法(從構造函數考慮)?
序列化和反序列化。反序列化失敗的場景
ArrayList和LinkedList的區別和底層實現?如何實現線程安全?
List遍歷時如何刪除元素?fail—fast是什么?fail—safe是什么?
詳細介紹HashMap
JDK1.7與1.8的區別
HashMap如何實現線程安全?ConcurrentHashMap的底層實現?JDK1.7與JDK1.8的區別
linux指令知道哪些?
JVM虛擬機
JVM運行時內存劃分?
堆內存分配策略
Full GC觸發條件
如何判斷對象是否存活?回收對象的兩次標記過程。
垃圾回收算法
垃圾收集器
CMS收集器
G1收集器
創建一個對象的步驟
詳細介紹類加載過程
雙親委派機制,使用這個機制的好處?如何破壞?
了解下tomcat的類加載機制
網絡篇
OSI七層模型與TCP/IP 五層模型
- ? OSI七層:物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層、應用層
- ? TCP/IP五層:物理層、數據鏈路層、網絡層、傳輸層、應用層
常見應用層協議和運輸層、網絡層協議,以及硬件如路由器之類在哪一層
- ?應用層:HTTP、SMTP、DNS、FTP
- ? 傳輸層:TCP 、UDP
- ? 網絡層:ICMP 、IP、路由器、防火墻
- ? 數據鏈路層:網卡、網橋、交換機
- ? 物理層:中繼器、集線器
TCP與UDP區別和應用場景,基于TCP的協議有哪些,基于UDP的有哪些
- 類型 特點 性能 應用過場景 首部字節
- TCP 面向連接、可靠、字節流 傳輸效率慢、所需資源多 文件、郵件傳輸 20-60
- UDP 無連接、不可靠、數據報文段 傳輸效率快、所需資源少 語音、視頻、直播 8個字節
- ? 基于TCP的協議:HTTP、FTP、SMTP
- ? 基于UDP的協議:RIP、DNS、SNMP
TCP可靠傳輸的保證,擁塞控制目的和過程
- TCP通過:應用數據分割、對數據包進行編號、校驗和、流量控制、擁塞控制、ARP協議、超時重傳等措施保證數據的可靠傳輸;
- 擁塞控制目的:為了防止過多的數據注入到網絡中,避免網絡中的路由器、鏈路過載;
- 擁塞控制過程:TCP發送發將維護一個擁塞窗口的狀態變量,該變量隨著網絡擁塞程度動態變化,通過慢開始、擁塞避免等算法減少網絡擁塞的發生。
TCP粘包現象原因和解決方法
- TCP粘包是指:發送方發送的若干包數據到接收方接收時粘成一包
- 發送方原因:
- TCP發送發將維護一個擁塞窗
- TCP默認使用Nagle算法(主要作用:減少網絡中報文段的數量),而Nagle算法主要做兩件事:
- 只有上一個分組得到確認,才會發送下一個分組收集多個小分組,在一個確認到來時一起發送
- Nagle算法造成了發送方可能會出現粘包問題
- 接收方原因
- TCP接收到數據包交到應用層進行處理時,并不會馬上,或者說應用層并不會立即處理。
- 實際上, TCP將接收到的數據包保存在接收緩存里,然后應用程序主動從緩存讀取收到的分組。
- 這樣一來,如果 TCP 接收數據包到緩存的速度大于應用程序從緩存中讀取數據包的速度,多個包就會被緩存,應用程序就有可能讀取到多個首尾相接粘到一起的包。解決粘包問題:
- 解決粘包問題
- 最本質原因在與接收對等方無法分辨消息與消息之間的邊界在哪,通過使用某種方案給出邊界,例如:
- 發送定長包。如果每個消息的大小都是一樣的,那么在接收對等方只要累計接收數據,直到數據等于一個定長的數值就將它作為一個消息。
- 包尾加上\r\n標記。FTP協議正是這么做的。但問題在于如果數據正文中也含有\r\n,則會誤判為消息的邊界。
- 包頭加上包體長度。包頭是定長的4個字節,說明了包體的長度。接收對等方先接收包體長度,依據包體長度來接收包體。
TCP三次握手過程以及每次握手后的狀態改變,為什么三次? 為什么兩次不行?
三次握手過程:
? 客戶端——發送帶有SYN標志的數據包——服務端 一次握手 Client進入syn_sent狀態
? 服務端——發送帶有SYN/ACK標志的數據包——客戶端 二次握手 服務端進入syn_rcvd
? 客戶端——發送帶有ACK標志的數據包——服務端 三次握手 連接就進入Established狀態
??為什么三次:
? 主要是為了建立可靠的通信信道,保證客戶端與服務端同時具備發送、接收數據的能力
? 為什么兩次不行?
? 1、防止已失效的請求報文又傳送到了服務端,建立了多余的鏈接,浪費資源
? 2、 兩次握手只能保證單向連接是暢通的。(為了實現可靠數據傳輸, TCP 協議的通信雙方, 都必須維 護一個序列號, 以標識發送出去的數據包中, 哪些是已經被對方收到的。 三次握手的過程即是通信雙方 相互告知序列號起始值, 并確認對方已經收到了序列號起始值的必經步驟;如果只是兩次握手, 至多只 有連接發起方的起始序列號能被確認, 另一方選擇的序列號則得不到確認)
TCP四次揮手過程以及狀態改變,為什么四次?CLOSE-WAIT和TIME-WAIT存在的意義?如何查看TIME-WAIT狀態的鏈接數量?為什么會TIME-WAIT過多?解決方法是怎樣的?
四次揮手過程:
? 客戶端——發送帶有FIN標志的數據包——服務端,關閉與服務端的連接 ,客戶端進入FIN-WAIT-1狀態
? 服務端收到這個 FIN,它發回? 個 ACK,確認序號為收到的序號加1,服務端就進入了CLOSE-WAIT狀態
? 服務端——發送?個FIN數據包——客戶端,關閉與客戶端的連接,客戶端就進入FIN-WAIT-2狀態
? 客戶端收到這個 FIN,發回 ACK 報?確認,并將確認序號設置為收到序號加1,TIME-WAIT狀態
為什么四次:
? 因為需要確保客戶端與服務端的數據能夠完成傳輸。
CLOSE-WAIT:
? 這種狀態的含義其實是表示在等待關閉
TIME-WAIT:
? 為了解決網絡的丟包和網絡不穩定所帶來的其他問題,確保連接方能在時間范圍內,關閉自己的連接
如何查看TIME-WAIT狀態的鏈接數量?
? netstat -an |grep TIME_WAIT|wc -l 查看連接數等待time_wait狀態連接數
為什么會TIME-WAIT過多?解決方法是怎樣的?
? 可能原因: 高并發短連接的TCP服務器上,當服務器處理完請求后立刻按照主動正常關閉連接
? 解決:負載均衡服務器;Web服務器首先關閉來自負載均衡服務器的連接
瀏覽器輸入URL并回車的過程以及相關協議,DNS查詢過程。
過程:DNS解析、TCP連接、發送HTTP請求、服務器處理請求并返回HTTP報文、瀏覽器渲染、結束
過程 使用的協議
1、瀏覽器查找域名DNS的IP地址
DNS查找過程(瀏覽器緩存、路由器緩存、DNS緩存) DNS:獲取域名對應的ip
2、根據ip建立TCP連接 TCP:與服務器建立連接
3、瀏覽器向服務器發送HTTP請求 HTTP:發送請求
4、服務器響應HTTP響應 HTTP
5、瀏覽器進行渲染
HTTP1.0、1.1、2.0之間的區別
HTTP1.0:默認使用Connection:cloose,瀏覽器每次請求都需要與服務器建立一個TCP連接,服務器處理完成后立即斷開TCP連接(無連接),服務器不跟蹤每個客戶端也不記錄過去的請求(無狀態)。
? HTTP1.1:默認使用Connection:keep-alive(長連接),避免了連接建立和釋放的開銷;通過Content-Length字段來判斷當前請求的數據是否已經全部接受。不允許同時存在兩個并行的響應。
HTTP2.0:引入二進制數據幀和流的概念,其中幀對數據進行順序標識;因為有了序列,服務器可以并行的傳輸數據。
http1.0和http1.1的主要區別如下:
- ?1、緩存處理:1.1添加更多的緩存控制策略(如:Entity tag,If-Match)
- ? 2、網絡連接的優化:1.1支持斷點續傳
- ? 3、錯誤狀態碼的增多:1.1新增了24個錯誤狀態響應碼,豐富的錯誤碼更加明確各個狀態
- ? 4、Host頭處理:支持Host頭域,不在以IP為請求方標志
- ? 5、長連接:減少了建立和關閉連接的消耗和延遲。
? http1.1和http2.0的主要區別:
- ?1、新的傳輸格式:2.0使用二進制格式,1.0依然使用基于文本格式
- ? 2、多路復用:連接共享,不同的request可以使用同一個連接傳輸(最后根據每個request上的id號組合成 正常的請求)
- ? 3、header壓縮:由于1.X中header帶有大量的信息,并且得重復傳輸,2.0使用encoder來減少需要傳輸的 hearder大小
- ? 4、服務端推送:同google的SPDUY(1.0的一種升級)一樣
HTTP與HTTPS之間的區別,HTTPS鏈接建立的過程,了解對稱加密算法和非對稱加密算法不?? HTTP與HTTPS之間的區別
| HTTP | ?HTTPS |
| 默認端口80 | HTTPS默認使用端口443 |
| 明文傳輸、數據未加密、安全性差 | 傳輸過程ssl加密、安全性較好 |
| 響應速度快、消耗資源少 | 響應速度較慢、消耗資源多、需要用到CA證書 |
? HTTPS鏈接建立的過程
? 1.首先客戶端先給服務器發送一個請求
? 2.服務器發送一個SSL證書給客戶端,內容包括:證書的發布機構、有效期、所有者、簽名以及公鑰
? 3.客戶端對發來的公鑰進行真偽校驗,校驗為真則使用公鑰對對稱加密算法以及對稱密鑰進行加密
? 4.服務器端使用私鑰進行解密并使用對稱密鑰加密確認信息發送給客戶端
? 5.隨后客戶端和服務端就使用對稱密鑰進行信息傳輸
對稱加密算法
? 雙方持有相同的密鑰,且加密速度快,典型對稱加密算法:DES、AES
? 非對稱加密算法
? 密鑰成對出現(私鑰、公鑰),私鑰只有自己知道,不在網絡中傳輸;而公鑰可以公開。相比對稱加密速度較慢,典型的非對稱加密算法有:RSA、DSA
HTTP請求有哪些?get和Post區別
方法 描述
- GET 向特定資源發送請求,查詢數據,并返回實體
- POST 向指定資源提交數據進行處理請求,可能會導致新的資源建立、已有資源修改
- PUT 向服務器上傳新的內容
- HEAD 類似GET請求,返回的響應中沒有具體的內容,用于獲取報頭
- DELETE 請求服務器刪除指定標識的資源
- OPTIONS 可以用來向服務器發送請求來測試服務器的功能性
- TRACE 回顯服務器收到的請求,用于測試或診斷
- CONNECT HTTP/1.1協議中預留給能夠將連接改為管道方式的代理服務器
get和Post區別:(post安全性更高非加密的原因)
| 方面 | GET | POST |
| 可見性 | 數據在URL中對所有人可見 | 數據不會顯示在URL中 |
| 安全性 | 與post相比,get的安全性較差,因為所 發送的數據是URL的一部分 | 安全,因為參數不會被保存在瀏覽器歷史或web服務器日志中 |
| 數據長度 | 受限制,最長2kb | 無限制 |
| 編碼類型 | application/x-www-form-urlencoded | multipart/form-data |
| 緩存 | 能被緩存 | 不能被緩存 |
HTTP常見響應狀態碼,從1xx到5xx
- ? 100:Continue --- 繼續。客戶端應繼續其請求。
- ? 200:OK --- 請求成功。一般用于GET與POST請求。
- ? 301:Moved Permanently --- 永久重定向。
- ? 302:Found --- 暫時重定向。
- ? 400:Bad Request --- 客戶端請求的語法錯誤,服務器無法理解。
- ? 403:Forbideen --- 服務器理解請求客戶端的請求,但是拒絕執行此請求。
- ? 404:Not Found --- 服務器無法根據客戶端的請求找到資源(網頁)。
- ? 500:Internal Server Error --- 服務器內部錯誤,無法完成請求。
- ? 502:Bad Gateway --- 作為網關或者代理服務器嘗試執行請求時,從遠程服務器接收到了無效的響應。
-
重定向和轉發區別
-
重定向:redirect
轉發:forward
? 地址欄發生變化
轉發地址欄路徑不變
重定向可以訪問其他站點(服務器)的資源
轉發只能訪問當前服務器下的資源
重定向是兩次請求。不能使用request對象來共享數據
轉發是一次請求,可以使用request對象共享數據
cookie和session區別
- ?Cookie 和 Session都是用來跟蹤瀏覽器用戶身份的會話方式,但兩者有所區別:
- ? Cookie 數據保存在客戶端(瀏覽器端),Session 數據保存在服務器端。
- ? cookie不是很安全,別人可以分析存放在本地的COOKIE并進行欺騙,考慮到安全應當使用session。
- ? Cookie ?般?來保存?戶信息,Session 的主要作?就是通過服務端記錄?戶的狀態
- ?
操作系統
進程和線程的區別
?進程:是資源分配的最小單位,是程序的執行過程,一個進程可以有多個線程,多個線程共享進程的堆和方法區資源,但每個線程又有屬于自己的本地方法棧、虛擬機棧、程序計數器
? 線程:是任務調度和執行的最小單位,線程間可能存在相互影響,執行開銷較小,不利于資源的管理和保護,線程間是共享進程中的資源的
協程
是一種比線程更加輕量級的存在,正如一個進程可以擁有多個線程一樣,一個線程可以擁有多個協程。
進程間通信方式IPC
匿名管道pipe
? 匿名管道是半雙工的,數據只能單向通信;需要雙方通信時,需要建立起兩個管道;只能用于父子進程或者兄弟進程之間(具有親緣關系的進程)。
命名管道FIFO
不同于匿名管道之處在于它提供一個路徑名與之關聯,以FIFO的文件形式存在于文件系統中。這樣,即使與FIFO的創建進程不存在親緣關系的進程,只要可以訪問該路徑,就能夠彼此通過FIFO相互通信(能夠訪問該路徑的進程以及FIFO的創建進程之間),因此,通過FIFO不相關的進程也能交換數據。值得注意的是,FIFO嚴格遵循先進先出(first in first out),對管道及FIFO的讀總是從開始處返回數據,對它們的寫則把數據添加到末尾。
信號
?信號是一種比較復雜的通信方式,信號產生的條件:按鍵、硬件異常、進程調用kill函數將信號發送給另一個進程、用戶調用kill命令將信號發送給其他進程,信號傳遞的消息比較少,主要用于通知接收進程某個時間已經發生。
消息隊列
? 消息隊列是消息的鏈表,存放在內核中并由消息隊列標識符標識,消息隊列克服了信號傳遞信息少,管道只能承載無格式字節流以及緩沖區大小受限等特點。消息隊列起信箱作用,到了就掛在那里,需要的時候去取。消息隊列提供了一種在兩個不相關進程間傳遞數據的簡單有效的方法。與命名管道相比:消息隊列的優勢在于,它獨立于發送和接收進程而存在,這消除了在同步命名管道的打開和關閉時可能產生的一些困難。消息隊列提供了一種從一個進程向另一個進程發送一個數據塊的方法。而且,每個數據塊被認為含有一個類型,接收進程可以獨立地接收含有不同類型值的數據塊。
? 優點:
? A. 我們可以通過發送消息來幾乎完全避免命名管道的同步和阻塞問題。
? B. 我們可以用一些方法來提前查看緊急消息。
? 缺點:
? A. 與管道一樣,每個數據塊有一個最大長度的限制。
? B. 系統中所有隊列所包含的全部數據塊的總長度也有一個上限。
使得多個進程可以可以直接讀寫同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。
為了在多個進程間交換信息,內核專門留出了一塊內存區,可以由需要訪問的進程將其映射到自己的私有地址空間。進程就可以直接讀寫這一塊內存而不需要進行數據的拷貝,從而大大提高效率。
由于多個進程共享一段內存,因此需要依靠某種同步機制(如信號量)來達到進程間的同步及互斥。
信號量(Semaphores)
信號量是?個計數器,?于多進程對共享數據的訪問,信號量的意圖在于進程間同步。這種通信?式主要?于解決與同步相關的問題并避免競爭條件。
套接字(Sockets)
此?法主要?于在客戶端和服務器之間通過?絡進?通信。套接字是?持TCP/IP 的?絡通信的基本操作單元,可以看做是不同主機之間的進程進?雙向通信的端點,簡單的說就是通信的兩?的?種約定,?套接字中的相關函數來完成通信過程。
用戶態和核心態
?在計算機系統中,分兩種程序:系統程序和應用程序,為了保證系統程序不被應用程序有意或無意地破壞,為計算機設置了兩種狀態——用戶態、核心態
用戶態:只能受限的訪問內存,運行所有的應用程序
核心態:運行操作系統程序,cpu可以訪問內存的所有數據,包括外圍設備
為什么要有用戶態和內核態:
? 由于需要限制不同的程序之間的訪問能力, 防止他們獲取別的程序的內存數據, 或者獲取外圍設備的數據, 并發送到網絡
用戶態切換到內核態的3種方式:
??a. 系統調用
? 這是用戶態進程主動要求切換到內核態的一種方式,用戶態進程通過系統調用申請使用操作系統提供的服務程序完成工作,比如前例中fork()實際上就是執行了一個創建新進程的系統調用。而系統調用的機制其核心還是使用了操作系統為用戶特別開放的一個中斷來實現,例如Linux的int 80h中斷。
??b. 異常
? 當CPU在執行運行在用戶態下的程序時,發生了某些事先不可知的異常,這時會觸發由當前運行進程切換到處理此異常的內核相關程序中,也就轉到了內核態,比如缺頁異常。
? c. 外圍設備的中斷
? 當外圍設備完成用戶請求的操作后,會向CPU發出相應的中斷信號,這時CPU會暫停執行下一條即將要執行的指令轉而去執行與中斷信號對應的處理程序,如果先前執行的指令是用戶態下的程序,那么這個轉換的過程自然也就發生了由用戶態到內核態的切換。比如硬盤讀寫操作完成,系統會切換到硬盤讀寫的中斷處理程序中執行后續操作等。
? 這3種方式是系統在運行時由用戶態轉到內核態的最主要方式,其中系統調用可以認為是用戶進程主動發起的,異常和外圍設備中斷則是被動的。
操作系統分配的進程空間是怎樣的?線程能共享哪些?
?棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。
? 堆區(heap)— 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。
? 靜態區(static)—存放全局變量和靜態變量的存儲
? 代碼區(text)—存放函數體的二進制代碼。
? 線程共享堆區、靜態區
操作系統內存管理方式,分頁分段以及段頁式的優缺點
存管理方式:塊式管理、頁式管理、段式管理、段頁式管理
分段管理:
? 在段式存儲管理中,將程序的地址空間劃分為若干段(segment),如代碼段,數據段,堆棧段;這樣每個進程有一個二維地址空間,相互獨立,互不干擾。段式管理的優點是:沒有內碎片(因為段大小可變,改變段大小來消除內碎片)。但段換入換出時,會產生外碎片(比如4k的段換5k的段,會產生1k的外碎片)
分頁管理:
? 在頁式存儲管理中,將程序的邏輯地址劃分為固定大小的頁(page),而物理內存劃分為同樣大小的頁框,程序加載時,可以將任意一頁放入內存中任意一個頁框,這些頁框不必連續,從而實現了離散分離。頁式存儲管理的優點是:沒有外碎片(因為頁的大小固定),但會產生內碎片(一個頁可能填充不滿)
段頁式管理:
? 段?式管理機制結合了段式管理和?式管理的優點。簡單來說段?式管理機制就是把主存先分成若? 段,每個段?分成若??,也就是說 段?式管理機制 中段與段之間以及段的內部的都是離散的。
頁面置換算法有哪些,FIFO為什么不好?如何改進?LRU思想,手寫LRU
置換算法:先進先出FIFO、最近最久未使用LRU、最佳置換算法OPT
先進先出FIFO:
? 原理:把內存中駐留時間最久的頁面置換算法予以淘汰
? 優點:實現簡單、直觀
? 缺點:沒有考慮到實際的頁面使用頻率,性能差、與通常頁面使用的規則不符合,實際應用較少
? 改進:給每個頁面增加一個R位,每次先從鏈表頭開始查找,如果R置位,清除R位并且把該頁面節點放 到鏈表結尾;如果R是0,那么就是又老又沒用到,替換掉。
最近最久未使用LRU:
? 原理:選擇最近且最久未使用的頁面進行淘汰
? 優點:考慮到了程序訪問的時間局部性,有較好的性能,實際應用也比較多
? 缺點:實現需要比較多的硬件支持,會增加一些硬件成本
public class LRUCache {private LinkedHashMap<Integer,Integer> cache;private int capacity; //容量大小/***初始化構造函數* @param capacity*/public LRUCache(int capacity) {cache = new LinkedHashMap<>(capacity);this.capacity = capacity;}public int get(int key) {//緩存中不存在此key,直接返回if(!cache.containsKey(key)) {return -1;}int res = cache.get(key);cache.remove(key); //先從鏈表中刪除cache.put(key,res); //再把該節點放到鏈表末尾處return res;}public void put(int key,int value) {if(cache.containsKey(key)) {cache.remove(key); //已經存在,在當前鏈表移除}if(capacity == cache.size()) {//cache已滿,刪除鏈表頭位置Set<Integer> keySet = cache.keySet();Iterator<Integer> iterator = keySet.iterator();cache.remove(iterator.next());}cache.put(key,value); //插入到鏈表末尾} } class LRUCache {private Map<Integer, Integer> map;private int capacity;/***初始化構造函數* @param capacity*/public LRUCache(int capacity) {this.capacity = capacity;map = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true) {@Overrideprotected boolean removeEldestEntry(Map.Entry eldest) {return size() > capacity; // 容量大于capacity 時就刪除}};}public int get(int key) {//返回key對應的value值,若不存在,返回-1return map.getOrDefault(key, -1);}public void put(int key, int value) {map.put(key, value);} }最佳置換算法OPT:
? 原理:每次選擇當前物理塊中的頁面在未來長時間不被訪問的或未來不再使用的頁面進行淘汰
? 優點:具有較好的性能,可以保證獲得最低的缺頁率
? 缺點:過于理想化,但是實際上無法實現(沒辦法預知未來的頁面)
死鎖條件,解決方式
死鎖是指兩個或兩個以上進程在執行過程中,因爭奪資源而造成的下相互等待的現象;
? 死鎖的條件:
? 互斥條件:進程對所分配到的資源不允許其他進程訪問,若其他進程訪問該資源,只能等待,直至占有該資源的進程使用完成后釋放該資源;
? 請求與保持條件:進程獲得一定的資源后,又對其他資源發出請求,但是該資源可能被其他進程占有,此時請求阻塞,但該進程不會釋放自己已經占有的資源
? 非剝奪條件:進程已獲得的資源,在未完成使用之前,不可被剝奪,只能在使用后自己釋放
? 循環等待條件:系統中若干進程組成環路,環路中每個進程都在等待相鄰進程占用的資源
? 解決方法:
破壞死鎖的任意一條件
? 資源一次性分配,從而剝奪請求和保持條件
? 可剝奪資源:即當進程新的資源未得到滿足時,釋放已占有的資源,從而破壞不可剝奪的條件
? 資源有序分配法:系統給每類資源賦予一個序號,每個進程按編號遞增的請求資源,釋放則相反,從而破壞環路等待的條件
Java基礎篇
Java面向對象特性介紹、與C++區別
特性:封裝、繼承、多態
? 封裝:對抽象的事物抽象化成一個對象,并對其對象的屬性私有化,同時提供一些能被外界訪問屬性的方法,這樣一個對象便有存在的意義了;
? 繼承:在已存在類的基礎上,建立新類并對其增加新的數據域或功能,同時該類可以復用父類的屬性與功能,這種思路可以稱為繼承;通過使用繼承能夠方便地復用舊代碼,減少不必要的代碼量;
? 多態:指程序中的某個引用變量,它所指向的具體類型以及該引用變量發出的方法調用,在編程時不能確定,要在程序運行并使用時由機器自己判別確定;實現多態的方式有兩種方式,可以通過繼承(多個?類對同??法的重寫)、也可以通過接?(實現接?并覆蓋接?中同??法)
Java與C++區別:
? 相同點:都是面向對象語言,并且都支持封裝、繼承、多態
? 不同點:c++支持多繼承,并且有指針的概念,由程序員自己管理內存;Java是單繼承,可以用接口實現多繼承,Java 不提供指針來直接訪問內存,程序內存更加安全,并且Java有JVM?動內存管理機制,不需要程序員?動釋放??內存
多態實現原理
多態的底層實現是動態綁定,即在運行時才把方法調用與方法實現關聯起來。
靜態綁定與動態綁定:
? JVM 的方法調用指令有五個,分別是:
? invokestatic:調用靜態方法;
? invokespecial:調用實例構造器<init>方法、私有方法和父類方法;</init>
? invokevirtual:調用虛方法;
? invokeinterface:調用接口方法,運行時確定具體實現;
? invokedynamic:運行時動態解析所引用的方法,然后再執行,用于支持動態類型語言。
? invokestatic 和 invokespecial 用于靜態綁定
? invokevirtual 和 invokeinterface 用于動態綁定
? 可以看出,動態綁定主要應用于虛方法和接口方法。
? 虛方法的方法調用與方法實現的關聯(也就是分派)有兩種,一種是在編譯期確定,被稱為靜態分派,比如方法的重載;一種是在運行時確定,被稱為動態分派,比如方法的覆蓋(重寫)。對象方法基本上都是虛方法。
多態的實現
? 虛擬機棧中會存放當前方法調用的棧幀(局部變量表、操作棧、動態連接 、返回地址)。多態的實現過程,就是方法調用動態分派的過程,通過棧幀的信息去找到被調用方法的具體實現,然后使用這個具體實現的直接引用完成方法調用。
以 invokevirtual 指令為例,在執行時,大致可以分為以下幾步:
先從操作棧中找到對象的實際類型 class;
找到 class 中與被調用方法簽名相同的方法,如果有訪問權限就返回這個方法的直接引用,如果沒有訪問權限就報錯 java.lang.IllegalAccessError ;
如果第 2 步找不到相符的方法,就去搜索 class 的父類,按照繼承關系自下而上依次執行第 2 步的操作;
如果第 3 步找不到相符的方法,就報錯 java.lang.AbstractMethodError ;
可以看到,如果子類覆蓋了父類的方法,則在多態調用中,動態綁定過程會首先確定實際類型是子類,從而先搜索到子類中的方法。這個過程便是方法覆蓋的本質。
抽象類和接口區別,以及各自的使用場景
抽象類
- 包含抽象方法的類,即使用abstract修飾的類;
- 不能使用final修飾,final修飾的類不能被繼承;
- 抽象類不能被實例化,只能被繼承
接口
- 接口是一個抽象類型,是抽象方法的集合,接口以interface來聲明。
- 一個類通過繼承接口的方式,從而來繼承接口的抽象方法;
- 接口只能繼承接口,不能繼承類,接口支持多繼承;
- 接口中的定義的成員變量,默認是public static final修飾的靜態常量;
- 接口中定義的方法,默認是public abstract修飾的抽象方法
相同點:
- 抽象類和接口都不能被實例化
- ? ② 抽象類和接口都可以定義抽象方法,子類/實現類必須覆寫這些抽象方法
不同點:
- ? ① 抽象類有構造方法,接口沒有構造方法
- ? ③抽象類可以包含普通方法,接口中只能是public abstract修飾抽象方法(Java8之后可以)
- ? ③ 抽象類只能單繼承,接口可以多繼承
- ? ④ 抽象類可以定義各種類型的成員變量,接口中只能是public static final修飾的靜態常量
抽象類的使用場景
既想約束子類具有共同的行為(但不再乎其如何實現),又想擁有缺省的方法,又能擁有實例變量
接口的應用場景
? 約束多個實現類具有統一的行為,但是不在乎每個實現類如何具體實現;
實現類需要具備很多不同的功能,但各個功能之間可能沒有任何聯系
泛型以及泛型擦除。List類型的list,可以加入無繼承關系的B類型對象嗎?如何加入?
泛型
? 泛型提供了編譯時類型安全檢測機制,該機制允許程序員在編譯時檢測到非法的類型。泛型的本質是參數化類型,也就是說所操作的數據類型被指定為一個參數。這種參數類型可以用在類、接口和方法的創建中,分別稱為泛型類、泛型接口和泛型方法。
泛型擦除
? Java的泛型是偽泛型,這是因為Java在編譯期間,所有的泛型信息都會被擦掉,正確理解泛型概念的首要前提是理解類型擦除。Java的泛型基本上都是在編譯器這個層次上實現的,在生成的字節碼中是不包含泛型中的類型信息的,使用泛型的時候加上類型參數,在編譯器編譯的時候會去掉,這個過程成為類型擦除。
? 如在代碼中定義的 List<object>和 List<string>等類型,在編譯之后都會變成 List。JVM 看到的只是 List,而由泛型附加的類型信息對 JVM 來說是不可見的。</string></object>
Java異常體系
Throwable 是 Java 語言中所有錯誤或異常的超類。下一層分為 Error 和 Exception
Error?
? 是指 java 運行時系統的內部錯誤和資源耗盡錯誤。應用程序不會拋出該類對象。如果出現了這樣的錯誤,除了告知用戶,剩下的就是盡力使程序安全的終止。
Exception 包含:RuntimeException 、CheckedException
- (1) RuntimeException:運行時異常
? 如 NullPointerException 、 ClassCastException ;
? RuntimeException 是那些可能在 Java 虛擬機正常運行期間拋出的異常的超類,這些異常一般是由程序邏輯錯誤引起的,程序應該從邏輯角度盡可能避免這類異常的發生。
(2) CheckedException:受檢異常
? 如 I/O 錯誤導致的 IOException、SQLException;
? CheckedException:一般是外部錯誤,這種異常都發生在編譯階段,Java 編譯器會強制程序去捕獲此類
異常,即會出現要求你把這段可能出現異常的程序進行 try catch,該類異常一般包括幾個方面:
? ①試圖在文件尾部讀取數據
? ②試圖打開一個錯誤格式的 URL
? ③試圖根據給定的字符串查找 class 對象,而這個字符串表示的類并不存在
反射原理以及使用場景
Java反射
是指在運行狀態中,對于任意一個類都能夠知道這個類所有的屬性和方法;并且對于任意一個對象,都能夠調用它的任意一個方法;這種動態獲取信息以及動態調用對象方法的功能成為 Java 語言的反射機制。
反射原理
? 反射首先是能夠獲取到Java中的反射類的字節碼,然后將字節碼中的方法,變量,構造函數等映射成 相應的 Method、Filed、Constructor 等類
如何得到Class的實例
- 1.類名.class(就是一份字節碼)
- 2.Class.forName(String className);根據一個類的全限定名來構建Class對象
- 3.每一個對象多有getClass()方法:obj.getClass();返回對象的真實類型
使用場景
逆向代碼 ,例如反編譯;
動態生成類框架,如Spring:xml的配置模式。Spring 通過 XML 配置模式裝載 Bean 的過程:
- 1) 將程序內所有 XML 或 Properties 配置文件加載入內存中;
- 2)Java類里面解析xml或properties里面的內容,得到對應實體類的字節碼字符串以及相關的屬性信息;
- 3)使用反射機制,根據這個字符串獲得某個類的Class實例;
- 4)動態配置實例的屬性
ThreadLocal原理,如何使用?
ThreadLocal簡介
? 通常情況下,我們創建的變量是可以被任何?個線程訪問并修改的。如果想實現每?個線程都有??的專屬本地變量該如何解決呢? JDK中提供的 ThreadLocal 類正是為了解決這樣的問題。
原理
? 首先 ThreadLocal 是一個泛型類,保證可以接受任何類型的對象。因為一個線程內可以存在多個 ThreadLocal 對象,所以其實是 ThreadLocal 內部維護了一個 Map ,這個 Map 不是直接使用的 HashMap ,而是 ThreadLocal 實現的一個叫做 ThreadLocalMap 的靜態內部類。
? 最終的變量是放在了當前線程的 ThreadLocalMap 中,并不是存在 ThreadLocal 上,ThreadLocal 可以理解為只是ThreadLocalMap的封裝,傳遞了變量值。
我們使用的 get()、set() 方法其實都是調用了這個ThreadLocalMap類對應的 get()、set() 方法。例如下面的
public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value); } get方法: public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) return (T)map.get(this); // 如果不存在,則創建它 T value = initialValue(); createMap(t, value); return value; } createMap方法: void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } ThreadLocalMap是個靜態的內部類如何使用
1)存儲用戶Session private static final ThreadLocal threadSession = new ThreadLocal();public static Session getSession() throws InfrastructureException {Session s = (Session) threadSession.get();try {if (s == null) {s = getSessionFactory().openSession();threadSession.set(s);}} catch (HibernateException ex) {throw new InfrastructureException(ex);}return s; } ? 2)解決線程安全的問題 public class DateUtil {//SimpleDateFormat不是線程安全的,所以每個線程都要有??獨?的副本private static ThreadLocal<SimpleDateFormat> format1 = new ThreadLocal<SimpleDateFormat>() {@Overrideprotected SimpleDateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");}};public static String formatDate(Date date) {return format1.get().format(date);} }ThreadLocal內存泄漏的場景
? 實際上 ThreadLocalMap 中使用的 key 為 ThreadLocal 的弱引用,? value 是強引?。弱引用的特點是,如果這個對象持有弱引用,那么在下一次垃圾回收的時候必然會被清理掉。
? 所以如果 ThreadLocal 沒有被外部強引用的情況下,在垃圾回收的時候會被清理掉的,這樣一來 ThreadLocalMap中使用這個 ThreadLocal 的 key 也會被清理掉。但是,value 是強引用,不會被清理,這樣一來就會出現 key 為 null 的 value。 假如我們不做任何措施的話,value 永遠?法被GC 回收,這個時候就可能會產?內存泄露。
? ThreadLocalMap實現中已經考慮了這種情況,在調用 set()、get()、remove() 方法的時候,會清理掉 key 為 null 的記錄。如果說會出現內存泄漏,那只有在出現了 key 為 null 的記錄后,沒有手動調用 remove() 方法,并且之后也不再調用 get()、set()、remove() 方法的情況下。
? 因此使?完ThreadLocal ?法后,最好?動調? remove() ?法。
static關鍵字和final關鍵字使用情況,一個類不能被繼承,除了final關鍵字之外,還有什么方法(從構造函數考慮)?
static:可以修飾屬性、方法
static修飾屬性
? 所有對象共享一份,一個對象對其修改,其他的調用也會受到影響,類級別;隨著類的加載而加載(只加載一次),先于對象的創建;可以使用類名直接調用。
static修飾方法
? 隨著類的加載而加載;可以使用類名直接調用;靜態方法中,只能調用靜態的成員;非靜態的方法中,可以調用靜態和非靜態的成員;在靜態方法中,不會出現this。
final:關鍵字主要?在三個地?:變量、?法、類。
? final修飾變量
? 對于?個 final 變量,如果是基本數據類型的變量,則其數值?旦在初始化之后便不能更改;如果是引?類型的變量,則在對其初始化之后便不能再讓其指向另?個對象。
? final修飾方法
? 把?法鎖定,以防任何繼承類修改它的含義(重寫);類中所有的 private ?法都隱式地指定為 final。
? final修飾類
? final 修飾類時,表明這個類不能被繼承。final 類中的所有成員?法都會被隱式地指定為 final ?法。
序列化和反序列化。反序列化失敗的場景
? 序列化的意思就是將對象的狀態轉化成字節流,以后可以通過這些值再生成相同狀態的對象。對象序列化是對象持久化的一種實現方法,它是將對象的屬性和方法轉化為一種序列化的形式用于存儲和傳輸。反序列化就是根據這些保存的信息重建對象的過程。
序列化:將java對象轉化為字節序列的過程。
反序列化:將字節序列轉化為java對象的過程。
優點
? a、實現了數據的持久化,通過序列化可以把數據永久地保存到硬盤上(通常存放在文件里)
? b、利用序列化實現遠程通信,即在網絡上傳送對象的字節序列。
反序列化失敗的場景
? 序列化ID:serialVersionUID不一致的時候,導致反序列化失敗
ArrayList和LinkedList的區別和底層實現?如何實現線程安全?
ArrayList
? 底層基于數組實現,支持對元素進行快速隨機訪問,支持元素重復;默認初始大小為10,當數組容量不夠時,會觸發擴容機制(擴大到當前的1.5倍),需要將原來數組的數據復制到新的數組中;當從 ArrayList 的中間位置插入或者刪除元素時,需要對數組進行復制、移動、代價比較高。因此,它適合隨機查找和遍歷,不適合插入和刪除。
LinkedList
? 底層基于雙向鏈表實現,適合數據的動態插入和刪除;內部提供了 List 接口中沒有定義的方法,用于操作表頭和表尾元素,可以當作堆棧、隊列和雙向隊列使用。
ArrayList與LinkedList區別
? 都是線程不安全的,ArrayList 適用于查找的場景,LinkedList 適用于 增加、刪除多的場景
線程安全
? 可以使用原生的Vector,或者是Collections.synchronizedList(List list)函數返回一個線程安全的ArrayList集合,或者使用concurrent并發包下的CopyOnWriteArrayList的。
? ①、Vector: 底層通過synchronize修飾保證線程安全,效率較差
? ② 、Collections.synchronizedList(List list):
//使用Collections.synchronizedList(List list)方法實現線程安全
List<?> list=Collections.synchronizedList(new ArrayList<>());
? ③、CopyOnWriteArrayList:寫時加鎖,使用了一種叫寫時復制的方法;讀操作是可以不用加鎖的
List遍歷時如何刪除元素?fail—fast是什么?fail—safe是什么?
①、普通for循環遍歷List刪除指定元素 for(int i=0; i < list.size(); i++){if(list.get(i) == 5)list.remove(i); } ② 、迭代遍歷,用list.remove(i)方法刪除元素 Iterator<Integer> it = list.iterator(); while(it.hasNext()){Integer value = it.next();if(value == 5){list.remove(value);} } ③、foreach遍歷List刪除元素 for(Integer i:list){if(i==3) list.remove(i); }fail—fast:快速失敗
? 當異常產生時,直接拋出異常,程序終止;
? fail-fast只要是體現在當我們在遍歷集合元素的時候,經常會使用迭代器,但在迭代器遍歷元素的過程中,如果集合的結構被改變的話,就會拋出異常ConcurrentModificationException,防止繼續遍歷。這就是所謂的快速失敗機制。這里要注意的這里說的結構被改變,是例如插入和刪除這種操作,只是改變集合里的值的話并不會拋出異常。
fail—safe:安全失敗
采用安全失敗機制的集合容器,在遍歷時不是直接在集合內容上訪問的,而是先復制原有集合內容,在拷貝的集合上進行遍歷。
原理:由于迭代時是對原集合的拷貝進行遍歷,所以在遍歷過程中對原集合所作的修改并不能被迭代器檢測到,所以不會觸發ConcurrentModificationException。
缺點:基于拷貝內容的優點是避免了ConcurrentModificationException,但同樣地,迭代器并不能訪問到修改后的內容,即:迭代器遍歷的是開始遍歷那一刻拿到的集合拷貝,在遍歷期間原集合發生的修改迭代器是不知道的。
場景:java.util.concurrent包下的容器都是安全失敗,可以在多線程下并發使用,并發修改。
詳細介紹HashMap
角度:數據結構+擴容情況+put查找的詳細過程+哈希函數+容量為什么始終都是2^N,JDK1.7與1.8的區別。
數據結構
? HashMap在底層數據結構上采用了數組+鏈表+紅黑樹,通過散列映射來存儲鍵值對數據
擴容情況
? 默認的負載因子是0.75,表示的是,如果數組中已經存儲的元素個數大于數組長度的75%,將會引發擴容操作。
- ? 【1】創建一個長度為原來數組長度兩倍的新數組。
- ? 【2】重新對原數組中的Entry對象進行哈希運算,以確定他們各自在新數組中的新位置。
put操作步驟
- ?1、判斷數組是否為空,為空進行初始化;
- ? 2、不為空,則計算 key 的 hash 值,通過(n - 1) & hash計算應當存放在數組中的下標 index;
- ? 3、查看 table[index] 是否存在數據,沒有數據就構造一個Node節點存放在 table[index] 中;
- ? 4、存在數據,說明發生了hash沖突(存在二個節點key的hash值一樣), 繼續判斷key是否相等,相等,用新的value替換原數據;
- ? 5、若不相等,判斷當前節點類型是不是樹型節點,如果是樹型節點,創造樹型節點插入紅黑樹中;
- ? 6、若不是紅黑樹,創建普通Node加入鏈表中;判斷鏈表長度是否大于 8,大于則將鏈表轉換為紅黑樹;
- ? 7、插入完成之后判斷當前節點數是否大于閾值,若大于,則擴容為原數組的二倍
哈希函數
? hash函數是先拿到 key 的hashcode,是一個32位的值,然后讓hashcode的高16位和低16位進行異或操作。該函數也稱為擾動函數,做到盡可能降低hash碰撞。
容量為什么始終都是2^N
? 為了能讓 HashMap 存取?效,盡量較少碰撞,也就是要盡量把數據分配均勻。我們上?也講到了過了,Hash 值的范圍值-2147483648到2147483647,前后加起來?概40億的映射空間,只要哈希函數映射得?較均勻松散,?般應?是很難出現碰撞的。但問題是?個40億?度的數組,內存是放不下的。所以這個散列值是不能直接拿來?的。?之前還要先做對數組的?度取模運算,得到的余數才能?來要存放的位置也就是對應的數組下標。這個數組下標的計算?法是“ (n - 1) & hash ”。(n代表數組?度)。這也就解釋了 HashMap 的?度為什么是2的冪次?。
JDK1.7與1.8的區別
JDK1.7 HashMap
? 底層是 數組和鏈表 結合在?起使?也就是 鏈表散列。HashMap 通過 key 的hashCode 經過擾動函數處理過后得到 hash 值,然后通過 (n - 1) & hash 判斷當前元素存放的位置(這?的 n 指的是數組的?度),如果當前位置存在元素的話,就判斷該元素與要存?的元素的 hash值以及 key 是否相同,如果相同的話,直接覆蓋,不相同就通過拉鏈法解決沖突。
JDK1.8 HashMap
? HashMap在底層數據結構上采用了數組+鏈表+紅黑樹,通過散列映射來存儲鍵值對數據;當鏈表?度?于閾值(默認為 8),數組的?度大于 64時,將鏈表轉化為紅?樹,以減少搜索時間
HashMap如何實現線程安全?ConcurrentHashMap的底層實現?JDK1.7與JDK1.8的區別
可以通過ConcurrentHashMap 和 Hashtable來實現線程安全;Hashtable 是原始API類,通過synchronize同步修飾,效率低下;ConcurrentHashMap 通過分段鎖實現,效率較比Hashtable要好;
ConcurrentHashMap的底層實現:
? JDK1.7的 ConcurrentHashMap 底層采? 分段的數組+鏈表 實現;采用 分段鎖(Sagment) 對整個桶數組進?了分割分段(Segment),每?把鎖只鎖容器其中?部分數據,多線程訪問容器?不同數據段的數據,就不會存在鎖競爭,提?并發訪問率。
? JDK1.8的 ConcurrentHashMap 采?的數據結構跟HashMap1.8的結構?樣,數組+鏈表/紅??叉樹;摒棄了Segment的概念,?是直接? Node 數組+鏈表+紅?樹的數據結構來實現,通過并發控制 synchronized 和CAS來操作保證線程的安全。
linux指令知道哪些?
文件管理:ls、cd、touch創建普通文件、rm刪除、mkdir新建目錄、mv移動、cp拷貝、chmod修改權限
進程管理:ps顯示進程信息、kill殺死進程
系統管理:top、free顯示系統運行信息、vmstat輸出各資源使用情況
網絡通訊:ping測試網絡連通性、netstat顯示網絡相關信息
JVM虛擬機
JVM運行時內存劃分?
JVM運行時數據區域:堆、方法區(元空間)、虛擬機棧、本地方法棧、程序計數器
Heap(堆)
? 對象的實例以及數組的內存都是要在堆上進行分配的,堆是線程共享的一塊區域,用來存放對象實例,也是垃圾回收(GC)的主要區域;
? 堆細分:新生代、老年代,對于新生代又分為:Eden區和Surviver1和Surviver2區;
方法區
? 對于JVM的方法區也可以稱之為永久區,它儲存的是已經被java虛擬機加載的類信息、常量、靜態變量;Jdk1.8以后取消了方法區這個概念,稱之為元空間(MetaSpace);
虛擬機棧
? 虛擬機棧是線程私有的,他的生命周期和線程的生命ava虛擬機規范中,對此區域規定了兩種異常周期是一致的。里面裝的是一個一個的棧幀,每一個方法在執行的時候都會創建一個棧幀,棧幀中用來存放(局部變量表、操作數棧 、動態鏈接 、返回地址);在J狀況:如果線程請求的棧深度大于虛擬機所允許的深度,將會拋出StackOverflowError異常;如果虛擬機棧動態擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError異常。
- 局部變量表:局部變量表是一組變量值存儲空間,用來存放方法參數、方法內部定義的局部變量。局部變量表的容量是以變量槽(variable slot)為最小的單位。Java虛擬機沒有明確規定一個slot所占的空間大小。只是導向性的說了每一個slot能存放8中基本數據類型中的一種(long 和double這種64位的需要兩個slot);
- 操作數棧:是用來記錄一個方法在執行的過程中,字節碼指令向操作數棧中進行入棧和出棧的過程。大小在編譯的時候已經確定了,當一個方法剛開始執行的時候,操作數棧中是空發的,在方法執行的過程中會有各種字節碼指令往操作數棧中入棧和出棧。
- 動態鏈接:因為字節碼文件中有很多符號的引用,這些符號引用一部分會在類加載的解析階段或第一次使用的時候轉化成直接引用,這種稱為靜態解析;另一部分會在運行期間轉化為直接引用,稱為動態鏈接。
- 返回地址(returnAddress):類型(指向了一條字節碼指令的地址)
本地方法棧
? 本地方法棧和虛擬機棧類似,不同的是虛擬機棧服務的是Java方法,而本地方法棧服務的是Native方法。在HotSpot虛擬機實現中是把本地方法棧和虛擬機棧合二為一的,同理它也會拋出StackOverflowError和OOM異常。
PC程序計數器
? PC,指的是存放下一條指令的位置的這么一個區域。它是一塊較小的內存空間,且是線程私有的。由于線程的切換,CPU在執行的過程中,一個線程執行完了,接下來CPU切換到另一個線程去執行,另外一個線程執行完再切回到之前的線程,這時需要記住原線程的下一條指令的位置,所以每一個線程都需要有自己的PC。
堆內存分配策略
對象優先分配在Eden區,如果Eden區沒有足夠的空間進行分配時,虛擬機執行一次MinorGC。而那些無需回收的存活對象,將會進到 Survivor 的 From 區(From 區內存不足時,直接進入 Old 區)。
大對象直接進入老年代(需要大量連續內存空間的對象)。這樣做的目的是避免在Eden區和兩個Survivor區之間發生大量的內存拷貝(新生代采用復制算法收集內存)。
長期存活的對象進入老年代。虛擬機為每個對象定義了一個年齡(Age Count)計數器,如果對象經過了1次Minor GC那么對象會進入Survivor區,之后每經過一次Minor GC那么對象的年齡加1,直到達到閥值(默認15次),對象進入老年區。
動態判斷對象的年齡。如果Survivor區中相同年齡的所有對象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象可以直接進入老年代。
Full GC觸發條件
每次進行Minor GC時,JVM會計算Survivor區移至老年區的對象的平均大小,如果這個值大于老年區的剩余值大小,則進行一次Full GC,如果小于檢查HandlePromotionFailure設置,如果true則只進行Monitor GC,如果false則進行Full GC
如何判斷對象是否存活?回收對象的兩次標記過程。
引用計數法
? 給對象添加一個引用計數器,每當由一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻計數器為0的對象就是不可能再被使用的。
? 優點:實現簡單,判定效率也很高
? 缺點:他很難解決對象之間相互循環引用的問題。
對象可達性
? 通過一系列的成為“GC Roots”的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑成為引用鏈,當一個對象到GC ROOTS沒有任何引用鏈相連時,則證明此對象時不可用的;
兩次標記過程
? 對象被回收之前,該對象的finalize()方***被調用;兩次標記,即第一次標記不在“關系網”中的對象。第二次的話就要先判斷該對象有沒有實現finalize()方法了,如果沒有實現就直接判斷該對象可回收;如果實現了就會先放在一個隊列中,并由虛擬機建立的一個低優先級的線程去執行它,隨后就會進行第二次的小規模標記,在這次被標記的對象就會真正的被回收了。
垃圾回收算法
垃圾回收算法:復制算法、標記清除、標記整理、分代收集
復制算法
? 將內存分為??相同的兩塊,每次使?其中的?塊。當這?塊的內存使?完后,就將還存活的對象復制到另?塊去,然后再把使?的空間?次清理掉。這樣就使每次的內存回收都是對內存區間的?半進?回收;
? 優點:實現簡單,內存效率高,不易產生碎片
? 缺點:內存壓縮了一半,倘若存活對象多,Copying 算法的效率會大大降低
標記清除
? 標記出所有需要回收的對象,在標記完成后統?回收所有被標記的對象
? 缺點:效率低,標記清除后會產??量不連續的碎?,可能發生大對象不能找到可利用空間的問題。
標記整理
? 標記過程仍然與“標記-清除”算法?樣,再讓所有存活的對象向?端移動,然后直接清理掉端邊界以外的內存;解決了產生大量不連續碎片問題
分代收集:根據各個年代的特點選擇合適的垃圾收集算法。
新生代采用復制算法,新生代每次垃圾回收都要回收大部分對象,存活對象較少,即要復制的操作比較少,一般將新生代劃分為一塊較大的 Eden 空間和兩個較小的 Survivor 空間(From Space, To Space),每次使用Eden 空間和其中的一塊 Survivor 空間,當進行回收時,將該兩塊空間中還存活的對象復制到另一塊 Survivor 空間中。
年代的對象存活?率是?較?的,?且沒有額外的空間對它進?分配擔保,所以我們必須選擇“標記-清除”或“標記-整理”算法進?垃圾收集。
垃圾收集器
Serial、Parnew、parallel Scavenge、Serialold 、Parnewold、CMS、G1
Serial:采用復制算法
Serial 是一個單線程的收集器,它不但只會使用一個 CPU 或一條線程去完成垃圾收集工作,并且在進行垃圾收集的同時,必須暫停其他所有的工作線程,直到垃圾收集結束。
Serial old:標記-整理算法
Serial收集器的?年代版本,它同樣是?個單線程收集器,使用標記-整理算法。主要有兩個用途:
ParNew:復制算法
?ParNew 垃圾收集器其實是 Serial 收集器的多線程版本,也使用復制算法,除了使用多線程進行垃圾收集之外,其余的行為和 Serial 收集器完全一樣,ParNew 垃圾收集器在垃圾收集過程中同樣也要暫停所有其他的工作線程
parallel Scavenge和CMS(老年代收集器,標記清除算法)
Parallel Scavenge收集器關注點是吞吐量(?效率的利?CPU)。CMS等垃圾收集器的關注點更多的是?戶線程的停頓時間(提??戶體驗);高吞吐量可以最高效率地利用 CPU 時間,盡快地完成程序的運算任務,主要適用于在后臺運算而不需要太多交互的任務。
parallel old:標記整理算法
? Parallel Scavenge收集器的?年代版本。使?多線程和“標記-整理”算法。
CMS收集器
? CMS收集器是一種年老代垃圾收集器,其最主要目標是獲取最短垃圾回收停頓時間,和其他年老代使用標記-整理算法不同,它使用多線程的標記-清除算法。最短的垃圾收集停頓時間可以為交互比較高的程序提高用戶體驗。CMS 工作機制相比其他的垃圾收集器來說更復雜,整個過程分為以下 4 個階段:
? 初始標記
只是標記一下 GC Roots 能直接關聯的對象,速度很快,仍然需要暫停所有的工作線程。
? 并發標記
進行 GC Roots 跟蹤的過程,和用戶線程一起工作,不需要暫停工作線程。
? 重新標記
為了修正在并發標記期間,因用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄,仍然需要暫停所有的工作線程。
? 并發清除
清除 GC Roots 不可達對象,和用戶線程一起工作,不需要暫停工作線程。由于耗時最長的并發標記和并發清除過程中,垃圾收集線程可以和用戶現在一起并發工作,所以總體上來看CMS 收集器的內存回收和用戶線程是一起并發地執行。
? 優點:并發收集、低停頓
? 缺點:對CPU資源敏感;?法處理浮動垃圾;使?“標記清除"算法會導致?量空間碎?產?。
G1收集器
?是?款?向服務器的垃圾收集器,主要針對配備多顆處理器及?容量內存的機器.以極?概率滿?GC停頓時間要求的同時,還具備?吞吐量性能特征;相比與 CMS 收集器,G1 收集器兩個最突出的改進是:
? 【1】基于標記-整理算法,不產生內存碎片。
? 【2】可以非常精確控制停頓時間,在不犧牲吞吐量前提下,實現低停頓垃圾回收。
? G1 收集器避免全區域垃圾收集,它把堆內存劃分為大小固定的幾個獨立區域,并且跟蹤這些區域的垃圾收集進度,同時在后臺維護一個優先級列表,每次根據所允許的收集時間,優先回收垃圾最多的區域。區域劃分和優先級區域回收機制,確保 G1 收集器可以在有限時間獲得最高的垃圾收集效率。
創建一個對象的步驟
類加載檢查、分配內存、初始化零值、設置對象頭、執行init方法
①類加載檢查
? 虛擬機遇到?條 new 指令時,?先將去檢查這個指令的參數是否能在常量池中定位到這個類的符號引?,并且檢查這個符號引?代表的類是否已被加載過、解析和初始化過。如果沒有,那必須先執?相應的類加載過程。
②分配內存
?在類加載檢查通過后,接下來虛擬機將為新?對象分配內存。對象所需的內存??在類加載完成后便可確定,為對象分配空間的任務等同于把?塊確定??的內存從 Java 堆中劃分出來。分配?式有 “指針碰撞” 和 “空閑列表” 兩種,選擇那種分配?式由 Java 堆是否規整決定,?Java堆是否規整?由所采?的垃圾收集器是否帶有壓縮整理功能決定。
③初始化零值
內存分配完成后,虛擬機需要將分配到的內存空間都初始化為零值,這?步操作保證了對象的實例字段在 Java 代碼中可以不賦初始值就直接使?,程序能訪問到這些字段的數據類型所對應的零值。
④設置對象頭
初始化零值完成之后,虛擬機要對對象進?必要的設置,例如這個對象是那個類的實例、如何才能找到類的元數據信息、對象的哈希嗎、對象的 GC 分代年齡等信息。 這些信息存放在對象頭中。 另外,根據虛擬機當前運?狀態的不同,如是否啟?偏向鎖等,對象頭會有不同的設置?式。
⑤執? init ?法
?在上??作都完成之后,從虛擬機的視?來看,?個新的對象已經產?了,但從Java 程序的視?來看,對象創建才剛開始, <init> ?法還沒有執?,所有的字段都還為零。所以?般來說,執? new 指令之后會接著執? <init> ?法,把對象按照程序員的意愿進?初始化,這樣?個真正可?的對象才算完全產?出來。</init></init>
詳細介紹類加載過程
過程:加載、驗證、準備、解析、初始化
加載階段
? 1.通過一個類的全限定名來獲取定義此類的二進制字節流。
? 2.將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。
? 3.在Java堆中生成一個代表這個類的java.lang.class對象,作為方法區這些數據的訪問入口。
驗證階段
? 1.文件格式驗證(是否符合Class文件格式的規范,并且能被當前版本的虛擬機處理)
? 2.元數據驗證(對字節碼描述的信息進行語意分析,以保證其描述的信息符合Java語言規范要求)
? 3.字節碼驗證(保證被校驗類的方法在運行時不會做出危害虛擬機安全的行為)
? 4.符號引用驗證(虛擬機將符號引用轉化為直接引用時,解析階段中發生)
準備階段
? 準備階段是正式為類變量分配內存并設置類變量初始值的階段。將對象初始化為“零”值
解析階段
? 解析階段時虛擬機將常量池內的符號引用替換為直接引用的過程。
初始化階段
? 初始化階段時加載過程的最后一步,而這一階段也是真正意義上開始執行類中定義的Java程序代碼。
雙親委派機制,使用這個機制的好處?如何破壞?
? 每?個類都有?個對應它的類加載器。系統中的 ClassLoder 在協同?作的時候會默認使? 雙親委派模型 。即在類加載的時候,系統會?先判斷當前類是否被加載過。已經被加載的類會直接返回,否則才會嘗試加載。加載的時候,?先會把該請求委派該?類加載器的 loadClass() 處理,因此所有的請求最終都應該傳送到頂層的啟動類加載器 BootstrapClassLoader 中。當?類加載器?法處理時,才由??來處理。當?類加載器為null時,會使?啟動類加載器 BootstrapClassLoader 作為?類加載器。
使用好處
? 此機制保證JDK核心類的優先加載;使得Java程序的穩定運?,可以避免類的重復加載,也保證了 Java 的核? API 不被篡改。如果不?沒有使?雙親委派模型,?是每個類加載器加載??的話就會出現?些問題,?如我們編寫?個稱為 java.lang.Object 類的話,那么程序運?的時候,系統就會出現多個不同的Object 類。
破壞雙親委派機制
可以??定義?個類加載器,重寫loadClass方法;
了解下tomcat的類加載機制
先在本地cache查找該類是否已經加載過,看看 Tomcat 有沒有加載過這個類。
如果Tomcat 沒有加載過這個類,則從系統類加載器的cache中查找是否加載過。如果沒有加載過這個類,嘗試用ExtClassLoader類加載器類加載,重點來了,這里并沒有首先使用 AppClassLoader 來加載類。這個Tomcat 的 WebAPPClassLoader 違背了雙親委派機制,直接使用了 ExtClassLoader來加載類。這里注意 ExtClassLoader 雙親委派依然
有效,ExtClassLoader 就會使用 Bootstrap ClassLoader 來對類進行加載,保證了 Jre 里面的核心類不會被重復加載。 比如在 Web 中加載一個 Object 類。WebAppClassLoader → ExtClassLoader → Bootstrap ClassLoader,這個加載鏈,就保證了 Object 不會被重復加載。
如果 BoostrapClassLoader,沒有加載成功,就會調用自己的 findClass 方法由自己來對類進行加載,findClass 加載類的地址是自己本 web 應用下的 class。
加載依然失敗,才使用 AppClassLoader 繼續加載。
都沒有加載成功的話,拋出異常。
總結一下以上步驟,WebAppClassLoader 加載類的時候,故意打破了JVM 雙親委派機制,繞開了 AppClassLoader,直接先使用 ExtClassLoader 來加載類。
?
?
?
總結
以上是生活随笔為你收集整理的JAVA开发面试题_网络_操作系统_JAVA基础_JVM虚拟机的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 3DevFest2015 珠海回顾!
- 下一篇: (2006) 农历节气