導航:首頁 > 編程語言 > java對象內存分配

java對象內存分配

發布時間:2022-11-17 07:38:32

java中實例化一個對象,成員方法中的參數會在哪分配內存(棧/堆)

首先內存總體分為了4個部分,包括 stack segment 、heap segment、code segment 、data segment ;
其中我們程序中用關鍵字new出來的東西都是存放在heap segment;
程序中的局部變數存放在stack segment,這些局部變數是在具體方法執行結束之後,系統自動釋放內存資源(而heap segment中的資源需要java垃圾回收機制來處理);
程序中的方法,是內存中的code segment中的,而且是多個對象 共享一個代碼空間區域;
static靜態變數,需要放在內存中的data segment中,

❷ 一個Java對象到底佔用多大內存

一個java對象包含
對象頭 和jvm相關 大小8位元組 包括兩個內容 mark klass 主要是標記gc狀態 ,鎖狀態,類信息等

對象的數據區域 實際的成員數據大小
對齊填充區域
而且對象要求在8位元組邊界上分配,所以佔用空間必須是8的倍數
因此會有對齊填充區域
而且和使用的jvm有關系 ,比如32位的Object用8個位元組 ,64位Object佔用16個位元組

❸ new一個java對象的時候,內存是怎麼分配的

以 Object obj = new Object();為例
obj 保存在虛擬機棧內存,是個地址. new Object();的內容保存在堆內存, new關鍵字代表的就是在堆內存開辟空間

❹ java 對象的屬性在內存里以什麼形式存在

java對象是引用類型,引用類型的對象的內存分配在堆中。

如果分配在堆中的java對象包含屬性,這可以分兩種情況分析:
1.屬性是基本類型(byte,char,int等)的
則對象的堆內存保存的就是基本類型的值本身。
2.屬性是引用類型的(String是引用類型的)
則對象的堆內存中保存的只是這個屬性的引用,屬性所指向的對象分配在其它堆內存中。

所以,你的理解1是對的,它就是屬性是引用類型時的情況。但理解得不完整,還應包括屬性是基本類型時的情形。

❺ Java使用運算符( )創建類的對象,給對象分配內存空間

這主要是構造函數的事情

類初始化時構造函數調用順序:
(1)初始化對象的存儲空間為零或null值;
(2)調用父類構造函數;
(3)按順序分別調用類成員變數和實例成員變數的初始化表達式,並在內存中分配相應的空間
(4)調用本身構造函數。

❻ 哪位能描述一下 java 中內存的分區情況和各類變數在內存中的存貯情況。

Java內存分配與管理是Java的核心技術之一,一般Java在內存分配時會涉及到以下區域:

◆寄存器:我們在程序中無法控制

◆棧:存放基本類型的數據和對象的引用,但對象本身不存放在棧中,而是存放在堆中

◆堆:存放用new產生的數據

◆靜態域:存放在對象中用static定義的靜態成員

◆常量池:存放常量

◆非RAM存儲:硬碟等永久存儲空間

Java內存分配中的棧

在函數中定義的一些基本類型的變數數據和對象的引用變數都在函數的棧內存中分配。

當在一段代碼塊定義一個變數時,Java就在棧中為這個變數分配內存空間,當該變數退出該作用域後,Java會自動釋放掉為該變數所分配的內存空間,該內存空間可以立即被另作他用。

Java內存分配中的堆

堆內存用來存放由new創建的對象和數組。在堆中分配的內存,由Java虛擬機的自動垃圾回收器來管理。

在堆中產生了一個數組或對象後,還可以在棧中定義一個特殊的變數,讓棧中這個變數的取值等於數組或對象在堆內存中的首地址,棧中的這個變數就成了數組或對象的引用變數。引用變數就相當於是為數組或對象起的一個名稱,以後就可以在程序中使用棧中的引用變數來訪問堆中的數組或對象。引用變數就相當於是為數組或者對象起的一個名稱。

