JVM探究

JVM探究

一、面試題

1.1、請談談你對JVM的理解?java8虛擬機和之前的變化更新?
1.2、什麼是OOM,什麼是棧溢出StackOverFlowError?怎麼分析?
1.3、JVM的常用調優參數有哪些?
1.4、內存快照如何抓取,怎麼分析Dump文件?知道嗎?
1.5、談談JVM中,類加載器的認知?

二、參考學習的視頻和文章:

本篇主要學自b站狂神,知識淺顯,待進一步學習和完善,學無止盡!
2.1視頻:

狂神說–JVM快速入門

2.2文章:

java中的基本數據類型和引用類型在JVM中存儲在哪?

堆棧知識講解

一看你就懂,超詳細java中的ClassLoader詳解

年輕代、老年代、GC原理詳細拆解

JVM參數列表

三、核心知識

3.1、JVM的位置

3.2、JVM的體系結構

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-BYqPmmvK-1592488206326)(E:\typoraPic\jvm例圖.jpg)]

3.3、類加載器
  1. Bootstrap ClassLoader:最頂層的加載類,主要加載核心類庫,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等;
  2. Extention ClassLoader:擴展的類加載器,加載目錄%JRE_HOME%\lib\ext目錄下的jar包和class文件;
  3. Appclass Loader:也稱爲SystemAppClass,加載當前應用的classpath的所有類;
package com.lxf.demo5;

public class Car {
    public static void main(String[] args) {
        //類是模板

        Car car1=new Car();
        Car car2=new Car();
        Car car3=new Car();

        //打印每個car對象的hashCode,發現不一樣
        System.out.println(car1.hashCode());
        System.out.println(car2.hashCode());
        System.out.println(car3.hashCode());


        Class<? extends Car> aClass1 = car1.getClass();
        Class<? extends Car> aClass2 = car2.getClass();
        Class<? extends Car> aClass3 = car3.getClass();

        //打印每個car對象的class,發現一樣
        System.out.println(aClass1.hashCode());
        System.out.println(aClass2.hashCode());
        System.out.println(aClass3.hashCode());


        ClassLoader classLoader = aClass1.getClassLoader();

        //打印類加載器

        System.out.println(classLoader);//AppClassLoader 

        System.out.println(classLoader.getParent());//ExtClassLoader 目錄位置:\jre\lib\ext

        System.out.println(classLoader.getParent().getParent());//null java程序獲取不到(底層c、c++寫的) rt.jar
    }
}

3.4、雙親委派機制
package java.lang;

public class String {
  /*
  *JVM加載一個class時先查看是否已經加載過,沒有則通過父加載器,然後遞歸下去,直到BootstrapClassLoader,如果    		   *BootstrapClassloader找到了,直接返回,如果沒有找到,則一級一級返回(查看規定加載路徑),最後到達自身去查找這些對象。這   *種機制就叫做雙親委託
  *
  *作用:
  *1.避免重複加載
  *2.防止惡意加載
  *3.編寫惡意類java.lang.Object,自定義加載替換系統原生類;
  */


    public String toString(){
        return "hello";
    }

    public static void main(String[] args) {
        String s=new String();
        s.toString();
    }
}

3.5、沙箱安全機制

學習文章:沙箱安全機制

3.6、Native(本地方法)
package com.lxf.demo5;

public class Demo {
    public static void main(String[] args) {
        new Thread(()->{

        },"my thread name").start();
    }
    //native:凡是帶了native 關鍵字的,說明java的作用域範圍抵達不到,會去調用底層c語言的庫!
    //會進入本地方法棧
    //調用本地方法接口 JNI
    //JNI作用:擴展java的使用,融合不同的編程語言爲java所用! 最初:c,c++
    //java誕生的時候:C、C++ 橫行,想要立足,必須要有調用C、C++的程序
    //它在內存區域中專門開啓一塊標記區域:Native Method Stack,登記了native方法
    //最終執行的時候,通過JNI加載本地方法庫中的方法

    //例如Java程序驅動打印機,管理系統
    private native void start0();
}

3.7、PC寄存器

程序計數器:Program Counter Register

​ 每個線程都有一個程序計數器,是線程私有的,就是一個指針,指向方法區的方法字節碼(用來存儲指向一條指令的地址,也將要執行的指令代碼),在執行引擎讀取下一條指令,是一個非常小的內存空間,幾乎可以忽略不計。

