java 数据保存内存_java中的各种数据类型在内存中存储的方式 一
1.java是如何管理內(nèi)存的
java的內(nèi)存管理就是對象的分配和釋放問題。(其中包括兩部分)
分配:內(nèi)存的分配是由程序完成的,程序員需要通過關(guān)鍵字new為每個(gè)對象申請內(nèi)存空間(基本類型除外),所有的對象都在堆(Heap)中分配空間。釋放:對象的釋放是由垃圾回收機(jī)制決定和執(zhí)行的,這樣做確實(shí)簡化了程序員的工作。但同時(shí),它也加重了JVM的工作。因?yàn)?#xff0c;GC為了能夠正確釋放對象,GC必須監(jiān)控每一個(gè)對象的運(yùn)行狀態(tài),包括對象的申請、引用、被引用、賦值等,GC都需要進(jìn)行監(jiān)控。
2.什么叫java的內(nèi)存泄露
在java中,內(nèi)存泄漏就是存在一些被分配的對象,這些對象有下面兩個(gè)特點(diǎn),首先,這些對象是可達(dá)的,即在有向圖中,存在通路可以與其相連(也就是說仍存在該內(nèi)存對象的引用);其次,這些對象是無用的,即程序以后不會(huì)再使用這些對象。如果對象滿足這兩個(gè)條件,這些對象就可以判定為Java中的內(nèi)存泄漏,這些對象不會(huì)被GC所回收,然而它卻占用內(nèi)存。
3.JVM的內(nèi)存區(qū)域組成
java把內(nèi)存分兩種:一種是棧內(nèi)存,另一種是堆內(nèi)存(1)在函數(shù)中定義的基本類型變量和對象的引用變量都在函數(shù)的棧內(nèi)存中分配;(2)堆內(nèi)存用來存放由new創(chuàng)建的對象和數(shù)組以及對象的實(shí)例變量。在函數(shù)(代碼塊)中定義一個(gè)變量時(shí),java就在棧中為這個(gè)變量分配內(nèi)存空間,當(dāng)超過變量的作用域后,java會(huì)自動(dòng)釋放掉為該變量所分配的內(nèi)存空間;在堆中分配的內(nèi)存由java虛擬機(jī)的自動(dòng)垃圾回收器來管理堆和棧的優(yōu)缺點(diǎn)
堆的優(yōu)勢是可以動(dòng)態(tài)分配內(nèi)存大小,生存期也不必事先告訴編譯器,因?yàn)樗窃谶\(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存的。
缺點(diǎn)就是要在運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存,存取速度較慢;棧的優(yōu)勢是,存取速度比堆要快,僅次于直接位于CPU中的寄存器。
另外,棧數(shù)據(jù)可以共享。但缺點(diǎn)是,存在棧中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性。
4.java中數(shù)據(jù)在內(nèi)存中是如何存儲(chǔ)的
a)基本數(shù)據(jù)類型
java的基本數(shù)據(jù)類型共有8種,即int,short,long,byte,float,double,boolean,char(注意,并沒有String的基本類型)。這種類型的定義是通過諸如int a = 3;long b = 255L;的形式來定義的。如int a = 3;這里的a是一個(gè)指向int類型的引用,指向3這個(gè)字面值。這些字面值的數(shù)據(jù),由于大小可知,生存期可知(這些字面值定義在某個(gè)程序塊里面,程序塊退出后,字段值就消失了),出于追求速度的原因,就存在于棧中。
另外,棧有一個(gè)很重要的特殊性,就是存在棧中的數(shù)據(jù)可以共享。比如:我們同時(shí)定義:
int a=3;
int b=3;
編譯器先處理int a = 3;首先它會(huì)在棧中創(chuàng)建一個(gè)變量為a的引用,然后查找有沒有字面值為3的地址,沒找到,就開辟一個(gè)存放3這個(gè)字面值的地址,然后將a指向3的地址。接著處理int b = 3;在創(chuàng)建完b這個(gè)引用變量后,由于在棧中已經(jīng)有3這個(gè)字面值,便將b直接指向3的地址。這樣,就出現(xiàn)了a與b同時(shí)均指向3的情況。
定義完a與b的值后,再令a = 4;那么,b不會(huì)等于4,還是等于3。在編譯器內(nèi)部,遇到時(shí),它就會(huì)重新搜索棧中是否有4的字面值,如果沒有,重新開辟地址存放4的值;如果已經(jīng)有了,則直接將a指向這個(gè)地址。因此a值的改變不會(huì)影響到b的值。
b)對象
在java中,創(chuàng)建一個(gè)對象包括對象的聲明和實(shí)例化兩步,下面用一個(gè)例題來說明對象的內(nèi)存模型。假設(shè)有類Rectangle定義如下:
1.publicclassRectangle{
2.doublewidth;
3.doubleheight;
4.publicRectangle(doublew,doubleh){
5.w=width;
6.h=height;
7.}
8.}
(1)聲明對象時(shí)的內(nèi)存模型用Rectangle rect;聲明一個(gè)對象rect時(shí),將在棧內(nèi)存為對象的引用變量rect分配內(nèi)存空間,但Rectangle的值為空,稱rect是一個(gè)空對象。空對象不能使用,因?yàn)樗€沒有引用任何”實(shí)體”。(2)對象實(shí)例化時(shí)的內(nèi)存模型當(dāng)執(zhí)行rect=new Rectangle(3,5);時(shí),會(huì)做兩件事:在堆內(nèi)存中為類的成員變量width,height分配內(nèi)存,并將其初始化為各數(shù)據(jù)類型的默認(rèn)值;接著進(jìn)行顯式初始化(類定義時(shí)的初始化值);最后調(diào)用構(gòu)造方法,為成員變量賦值。返回堆內(nèi)存中對象的引用(相當(dāng)于首地址)給引用變量rect,以后就可以通過rect來引用堆內(nèi)存中的對象了。
c)創(chuàng)建多個(gè)不同的對象實(shí)例
一個(gè)類通過使用new運(yùn)算符可以創(chuàng)建多個(gè)不同的對象實(shí)例,這些對象實(shí)例將在堆中被分配不同的內(nèi)存空間,改變其中一個(gè)對象的狀態(tài)不會(huì)影響其他對象的狀態(tài)。例如:
1.Rectangler1=newRectangle(3,5);
2.Rectangler2=newRectangle(4,6);
此時(shí),將在堆內(nèi)存中分別為兩個(gè)對象的成員變量width、height分配內(nèi)存空間,兩個(gè)對象在堆內(nèi)存中占據(jù)的空間是互不相同的。如果有:
1.Rectangler1=newRectangle(3,5);
2.Rectangler2=r1;
則在堆內(nèi)存中只創(chuàng)建了一個(gè)對象實(shí)例,在棧內(nèi)存中創(chuàng)建了兩個(gè)對象引用,兩個(gè)對象引用同時(shí)指向一個(gè)對象實(shí)例。
d)包裝類
基本類型都有對應(yīng)的包裝類:如int對應(yīng)Integer類,double對應(yīng)Double類等,基本類型的定義都是直接在棧中,如果用包裝類來創(chuàng)建對象,就和普通對象一樣了。例如:int i=0;i直接存儲(chǔ)在棧中。Integer i(i此時(shí)是對象)= newInteger(5);這樣,i對象數(shù)據(jù)存儲(chǔ)在堆中,i的引用存儲(chǔ)在棧中,通過棧中的引用來操作對象。
e)String
String是一個(gè)特殊的包裝類數(shù)據(jù)。可以用以下兩種方式創(chuàng)建:String str= new String(“abc”);String str = “abc”;第一種創(chuàng)建方式,和普通對象的的創(chuàng)建過程一樣;第二種創(chuàng)建方式,java內(nèi)部將此語句轉(zhuǎn)化為以下幾個(gè)步驟:(1)先定義一個(gè)名為str的對String類的對象引用變量:String str;(2)在棧中查找有沒有存放值為”abc”的地址,如果沒有,則開辟一個(gè)存放字面值為”abc”地址,接著創(chuàng)建一個(gè)新的String類的對象o,并將o的字符串值指向這個(gè)地址,而且在棧這個(gè)地址旁邊記下這個(gè)引用的對象o。如果已經(jīng)有了值為”abc”的地址,則查找對象o,并回o的地址。(3)將str指向?qū)ο髈的地址。值得注意的是,一般String類中字符串值都是直接存值的。但像String str = “abc”;這種合下,其字符串值卻是保存了一個(gè)指向存在棧中數(shù)據(jù)的引用。為了更好地說明這個(gè)問題,我們可以通過以下的幾個(gè)代碼進(jìn)行驗(yàn)證。
1.Stringstr1="abc";
2.Stringstr2="abc";
3.System.out.println(s1==s2);//true
注意,這里并不用str1.equals(str2);的方式,因?yàn)檫@將比較兩個(gè)字符串的值是否相等。==號(hào),根據(jù)JDK的說明,只有在兩個(gè)引用都指向了同一個(gè)對象時(shí)才返回真值。而我們在這里要看的是,str1與str2是否都指向了同一個(gè)對象。我們再接著看以下的代碼。
1.Stringstr1=newString("abc");
2.Stringstr2="abc";
3.System.out.println(str1==str2);//false
創(chuàng)建了兩個(gè)引用。創(chuàng)建了兩個(gè)對象。兩個(gè)引用分別指向不同的兩個(gè)對象。以上兩段代碼說明,只要是用new()來新建對象的,都會(huì)在堆中創(chuàng)建,而且其字符串是單獨(dú)存值的,即使與棧中的數(shù)據(jù)相同,也不會(huì)與棧中的數(shù)據(jù)共享。
f)數(shù)組
當(dāng)定義一個(gè)數(shù)組,int x[];或int[] x;時(shí),在棧內(nèi)存中創(chuàng)建一個(gè)數(shù)組引用,通過該引用(即數(shù)組名)來引用數(shù)組。x=newint[3];將在堆內(nèi)存中分配3個(gè)保存int型數(shù)據(jù)的空間,堆內(nèi)存的首地址放到棧內(nèi)存中,每個(gè)數(shù)組元素被初始化為0。
g)靜態(tài)變量
用static的修飾的變量和方法,實(shí)際上是指定了這些變量和方法在內(nèi)存中的”固定位置”-static storage,可以理解為所有實(shí)例對象共有的內(nèi)存空間。static變量有點(diǎn)類似于C中的全局變量的概念;靜態(tài)表示的是內(nèi)存的共享,就是它的每一個(gè)實(shí)例都指向同一個(gè)內(nèi)存地址。把static拿來,就是告訴JVM它是靜態(tài)的,它的引用(含間接引用)都是指向同一個(gè)位置,在那個(gè)地方,你把它改了,它就不會(huì)變成原樣,你把它清理了,它就不會(huì)回來了。
那靜態(tài)變量與方法是在什么時(shí)候初始化的呢?對于兩種不同的類屬性,static屬性與instance屬性,初始化的時(shí)機(jī)是不同的。instance屬性在創(chuàng)建實(shí)例的時(shí)候初始化,static屬性在類加載,也就是第一次用到這個(gè)類的時(shí)候初始化,對于后來的實(shí)例的創(chuàng)建,不再次進(jìn)行初始化。
我們常可看到類似以下的例子來說明這個(gè)問題:
1.classStudent{
2.staticintnumberOfStudents=0;
3.Student()
4.{
5.numberOfStudents++;
6.}
7.}
每一次創(chuàng)建一個(gè)新的Student實(shí)例時(shí),成員numberOfStudents都會(huì)不斷的遞增,并且所有的Student實(shí)例都訪問同一個(gè)numberOfStudents變量,實(shí)際上intnumberOfStudents變量在內(nèi)存中只存儲(chǔ)在一個(gè)位置上。
5.java的內(nèi)存管理實(shí)例
Java程序的多個(gè)部分(方法,變量,對象)駐留在內(nèi)存中以下兩個(gè)位置:即堆和棧,現(xiàn)在我們只關(guān)心三類事物:實(shí)例變量,局部變量和對象:實(shí)例變量和對象駐留在堆上局部變量駐留在棧上讓我們查看一個(gè)java程序,看看他的各部分如何創(chuàng)建并且映射到棧和堆中:
1.publicclassDog{
2.Collarc;
3.Stringname;
4.//1.main()方法位于棧上
5.publicstaticvoidmain(String[]args){
6.//2.在棧上創(chuàng)建引用變量d,但Dog對象尚未存在
7.Dogd;
8.//3.創(chuàng)建新的Dog對象,并將其賦予d引用變量
9.d=newDog();
10.//4.將引用變量的一個(gè)副本傳遞給go()方法
11.d.go(d);
12.}
13.//5.將go()方法置于棧上,并將dog參數(shù)作為局部變量
14.voidgo(Dogdog){
15.//6.在堆上創(chuàng)建新的Collar對象,并將其賦予Dog的實(shí)例變量
16.c=newCollar();
17.}
18.//7.將setName()添加到棧上,并將dogName參數(shù)作為其局部變量
19.voidsetName(StringdogName){
20.//8.name的實(shí)例對象也引用String對象
21.name=dogName;
22.}
23.//9.程序執(zhí)行完成后,setName()將會(huì)完成并從棧中清除,此時(shí),局部變量dogName也會(huì)消失,盡管它所引用的String仍在堆上
24.}
總結(jié)
以上是生活随笔為你收集整理的java 数据保存内存_java中的各种数据类型在内存中存储的方式 一的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [转载] java注释
- 下一篇: [转载] 向集合中添加自定义类型--建议