亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java實(shí)戰(zhàn)之OutOfMemoryError異常問題及解決方法

 更新時(shí)間:2022年04月18日 10:28:21   作者:怪咖軟妹@  
這篇文章主要介紹了Java實(shí)戰(zhàn)之OutOfMemoryError異常,主要結(jié)合著深入理解Java虛擬機(jī)一書當(dāng)中整理了本篇內(nèi)容,感興趣的朋友一起看看吧

在Java虛擬機(jī)規(guī)范的描述中,除了程序計(jì)數(shù)器外,虛擬機(jī)內(nèi)存的其他幾個(gè)運(yùn)行時(shí)區(qū)域都有發(fā)生OutOfMemoryError (下文稱OOM)異常的可能。本篇主要結(jié)合著【深入理解Java虛擬機(jī)】一書當(dāng)中整理了本篇博客,感興趣的跟著小編一塊來學(xué)習(xí)呀!

本篇文章和上一篇寫到的 Java內(nèi)存區(qū)域劃分 息息相關(guān),如果您對(duì)Java內(nèi)存區(qū)域劃分不是很了解,建議了解一下,不然這篇文章讀起來會(huì)很痛苦。。。

一、簡(jiǎn)言

本節(jié)內(nèi)容的目的有兩個(gè):

  • 第一,通過代碼驗(yàn)證Java虛擬機(jī)規(guī)范中描述的各個(gè)運(yùn)行時(shí)區(qū)域儲(chǔ)存的內(nèi)容;
  • 第二,希望讀者在工作中遇到實(shí)際的內(nèi)存溢出異常時(shí),能根據(jù)異常的信息快速判斷是哪個(gè)區(qū)域的內(nèi)存溢出,知道怎樣的代碼可能會(huì)導(dǎo)致這些區(qū)域的內(nèi)存溢出,以及出現(xiàn)這些異常后該如何處理。

下面代碼的開頭都注釋了執(zhí)行時(shí)所需要設(shè)置的虛擬機(jī)啟動(dòng)參數(shù)(注釋中“VM Args”后面跟著的參數(shù)),這些參數(shù)對(duì)實(shí)驗(yàn)的結(jié)果有直接影響,請(qǐng)讀者調(diào)試代碼的時(shí)候不要忽略掉。(本篇文章所有案例都采用了JDK1.8版本進(jìn)行測(cè)試)

如果讀者使用控制臺(tái)命令來執(zhí)行程序,那直接跟在Java命令之后書寫就可以。如果讀者使用Eclipse IDE,可以在Debug/Run頁簽中的設(shè)置。

二、代碼實(shí)戰(zhàn)

1、Java堆溢出

Java堆用于儲(chǔ)存對(duì)象實(shí)例,我們只要不斷地創(chuàng)建對(duì)象,并且保證GC Roots到對(duì)象之間有可達(dá)路徑來避免垃圾回收機(jī)制清除這些對(duì)象,就會(huì)在對(duì)象數(shù)量到達(dá)最大堆的容量限制后產(chǎn)生內(nèi)存溢出異常。

將Java堆設(shè)置大小為20MB,不可擴(kuò)展(將堆的最小值-Xms參數(shù)與最大值-Xmx參數(shù)設(shè)置為一樣即可避免堆自動(dòng)擴(kuò)展),通過參數(shù)-XX:+HeapDumpOnOutOfMemoryError 可以讓虛擬機(jī)在出現(xiàn)內(nèi)存溢出異常時(shí)Dump出當(dāng)前的內(nèi)存堆轉(zhuǎn)儲(chǔ)快照以便事后進(jìn)行分析(內(nèi)存堆轉(zhuǎn)儲(chǔ)快照 指的是溢出后,內(nèi)存當(dāng)中的對(duì)象占用情況)。

我用的是ider:

設(shè)置啟動(dòng)參數(shù):
Xms:最小堆內(nèi)存 Xmx:最大可擴(kuò)展內(nèi)存
XX:+HeapDumpOnOutOfMemoryError:可以讓虛擬機(jī)在出現(xiàn)內(nèi)存溢出異常時(shí)Dump出當(dāng)前的內(nèi)存堆轉(zhuǎn)儲(chǔ)快照以便事后進(jìn)行分析

-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

import java.util.ArrayList;
import java.util.List;
public class HeapOOM {
    static class OOMObject{
    }
    public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<>();
        while (true){
            list.add(new OOMObject());
        }
}

