JVM--內存結構

一、什么是JVM

定義

Java Virtual Machine刻剥,JAVA程序的運行環(huán)境(JAVA二進制字節(jié)碼的運行環(huán)境)

好處

  • 一次編寫造虏,到處運行
  • 自動內存管理,垃圾回收機制
  • 數(shù)組下標越界檢查
  • 多態(tài) (虛方法表機制實現(xiàn))

比較

JVM JRE JDK的區(qū)別

截屏2021-09-18 20.57.24.png

二、內存結構

整體架構

截屏2021-09-18 21.00.52.png

分成3大塊:類加載器揍诽、JVM內存結構暑脆、執(zhí)行引擎添吗。

  • 從java源代碼編譯成java class份名,通過類加載器加載到JVM內存中去運行同窘;
  • 類放在方法區(qū)想邦,創(chuàng)建的實例對象放在Heap(堆)中丧没,堆里的對象在調用方法時,又會用到虛擬機棧(Stack)漆际、程序計數(shù)器(PC)奸汇、本地方法棧(Native)擂找;
  • 方法執(zhí)行時浩销,每行代碼是由執(zhí)行引擎中的解釋器逐行執(zhí)行慢洋,一些熱點代碼會由JIT即時編譯器編譯優(yōu)化,GC會對不再引用的對象進行回收礁遣,一些與操作系統(tǒng)打交道的就需要調用操作系統(tǒng)提供的本地方法接口祟霍。

1盈包、程序計數(shù)器

作用

用于保存JVM中下一條所要執(zhí)行的指令的地址

ps: 二進制字節(jié)碼(jvm指令)----》解釋器 來解釋成----》機器碼 ----》交給cpu來運行(cpu只認識機器碼)

特點

  • 線程私有
    -- CPU會為每個線程分配時間片呢燥,當當前線程的時間片使用完以后叛氨,CPU就會去執(zhí)行另一個線程中的代碼
    -- 程序計數(shù)器是每個線程私有的寞埠,當另一個線程的時間片用完仁连,又返回來執(zhí)行當前線程的代碼時,通過程序計數(shù)器可以知道應該執(zhí)行哪一句指令
  • 不會內存溢出

2使鹅、虛擬機棧

定義

  • 每個線程運行需要的內存空間患朱,稱為虛擬機棧
  • 每個棧由多個棧幀(Frame)組成裁厅,對應著每次調用方法時所占用的內存
  • 每個線程只能有一個活動棧幀姐直,對應著當前正在執(zhí)行的方法

演示

public class Main {
    public static void main(String[] args) {
        method1();
    }
    private static void method1() {
        method2(1, 2);
    }
    private static int method2(int a, int b) {
        int c = a + b;
        return c;
    }
}
截屏2021-09-18 21.16.57.png

在控制臺中可以看到,主類中的方法在進入虛擬機棧的時候姻成,符合棧的特點

問題辨析

  • 垃圾回收是否涉及棧內存科展?
    -- 不需要才睹。因為虛擬機棧中是由一個個棧幀組成的琅攘,在方法執(zhí)行完畢后坞琴,對應的棧幀就會被彈出棧。所以無需通過垃圾回收機制去回收內存寒亥。
  • 棧內存的分配越大越好嗎溉奕?
    -- 不是腐宋。因為物理內存是一定的胸竞,棧內存越大卫枝,可以支持更多的遞歸調用讹挎,但是可執(zhí)行的線程數(shù)就會越少筒溃。
  • 方法內的局部變量是否是線程安全的怜奖?
    -- 如果方法內局部變量沒有逃離方法的作用范圍,則是線程安全
    -- 如果如果局部變量引用了對象掷匠,并逃離了方法的作用范圍讹语,則需要考慮線程安全問題

棧內存溢出

Java.lang.stackOverflowError 棧內存溢出
使用 -Xss256k 指定棧內存大小顽决,配置在vm參數(shù)里才菠。

發(fā)生原因

  • 虛擬機棧中鸠儿,棧幀過多
    a.無限遞歸
    b.兩個對象的屬性互相依賴厕氨,當json解析這類對象時就會一直循環(huán)解析命斧。(對某屬性@JsonIgnore)
  • 每個棧幀所占用過大

