java堆內存和棧內存分析

1. 區別:堆和棧區別
堆:主要用於儲存實例化的對象,數組。由JVM動態分配內存空間。一個JVM只有一個堆內存,線程是可以共享數據的。
棧:主要用於儲存局部變量和對象的引用變量,每個線程都會有一個獨立的棧空間,所以線程之間是不共享數據的。
2. 堆內存和棧內存區別
堆內存:儲存的是數組和對象(其實數組就是對象),凡是new建立的都是在堆中,堆中存放的都是實體(對象),實體用於封裝數據,而且是封裝多個(實體的多個屬性),如果一個數據消失,這個實體也沒有消失,還可以用,所以堆是不會隨時釋放的,但是棧不一樣,棧裏存放的都是單個變量,變量被釋放了,那就沒有了。堆裏的實體雖然不會被釋放,但是會被當成垃圾,Java有垃圾回收機制不定時的收取。
棧內存:棧內存首先是一片內存區域,存儲的都是局部變量,凡是定義在方法中的都是局部變量(方法外的是全局變量),for循環內部定義的也是局部變量,是先加載函數才能進行局部變量的定義,所以方法先進棧,然後再定義變量,變量有自己的作用域,一旦離開作用域,變量就會被釋放。棧內存的更新速度很快,因爲局部變量的生命週期很短。
3. Java中變量在內存中的分配
類變量(static修飾的變量):在程序加載時系統就爲它在堆中開闢了內存,堆中的內存地址存放於棧以便於高速訪問。靜態變量的生命週期------一直持續到整個“系統”關閉;
實例變量:當你使用java關鍵字new的時候,系統在堆中開闢一塊空間(不一定連續)分配給變量,然後根據堆內存地址(可能是零散的),通過哈希算法換算爲一長串數字以表徵這個變量在堆中的“物理位置”。實例變量的生命週期-------當實例變量的引用丟失後,將被GC(垃圾回收器)列入可回收“名單”中,但並不是馬上就釋放對中內存;
局部變量:局部變量,由聲明在某方法,或某代碼段裏(比如for循環),執行到它的時候在棧中開闢內存,當局部變量一旦脫離作用域,內存立即釋放;

4. Java中變量的作用域
在Java中,變量的作用域分爲四個級別:類級、對象實例級、方法級、塊級
類級變量:又稱全局變量或靜態變量,需要使用static關鍵字修飾,可以通過類名來訪問,不需要實例化。
對象實例級變量:又稱成員變量,實例化後纔會分配內存空間,才能訪問。
方法級變量:就是在方法內部定義的變量,就是局部變量。
塊級變量:就是定義在一個塊內部的變量,變量的生存週期就是這個塊,出了這個塊就消失了,比如if、for語句的塊。

說明:
方法內部除了能訪問方法級的變量,還可以訪問類級和實例級的變量。
塊內部能夠訪問類級、實例級變量,如果塊被包含在方法內部,它還可以訪問方法級的變量。

示例代碼:

package com.compareObject;

/**
 * @Auther: 13213
 * @Date: 2020/5/28 11:37
 * @Description:
 */
public class Demo {
    public static String name = "微學苑";  // 類級變量
    public int i; // 對象實例級變量

    // 屬性塊,在類初始化屬性時候運行
    {
        int j = 2;// 塊級變量
    }

    public void test1() {
        int j = 3;  // 方法級變量
        if(j == 3) {
            int k = 5;  // 塊級變量
        }
        // 這裏不能訪問塊級變量,塊級變量只能在塊內部訪問
        System.out.println("name=" + name + ", i=" + i + ", j=" + j);
    }

    public static void main(String[] args) {
        // 不創建對象,直接通過類名訪問類級變量
        System.out.println(Demo.name);

        // 創建對象並訪問它的方法
        Demo t = new Demo();
        t.test1();
    }
}

比如主函數裏的語句   int [] arr=new int [3];在內存中是怎麼被定義的:
主函數先進棧,在棧中定義一個變量arr,接下來爲arr賦值,但是右邊不是一個具體值,是一個實體。實體創建在堆裏,在堆裏首先通過new關鍵字開闢一個空間,內存在存儲數據的時候都是通過地址來體現的,地址是一塊連續的二進制,然後給這個實體分配一個內存地址。把堆的地址賦給arr,arr就通過地址指向了數組。所以arr想操縱數組時,就通過地址,而不是直接把實體都賦給它。這種我們不再叫他基本數據類型,而叫引用數據類型。稱爲arr引用了堆內存當中的實體。

 所以堆與棧的區別很明顯:
1.棧內存存儲的是局部變量而堆內存存儲的是實體;
2.棧內存的更新速度要快於堆內存,因爲局部變量的生命週期很短;
3.棧內存存放的變量生命週期一旦結束就會被釋放,而堆內存存放的實體會被垃圾回收機制不定時的回收。

總結:最主要的區別就是棧內存用來存儲局部變量和方法調用。而堆內存用來存儲Java中的對象。無論是成員變量,局部變量,還是類變量,它們指向的對象都存儲在堆內存中。
棧內存歸屬於單個線程,每個線程都會有一個棧內存,其存儲的變量只能在其所屬線程中可見,即棧內存可以理解成線程的私有內存。而堆內存中的對象對所有線程可見。堆內存中的對象可以被所有線程訪問。
可以通過-Xss選項設置棧內存的大小。-Xms選項可以設置堆的開始時的大小,-Xmx選項可以設置堆的最大值,一般情況下這兩個值設爲相同大小。因爲如果不相同且內存不夠用時會發生內存抖動現象,非常影響程序運行。

jvm調優常用設置:
-Xms:初始堆大小 
-Xmx:最大堆大小 
-XX:NewSize=n:設置年輕代大小 
-XX:NewRatio=n:設置年輕代和年老代的比值.如:爲3,表示年輕代與年老代比值爲1:3,年輕代佔整個年輕代年老代和的1/4 
-XX:SurvivorRatio=n:年輕代中Eden區與兩個Survivor區的比值.注意Survivor區有兩個.如:3,表示Eden:Survivor=3:2,一個Survivor區佔整個年輕代的1/5 
-XX:MaxPermSize=n:設置持久代大小

典型設置:
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
-Xmx3550m:設置JVM最大可用內存爲3550M.
-Xms3550m:設置JVM促使內存爲3550m.此值可以設置與-Xmx相同,以避免每次垃圾回收完成後JVM重新分配內存.
-Xmn2g:設置年輕代大小爲2G.整個堆大小=年輕代大小 + 年老代大小 + 持久代大小.持久代一般固定大小爲64m,所以增大年輕代後,將會減小年老代大小.此值對系統性能影響較大,Sun官方推薦配置爲整個堆的3/8
-Xss128k:設置每個線程的堆棧大小,JDK5.0以後每個線程堆棧大小爲1M,以前每個線程堆棧大小爲256K.根據應用的線程所需內存大小進行調整。在相同物理內存下,減小這個值能生成更多的線程.但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章