運(yùn)行結(jié)果:

因?yàn)樵O(shè)置了-XX:+HeapDumpOnOutOfMemoryError參數(shù),所以生成了 這個(gè)報(bào)告??梢圆榭磳?duì)象占用內(nèi)存。

Java堆內(nèi)存的OOM異常是實(shí)際應(yīng)用中最常見的內(nèi)存溢出異常情況。出現(xiàn)Java堆內(nèi)存溢出時(shí),異常堆棧信息"java.lang.OutOfMemoryError”會(huì)跟著進(jìn)一步提示“Java heapspace"。

要解決這個(gè)區(qū)域的異常,一般的手段是首先通過內(nèi)存映像分析工具(如EclipseMemory Analyzer、Dier的jprofiler)對(duì)dump出來的堆轉(zhuǎn)儲(chǔ)快照進(jìn)行分析,重點(diǎn)是確認(rèn)內(nèi)存中的對(duì)象是否是必要的,也就是要先分清楚到底是出現(xiàn)了內(nèi)存泄漏(Memory Leak)還是內(nèi)存溢出(Memory Overflow)。

如果是內(nèi)存泄漏,可進(jìn)一步通過工具查看泄漏對(duì)象到GC Roots的引用鏈。于是就能找到泄漏對(duì)象是通過怎樣的路徑與GC Roots相關(guān)聯(lián)并導(dǎo)致垃圾收集器無法自動(dòng)回收它們的。掌握了泄漏對(duì)象的類型信息,以及GC Roots引用鏈的信息,就可以比較準(zhǔn)確地定位出泄漏代碼的位置。

如果不存在泄漏,換句話說就是內(nèi)存中的對(duì)象確實(shí)都還必須存活著,那就應(yīng)當(dāng)檢查虛擬機(jī)的堆參數(shù)(-Xmx與-Xms),與機(jī)器物理內(nèi)存對(duì)比看是否還可以調(diào)大,從代碼上檢查是否存在某些對(duì)象生命周期過長、持有狀態(tài)時(shí)間過長的情況,嘗試減少程序運(yùn)行期的內(nèi)存消耗。

后面我會(huì)專門寫一篇關(guān)于內(nèi)存分析工具的博客,XX:+HeapDumpOnOutOfMemoryError這個(gè)只是有內(nèi)存占用情況,工具可以幫我們看到對(duì)象的引用鏈情況。

2、虛擬機(jī)棧和本地方法棧溢出

由于在HotSpot虛擬機(jī)中并不區(qū)分虛擬機(jī)棧和本地方法棧,因此對(duì)于HotSpot來說,-Xoss參數(shù)(設(shè)置本地方法棧大小)雖然存在,但實(shí)際上是無效的,棧容量只由-Xss參數(shù)設(shè)定。關(guān)于虛擬機(jī)棧和本地方法棧,在Java虛擬機(jī)規(guī)范中描述了兩種異常。
注意:HotSpot虛擬機(jī)的棧容量是不可以動(dòng)態(tài)擴(kuò)展的。

  • 如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度,將拋出StackOverflowError異常
  • 允許??臻g動(dòng)態(tài)擴(kuò)展時(shí),當(dāng)擴(kuò)展時(shí)無法申請(qǐng)到足夠的內(nèi)存時(shí)會(huì)拋出OutOfMemoryError異常
public class JavaVMStackSOF {
    private int stackLength = 1;

    public void stackLeak() {
        stackLength++;
        stackLeak();
    }

    public static void main(String[] args) {
        JavaVMStackSOF oom = new JavaVMStackSOF();
        try {
            oom.stackLeak();
        } catch (Throwable e) {
            System.out.println("stack length:" + oom.stackLength);
            throw e;
        }
    }
}
  1. 當(dāng)聲明是基本類型的變量的時(shí),其變量名及值(變量名及值是兩個(gè)概念)是放在JAVA虛擬機(jī)棧中
  2. 當(dāng)聲明的是引用變量時(shí),所聲明的變量(該變量實(shí)際上是在方法中存儲(chǔ)的是內(nèi)存地址值)是放在JAVA虛擬機(jī)的棧中,該變量所指向的對(duì)象是放在堆類存中的。

