大廠面試題之JVM

在這裏插入圖片描述

JVM結構

Java8 JVM內存結構

基本結構與之前類似,只是Java8取消了之前的“永久代”,取而代之的是“元空間”——Metaspace,兩者本質是一樣的。“永久代”使用的是JVM的堆內存,而“元空間”是直接使用的本機物理內存。
在這裏插入圖片描述
在這裏插入圖片描述
簡單來說就是棧管運行,而堆管存儲
在這裏插入圖片描述

GC Roots

如果判斷一個對象可以被回收?

引用計數算法

維護一個計數器,如果有對該對象的引用,計數器+1,反之-1。無法解決循環引用的問題。

可達性分析算法

從一組名爲“GC Roots”的根節點對象出發,向下遍歷。那些沒有被遍歷到、與GC Roots形成通路的對象,會被標記爲“回收”。

哪些對象可以作爲GC Roots?

  1. 虛擬機棧(棧幀中的局部變量)中引用的對象。
  2. 本地方法棧(native)中引用的對象。
  3. 方法區中常量引用的對象。
  4. 方法區中類靜態屬性引用的對象。

JVM參數

JVM 三種類型參數

標配參數

比如-version-help-showversion等,幾乎不會改變。

X參數

用得不多,比如-Xint,解釋執行模式;-Xcomp,編譯模式;-Xmixed,開啓混合模式(默認)。

XX參數

重要,用於JVM調優。

JVM XX參數

布爾類型

公式-XX:+某個屬性-XX:-某個屬性,開啓或關閉某個功能。
比如-XX:+PrintGCDetails,開啓GC詳細信息。

KV鍵值類型

公式-XX:屬性key=值value。比如-XX:Metaspace=128m
-XX:MaxTenuringThreshold=15

JVM Xms/Xmx參數

-Xms-Xmx十分常見,用於設置初始堆大小最大堆大小。第一眼看上去,既不像X參數,也不像XX參數。實際上-Xms等價於-XX:InitialHeapSize-Xmx等價於-XX:MaxHeapSize。所以-Xms-Xmx屬於XX參數。

JVM 查看參數

查看某個參數

使用jps -l配合jinfo -flag JVM參數 pid 。先用jsp -l查看java進程,選擇某個進程號。
在這裏插入圖片描述
jinfo -flag PrintGCDetails 18052可以查看18052 Java進程的PrintGCDetails參數信息。
公式-XX:+某個屬性-XX:-某個屬性,開啓或關閉某個功能。
比如-XX:+PrintGCDetails,開啓GC詳細信息。
在這裏插入圖片描述

查看所有參數

使用jps -l配合jinfo -flags pid可以查看所有參數。

也可以使用java -XX:+PrintFlagsInitial
在這裏插入圖片描述
在這裏插入圖片描述

查看修改後的參數

使用java -XX:PrintFlagsFinal可以查看修改後的參數,與上面類似。只是修改過後是:=而不是=

查看常見參數

如果不想查看所有參數,可以用-XX:+PrintCommandLineFlags查看常用參數。
在這裏插入圖片描述

JVM 常用參數

-Xmx/-Xms

最大和初始堆大小。最大默認爲物理內存的1/4,初始默認爲物理內存的1/64。

-Xss

等價於-XX:ThresholdStackSize。用於設置單個棧的大小,系統默認值是0,不代表棧大小爲0。而是根據操作系統的不同,有不同的值。比如64位的Linux系統是1024K,而Windows系統依賴於虛擬內存。

-Xmn

新生代大小,一般不調。

-XX:MetaspaceSize

設置元空間大小。

-XX:+PrintGCDetails

輸出GC收集信息,包含GCFull GC信息。

-XX:SurvivorRatio

新生代中,Eden區和兩個Survivor區的比例,默認是8:1:1。通過-XX:SurvivorRatio=4改成4:1:1

-XX:NewRatio

老生代和新年代的比列,默認是2,即老年代佔2,新生代佔1。如果改成-XX:NewRatio=4,則老年代佔4,新生代佔1。

-XX:MaxTenuringThreshold

新生代設置進入老年代的時間,默認是新生代逃過15次GC後,進入老年代。如果改成0,那麼對象不會在新生代分配,直接進入老年代。

