国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

淺析JVM之內存管理

Eric / 2252人閱讀

摘要:概要要理解的內存管理策略,首先就要熟悉的運行時數據區,如上圖所示,在執行程序的時候,虛擬機會把它所管理的內存劃分為多個不同的數據區,稱為運行時數據區。

這是一篇有關JVM內存管理的文章。這里將會簡單的分析一下Java如何使用從物理內存上申請下來的內存,以及如何來劃分它們,后面還會介紹JVM的核心技術:如何分配和回收內存。

JMM ( Java Memory Model )概要

要理解JVM的內存管理策略,首先就要熟悉Java的運行時數據區,如上圖所示,在執行Java程序的時候,虛擬機會把它所管理的內存劃分為多個不同的數據區,稱為運行時數據區。在程序執行過程中對內存的分配、垃圾的回收都在運行時數據區中進行。對于Java程序員來說,其中最重要的就是堆區和JVM棧區了。注意圖中的圖形面積比例并不代表實際的內存比例

綠色的區域代表被線程所共享

黃色的區域代表被線程所獨享

下面來簡單的講一下圖中的區塊。

方法區:存儲虛擬機運行時加載的類信息、常量、靜態變量和即時編譯的代碼,因此可以把這一部分考慮為一個保存相對來說數據較為固定的部分,常量和靜態變量在編譯時就確定下來進入這部分內存,運行時類信息會直接加載到這部分內存,所以都是相對較早期進入內存的。

- **運行時常量池**:在JVM規范中是這樣定義運行時常量池這個數據結構的:Runtime Constant Pool代表運行時每個class文件的常量表。它包含幾種常量:編譯期的數字常量、方法和域的引用(在運行時解析)。它的功能類似于傳統編程語言的符號表,盡管它包含的數據比典型的符號表要豐富得多。每個Runtime Constant Pool都是在JVM的Method area中分配的,每個Class或者Interface的Constant Pool都是在JVM創建class或接口時創建的。它是**屬于方法區**的一部分,所以它的存儲也受方法區的規范約束,如果常量池無法分配,同樣會拋出OutOfMemoryError。

堆區:是JVM所管理的內存中最大的一塊。主要用于存放對象實例,每一個存儲在堆中的Java對象都會是這個對象的類的一個副本,它會復制包括繼承自它父類的所有非靜態屬性。而所謂的垃圾回收也主要是在堆區進行。 根據Java虛擬機規范的規定,Java堆可以處于物理上不連續的內存空間中,只要邏輯是上連續的即可,就像我們的磁盤空間一樣。在實現上,既可以實現成固定大小的,也可以是可擴展的:

- 如果是固定大小的,那么堆的大小在JVM啟動時就一次向操作系統申請完成,旦分配完成,堆的大小就將固定,不能在內存不夠時再向操作系統重新申請,同時當內存空閑時也不能將多余的空間交還給操作系統。
- 如果是可擴展的,則通過 -Xmx和 -Xms兩個選項來控制大小,Xmx來表示堆的最大大小,Xms表示初始大小。

JVM棧區:則主要存放一些對象的引用和編譯期可知的基本數據類型,這個區域是線程私有的,即每個線程都有自己的棧。在Java虛擬機規范中,對這個區域規定了兩種異常情況:

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

如果虛擬機棧可以動態擴展,擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError異常

本地方法棧:本地方法在運行時存儲數據產生的棧區。為JVM運行Native方法準備的空間,它和前面介紹的Java棧的作用是類似的,由于很多Native方法都是C語言實現的,所以它通常又叫C棧。和虛擬機棧一樣,也會拋出StackOverflowErrorOutOfMemoryError異常。

程序計數器:則是用來記錄程序運行到什么位置的,顯然它應該是線程私有的,相信這個學過微機原理與接口課程的同學都應該能夠理解的。

舉個栗子

通常我們定義一個基本數據類型的變量,一個對象的引用,還有就是函數調用的現場保存都使用內存中的棧空間;而通過new關鍵字和構造器創建的對象放在堆空間;程序中的字面量(literal)如直接書寫的100、“hello”和常量都是放在靜態存儲區中。棧空間操作最快但是也很小,通常大量的對象都是放在堆空間,整個內存包括硬盤上的虛擬內存都可以被當成堆空間來使用。

String str = new String(“hello”);

str 這個引用放在棧上