實(shí)驗(yàn)結(jié)果表明:在單個(gè)線程下,無論是由于棧幀太大,還是虛擬機(jī)棧容量太小,當(dāng)內(nèi)存無法分配的時(shí)候,虛擬機(jī)拋出的都是StackOverflowError異常。換成遠(yuǎn)古時(shí)代的Classic虛擬機(jī),這款虛擬機(jī)可以支持動(dòng)態(tài)擴(kuò)展 棧內(nèi)存的容量,這時(shí)候就會(huì)報(bào)StackOverflowError異常了。

也就是當(dāng)我設(shè)置-Xss128k和不設(shè)置都是報(bào)同樣的錯(cuò)誤,并沒有出現(xiàn)內(nèi)存溢出異常,原因就是 HotSpot虛擬機(jī)的棧容量是不可以動(dòng)態(tài)擴(kuò)展的,但是值得注意的是我的電腦是16G運(yùn)行內(nèi)存的,當(dāng)我設(shè)置-Xss128k的時(shí)候輸出的長度是將近1000,當(dāng)我不限制-Xss128k大小的時(shí)候輸出的長度是20000左右,也就意味著每個(gè)線程的棧幀大小默認(rèn)最大是2MB。

如果測(cè)試時(shí)不限于單線程,通過不斷地建立線程的方式倒是可以產(chǎn)生內(nèi)存溢出異常,在這種情況下,給每個(gè)線程的棧分配的內(nèi)存越大,反而越容易產(chǎn)生內(nèi)存溢出異常。

原因其實(shí)不難理解,操作系統(tǒng)分配給每個(gè)進(jìn)程的內(nèi)存是有限制的,譬如32位Windows的單個(gè)進(jìn)程 最大內(nèi)存限制為2GB。HotSpot虛擬機(jī)提供了參數(shù)可以控制Java堆和方法區(qū)這兩部分的內(nèi)存的最大值。那么虛擬機(jī)棧和本地方法棧內(nèi)存如下:

虛擬機(jī)棧和本地方法棧內(nèi)存=2GB-最大堆容量-最大方法區(qū)容量-程序計(jì)數(shù)器容量

因此為每個(gè)線程分配到的棧內(nèi)存越大,可以建立的線程數(shù)量自 然就越少,建立線程時(shí)就越容易把剩下的內(nèi)存耗盡。

通過上面了解到,出現(xiàn)StackOverflowError異常時(shí)有錯(cuò)誤堆??梢蚤喿x,相對(duì)來說,比較容易找到問題的所在(一般出現(xiàn)死循環(huán)可能會(huì)導(dǎo)致)。

如果是建立過多線程導(dǎo)致的內(nèi)存溢出,而不是棧溢出,在不能減少線程數(shù)或者更換64位虛擬機(jī)的情況下,就只能通過減少最大堆和減少棧容量來換取更多的線程。如果沒有這方面的經(jīng)驗(yàn),這種通過“減少內(nèi)存”的手段來解決內(nèi)存溢出的方式會(huì)比較難以想到。

public class JavaVMStackOOM {
    private void dontStop(){
        while (true){

        }
    }

    public void stackLeakByThread(){
        while (true){
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    dontStop();
                }
            });
            thread.start();
        }
    }

    public static void main(String[] args) {
        JavaVMStackOOM oom = new JavaVMStackOOM();
        oom.stackLeakByThread();
    }
}

注意 重點(diǎn)提示一下,如果讀者要嘗試運(yùn)行上面這段代碼,記得要先保存當(dāng)前的工作,由于在 Windows平臺(tái)的虛擬機(jī)中,Java的線程是映射到操作系統(tǒng)的內(nèi)核線程上,無限制地創(chuàng)建線程會(huì)對(duì)操 作系統(tǒng)帶來很大壓力,上述代碼執(zhí)行時(shí)有很高的風(fēng)險(xiǎn),可能會(huì)由于創(chuàng)建線程數(shù)量過多而導(dǎo)致操作系統(tǒng) 假死(電腦可能直接死機(jī))。

在32位操作系統(tǒng)下的運(yùn)行結(jié)果:
原因:32位有進(jìn)程大小內(nèi)存限制。

Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread

注意:如果要測(cè)試上面內(nèi)存溢出代碼,記住先保存當(dāng)前的工作,避免電腦卡死帶來的麻煩。

3、運(yùn)行時(shí)常量池溢出