線程運行診斷

案例一:cpu 占用過多

  • Linux環(huán)境下運行某些程序的時候国葬,可能導致CPU的占用過高汇四,這時需要定位占用CPU過高的線程
    1通孽、確定哪個進程占用CPU過高:
    ?命令:top
    2背苦、確定哪個線程占用CPU過高:
    ?命令:ps H -eo pid, tid, %cpu | grep 進程ID
    3行剂、定位具體代碼:
    ?命令:jstack 進程id
    ?通過查看進程中的線程的nid厚宰,剛才ps命令看到的tid來對比定位,注意jstack查找出的線程id是16進制的城菊,需要轉換。
    4漏麦、jstack 進程id 工具還可以定位死鎖信息撕贞。

3测垛、本地方法棧

一些帶有native關鍵字的方法就是需要JAVA去調用本地的C或者C++方法号涯,因為JAVA有時候沒法直接和操作系統(tǒng)底層交互锯七,所以需要用到本地方法

4眉尸、堆

定義

通過new關鍵字創(chuàng)建的對象都會被放在堆內存

特點

  • 所有線程共享噪猾,堆內存中的對象都需要考慮線程安全問題
  • 有垃圾回收機制

堆內存溢出

java.lang.OutofMemoryError :java heap space. 堆內存溢出
可以使用 -Xmx8m 來指定堆內存大小袱蜡。
調試程序時懷疑是堆內存溢出半夷,可以嘗試把堆內存調小一點試試迅细。

堆內存診斷

  • jps 工具
    -- 查看當前系統(tǒng)中有哪些 java 進程
  • jmap 工具
    -- 查看堆內存占用情況: jmap - heap 進程id
        System.out.println("1");
        Thread.sleep(30000);

        System.out.println("2");
        byte[] bytes = new byte[1024 * 1024 *10];
        Thread.sleep(10000);

        System.out.println(3);
        bytes = null;
        System.gc();
        Thread.sleep(10000);

-- java11環(huán)境:jhsdb jmap --heap --pid 922

  • jconsole 工具
    -- 圖形界面的湘换,多功能的監(jiān)測工具彩倚,可以連續(xù)監(jiān)測
  • jvirsualvm 工具
    --監(jiān)視--》堆dump (可dump下來當前時刻的堆信息)
    ----〉檢查--查找(指定大小堆)---》定位具體哪個對象占用空間比較大

5帆离、方法區(qū)

定義

JVM線程之間共享的方法區(qū)域哥谷。它存儲每個類的結構數(shù)據(jù)们妥,例如運行時常量池监婶、字段方法數(shù)據(jù),以及方法和構造函數(shù)的代碼,包括特殊方法孕似,用于類和實例初始化以及接口初始化方法區(qū)域是在虛擬機啟動時創(chuàng)建的喉祭。

  • 實現(xiàn):

永久代(1.8之前):存儲包括類信息泛烙、常量蔽氨、字符串常量鹉究、類靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)踪宠。

元空間(1.8以后):使用的是物理內存自赔,元空間是方法區(qū)的在 HotSpot JVM 中的實現(xiàn),方法區(qū)主要用于存儲類信息柳琢、常量池绍妨、方法數(shù)據(jù)润脸、方法代碼符號引用等毙驯。元空間的本質和永久代類似,都是對 JVM 規(guī)范中方法區(qū)的實現(xiàn)灾测。

方法區(qū)內存溢出

  • 1.8 之前會導致永久代內存溢出
    -- 使用 -XX:MaxPermSize=8m 指定永久代內存大小
  • 1.8 之后會導致元空間內存溢出
    -- 使用 -XX:MaxMetaspaceSize=8m 指定元空間大小

常量池

二進制字節(jié)碼的組成:類的基本信息爆价、常量池、類的方法定義(包含了虛擬機指令)

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello world!");
    }
}

使用 javap -v Hello.class 命令反編譯查看結果:

截屏2021-09-20 11.05.02.png

