Java?對象在?JVM?中的內(nèi)存布局超詳細(xì)解說
一、new 對象的幾種說法
初學(xué) Java 面向?qū)ο蟮臅r候,實(shí)例化對象的說法有很多種,我老是被這些說法給弄暈。
public class Test {
public static void main(String[] args) {
// 創(chuàng)建一個 ProgramLanguage 對象, 對象名是 java
ProgramLanguage java = new ProgramLanguage();
// 實(shí)例化一個 ProgramLanguage 對象, 對象名是 c
ProgramLanguage c = new ProgramLanguage();
// 把 ProgramLanguage 類實(shí)例化, 實(shí)例化后的對象的對象名是 python
ProgramLanguage python = new ProgramLanguage();
}
}
class ProgramLanguage {
private Integer id;
private String name;
}下面的三種說法的操作都是實(shí)例化對象,只是說法不一樣而已
- ① 創(chuàng)建一個 xxx 對象
- ② 實(shí)例化一個 xxx 對象
- ③ 把 xxx 類實(shí)例化
二、Java 對象在內(nèi)存中的存在形式
這里先簡單看一看 Java 對象在內(nèi)存中的存在形式和幾個內(nèi)存相關(guān)的概念,后面還會詳細(xì)介紹的。先看下面的幾個知識點(diǎn):
1. 棧幀(Frame)
- ① 方法被調(diào)用則棧幀創(chuàng)建,方法執(zhí)行結(jié)束則棧幀銷毀
- ② 棧幀中存儲了方法的局部變量信息
- ③ 棧幀是分配給方法的一段??臻g

