日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java内存溢出的几个区域,注意避坑

發布時間:2024/3/24 java 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java内存溢出的几个区域,注意避坑 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在開發過程中,時常會遇到內存溢出的問題,有可能是在生產環境,有的就在開發中,今天就聊一聊內存溢出。

存在內存的區域:

  • Java堆溢出

  • 虛擬機棧和本地方法棧溢出

  • 方法區和運行時常量池溢出

  • 本機內存溢出

1、Java堆溢出

Java堆用于儲存對象實例,我們只要不斷地創建對象,并且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那么隨著對象數量的增加,總容量觸及最大堆的容量限制后就會產生內存溢出異常。

1、案例創建

需要手動調節JVM參數,不然需要等很長時間:-Xms20m -Xmx20m

?public class JavaHeapDemo {static class OOMObject {}public static void main(String[] args) {List<OOMObject> list = new ArrayList<OOMObject>();//利用while循環不斷創建對象while (true) {list.add(new OOMObject());}}}

2、處理方法

常規的處理方法是首先通過內存映像分析工具(如Eclipse Memory Analyzer)對Dump出來的堆轉儲快照進行分析

  • 分清楚到底是出現了內存泄漏(Memory Leak)還是內存溢出(Memory Overflow)

  • 內存泄漏:通過工具查看泄漏對象到GC Roots的引用鏈,找到泄漏對象是通過怎樣的引用路徑、與哪些GC Roots相關聯,才導致垃圾收集器無法回收它們,根據泄漏對象的類型信息以及它到GC Roots引用鏈的信息,一般可以比較準確地定位到這些對象創建的位置,進而找出產生內存泄漏的代碼的具體位置

  • 內存溢出:檢查Java虛擬機的堆參數(-Xmx與-Xms)設置,與機器的內存對比,看看是否還有向上調整的空間。再從代碼上檢查 是否存在某些對象生命周期過長、持有狀態時間過長、存儲結構設計不合理等情況,盡量減少程序運行期的內存消耗

  • 2、虛擬機棧和本地方法棧溢出

    關于虛擬機棧和本地方法棧,在《Java虛擬機規范》中描述了兩種異常:

    • 如果線程請求的棧深度大于虛擬機所允許的最大深度,將拋出StackOverflowError異常。

    • 如果虛擬機的棧內存允許動態擴展,當擴展棧容量無法申請到足夠的內存時,將拋出OutOfMemoryError異常

    《Java虛擬機規范》明確允許Java虛擬機實現自行選擇是否支持棧的動態擴展,而HotSpot虛擬機的選擇是不支持擴展,所以除非在創建

    線程申請內存時就因無法獲得足夠內存而出現OutOfMemoryError異常,否則在線程運行時是不會因為擴展而導致內存溢出的,只會因為

    棧容量無法容納新的棧幀而導致StackOverflowError異常

    1、使用-Xss參數減少棧內存容量

    ?public class JavaVMStackSOF {private int stackLength = 1;public void stackLength() {stackLength++;//無限遞歸stackLength();}public static void main(String[] args) {JavaVMStackSOF sof = new JavaVMStackSOF();try {sof.stackLength();} catch (Throwable e) {System.out.println("stack length:" + sof.stackLength);throw e;}}}

    這里可以通過指定參數-Xss128k,用來測試棧溢出的情況

    3、方法區和運行時常量池溢出

    HotSpot從JDK 7開始逐步“去永久代”的計劃,并在JDK 8中完全使用元空間來代替永久代的背景故事,使用“永久代”還是“元空間”來

    實現方法區,對程序有什么實際的影響。

    String::intern()是一個本地方法,它的作用是如果字符串常量池中已經包含一個等于此String對象的字符串,則返回代表池中這個字符串的

    String對象的引用;否則,會將此String對象包含的字符串添加到常量池中,并且返回此String對象的引用。

    這里測試需要JDK6:-XX:PermSize=6M -XX:MaxPermSize=6M

    ?public class RuntimeConstantPoolOOM {public static void main(String[] args) {// 使用Set保持著常量池引用,避免Full GC回收常量池行為Set<String> set = new HashSet<String>();// 在short范圍內足以讓6MB的PermSize產生OOM了short i = 0;while (true) {set.add(String.valueOf(i++).intern());}}}

    JDK8模擬測試

    ?package jdk8;import java.io.File;import java.lang.management.ClassLoadingMXBean;import java.lang.management.ManagementFactory;import java.net.URL;import java.net.URLClassLoader;import java.util.ArrayList;import java.util.List;/**** @ClassName:OOMTest* @Description:模擬類加載溢出(元空間oom)* 為了快速溢出,設置參數:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=80m* @author diandian.zhang*/public class OOMTest {public static void main(String[] args) {try {//準備urlURL url = new File("D:/58workplace/11study/src/main/java/jdk8").toURI().toURL();URL[] urls = {url};//獲取有關類型加載的JMX接口ClassLoadingMXBean loadingBean = ManagementFactory.getClassLoadingMXBean();//用于緩存類加載器List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();while (true) {//加載類型并緩存類加載器實例ClassLoader classLoader = new URLClassLoader(urls);classLoaders.add(classLoader);classLoader.loadClass("ClassA");//顯示數量信息(共加載過的類型數目,當前還有效的類型數目,已經被卸載的類型數目)System.out.println("total: " + loadingBean.getTotalLoadedClassCount());System.out.println("active: " + loadingBean.getLoadedClassCount());System.out.println("unloaded: " + loadingBean.getUnloadedClassCount());}} catch (Exception e) {e.printStackTrace();}}}

    方法區溢出也是一種常見的內存溢出異常,一個類如果要被垃圾收集器回收,要達成的條件是比較苛刻的。在經常運行時生成大量動態類的應用場景里,就應該特別關注這些類的回收狀況。這類場景除了之前提到的程序使用了CGLib字節碼增強和動態語言外,常見的還有:大量JSP或動態產生JSP文件的應用(JSP第一次運行時需要編譯為Java類)、基于OSGi的應用(即使是同一個類文件,被不同的加載器加載也會視為不同的類)等。

    在JDK 8以后,永久代便完全退出了歷史舞臺,元空間作為其替代者登場。在默認設置下,前面列舉的那些正常的動態創建新類型的測試用例已經很難再迫使虛擬機產生方法區的溢出異常了。不過為了讓使用者有預防實際應用里出現類似于代碼清單2-9那樣的破壞性的操作,HotSpot還是提供了一些參數作為元空間的防御措施,主要包括:

    • -XX:MaxMetaspaceSize:設置元空間最大值,默認是-1,即不限制,或者說只受限于本地內存大小。

    • -XX:MetaspaceSize:指定元空間的初始空間大小,以字節為單位,達到該值就會觸發垃圾收集進行類型卸載,同時收集器會對該值進行調整:如果釋放了大量的空間,就適當降低該值;如果釋放了很少的空間,那么在不超過-XX:MaxMetaspaceSize(如果設置了的話)的情況下,適當提高該值。

    • -XX:MinMetaspaceFreeRatio:作用是在垃圾收集之后控制最小的元空間剩余容量的百分比,可減少因為元空間不足導致的垃圾收集的頻率。類似的還有-XX:Max-MetaspaceFreeRatio,用于控制最大的元空間剩余容量的百分比。

    4、本機直接內存溢出

    直接內存(Direct Memory)的容量大小可通過-XX:MaxDirectMemorySize參數來指定,如果不去指定,則默認與Java堆最大值(由-Xmx指定)一致。

    JVM參數:-Xmx20M -XX:MaxDirectMemorySize=10M

    ?public class DirectMemoryOOM {private static final int _1MB = 1024 * 1024;public static void main(String[] args) throws Exception {Field unsafeField = Unsafe.class.getDeclaredFields()[0];unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe) unsafeField.get(null);while (true) {unsafe.allocateMemory(_1MB);}}}

    越過了DirectByteBuffer類直接通過反射獲取Unsafe實例進行內存分配(Unsafe類的getUnsafe()方法指定只有引導類加載器才會返回實

    例,體現了設計者希望只有虛擬機標準類庫里面的類才能使用Unsafe的功能,在JDK 10時才將Unsafe的部分功能通過VarHandle開放給

    外部使用),因為雖然使用DirectByteBuffer分配內存也會拋出內存溢出異常,但它拋出異常時并沒有真正向操作系統申請分配內存,而

    是通過計算得知內存無法分配就會在代碼里手動拋出溢出異常,真正申請分配內存的方法是Unsafe::allocateMemory()

    總結

    以上是生活随笔為你收集整理的Java内存溢出的几个区域,注意避坑的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。