運行時常量池

  • 常量池
    -- 就是一張表(如上圖中的constant pool)行施,虛擬機指令根據(jù)這張常量表找到要執(zhí)行的類名允坚、方法名魂那、參數(shù)類型蛾号、字面量信息
  • 運行時常量池
    -- 常量池是.class文件中的,當該類被加載以后涯雅,它的常量池信息就會放入運行時常量池鲜结,并把里面的符號地址變?yōu)檎鎸嵉刂?/em>*

串池StringTable

  • 常量池中的字符串僅是符號,只有在被用到時才會轉化為對象
  • 利用串池的機制活逆,來避免重復創(chuàng)建字符串對象
  • 字符串變量拼接的原理是StringBuilder
  • 字符串常量拼接的原理是編譯器優(yōu)化
  • 可以使用intern方法精刷,主動將串池中還沒有的字符串對象放入串池中
  • 注意:無論是串池還是堆里面的字符串,都是對象
public class StringTableStudy {
    public static void main(String[] args) {
        String a = "a"; 
        String b = "b";
        String ab = "ab";
    }
}

編譯后的二進制中蔗候,常量池中的信息怒允,都會被加載到運行時常量池中,但這是a b ab 僅是常量池中的符號锈遥,還沒有成為java字符串

0: ldc           #2                  // String a
2: astore_1
3: ldc           #3                  // String b
5: astore_2
6: ldc           #4                  // String ab
8: astore_3
9: return

當執(zhí)行到 ldc #2 時纫事,會把符號 a 變?yōu)?“a” 字符串對象,并放入串池中(hashtable結構 不可擴容)

當執(zhí)行到 ldc #3 時所灸,會把符號 b 變?yōu)?“b” 字符串對象丽惶,并放入串池中

當執(zhí)行到 ldc #4 時,會把符號 ab 變?yōu)?“ab” 字符串對象爬立,并放入串池中

最終StringTable [“a”, “b”, “ab”]

注意:字符串對象的創(chuàng)建都是懶惰的钾唬,只有當運行到那一行字符串且在串池中不存在的時候(如 ldc #2)時,該字符串才會被創(chuàng)建并放入串池中侠驯。

  • 使用拼接字符串變量對象創(chuàng)建字符串的過程:
public class StringTableStudy {
    public static void main(String[] args) {
        String a = "a";
        String b = "b";
        String ab = "ab";
        //拼接字符串對象來創(chuàng)建新的字符串
        String ab2 = a+b; 
    }
}

反編譯后的結果:

     Code:
      stack=2, locals=5, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         8: astore_3
         9: new           #5                  // class java/lang/StringBuilder
        12: dup
        13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        16: aload_1
        17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String
;)Ljava/lang/StringBuilder;
        20: aload_2
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String
;)Ljava/lang/StringBuilder;
        24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/Str
ing;
        27: astore        4
        29: return

通過變量拼接的方式來創(chuàng)建字符串的過程是:StringBuilder().append(“a”).append(“b”).toString()

最后的toString方法的返回值是一個新的字符串抡秆,但字符串的值和拼接的字符串一致,但是兩個不同的字符串吟策,一個存在于串池之中儒士,一個存在于堆內存之中

String ab = "ab";
String ab2 = a+b;
//結果為false,因為ab是存在于串池之中,ab2是由StringBuffer的toString方法所返回的一個對象踊挠,存在于堆內存之中
System.out.println(ab == ab2);
  • 使用拼接字符串常量對象的方法創(chuàng)建字符串:
public class StringTableStudy {
    public static void main(String[] args) {
        String a = "a";
        String b = "b";
        String ab = "ab";
        String ab2 = a+b;
        //使用拼接字符串的方法創(chuàng)建字符串
        String ab3 = "a" + "b";
    }
}

反編譯后的結果:

      Code:
      stack=2, locals=6, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         8: astore_3
         9: new           #5                  // class java/lang/StringBuilder
        12: dup
        13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        16: aload_1
        17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String
;)Ljava/lang/StringBuilder;
        20: aload_2
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String
;)Ljava/lang/StringBuilder;
        24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/Str