引用變數是普通的變數,定義時在棧中分配,引用變數在程序運行到其作用域之外後被釋放。而數組和對象本身在堆中分配,即使程序運行到使用new產生數組或者對象的語句所在的代碼塊之外,數組和對象本身占據的內存不會被釋放,數組和對象在沒有引用變數指向它的時候,才變為垃圾,不能在被使用,但仍然占據內存空間不放,在隨後的一個不確定的時間被垃圾回收器收走(釋放掉)。這也是Java比較占內存的原因。

實際上,棧中的變數指向堆內存中的變數,這就是Java中的指針!

常量池(constantpool)

常量池指的是在編譯期被確定,並被保存在已編譯的.class文件中的一些數據。除了包含代碼中所定義的各種基本類型(如int、long等等)和對象型(如String及數組)的常量值(final)還包含一些以文本形式出現的符號引用,比如:

◆類和介面的全限定名;

◆欄位的名稱和描述符;

◆方法和名稱和描述符。

虛擬機必須為每個被裝載的類型維護一個常量池。常量池就是該類型所用到常量的一個有序集和,包括直接常量(string,integer和floatingpoint常量)和對其他類型,欄位和方法的符號引用。

對於String常量,它的值是在常量池中的。而JVM中的常量池在內存當中是以表的形式存在的,對於String類型,有一張固定長度的CONSTANT_String_info表用來存儲文字字元串值,注意:該表只存儲文字字元串值,不存儲符號引用。說到這里,對常量池中的字元串值的存儲位置應該有一個比較明了的理解了。

在程序執行的時候,常量池會儲存在MethodArea,而不是堆中。

堆與棧

