java堆和栈 常量池_GitHub - han-guang-xue/difference-of-stack-heap-pool: Java中堆、栈和常量池的区别...
Java中堆、棧和常量池的區別
棧 堆 常量池的概念
首先我們先了解一下概念,Java把內存分成兩種,一種叫做棧內存,一種叫做堆內存。
棧內存
存放基本類型的變量數據和對象類型的引用(請注意存放的是引用),對象本身不存放在棧中,而是存放在堆(new 出來的對象)或者常量池中(字符串常量對象存放在常量池中)
堆內存
堆內存用來存放由new創建的對象和數組。在堆中分配的內存,由java虛擬機自動垃圾回收器來管理
常量池
存放字符串常量和基本類型常量(public static final)
堆棧的比較
一般來說,棧的速度比堆的速度要快,那為什么還要引入堆呢?這就要涉及到堆棧的優缺點了。
棧的優勢是,存取速度比堆要快,僅次于寄存器,棧數據可以共享(指的是線程共享,而給進程共享)。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。
堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由于要 在運行時動態分配內存,存取速度較慢。
##奇怪的問題
在學習java堆棧的知識時,也遇到一點有趣的小問題,我很奇怪,查了很多資料后發現有不同的說法。
對于String s=new String("abc");的創建
一種說法:對于通過new產生一個字符串(假設為”abc”)時,會先去常量池中查找是否已經有了”abc”對象,如果已經創建"abc",則不會繼續創建。如果沒有則在常量池中創建一個此字符串對象,然后堆中再創建一個常量池中此”abc”對象的拷貝對象。一個是編譯時決定的,最后放在常量池中。一個是運行時放在堆里面的。
另一種說法:1、首先在堆中(不是常量池)創建一個指定的對象"abc",并讓str引用指向該對象 2、在字符串常量池中查看,是否存在內容為"abc"字符串對象 3、若存在,則將new出來的字符串對象與字符串常量池中的對象聯系起來 4、若不存在,則在字符串常量池中創建一個內容為"abc"的字符串對象,并將堆中的對象與之聯系起來 intern 方法可以返回該字符串在常量池中的對象的引用
String str1 = "abc";
一種說法:
棧中開辟一塊空間存放引用str1,
String池中開辟一塊空間,存放String常量"abc",
引用str1指向常量池中String常量"abc",
str1所指代的地址即常量"abc"所在地址,輸出為true
另一種說法:先在棧中創建一個對String類的對象引用變量str1 ,然后查找棧中有沒有存放"abc",如果沒有,則將"abc"存放進棧,并令str1 指向"abc",如果已經有"abc",則直接令str1 指向"abc"
對于上述兩個問題的兩種說法,本人均更傾向于第一種說法,因為第一種說法對程序有優化的作用,第二種說法是多此一舉,并未帶來任何好處。
討論:int a=1;那么這個1是存在棧里還是存在常量池?
對于這個問題我也困惑了好久,首先可以肯定的a是int類型的引用,存儲在棧里,那么這個1到底是存在哪里呢? 我們通常的理解是1作為一個常量肯定是存儲在常量池中啊。在查閱了很多資料后發現,這個1是存儲在棧中,并未存儲在常量池中。 這里我們需要注意的是對于1我們看起來是常量,但j虛擬機卻并不是將其看作是常量而是一個變量,只有聲明為final類型的數據JVM才會將其看做是常量,而常量池是存放字符串常量和基本類型常量(public static final)的,所以這里的1是存儲在棧里的。
實例分析
我們舉一個最常見的例子,分析一下棧基本的處理過程:
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,它是由編譯器完成的,它有利于節省空間。而一個對象引用變量修改了這個對象的內部狀態,會影響到另一個對象引用變量。
總結以及需要注意的地方
對于字符串:其對象的引用都是存儲在棧中的,如果是編譯期已經創建好(直接用雙引號定義的)的就存儲在常量池中,如果是運行期(new出來的)才能確定的就存儲在堆中。
對于基本類型應該不會用到常量池,因為基本類型的值就存在棧中,在Java中對基本類型變量的運算、判斷以及賦值都是對值的操作,沒有對地址操作,例如:int a = 1; int b = 2; a = b; 這是將b的值賦給a,而不是a引用b;
我們在使用諸如String str = "abc";的格式定義類時,總是想當然地認為,我們創建了String類的對象str。請注意,對象可能并沒有被創建!唯一可以肯定的是,指向 String類的引用被創建了。至于這個引用到底是否指向了一個新的對象,必須根據上下文來考慮,除非你通過new()方法來顯要地創建一個新的對象。因 此,更為準確的說法是,我們創建了一個指向String類的對象的引用變量str,這個對象引用變量指向了某個值為"abc"的String類。
總結
以上是生活随笔為你收集整理的java堆和栈 常量池_GitHub - han-guang-xue/difference-of-stack-heap-pool: Java中堆、栈和常量池的区别...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中药肚脐贴真能减肥吗
- 下一篇: 解决Java当中 用point 画图时背