ing;
        27: astore        4
        //ab3初始化時直接從串池中獲取字符串
        29: ldc           #4                  // String ab
        31: astore        5
        33: return
  • 使用拼接字符串常量的方法來創(chuàng)建新的字符串時乍桂,因為內容是常量冲杀,javac在編譯期會進行優(yōu)化,結果已在編譯期確定為ab睹酌,而創(chuàng)建ab的時候已經(jīng)在串池中放入了“ab”权谁,所以ab3直接從串池中獲取值,所以進行的操作和 ab = “ab” 一致憋沿。
  • 使用拼接字符串變量的方法來創(chuàng)建新的字符串時旺芽,因為內容是變量,只能在運行期確定它的值辐啄,所以需要使用StringBuilder來創(chuàng)建

intern方法 (jkd1.8)

一般采章,只有字面量才能放入串池中。

但壶辜,調用字符串對象的intern方法悯舟,會將該字符串對象嘗試放入到串池中

  • 如果串池中沒有該字符串對象,則放入成功
  • 如果有該字符串對象砸民,則放入失敗
    無論放入是否成功抵怎,都會返回串池中的字符串對象

注意:此時如果調用intern方法成功(放入串池成功),堆內存與串池中的字符串對象是同一個對象岭参;如果失敺刺琛(放入串池失敗,也就是串池中已經(jīng)存在)演侯,則不是同一個對象
例1

public class Main {
    public static void main(String[] args) {
        //"a" "b" 被放入串池中姿染,str則存在于堆內存之中
        String str = new String("a") + new String("b");
        //調用str的intern方法,這時串池中沒有"ab"秒际,則會將該字符串對象放入到串池中悬赏,此時堆內存與串池中的"ab"是同一個對象
        String st2 = str.intern();
        //給str3賦值,因為此時串池中已有"ab"程癌,則直接將串池中的內容返回
        String str3 = "ab";
        //因為堆內存與串池中的"ab"是同一個對象舷嗡,所以以下兩條語句打印的都為true
        System.out.println(str == st2);
        System.out.println(str == str3);
    }
}

例2

public class Main {
    public static void main(String[] args) {
        //此處創(chuàng)建字符串對象"ab",因為串池中還沒有"ab"嵌莉,所以將其放入串池中
        String str3 = "ab";
        //"a" "b" 被放入串池中进萄,str則存在于堆內存之中
        String str = new String("a") + new String("b");
        //此時因為在創(chuàng)建str3時,"ab"已存在與串池中锐峭,所以放入失敗中鼠,但是會返回串池中的"ab"
        String str2 = str.intern();
        //false
        System.out.println(str == str2);
        //false
        System.out.println(str == str3);
        //true
        System.out.println(str2 == str3);
    }
}

intern方法 (jkd1.6)

調用字符串對象的intern方法,會將該字符串對象嘗試放入到串池中

  • 如果串池中沒有該字符串對象沿癞,會將該字符串對象復制一份援雇,再放入到串池中
  • 如果有該字符串對象,則放入失敗
    無論放入是否成功椎扬,都會返回串池中的字符串對象

注意:此時無論調用intern方法成功與否惫搏,串池中的字符串對象和堆內存中的字符串對象都不是同一個對象

StringTable 的位置

jdk1.6 StringTable 位置是在永久代中具温,1.8 StringTable 位置是在堆中。

永久代中gc是full gc筐赔,堆內存中gc是minorr gc铣猩。

/**
 * 演示 StringTable 位置
 * 在jdk8下設置 -Xmx10m -XX:-UseGCOverheadLimit
 * 在jdk6下設置 -XX:MaxPermSize=10m
 */
public class Demo1_6 {

    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();
        int i = 0;
        try {
            for (int j = 0; j < 260000; j++) {
                list.add(String.valueOf(j).intern());
                I++;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(i);
        }
    }
}

通過不同版本jdk的StringTable內存不足時報錯信息:

jdk1.6:java.lang.OutOfMemoryError:PermGen space
jdk1.8:java.lang.OutOfMemoryError:GC overhead limit exceeded
或者:java.lang.OutOfMemoryError:Java heap space

StringTable 垃圾回收

StringTable在內存緊張時,會發(fā)生垃圾回收