new 創建出來的對象實例放在堆上

JVM內存分配策略

在分析JVM內存分配策略之前,我們先介紹一下通常情況下操作系統都是采用哪些策略來分配內存的。

通常的內存分配策略

在操作系統中,將內存分配策略分為三種,分別是:

靜態內存分配

棧內存分配

堆內存分配

靜態內存分配 是指在程序編譯時鋸能確定每個數據在運行時的存儲空間需求,因此在編譯時就可以給它們分配固定的內存空間。這種分配策略不允許在程序代碼中有可變數據結構(如可變數組)的存在,也不允許有嵌套或者遞歸的結構出現,因為它們都會導致編譯程序無法計算機準確的存儲空間需求。

棧內存分配 也可稱為動態存儲分配,是由一個類似于堆棧的運行棧來實現的。和靜態內存分配相反,在棧式內存方案執行宏,程序對數據區的需求在編譯時是完全無知的,只有運行時才能知道,但是規定在運行中進入一個程序模塊時,必須知道該程序模塊所需數據區大小才能為其分配內存。和我們所數值的數據結構中的棧一樣,棧式內存分配按照先進后出的原則進行分配。

堆內存分配 當程序真正運行到相應代碼時才會知道空間大小。

Java中的內存分配一覽

JVM內存分配主要基于兩種:堆和棧。

先來說說

Java棧的分配是和線程綁定在一起的,當我們創建一個線程時,很顯然,JVM就會為這個線程創建一個新的Java棧,一個線程的方法的調用和返回對應這個Java棧的壓棧和出棧。當線程激活一個Java方法時,JVM就會在線程的Java棧里新壓入一個幀,這個幀自然成了當前幀。在此方法執行期間,這個幀將用來保存參數、局部變量、中間計算過程和其他數據。

棧中主要存放一些基本類型的變量數據和對象句柄(引用)。存取速度比堆要快,僅次于寄存器,棧數據可以共享。缺點是,存在棧中的數據大小與生存期必須是確定的,這也導致缺乏了其靈活性。

Java的 是一個運行時數據區,它們不需要程序代碼來顯示地釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因為它是在運行時動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由于要運行時動態分配內存,存取速度慢。

從堆和棧的功能和作用通俗地比較,堆主要用來存放對象,棧主要用來執行程序,這種不同主要由堆和棧的特點決定的。

在編程中,如C/C++,所有的方法調用是通過棧進行的,所有的局部變量、形式參數都是從棧中分配內存空間的。實際上也不是什么分配,只是從棧向上用就行,就好像工廠中的傳送帶一樣,棧指針會自動指引你到放東西的位置,你所要做的只是把東西放下來就行。在退出函數時,修改棧指針就可以把棧中的內潤銷毀。這樣的模式速度最快,當然要用來運行程序了。需要注意的是,在分配時,如為一個即將要調用的程序模塊分配數據區時,應事先知道這個數據區的大小,也就是說上雖然分配是在程序運行中進行的,但是分配的大小是確定的、不變的,而這個“大小多少”是在編譯時確定的,而不是在運行時。

堆在應用程序運行時請求操作系統給自己分配內存,由于操作系統管理內存分配,所以在分配和銷毀時都要占用時間,因此用堆的效率非常低。但是堆的優點在于,編譯器不必知道從堆里分配多少存儲空間,也不必知道存儲的數據要在堆里停留多長時間。因此,用堆保存數據時會得到更大的靈活性,事實上,由于面向對象的多態性,堆內存分配是必不可少的,因為多態變量所需的存儲空間只有在運行時創建了對象之后才能確定。在C++中,要求創建一個對象時,只需用new命令編制相關命令即可。執行這些代碼時,會在堆里自動進行數據的保存。當然,為達到這種靈活性,必然會付出一定的代價——在堆里分配存儲空間會花掉更長的時間。

JVM內存回收策略 基本術語 垃圾(Garbage)

即需要回收的對象。作為編寫程序的人,是可以做出“這個對象已經不再需要了”這樣的判斷,但計算機是做不到的。因此,如果程序(通過某個變量等等)可能會直接或間接地引用一個對象,那么這個對象就被視為“存活”;與之相反,已經引用不到的對象被視為“死亡”。將這些“死亡”對象找出來,然后作為垃圾進行回收,這就是GC的本質。

根(Root)

