java堆和栈 常量池_GitHub - han-guang-xue/difference-of-stack-heap-pool: Java中堆、栈和常量池的区别...
Java中堆、棧和常量池的區(qū)別
棧 堆 常量池的概念
首先我們先了解一下概念,Java把內(nèi)存分成兩種,一種叫做棧內(nèi)存,一種叫做堆內(nèi)存。
棧內(nèi)存
存放基本類型的變量數(shù)據(jù)和對(duì)象類型的引用(請(qǐng)注意存放的是引用),對(duì)象本身不存放在棧中,而是存放在堆(new 出來(lái)的對(duì)象)或者常量池中(字符串常量對(duì)象存放在常量池中)
堆內(nèi)存
堆內(nèi)存用來(lái)存放由new創(chuàng)建的對(duì)象和數(shù)組。在堆中分配的內(nèi)存,由java虛擬機(jī)自動(dòng)垃圾回收器來(lái)管理
常量池
存放字符串常量和基本類型常量(public static final)
堆棧的比較
一般來(lái)說(shuō),棧的速度比堆的速度要快,那為什么還要引入堆呢?這就要涉及到堆棧的優(yōu)缺點(diǎn)了。
棧的優(yōu)勢(shì)是,存取速度比堆要快,僅次于寄存器,棧數(shù)據(jù)可以共享(指的是線程共享,而給進(jìn)程共享)。但缺點(diǎn)是,存在棧中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性。
堆的優(yōu)勢(shì)是可以動(dòng)態(tài)地分配內(nèi)存大小,生存期也不必事先告訴編譯器,Java的垃圾收集器會(huì)自動(dòng)收走這些不再使用的數(shù)據(jù)。但缺點(diǎn)是,由于要 在運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存,存取速度較慢。
##奇怪的問(wèn)題
在學(xué)習(xí)java堆棧的知識(shí)時(shí),也遇到一點(diǎn)有趣的小問(wèn)題,我很奇怪,查了很多資料后發(fā)現(xiàn)有不同的說(shuō)法。
對(duì)于String s=new String("abc");的創(chuàng)建
一種說(shuō)法:對(duì)于通過(guò)new產(chǎn)生一個(gè)字符串(假設(shè)為”abc”)時(shí),會(huì)先去常量池中查找是否已經(jīng)有了”abc”對(duì)象,如果已經(jīng)創(chuàng)建"abc",則不會(huì)繼續(xù)創(chuàng)建。如果沒(méi)有則在常量池中創(chuàng)建一個(gè)此字符串對(duì)象,然后堆中再創(chuàng)建一個(gè)常量池中此”abc”對(duì)象的拷貝對(duì)象。一個(gè)是編譯時(shí)決定的,最后放在常量池中。一個(gè)是運(yùn)行時(shí)放在堆里面的。
另一種說(shuō)法:1、首先在堆中(不是常量池)創(chuàng)建一個(gè)指定的對(duì)象"abc",并讓str引用指向該對(duì)象 2、在字符串常量池中查看,是否存在內(nèi)容為"abc"字符串對(duì)象 3、若存在,則將new出來(lái)的字符串對(duì)象與字符串常量池中的對(duì)象聯(lián)系起來(lái) 4、若不存在,則在字符串常量池中創(chuàng)建一個(gè)內(nèi)容為"abc"的字符串對(duì)象,并將堆中的對(duì)象與之聯(lián)系起來(lái) intern 方法可以返回該字符串在常量池中的對(duì)象的引用
String str1 = "abc";
一種說(shuō)法:
棧中開(kāi)辟一塊空間存放引用str1,
String池中開(kāi)辟一塊空間,存放String常量"abc",
引用str1指向常量池中String常量"abc",
str1所指代的地址即常量"abc"所在地址,輸出為true
另一種說(shuō)法:先在棧中創(chuàng)建一個(gè)對(duì)String類的對(duì)象引用變量str1 ,然后查找棧中有沒(méi)有存放"abc",如果沒(méi)有,則將"abc"存放進(jìn)棧,并令str1 指向"abc",如果已經(jīng)有"abc",則直接令str1 指向"abc"
對(duì)于上述兩個(gè)問(wèn)題的兩種說(shuō)法,本人均更傾向于第一種說(shuō)法,因?yàn)榈谝环N說(shuō)法對(duì)程序有優(yōu)化的作用,第二種說(shuō)法是多此一舉,并未帶來(lái)任何好處。
討論:int a=1;那么這個(gè)1是存在棧里還是存在常量池?
對(duì)于這個(gè)問(wèn)題我也困惑了好久,首先可以肯定的a是int類型的引用,存儲(chǔ)在棧里,那么這個(gè)1到底是存在哪里呢? 我們通常的理解是1作為一個(gè)常量肯定是存儲(chǔ)在常量池中啊。在查閱了很多資料后發(fā)現(xiàn),這個(gè)1是存儲(chǔ)在棧中,并未存儲(chǔ)在常量池中。 這里我們需要注意的是對(duì)于1我們看起來(lái)是常量,但j虛擬機(jī)卻并不是將其看作是常量而是一個(gè)變量,只有聲明為final類型的數(shù)據(jù)JVM才會(huì)將其看做是常量,而常量池是存放字符串常量和基本類型常量(public static final)的,所以這里的1是存儲(chǔ)在棧里的。
實(shí)例分析
我們舉一個(gè)最常見(jiàn)的例子,分析一下棧基本的處理過(guò)程:
int a = 3; int b = 3;
編譯器會(huì)先處理int a = 3;,首先它會(huì)在棧中創(chuàng)建一個(gè)變量為a 的引用,然后查找棧中是否有3 這個(gè)值,如果沒(méi)找到,就將3存放進(jìn)來(lái),然后將a 指向3 。接著處理int b = 3; ,在創(chuàng)建完 b 的引用變量后,因?yàn)樵跅V幸呀?jīng)有3 這個(gè)值,便將b 直接指向3 。 這樣,就出現(xiàn)了 a 與 b 同時(shí)均指向 3 的情況。這時(shí),如果再令 a = 4;,那么編譯器會(huì)重新搜索棧中是否有 4 這個(gè)值,如果沒(méi)有,就將4 存放進(jìn)來(lái),并令a 指向 4;如果已經(jīng)有了,則直接將 a 指向這個(gè)地址。 因此a值的改變不會(huì)影響到 b 值。?要注意這種數(shù)據(jù)的共享與兩個(gè)對(duì)象的引用同時(shí)指向一個(gè)對(duì)象的這種共享是不同的,因?yàn)檫@種情況 a 的修改并不會(huì)影響到 b,它是由編譯器完成的,它有利于節(jié)省空間。而一個(gè)對(duì)象引用變量修改了這個(gè)對(duì)象的內(nèi)部狀態(tài),會(huì)影響到另一個(gè)對(duì)象引用變量。
總結(jié)以及需要注意的地方
對(duì)于字符串:其對(duì)象的引用都是存儲(chǔ)在棧中的,如果是編譯期已經(jīng)創(chuàng)建好(直接用雙引號(hào)定義的)的就存儲(chǔ)在常量池中,如果是運(yùn)行期(new出來(lái)的)才能確定的就存儲(chǔ)在堆中。
對(duì)于基本類型應(yīng)該不會(huì)用到常量池,因?yàn)榛绢愋偷闹稻痛嬖跅V?#xff0c;在Java中對(duì)基本類型變量的運(yùn)算、判斷以及賦值都是對(duì)值的操作,沒(méi)有對(duì)地址操作,例如:int a = 1; int b = 2; a = b; 這是將b的值賦給a,而不是a引用b;
我們?cè)谑褂弥T如String str = "abc";的格式定義類時(shí),總是想當(dāng)然地認(rèn)為,我們創(chuàng)建了String類的對(duì)象str。請(qǐng)注意,對(duì)象可能并沒(méi)有被創(chuàng)建!唯一可以肯定的是,指向 String類的引用被創(chuàng)建了。至于這個(gè)引用到底是否指向了一個(gè)新的對(duì)象,必須根據(jù)上下文來(lái)考慮,除非你通過(guò)new()方法來(lái)顯要地創(chuàng)建一個(gè)新的對(duì)象。因 此,更為準(zhǔn)確的說(shuō)法是,我們創(chuàng)建了一個(gè)指向String類的對(duì)象的引用變量str,這個(gè)對(duì)象引用變量指向了某個(gè)值為"abc"的String類。
總結(jié)
以上是生活随笔為你收集整理的java堆和栈 常量池_GitHub - han-guang-xue/difference-of-stack-heap-pool: Java中堆、栈和常量池的区别...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 中药肚脐贴真能减肥吗
- 下一篇: 减肥要补充哪种维生素呢