欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入了解Java虛擬機棧以及內(nèi)存模型

 更新時間:2021年04月14日 11:21:36   作者:chun_soft  
這篇文章主要介紹了深入了解Java虛擬機棧以及內(nèi)存模型,文中有非常詳細的代碼示例,對正在學習java的小伙伴們有很大的幫助,需要的朋友可以參考下

1、結合字節(jié)碼指令理解Java虛擬機棧和棧幀

棧幀:每個棧幀對應一個被調(diào)用的方法,可以理解為一個方法的運行空間。

每個棧幀中包括局部變量表(Local Variables)、操作數(shù)棧(Operand Stack)、指向運行時常量池的引用(A reference to the run-time constant pool)、方法返回地址(Return Address)和附加信息。

局部變量表:方法中定義的局部變量以及方法的參數(shù)存放在這張表中,局部變量表中的變量不可直接使用,如需要使用的話,必須通過相關指令將其加載至操作數(shù)棧中作為操作數(shù)使用。

操作數(shù)棧:以壓棧和出棧的方式存儲操作數(shù)的。

動態(tài)鏈接:每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用,持有這個引用是為了支持方法調(diào)用過程中的動態(tài)連接(Dynamic Linking)。

方法返回地址:當一個方法開始執(zhí)行后,只有兩種方式可以退出,一種是遇到方法返回的字節(jié)碼指令;一種是遇見異常,并且這個異常沒有在方法體內(nèi)得到處理。

在這里插入圖片描述

class Person{
    private String name="Jack";
    private int age;
    private final double salary=100;
    private static String address;
    private final static String hobby="Programming";
   
    public void say(){
        System.out.println("person say..."); 
    }
    
    public static int calc(int op1,int op2){
        op1=3;
        int result=op1+op2;
        return result;
    }
    
    public static void order(){
    }
    
    public static void main(String[] args){
        calc(1,2);
        order(); 
    }
}
Compiled from "Person.java" class Person {
...
  public static int calc(int, int);
    Code:
        0: iconst_3 //將int類型常量3壓入[操作數(shù)棧]
        1: istore_0 //將int類型值存入[局部變量0]
        2: iload_0  //從[局部變量0]中裝載int類型值入棧
        3: iload_1  //從[局部變量1]中裝載int類型值入棧
        4: iadd //將棧頂元素彈出棧,執(zhí)行int類型的加法,結果入棧
        【For example, the iadd instruction (§iadd) adds two int values together. It requires that the int values to be added be the top two values of the operand stack, pushed there by previous instructions. Both of the int values are popped from the operand stack. They are added, and their sum is pushed back onto the operand stack. Subcomputations may be nested on the operand stack, resulting in values that can be used by the encompassing computation.】
        5: istore_2 //將棧頂int類型值保存到[局部變量2]中
        6: iload_2  //從[局部變量2]中裝載int類型值入棧
        7: ireturn  //從方法中返回int類型的數(shù)據(jù)
...
}

在這里插入圖片描述

2、深入分析

2.1 棧指向堆

如果在棧幀中有一個變量,類型為引用類型,比如 Object obj=new Object(),這時候就是典型的棧中元素指向堆中的對象。

在這里插入圖片描述

2.2 方法區(qū)指向堆

方法區(qū)中會存放靜態(tài)變量,常量等數(shù)據(jù)。如果是下面這種情況,就是典型的方法區(qū)中元素指向堆中的對象。

private static Object obj=new Object();

在這里插入圖片描述

2.3 堆指向方法區(qū)

方法區(qū)中會包含類的信息,堆中會有對象,那怎么知道對象是哪個類創(chuàng)建的呢?

在這里插入圖片描述

思考:一個對象怎么知道它是由哪個類創(chuàng)建出來的?怎么記錄?這就需要了解一個Java對象的具體信息咯。

2.4 Java對象內(nèi)存布局

一個Java對象在內(nèi)存中包括3個部分:對象頭、實例數(shù)據(jù)和對齊填充。

在這里插入圖片描述

3、內(nèi)存模型

 3.1 圖解

一塊是非堆區(qū),一塊是堆區(qū)。
堆區(qū)分為兩大塊,一個是Old區(qū),一個是Young區(qū)。 Young區(qū)分為兩大塊,一個是Survivor區(qū)(S0+S1),一塊是Eden區(qū)。 Eden:S0:S1=8:1:1 S0和S1一樣大,也可以叫From和To。

