1. 程式人生 > >對Java記憶體的理解

對Java記憶體的理解

    理解了記憶體,就理解了一切!

    這是我之前看到一個視訊裡面老師經常說的一句話,在當初聽到這句話還沒有什麼感悟,只是當做一句很普通的感悟而已。一年多過去了,也算寫了一些程式碼,再回過頭來看JavaSE部分的知識時,才發現這句話的重要性。這簡直就是對javase最直白簡潔又富有深意的總結。

在理解記憶體之前我們需要知道的預備知識既基本資料型別引用資料型別,java程式在記憶體中的執行就是對這兩種資料型別的操作。如下圖所示:

                               

    java記憶體博大精深,如果仔細深究肯定夠我們學習好多天了。作為初學者,我們學習記憶體的目的是為了更好的理解程式碼背後的執行原理,寫出更加健壯且安全的程式碼

,所以我們只需要知道如下圖所示的記憶體區域,便可以讓我們達到目的。

    從上圖我們看到java虛擬機器給我們分配的記憶體區域有棧空間,堆空間和方法區。棧空間裡面主要存放基本資料型別和物件的引用;堆空間裡面主要存放new出來的物件;方法區中包括Class資訊、靜態變數和常量池,常量池中主要存放常量,如字串常量,基本包裝類常量等。

現在,以建立一個Student物件為例,先建立一個Student類,如下程式碼

package com.bjsxt.test;
/**
 * 定義一個Student類
 *
 */
public class Student {
	//靜態變數
	public static int i = 5;
	//屬性
	private int id;
	private String name;
	private char gender;
	//構造方法
	public Student() {}
	public Student(int id, String name, char gender) {
		this.id = id;
		this.name = name;
		this.gender = gender;
	}
	//get/set方法
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public char getGender(){
		return gender;
	}
	public void setGender(char gender) {
		this.gender = gender;
	}
	//生成toString方法
	public String toString() {
		return "Student[id="+id+",name="+name+",gender="+gender+"]";
	}
}

    再定義一個Test類,new一個Student物件

package com.bjsxt.test;
/**
 * 定義一個測試類,建立一個Student物件
 *
 */
public class Test {
	public static void main(String[] args) {
		//建立一個變數i
		int i = 12;
		//建立Student物件
		Student stu = new Student(101, "小明", '男');
	}
}

在上面的程式碼中,我們在Test類中定義了一個Student類的例項物件,然後再建立了一個區域性變數i那麼在記憶體中是什麼樣子的呢,請看下圖:

  從上圖中,我們可以看到:區域性變數儲存在棧空間中,stu在棧空間中儲存的是物件Student的引用地址值,既物件的引用也是儲存在棧空間中;而new出來的物件則儲存在堆空間中,注意Student類中定義了一個靜態變數i,它從屬於類,被所有Student例項物件共享,儲存在方法區中;因為Stirng型別的變數屬於字串常量,儲存在常量池中,所以name儲存的內容是字串"小明"的引用地址。

    通過上面的展示,大家是否對java對資料在記憶體中的儲存有了深刻的認識,感覺java記憶體是不是很神奇呢,簡單的建立一個變數或者new一個物件就會在記憶體中出現這麼多有趣的變化。如果大家感覺還是似懂非懂,那麼我們再通過下面的一個例子對java記憶體進行更深入的應用,從而更好的理解java記憶體的執行原理。

    首先看如下程式碼

package com.bjsxt.test;
/**
 * 定義一個測試類
 *
 */
