js的棧與堆


js的棧與堆

堆heap與棧stack基本是所有的程序語言中都帶有的,它將數據分配到內存空間來完成各種調用。(當然了,內存里除了heap和stack還有常量池。)

為啥要有heap和stack?

為什么會有棧內存和堆內存之分?

通常與垃圾回收機制有關。為了使程序運行時占用的內存最小。

當一個方法執行時,每個方法都會建立自己的內存棧,在這個方法內定義的變量將會逐個放入這塊棧內存里,隨着方法的執行結束,這個方法的內存棧也將自然銷毀了。因此,所有在方法中定義的變量都是放在棧內存中的;

當我們在程序中創建一個對象時,這個對象將被保存到運行時數據區中,以便反復利用(因為對象的創建成本通常較大),這個運行時數據區就是堆內存。堆內存中的對象不會隨方法的結束而銷毀,即使方法結束后,這個對象還可能被另一個引用變量所引用(方法的參數傳遞時很常見),則這個對象依然不會被銷毀,只有當一個對象沒有任何引用變量引用它時,系統的垃圾回收機制才會在核實的時候回收它。

棧中存儲的是基礎變量以及一些對象的引用變量,基礎變量的值是存儲在棧中,而引用變量存儲在棧中的是指向堆中的數組或者對象的地址,這就是為何修改引用類型總會影響到其他指向這個地址的引用變量。

js和java類似,有垃圾回收機器自動釋放那些不需要的。在java中,變量超出作用域后,便會被銷毀,引用變量被銷毀后,數組和對象依舊占據着堆內存的空間。數組和對象在沒有引用變量指向它的時候,才變成垃圾,不能再被使用,但是仍然占着內存,在隨后的一個不確定的時間被垃圾回收器釋放掉。這個也是java比較占內存的主要原因。

棧的特點

棧的優勢是,存取速度比堆要快,僅次於直接位於CPU中的寄存器。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。

Java中的數據類型有兩種。 一種是基本類型(primitive types), 共有8種,即int, short, long, byte, float, double, boolean, char(注意,並沒有string的基本類型)。這種類型的定義是通過諸如int a = 3; long b = 255L;的形式來定義的,稱為自動變量。值得注意的是,自動變量存的是字面值,不是類的實例,即不是類的引用,這里並沒有類的存在。如int a = 3; 這里的a是一個指向int類型的引用,指向3這個字面值。這些字面值的數據,由於大小可知,生存期可知(這些字面值固定定義在某個程序塊里面,程序塊退出后,字段值就消失了),出於追求速度的原因,就存在於棧中。

另外,棧有一個很重要的特殊性,就是存在棧中的數據可以共享。假設我們同時定義
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與 b的值后,再令a=4;那么,b不會等於4,還是等於3。在編譯器內部,遇到a=4;時,它就會重新搜索棧中是否有4的字面值,如果沒有,重新開辟地址存放4的值;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。

js中除了null和undefined,其他值都可以擁有方法。數組和對象屬於可變類型,number,boolean,null,undefined都是不可變的,string也是同樣,雖然可以看作字符組成的數組,但它是不可變的,只會被包含新值的字符串替換。

var str = 'hello';
str += ' world'; //這個操作其實是這樣,創建一個11個字符長度的新字符串,然后填充'hello'和' world',最后銷毀這兩個字符串,完成值的變更。
log(str)//hello world

棧和堆的溢出

如果想要堆溢出,比較簡單,可以循環創建對象或大的對象;
如果想要棧溢出,可以遞歸調用方法,這樣隨着棧深度的增加,JVM 維持着一條長長的方法調用軌跡,直到內存不夠分配,產生棧溢出。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM