JVM的棧上分配

棧上分配是JVM的一個優化選項。java

Java的對象通常都是分配在堆內存中的,而JVM開啓了棧上分配後,容許把線程私有的對象(其它線程訪問不到的對象)打散分配在棧上。這些分配在棧上的對象在方法調用結束後即自行銷燬,不須要JVM觸發垃圾回收器來回收,所以提高了JVM的性能。bash

棧上分配在JDK6u23後默認是開啓了的。下面經過代碼來驗證這一點。jvm

驗證

寫一段代碼:性能

public class OnStackTest {
    // User類
    public static class User{
        public int id=0;
        public String name="";
    }
    // 建立User類對象
    public static void alloc(){
        User u=new User();
        u.id=5;
        u.name="geym";
    }
    // 程序入口
    public static void main(String[] args) throws InterruptedException {
        long b=System.currentTimeMillis();
        // 建立大量的對象
        for(int i=0;i<99999999;i++){
            alloc();
        }
        long e=System.currentTimeMillis();
        // 打印執行時間
        System.out.println(e-b);
    }
}

JDK6u22環境下執行的結果:優化

-Xmx:指定最大堆內存spa

-Xms:指定最大堆內存線程

-XX:+PrintGC:打印GC日誌,+號表示啓用,-號表示禁用日誌

D:\develop\jdk\6u22\bin\java.exe -Xmx5m -Xms5m -XX:+PrintGC geym.zbase.ch2.onstackalloc.OnStackTest
[GC 2048K->288K(5824K), 0.0049399 secs]
[GC 2336K->288K(5824K), 0.0013872 secs]
[GC 2336K->320K(5824K), 0.0026034 secs]
......
[GC 3280K->720K(6080K), 0.0001026 secs]
3304

JDK6u23環境下執行的結果:code

D:\develop\jdk\6u23\bin\java.exe -Xmx5m -Xms5m -XX:+PrintGC geym.zbase.ch2.onstackalloc.OnStackTest
70

從以上兩個執行的結果能夠看出,JDK6u22默認是沒有開啓棧上分配的,因此alloc()方法中new出來的User對象,是存放在堆上的。因爲指定了最大堆內存只有5m,當堆內存不足就會觸發GC。從JDK6u22的GC日誌能夠看出執行過程頻繁觸發GC,執行耗時3304,明顯比JDK6u23的長。server

而JDK6u23的執行過程當中沒有打印任何GC日誌,證實這時候alloc()方法中new出來的User對象,是存放在alloc()方法對應的棧幀上的。當每一次執行alloc()方法,線程會向Java棧壓入一個棧幀,new User()建立的對象就存放在這個棧幀上;alloc()方法執行結束,棧幀從Java棧彈出,user對象隨棧幀的彈出銷燬。

示意圖:

相關JVM參數

JDK6u22及以前的版本若是須要使用棧上分配來優化,能夠加入如下參數:

java -server -Xmx5m -Xms5m -XX:+PrintGC -XX:+DoEscapeAnalysis -XX:-UseTLAB -XX:+EliminateAllocations  geym.zbase.ch2.onstackalloc.OnStackTest

-XX:+DoEscapeAnalysis:開啓逃逸分析

-XX:-UseTLAB:禁用TLAB(Thread Local Allocation Buffer)

-XX:+EliminateAllocations:開啓標量替換

關於逃逸分析

逃逸分析的做用就是判斷一個對象的做用域有沒有可能逃出一個Java方法的做用域。請看下面例子演示:

// u對象逃出alloc的做用域,不符合棧上分配的條件
public class OnStackTest {
    private static User u;
    public static void alloc(){
        u=new User();
    }
}
// u對象沒有逃出alloc的做用域,符合棧上分配的條件
public class OnStackTest {
    public static void alloc(){
        User u=new User();
    }
}

關於標量替換

啓用標量替換後,容許把對象打散分配在棧上。
好比user對象有id和name屬性,在啓用標量替換後,user對象的id和name屬性會視爲局部變量分配在棧上

注意:逃逸分析和標量替換是棧上分配的前提,因此,在jvm參數中關閉了兩者其中一個選項,棧上分配都不會生效。

相關文章
相關標籤/搜索