3.8、方法區

Method Area方法區:

​ 方法區是被所有的線程共享,所有字段和方法字節碼,以及一些特殊方法,如構造函數,接口代碼也在此定義,簡單說,所有 定義的方法的信息都保存在該區域,此區域屬於共享空間;

靜態變量、常量、類信息(構造函數、接口定義)、運行時的常量池存在方法區中,但是實例變量存在堆內存中,和方法區無關

比如:static、final、Class、常量池(jdk1.7在堆,jdk1.8在元空間)

3.9、棧

存放:八大基本類型、類對象的引用類型

一:在方法中聲明的變量,即該變量是局部變量,每當程序調用方法時,系統都會爲該方法建立一個方法棧,其所在方法中聲明的變量就放在方法棧中,當方法結束系統會釋放方法棧,其對應在該方法中聲明的變量隨着棧的銷燬而結束,這就局部變量只能在方法中有效的原因

在方法中聲明的變量可以是基本類型的變量,也可以是引用類型的變量。

  • 當聲明是基本類型的變量的時,其變量名及值(變量名及值是兩個概念)是放在JAVA虛擬機棧中
  • 當聲明的是引用變量時,所聲明的變量(該變量實際上是在方法中存儲的是內存地址值)是放在JAVA虛擬機的棧中,該變量所指向的對象是放在堆類存中的。
二:在類中聲明的變量是成員變量,也叫全局變量,放在堆中的(因爲全局變量不會隨着某個方法執行結束而銷燬)。

同樣在類中聲明的變量即可是基本類型的變量 也可是引用類型的變量

  1. 當聲明的是基本類型的變量其變量名及其值放在堆內存中的
  2. 引用類型時,其聲明的變量仍然會存儲一個內存地址值,該內存地址值指向所引用的對象。引用變量名和對應的對象仍然存儲在相應的堆中

3.10、三種JVM
  • Sun公司 HotSpot (常用)
  • BEA JRocjit
  • IBM J9 VM
3.11、堆
一個JVM只有一個堆內存,堆內存的大小是可以調節的

類、方法、常量、變量,保存我們所有的引用類型的真實對象;

堆內存中還要細分爲三個區域:

  • 新生代
    • 伊甸園區
    • from區
    • to區
  • 老年代
  • 永久區(jdk1.8後改爲元空間)
2.12、新生代
  • 類:誕生和成長的地方,甚至死亡
  • 伊甸園區:所有的對象都是在伊甸園區創建的
2.13、老年代

當年輕帶隨着不斷地Minor GC ,from survivor中的對象會不斷成長,當from survivor中的對象成長大15歲的時候,就會進入老年代,在老年代的就能存儲很久了,只有重GC時纔會來清理老年代中的垃圾

3.14、永久區(jdk改名元空間,效果也有改變)

這個區域常駐內存的。用來存放JDK自身攜帶的Class對象。Interface元數據,存儲的是Java運行時的一些環境,這個區域不存在垃圾回收!關閉VM虛擬機就會釋放這個區域的內存

當一個啓動類加載了大量的第三方jar包。tomcat部署了太多的應用,大量動態生成的反射類,不斷的被加載。直到內存滿,就會出現OOM;

  • jdk1.6之前:永久代,常量池是在方法區
  • jdk1.7:永久代,但是慢慢退化了,去永久代,常量池在堆中
  • jdk1.8之後:無永久代,常量池在元空間
3.15、堆內存調優
1.設置jvm最大內存和初始化內存大小

-Xms1024m -Xmx1024m -XX:+PrintGCDetails

-Xms:設置初始化內存分配大小(默認1/64)

-Xms:設置最大分配內存(默認1/4)

PrintGCDetails :打印GC垃圾回收線程清理信息

package com.lxf.demo5;

public class MyTest {
    public static void main(String[] args) {
        long max = Runtime.getRuntime().maxMemory();
        long total = Runtime.getRuntime().totalMemory();

        System.out.println("max = " + max/1024/1024);
        System.out.println("total = " + total/1024/1024);
    }
}

2.內存快照分析:

MAT插件:eclipse集成的

Jprofiler插件