- main 方法作為程序的入口肯定是第一個被調(diào)用的方法,所以會先創(chuàng)建 main 方法的棧幀
- 在 main 方法中調(diào)用了 test1 方法,并傳入【55】作為參數(shù)給 test1 方法的局部變量 v,所以第二個創(chuàng)建的是 test1 方法的棧幀
- test1 方法中的代碼很快就執(zhí)行完了,所以 test1 的棧幀很快會被銷毀(方法執(zhí)行結(jié)束后該方法的棧幀銷毀)
- 在 main 方法中調(diào)用了 test2 方法,并傳入【88】作為參數(shù)給 test2 方法的局部變量 v,所以第三個創(chuàng)建的是 test2 方法的棧幀
- 在 test2 方法中調(diào)用了 test3 方法,并傳入【666】作為參數(shù)給 test3 方法的局部變量 v,所以第四個創(chuàng)建的是 test3 方法的棧幀
- 當(dāng) test3 方法執(zhí)行完畢后,test3 方法的棧幀被銷毀
- test3 方法的結(jié)束也正是 test2 方法的結(jié)束,所以 test2 方法的棧幀也被銷毀
- test2 方法的結(jié)束表示 main 方法的結(jié)束,所以 main 方法的棧幀會被銷毀
2. 對象在內(nèi)存中的存在形式 ①
- Java 中的所有對象都是通過
new關(guān)鍵字創(chuàng)建出來的(new 關(guān)鍵字:實(shí)例化一個對象;向堆空間申請一段內(nèi)存,用于存放剛剛實(shí)例化的對象) - 所有的對象都存儲在堆空間
- 所有保存對象的變量都是引用類型
- 局部變量是放在棧空間
- Java 運(yùn)行時環(huán)境中有個垃圾回收器(garbage collector),會自動回收沒有被使用的(堆空間的)內(nèi)存
- 當(dāng)一個對象沒有被任何引用指向的時候,會被 GC 回收掉內(nèi)存
分析下面的代碼的內(nèi)存布局:
public class DogDemo {
public static void main(String[] args) {
Dog doggy = new Dog();
doggy.age = 6;
doggy.weight = 3.14;
}
}
- main 方法被調(diào)用,會在??臻g創(chuàng)建 main 方法的棧幀,main 方法的棧幀中會存放 main 方法中的局部變量信息(包括 args 和 main 方法中對象的引用 doggy)
- 在 main 方法中,通過
new關(guān)鍵字實(shí)例化了 Dog 對象,Dog 對象存儲在堆空間 - 堆空間中有一段內(nèi)存用于存儲類的對象的數(shù)據(jù),這段內(nèi)存中存放了 Dog 對象的屬性信息(如 age、weight)
- 棧空間中的 doggy 變量代表堆空間中的對象的地址(通過地址可以訪問對象)
分析下面的代碼的內(nèi)存布局(稍微復(fù)雜)
public class Dog {
public int price;
}public class Person {
public int age;
public Dog dog;
}public class Test {
public static void main(String[] args) {
Dog doggy = new Dog();
doggy.price = 255;
Person yeTing = new Person();
yeTing.age = 20;
yeTing.dog = doggy;
}
}
- main 方法被調(diào)用,會在??臻g創(chuàng)建 main 方法的棧幀,main 方法的棧幀中會存放 main 方法中的局部變量信息(包括 args、main 方法中對象的引用 doggy、對象的引用 yeTing)
- 在 main 方法中,通過
new關(guān)鍵字實(shí)例化了 Dog 對象,Dog 對象存儲在堆空間。堆空間中有一段內(nèi)存用于存儲 Dog 對象的屬性信息(如 price = 255) - 在 main 方法中,通過
new關(guān)鍵字實(shí)例化了 Person 對象,Person 對象存儲在堆空間。堆空間中有一段內(nèi)存用于存儲 Person 對象的屬性信息(如 age = 20),堆空間中,Person 對象的屬性 dog 是 Dog 對象的引用,所以它指向的是堆空間中的 Dog 對象(dog 指向的是??臻g中的 doggy 引用的堆空間的 Dog 對象。doggy 和 yeTing 指向的 Person 對象中的 dog 屬性指向的是同一個對象) - 引用變量不一定是在棧空間(也可能是在堆空間,如上圖中 yeTing 指向的 Person 對象中 dog,這個 dog 就是引用變量。但是,它是在堆空間。)
- 引用變量指向?qū)ο髮?shí)際上保存的是對象在堆空間中的地址值(如:doggy 保存的是 Dog 對象在堆空間的地址值、yeTing 保存的是 Person 對象在堆空間的地址值)
3. 對象中的方法存儲在那兒?
看下面的代碼,思考對象中的方法存儲在那兒?
public class Dog {
public int price;
public void run() {
System.out.println(price + "_" + "run()");
}
public void eat() {
System.out.println(price + "_" + "eat()");
}
}public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog();
dog1.price = 255;
dog1.run();
dog1.eat();
Dog dog2 = new Dog();
dog2.price = 552;
dog2.run();
dog2.eat();
}
}Java 虛擬機(jī)執(zhí)行 Java 程序時會把內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域,主要包括:
- ① PC 寄存器(Program Counter Register):存儲 Java 虛擬機(jī)正在執(zhí)行的字節(jié)碼指令的地址
- ② Java 虛擬機(jī)棧(Java Virtual Machine Stack):存儲 Java 方法的棧幀(① 方法被調(diào)用的時候會在棧空間創(chuàng)建該方法的棧幀,該方法執(zhí)行完畢后,該方法對應(yīng)的棧幀會銷毀;② 棧幀中會存放方法中的局部變量信息)【??臻g】
- ③ 堆空間(Heap):存儲被 GC(垃圾回收器) 所管理的各種對象(GC 管理的是通過 new 關(guān)鍵字創(chuàng)建的對象)
- ④ 方法區(qū)(Method Area):存儲每個類的結(jié)構(gòu)信息(如:字段和方法信息、構(gòu)造方法和普通方法的字節(jié)碼信息)
- ⑤ 本地方法棧(Native Method Stack):用來支持 native 方法的調(diào)用(如:用 C 語言編寫的方法)
4. Java 對象在內(nèi)存中的存在形式 ②
String:
- 是字符串,在 Java 編程中被頻繁地使用,但它是引用類型
- Java 中雙引號包裹的內(nèi)容默認(rèn)就是字符串類型
- Java 中被雙引號包裹的內(nèi)容叫做字符串常量
- 字符串常量存儲在字符串常量池中(String Constant Pool)
- jdk1.7 之前,字符串常量池在方法區(qū);后來被移動到了堆空間。所以,jdk1.8的字符串常量存儲在堆空間的字符串常量池中
- 后面學(xué)習(xí) String 的時候還會細(xì)說
分析下面代碼的內(nèi)存布局:
public class Dog {
String name;
int age;
String color;
}public class DogDemo {
public static void main(String[] args) {
Dog doggy = new Dog();
doggy.name = "笑天";
doggy.age = 6;
doggy.color = "黑";
}
}
三、類中屬性詳細(xì)說明
- 現(xiàn)實(shí)世界中的對象有狀態(tài)(State)和行為(Behavior),面向編程中的對象有屬性(Field)和方法(Method)。
- 類是創(chuàng)建單個對象的藍(lán)圖(模板)

