1. java的堆內存是什麼
Java堆(Java Heap)是java虛擬機所管理的內存中最大的一塊
java堆被所有線程共享的一塊內存區域
虛擬機啟動時創建java堆
java堆的唯一目的就是存放對象實例。
java堆是垃圾收集器管理的主要區域。
從內存回收的角度來看, 由於現在收集器基本都採用分代收集演算法, 所以Java堆可以細分為:新生代(Young)和老年代(Old)。 新生代又被劃分為三個區域Eden、From Survivor, To Survivor等。無論怎麼劃分,最終存儲的都是實例對象, 進一步劃分的目的是為了更好的回收內存, 或者更快的分配內存。
java堆的大小是可擴展的, 通過-Xmx和-Xms控制。
如果堆內存不夠分配實例對象, 並且對也無法在擴展時, 將會拋出outOfMemoryError異常。
2. 內存條abcd分布
java內存區域主要分程序計數器、Java虛擬機棧、本地方法棧、Java堆、方法區、直接內存。其中程序計數器、Java虛擬機棧、本地方法棧屬於線程隔離,即他們都有自己的線程歸屬,其他屬於線程共享的。
1、程序計數器。這個是當前線程正在執行的位元組碼行號指示器。根據這裡面的內存數據來確定程序接下來執行的指令。每個線程都有一個,相互隔離,線程切換回來時才知道怎麼執行。如果執行的是方法,這里記錄的是虛擬機位元組碼指令的地址。當執行的是Native方法的時候為空(Undefined)。
2、Java虛擬機棧。每個線程私有,裡面裝的多個棧幀,每個棧幀對於的一個方法。裡面存儲的是Java方法的內存模型。相當於描述的是一個方法需要的內容。
3、本地方法棧。線程私有,和上一個Java虛擬機棧作用相似,Java虛擬機棧是為Java方法服務,本地方法棧是為Native服務。
4、Java虛擬機管理最大的一塊,線程共享,存放對象實例和數組。分新生代(1/3)和老年代(2/3),新生代還可以分Eden(8/10)、FromSurvivor(1/10)、ToSurvivor(1/10),是主要根據垃圾清理來分的。
5、方法區。線程共享,主要存儲被虛擬機載入的類信息、常量、靜態變數、即時編譯器編譯後的代碼。運行時常量池也是方法區的一部分,比如String有一個常量池,他就是放到這個裡面的。
6、直接內存。NIO通過使用Native函數庫直接分配對外內存。
3. 北大青鳥java培訓:java編程內存管理需要注意的問題
大家在進行程序系統維護的時候是否因為java編程的內存管理問題而無法快速解決導致系統出錯呢?下面我們就一起來了解和學習一下,關於java編程內存管理都有哪些知識點。
程序計數器(了解)程序計數器,可以看做是當前線程所執行的位元組碼的行號指示器。
在虛擬機的概念模型里,位元組碼解釋器工作就是通過改變程序計數器的值來選擇下一條需要執行的位元組碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都要依賴這個計數器來完成。
Java虛擬機棧(了解)Java虛擬機棧也是線程私有的,它的生命周期與線程相同。
虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀用於存儲局部變數表、操作數棧、動態鏈表、方法出口信息等。
每一個方法從調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程。
局部變數表中存放了編譯器可知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用和returnAddress類型(指向了一條位元組碼指令的地址)。
如果擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError異常。
本地方法棧(了解)本地方法棧與虛擬機的作用相似,不同之處在於虛擬機棧為虛擬機執行的Java方法服務,而本地方法棧則為虛擬機使用到的Native方法服務。
有的虛擬機直接把本地方法棧和虛擬機棧合二為一。
會拋出stackOverflowError和OutOfMemoryError異常。
Java堆堆內存用來存放由new創建的對象實例和數組。
(重點)Java堆是所有線程共享的一塊內存區域,在虛擬機啟動時創建,此內存區域的目的就是存放對象實例。
Java堆是垃圾收集器管理的主要區域。
java課程培訓機構http://www.kmbdqn.cn/發現由於現在收集器基本採用分代回收演算法,所以Java堆還可細分為:新生代和老年代。
從內存分配的角度來看,線程共享的Java堆中可能劃分出多個線程私有的分配緩沖區(TLAB)。
4. 主內存與java內存區域(堆,方法區)有什麼區別
這兩天看了一下深入淺出JVM這本書,推薦給高級的java程序員去看,對你了解JAVA的底層和運行機制有
比較大的幫助。
廢話不想講了.入主題:
先了解具體的概念:
JAVA的JVM的內存可分為3個區:堆(heap)、棧(stack)和方法區(method)
堆區:
1.存儲的全部是對象,每個對象都包含一個與之對應的class的信息。(class的目的是得到操作指令)
2.jvm只有一個堆區(heap)被所有線程共享,堆中不存放基本類型和對象引用,只存放對象本身
棧區:
1.每個線程包含一個棧區,棧中只保存基礎數據類型的對象和自定義對象的引用(不是對象),對象都存放在堆區中
2.每個棧中的數據(原始類型和對象引用)都是私有的,其他棧不能訪問。
3.棧分為3個部分:基本類型變數區、執行環境上下文、操作指令區(存放操作指令)。
方法區:
1.又叫靜態區,跟堆一樣,被所有的線程共享。方法區包含所有的class和static變數。
2.方法區中包含的都是在整個程序中永遠唯一的元素,如class,static變數。
為了更清楚地搞明白發生在運行時數據區里的黑幕,我們來准備2個小道具(2個非常簡單的小程序)。
AppMain.java
public class AppMain
//運行時, jvm 把appmain的信息都放入方法區
{
public static void main(String[] args) //main 方法本身放入方法區。
{
Sample test1 = new Sample( " 測試1 " ); //test1是引用,所以放到棧區里, Sample是自定義對象應該放到堆裡面
Sample test2 = new Sample( " 測試2 " );
test1.printName();
test2.printName();
}
}
Sample.java
public class Sample //運行時, jvm 把appmain的信息都放入方法區
{
/** 範例名稱 */
private name; //new Sample實例後, name 引用放入棧區里, name 對象放入堆里
/** 構造方法 */
public Sample(String name)
{
this .name = name;
}
/** 輸出 */
public void printName() //print方法本身放入 方法區里。
{
System.out.println(name);
}
}
OK,讓我們開始行動吧,出發指令就是:「java AppMain」,包包里帶好我們的行動向導圖,Let』s GO!
系統收到了我們發出的指令,啟動了一個Java虛擬機進程,這個進程首先從classpath中找到AppMain.class文件,讀取這個文件中的二進制數據,然後把Appmain類的類信息存放到運行時數據區的方法區中。這一過程稱為AppMain類的載入過程。
接著,Java虛擬機定位到方法區中AppMain類的Main()方法的位元組碼,開始執行它的指令。這個main()方法的第一條語句就是:
Sample test1=new Sample("測試1");
語句很簡單啦,就是讓java虛擬機創建一個Sample實例,並且呢,使引用變數test1引用這個實例。貌似小case一樁哦,就讓我們來跟蹤一下Java虛擬機,看看它究竟是怎麼來執行這個任務的:
1、 Java虛擬機一看,不就是建立一個Sample實例嗎,簡單,於是就直奔方法區而去,先找到Sample類的類型信息再說。結果呢,嘿嘿,沒找到@@,這會兒的方法區里還沒有Sample類呢。可Java虛擬機也不是一根筋的笨蛋,於是,它發揚「自己動手,豐衣足食」的作風,立馬載入了Sample類,把Sample類的類型信息存放在方法區里。
2、 好啦,資料找到了,下面就開始幹活啦。Java虛擬機做的第一件事情就是在堆區中為一個新的Sample實例分配內存, 這個Sample實例持有著指向方法區的Sample類的類型信息的引用。這里所說的引用,實際上指的是Sample類的類型信息在方法區中的內存地址,其實,就是有點類似於C語言里的指針啦~~,而這個地址呢,就存放了在Sample實例的數據區里。
3、 在JAVA虛擬機進程中,每個線程都會擁有一個方法調用棧,用來跟蹤線程運行中一系列的方法調用過程,棧中的每一個元素就被稱為棧幀,每當線程調用一個方法的時候就會向方法棧壓入一個新幀。這里的幀用來存儲方法的參數、局部變數和運算過程中的臨時數據。OK,原理講完了,就讓我們來繼續我們的跟蹤行動!位於「=」前的Test1是一個在main()方法中定義的變數,可見,它是一個局部變數,因此,它被會添加到了執行main()方法的主線程的JAVA方法調用棧中。而「=」將把這個test1變數指向堆區中的Sample實例,也就是說,它持有指向Sample實例的引用。
OK,到這里為止呢,JAVA虛擬機就完成了這個簡單語句的執行任務。參考我們的行動向導圖,我們終於初步摸清了JAVA虛擬機的一點點底細了,COOL!
接下來,JAVA虛擬機將繼續執行後續指令,在堆區里繼續創建另一個Sample實例,然後依次執行它們的printName()方法。當JAVA虛擬機執行test1.printName()方法時,JAVA虛擬機根據局部變數test1持有的引用,定位到堆區中的Sample實例,再根據Sample實例持有的引用,定位到方法去中Sample類的類型信息,從而獲得printName()方法的位元組碼,接著執行printName()方法包含的指令。
5. java內存溢出是什麼情況
首先先說一下JVM內存結構問題,JVM為兩塊:PermanentSapce和HeapSpace,其中x0dx0aHeap = }。PermantSpace負責保存反射對象,一般不用配置。JVM的Heap區可以通過-X參數來設定。x0dx0a 當一個URL被訪問時,內存申請過程如下:x0dx0aA. JVM會試圖為相關Java對象在Eden中初始化一塊內存區域x0dx0aB. 當Eden空間足夠時,內存申請結束。否則到下一步x0dx0aC. JVM試圖釋放在Eden中所有不活躍的對象(這屬於1或更高級的垃圾回收), 釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區x0dx0aD. Survivor區被用來作為Eden及OLD的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,否則會被保留在Survivor區x0dx0aE. 當OLD區空間不夠時,JVM會在OLD區進行完全的垃圾收集(0級)x0dx0aF. 完全垃圾收集後,若Survivor及OLD區仍然無法存放從Eden復制過來的部分對象,導致JVM無法在Eden區為新對象創建內存區域,則出現」out of memory錯誤」x0dx0ax0dx0aJVM調優建議:x0dx0ax0dx0ams/mx:定義YOUNG+OLD段的總尺寸,ms為JVM啟動時YOUNG+OLD的內存大小;mx為最大可佔用的YOUNG+OLD內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。x0dx0aNewSize/MaxNewSize:定義YOUNG段的尺寸,NewSize為JVM啟動時YOUNG的內存大小;MaxNewSize為最大可佔用的YOUNG內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。x0dx0aPermSize/MaxPermSize:定義Perm段的尺寸,PermSize為JVM啟動時Perm的內存大小;MaxPermSize為最大可佔用的Perm內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。x0dx0aSurvivorRatio:設置Survivor空間和Eden空間的比例x0dx0ax0dx0a內存溢出的可能性x0dx0ax0dx0a1. OLD段溢出x0dx0a這種內存溢出是最常見的情況之一,產生的原因可能是:x0dx0a1) 設置的內存參數過小(ms/mx, NewSize/MaxNewSize)x0dx0a2) 程序問題x0dx0a單個程序持續進行消耗內存的處理,如循環幾千次的字元串處理,對字元串處理應建議使用StringBuffer。此時不會報內存溢出錯,卻會使系統持續垃圾收集,無法處理其它請求,相關問題程序可通過Thread Dump獲取(見系統問題診斷一章)單個程序所申請內存過大,有的程序會申請幾十乃至幾百兆內存,此時JVM也會因無法申請到資源而出現內存溢出,對此首先要找到相關功能,然後交予程序員修改,要找到相關程序,必須在Apache日誌中尋找。x0dx0a當Java對象使用完畢後,其所引用的對象卻沒有銷毀,使得JVM認為他還是活躍的對象而不進行回收,這樣累計佔用了大量內存而無法釋放。由於目前市面上還沒有對系統影響小的內存分析工具,故此時只能和程序員一起定位。x0dx0ax0dx0a2. Perm段溢出x0dx0a通常由於Perm段裝載了大量的Servlet類而導致溢出,目前的解決辦法:x0dx0a1) 將PermSize擴大,一般256M能夠滿足要求x0dx0a2) 若別無選擇,則只能將servlet的路徑加到CLASSPATH中,但一般不建議這么處理x0dx0ax0dx0a3. C Heap溢出x0dx0a系統對C Heap沒有限制,故C Heap發生問題時,Java進程所佔內存會持續增長,直到佔用所有可用系統內存x0dx0ax0dx0a參數說明:x0dx0ax0dx0aJVM 堆內存(heap)設置選項 x0dx0a 參數格式 x0dx0a 說 明 x0dx0a x0dx0a設置新對象生產堆內存(Setting the Newgeneration heap size) x0dx0a -XX:NewSize x0dx0a 通過這個選項可以設置Java新對象生產堆內存。在通常情況下這個選項的數值為1 024的整數倍並且大於1MB。這個值的取值規則為,一般情況下這個值-XX:NewSize是最大堆內存(maximum heap size)的四分之一。增加這個選項值的大小是為了增大較大數量的短生命周期對象 x0dx0ax0dx0a增加Java新對象生產堆內存相當於增加了處理器的數目。並且可以並行地分配內存,但是請注意內存的垃圾回收卻是不可以並行處理的 x0dx0a x0dx0a設置最大新對象生產堆內存(Setting the maximum New generation heap size) x0dx0a -XX:MaxNewSize x0dx0a 通過這個選項可以設置最大Java新對象生產堆內存。通常情況下這個選項的數值為1 024的整數倍並且大於1MB x0dx0ax0dx0a其功用與上面的設置新對象生產堆內存-XX:NewSize相同x0dx0ax0dx0a設置新對象生產堆內存的比例(Setting New heap size ratios) x0dx0a -XX:SurvivorRatio x0dx0a 新對象生產區域通常情況下被分為3個子區域:伊甸園,與兩個殘存對象空間,這兩個空間的大小是相同的。通過用-XX:SurvivorRatio=X選項配置伊甸園與殘存對象空間(Eden/survivor)的大小的比例。你可以試著將這個值設置為8,然後監控、觀察垃圾回收的工作情況x0dx0ax0dx0a設置堆內存池的最大值(Setting maximum heap size) x0dx0a -Xmx x0dx0a 通過這個選項可以要求系統為堆內存池分配內存空間的最大值。通常情況下這個選項的數值為1 024的整數倍並且大於1 MB x0dx0ax0dx0a一般情況下這個值(-Xmx)與最小堆內存(minimum heap size _Xms)相同,以降低垃圾回收的頻度 x0dx0a x0dx0a取消垃圾回收 x0dx0a -Xnoclassgc x0dx0a 這個選項用來取消系統對特定類的垃圾回收。它可以防止當這個類的所有引用丟失之後,這個類仍被引用時不會再一次被重新裝載,因此這個選項將增大系統堆內存的空間 x0dx0a x0dx0a設置棧內存的大小 x0dx0a -Xss x0dx0a 這個選項用來控制本地線程棧的大小,當這個選項被設置的較大(>2MB)時將會在很大程度上降低系統的性能。因此在設置這個值時應該格外小心,調整後要注意觀察系統的性能,不斷調整以期達到最優 x0dx0a x0dx0a最後說一句,你的機器的連接數設置也至關重要,連接的關閉最好把時間設置的少些,那些連接非常耗費資源。也是引起內存泄露的主要原因。
6. Java中內存分為幾塊
你說的是jvm的內存空間吧。
在方法(代碼塊)中定義一個變數時,java就在棧中為這個變數分配JVM內存空間,當超過變數的作用域後,java會自動釋放掉為該變數所分配的JVM內存空間;而在堆中分配的JVM內存由java虛擬機的自動垃圾回收器來管理。
JVM內存區域組成
JVM內存分四種:
1、棧區(stacksegment)—由編譯器自動分配釋放,存放函數的參數值,局部變數的值等,具體方法執行結束之後,系統自動釋放JVM內存資源
2、堆區(heapsegment)—一般由程序員分配釋放,存放由new創建的對象和數組,jvm不定時查看這個對象,如果沒有引用指向這個對象就回收
3、靜態區(datasegment)—存放全局變數,靜態變數和字元串常量,不釋放
4、代碼區(codesegment)—存放程序中方法的二進制代碼,而且是多個對象共享一個代碼空間區域
在方法(代碼塊)中定義一個變數時,java就在棧中為這個變數分配JVM內存空間,當超過變數的作用域後,java會自動釋放掉為該變數所分配的JVM內存空間;在堆中分配的JVM內存由java虛擬機的自動垃圾回收器來管理,堆的優勢是可以動態分配JVM內存大小,生存期也不必事先告訴編譯器,因為它是在運行時動態分配JVM內存的。缺點就是要在運行時動態分配JVM內存,存取速度較慢;棧的優勢是存取速度比堆要快,缺點是存在棧中的數據大小與生存期必須是確定的無靈活性。
◆java堆由Perm區和Heap區組成,Heap區則由Old區和New區組成,而New區又分為Eden區,From區,To區,Heap={Old+NEW={Eden,From,To}},見圖1所示。
Heap區分兩大塊,一塊是NEWGeneration,另一塊是OldGeneration.在NewGeneration中,有一個叫Eden的空間,主要是用來存放新生的對象,還有兩個SurvivorSpaces(from,to),它們用來存放每次垃圾回收後存活下來的對象。在OldGeneration中,主要存放應用程序中生命周期長的JVM內存對象,還有個PermanentGeneration,主要用來放JVM自己的反射對象,比如類對象和方法對象等。
在NewGeneration塊中,垃圾回收一般用Copying的演算法,速度快。每次GC的時候,存活下來的對象首先由Eden拷貝到某個SurvivorSpace,當SurvivorSpace空間滿了後,剩下的live對象就被直接拷貝到OldGeneration中去。因此,每次GC後,EdenJVM內存塊會被清空。在OldGeneration塊中,垃圾回收一般用mark-compact的演算法,速度慢些,但減少JVM內存要求.
垃圾回收分多級,0級為全部(Full)的垃圾回收,會回收OLD段中的垃圾;1級或以上為部分垃圾回收,只會回收NEW中的垃圾,JVM內存溢出通常發生於OLD段或Perm段垃圾回收後,仍然無JVM內存空間容納新的Java對象的情況。
JVM調用GC的頻度還是很高的,主要兩種情況下進行垃圾回收:當應用程序線程空閑;另一個是JVM內存堆不足時,會不斷調用GC,若連續回收都解決不了JVM內存堆不足的問題時,就會報outofmemory錯誤。因為這個異常根據系統運行環境決定,所以無法預期它何時出現。
根據GC的機制,程序的運行會引起系統運行環境的變化,增加GC的觸發機會。為了避免這些問題,程序的設計和編寫就應避免垃圾對象的JVM內存佔用和GC的開銷。顯示調用System.GC()只能建議JVM需要在JVM內存中對垃圾對象進行回收,但不是必須馬上回收,一個是並不能解決JVM內存資源耗空的局面,另外也會增加GC的消耗。
◆當一個URL被訪問時,JVM內存區域申請過程如下:
A.JVM會試圖為相關Java對象在Eden中初始化一塊JVM內存區域
B.當Eden空間足夠時,JVM內存申請結束。否則到下一步
C.JVM試圖釋放在Eden中所有不活躍的對象(這屬於1或更高級的垃圾回收),釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區
D.Survivor區被用來作為Eden及OLD的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,否則會被保留在Survivor區
E.當OLD區空間不夠時,JVM會在OLD區進行完全的垃圾收集(0級)
F.完全垃圾收集後,若Survivor及OLD區仍然無法存放從Eden復制過來的部分對象,導致JVM無法在Eden區為新對象創建JVM內存區域,則出現"outofmemory錯誤"
7. Java內存劃分到底是4個部分還是5個部分
Java把內存劃分成兩種:一種是棧內存,一種是堆內存。在函數中定義的一些基本類型的變數和對象的引用變數都在函數的棧內存中分配。當在一段代碼塊定義一個變數時,Java就在棧中為這個變數分配內存空間,當超過變數的作用域後,Java會自動釋放掉為該變數所分配的內存空間,該內存空間可以立即被另作他用。堆內存用來存放由new創建的對象和數組。在堆中分配的內存,由Java虛擬機的自動垃圾回收器來管理。在堆中產生了一個數組或對象後,還可以在棧中定義一個特殊的變數,讓棧中這個變數的取值等於數組或對象在堆內存中的首地址,棧中的這個變數就成了數組或對象的引用變數。引用變數就相當於是為數組或對象起的一個名稱,以後就可以在程序中使用棧中的引用變數來訪問堆中的數組或對象。具體的說:棧與堆都是Java用來在Ram中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。Java的堆是一個運行時數據區,類的(對象從中分配空間。這些對象通過new、newarray、anewarray和multianewarray等指令建立,它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因為它是在運行時動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。棧的優勢是,存取速度比堆要快,僅次於寄存器,棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類型的變數(,int,short,long,byte,float,double,boolean,char)和對象句柄。棧有一個很重要的特殊性,就是存在棧中的數據可以共享。假設我們同時定義:inta=3;intb=3;編譯器先處理inta=3;首先它會在棧中創建一個變數為a的引用,然後查找棧中是否有3這個值,如果沒找到,就將3存放進來,然後將a指向3。接著處理intb=3;在創建完b的引用變數後,因為在棧中已經有3這個值,便將b直接指向3。這樣,就出現了a與b同時均指向3的情況。這時,如果再令a=4;那麼編譯器會重新搜索棧中是否有4值,如果沒有,則將4存放進來,並令a指向4;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。要注意這種數據的共享與兩個對象的引用同時指向一個對象的這種共享是不同的,因為這種情況a的修改並不會影響到b,它是由編譯器完成的,它有利於節省空間。而一個對象引用變數修改了這個對象的內部狀態,會影響到另一個對象引用變數。