日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java内存模型 创建类_JVM内存模型及String对象内存分配

發布時間:2023/11/30 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java内存模型 创建类_JVM内存模型及String对象内存分配 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

昨天看了一篇關于《Java后端程序員1年工作經驗總結》的文章,其中有一段關于String和StringBuffer的描述,對于執行結果仍然把握不準,趁此機會也總結了下JVM內存模型。

1、JVM運行時數據區域

關于JVM內存模型之前也了解過一些,也是看過就忘,好記性比如爛筆頭,記下來吧。參考此文章http://chenzhou123520.iteye.com/blog/1585224

圖1 JVM運行時數據區域

(1)、程序計數器(Program Counter Register):

程序計數器是一塊較小的內存空間,可以看做是當前線程所執行的字節碼的行號指示器。

由于java虛擬機的多線程是通過線程輪流切換并分配處理器執行時間的方式來實現的,在任何一個確定的時刻,一個處理器(對于多核處理器來說是一個內核)都只會執行一條線程中的指令。因此為了線程切換后能恢復到正確的執行位置,每個線程都需要有一個獨立的程序計數器,各條線程之間計數器互不影響,獨立存儲,是‘線程私有’的內存。

(2)、JAVA虛擬機棧(Java Virtual Machine Stack):

與程序計數器一樣,java虛擬機棧也是線程私有的,虛擬機棧描述的是Java方法執行的內存模型:在執行的同時會創建一個棧幀用于存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每一個方法從調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程。

局部變量表存放了編譯期可知的各種基本數據類型()、對象引用和returnAddress類型(指向了一條字節碼指令的地址)

局部變量表所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。

JVM Stack 異常情況:

StackOverflowError:當線程請求分配的棧容量超過JVM允許的最大容量時拋出

OutOfMemoryError:如果JVM Stack可以動態擴展,但是在嘗試擴展時無法申請到足夠的內存去完成擴展,或者在建立新的線程時沒有足夠的內存去創建對應的虛擬機棧時拋出

(3)、本地方法棧(Native Method Stack):

本地方法棧與虛擬機棧所發揮的作用是非常相似,區別不過是虛擬機棧為虛擬機執行java方法(也就是字節碼)服務,而本地方法棧則為虛擬機使用到的Native方法(使用Java語言以外的其它語言編寫的方法)服務。

本地方法棧也可以拋出StackOverflowError和OutOfMemoryError異常

(4)、JAVA堆(Java Heap):

虛擬機管理的內存中最大的一塊,同時也是被所有線程所共享的,它在虛擬機啟動時創建,此內存區域存在的唯一目的就是存放對象實例,幾乎所有的對象實例以及數組都要在這里分配內存。這里面的對象被自動管理,也就是俗稱的GC(Garbage Collector)所管理。用就是了,有GC扛著呢,不用操心銷毀回收的事兒。

Java堆的容量可以是固定大小,也可以隨著需求動態擴展(-Xms和-Xmx),并在不需要過多空間時自動收縮。

Java堆所使用的內存不需要保證是物理連續的,只要邏輯上是連續的即可。

JVM實現應當提供給程序員調節Java 堆初始容量的手段,對于可動態擴展和收縮的堆來說,則應當提供調節其最大和最小容量的手段。

如果堆中沒有內存完成實例分配并且堆也無法擴展,就會拋OutOfMemoryError。

(5)、方法區(Method Area):

跟堆一樣是被各個線程共享的內存區域,用于存儲以被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。雖然這個區域被虛擬機規范把方法區描述為堆的一個邏輯部分,但是它的別名叫非堆,用來與堆做一下區別。

(6)、運行時常量池(Runtime Constant Pool):

運行時常量池是方法區一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用于存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載后進入方法區的運行時常量池中存放。

2、String對象內存分配分析

先看以下代碼,運行后,結果如代碼1,2,3,4,5,6行所示

[java] view plain copy

package com.xtli.controller;

