java方法調用機制_Java方法調用機制 - osc_bkdv2it5的個人空間 - OSCHINA - 中文開源技術交流社區...

最近在編程時,修改方法傳入對象的對象引用,并沒有將修改反映到調用方法中。奇怪為什么結果沒有變化,原因是遺忘了Java對象引用和內存分配機制。本文介紹3個點:

① 該問題舉例說明

② 簡要闡述Java內存區域

③ 介紹JVM中方法調用的機制

1. Java方法調用傳參實例解析

Java中參數傳遞是值傳遞,即調用方法時,所有參數的傳遞都是值傳遞。基本類型直接將值拷貝給方法參數,引用類型將引用地址拷貝給方法參數。先看兩個String類型和對象引用的實例。

(1)字符串對象引用

public static voidmain(String[] args) {

String a = "123";

app(a);

System.out.println(a);

}

private static voidapp(String a) {

//String不可修改,只會重新創建,故main中a不變

a += "456"; }

輸出:123

分析:結果并沒有因為調用了app方法,而輸出123456。如注釋中描述,String(由字符數組實現)是不可修改的,所有的修改都會重新創建新的String對象,并且字符串拼接也會重新創建String對象(具體見下文)。也就是說app中的a字符串引用已不再指向main中a指向的內存塊,即main方法中a指向的內存塊中字符串的值沒有發送變化。

下圖展示了對象引用與內存塊的關系,可以看出來main方法中的與app方法中的a沒關系,只是剛調用賦值的時候指向同一個內存塊。

9a73921a0aa88d48e24421b8352e24d1.png

(2)字符串拼接源碼實現

通過下圖中字節碼命令可以看到,字符串拼接是通過StringBuilder實現的。

c5e01d1d3271811e4dc085e43d0dc090.png

讀取的數據經第8行構建為String對象;

字符串拼接拆分為11-19,第11行構建StringBuilder對象,第16行調append方法,將字段串拼接到后邊(底層通過將字符串中的字符放入原字符串字符數組中)

第19行,調StringBuilder.toString方法返回拼接好的字符串。

toString()的源碼如下:

@Override

publicString toString() {

// Create a copy, don't share the array

return new String(value, 0, count);

}

重新構建String對象,并且注釋中說明創建拷貝,但不共享字符數組。String構造函數底層會調Arrays.copyOfRange(char[] original, int from, int to)方法,將original字符數組拷貝到一個新的字符數組中,源碼如下:

public static char[] copyOfRange(char[] original, int from, intto) {

int newLength = to -from;

if (newLength < 0)

throw new IllegalArgumentException(from + " > " +to);

char[] copy = new char[newLength]; System.arraycopy(original, from, copy, 0, Math.min(original.length -from, newLength)); returncopy; }

(3)普通對象引用

public static voidmain(String[] args) {

ListNode node = new ListNode(4);

System.out.println(node);

chg(node);

System.out.println(node.val);

}

private static voidchg(ListNode node) {

node.val+=1;

System.out.println(node);

node= new ListNode(2); System.out.println(node); } static classListNode{ private intval; privateListNode next; ListNode(intval){ this.val =val; } }

輸出:

test.InsertSortTest$ListNode@2a139a55

test.InsertSortTest$ListNode@2a139a55

test.InsertSortTest$ListNode@15db9742

5

分析:①??test.InsertSortTest$ListNode@2a139a55地址對應的對象,在main方法調chg方法傳遞參數的時候,將地址拷貝給chg方法的參數node,在chg方法中修改對象的值,此時兩個方法中的node仍指向統一內存塊,故main方法中輸出為5。

②??node= new ListNode(2) 語句將chg方法中的node重新指向另一個對象地址test.InsertSortTest$ListNode@15db9742,此時main方法和chg方法中的node指向不同的對象。

40021dad8fc316a8cf90d32fca250da6.png

2. Java內存區域