在這里插入圖片描述

根據(jù)之前對于Heap的介紹可以知道,一般對象和數(shù)組的創(chuàng)建會在堆中分配內(nèi)存空間,關鍵是堆中有這么多區(qū)域,那一個對象的創(chuàng)建到底在哪個區(qū)域呢?

3.2 對象創(chuàng)建所在區(qū)域

一般情況下,新創(chuàng)建的對象都會被分配到Eden區(qū),一些特殊的大的對象會直接分配到Old區(qū)。
比如有對象A,B,C等創(chuàng)建在Eden區(qū),但是Eden區(qū)的內(nèi)存空間肯定有限,比如有100M,假如已經(jīng)使用了 100M 或者達到一個設定的臨界值,這時候就需要對Eden內(nèi)存空間進行清理,即垃圾收集(Garbage Collect), 這樣的GC我們稱之為Minor GC,Minor GC指的是Young區(qū)的GC。

經(jīng)過GC之后,有些對象就會被清理掉,有些對象可能還存活著,對于存活著的對象需要將其復制到Survivor 區(qū),然后再清空Eden區(qū)中的這些對象。

3.3 Survivor區(qū)詳解

由圖解可以看出,Survivor區(qū)分為兩塊S0和S1,也可以叫做From和To。 在同一個時間點上,S0和S1只能有一個區(qū)有數(shù)據(jù),另外一個是空的。

接著上面的GC來說,比如一開始只有Eden區(qū)和From中有對象,To中是空的。 此時進行一次GC操作,F(xiàn)rom區(qū)中對象的年齡就會+1,我們知道Eden區(qū)中所有存活的對象會被復制到To區(qū),
From區(qū)中還能存活的對象會有兩個去處。

若對象年齡達到之前設置好的年齡閾值,此時對象會被移動到Old區(qū),􏰀􏰁Eden􏰂􏰃From􏰂沒有達到閾值的 對象會被復制到To區(qū)。 此時Eden區(qū)和From區(qū)已經(jīng)被清空(被GC的對象肯定沒了,沒有被GC的對象都有了各自的去處)。
這時候From和To交換角色,之前的From變成了To,之前的To變成了From。 也就是說無論如何都要保證名為To的Survivor區(qū)域是空的。
Minor GC會一直重復這樣的過程,直到To區(qū)被填滿,然后會將所有對象復制到老年代中。

3.4 Old區(qū)詳解

從上面的分析可以看出,一般Old區(qū)都是年齡比較大的對象,或者相對超過了某個閾值的對象。

在Old區(qū)也會有GC的操作,Old區(qū)的GC我們稱作為Major GC。

3.5 對象的一輩子理解

我是一個普通的Java對象,我出生在Eden區(qū),在Eden區(qū)我還看到和我長的很像的小兄弟,我們在Eden區(qū)中玩了挺長時間。有 一天Eden區(qū)中的人實在是太多了,我就被迫去了Survivor區(qū)的“From”區(qū),自從去了Survivor區(qū),我就開始漂了,有時候在 Survivor的“From”區(qū),有時候在Survivor的“To”區(qū),居無定所。直到我18歲的時候,爸爸說我成人了,該去社會上闖闖了。于是我就去了年老代那邊,年老代里,人很多,并且年齡都挺大的,我在這里也認識了很多人。在年老代里,我生活了20年(每次 GC加一歲),然后被回收。

在這里插入圖片描述

3.6 常見問題

  •  如何理解Minor/Major/Full GC

Minor GC:新生代

Major GC:老年代

Full GC:新生代+老年代

  • 為什么需要Survivor區(qū)?只有Eden不行嗎?

如果沒有Survivor,Eden區(qū)每進行一次Minor GC,存活的對象就會被送到老年代。 這樣一來,老年代很快被填滿,觸發(fā)Major GC(因為Major GC一般伴隨著Minor GC,也可以看做觸發(fā)了Full GC)。 老年代的內(nèi)存空間遠大于新生代,進行一次Full GC消耗的時間比Minor GC長得多。執(zhí)行時間長有什么壞處?頻發(fā)的Full GC消耗的時間很長,會影響大型程序的執(zhí)行和響應速度。

可能你會說,那就對老年代的空間進行增加或者較少咯。假如增加老年代空間,更多存活對象才能填滿老年代。雖然降低Full GC頻率,但是隨著老年代空間加大,一旦發(fā)生Full GC,執(zhí)行所需要的時間更長。

