JAVA基礎篇面試題
文章目錄
- JAVA基礎篇面試題
- 1. 什么是GC Roots
- 2. JVM調優和引數配置
- 3. 常用的JVM調優引數
- 4. 分析GC日志
- 5. 四種參考
- 強參考
- 軟參考
- 弱參考
- 虛參考
- 6. 常見的JVM例外/錯誤
1. 什么是GC Roots
GC Roots是一組活躍的參考;常用于判斷物件是否被回收的可達性分析法中;
可達性分析:通過一系列稱為GC Roots的跟物件作為起始節點集,從這些結點開始,根據參考關系向下搜索,搜索程序所走過的路徑稱為“參考鏈”,如果某個物件到根節點間沒有任何參考鏈相連,則證明此物件可再被使用,
能夠作為GC Roots物件:
- 在虛擬機堆疊(堆疊幀中的本地變數表)中參考的物件,譬如各個執行緒被呼叫的方法堆疊中使用到的引數、布局變數、臨時變數等,
- 在方法區中類靜態屬性參考的物件,譬如Java類的參考型別靜態變數,
- 在方法區中常量參考物件,譬如字串常量池里的參考,
- 在本地方法堆疊JNI(Native)參考的物件,
- Java虛擬機內部的參考,如基本資料型別對應的Class現象,一些常駐的例外物件(比如NullPointException、OutOfMemoryError)等,還有系統類加載器,
- 所有被同步鎖(synchronized關鍵字)持有的物件,
- 反應Java虛擬機內部情況的JMXBean、JVMTI中注冊的回呼、本地代碼快取等,
- 其他物件臨時性的加入,共同構成GC Roots集合,
在HotSpot虛擬機中,GC Roots中一般為全域性參考(常量或類靜態屬性)和執行背景關系(堆疊幀中的本地變數表)中;
2. JVM調優和引數配置
JVM引數型別:
-
標配引數(了解):
java -version java -help java -showversion -
x引數(了解):
-Xint # 解釋執行 -Xcomp # 第一次使用久編譯成本地代碼 -Xmixed #混合模式 先編譯再執行 默認 -
xx引數:
-
Boolean型別
-XX:+或者-某個屬性值 +表示開啟 -表示關閉# 執行時增加PrintGCDetails引數(垃圾回收細節) java -jar -XX:+PrintGCDetails 某個.jar # 查看正在運行的Java行程,他的某個jvm引數是否配置 jinfo -flag PrintGCDetails(引數) pid # 配置使用串行垃圾回收器 -XX:+UseSerialGC -
KV設定型別
-XX:屬性Key=屬性值Value# 配置元空間大小 -XX:MetaspaceSize=128m #Young區升到養老區的次數 -XX:MaxTenuringThreshold=15 # 查看全部帶的引數 jinfo -flags pid -
注意
-Xms #等價于-XX:InitialHeapSize 默認本機記憶體的1/64 -Xmx #等價于-XX:MaxHeapSize 默認本機記憶體的1/4
-
-
其余默認引數
-XX:+PrintFlagsInitial #查看JVM初始化引數 java -XX:+PrintFlagsInitial-XX:+PrintFlagsFinal # 查看修改更新的引數 := 帶:表示被修改過的值 java -XX:+PrintFlagsFinal -version # 運行程式并列印引數 java -XX:+PrintFlagsFinal -jar xxx.jar# 查看程式使用的默認JVM引數 java -XX:+PrintCommandLineFlags -version
3. 常用的JVM調優引數
-Xss #設定單個執行緒堆疊的大小,一般默認為512k~1024k 等價于 -XX:ThreadStackSize 若為0則使用JVM默認的值
-Xmn #設定年輕代大小
-XX:MetaspaceSize #設定元空間大小,本質和永久代類似,都是對JVM規范中方法區的實作,不過元空間與永久代之間最大的區別在于,元空間不在虛擬機中,而是使用本地記憶體,因此,默認情況下,元空間的大小僅受本地記憶體限制,默認固定就21810376byte=21M
-XX:MaxDirectMemorySize #設定直接記憶體(堆外記憶體)大小
-XX:+PrintGCDetails # 列印GC的詳細日志記錄
-XX:SurvivorRatio # 設定新生代eden和S0,S1空間的比例
默認-XX:SurvivorRatio=8 # Eden:S0:S1=8:1:1
若設定
-XX:SurvivorRatio=4, Eden:SO:S1=4:1:1
SurvivorRatio值就是設定Eden區的比例,S0和S1相同
-XX:NewRatio # 配置年輕代與老年代在堆結構的占比
默認 -XX:NewRatio=2 # 新生代占1,老年代占2,年輕代占整個堆的1/3
若配置
-XX:NewRatio=4 # 新生代占1,老年代占4,年輕代占整個堆的1/5
NewRatio值是老年代的占比,新生代占1
-XX:MaxTenuringThreshold # 設定垃圾最大年齡
如果設定為0的話,年輕代物件不經過Survivor區,直接進入老年代,如果將值設的偏大,則可以讓物件在年輕代存活時間長;只能在[0~15]次內設定,不能設定不在區間的值;
# 通常配置的引數
-Xms128m
-Xmx4096m
-Xss1024k
-XX:MetaspaceSize=512m
-XX:+PrintCommandLineFlags
-XX:+PrintGCDetails
-XX:+UseSerialGC
# 我們專案的配置
-Dio.netty.leakDetectionLevel=PARANOID
-agentlib:jdwp=transport=dt_socket,address=0,suspend=n,server=y #遠程除錯
-server #設定jvm使server模式,特點是啟動速度比較慢,但運行時性能和記憶體管理效率很高,適用于生產環境,在具有64位能力的jdk環境下將默認啟用該模式,而忽略-client引數,-client是設定jvm使用client模式,特點是啟動速度比較快,但運行時性能和記憶體管理效率不高,通常用于客戶端應用程式或者PC應用開發和除錯,
-Xmx512m #最大堆記憶體
-XX:MaxPermSize=256m #JVM最大允許分配的非堆記憶體,按需分配
-Xnoclassgc #禁用類垃圾回收,此選項關閉與 JVM 不再使用的 Java? 技術類關聯的存盤器的垃圾回收,預設行為如 -Xclassgc 所定義,除非 IBM 支持團隊指示,否則建議不要啟用此選項,原因是該選項可能導致無限制的本機記憶體增長,從而導致記憶體耗盡錯誤,
-XX:+UseParNewGC #使用這個引數后會在新生代進行并行回收,老年代仍舊使用串行回收,新生代S區任然使用復制演算法,作業系統是多核CPU上效果明顯,單核CPU建議使用串行回收器,列印GC詳情時ParNew標識著使用了ParNewGC回收器,默認關閉,
-XX:+UseConcMarkSweepGC #并發標記清除,即使用CMS收集器,它是和應用程式執行緒一起執行,相對于Stop The World來說虛擬機停頓時間較少,停頓減少,吞吐量會降低,它使用的是 標記清除演算法,運作程序為四個步驟,分別是 初始標記—并發標識—重新標記—并發清除,它是老年代的收集演算法,新生代使用ParNew收集演算法,默認關閉
-XX:+UseCMSCompactAtFullCollection # Full GC后,進行一次整理,整理程序是獨占的,會引起停頓時間變長,僅在使用CMS收集器時生效,
-XX:CMSFullGCsBeforeCompaction=0 #設定在執行多少次Full GC后對記憶體空間進行壓縮整理,
-XX:+CMSClassUnloadingEnabled
-XX:+CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=80
-XX:+PrintClassHistogram
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintHeapAtGC
-Xloggc:gc.log #gc日志記錄
-jar game-logic.jar
4. 分析GC日志
[GC (Allocation Failure) [PSYoungGen: 2048K->504K(2560K)] 2048K->1034K(9728K), 0.0063607 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 2156K->512K(2560K)] 2687K->1527K(9728K), 0.0019089 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 512K->512K(2560K)] 1527K->1607K(9728K), 0.0011604 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 512K->0K(2560K)] [ParOldGen: 1095K->1279K(7168K)] 1607K->1279K(9728K), [Metaspace: 3476K->3476K(1056768K)], 0.0149073 secs] [Times: user=0.20 sys=0.00, real=0.02 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 1279K->1279K(9728K), 0.0009314 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 1279K->1252K(7168K)] 1279K->1252K(9728K), [Metaspace: 3476K->3476K(1056768K)], 0.0268015 secs] [Times: user=0.20 sys=0.00, real=0.03 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.wooduan.hall.rpc.automation.test.TestJVM.main(TestJVM.java:11)
Heap
PSYoungGen total 2560K, used 207K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
eden space 2048K, 10% used [0x00000000ffd00000,0x00000000ffd33e28,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 7168K, used 1252K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
object space 7168K, 17% used [0x00000000ff600000,0x00000000ff739118,0x00000000ffd00000)
Metaspace used 3550K, capacity 4498K, committed 4864K, reserved 1056768K
class space used 388K, capacity 390K, committed 512K, reserved 1048576K

5. 四種參考

強參考
概念:當記憶體不足時,JVM開始記憶體回收,對于強參考的物件,就算出現了OOM也不會對該物件進行回收;
說明:最常見的一種物件參考方式,只要有強參考指向一個物件就保證物件還活著,處于可達狀態,垃圾回收器是不會回收此類物件,即使該物件永遠不會被使用,JVVM也不會回收,因此是造成Java記憶體泄漏的主要原因之一;
軟參考
概念:一種相對強參考榷訓了一些的參考,需要用java.lang.ref.SoftReference類實作,當系統記憶體足夠時,不會被回收,當系統記憶體不足時才會被回收;
用途:通常在對記憶體使用很頻繁的程式中,例如高速快取就有用到軟參考;、
場景:需要讀大量的本地圖片
- 如果每次從硬碟加載會嚴重影響性能;
- 如果一次全部加載會造成記憶體溢位;
因此這種情況可以使用軟參考去解決;通過一個HashMap保存圖片的路徑和相應圖片物件關聯的參考之間的映射關系,在記憶體不足時,JVM會自動回收這些快取圖片所咱占用的空間,有效避免了OOM(OutOfMemory)的問題;
// -Xms5m -Xmx5m -XX:+PrintGCDetails
public static void testSoftReference(String[] args) {
Object o = new Object();
SoftReference<Object> softReference = new SoftReference<>(o);
System.out.println("前:"+o);
// 通過get獲取
System.out.println("前:"+softReference.get());
o = null;
try{
byte[] bytes = new byte[5*1024*1024];
} catch (Throwable ignore){
System.out.println("例外");
} finally {
System.out.println("后:"+o); // null
System.out.println("后:"+softReference.get()); // null 記憶體不夠用回收
}
}
弱參考
概念:只要有GC回收,就被回收;
public static void weakReference(String[] args) {
Object o = new Object();
WeakReference<Object> weakReference = new WeakReference<>(o);
System.out.println("前:"+o);
// 通過get獲取
System.out.println("前:"+weakReference.get());
o = null;
try{
System.gc();
} finally {
System.out.println("后:"+o); // null
System.out.println("后:"+weakReference.get()); // null 有GC就回收
}
}
常見應用:WeakHashMap,key物件為弱參考,當GC后回收;
public static void weakHashMap(String[] args) {
Map<Integer, String> map = new WeakHashMap<>();
Integer key = new Integer(1);
map.put(key, "hh");
System.out.println(map); // {1=hh}
key = null;
System.gc();
System.out.println(map);// {}
}
// 等同于
{
Map<Integer, String> map = new WeakHashMap<>();
map.put(new Integer(2), "hh");
System.out.println(map); // {2=hh}
System.gc();
System.out.println(map); // {}
}
虛參考
概念:PhantomReference 不會決定物件的生命周期,如果物件僅持有虛參考,那么他和沒有任何參考一樣,在任何時候都可能被垃圾回收器回收,不能單獨通過他訪問物件,虛參考必須與參考佇列ReferenceQuene一起使用;
作用:跟蹤物件被垃圾回收的狀態,僅僅是提供了一種確保物件被finalize后,做某些事情的機制,其get()方法總是回傳null,因此無法訪問對應被參考物件;在被回收后,可以通過其queue獲取到被回收的參考物件;
意義:說明一個物件已經進入Finalization階段,可以被gc回收,用來實作比finalization機制更靈活的回收操作;設定虛參考關聯的唯一目的在于對這個物件被回收時收到一個系統通知或者后續添加進一步的處理,Java允許使用finalize()方法在垃圾收集器將物件從記憶體中清除前做必要的清理作業(類似AOP的后置通知),
參考佇列:在參考的物件Object@6d6f6e28被回收時,會將參考物件WeakReference@135fbaa4放入佇列中;
public static void referenceQueue(String[] args) {
Object o = new Object();
ReferenceQueue<Object> queue = new ReferenceQueue<>();
WeakReference<Object> weakReference = new WeakReference<>(o, queue);
System.out.println("前 物件地址: "+o); // Object@6d6f6e28
System.out.println("前 弱參考地址: "+weakReference);// WeakReference@135fbaa4
System.out.println("前 弱參考物件地址: "+weakReference.get());//Object@6d6f6e28
System.out.println("前 參考佇列中的地址: "+queue.poll());//null
o = null;
System.gc();
System.out.println("后 物件地址: "+o);//null
System.out.println("后 弱參考地址: "+weakReference);//WeakReference@135fbaa4
System.out.println("后 弱參考物件地址: "+weakReference.get());// null
System.out.println("后 參考佇列中的地址: "+queue.poll());// WeakReference@135fbaa4
}
虛參考代碼:
public static void phantomReference(String[] args) {
Object o = new Object();
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(o, queue);
System.out.println("前 物件地址: "+o);// Object@6d6f6e28
System.out.println("前 參考地址: "+phantomReference);// PhantomReference@135fbaa4
System.out.println("前 參考的物件地址: "+phantomReference.get());// null 虛參考無法通過get獲取
System.out.println("前 參考佇列元素地址: "+queue.poll());// null
o = null;
System.gc();
System.out.println("后 物件地址: "+o);// Object@6d6f6e28
System.out.println("后 參考地址: "+phantomReference);// PhantomReference@135fbaa4
System.out.println("后 參考的物件地址: "+phantomReference.get());// null
System.out.println("后 參考佇列元素地址: "+queue.poll());// PhantomReference@135fbaa4
}
6. 常見的JVM例外/錯誤
例外Throwable分為Error和Exception,錯誤和例外
-
StackOverflowError: 堆疊空間溢位錯誤,死回圈等會拋出;
-
OutOfMemoryError:java heap space: 堆記憶體溢位錯誤;
-
OutOfMemoryError:GC overhead limit exceeded:GC回收時間過長,連續多次超過了98%的時間用來做GC并且回收了不到2%的堆記憶體才會拋出,如果不拋錯誤的后果就是GC清理的記憶體很快被再次填滿,迫使繼續GC,形成死回圈,造成的結果就是CPU100%,沒有回收到空間;
-
OutOfMemoryError:Direct buffer memory:直接記憶體不足(直接記憶體使用的是本地記憶體);由于不斷分配本地記憶體,而堆記憶體使用少,因此JVM無需執行GC,DirectByteBuffer物件們不會被回收,因此,當本地空間使用完后再次分配記憶體就會出現該錯誤;
導致的原因:
寫NIO程式時經常使用ByteBuffer來讀取或寫入資料,這是基于通道和緩沖區的I/O方式,它可以使用Native函式庫直接分配堆外記憶體,然后通過一個存盤再Java堆里面的DirectByteBuffer物件作為這塊記憶體的參考進行操作,這樣能在一些場景中提高性能,并且避免了JAVA堆和Native堆來回復制資料;
ByteBuffer.allocate(capability) 分配JVM堆記憶體,屬于GC管轄范圍,但需要在兩個堆中拷貝資料;
ByteBuffer.allocateDirect(capability) 分配OS本地記憶體,不屬于GC管轄,不需要記憶體拷貝,速度較快;
堆外記憶體通常是本機記憶體的1/4;
-
OutOfMemoryError:unable to create new native thread:這個例外和平臺有關,由于一個行程創建的執行緒數超過了系統所能承載的執行緒數,就會報錯,例如linux允許單個行程可創建的執行緒數是1024個(root賬戶無上限);遇到這種問題,要么優化代碼降低執行緒數,要么修改系統配置,擴大linux默認限制;
linux查看與修改執行緒數:
查看:ulimit -n
修改,重啟生效:
/etc/security/limits.d/90-nproc.conf檔案尾添加
? soft nproc 204800
? hard nproc 204800
/etc/security/limits.d/def.conf檔案尾添加? soft nofile 204800
? hard nofile 204800
-
OutOfMemoryError:Metaspace:Java8以后使用Metaspace代替永久代,他是方法區在HotSpot中的實作,他與持久代的區別就是元空間并不在JVM記憶體中,而是使用的是本地記憶體;
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/303030.html
標籤:java
上一篇:聊聊redis分布式鎖的8大坑
下一篇:詳解 八大排序