具體Java虛擬機運行時數據區的劃分,網上有很多相關資料,還可以看《深入理解Java虛擬機》,在此就不贅述了。但需要說明一點JDK7和JDK8稍有不同,就是JDK8中將原有的方法區(Method Area)或永久代改為元空間(MetaSpace),即將存儲類信息、靜態變量等元數據信息從方法區(也是堆內存)移動到本地內存(native memory)中。將不會出現java.lang.OutOfMemoryError: PermGen異常,如果該區域設置了大小,可能會出現java.lang.OutOfMemoryError: Metadata space異常,如果不設置大小,默認是自增的。

JDK7中JVM運行時數據區的劃分如下圖:(JDK7時,已將字符串常量池從方法區移到堆中)

50c73649f498ad35cbfde8a9be54a6da.png

JDK8中JVM運行時數據區的劃分:

87f9bad86823619efe727db52d3693ca.png

3. Java方法調用機制(字節碼執行引擎)

棧幀是支持虛擬機方法調用和方法執行的數據結構,每個方法調用都對應一個棧幀。棧幀中包含局部變量表、操作棧、動態連接和方法返回地址等信息,結構如下圖所示(圖摘自Java —— 運行時棧幀結構):

40a290276100aeb31466efadd1526221.png

具體內容的介紹參考書《深入理解Java虛擬機》。簡單總結如下:

1. 基本概念

棧幀中局部變量表的大小、操作數棧的大小在編譯期確定;

局部變量表用于存儲方法參數和方法內部定義的局部變量,以槽(slot)為最小單位;

操作數棧用于存儲指令計算對應的數據元素;

動態連接是指向運行時常量池中該棧幀所屬方法放入引用,在運行期間可以轉化為直接引用;

返回地址保存棧幀退出時,返回到方法被調用的位置,有正常退出(由PC計數器確定)和異常退出(由異常處理器表確定);

2. 方法調用

(1)解析

方法調用在Class文件中存儲的都是符號引用,而不是方法在實際運行時內存布局中的入口地址(或直接引用)

在類加載的解析階段,會將其中一部分符號引用轉為直接引用,如編譯期可知、運行期不可變的方法,包括靜態方法和私有方法兩大類,他們不可能被繼承或重寫;

5條字節碼指令:invokestatic(調靜態方法)、invokespecial(調構造方法、私有方法和父類方法)、invokevirtual(調虛方法)、invokeinterface(調接口方法,運行時確定實現該接口的對象)、invokedynamic(用于動態類型語言,暫不深究)。

前2種對應的為非虛方法,解析階段可以將符號引用轉為直接引用,不會延遲到運行期;invokevirtual和invokeinterface作用于虛方法(除final方法外)

(2)分派

Java多態性主要通過重載和重寫實現,他們在JAVA虛擬機中是通過分派完成,包含靜態分派(對應重載)和動態分派(對應重寫)

定義一個變量,其有靜態類型和實際類型,靜態類型變量本身不會被改變,在編譯期可知;編譯時程序不知道一個對象實際類型是什么。

重載時,是通過參數的靜態類型作為判斷依據的;重寫時,是在運行時根據實際類型作為判斷依據,根據操作數棧頂所指的實際類型,去類型和父類型的常量池中查找對應方法。

3. 方法執行

Java虛擬機采用基于棧的字節碼解釋執行,過程涉及字節碼指令、程序計數器、局部變量表和操作棧等,具體例子可參考書《深入理解Java虛擬機》。

4. 總結

方法調用時,方法參數是通過值傳遞的,并且方法參數會存儲在棧幀中的局部變量表中,當修改該參數變量的指針時,與原來變量所指的內存塊不同

Java虛擬機在JDK8時,將原來的永久代(方法區)改為元空間,放入本地內存。其中一個好處是防止永久代空間溢出問題

Java方法調用和執行是基于棧的字節碼指令解釋執行引擎,調用過程中涉及什么時機將符號引用轉為直接引用,非虛方法調用發生在解析階段,重載發生在編譯期,重寫發生在運行時。

5.參考