public class Test2 {
	public static void main(String[] args) {
		//建立一個Student物件
		Student stu = new Student(101, "小明", '男');
		//建立一個字串
		String str = "hello";
		//呼叫change()方法
		change(stu, str);
		//列印str和stu
		System.out.println("str="+str+", stu="+stu);
	}
	//change方法
	public static void change(Student stu, String str) {
		str = "welcome";
		stu.setGender('女');
		stu = new Student(102, "小紅", '女');
	}
}

    最後列印的結果是 :str=hello, stu=Student[id=101,name=小明,gender=女]

    看到這個結果,大家是否很驚訝,str被賦予了一個新的字串常量,應該是"welcome"才對吧,怎麼還是"hello",又或者stu應該是新賦予的物件,怎麼還是原來這個物件呢。那麼接下來,我們再通過記憶體圖畫一遍程式的執行過程,就能洞悉其中的原理。

    程式的執行過程如下圖所示:

    1.在main方法中未執行change()方法之前的圖片

    2.在main方法中執行change()方法時

    在上圖中我們可以看到在呼叫change()方法中的執行過程,這個過程可以這樣解釋:

    1.main方法中的變數stu把物件Student地址賦值給了chang方法中的變數stu,變數str把字串"hello"的地址賦值給了change方法中的變數str;

    2.根據str="welcome"這行程式碼,str的指向地址由原來的0x12變成了0x22,指向常量池中的字串"welcome";

    3.根據stu.setGender('女')這行程式碼,stu指向的Student物件修改了變數gender由'男'變成了'女';

    4.根據stu = new Student(102, "小紅", '女')這行程式碼,stu指向的物件的地址由0x101變成了0x102

    5.根據stu.i = 6這行程式碼,因為靜態成員變數從屬於類任何Student的例項物件都可以共享該變數,所以change方法中的變數stu指向的Student例項物件也可以對靜態變數i進行修改變成了6。

    change方法執行完畢之後,進入main方法繼續執行,接下來的記憶體就變成了如下圖所示的情況:

    change方法執行完畢,changge方法中的變數stu和str都會消失,new出來的0x102指向的物件沒有被任何變數引用,雖然不會馬上消失,但是也會被java垃圾回收器回收,最後的記憶體在main方法中就變成了上面這個樣子,於是打印出來的結果就是我們上面看到的結果。

    這就是比較典型的java程式記憶體的執行過程,作為一名程式設計師,這也是我們需要掌握的基本技能,使我們寫程式碼、理解程式碼的的有效手段。當然,上面的知識點只是拋磚引玉,java記憶體的知識博大精深,遠不是現在看到的這樣簡單,大家如果對java虛擬機器中記憶體的分配有更多的興趣,可以到網上找找相關資料學習,也可以買java虛擬機器的相關書籍學習,比如《深入理解java虛擬機器》等書籍。

相關推薦

談談我JAVA記憶體可見性的理解 JAVA

首先要明確一點,每個執行緒都有屬於自己的工作記憶體。 出了執行緒自己擁有的工作記憶體外,還有公共記憶體。 假設我們有一個變數i,然後我們啟動兩個執行緒,這個時候i就會被拷貝成兩份副本分別給兩個執行緒的工作記憶體。 然後,這兩個執行緒如果對i進行操作,系統首先會將改變後的i先寫到執行緒的工

聊聊我Java記憶體模型的理解

所有的程式語言中都有記憶體模型這個概念,區別於微架構的記憶體模型,高階語言的記憶體模型包括了編譯器和微架構兩部分。我試圖瞭解了Java、C#和Go語言的記憶體模型,發現內容基本大同小異,只是這些語言在具體實現的時候略有不同。 我們來看看Java記憶體模型吧,提到Java記憶體模型大家對這個圖一

Java記憶體理解

    理解了記憶體,就理解了一切!     這是我之前看到一個視訊裡面老師經常說的一句話,在當初聽到這句話還沒有什麼感悟,只是當做一句很普通的感悟而已。一年多過去了,也算寫了一些程式碼,再回過頭來看JavaSE部分的知識時,才發現這句話的重要性。這簡直就是對javase最

談談java理解