假如減少老年代空間,雖然Full GC所需時間減少,但是老年代很快被存活對象填滿,F(xiàn)ull GC頻率增加。

所以Survivor的存在意義,就是減少被送到老年代的對象,進而減少Full GC的發(fā)生,Survivor的預篩選保證,只有經(jīng)歷16 次Minor GC還能在新生代中存活的對象,才會被送到老年代。

  • 為什么需要兩個Survivor區(qū)?

最大的好處就是解決了碎片化。也就是說為什么一個Survivor區(qū)不行?第一部分中,我們知道了必須設置Survivor區(qū)。假設,現(xiàn)在只有一個Survivor區(qū),我們來模擬一下流程:

剛剛新建的對象在Eden中,一旦Eden滿了,觸發(fā)一次Minor GC,Eden中的存活對象就會被移動到Survivor區(qū)。這樣繼續(xù)循環(huán)下去,下一次Eden滿了的時候,問題來了,此時進行Minor GC,Eden和Survivor各有一些存活對象,如果此時把Eden區(qū)的,存活對象硬放到Survivor區(qū),很明顯這兩部分對象所占有的內(nèi)存是不連續(xù)的,也就導致了內(nèi)存碎片化。
永遠有一個Survivor space是空的,另一個非空的Survivor space無碎片。

  • 新生代中Eden:S1:S2為什么是8:1:1?

新生代中的可用內(nèi)存:復制算法用來擔保的內(nèi)存為9:1

可用內(nèi)存中Eden:S1區(qū)為8:1

即新生代中Eden:S1:S2 = 8:1:1

4、驗證

4.1 堆內(nèi)存溢出

程序:

@RestController
public class HeapController {
    List<Person> list=new ArrayList<Person>();
    @GetMapping("/heap")
    public String heap() throws Exception{
        while(true){
            list.add(new Person()); 
            Thread.sleep(1);
        } 
    }
}

記得設置參數(shù)比如-Xmx20M -Xms20M

運行結果:

Exception in thread "http-nio-8080-exec-2" java.lang.OutOfMemoryError: GC overhead limit exceeded

4.2 方法區(qū)內(nèi)存溢出

比如向方法區(qū)中添加Class的信息

4.2.1 asm依賴和Class代碼

<dependency> 
    <groupId>asm</groupId> 
    <artifactId>asm</artifactId> 
    <version>3.3.1</version>
</dependency>
public class MyMetaspace extends ClassLoader {
    public static List<Class<?>> createClasses() {
        List<Class<?>> classes = new ArrayList<Class<?>>();
        for (int i = 0; i < 10000000; ++i) {
            ClassWriter cw = new ClassWriter(0);
            cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null,
            "java/lang/Object", null);
            MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
            "()V", null, null);
            mw.visitVarInsn(Opcodes.ALOAD, 0); mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object",
            "<init>", "()V"); mw.visitInsn(Opcodes.RETURN);
            mw.visitMaxs(1, 1);
            mw.visitEnd();
            Metaspace test = new Metaspace();
            byte[] code = cw.toByteArray();
            Class<?> exampleClass = test.defineClass("Class" + i, code, 0, code.length); 
            classes.add(exampleClass);
        }
        return classes;
    }
}

4.2.2 代碼

@RestController
public class NonHeapController {
    List<Class<?>> list=new ArrayList<Class<?>>();
    @GetMapping("/nonheap")
    public String nonheap() throws Exception{
        while(true){ 
        list.addAll(MyMetaspace.createClasses()); 
        Thread.sleep(5);
        } 
    }
}

設置Metaspace的大小,比如-XX:MetaspaceSize=50M -XX:MaxMetaspaceSize=50M

運行結果:

java.lang.OutOfMemoryError: Metaspace
at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_191]
at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[na:1.8.0_191]

4.3 虛擬機棧

 4.3.1 代碼演示StackOverFlow

public class StackDemo {
    public static long count=0;
    public static void method(long i){
        System.out.println(count++);
        method(i); 
    }
    public static void main(String[] args) {
        method(1);
    }
}

運行結果:

在這里插入圖片描述

4.3.2 理解和說明

Stack Space用來做方法的遞歸調(diào)用時壓入Stack Frame(棧幀)。所以當遞歸調(diào)用太深的時候,就有可能耗盡Stack Space,爆出StackOverflow的錯誤。