《深入理解Java虛擬機》

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/370448.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/370448.shtml
英文地址,請注明出處:http://en.pswp.cn/news/370448.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CSS染色圖標(圖片)

之前一直以為用background引入的圖標無法染色&#xff08;非字體圖標&#xff09;&#xff0c;現在才知道有黑科技可以用&#xff0c;就是利用drop-shadow。 代碼示例 <!DOCTYPE html> <html> <head lang"en"><meta charset"UTF-8"&…

eclipse安裝java web插件

1 查看eclipse版本 找到eclipse的安裝目錄&#xff0c;找到readme文件&#xff0c;打開其中的html文件&#xff0c;我的是4.6版本的,代號是oxygen 2 安裝 打開eclipse,點擊help-Install new software-單擊add&#xff0c;在彈出窗口中輸入網址&#xff1a; http://download.ecl…

python正則表達式指南_Python正則表達式指南

1. 正則表達式基礎1.1. 簡單介紹正則表達式并不是Python的一部分。正則表達式是用于處理字符串的強大工具&#xff0c;擁有自己獨特的語法以及一個獨立的處理引擎&#xff0c;效率上可能不如str自帶的方法&#xff0c;但功能十分強大。得益于這一點&#xff0c;在提供了正則表達…

Google Guava EventBus用于事件編程

在任何軟件應用程序中都是如此&#xff0c;有些對象需要共享信息才能完成工作。 在Java應用程序中&#xff0c;實現信息共享的一種方法是擁有事件偵聽器&#xff0c;其唯一目的是在發生所需事件時采取某些措施。 在大多數情況下&#xff0c;此過程有效&#xff0c;并且最有經驗…

system類