-Xmx10m 指定堆內存大小
-XX:+PrintStringTableStatistics 打印字符串常量池信息
-XX:+PrintGCDetails
-verbose:gc 打印 gc 的次數(shù)茴丰,耗費時間等信息

【代碼演示】

/**
 * 演示 StringTable 垃圾回收
 * -Xmx10m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc
 */
public class Demo1_7 {
    public static void main(String[] args) throws InterruptedException {
        int i = 0;
        try {
            for (int j = 0; j < 100000; j++) { // j=100, j=10000
                String.valueOf(j).intern();
                I++;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(i);
        }
    }
}

分別看往StringTable里添加100和10000個字符串常量看串池常量數(shù)增加和gc現(xiàn)象达皿。

StringTable調優(yōu)

  • 因為StringTable是由HashTable實現(xiàn)的,所以可以適當增加HashTable桶的個數(shù)贿肩,來減少字符串放入串池所需要的時間
-XX:StringTableSize=桶個數(shù)(最少設置為 1009 以上)
/**
 * 演示串池大小對性能的影響
 * -Xms500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=1009
 */
public class Demo1_24 {
    public static void main(String[] args) throws IOException {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
            String line = null;
            long start = System.nanoTime();
            while (true) {
                line = reader.readLine();
                if (line == null) {
                    break;
                }
                line.intern();
            }
            System.out.println("cost:" + (System.nanoTime() - start) / 1000000);
        }
    }
}
  • 考慮是否需要將字符串對象入池
    可以通過 intern 方法減少重復入池
/**
 * 演示 intern 減少內存占用
 * -XX:StringTableSize=200000 -XX:+PrintStringTableStatistics
 * -Xsx500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=200000
 */
public class Demo1_25 {
    public static void main(String[] args) throws IOException {
        List<String> address = new ArrayList<>();
        System.in.read();
        for (int i = 0; i < 10; i++) {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
                String line = null;
                long start = System.nanoTime();
                while (true) {
                    line = reader.readLine();
                    if(line == null) {
                        break;
                    }
                    address.add(line.intern());
                }
                System.out.println("cost:" +(System.nanoTime()-start)/1000000);
            }
        }
        System.in.read();
    }
}

6峦椰、直接內存

  • 屬于操作系統(tǒng),常見于NIO操作時汰规,用于數(shù)據(jù)緩沖區(qū) --ByteBuffer
  • 分配回收成本較高汤功,但讀寫性能高
  • 不受JVM內存回收管理

文件讀寫流程:

使用了DirectBuffer:

直接內存是操作系統(tǒng)和Java代碼都可以訪問的一塊區(qū)域,無需將代碼從系統(tǒng)內存復制到Java堆內存控轿,從而提高了效率

釋放原理

直接內存的回收不是通過JVM的垃圾回收來釋放的冤竹,而是通過unsafe.freeMemory來手動釋放

通過
//通過ByteBuffer申請1M的直接內存
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1M);

申請直接內存,但JVM并不能回收直接內存中的內容茬射,它是如何實現(xiàn)回收的呢?

allocateDirect的實現(xiàn)

public static ByteBuffer allocateDirect(int capacity) {
    return new DirectByteBuffer(capacity);
}

DirectByteBuffer類

DirectByteBuffer(int cap) {   // package-private

    super(-1, 0, cap, cap);
    boolean pa = VM.isDirectMemoryPageAligned();
    int ps = Bits.pageSize();
    long size = Math.max(1L, (long)cap + (pa ? ps : 0));
    Bits.reserveMemory(size, cap);

    long base = 0;
    try {
        base = unsafe.allocateMemory(size); //申請內存
    } catch (OutOfMemoryError x) {
        Bits.unreserveMemory(size, cap);
        throw x;
    }
    unsafe.setMemory(base, size, (byte) 0);
    if (pa && (base % ps != 0)) {
        // Round up to page boundary
        address = base + ps - (base & (ps - 1));
    } else {
        address = base;
    }
    cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); //通過虛引用冒签,來實現(xiàn)直接內存的釋放在抛,this為虛引用的實際對象
    att = null;
}