即判斷對象是否可被引用的起始點。至于哪里才是根,不同的語言和編譯器都有不同的規定,但基本上是將變量和運行棧空間作為根。各位肯定會好奇根對象集合中都是些什么,下面就來簡單的講一講:

在方法中局部變量區的對象的引用

在Java操作棧中的對象引用:有些對象是直接在操作棧中持有的,所以操作棧肯定也包含根對象集合。

在常量池中的對象引用:每個類都會包含一個常量池,這些常量池中也會包含很多對象引用,如表示類名的字符串就保存在堆中,那么常量池只會持有這個字符串對象的引用。

在本地方法中持有的對象引用:有些對象被傳入本地方法中,但是這些對象還沒有被釋放。

類Class對象:當每個類被JVM加載時都會創建一個代表這個類的唯一數據類型的Class對象,而這個Class對象也同樣存放在堆中,當這個類不再被使用時,在方法去中類數據和這個Class對象同樣需要被回收。

JVM在做GC時會檢查堆中所有對象是否都會被這些根對象直接或間接引用,能夠被引用的對象就是活動對象,否則就可以被垃圾收集器回收。

stop-the-world

不管選擇哪種GC算法,stop-the-world都是不可避免的。Stop-the-world意味著從應用中停下來并進入到GC執行過程中去。一旦Stop-the-world發生,除了GC所需的線程外,其他線程都將停止工作,中斷了的線程直到GC任務結束才繼續它們的任務。不然由于應用代碼一直在運行中,會不斷創建和修改對象,導致結果腐化。GC調優通常就是為了改善stop-the-world的時間。

內存的分配方法 指針碰撞

在連續剩余空間中分配內存。用一個指針指向內存已用區和空閑區的分界點,需要分配新的內存時候,只需要將指針向空閑區移動相應的距離即可。

空閑列表

在不規整的剩余空間中分配內存。如果剩余內存是不規整的,就需要用一個列表記錄下哪些內存塊是可用的,當需要分配內存的時候就需要在這個列表中查找,找到一個足夠大的空間進行分配,然后在更新這個列表。

分配方式的選擇

指針碰撞的分配方式明顯要優于空閑列表的方式,但是使用哪種方式取決于堆內存是否規整,而堆內存是否規整則由使用的垃圾收集算法決定。如果堆內存是規整的,則采用指針碰撞的方式分配內存,而如果堆是不規整的,就會采用空閑列表的方式。

垃圾回收是如何進行的? 尋找垃圾

要對對象進行回收,首先需要找到哪些對象是垃圾,需要回收。有兩種方法可以找到需要回收的對象,第一種叫做引用計數法

具體方法就是給對象添加一個引用計數器,計數器的值代表著這個對象被引用的次數,當計數器的值為0的時候,就代表沒有引用指向這個對象,那么這個對象就是不可用的,所以就可以對它進行回收。但是有一個問題就是當對象之間循環引用時,比如這樣:

public class Main {
   public static void main(String[] args) {
       MyObject object1 = new MyObject();
       MyObject object2 = new MyObject();

       object1.object = object2;
       object2.object = object1;
//最后面兩句將object1和object2賦值為null,也就是說object1和object2指向的對象已經不可能再被訪問,
//但是由于它們互相引用對方,導致它們的引用計數都不為0,那么垃圾收集器就永遠不會回收它們。
       object1 = null;
       object2 = null;
   }
}

class MyObject{
   public Object object = null;
}

其中每個對象的引用計數器的值都不為0,但是這些對象又是作為一個孤立的整體在內存中存在,其他的對象不持有這些對象的引用,這種情況下這些對象就無法被回收,這也是主流的Java虛擬機沒有選用這種方法的原因。

另一種方法就是把堆中的對象和對象之間的引用分別看作有向圖的頂點和有向邊——即可達性分析法。這樣只需要從一些頂點開始,對有向圖中的每個頂點進行可達性分析(深度優先遍歷是有向圖可達性算法的基礎),這樣就可以把不可達的對象找出來,這些不可達的對象還要再進行一次篩選,因為如果對象需要執行finalize()方法,那么它完全可以在finalize()方法中讓自己變的可達。這個方法解決了對象之間循環引用的問題。上面提到了“從一些對象開始”進行可達性分析,這些起始對象被稱為GC Roots,可以作為GC Roots的對象有:

棧區中引用的對象