package system.cn; /** system類的方法 都是靜態方法&#xff0c;可以直接用類名直接調用* 常用的方法&#xff1a;* static long currentTimeMillis() 返回以毫秒為單位的當前時間。 static void exit(int status) 終止當前正在運行的 Java 虛擬機。 static void gc…

c await和java_blog/java/test/awaitility.zh.md at master · c-rainstorm/blog · GitHub

javaAtomicInteger atomic new AtomicInteger(0);// Do some async stuff that eventually updates the atomic integerawait().untilAtomic(atomic, equalTo(1));等待一個 AtomicBoolean 更簡單&#xff1a;javaAtomicBoolean atomic new AtomicBoolean(false);// Do some a…

實現輸入框小數多 自動進位展示,編輯時實際值不變

今天遇到個業務需求&#xff0c;要求輸入框&#xff0c;輸入數字的小數位數可以很多位&#xff0c;但移開后顯示&#xff0c;只顯示小數點后兩位 &#xff08;四舍五入&#xff09;&#xff0c;當要編輯的時候&#xff0c;展現其原來的輸入數據。 閑話不多說&#xff0c;當時也…

使用Jasper Reports以Java創建報告

上周&#xff0c;我試圖使用Jasper創建報告。 在這篇文章中&#xff0c;我將記錄一些資源和鏈接&#xff0c;以便對任何尋求類似信息的人都有用。 我將介紹Jasper報告&#xff0c;示例和Dynamic Jasper的生命周期。 Jasper Reports是世界上最受歡迎的開源報告引擎。 它完全用…

CentOS7 安裝NodeJS

一、切換目錄到/usr/local/src 命令行&#xff1a;cd /usr/local/src 二、下載node.js&#xff08;我這里下載的是二進制的源碼&#xff09; 命令行&#xff1a; wget https://nodejs.org/dist/v8.9.1/node-v8.9.1-linux-x64.tar.xz 圖片&#xff1a; 三、解壓壓縮包 命令行&am…

CSS3基礎2(變形與動畫)

<!DOCTYPE html5><html lang"en"><head> <meta charset"UTF-8"> <title>CSS3基礎知識&#xff08;動畫&#xff09;</title> <style> /*div{*/ /*width: 150px;*/ /*hei…

java對hashmap迭代_Java:通過HashMap迭代,這樣更有效率?

第二個選項肯定更有效&#xff0c;因為在第一個選項中只進行一次查找&#xff0c;次數為n次。但是&#xff0c;沒有什么比嘗試它更好&#xff0c;當你可以。所以這里 –(不完美&#xff0c;但足夠好驗證假設和我的機器)public static void main(String args[]) {Map map new H…

html-edm(郵件營銷)編寫規則

最近寫了一個edm郵件 以前沒有接觸過 使用的是很老的html頁面編寫規則 只能用table標簽 在此記錄一下edm編寫的一些規則 個人參考的是這兩個網址&#xff0c;轉載一下 http://www.zcool.com.cn/article/ZMTM5MDgw.html https://www.cnblogs.com/lhweb15/p/6404626.html …

ASP.NET Core2.0 環境下MVC模式的支付寶PC網站支付接口-沙箱環境開發測試

1.新建.NET Core web項目 2.Controllers-Models-Views 分三個大部分 3.下載安裝最新sdk 官方的SDK以及Demo都還是.NET Framework的&#xff0c;根據官方文檔說明新建網站后還是需要引用官方SDK的源碼&#xff0c; 在這里直接使用網上一位朋友的用.NET Standard 2.0 進行實現了支…

如何在redhat8里使用gcc命令_如何使用who命令檢查用戶登錄信息

請關注本頭條號&#xff0c;每天堅持更新原創干貨技術文章。如需學習視頻&#xff0c;請在微信搜索公眾號“智傳網優”直接開始自助視頻學習1. 前言本教程主要介紹如何使用who命令檢查用戶登錄信息。如何使用who命令檢查用戶登錄信息Linux中的who命令列出了系統上的所有登錄用戶…

研究僵局–第4部分:修復代碼

在這個簡短的博客系列的最后BadTransferOperation中&#xff0c;我一直在討論分析死鎖&#xff0c;我將修復BadTransferOperation代碼。 如果您看過本系列的其他博客 &#xff0c;那么您將知道&#xff0c;為了達到這一點&#xff0c;我創建了死鎖的演示代碼&#xff0c;展示了…

chrome插件2

轉自&#xff1a;http://www.codeceo.com/article/15-chrome-extension.html 1. Web Developer 支持Chrome的Web Developer擴展&#xff0c;允許你通過添加一個小工具欄來使用不同的工具。 官方網站&#xff1a;https://chrome.google.com/webstore/detail/web-developer/bfbam…

java月歷組件_vue之手把手教你寫日歷組件

---恢復內容開始---1.日歷組件1.分析功能&#xff1a;日歷基本功能&#xff0c;點擊事件改變日期&#xff0c;樣式的改變1.結構分析&#xff1a;html1.分為上下兩個部分2.上面分為左按鈕&#xff0c;中間內容展示&#xff0c;右按鈕下面分為周幾展示和日期展示3.基本結構頁面ht…

HTML5和css3

超鏈接 <a target"頁面打開位置" href"鏈接地址">內容</a>target:_blank 重新打開一個頁面target:_self 當前頁面打開 1.頁面地址&#xff1a; 基礎功能&#xff0c;用于進入該鏈接的頁面&#xff1b; 2.錨點&#xff1a; 需要給標簽名定義id…

python下載顯示文件丟失_Microsoft.PythonTools.resources.dll

我該如何安裝從金山毒霸下載的DLL文件&#xff1f;一&#xff1a;1、從金山毒霸下載壓縮文件。2、將DLL文件解壓到電腦上的某個地方。3、把該文件跟要求使用它的程序放在同一路徑上。注意32位程序需要使用32位的DLL文件&#xff0c;64位程序需要使用64位的DLL文件。否則會出現0…

maven project module 依賴項目創建 ---轉

一、創建Maven Project 1.右擊 --> New --> Other&#xff0c;--> Maven --> Maven Project --> Next 2.如下圖&#xff0c;選中Create a simple project --> Next 3.輸入Group Id, Artifact Id, Version, Packaging選擇pom&#xff0c;因為創建的Maven Pr…