前言
Java 的性能調優,主要就是為了防止出現out of memory(oom)。Java出現oom就會直接導致程序停止運行。
調優
模擬元空間oom的情況
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class MetaSpaceOverTest { public static void main(String[] args) throws InterruptedException { while (true){ Thread.sleep(20); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MetaSpaceOverTest.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return methodProxy.invoke(o,objects); } }); enhancer.create(); } } }
需要設置的參數:-XX:+PrintGCDetails -XX:MetaspaceSize=20M -XX:MaxMetaspaceSize=20M 分別是打印GC日志,設置元空間的內存初始值的最大值。
代碼中使用了CGLib的jar包:具體說明見:https://www.cnblogs.com/jssj/p/12635206.html
運行結果:
Metaspace 空間出現oom的情況,通過jvisualvm 工具可以比較清楚的看到元空間的使用情況。
GC日志說明:
[GC (Metadata GC Threshold) [PSYoungGen: 555K->128K(35328K)] 4349K->3922K(105472K), 0.0012436 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Metadata GC Threshold) [PSYoungGen: 128K->0K(35328K)] [ParOldGen: 3794K->3789K(108544K)] 3922K->3789K(143872K), [Metaspace: 19831K->19831K(1067008K)], 0.0169102 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
GC (Metadata GC Threshold): 這是一個young GC,表示GC作用的內存位置,Metadata是元空間的意思。
[PSYoungGen: 555K->128K(35328K)] :這是一個youngGC 新生代堆區從555K 變成 128K, 新生代堆區總大小是35328K。
4349K->3922K(105472K) :總堆區從4349K到3922K, 總堆區的大小105472K。
0.0012436 secs:GC消耗的時間。
Full GC (Metadata GC Threshold) : 這是一個Full GC。
[PSYoungGen: 128K->0K(35328K)]:新生代對象占用內存移動到了老年代。
[ParOldGen: 3794K->3789K(108544K)]:老年代堆區從3794K->3789K。
3922K->3789K(143872K):整個堆區的變化情況。
[Metaspace: 19831K->19831K(1067008K)]:元空間的占用內存的變化情況。這里可以看到已經快超20M。
重點:方法區調優,-XX:MetaspaceSize=20M -XX:MaxMetaspaceSize=20M 兩個參數設置一樣大,程序運行后查詢元空間被使用的情況,在這個基礎上加20%的。查看元空間的使用情況可以使用工具:jvisualvm
模擬堆區的oom
import java.util.ArrayList; import java.util.List; public class HeapOverTest { int[] intArr = new int[50]; public static void main(String[] args) throws InterruptedException { List<HeapOverTest> heapOverTestList = new ArrayList<>(); for(;;){ Thread.sleep(1); heapOverTestList.add(new HeapOverTest()); } } }
-Xmx10m -Xms10m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:
Jvm 參數設置堆的最大最小內存大小和打印GC日志, 剩下的是用於打印日志和將Heap信息的日志存放到D盤。
運行結果:(運行一段時間會出現這個報錯。)
利用該工具可以很好的看出來堆內存的使用情況。
根據前面的信息,我們已經獲取到一個HeapOOM的日志,我們來分析看看。
上傳日志分析,點擊類視圖,查看什么對象比較占用內存。
可以比較明顯的看出什么比較暫用內存。當然生產環境會比這個大很多很多,分析也會更加復雜。
這里的int[] 我們其實還不知道具體是被哪個對象引用,所以我需要查看被引用對象。
然后找到他的引用對象:
堆區調優:堆區最大值和最小值調整一樣大,防止內存抖動,具體調整多大根據程序正常運行下再加30%。
-Xmx10m 設置堆內存的最大值
-Xms10m 設置堆內存的最小值
虛擬機棧-OOM
public class StackOverTest { private int val = 0; public void test(){ val++; test(); } public static void main(String[] args) { StackOverTest stackOverTest = new StackOverTest(); try { stackOverTest.test(); } catch (Throwable e) { System.out.println("棧深度:"+stackOverTest.val); } } }
運行結果:
這個棧深度每次運行會出現略微的差別:原因是因為會出現棧上分配,輕量級鎖的存在。
-Xss100K 設置虛擬機棧大小,部分機器最小要求不同
Java Agent
待補充
實戰
1. 死鎖
2. CPU消耗高
top -H -p 6290 --6290是進程號
總結
Jvm性能調優是Java工程師的必修課。