方法區中靜態屬性或常量引用的對象

上文中提到的引用均是強引用,Java中還存在其他三種引用,分別是,軟引用、弱引用和虛引用,當系統即將發生內存溢出時,才會對軟引用所引用的對象進行回收;而被弱引用所引用的對象會在下一次觸發GC時被回收;虛引用則僅僅是為了在對象被回收時能夠收到系統通知。

生存還是死亡

即使在可達性分析算法中不可達的對象,也并非是“非死不可”的,這時候它們暫時處于“緩刑”階段,要真正宣告一個對象死亡,至少要經歷再次標記過程。

標記的前提是對象在進行可達性分析后發現沒有與GC Roots相連接的引用鏈。

第一次標記并進行一次篩選

篩選的條件是此對象是否有必要執行finalize()方法。
當對象沒有覆蓋finalize方法,或者finzlize方法已經被虛擬機調用過,虛擬機將這兩種情況都視為“沒有必要執行”,對象被回收。

第二次標記

如果這個對象被判定為有必要執行finalize()方法,那么這個對象將會被放置在一個名為:F-Queue的隊列之中,并在稍后由一條虛擬機自動建立的、低優先級的Finalizer線程去執行。這里所謂的“執行”是指虛擬機會觸發這個方法,但并不承諾會等待它運行結束。這樣做的原因是,如果一個對象finalize()方法中執行緩慢,或者發生死循環(更極端的情況),將很可能會導致F-Queue隊列中的其他對象永久處于等待狀態,甚至導致整個內存回收系統崩潰。

Finalize()方法是對象脫逃死亡命運的最后一次機會,稍后GC將對F-Queue中的對象進行第二次小規模標記,如果對象要在finalize()中成功拯救自己————只要重新與引用鏈上的任何的一個對象建立關聯即可,譬如把自己賦值給某個類變量或對象的成員變量,那在第二次標記時它將移除出“即將回收”的集合。如果對象這時候還沒逃脫,那基本上它就真的被回收了。

/**
 * 此代碼演示了兩點
 * 1、對象可以在被GC時自我拯救
 * 2、這種自救的機會只有一次,因為一個對象的finalize()方法最多只能被系統自動調用一次。
 */
public class FinalizeEscapeGC {
    public static FinalizeEscapeGC SAVE_HOOK = null;

    public void isAlive() {
        System.out.println("yes, I am still alive");
    }

    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed!");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }

    public static void main(String[] args) throws InterruptedException {
        SAVE_HOOK = new FinalizeEscapeGC();

        //對象第一次成功拯救自己
        SAVE_HOOK = null;
        System.gc();

        //因為finalize方法優先級很低,所有暫停0.5秒以等待它
        Thread.sleep(500);
        if (SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("no ,I am dead QAQ!");
        }

        //-----------------------
        //以上代碼與上面的完全相同,但這次自救卻失敗了!!!
        SAVE_HOOK = null;
        System.gc();

        //因為finalize方法優先級很低,所有暫停0.5秒以等待它
        Thread.sleep(500);
        if (SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("no ,I am dead QAQ!");
        }
    }
}

最后想說的是:請不要使用finalize()方法,使用try-finalize可以做的更好。這是一個歷史遺留的問題——當年為了讓C/C++程序員更好的接受它而做出的妥協。

垃圾收集算法

好了,我們找到了垃圾。來談談如何處理這些垃圾吧。

標記-清除算法

標記清除(Mark and Sweep)是最早開發出的GC算法(1960年)。它的原理非常簡單,首先從根開始將可能被引用的對象用遞歸的方式進行標記,然后將沒有標記到的對象作為垃圾進行回收。

通過可達性分析算法找到可以回收的對象后,要對這些對象進行標記,代表它可以被回收了。標記完成之后就統一回收所有被標記的對象。這就完成了回收,但是這種方式會產生大量的內存碎片,就導致了可用內存不規整,于是分配新的內存時就需要采用空閑列表的方法,如果沒有找到足夠大的空間,那么就要提前觸發下一次垃圾收集。

標記-整理算法

作為標記清除的變形,還有一種叫做標記整理(Mark and Compact)的算法。

標記的過程和標記-清除算法一樣,但是標記完成之后,讓所有存活的對象都向堆內存的一端移動,最后直接清除掉邊界以外的內存。這樣對內存進行回收之后,內存是規整的,于是可以使用指針碰撞的方式分配新的內存。