public class StringTest {

public static void main(String[] args) {

String s1 = "hello";

String s2 = "world";

System.out.println(s1+"---"+s2);//1:hello---world

change(s1,s2);

System.out.println(s1+"---"+s2);//3:hello---world

StringBuffer sb1 = new StringBuffer("hello");

StringBuffer sb2 = new StringBuffer("world");

System.out.println(sb1+"---"+sb2);//4:hello---world

change(sb1,sb2);

System.out.println(sb1+"---"+sb2);//6:hello---worldworld

}

public static void change(String s1, String s2) {

s1 = s2;

s2 = s1+s2;

System.out.println("change(s1,s2)---"+s1+"---"+s2);//2:change(s1,s2)---world---worldworld

}

public static void change(StringBuffer sb1, StringBuffer sb2) {

sb1 = sb2;

sb2.append(sb1);

System.out.println("change(sb1,sb2)---"+sb1+"---"+sb2);//5:change(sb1,sb2)---worldworld---worldworld

}

}

對以上代碼進行分析說明,如下

[java] view plain copy

public class StringTest {

public static void main(String[] args) {

//在main方法的棧中創建引用s1和引用s2,此引用s1和引用s2存放在棧(main方法的棧)中;編譯時,在常量池中創建兩個常量"hello"和"world",s1和s2分別

//指向兩個常量

String s1 = "hello";

String s2 = "world";

System.out.println(s1+"---"+s2);//1:hello---world

change(s1,s2);//引用s1和s2作為參數傳遞到change方法中

//change方法中的引用s1,s2和main方法中的引用s1,s2存放地址并不同,以下輸出的是main方法棧中的s1和s2,并沒有發生變化,故代碼3有以下輸出

System.out.println(s1+"---"+s2);//3:hello---world

//以下兩行代碼將會在main方法棧中創建引用sb1和sb2,并在堆內存中創建兩個對象"hello"和"world",sb1和sb2分別指向兩個對象

StringBuffer sb1 = new StringBuffer("hello");

StringBuffer sb2 = new StringBuffer("world");

System.out.println(sb1+"---"+sb2);//4:hello---world

change(sb1,sb2);//引用sb1和sb2作為參數傳遞到change方法中

//main方法中的sb1所指向的堆內存地址未發生變化,故仍為"hello",而change(sb1,sb2)方法改變了main方法中sb2所指向的堆內存地址的內容,故代碼6有以下輸出

System.out.println(sb1+"---"+sb2);//6:hello---worldworld

}

public static void change(String s1, String s2) {//在change方法的棧中創建引用s1和s2,并指向常量池中的常量

s1 = s2;//將引用s1指向s2的常量池中的"world"

s2 = s1+s2;//在堆內存中創建"worldworld"對象,并將s2指向此堆內存地址

System.out.println("change(s1,s2)---"+s1+"---"+s2);//2:change(s1,s2)---world---worldworld

}

public static void change(StringBuffer sb1, StringBuffer sb2) {//在change方法的棧(和上面的change方法棧不同)中創建引用sb1和sb2,并指向main方法棧中sb1和sb2所指向的對象

sb1 = sb2;//將引用sb1指向sb2所引用的對象"world"

sb2.append(sb1);//引用sb2所指向的對象發生變化,變為"worldworld",注意此時外部main方法中的sb2和此方法中的sb1均指向此堆內存地址,

//此地址內容發生變化后,外部main方法中的sb2指向的內容也跟著變化

System.out.println("change(sb1,sb2)---"+sb1+"---"+sb2);//5:change(sb1,sb2)---worldworld---worldworld

}

}

為了進一步說明change(String s1, String s2)中的結果,可以進行以下驗證。

[java] view plain copy

public static void change(String s1, String s2) {

String s= "world";

String ss= "worldworld";

s1 = s2;

System.out.println(s==s1);//輸出true

s2 = s1+s2;

System.out.println(ss==s2);//輸出false

System.out.println("change(s1,s2)---"+s1+"---"+s2);//2:change(s1,s2)---world---worldworld

}

故在change(String s1, String s2)方法中s1=s2后,s1所指向的是常量池中的"world",s2=s1+s2代碼執行后,會在堆內存中重新創建對象,并將s2指向此堆內存地址。

以上均為個人總結,如有不正確之處,請指出,大家共同進步。轉載請注明出處。

本文出自BiggerLee的博客http://blog.csdn.net/lixingtao0520

總結

以上是生活随笔為你收集整理的java内存模型 创建类_JVM内存模型及String对象内存分配的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。