四大引用

在這裏插入圖片描述

強引用

使用new方法創造出來的對象,默認都是強引用。GC的時候,就算內存不夠,拋出OutOfMemoryError也不會回收對象,死了也不回收。我們一般創建對象都是強引用

軟引用

需要用Object.Reference.SoftReference來顯示創建。如果內存夠,GC的時候不回收內存不夠則回收。常用於內存敏感的應用,比如高速緩存。

package jvm;

import java.lang.ref.SoftReference;

public class SoftReferenceDemo {
    public static void main(String[] args) {
        softRef_Memory_Enough();
        System.out.println("Not Enough");
        softRef_Memory_NotEnough();
    }

    private static void softRef_Memory_Enough() {
        Object o1 = new Object();
        SoftReference<Object> softReference = new SoftReference<>(o1);
        System.out.println(o1);
        System.out.println(softReference.get());
        System.out.println("===========");
        o1 = null;
        System.gc();
        System.out.println(o1);
        System.out.println(softReference.get());
    }

    private static void softRef_Memory_NotEnough() {
        Object o1 = new Object();
        SoftReference<Object> softReference = new SoftReference<>(o1);
        System.out.println(o1);
        System.out.println(softReference.get());
        System.out.println("===========");
        o1 = null;
        System.gc();

        try {
            byte[] bytes = new byte[30 * 1024 * 1024];
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(o1);
            System.out.println(softReference.get());
        }
    }
}

弱引用

需要用Object.Reference.WeakReference來顯示創建。無論內存夠不夠,GC的時候都回收,也可以用在高速緩存上。

   public static void main(String[] args) {
        Object o1 = new Object();
        WeakReference<Object> weakReference = new WeakReference<>(o1);
        System.out.println(o1);
        System.out.println(weakReference.get());
        System.out.println("==========");
        o1 = null;
        System.gc();
        System.out.println(o1);
        System.out.println(weakReference.get());
    }

在這裏插入圖片描述

WeakHashMap

傳統的HashMap就算key==null了,也不會回收鍵值對。但是如果是WeakHashMap,一旦內存不夠用時,且key==null時,會回收這個鍵值對。

public static void main(String[] args) {
        byte[] bytes = new byte[30 * 1024 * 1024];
        myHashMap();
        System.out.println("===============");
        myWeakHashMap();
    }

    private static void myHashMap() {
        HashMap<Integer, String> map = new HashMap<>();
        Integer key = 1;
        String value = "HashMap";
        map.put(key, value);
        System.out.println(map);

        key = null;
        System.out.println(map);
        System.gc();
        System.out.println(map + "\t" + map.size());
    }

    private static void myWeakHashMap() {
        WeakHashMap<Integer, String> map = new WeakHashMap<>();
        Integer key = 2;
        String value = "WeakHashMap";
        map.put(key, value);
        System.out.println(map);

        key = null;
        System.out.println(map);

        System.gc();
        System.out.println(map + "\t" + map.size());
    }

虛引用

軟應用和弱引用可以通過get()方法獲得對象,但是虛引用不行。虛引形同虛設,在任何時候都可能被GC,不能單獨使用,必須配合引用隊列(ReferenceQueue)來使用。設置虛引用的唯一目的,就是在這個對象被回收時,收到一個通知以便進行後續操作,有點像Spring的後置通知。

public static void main(String[] args) throws InterruptedException {
        Object o1 = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        PhantomReference phantomReference = new PhantomReference(o1, referenceQueue);
        System.out.println(o1);
        System.out.println(phantomReference.get());
        System.out.println(referenceQueue.poll());

        System.out.println("===========");

        o1 = null;
        System.gc();
        Thread.sleep(500);
        System.out.println(o1);
        System.out.println(phantomReference.get());
        System.out.println(referenceQueue.poll());
    }

在這裏插入圖片描述

引用隊列

弱引用、虛引用被回收後,會被放到引用隊列裏面,通過poll方法可以得到。關於引用隊列和弱、虛引用的配合使用,

在這裏插入圖片描述

發佈了106 篇原創文章 · 獲贊 9 · 訪問量 6172
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章