Java的堆是一個運行時數據區,類的(對象從中分配空間。這些對象通過new、newarray、anewarray和multianewarray等指令建立,它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因為它是在運行時動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。

棧的優勢是,存取速度比堆要快,僅次於寄存器,棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類型的變數數據(int,short,long,byte,float,double,boolean,char)和對象句柄(引用)。

棧有一個很重要的特殊性,就是存在棧中的數據可以共享。假設我們同時定義:

1. inta=3;

2. 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,它是由編譯器完成的,它有利於節省空間。而一個對象引用變數修改了這個對象的內部狀態,會影響到另一個對象引用變數。

String是一個特殊的包裝類數據。可以用:

Stringstr=newString("abc");

Stringstr="abc";

兩種的形式來創建,第一種是用new()來新建對象的,它會在存放於堆中。每調用一次就會創建一個新的對象。而第二種是先在棧中創建一個對String類的對象引用變數str,然後通過符號引用去字元串常量池裡找有沒有"abc",如果沒有,則將"abc"存放進字元串常量池,並令str指向」abc」,如果已經有」abc」則直接令str指向「abc」。

比較類裡面的數值是否相等時,用equals()方法;當測試兩個包裝類的引用是否指向同一個對象時,用==,下面用例子說明上面的理論。

1.Stringstr1="abc";

2.Stringstr2="abc";

3.System.out.println(str1==str2);//true

可以看出str1和str2是指向同一個對象的。

1.Stringstr1=newString("abc");

2.Stringstr2=newString("abc");

3.System.out.println(str1==str2);//false

用new的方式是生成不同的對象。每一次生成一個。

因此用第二種方式創建多個」abc」字元串,在內存中其實只存在一個對象而已.這種寫法有利與節省內存空間.同時它可以在一定程度上提高程序的運行速度,因為JVM會自動根據棧中數據的實際情況來決定是否有必要創建新對象。而對於Stringstr=newString("abc");的代碼,則一概在堆中創建新對象,而不管其字元串值是否相等,是否有必要創建新對象,從而加重了程序的負擔。

另一方面,要注意:我們在使用諸如Stringstr="abc";的格式定義類時,總是想當然地認為,創建了String類的對象str。擔心陷阱!對象可能並沒有被創建!而可能只是指向一個先前已經創建的對象。只有通過new()方法才能保證每次都創建一個新的對象。

由於String類的immutable性質,當String變數需要經常變換其值時,應該考慮使用StringBuffer類,以提高程序效率。

1.首先String不屬於8種基本數據類型,String是一個對象。因為對象的默認值是null,所以String的默認值也是null;但它又是一種特殊的對象,有其它對象沒有的一些特性。

2.newString()和newString(」")都是申明一個新的空字元串,是空串不是null;

3.Stringstr=」kvill」;Stringstr=newString(」kvill」)的區別

示例:

1.Strings0="kvill";

2.Strings1="kvill";

3.Strings2="kv"+"ill";

4.System.out.println(s0==s1);

5.System.out.println(s0==s2);

結果為:

true

true

首先,我們要知結果為道Java會確保一個字元串常量只有一個拷貝。

因為例子中的s0和s1中的」kvill」都是字元串常量,它們在編譯期就被確定了,所以s0==s1為true;而」kv」和」ill」也都是字元串常量,當一個字元串由多個字元串常量連接而成時,它自己肯定也是字元串常量,所以s2也同樣在編譯期就被解析為一個字元串常量,所以s2也是常量池中」kvill」的一個引用。所以我們得出s0==s1==s2;用newString()創建的字元串不是常量,不能在編譯期就確定,所以newString()創建的字元串不放入常量池中,它們有自己的地址空間。

示例:

6.Strings0="kvill";

7.Strings1=newString("kvill");

8.Strings2="kv"+newString("ill");

9.System.out.println(s0==s1);

10.System.out.println(s0==s2);

11.System.out.println(s1==s2);

結果為:

false

false

false

例2中s0還是常量池中"kvill」的應用,s1因為無法在編譯期確定,所以是運行時創建的新對象」kvill」的引用,s2因為有後半部分newString(」ill」)所以也無法在編譯期確定,所以也是一個新創建對象」kvill」的應用;明白了這些也就知道為何得出此結果了。

4.String.intern():

再補充介紹一點:存在於.class文件中的常量池,在運行期被JVM裝載,並且可以擴充。String的intern()方法就是擴充常量池的一個方法;當一個String實例str調用intern()方法時,Java查找常量池中是否有相同Unicode的字元串常量,如果有,則返回其的引用,如果沒有,則在常量池中增加一個Unicode等於str的字元串並返回它的引用;看示例就清楚了

示例:

1.Strings0="kvill";

2.Strings1=newString("kvill");

3.Strings2=newString("kvill");

4.System.out.println(s0==s1);

5.System.out.println("**********");

6.s1.intern();

7.s2=s2.intern();//把常量池中"kvill"的引用賦給s2

8.System.out.println(s0==s1);

9.System.out.println(s0==s1.intern());

10.System.out.println(s0==s2);

結果為:

false

false//雖然執行了s1.intern(),但它的返回值沒有賦給s1

true//說明s1.intern()返回的是常量池中"kvill"的引用

true

最後我再破除一個錯誤的理解:有人說,「使用String.intern()方法則可以將一個String類的保存到一個全局String表中,如果具有相同值的Unicode字元串已經在這個表中,那麼該方法返回表中已有字元串的地址,如果在表中沒有相同值的字元串,則將自己的地址注冊到表中」如果我把他說的這個全局的String表理解為常量池的話,他的最後一句話,」如果在表中沒有相同值的字元串,則將自己的地址注冊到表中」是錯的:

示例:

1.Strings1=newString("kvill");

2.Strings2=s1.intern();

3.System.out.println(s1==s1.intern());

4.System.out.println(s1+""+s2);

5.System.out.println(s2==s1.intern());

結果:

1.false

2.kvillkvill

3.true

在這個類中我們沒有聲名一個」kvill」常量,所以常量池中一開始是沒有」kvill」的,當我們調用s1.intern()後就在常量池中新添加了一個」kvill」常量,原來的不在常量池中的」kvill」仍然存在,也就不是「將自己的地址注冊到常量池中」了。

s1==s1.intern()為false說明原來的」kvill」仍然存在;s2現在為常量池中」kvill」的地址,所以有s2==s1.intern()為true。

5.關於equals()和==:

這個對於String簡單來說就是比較兩字元串的Unicode序列是否相當,如果相等返回true;而==是比較兩字元串的地址是否相同,也就是是否是同一個字元串的引用。

6.關於String是不可變的

這一說又要說很多,大家只要知道String的實例一旦生成就不會再改變了,比如說:Stringstr=」kv」+」ill」+」「+」ans」;就是有4個字元串常量,首先」kv」和」ill」生成了」kvill」存在內存中,然後」kvill」又和」」生成「kvill「存在內存中,最後又和生成了」kvillans」;並把這個字元串的地址賦給了str,就是因為String的」不可變」產生了很多臨時變數,這也就是為什麼建議用StringBuffer的原因了,因為StringBuffer是可改變的。

下面是一些String相關的常見問題:

String中的final用法和理解

finalStringBuffera=newStringBuffer("111");

finalStringBufferb=newStringBuffer("222");

a=b;//此句編譯不通過

finalStringBuffera=newStringBuffer("111");

a.append("222");//編譯通過

可見,final只對引用的"值"(即內存地址)有效,它迫使引用只能指向初始指向的那個對象,改變它的指向會導致編譯期錯誤。至於它所指向的對象的變化,final是不負責的。

String常量池問題的幾個例子

下面是幾個常見例子的比較分析和理解:

Stringa="a1";

Stringb="a"+1;

System.out.println((a==b));//result=true

Stringa="atrue";

Stringb="a"+"true";

System.out.println((a==b));//result=true

Stringa="a3.4";

Stringb="a"+3.4;

System.out.println((a==b));//result=true

分析:JVM對於字元串常量的"+"號連接,將程序編譯期,JVM就將常量字元串的"+"連接優化為連接後的值,拿"a"+1來說,經編譯器優化後在class中就已經是a1。在編譯期其字元串常量的值就確定下來,故上面程序最終的結果都為true。

Stringa="ab";

Stringbb="b";

Stringb="a"+bb;

System.out.println((a==b));//result=false

分析:JVM對於字元串引用,由於在字元串的"+"連接中,有字元串引用存在,而引用的值在程序編譯期是無法確定的,即"a"+bb無法被編譯器優化,只有在程序運行期來動態分配並將連接後的新地址賦給b。所以上面程序的結果也就為false。

Stringa="ab";

finalStringbb="b";

Stringb="a"+bb;

System.out.println((a==b));//result=true

分析:和[3]中唯一不同的是bb字元串加了final修飾,對於final修飾的變數,它在編譯時被解析為常量值的一個本地拷貝存儲到自己的常量池中或嵌入到它的位元組碼流中。所以此時的"a"+bb和"a"+"b"效果是一樣的。故上面程序的結果為true。

Stringa="ab";

finalStringbb=getBB();

Stringb="a"+bb;

System.out.println((a==b));//result=false

privatestaticStringgetBB(){

return"b";

}

分析:JVM對於字元串引用bb,它的值在編譯期無法確定,只有在程序運行期調用方法後,將方法的返回值和"a"來動態連接並分配地址為b,故上面程序的結果為false。

通過上面4個例子可以得出得知:

Strings="a"+"b"+"c";

就等價於Strings="abc";

Stringa="a";

Stringb="b";

Stringc="c";

Strings=a+b+c;

這個就不一樣了,最終結果等於:

1.StringBuffertemp=newStringBuffer();

2.temp.append(a).append(b).append(c);

3.Strings=temp.toString();

由上面的分析結果,可就不難推斷出String採用連接運算符(+)效率低下原因分析,形如這樣的代碼:

publicclassTest{

publicstaticvoidmain(Stringargs[]){

Strings=null;

for(inti=0;i<100;i++){

s+="a";

}

}

}

每做一次+就產生個StringBuilder對象,然後append後就扔掉。下次循環再到達時重新產生個StringBuilder對象,然後append字元串,如此循環直至結束。如果我們直接採用StringBuilder對象進行append的話,我們可以節省N-1次創建和銷毀對象的時間。所以對於在循環中要進行字元串連接的應用,一般都是用StringBuffer或StringBulider對象來進行append操作。

String對象的intern方法理解和分析:

1.publicclassTest4{

2.privatestaticStringa="ab";

3.publicstaticvoidmain(String[]args){

4.Strings1="a";

5.Strings2="b";

6.Strings=s1+s2;

7.System.out.println(s==a);//false

8.System.out.println(s.intern()==a);//true

9.}

10.}

這里用到Java裡面是一個常量池的問題。對於s1+s2操作,其實是在堆裡面重新創建了一個新的對象,s保存的是這個新對象在堆空間的的內容,所以s與a的值是不相等的。而當調用s.intern()方法,卻可以返回s在常量池中的地址值,因為a的值存儲在常量池中,故s.intern和a的值相等。

總結

棧中用來存放一些原始數據類型的局部變數數據和對象的引用(String,數組.對象等等)但不存放對象內容

堆中存放使用new關鍵字創建的對象.

字元串是一個特殊包裝類,其引用是存放在棧里的,而對象內容必須根據創建方式不同定(常量池和堆).有的是編譯期就已經創建好,存放在字元串常量池中,而有的是運行時才被創建.使用new關鍵字,存放在堆中。

❼ 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錯誤"

❽ JAVA虛擬機內存分配與回收機制

[轉帖] Java 中的堆和棧
簡單的說:
Java把內存劃分成兩種:一種是棧內存,一種是堆內存。
在函數中定義的一些基本類型的變數和對象的引用變數都在函數的棧內存中分配。
當在一段代碼塊定義一個變數時,Java就在棧中為這個變數分配內存空間,當超過變數的作用域後,Java會自動釋放掉為該變數所分配的內存空間,該內存空間可以立即被另作他用。
堆內存用來存放由new創建的對象和數組。
在堆中分配的內存,由Java虛擬機的自動垃圾回收器來管理。
在堆中產生了一個數組或對象後,還可以在棧中定義一個特殊的變數,讓棧中這個變數的取值等於數組或對象在堆內存中的首地址,棧中的這個變數就成了數組或對象的引用變數。
引用變數就相當於是為數組或對象起的一個名稱,以後就可以在程序中使用棧中的引用變數來訪問堆中的數組或對象。

具體的說:
棧與堆都是Java用來在Ram中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。
Java的堆是一個運行時數據區,類的(對象從中分配空間。這些對象通過new、newarray、anewarray和multianewarray等 指令建立,它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因為它是在運行時 動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。
棧的優勢是,存取速度比堆要快,僅次於寄存器,棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本 類型的變數(,int, short, long, byte, float, double, boolean, char)和對象句柄。
棧有一個很重要的特殊性,就是存在棧中的數據可以共享。假設我們同時定義:
int a = 3;
int b = 3;
編譯器先處理int a = 3;首先它會在棧中創建一個變數為a的引用,然後查找棧中是否有3這個值,如果沒找到,就將3存放進來,然後將a指向3。接著處理int b = 3;在創建完b的引用變數後,因為在棧中已經有3這個值,便將b直接指向3。這樣,就出現了a與b同時均指向3的情況。這時,如果再令a=4;那麼編譯器 會重新搜索棧中是否有4值,如果沒有,則將4存放進來,並令a指向4;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。要注意這 種數據的共享與兩個對象的引用同時指向一個對象的這種共享是不同的,因為這種情況a的修改並不會影響到b, 它是由編譯器完成的,它有利於節省空間。而一個對象引用變數修改了這個對象的內部狀態,會影響到另一個對象引用變數。

String是一個特殊的包裝類數據。可以用:
String str = new String("abc");
String str = "abc";
兩種的形式來創建,第一種是用new()來新建對象的,它會在存放於堆中。每調用一次就會創建一個新的對象。
而第二種是先在棧中創建一個對String類的對象引用變數str,然後查找棧中有沒有存放"abc",如果沒有,則將"abc"存放進棧,並令str指向」abc」,如果已經有」abc」 則直接令str指向「abc」。

比較類裡面的數值是否相等時,用equals()方法;當測試兩個包裝類的引用是否指向同一個對象時,用==,下面用例子說明上面的理論。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一個對象的。

String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的對象。每一次生成一個。
因此用第二種方式創建多個」abc」字元串,在內存中其實只存在一個對象而已. 這種寫法有利與節省內存空間. 同時它可以在一定程度上提高程序的運行速度,因為JVM會自動根據棧中數據的實際情況來決定是否有必要創建新對象。而對於String str = new String("abc");的代碼,則一概在堆中創建新對象,而不管其字元串值是否相等,是否有必要創建新對象,從而加重了程序的負擔。
另一方面, 要注意: 我們在使用諸如String str = "abc";的格式定義類時,總是想當然地認為,創建了String類的對象str。擔心陷阱!對象可能並沒有被創建!而可能只是指向一個先前已經創建的 對象。只有通過new()方法才能保證每次都創建一個新的對象。 由於String類的immutable性質,當String變數需要經常變換其值時,應該考慮使用StringBuffer類,以提高程序效率。

java中內存分配策略及堆和棧的比較
2.1 內存分配策略
按照編譯原理的觀點,程序運行時的內存分配有三種策略,分別是靜態的,棧式的,和堆式的.
靜態存儲分配是指在編譯時就能確定每個數據目標在運行時刻的存儲空間需求,因而在編譯時就可以給他們分配固定的內存空間.這種分配策略要求程序代碼中不允 許有可變數據結構(比如可變數組)的存在,也不允許有嵌套或者遞歸的結構出現,因為它們都會導致編譯程序無法計算準確的存儲空間需求.
棧式存儲分配也可稱為動態存儲分配,是由一個類似於堆棧的運行棧來實現的.和靜態存儲分配相反,在棧式存儲方案中,程序對數據區的需求在編譯時是完全未知 的,只有到運行的時候才能夠知道,但是規定在運行中進入一個程序模塊時,必須知道該程序模塊所需的數據區大小才能夠為其分配內存.和我們在數據結構所熟知 的棧一樣,棧式存儲分配按照先進後出的原則進行分配。
靜態存儲分配要求在編譯時能知道所有變數的存儲要求,棧式存儲分配要求在過程的入口處必須知道所有的存儲要求,而堆式存儲分配則專門負責在編譯時或運行時 模塊入口處都無法確定存儲要求的數據結構的內存分配,比如可變長度串和對象實例.堆由大片的可利用塊或空閑塊組成,堆中的內存可以按照任意順序分配和釋 放.

2.2 堆和棧的比較
上面的定義從編譯原理的教材中總結而來,除靜態存儲分配之外,都顯得很呆板和難以理解,下面撇開靜態存儲分配,集中比較堆和棧:
從堆和棧的功能和作用來通俗的比較,堆主要用來存放對象的,棧主要是用來執行程序的.而這種不同又主要是由於堆和棧的特點決定的:
編程中,例如C/C++中,所有的方法調用都是通過棧來進行的,所有的局部變數,形式參數都是從棧中分配內存空間的。實際上也不是什麼分配,只是從棧頂 向上用就行,就好像工廠中的傳送帶(conveyor belt)一樣,Stack Pointer會自動指引你到放東西的位置,你所要做的只是把東西放下來就行.退出函數的時候,修改棧指針就可以把棧中的內容銷毀.這樣的模式速度最快, 當然要用來運行程序了.需要注意的是,在分配的時候,比如為一個即將要調用的程序模塊分配數據區時,應事先知道這個數據區的大小,也就說是雖然分配是在程 序運行時進行的,但是分配的大小多少是確定的,不變的,而這個"大小多少"是在編譯時確定的,不是在運行時.
堆是應用程序在運行的時候請求操作系統分配給自己內存,由於從操作系統管理的內存分配,所以在分配和銷毀時都要佔用時間,因此用堆的效率非常低.但是堆的 優點在於,編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數據要在堆里停留多長的時間,因此,用堆保存數據時會得到更大的靈活性。事實上,面 向對象的多態性,堆內存分配是必不可少的,因為多態變數所需的存儲空間只有在運行時創建了對象之後才能確定.在C++中,要求創建一個對象時,只需用 new命令編制相關的代碼即可。執行這些代碼時,會在堆里自動進行數據的保存.當然,為達到這種靈活性,必然會付出一定的代價:在堆里分配存儲空間時會花 掉更長的時間!這也正是導致我們剛才所說的效率低的原因,看來列寧同志說的好,人的優點往往也是人的缺點,人的缺點往往也是人的優點(暈~).

2.3 JVM中的堆和棧
JVM是基於堆棧的虛擬機.JVM為每個新創建的線程都分配一個堆棧.也就是說,對於一個Java程序來說,它的運行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態。JVM對堆棧只進行兩種操作:以幀為單位的壓棧和出棧操作。
我們知道,某個線程正在執行的方法稱為此線程的當前方法.我們可能不知道,當前方法使用的幀稱為當前幀。當線程激活一個Java方法,JVM就會在線程的 Java堆棧里新壓入一個幀。這個幀自然成為了當前幀.在此方法執行期間,這個幀將用來保存參數,局部變數,中間計算過程和其他數據.這個幀在這里和編譯 原理中的活動紀錄的概念是差不多的.
從Java的這種分配機制來看,堆棧又可以這樣理解:堆棧(Stack)是操作系統在建立某個進程時或者線程(在支持多線程的操作系統中是線程)為這個線程建立的存儲區域,該區域具有先進後出的特性。
每一個Java應用都唯一對應一個JVM實例,每一個實例唯一對應一個堆。應用程序在運行中所創建的所有類實例或數組都放在這個堆中,並由應用所有的線程 共享.跟C/C++不同,Java中分配堆內存是自動初始化的。Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在堆棧中分配,也 就是說在建立一個對象時從兩個地方都分配內存,在堆中分配的內存實際建立這個對象,而在堆棧中分配的內存只是一個指向這個堆對象的指針(引用)而已。

轉自:http://www.anyuok.cn/showbbs.asp?bd=22&id=142&totable=1

呵呵,在看到你的問題前10分鍾才看的這個文章...

❾ 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,它是由編譯器完成的,它有利於節省空間。而一個對象引用變數修改了這個對象的內部狀態,會影響到另一個對象引用變數。

閱讀全文

與java對象內存分配相關的資料

熱點內容
網盤忘記解壓碼怎麼辦 瀏覽:852
文件加密看不到裡面的內容 瀏覽:651
程序員腦子里都想什麼 瀏覽:430
oppp手機信任app在哪裡設置 瀏覽:185
java地址重定向 瀏覽:268
一年級下冊摘蘋果的演算法是怎樣的 瀏覽:448
程序員出軌電視劇 瀏覽:88
伺服器系統地址怎麼查 瀏覽:54
解壓游戲發行官 瀏覽:601
國外小伙解壓實驗 瀏覽:336
頂級大學開設加密貨幣 瀏覽:437
java重載與多態 瀏覽:528
騰訊應屆程序員 瀏覽:942
一鍵編譯程序 瀏覽:129
語音加密包哪個好 瀏覽:339
有什麼學習高中語文的app 瀏覽:282
安卓手機的表格里怎麼打勾 瀏覽:409
阿里雲伺服器有網路安全服務嗎 瀏覽:969
超解壓兔子視頻 瀏覽:24
單片機怎麼測負脈沖 瀏覽:174