java判斷迴文字串的幾種方法,進階學習
1. 垃圾回收機制
Stop-the-World:
JVM由於要執行GC而停止了應用程式的執行稱之為Stop-the-World,該情形會在任何一種GC演算法中發生。當Stop-the-world發生時,除了GC所需的執行緒以外,所有執行緒都處於等待狀態直到GC任務完成。事實上,GC優化很多時候就是指減少Stop-the-world發生的時間,從而使系統具有 高吞吐 、低停頓 的特點。
2. java執行時的記憶體劃分
1. 程式計數器
記錄當前執行緒所執行的位元組碼行號,用於獲取下一條執行的位元組碼。
當多執行緒執行時,每個執行緒切換後需要知道上一次所執行的狀態、位置。
由此也可以看出程式計數器是每個執行緒私有的。
3. 虛擬機器棧
虛擬機器棧由一個一個的棧幀組成,棧幀是在每一個方法呼叫時產生的。
每一個棧幀由區域性變數區、運算元棧等組成。每建立一個棧幀壓棧,當一個方法執行完畢之後則出棧。
- 如果出現方法遞迴調用出現死迴圈的話就會造成棧幀過多,最終會丟擲 StackOverflowError。
- 若執行緒執行過程中棧幀大小超出虛擬機器棧限制,則會丟擲 StackOverflowError。
- 若虛擬機器棧允許動態擴充套件,但在嘗試擴充套件時記憶體不足,或者在為一個新執行緒初始化新的虛擬機器棧時申請不到足夠的記憶體,則會丟擲 OutOfMemoryError。
這塊記憶體區域也是執行緒私有的。
4. Java堆
Java 堆是整個虛擬機器所管理的最大記憶體區域,所有的物件建立都是在這個區域進行記憶體分配。
可利用引數 -Xms -Xmx 進行堆記憶體控制。
這塊區域也是垃圾回收器重點管理的區域,由於大多數垃圾回收器都採用分代回收演算法,所有堆記憶體也分為 新生代、老年代,可以方便垃圾的準確回收。
這塊記憶體屬於執行緒共享區域。
5. 方法區
方法區主要用於存放已經被虛擬機器載入的類資訊,如常量,靜態變數。 這塊區域也被稱為永久代。
可利用引數 -XX:PermSize -XX:MaxPermSize 控制初始化方法區和最大方法區大小。
6. 元資料區
在 JDK1.8 中已經移除了方法區(永久代),並使用了一個元資料區域進行代替(Metaspace)。
預設情況下元資料區域會根據使用情況動態調整,避免了在 1.7 中由於載入類過多從而出現 java.lang.OutOfMemoryError: PermGen。
但也不能無線擴充套件,因此可以使用 -XX:MaxMetaspaceSize來控制最大記憶體。
7. 執行時常量池
執行時常量池是方法區的一部分,其中存放了一些符號引用。當 new 一個物件時,會檢查這個區域是否有這個符號的引用。
8. 直接記憶體
直接記憶體又稱為 Direct Memory(堆外記憶體),它並不是由 JVM 虛擬機器所管理的一塊記憶體區域。
有使用過 Netty 的朋友應該對這塊並記憶體不陌生,在 Netty 中所有的 IO(nio) 操作都會通過 Native 函式直接分配堆外記憶體。
它是通過在堆記憶體中的 DirectByteBuffer 物件操作的堆外記憶體,避免了堆記憶體和堆外記憶體來回複製交換複製,這樣的高效操作也稱為零拷貝。
既然是記憶體,那也得是可以被回收的。但由於堆外記憶體不直接受 JVM 管理,所以常規 GC 操作並不能回收堆外記憶體。它是藉助於老年代產生的 fullGC 順便進行回收。同時也可以顯式呼叫 System.gc() 方法進行回收(前提是沒有使用 -XX:+DisableExplicitGC 引數來禁止該方法)。
值得注意的是:由於堆外記憶體也是記憶體,是由作業系統管理。如果應用有使用堆外記憶體則需要平衡虛擬機器的堆記憶體和堆外記憶體的使用佔比。避免出現堆外記憶體溢位。
9. 常用引數
通過上圖可以直觀的檢視各個區域的引數設定。
常見的如下:
-
-Xms64m 最小堆記憶體 64m.
-
-Xmx128m 最大堆記憶體 128m.
-
-XX:NewSize=30m 新生代初始化大小為30m.
-
-XX:MaxNewSize=40m 新生代最大大小為40m.
-
-Xss=256k 執行緒棧大小。
-
-XX:+PrintHeapAtGC 當發生 GC 時列印記憶體佈局。
-
-XX:+HeapDumpOnOutOfMemoryError 傳送記憶體溢位時 dump 記憶體。
新生代和老年代的預設比例為 1:2,也就是說新生代佔用 1/3的堆記憶體,而老年代佔用 2/3 的堆記憶體。
可以通過引數 -XX:NewRatio=2 來設定老年代/新生代的比例。
物件的建立
下圖便是 Java 物件的建立過程,我建議最好是能默寫出來,並且要掌握每一步在做什麼。
Step1:類載入檢查
虛擬機器遇到一條 new 指令時,首先將去檢查這個指令的引數是否能在常量池中定位到這個類的符號引用,並且檢查這個符號引用代表的類是否已被載入過、解析和初始化過。如果沒有,那必須先執行相應的類載入過程。
Step2:分配記憶體
在類載入檢查通過後,接下來虛擬機器將為新生物件分配記憶體。物件所需的記憶體大小在類載入完成後便可確定,為物件分配空間的任務等同於把一塊確定大小的記憶體從 Java 堆中劃分出來。分配方式有 “指標碰撞” 和 “空閒列表” 兩種,選擇那種分配方式由 Java 堆是否規整決定,而 Java 堆是否規整又由所採用的垃圾收集器是否帶有壓縮整理功能決定。
記憶體分配的兩種方式:(補充內容,需要掌握)
選擇以上兩種方式中的哪一種,取決於 Java 堆記憶體是否規整。而 Java 堆記憶體是否規整,取決於 GC 收集器的演算法是"標記-清除",還是"標記-整理"(也稱作"標記-壓縮"),值得注意的是,複製演算法記憶體也是規整的
記憶體分配併發問題(補充內容,需要掌握)
在建立物件的時候有一個很重要的問題,就是執行緒安全,因為在實際開發過程中,建立物件是很頻繁的事情,作為虛擬機器來說,必須要保證執行緒是安全的,通常來講,虛擬機器採用兩種方式來保證執行緒安全:
-
CAS+失敗重試: CAS 是樂觀鎖的一種實現方式。所謂樂觀鎖就是,每次不加鎖而是假設沒有衝突而去完成某項操作,如果因為衝突失敗就重試,直到成功為止。虛擬機器採用 CAS 配上失敗重試的方式保證更新操作的原子性。
-
TLAB: 為每一個執行緒預先在 Eden 區分配一塊兒記憶體,JVM 在給執行緒中的物件分配記憶體時,首先在 TLAB 分配,當物件大於 TLAB 中的剩餘記憶體或 TLAB 的記憶體已用盡時,再採用上述的 CAS 進行記憶體分配
Step3:初始化零值
記憶體分配完成後,虛擬機器需要將分配到的記憶體空間都初始化為零值(不包括物件頭),這一步操作保證了物件的例項欄位在 Java 程式碼中可以不賦初始值就直接使用,程式能訪問到這些欄位的資料型別所對應的零值。
Step4:設定物件頭
初始化零值完成之後,虛擬機器要對物件進行必要的設定,例如這個物件是那個類的例項、如何才能找到類的元資料資訊、物件的雜湊碼、物件的 GC 分代年齡等資訊。 這些資訊存放在物件頭中。 另外,根據虛擬機器當前執行狀態的不同,如是否啟用偏向鎖等,物件頭會有不同的設定方式。
最後
總而言之,面試官問來問去,問的那些Redis知識點也就這麼多吧,複習的不夠到位,知識點掌握不夠熟練,所以面試才會卡殼。將這些Redis面試知識解析以及我整理的一些學習筆記分享出來給大家參考學習
有需要這些學習筆記資料的朋友注意啦:戳這裡即可免費領取
還有更多學習筆記面試資料也分享如下(都可免費領取):