MAT,Jprofiler作用:

  • 分析Dump內存文件,快速定位內存泄漏
  • 獲得堆中的數據
  • 獲得大的對象

下載Jprofiler

  • 在Jprofier官網下載安裝
  • idea下載jprofiler插件
  • 註冊碼:[email protected]#36573-fdkscp15axjj6#25257
  • 配置idea:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-i7rsbfIf-1592488206346)(E:\typoraPic\image-20200618153830768.png)]

測試:

package com.lxf.demo5;

import java.util.ArrayList;

public class Demo2 {
    byte[] array=new byte[1*1024*1024];//1m
    public static void main(String[] args) {
        ArrayList<Demo2> list=new ArrayList<Demo2>();

        int count=0;
        try {
            while(true){
                list.add(new Demo2());
                count++;
            }
        } catch (OutOfMemoryError e) {
            System.out.println("count = " + count);
            e.printStackTrace();
        }
    }
}

報錯:

count = 819
java.lang.OutOfMemoryError: Java heap space//堆內存滿了
	at com.lxf.demo5.Demo2.<init>(Demo2.java:6)
	at com.lxf.demo5.Demo2.main(Demo2.java:13)

打印Dump文件配置:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-VGEwa8U8-1592488206356)(E:\typoraPic\image-20200618155007409.png)]

-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError

HeapDumpOnOutOfMemoryError:報OOM錯時生成Dump文件

再次運行Demo2:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid21752.hprof ...
Heap dump file created [7901177 bytes in 0.036 secs]//生成Dump文件
count = 6
java.lang.OutOfMemoryError: Java heap space
	at com.lxf.demo5.Demo2.<init>(Demo2.java:7)
	at com.lxf.demo5.Demo2.main(Demo2.java:14)

打開Demo.java文件的位置,向上一直找到src平級,找到java_pid21752.hprof文件,雙擊打開

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-uXQxy8s6-1592488206365)(E:\typoraPic\image-20200618155526578.png)]

3.16、GC(垃圾回收)

jvm算法講解1

jvm算法講解2

概述:

  1. 常用算法:複製算法、標記整理(壓縮)、引用計數器、標記清除法

  2. JVM在進行GC時,並不是對這三個區域統一回收。大部分的時候,回收都是新生代

  3. GC兩種類:輕GC(普通的GC)、重GC(全局GC)

題目:

  • JVM的內存模型和分區-詳細到每個區
  • 堆裏面的分區有哪些?Eden,from,to,老年代,說說他們的特點!
  • GC的算法有哪些?複製算法、標記整理(壓縮)、引用計數器、標記清除法,怎麼用?
  • 輕GC和重GC分別在什麼時候發生?

引用計數法:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-1HY1P6kl-1592488206378)(E:\typoraPic\image-20200618162528364.png)]

複製算法:

解釋:年輕代每次GC後,倖存下來的會進入一個倖存區,這個倖存區就叫from,第二次GC後,再將from還在用的和Eden Space倖存下來的交給to區(from中的內容通過複製算法轉移),此時to變from,from變to(清空變to)。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-f8exhPXK-1592488206384)(E:\typoraPic\image-20200618165010837.png)]

  • 好處:沒有內存的碎片

  • 壞處:浪費了內存空間:多了一半空間永遠是空to。假設對象100%存活(極端情況),就極大浪費資源

  • 複製算法最佳使用場景:對象存活度較低的時候(新生區);

標記清除法:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-UaX3RchL-1592488206390)(E:\typoraPic\image-20200618170616408.png)]

  • 優點:不需要額外的空間!
  • 缺點:兩次掃描,嚴重浪費時間,會產生內存碎片

標記壓縮(標記清除的優化):

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-yHt1scuG-1592488206399)(E:\typoraPic\image-20200618171118004.png)]

總結:

  • 內存效率:複製算法>標記清除算法>標記壓縮算法(時間複雜度)
  • 內存整齊度:複製算法=標記清除算法>標記清除算法
  • 內存利用率:標記壓縮算法=標記清除算法>複製算法

思考:

  • 問題:難道沒有最優的算法?
  • 答案:沒有,沒有最好的算法,只有最合適的算法–>GC:分代收集算法
  • 年輕代:存活率底–>複製算法
  • 老年代:區域大,存活率高–>標記清除+標記壓縮混合 實現
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章