jvm在執(zhí)行java程序的過程中會把所管理的內存分為若干不同的區(qū)域,有的區(qū)域隨著虛擬機進程的啟動而存在,有些區(qū)域則是依賴用戶線程的啟動和結束而建立和銷毀的。
以下是java運行時內存區(qū)域圖:
程序計數(shù)器是一塊較小的區(qū)域,它可以看做是當前所執(zhí)行的字節(jié)碼的行號指示器。在java的多線程交互執(zhí)行的過程中,被中斷的線程當前執(zhí)行到那條指令的內存地址需要被保存下來,以便于該中斷線程恢復執(zhí)行的時候可以按照被中斷時的指令繼續(xù)執(zhí)行下去。
為了能使每個線程切換后都能恢復到正確的位置,因此每一個線程都是需要有自己的程序計數(shù)器。每個線程獨立存儲,互不影響,因此我們成這個內存為“線程私有”的內存。
每當創(chuàng)建一個線程,jvm就會為該線程創(chuàng)建一個java虛擬機棧,虛擬機棧的生命周期與線程相同,它隨線程的創(chuàng)建與結束而啟動和銷毀的。該線程每調用一個方法,jvm就會在該虛擬機棧中創(chuàng)建一個棧幀,用于存放局部變量表,操作數(shù)棧,接口信息等。每一個方法的從調用到完成的過程就對應一個棧幀在虛擬機棧中的入棧以及出棧的過程。正在執(zhí)行的方法的棧幀位于棧頂。
虛擬機棧的這個內存區(qū)域為"線程私有"的內存。
該區(qū)域可拋出兩種異常情況:
StackOverflowError異常:線程請求的棧的深度大于了虛擬機所允許的深度。
OutOfMemoryError異常:虛擬機棧東塔擴展時無法申請到足夠長度。
本地方法棧和Java棧所發(fā)揮的作用非常相似,區(qū)別不過是Java棧為JVM執(zhí)行Java方法服務,而本地方法棧為JVM執(zhí)行Native方法服務。 本地方法棧也會拋出StackOverflowError和OutOfMemoryError異常。
本地方法棧的這個內存區(qū)域為"線程私有"的內存。
堆是JVM所管理的內存中國最大的一塊,是被所有Java線程鎖共享的,不是線程安全的,在JVM啟動時創(chuàng)建。堆是存儲Java對象的地方,這一點Java虛擬機規(guī)范中描述是:所有的對象實例以及數(shù)組都要在堆上分配。Java堆是GC管理的主要區(qū)域,從內存回收的角度來看,由于現(xiàn)在GC基本都采用分代收集算法,所以Java堆還可以細分為:新生代和老年代;新生代再細致一點有Eden空間、From Survivor空間、To Survivor空間等。
方法區(qū)用于存儲已被虛擬機加載的類信息,常量,靜態(tài)變量即編譯后的代碼以及數(shù)據(jù)。
在方法區(qū)中存在一部分內存稱為“運行時常量區(qū)”,用于存放編譯器生成的各種字面量以及符號引用量?!酒渲?,字面量就是指final變量等;引用變量包括類和接口的全限定名,字段名稱,方法名和描述符】
方法區(qū)為java線程共享
當方法區(qū)無法滿足內存需求的時候,將拋出OutOfMemoryError異常。
1. java內存模型(java Memory Model , JMM)就是一種符合內存模型規(guī)范的,屏蔽了各種硬件和操作系統(tǒng)的訪問差異的,保證了java程序在各種平臺下對內存訪問都能保證一致的機制及規(guī)范。
** 2. **Java內存模型規(guī)定了所有的變量都存儲在主內存中,每條線程還有自己的工作內存,線程的工作內存中保存了該線程中是用到的變量的主內存副本拷貝,線程對變量的所有操作都必須在工作內存中進行,而不能直接讀寫主內存。不同的線程之間也無法直接訪問對方工作內存中的變量,線程間變量的傳遞均需要自己的工作內存和主存之間進行數(shù)據(jù)同步進行。
如:線程1 要與 線程2 進行數(shù)據(jù)交換,首先,線程1要把自己工作內存1中的數(shù)據(jù)更新保存到主內存中,然后線程2要去主內存中拷貝一份數(shù)據(jù),刷新到自己的工作線程2之中。
3.java內存模型是圍繞著并發(fā)編程中的原子性,可見性,有序性這三個特征來建立的。
原子性:一個操作要么不執(zhí)行,要么全部執(zhí)行完畢,不能被打斷。
可見性:一個線程對共享變量做了修改之后,其他的線程立即能夠看到該變量的變化。
有序性:在本線程內觀察,所有的操作都是無序的;如果在一個線程中觀察另一個線程,所有的操作都是無序的。前半句是指“線程內表現(xiàn)為串行語義”,后半句是指“指令重排”現(xiàn)象以及“工作內存與主內存同步延遲”現(xiàn)象。
4.總結:JMM是一種規(guī)范,目的是解決由于多線程通過共享內存進行通信時,存在的本地內存數(shù)據(jù)不一致、編譯器會對代碼指令重排序、處理器會對代碼亂序執(zhí)行等帶來的問題。目的是保證并發(fā)編程場景中的原子性、可見性和有序性。
注:主內存和工作內存與JVM內存結構中的Java堆、棧、方法區(qū)等并不是同一個層次的內存劃分,無法直接類比。