-Xss128k:設置每個線程的堆棧大小。JDK 5以后每個線程堆棧大小為1M,以前每個線程堆棧大小為256K。根據(jù)應用的線程所需內(nèi)存大小進行調(diào)整。在相同物理內(nèi)存下,減小這個值能生成更多的線程。但是操作系統(tǒng)對一個進程內(nèi)的線程數(shù)還是有限制的,不能無限生成,經(jīng)驗值在3000~5000左右。

線程棧的大小是個雙刃劍,如果設置過小,可能會出現(xiàn)棧溢出,特別是在該線程內(nèi)有遞歸、大的循環(huán)時出現(xiàn)溢出的可能性更大,如果該值設置過大,就有影響到創(chuàng)建棧的數(shù)量,如果是多線程的應用,就會出現(xiàn)內(nèi)存溢出的錯誤。

到此這篇關于深入了解Java虛擬機棧以及內(nèi)存模型的文章就介紹到這了,更多相關java虛擬機棧和內(nèi)存模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java適配器模式之如何靈活應對不匹配的接口

    Java適配器模式之如何靈活應對不匹配的接口

    本文介紹了Java中的適配器模式,包括對象適配器模式和接口適配器模式,適配器模式通過將一個類的接口轉(zhuǎn)換成客戶期望的另一個接口,解決了不同接口之間的不兼容問題,它提高了系統(tǒng)的靈活性、復用性和解耦性,需要的朋友可以參考下
    2024-10-10
  • Spring boot通過切面,實現(xiàn)超靈活的注解式數(shù)據(jù)校驗過程

    Spring boot通過切面,實現(xiàn)超靈活的注解式數(shù)據(jù)校驗過程

    這篇文章主要介紹了Spring boot通過切面,實現(xiàn)超靈活的注解式數(shù)據(jù)校驗過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Spring Boot非Web項目運行配置的方法教程

    Spring Boot非Web項目運行配置的方法教程

    這篇文章主要介紹了Spring Boot非Web項目運行配置的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Spring Boot具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-09-09
  • java利用正則表達式處理特殊字符的方法實例

    java利用正則表達式處理特殊字符的方法實例

    這篇文章主要給大家介紹了關于java利用正則表達式處理特殊字符的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-12-12
  • Java中四種線程池的使用示例詳解

    Java中四種線程池的使用示例詳解

    這篇文章主要給大家介紹了關于Java中四種線程池的使用方法,四種線程池分別包括FixedThreadPool、CachedThreadPool、ScheduledThreadPool以及SingleThreadExecutor,文中給出了詳細的示例代碼供大家參考,需要的朋友們下面來一起看看吧。
    2017-08-08
  • Java構建JDBC應用程序的實例操作

    Java構建JDBC應用程序的實例操作

    在本篇文章里小編給大家整理了一篇關于Java構建JDBC應用程序的實例操作,有興趣的朋友們可以學習參考下。
    2021-03-03
  • J2SE基礎之下載eclipse并創(chuàng)建項目

    J2SE基礎之下載eclipse并創(chuàng)建項目

    本文給大家介紹的是最流行的java 集成開發(fā)環(huán)境IDE eclipse的使用方法,非常的簡單,有需要的小伙伴可以參考下
    2016-05-05
  • spring-boot-maven-plugin?配置有啥用

    spring-boot-maven-plugin?配置有啥用

    這篇文章主要介紹了spring-boot-maven-plugin?配置是干啥的,這個是SpringBoot的Maven插件,主要用來打包的,通常打包成jar或者war文件,本文通過示例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2022-08-08
  • java如何獲取指定文件夾下的所有文件名

    java如何獲取指定文件夾下的所有文件名

    這篇文章主要介紹了java如何獲取指定文件夾下的所有文件名問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Spring MVC項目中l(wèi)og4J和AOP使用詳解

    Spring MVC項目中l(wèi)og4J和AOP使用詳解

    項目日志記錄是項目開發(fā)、運營必不可少的內(nèi)容,有了它可以對系統(tǒng)有整體的把控,出現(xiàn)任何問題都有蹤跡可尋。下面這篇文章主要給大家介紹了關于Spring MVC項目中l(wèi)og4J和AOP使用的相關資料,需要的朋友可以參考下。
    2017-12-12

最新評論