目錄
一、JVM概述
①jvm結構
1.Class files ---------------------------------------類文件
2.Class loader ------------------------------------類裝載器子系統
3.Runtime Data Area ---------------------------運行時數據區
3.1 Java VM stack --------------------------------(線程私有)Java虛擬機棧:一個方法一個棧幀--->局部變量表、操作數棧、動態鏈接、方法出口
3.2 Native Method Stack ------------------------(線程私有)本地方法棧:爲虛擬機使用到的 Native 方法服務
3.3 Program Counter Register ----------------(線程私有)程序計數器:記錄的是正在執行的虛擬機字節碼指令的地址或Undefined
3.4 Method Area ----------------------------------(線程公有)方法區:類信息、常量、靜態變量、即時編譯器編譯後的代碼
3.5 Heap --------------------------------------------(線程公有)堆:存放對象實例和數組、年輕代+老年代
4.Native Interface --------------------------------本地方法接口
4.1本地方法庫 -------------------------------------本地方法庫
5.Execution Engine ------------------------------執行引擎
②常見垃圾回收算法
1.引用計數:引用即+1,一般不採用
2.複製:年輕代(Eden--->From<--->To)
3.標記清除:產生空間碎片
4.標記整理:標記清除升級版,移動整理需要成本
二、JVM如何確定垃圾及GC Roots的理解
1.內存中不再使用的空間就是垃圾
2.通過引用計數法或者枚舉根節點做可達性分析來確認垃圾是否可以回收
2.1枚舉根節點做可達性分析就是判斷GC Roots對象是否被使用
GC Roots對象:
①虛擬機棧中對象的引用
②方法區中類靜態屬性的引用
③方法區中常量對象的引用
④本地方法棧中JNI(Native方法)對象的引用
三、JVM的參數類型及設置
1.JVM的參數類型
1.1標配參數:
java -version
java -help
java -showversion
1.2X參數
解釋執行:java -Xint -version
編譯執行:java -Xcomp -version
(默認)混合執行:java -Xmixed -version
1.3XX參數
查看當前進程信息
jps -l
jinfo -flags 進程號
是否打印GC收集細節:
-XX:+PrintGCDetails
-XX:-PrintGCDetails
是否使用串行垃圾收集器:
-XX:+UseSerialGC
-XX:-UseSerialGC
年輕代中eden和S0/S1的空間佔比
-XX:SurvivorRatio=8--->默認,eden:s0:s1=8:1:1
年輕代大小
-Xmn
年輕代和老年代的堆結構佔比
-XX:NewRatio=2--->默認1:2
晉升老年代轉換閾值,即垃圾的最大年齡
-XX:MaxtenuringThreshold=15
元空間大小設置
-XX:MetaspaceSize=128m
堆大小及最大值
-Xms--->-XX:IntianlHeapSize---->初始大小:物理內存1/16
-Xmx--->-XX:MaxHeapSize------->初始大小:物理內存1/4
單個線程大小
-Xss--->-XX:ThreadStackSize---->一般默認爲512-1024K
查看初始默認值
java -XX:+PrintFlagsInitial
查看修改更新值
java -XX:+PrintFlagsFinal
=標識默認值,:=標識修改後的值
快速查看修改的主要參數值
java -XX:+PrintCommandLineFlags
四、強引用、軟引用、弱引用、虛引用的理解
1.強引用
普通對象,即使發生OOM也不會被垃圾回收。
示例
Object Strong = new Object(); --->o1引用指向實例對象
2.軟引用
內存充足,不會被 回收;內存不夠,就會被回收
適用場景:讀取大量本地圖片緩存到內存,內存不夠就釋放資源。
示例
//-Xms5m -Xmx5m -XX:+PrintGCDetails 手動造成OOM System.out.println("軟引用"); Object strong = new Object(); SoftReference<Object> soft = new SoftReference<>(strong); strong = null; try { byte[] bytes = new byte[30 * 1024 * 1024]; } finally { System.out.println("strong:" + strong); System.out.println("soft:" + soft.get()); }
3.弱引用
不管內存是否充足,都會被回收
使用場景:讀取大量本地圖片緩存到內存,只要垃圾回收就釋放資源。
示例
System.out.println("弱引用"); Object strong = new Object(); WeakReference<Object> weak = new WeakReference<>(strong); System.out.println("strong:" + strong); System.out.println("soft:" + weak.get()); strong = null; System.gc(); System.out.println("strong:" + strong); System.out.println("soft:" + weak.get());
4.虛引用
本身就是不存儲任何數據,返回null,虛引用必須和引用隊列(ReferenceQueue)一起使用,一般配合監控垃圾回收
System.out.println("虛引用"); Object object = new Object(); ReferenceQueue<Object> ref = new ReferenceQueue<>(); PhantomReference<Object> phantomReference = new PhantomReference<>(object, ref); System.out.println("object: " + object); System.out.println("虛引用: " + phantomReference.get()); System.out.println("應用隊列" + ref.poll()); System.out.println("垃圾回收===="); object = null; System.gc(); System.out.println("object: " + object); System.out.println("虛引用: " + phantomReference.get()); System.out.println("應用隊列" + ref.poll());
五、常見OOM異常
Error----------->錯誤
Exception---->異常
1.java.lang.StackOverflowError
示例:循環調用,棧空間不足,導致棧溢出
public void test1() { System.out.println("java.lang.StackOverflowError======="); method01(); } static void method01() { method01(); }
2.java.lang.OutOfMemoryError: Java heap space
示例:new一個超過堆內存大小的對象,導致堆內存不足
//-Xms30m -Xmx30m System.out.println("java.lang.OutOfMemoryError: Java heap space======="); byte[] bytes = new byte[30 * 1024 * 1024];
3.java.lang.OutOfMemoryError: GC overhead limit exceeded
示例:不斷創建新對象,導致GC無法正常回收
//-Xms10m -Xmx10m System.out.println("java.lang.OutOfMemoryError: GC overhead limit exceeded======="); List<String> list = new ArrayList<>(); int i = 0; try { while (true) { list.add(String.valueOf(i)); } } catch (Exception e) { System.out.println("-----> i : " + i); e.printStackTrace(); throw e; }
4.java.lang.OutOfMemoryError: Direct buffer memory
示例:直接內存溢出,一般NIO程序經常出現
//-Xms10m -Xmx10m -XX:MaxDirectMemorySize=5m -XX:+PrintGCDetails System.out.println("java.lang.OutOfMemoryError: Direct buffer memory ======="); long maxDirectMemory = VM.maxDirectMemory() / 1024l / 1024l; System.out.println("系統最大直接內存:" + maxDirectMemory + "M"); ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);
5.java.lang.OutOfMemoryError: unable to creat new native thread
示例:服務器線程限制,導致不能創建更多線程
進入linux系統,查看能開啓的最大線程數,代表root用戶沒有限制,子用戶最大4096,可自行手動修改
[root@v2ray limits.d]# pwd
/etc/security/limits.d
[root@v2ray limits.d]# ls
20-nproc.conf
[root@v2ray limits.d]# more 20-nproc.conf
# Default limit for number of user's processes to prevent
# accidental fork bombs.
# See rhbz #432903 for reasoning.* soft nproc 4096
root soft nproc unlimited
[root@v2ray limits.d]#
System.out.println("java.lang.OutOfMemoryError: unable to creat new native thread ======="); for (int i = 0; i >= 0; i++) { System.out.println("num--->" + i); new Thread(() -> { try { TimeUnit.SECONDS.sleep(1l); } catch (InterruptedException e) { e.printStackTrace(); } }, String.valueOf(i)).start(); }
6.java.lang.OutOfMemoryError: Metaspace
示例:元空間不足,Metaspace並不在虛擬機內存中,而是使用本地內存
//-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
System.out.println("java.lang.OutOfMemoryError: Metaspace ======="); int num = 0; while (true) { num++; try { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMTest.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(o, args); } }); enhancer.create(); } catch (Exception e) { System.out.println("創建類超出元空間限制:" + num); e.printStackTrace(); } }
六、GC垃圾回收算法和垃圾收集器
1.分類
垃圾回收算法:引用計數、複製、標記清除、標記整理。
垃圾收集器:串行垃圾收集器(Serial)、並行垃圾收集器(Parallel)、併發垃圾收集器(CMS)、G1垃圾收集器。
2.具體實現
2.1串行垃圾回收器Serial
①單線程環境,垃圾回收時暫停用戶線程,不適合服務器,更不適合高併發服務
②程序--->GC--->程序--->GC...
2.2並行垃圾回收器Parallel
①多線程並行工作,垃圾回收時暫停所有用戶線程,適合大數據等弱交互場景
②程序--->GC--->程序--->GC...
2.3併發垃圾回收CMS(Concurrent Mark Sweep併發標記清除)
①多線程工作,垃圾回收時可不暫停用戶線程,適合大型互聯網公司
②程序--->GC(Mark)--->程序/GC(Mark/PreClean)--->GC(Remark)--->程序/GC(Sweep)...
③-XX:+UseConcMarksSweepGC:併發執行,低停頓;CPU壓力大,產生大量碎片,一旦堆內存耗費前未完成GC則觸發Serial Old進行垃圾收集,導致系統長時間停頓。
2.4G1垃圾回收器
①將堆內存分割成多個區域,然後併發工作
②java -server-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -jar XXX.jar
3.查看本地垃圾回收器
java -XX:+PrintCommandLineFlags
-XX:InitialHeapSize=198604096 -XX:MaxHeapSize=3177665536 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-Use
LargePagesIndividualAllocation -XX:+UseParallelGC
4.java垃圾回收器--->Java8默認:UseParallelGC
新生代收集器 | Serial | ParNew | Parallel Scavenge |
老年代收集器 | Serial Old | Parallel Old | CMS |
整堆收集器 | G1 |
5.G1垃圾收集器
5.1舊版本收集器特點:
年輕代和老年代是各自獨立且連續的內存塊 |
年輕代收集使用eden+s0+s1進行復制算法 |
老年代收集時必須掃描整個老年代區域 |
特點都是少而快速的執行GC |
5.2G1的特點
把內存劃分成多個獨立的子區域(Regin) |
物理上不區分年輕代和老年代,只有邏輯上的分代概念,可按需切換 |
整體上採用標記整理算法,局部通過複製算法,不會產生內存碎片 |
充分利用多CPU、多核優勢,縮短STW(Stop the work),可配置停頓時間 |
5.3G1執行步驟:初始標記--->併發標記--->最終標記--->篩選回收
七、Linux服務器變慢排查步驟
yum install -y sysstat
pidstat -d 採樣間隔 -p 進程號
1.整機性能查看:
top--->查看load average(1/5/15分鐘平均負載)、%CPU、%MEM
2.CPU查看:
vm -n 2 3--->查看us、sy內存消耗佔比,2秒鐘採樣一次,共3次
3.內存查看:
free -m
4.硬盤查看:
df -h
5.硬盤IO查看:
iostat -xdk 2 3--->2秒鐘採樣一次,共3次
6.網絡IO查看:
ifstat
7.進程命令查看:
jps -l
ps -ef | grep java | grep -v grep
8.線程號查看:
ps -mp 進程號 -o THREAD,tid,time
9.前60行錯誤代碼輸出,精準定位問題代碼行數
jstack 進程號 | grep tid(小寫線程號16進制轉換值) -A60