下面詳細(xì)說明一下類中【屬性】這個概念。其實(shí)上篇文章已經(jīng)能夠很好理解,這里只是再補(bǔ)充一下而已。
- 屬性、成員變量、字段(field)指的是同一個東西(即一個類的狀態(tài))
習(xí)慣上把現(xiàn)實(shí)世界的對象的狀態(tài)(State)和編程中的屬性聯(lián)系在一起,便于理解 - 屬性可以是基本數(shù)據(jù)類型或引用類型(自定義類,接口,數(shù)組 …)
- 定義屬性的語法:訪問修飾符 + 屬性類型(eg: String、int、Dog、Bicycle) + 屬性名
- 訪問修飾符(控制屬性被訪問的范圍)有四種:public、protected、默認(rèn)(不寫)、private【后面會詳細(xì)說】
/**
* 訪問修飾符有四種:public、protected、默認(rèn)(不寫)、private
*/
public class Dog {
public String name;
protected int age;
String color;
private double weight;
}如果不給對象的屬性賦值,屬性會有初始值
/**
* 測試若不給對象的屬性賦初始值, 它們的默認(rèn)初始值
*/
public class FiledInitialValueTest {
private int score;
private short age;
private byte month;
private long salary;
private float height;
private double pi;
private char firstName;
private boolean isTrueLove;
private Person person;
public static void main(String[] args) {
FiledInitialValueTest test = new FiledInitialValueTest();
System.out.println("\n若不給對象的屬性賦值, 初始值如下所示:");
System.out.println("score【int】 = " + test.score);
System.out.println("age【short】 = " + test.age);
System.out.println("month【byte】 = " + test.month);
System.out.println("salary【long】 = " + test.salary);
System.out.println("height【float】 = " + test.height);
System.out.println("pi【double】 = " + test.pi);
// 字符類型的屬性的初始值是空串(在控制臺無法看到)
System.out.println("firstName【char】 = " + test.firstName);
// 字符類型的屬性的初始值強(qiáng)制類型轉(zhuǎn)換為 int 后是【0】
System.out.println("firstName【(int)char】 = " + (int) test.firstName);
System.out.println("isTrueLove【boolean】 = " + test.isTrueLove);
System.out.println("person【person】 = " + test.person);
}
}
四、細(xì)小知識點(diǎn)
1. 如何創(chuàng)建對象
必須先有類(模板)才能創(chuàng)建對象
通過【new】關(guān)鍵字創(chuàng)建類的對象?!緉ew】:向堆空間申請一塊內(nèi)存存儲對象數(shù)據(jù)
public class TestCreateObject {
public static void main(String[] args) {
// (1) 先聲明再創(chuàng)建
Dog dog; // 聲明
dog = new Dog(); // 通過 new 關(guān)鍵字創(chuàng)建對象
// (2) 聲明并創(chuàng)建對象
Dog doggy = new Dog();
}
}2. 如何訪問屬性
可通過【.】號訪問屬性或調(diào)用方法
可把 . 看做【的】、【の】

五、Exercise
看代碼,畫圖:
public class Person {
private int age;
private String name;
public static void main(String[] args) {
Person yangJiaLi = new Person();
yangJiaLi.age = 17;
yangJiaLi.name = "楊嘉立";
// 下面的一行代碼有2種說法:
// 1. 把 yangJiaLi 賦值給 yjl
// 2. yjl 指向 yangJiaLi
Person yjl = yangJiaLi;
System.out.println(yjl.age); // 17
}
}
六、總結(jié)
本篇文章的重點(diǎn)是第二節(jié)【Java 對象在內(nèi)存中的存在形式】
需重點(diǎn)知道:

到此這篇關(guān)于Java 對象在 JVM 中的內(nèi)存布局超詳細(xì)解說的文章就介紹到這了,更多相關(guān)Java JVM 內(nèi)存布局內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java正則表達(dá)式獲取指定HTML標(biāo)簽的指定屬性值且替換的方法
下面小編就為大家?guī)硪黄猨ava正則表達(dá)式獲取指定HTML標(biāo)簽的指定屬性值且替換的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-12-12
Spring的事件發(fā)布與監(jiān)聽方式案例講解
今天去官網(wǎng)查看spring?boot資料時,在特性中看見了系統(tǒng)的事件及監(jiān)聽章節(jié),所以下面這篇文章主要給大家介紹了關(guān)于SpringBoot事件發(fā)布和監(jiān)聽的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03
SpringBoot中的@ResponseStatus注解處理異常狀態(tài)碼
這篇文章主要介紹了SpringBoot中的@ResponseStatus注解處理異常狀態(tài)碼,在?SpringBoot?應(yīng)用程序中,異常處理是一個非常重要的話題。當(dāng)應(yīng)用程序出現(xiàn)異常時,我們需要對異常進(jìn)行處理,以保證應(yīng)用程序的穩(wěn)定性和可靠性,需要的朋友可以參考下2023-08-08
FeignClient如何通過配置變量調(diào)用配置文件url
這篇文章主要介紹了FeignClient如何通過配置變量調(diào)用配置文件url,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
IDEA中Maven依賴包無法下載或?qū)氲慕鉀Q方案(系統(tǒng)缺失文件導(dǎo)致)
在配置Maven環(huán)境時,可能會遇到各種報錯問題,首先確保Maven路徑配置正確,例如使用apache-maven-3.5.0版本,則需要在系統(tǒng)環(huán)境變量的Path中添加其bin目錄路徑,并上移優(yōu)先級,接下來,在Maven的conf目錄下修改settings.xml文件,將鏡像源改為阿里云2024-09-09
?Java圖形化界面編程實(shí)現(xiàn)簡單計(jì)算器
這篇文章主要介紹了Java圖形化界面編程實(shí)現(xiàn)簡單計(jì)算器,下面文章圍繞Java圖形化界面編程實(shí)現(xiàn)簡單計(jì)算器的相關(guān)資料展開詳細(xì)內(nèi)容,具有一定的參考價值,需要的小伙伴可以參考一下2022-01-01