java基礎1、java一次編譯,到處執行----跨平臺2、gc3、jre和jdk 區別: jre(java runtime environment)java運行環境,包含jvm的client,類庫(rt.jar:常用的集合、io、math、lang等都在rt.jar包內) jdk(java de

java記憶體理解

一、Java記憶體的構成   先貼原圖  如圖可知:整塊區域分為Young Generation、Tenured Generation、Permanent Generation。(年輕代→老年代→永久代 ) 咱們看下Young區:     Young區又分為

JAVA理解

面向物件程式設計 java是一門純粹的面向物件的語言。 面向物件這種程式設計模式它將現實世界中的一切事物都看作是物件,例如,一個人是一個物件,汽車、飛機、小鳥等等,都是物件;它強調從物件出發,以物件為中心用人類的思維方式來認識和思考問題。每個物件

stm32記憶體理解,檢視以及面試題的一些總結(一)

基礎知識儲備 1、keil中如何調用出map檔案,以及map檔案的作用 我是拿正點原子的標準工程來使用分析。 如何獲得map檔案, 雙擊工程名即可得到map檔案。 2、map檔案包含哪些東西 1.Section CrossReferences:模組、段(入口)

java序列化象簡單理解

debug .html 由於 info 傳遞對象 訪問 found 枚舉類型 args 1. 什麽是Java對象序列化   Java平臺允許我們在內存中創建可復用的Java對象,但一般情況下,只有當JVM處於運行時,這些對象才可能存在,即,這些對象的生命周期不會比JVM的生

java中面向象的理解

log () 屬性 順序執行 pub 類的方法 調用 一個 相對 面對對象就是: 把數據及對數據的操作方法放在一起,作為一個相互依存的整體——對象。對同類對象抽象出其共性,形成類。類中的大多數數據,只能用本類的方法進行處理。類通過一個簡單的外部接口與外界發生關系,對象與對象

Java代理模式的理解

java proxy 代理 反射 Java的代理分為靜態代理和動態代理。靜態代理模式的構成:1.一個共同的接口或抽象類 2.真實的類 3.代理類其中真實類和代理類都實現了那個共同的接口,代理類內部有一個對真實類對象的引用,用戶使用代理類時,實際會調用真實對象的對應方法。靜態代理的局限在於 1.

Java Serializable(序列化)的理解和總結

編碼 多種方法 light 定制 http 學習 功能 垃圾回收 對象序列化保存 1、序列化是幹什麽的? 簡單說就是為了保存在內存中的各種對象的狀態(也就是實例變量,不是方法),並且可以把保存的對象狀態再讀出來。雖然你可以用你自己的各種各樣的方法來保存objec

JAVA面向象思想理解分析

境界 吃飯 定義類 分析 標簽 消失 ava 棧內存 靜態方法 1.面向對象是面向過程而言.兩者都是一種思想。面向過程:強調的是功能行為。(強調過程、動作)面向對象:將功能封裝進對象,強調了具備了功能的對象。(強調對象、事物)面向對象是基於面向過程的。將復雜的事情變簡單了

如何理解IEEE 754標準Java中float值和double值的規定

rac tro zh-cn 分享圖片 編號 如何 ins font 指數 在Java語言中,我們可以使用float和double這兩種基本數據類型來表示特定的數據。 這兩種數據類型,本質上是浮點數(floating-point number),浮點是一種對於實數的近似值數值

java前後端分離的理解

成功 web服務 json數據 一個人 pri dubbo 權重 面向切面編程 docker 到目前為止,身為一個java後端開發人員的我, 在工作期間,無非就是ui設計頁面,前端開發html,之後將做好的頁面交給我,我負責後臺邏輯一件html的頁面渲染。 好好滴一個後臺開

Java象的認識與理解

創建類型 鞏固 當我 com 編寫 回收 jpg 回收機制 span   今天是我學習編程以來第一次寫博客,記下平日學習所得,本來這幾日都在學習web框架 但覺得梳理一下之前所學很有必要。畢竟之前學習Java感覺很粗略只是以考試為目的。所以就以《Thinking in

Java單例模式 volatile關鍵字作用的理解

初始 urn class .com 重新 on() 內存空間 sta 兩個   單例模式是程序設計中經常用到的,簡單便捷的設計模式,也是很多程序猿對設計模式入門的第一節課。其中最經典的一種寫法是: class Singleton { private volatil

讀薄《深入理解 JAVA 虛擬機器》Java記憶體分配策略

記憶體分配規則不是固定的,取決於當前使用的是哪一種垃圾收集器以及虛擬機器配置。 物件優先在 Eden 上分配 大多數情況下,物件分配在 Eden 上,當記憶體不足的時候觸發一次 Minor GC。 大物件分配進老年代 需要連續記憶體空間的物件,最典型的是很長的字串已經陣列,寫程式的時候應該避免生命週期

讀薄《深入理解 JAVA 虛擬機器》Java記憶體區域

很早之前看了《深入理解 JAVA 虛擬機器》並寫下了讀書筆記。最近在結合一些其他資料整理部落格。希望能幫助到其他人抓住書的重點。 Java執行時資料區域 Java執行時資料區域 白色為執行緒獨佔的,灰色為執行緒共享的。 Java在執行的時候會把他所管理的記憶體劃分為若干區域,經常有人把記憶體區域分為

java的淺拷貝和深拷貝的理解

參考網址:http://www.cnblogs.com/chenssy/p/3308489.html 、https://blog.csdn.net/chenssy/article/details/12952063 淺拷貝:使用一個已知例項對新建立例項的成員變數逐個賦值,這個方式被稱為淺

《深入理解 Java 記憶體模型》讀書筆記(下)(乾貨,萬字長文)

0. 前提 1. 基礎 2. 重排序 3. 順序一致性 4. Volatile 5. 鎖 6. final 7. 總結 4. Volatile 4.1 VOLATILE 特性 舉個例子: publ