由于運(yùn)行時(shí)常量池是方法區(qū)的一部分,所以這兩個(gè)區(qū)域的溢出測(cè)試可以放到一起進(jìn)行。前面曾經(jīng) 提到HotSpot從JDK 7開始逐步“去永久代”的計(jì)劃,并在JDK 8中完全使用元空間來代替永久代,在此我們就以測(cè)試代碼來觀察一下,使用“永久代”還是“元空間”來實(shí)現(xiàn)方法區(qū),對(duì)程序有什么 實(shí)際的影響。

String::intern()是一個(gè)本地方法,它的作用是如果字符串常量池中已經(jīng)包含一個(gè)等于此String對(duì)象的 字符串,則返回代表池中這個(gè)字符串的String對(duì)象的引用;否則,會(huì)將此String對(duì)象包含的字符串添加 到常量池中,并且返回此String對(duì)象的引用

import java.util.ArrayList;
import java.util.List;

public class RuntimeConstantPoolOOM {
    public static void main(String[] args) {
        // 使用List保持著常量池引用,避免Full GC回收常量池行為
        List<String> list = new ArrayList<>();
        // 10MB的PerSize在integer范圍內(nèi)足夠產(chǎn)生00M
        int i = 0;
        while (true){
            list.add(String.valueOf(i++).intern());
        }
    }
}
  • JDK7及以前(了解):-XX:PermSize設(shè)置永久代初始大小。-XX:MaxPermSize設(shè)置永久代最大可分配空間。(JDK7目前已經(jīng)很少用了,這兩個(gè)參數(shù)在JDK8及以后已經(jīng)沒有了,所以不必掌握,了解一下)
  • JDK8及以后:可以使用-XX:MetaspaceSize和-XX:MaxMetaspaceSize設(shè)置元空間初始大小以及最大可分配大小。

使用JDK 7或更高版本的JDK來運(yùn)行這段程序并不會(huì)得到相同的結(jié)果,無論是在JDK 7中繼續(xù)使 用-XX:MaxPermSize參數(shù)或者在JDK 8及以上版本使用-XX:MaxMeta-spaceSize參數(shù)把方法區(qū)容量同 樣限制在6MB,都不會(huì)出現(xiàn)溢出異常,循環(huán)將一直進(jìn)行下去,永不停歇。出現(xiàn)這種變 化,是因?yàn)樽訨DK 7起,原本存放在永久代的字符串常量池被移至Java堆之中,所以在JDK 7及以上版 本,限制方法區(qū)的容量對(duì)該測(cè)試用例來說是毫無意義的。

在JDK1.7中(包括1.7以上)常量池存儲(chǔ)的不再是對(duì)象,而是對(duì)象引用,真正的對(duì)象是存儲(chǔ)在堆中的。把RuntimeConstantPoolOOM.java運(yùn)行時(shí)的VM參數(shù)改為如下(設(shè)置堆大小)所示:

-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

運(yùn)行結(jié)果:

查看生成的堆內(nèi)存快照:

4、方法區(qū)溢出

方法區(qū)用于存放Class的相關(guān)信息,如類名、訪問修飾符、常量池、字段描述、方法描述等。對(duì)于這個(gè)區(qū)域的測(cè)試,基本的思路是運(yùn)行時(shí)產(chǎn)生大量的類去填滿方法區(qū),直到溢出。雖然直接使用Java SE API也可以動(dòng)態(tài)產(chǎn)生類(如反射時(shí)的GeneratedConstructorAccessor 和動(dòng)態(tài)代理等),但在本次實(shí)驗(yàn)中操作起來比較麻煩。借助CGLib直接操作字節(jié)碼運(yùn)行時(shí),生成了大量的動(dòng)態(tài)類。

值得特別注意的是,我們?cè)谶@個(gè)例子中模擬的場(chǎng)景并非純粹是一個(gè)實(shí)驗(yàn),這樣的應(yīng)用經(jīng)常會(huì)出現(xiàn)在實(shí)際應(yīng)用中:當(dāng)前的很多主流框架,如Spring和Hibernate對(duì)類進(jìn)行增強(qiáng)時(shí),都會(huì)使用到CGLib這類字節(jié)碼技術(shù),增強(qiáng)的類越多,就需要越大的方法區(qū)來保證動(dòng)態(tài)生成的Class可以加載人內(nèi)存。

測(cè)試示例:

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class JavaMethodAreaOOM {
    public static void main(String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(OOMObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    return proxy.invokeSuper(obj, args);
                }
            });
            enhancer.create();
        }
    }

    static class OOMObject {
    }
}