復制收集算法

“標記”系列的算法有一個缺點,就是在分配了大量對象,并且其中只有一小部分存活的情況下,所消耗的時間會大大超過必要的值,這是因為在清除階段還需要對大量死亡對象進行掃描。復制收集(Copy and Collection)則試圖克服這一缺點。在這種算法中,會將從根開始被引用的對象復制到另外的空間中,然后,再將復制的對象所能夠引用的對象用遞歸的方式不斷復制下去。

圖2的(1)部分是GC開始前的內存狀態,這和圖1的(1)部分是一樣的

圖2的(2)部分中,在舊對象所在的“舊空間”以外,再準備出一塊“新空間”,并將可能從根被引用的對象復制到新空間中

圖2的(3)部分中,從已經復制的對象開始,再將可以被引用的對象像一串糖葫蘆一樣復制到新空間中。復制完成之后,“死亡”對象就被留在了舊空間中

圖2的(4)部分中,將舊空間廢棄掉,就可以將死亡對象所占用的空間一口氣全部釋放出來,而沒有必要再次掃描每個對象。下次GC的時候,現在的新空間也就變成了將來的舊空間

通過圖2我們可以發現,復制收集方式中,只存在相當于標記清除方式中的標記階段。由于清除階段中需要對現存的所有對象進行掃描,在存在大量對象,且其中大部分都即將死亡的情況下,全部掃描一遍的開銷實在是不小。而在復制收集方式中,就不存在這樣的開銷。

但是,和標記相比,將對象復制一份所需要的開銷則比較大,因此在“存活”對象比例較高的情況下,反而會比較不利。這種算法的另一個好處是它具有局部性(Lo-cality)。在復制收集過程中,會按照對象被引用的順序將對象復制到新空間中。于是,關系較近的對象被放在距離較近的內存空間中的可能性會提高,這被稱為局部性。局部性高的情況下,內存緩存會更容易有效運作,程序的運行性能也能夠得到提高。

基于分代技術的算法抉擇

上文提到了幾種GC算法,但是各自的各自的優點,必須放到適合的場景內才能發揮最大的效率。

在JVM堆里分有兩部分:新生代(young generate)和老年代(old generation)。

在新生代中長期存活的對象會逐漸向老年代過渡,新生代中的對象每經歷一次GC,年齡就增加一歲,當年齡超過一定值時,就會被移動到老年代。

新生代

大部分的新創建對象分配在新生代。因為大部分對象很快就會變得不可達,所以它們被分配在新生代,然后消失不再。當對象從新生代移除時,我們稱之為"Minor GC"。新生代使用的是復制收集算法

新生代劃分為三個部分:分別為Eden、Survivor from、Survivor to,大小比例為8:1:1(為了防止復制收集算法的浪費內存過大)。每次只使用Eden和其中的一塊Survivor,回收時將存活的對象復制到另一塊Survivor中,這樣就只有10%的內存被浪費,但是如果存活的對象總大小超過了Survivor的大小,那么就把多出的對象放入老年代中。

在三個區域中有兩個是Survivor區。對象在三個區域中的存活過程如下:

大多數新生對象都被分配在Eden區。

第一次GC過后Eden中還存活的對象被移到其中一個Survivor區。

再次GC過程中,Eden中還存活的對象會被移到之前已移入對象的Survivor區。

一旦該Survivor區域無空間可用時,還存活的對象會從當前Survivor區移到另一個空的Survivor區。而當前Survivor區就會再次置為空狀態。

經過數次(默認是15次)在兩個Survivor區域移動后還存活的對象最后會被移動到老年代。

如上所述,兩個Survivor區域在任何時候必定有一個保持空白。如果同時有數據存在于兩個Survivor區或者兩個區域的的使用量都是0,則意味著你的系統可能出現了運行錯誤。

老年代

存活在新生代中但未變為不可達的對象會被復制到老年代。一般來說老年代的內存空間比新生代大,所以在老年代GC發生的頻率較新生代低一些。當對象從老年代被移除時,我們稱之為 "Major GC"(或者Full GC)。 老年代使用標記-清理或標記-整理算法

老年代里放著什么?

new 出來的大對象

長期存活的對象(前面說過)