這里調用了一個Cleaner的create方法,且后臺線程還會對虛引用的對象監(jiān)測萧恕,如果虛引用的實際對象(這里是DirectByteBuffer)被回收以后刚梭,就會調用Cleaner的clean方法,來清除直接內存中占用的內存

public void clean() {
       if (remove(this)) {
           try {
               this.thunk.run(); //調用run方法
           } catch (final Throwable var2) {
               AccessController.doPrivileged(new PrivilegedAction<Void>() {
                   public Void run() {
                       if (System.err != null) {
                           (new Error("Cleaner terminated abnormally", var2)).printStackTrace();
                       }

                       System.exit(1);
                       return null;
                   }
               });
           }

對應對象的run方法

public void run() {
    if (address == 0) {
        // Paranoia
        return;
    }
    unsafe.freeMemory(address); //釋放直接內存中占用的內存
    address = 0;
    Bits.unreserveMemory(size, capacity);
}

直接內存的回收機制總結

  • 使用了Unsafe類來完成直接內存的分配回收票唆,回收需要主動調用freeMemory方法
  • ByteBuffer的實現(xiàn)內部,使用了Cleaner(虛引用)來監(jiān)測ByteBuffer對象。一旦ByteBuffer被垃圾回收墓臭,那么會由ReferenceHandler來調用Cleaner的clean方法調用freeMemory來釋放內存森逮。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
禁止轉載,如需轉載請通過簡信或評論聯(lián)系作者簿煌。
  • 序言:七十年代末氮唯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子姨伟,更是在濱河造成了極大的恐慌惩琉,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夺荒,死亡現(xiàn)場離奇詭異瞒渠,居然都是意外死亡良蒸,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門伍玖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诚啃,“玉大人,你說我怎么就攤上這事私沮∈际辏” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵仔燕,是天一觀的道長造垛。 經(jīng)常有香客問我,道長晰搀,這世上最難降的妖魔是什么五辽? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮外恕,結果婚禮上杆逗,老公的妹妹穿的比我還像新娘。我一直安慰自己鳞疲,他們只是感情好罪郊,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著尚洽,像睡著了一般悔橄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上腺毫,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天癣疟,我揣著相機與錄音,去河邊找鬼潮酒。 笑死睛挚,一個胖子當著我的面吹牛,可吹牛的內容都是我干的急黎。 我是一名探鬼主播扎狱,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼叁熔!你這毒婦竟也來了委乌?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤荣回,失蹤者是張志新(化名)和其女友劉穎遭贸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體心软,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡壕吹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年著蛙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耳贬。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡踏堡,死狀恐怖,靈堂內的尸體忽然破棺而出咒劲,到底是詐尸還是另有隱情顷蟆,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布腐魂,位于F島的核電站帐偎,受9級特大地震影響,放射性物質發(fā)生泄漏蛔屹。R本人自食惡果不足惜削樊,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望兔毒。 院中可真熱鬧漫贞,春花似錦、人聲如沸育叁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽擂红。三九已至仪际,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昵骤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工肯适, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留变秦,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓框舔,卻偏偏與公主長得像蹦玫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子刘绣,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內容

  • jvm內存結構 1.程序計數(shù)器 1.1 定義 Program Counter Register 程序計數(shù)器(寄存器...
    韓who閱讀 376評論 0 0
  • 1 JVM 概述圖 2 內存結構 2.1 程序計數(shù)器 2.1.1 概述 程序計數(shù)器(寄存器):是記錄下一條jvm指...
    攻城老獅閱讀 278評論 0 1
  • Java虛擬機在執(zhí)行java程序的過程中會把它所管理的內存化分為若干個不同的數(shù)據(jù)區(qū)域樱溉。這些區(qū)域有各自的用途,以及創(chuàng)...
    lilykeke閱讀 146評論 0 0
  • java的內存結構(一) 大佬都講的巨詳細了纬凤,深度干貨啊福贞,沒啥要補充的了http://www.cnblogs.co...
    黃靠譜閱讀 1,220評論 0 0
  • 擱置爭議 系統(tǒng)性學一下JVM 原因:刷java題目的時候有很多JVM的題目,我之前完全就是靠經(jīng)驗或者蒙做出來的停士,知...
    黃先生hws閱讀 188評論 0 1