設(shè)置元空間最大空間,和初始化空間參數(shù):
類信息是都存在方法區(qū)的,方法區(qū)在jdk1.8將永久區(qū)改為了元空間。自此以后,常量池在元空間都是存儲(chǔ)的引用。實(shí)際對(duì)象是在堆中。

-XX:MaxMetaspaceSize=10m -XX:MetaspaceSize=10m

運(yùn)行結(jié)果:

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

5、本機(jī)直接內(nèi)存溢出

直接內(nèi)存(Direct Memory)并不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,也不是《Java虛擬機(jī)規(guī)范》中 定義的內(nèi)存區(qū)域。但是這部分內(nèi)存也被頻繁地使用,而且也可能導(dǎo)致OutOfMemoryError異常出現(xiàn)。

直接內(nèi)存:可以使用Native函數(shù)庫直接分配堆外內(nèi)存,然后通過一個(gè)存儲(chǔ)在Java堆里面的DirectByteBuffer對(duì)象作為這塊內(nèi)存的引用`進(jìn)行操作。這樣能在一些場(chǎng)景中顯著提高性能,因?yàn)楸苊饬嗽贘ava堆和Native堆中來回復(fù)制數(shù)據(jù)(但是有一點(diǎn)注意,雖然不占用堆內(nèi)存,但是他占用了服務(wù)器內(nèi)存)。

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

代碼示例:

越過了DirectByteBuffer類直接通 過反射獲取Unsafe實(shí)例進(jìn)行內(nèi)存分配Unsafe類的getUnsafe()方法指定只有引導(dǎo)類加載器才會(huì)返回實(shí) 例,體現(xiàn)了設(shè)計(jì)者希望只有虛擬機(jī)標(biāo)準(zhǔn)類庫里面的類才能使用Unsafe的功能,在JDK 10時(shí)才將Unsafe 的部分功能通過VarHandle開放給外部使用),因?yàn)殡m然使用DirectByteBuffer分配內(nèi)存也會(huì)拋出內(nèi)存溢 出異常,但它拋出異常時(shí)并沒有真正向操作系統(tǒng)申請(qǐng)分配內(nèi)存,而是通過計(jì)算得知內(nèi)存無法分配就會(huì) 在代碼里手動(dòng)拋出溢出異常,真正申請(qǐng)分配內(nèi)存的方法是Unsafe::allocateMemory()

import sun.misc.Unsafe;

import java.lang.reflect.Field;

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);
        }
    }
}

運(yùn)行參數(shù):

-Xmx20M -XX:MaxDirectMemorySize=10M -XX:+HeapDumpOnOutOfMemoryError

運(yùn)行結(jié)果:

我設(shè)置了-XX:+HeapDumpOnOutOfMemoryError發(fā)現(xiàn)運(yùn)行完成之后并沒有發(fā)現(xiàn)有內(nèi)存快照。

由直接內(nèi)存導(dǎo)致的內(nèi)存溢出,一個(gè)明顯的特征是在Heap Dump文件中不會(huì)看見有什么明顯的異常 情況,如果讀者發(fā)現(xiàn)內(nèi)存溢出之后產(chǎn)生的Dump文件很小,而程序中又直接或間接使用了 DirectMemory(典型的間接使用就是NIO),那就可以考慮重點(diǎn)檢查一下直接內(nèi)存方面的原因了。

三、JVM常用的啟動(dòng)參數(shù)

堆:

  • -Xms3550m:設(shè)置JVM初始內(nèi)存為3550M。表示初始化JAVA堆的大小及該進(jìn)程剛創(chuàng)建出來的時(shí)候,他的專屬JAVA堆的大小,一旦對(duì)象容量超過了JAVA堆的初始容量,JAVA堆將會(huì)自動(dòng)擴(kuò)容到-Xmx大小。
  • -Xmx3550m:設(shè)置JVM最大可用內(nèi)存為3550M。表示java堆可以擴(kuò)展到的最大值,在很多情況下,通常將-Xms和-Xmx設(shè)置成一樣的,因?yàn)楫?dāng)堆不夠用而發(fā)生擴(kuò)容時(shí),會(huì)發(fā)生內(nèi)存抖動(dòng)影響程序運(yùn)行時(shí)的穩(wěn)定性。

棧:

  • -Xss128k:規(guī)定了每個(gè)線程虛擬機(jī)棧及堆棧的大小,一般情況下,256k是足夠的,此配置將會(huì)影響此進(jìn)程中并發(fā)線程數(shù)的大?。ê投咽遣灰粯拥?,不支持動(dòng)態(tài)擴(kuò)展)。

方法區(qū):

  • JDK7及以前(了解):-XX:PermSize設(shè)置永久代初始大小。
  • -XX:MaxPermSize設(shè)置永久代最大可分配空間。(JDK7目前已經(jīng)很少用了,這兩個(gè)參數(shù)在JDK8及以后已經(jīng)沒有了,所以不必掌握,了解一下)-XX:MaxMetaspaceSize=10m:設(shè)置元空間最大值,默認(rèn)是-1,即不限制,或者說只受限于本地內(nèi)存 大小。
  • -XX:MetaspaceSize=10m:指定元空間的初始空間大小,以字節(jié)為單位,達(dá)到該值就會(huì)觸發(fā)垃圾收集 進(jìn)行類型卸載,同時(shí)收集器會(huì)對(duì)該值進(jìn)行調(diào)整:如果釋放了大量的空間,就適當(dāng)降低該值;如果釋放 了很少的空間,那么在不超過-XX:MaxMetaspaceSize(如果設(shè)置了的話)的情況下,適當(dāng)提高該值。
  • -XX:MinMetaspaceFreeRatio:作用是在垃圾收集之后控制最小的元空間剩余容量的百分比,可 減少因?yàn)樵臻g不足導(dǎo)致的垃圾收集的頻率。類似的還有-XX:Max-MetaspaceFreeRatio,用于控制最 大的元空間剩余容量的百分比。

內(nèi)存:

  • -XX:+HeapDumpOnOutOfMemoryError 可以讓虛擬機(jī)在出現(xiàn)內(nèi)存溢出異常時(shí)Dump出當(dāng)前的內(nèi)存堆轉(zhuǎn)儲(chǔ)快照以便事后進(jìn)行分析(內(nèi)存堆轉(zhuǎn)儲(chǔ)快照 指的是溢出后,內(nèi)存當(dāng)中的對(duì)象占用情況)

GC:

  • -XX:-PrintGCDetails:每次GC時(shí)打印詳細(xì)信息。

四、面試題

public static void main(String[] args) {
    String str1 = new StringBuilder("計(jì)算機(jī)").append("軟件").toString();
    System.out.println(str1.intern() == str1);
    String str2 = new StringBuilder("ja").append("va").toString();
    System.out.println(str2.intern() == str2);
}

這段代碼在JDK 6中運(yùn)行,會(huì)得到兩個(gè)false,而在JDK 7中運(yùn)行,會(huì)得到一個(gè)true和一個(gè)false。在jdk1.8運(yùn)行也是,true、false。

產(chǎn) 生差異的原因是,在JDK 6中,intern()方法會(huì)把首次遇到的字符串實(shí)例復(fù)制到永久代的字符串常量池 中存儲(chǔ),返回的也是永久代里面這個(gè)字符串實(shí)例的引用,而由StringBuilder創(chuàng)建的字符串對(duì)象實(shí)例在 Java堆上,所以必然不可能是同一個(gè)引用,結(jié)果將返回false。

而JDK 7(以及部分其他虛擬機(jī),例如JRockit)的intern()方法實(shí)現(xiàn)就不需要再拷貝字符串的實(shí)例 到永久代了,既然字符串常量池已經(jīng)移到Java堆中,那只需要在常量池里記錄一下首次出現(xiàn)的實(shí)例引 用即可,因此intern()返回的引用和由StringBuilder創(chuàng)建的那個(gè)字符串實(shí)例就是同一個(gè)。
而對(duì)str2比較返 回false,這是因?yàn)?ldquo;java”這個(gè)字符串在執(zhí)行String-Builder.toString()之前就已經(jīng)出現(xiàn)過了,字符串常量 池中已經(jīng)有它的引用,不符合intern()方法要求“首次遇到”的原則,“計(jì)算機(jī)軟件”這個(gè)字符串則是首次 出現(xiàn)的,因此結(jié)果返回true。(這塊說實(shí)話不好理解,說白了就是java是個(gè)特殊的字符串,他在常量池里面就一直存在)

總結(jié):在1.8之后通過intern()添加到常量池,只有字符串在常量池不存在的時(shí)候才會(huì)返回字符串的引用。

五、總結(jié)

到此為止,我們明白了虛擬機(jī)里面的內(nèi)存是如何劃分的,哪部分區(qū)域、什么樣的代碼和操作可能 導(dǎo)致內(nèi)存溢出異常。雖然Java有垃圾收集機(jī)制,但內(nèi)存溢出異常離我們并不遙遠(yuǎn),本章只是講解了各 個(gè)區(qū)域出現(xiàn)內(nèi)存溢出異常的原因,下一章將詳細(xì)講解Java垃圾收集機(jī)制為了避免出現(xiàn)內(nèi)存溢出異常都 做了哪些努力。

到此這篇關(guān)于Java實(shí)戰(zhàn)之OutOfMemoryError異常的文章就介紹到這了,更多相關(guān)java  OutOfMemoryError異常內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用Spring的ApplicationEvent實(shí)現(xiàn)本地事件驅(qū)動(dòng)的實(shí)現(xiàn)方法

    使用Spring的ApplicationEvent實(shí)現(xiàn)本地事件驅(qū)動(dòng)的實(shí)現(xiàn)方法

    本文介紹了如何使用Spring的ApplicationEvent實(shí)現(xiàn)本地事件驅(qū)動(dòng),通過自定義事件和監(jiān)聽器,實(shí)現(xiàn)模塊之間的松耦合,提升代碼的可維護(hù)性和擴(kuò)展性。同時(shí)還介紹了異步事件和事件傳遞的相關(guān)知識(shí)
    2023-04-04
  • Java面試題 從源碼角度分析HashSet實(shí)現(xiàn)原理

    Java面試題 從源碼角度分析HashSet實(shí)現(xiàn)原理

    這篇文章主要介紹了Java面試題 從源碼角度分析HashSet實(shí)現(xiàn)原理?,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • Java通過SSH連接路由器輸入命令并讀取響應(yīng)的操作方法

    Java通過SSH連接路由器輸入命令并讀取響應(yīng)的操作方法

    最近需要讀取和修改華為路由器的配置,使用Java語言開發(fā),通過SSH連接,輸入命令并讀取響應(yīng),接下來通過本文給大家介紹下Java通過SSH連接路由器,輸入命令并讀取響應(yīng),需要的朋友可以參考下
    2024-01-01
  • Java中抽象類用法與注意點(diǎn)實(shí)例詳解

    Java中抽象類用法與注意點(diǎn)實(shí)例詳解

    這篇文章主要介紹了Java中抽象類用法與注意點(diǎn),結(jié)合實(shí)例形式詳細(xì)分析了java抽象類的定義、使用及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2019-08-08
  • Spring使用IOC與DI實(shí)現(xiàn)完全注解開發(fā)

    Spring使用IOC與DI實(shí)現(xiàn)完全注解開發(fā)

    IOC也是Spring的核心之一了,之前學(xué)的時(shí)候是采用xml配置文件的方式去實(shí)現(xiàn)的,后來其中也多少穿插了幾個(gè)注解,但是沒有說完全采用注解實(shí)現(xiàn)。那么這篇文章就和大家分享一下,全部采用注解來實(shí)現(xiàn)IOC + DI
    2022-09-09
  • Event?Sourcing事件溯源模式優(yōu)化業(yè)務(wù)系統(tǒng)

    Event?Sourcing事件溯源模式優(yōu)化業(yè)務(wù)系統(tǒng)

    這篇文章主要為大家介紹了Event?Sourcing事件溯源模式優(yōu)化業(yè)務(wù)系統(tǒng)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • spring是如何實(shí)現(xiàn)聲明式事務(wù)的

    spring是如何實(shí)現(xiàn)聲明式事務(wù)的

    這篇文章主要介紹了spring是如何實(shí)現(xiàn)聲明式事務(wù)的,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Spring Boot 項(xiàng)目設(shè)置網(wǎng)站圖標(biāo)的方法

    Spring Boot 項(xiàng)目設(shè)置網(wǎng)站圖標(biāo)的方法

    這篇文章主要介紹了Spring Boot 項(xiàng)目設(shè)置網(wǎng)站圖標(biāo)的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • java實(shí)現(xiàn)快速打字游戲

    java實(shí)現(xiàn)快速打字游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)快速打字游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • Jenkins+Maven+SVN自動(dòng)化部署java項(xiàng)目

    Jenkins+Maven+SVN自動(dòng)化部署java項(xiàng)目

    這篇文章主要介紹了Jenkins+Maven+SVN自動(dòng)化部署java項(xiàng)目,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01

最新評(píng)論