如果在Survivor空間中相同年齡所有對象的綜合大于Survivor空間的一半,年齡大于或等于該年齡的對象就可以直接進入老年代,無須等待MaxTenuringThreshold中要求的年齡(默認是15)。

空間分配擔保

在發生Minor GC前,虛擬機會先檢查老年代最大可用的連續空間是否大于新生代所有對象總空間。

如果大于,那么Minor GC可以確保是安全的。

如果小于,虛擬機會查看HandlePromotionFailure設置值是否允許擔任失敗。

如果允許,那么會繼續檢查老年代最大可用連續空間是否大于歷次晉升老年代對象的平均大小

如果大于,將嘗試著進行一次Minor GC,盡管這次Minor GC是有風險的

.如果小于,進行一次Full GC.

如果不允許,也要改為進行一次Full GC.

前面提到過,新生代使用復制收集算法,但為了內存利用率,只使用其中一個Survivor空間來作為輪換備份,因此當出現大量對象在Minor GC后仍然存活的情況時(最極端就是內存回收后新生代中所有對象都存活),就需要老年代進行分配擔保,讓Survivor無法容納的對象直接進入老年代。與生活中的貸款擔保類似,老年代要進行這樣的擔保,前提是老年代本身還有容納這些對象的剩余空間,一共有多少對象會活下來,在實際完成內存回收之前是無法明確知道的,所以只好取之前每一次回收晉升到老年代對象容量的平均大小值作為經驗值,與老年代的剩余空間進行比較,決定是否進行Full GC來讓老年代騰出更多空間。

取平均值進行比較其實仍然是一種動態概率的手段,也就是說如果某次Minor GC存活后的對象突增,遠遠高于平均值的話,依然會導致擔保失敗(Handle Promotion Failure)。如果出現了HandlePromotionFailure失敗,那就只好在失敗后重新發起一次Full GC。雖然擔保失敗時繞的圈子是最大的,但大部分情況下都還是會將HandlePromotionFailure開關打開,避免Full GC過于頻繁。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65756.html

相關文章

  • java學習(四) —— 內存分配淺析

    摘要:內存分配解析四方法執行完畢,立即釋放局部變量所占用的棧空間。內存分配解析五調用對象的方法,以實例為參數。堆和棧的小結以上就是程序運行時內存分配的大致情況。 前言 java中有很多類型的變量、靜態變量、全局變量及對象等,這些變量在java運行的時候到底是如何分配內存的呢?接下來有必要對此進行一些探究。 基本知識概念: (1)寄存器:最快的存儲區, 由編譯器根據需求進行分配,我們在程序...

    henry14 評論0 收藏0
  • Java深入-框架技巧

    摘要:從使用到原理學習線程池關于線程池的使用,及原理分析分析角度新穎面向切面編程的基本用法基于注解的實現在軟件開發中,分散于應用中多出的功能被稱為橫切關注點如事務安全緩存等。 Java 程序媛手把手教你設計模式中的撩妹神技 -- 上篇 遇一人白首,擇一城終老,是多么美好的人生境界,她和他歷經風雨慢慢變老,回首走過的點點滴滴,依然清楚的記得當初愛情萌芽的模樣…… Java 進階面試問題列表 -...

    chengtao1633 評論0 收藏0
  • OpenJDK9 Hotspot : synchronized 淺析

    摘要:前言網上各路大神總結過各種關于內部實現,看別人的文章總覺得不過癮,所以有了這篇文章,嘗試再扒一次的底褲數據結構在分析源代碼之前需要了解相關概念,比如等,參考網絡上各種解說或者之前系列文章,這里重點介紹一下,,每個在內部都有一個的對象與之對應 前言 網上各路大神總結過各種關于 hotspot jvm synchronized 內部實現,看別人的文章總覺得不過癮,所以有了這篇文章,嘗試再扒...

    Amio 評論0 收藏0
  • jvm原理

    摘要:在之前,它是一個備受爭議的關鍵字,因為在程序中使用它往往收集器理解和原理分析簡稱,是后提供的面向大內存區數到數多核系統的收集器,能夠實現軟停頓目標收集并且具有高吞吐量具有更可預測的停頓時間。 35 個 Java 代碼性能優化總結 優化代碼可以減小代碼的體積,提高代碼運行的效率。 從 JVM 內存模型談線程安全 小白哥帶你打通任督二脈 Java使用讀寫鎖替代同步鎖 應用情景 前一陣有個做...

    lufficc 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<