0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Java中的對(duì)象一定在堆中分配嗎

科技綠洲 ? 來(lái)源:Java技術(shù)指北 ? 作者:Java技術(shù)指北 ? 2023-09-30 10:19 ? 次閱讀

在我們的日常編程實(shí)踐中,我們經(jīng)常會(huì)遇到各種類型的對(duì)象,比如字符串、列表、自定義類等等。這些對(duì)象在內(nèi)存中是如何存儲(chǔ)的呢?

你可能會(huì)毫不猶豫地回答:“在堆中!”如果你這樣回答了,那你大部分情況下是正確的。但是,有沒(méi)有例外呢?Java中的對(duì)象一定在堆中分配嗎?

接下來(lái),小編帶你揭開(kāi)Java內(nèi)存模型的神秘面紗。

1、Java內(nèi)存模型簡(jiǎn)介

Java內(nèi)存模型是Java虛擬機(jī)(JVM)的一部分,它規(guī)定了JVM如何和計(jì)算機(jī)內(nèi)存進(jìn)行交互。Java內(nèi)存模型主要包括五個(gè)部分:

圖片

  1. 堆(Heap):這是運(yùn)行時(shí)數(shù)據(jù)區(qū)域,所有的對(duì)象實(shí)例以及數(shù)組都在這里分配內(nèi)存。
  2. 棧(Stack):每個(gè)線程有一個(gè)私有的棧,每次方法調(diào)用都會(huì)在棧上創(chuàng)建一個(gè)棧幀,用于存儲(chǔ)局部變量、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息
  3. 方法區(qū)(Method Area):所有的類信息、常量、靜態(tài)變量以及即時(shí)編譯器編譯后的代碼都被存儲(chǔ)在方法區(qū)。
  4. 本地方法棧(Native Method Stack):對(duì)于執(zhí)行Native方法,JVM使用本地方法棧。
  5. 程序計(jì)數(shù)器(Program Counter Register):程序計(jì)數(shù)器是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器。

當(dāng)我們?cè)诖a中創(chuàng)建一個(gè)新的對(duì)象時(shí),這個(gè)對(duì)象的內(nèi)存通常是在堆上分配的。然后我們可以在棧上的方法幀中保存對(duì)這個(gè)對(duì)象的引用。這是對(duì)象內(nèi)存分配的常規(guī)方式,但是并不是唯一的方式。

2、對(duì)象的常規(guī)分配策略

在Java中,新創(chuàng)建的對(duì)象通常會(huì)被分配在堆中。這是因?yàn)槎咽怯伤芯€程共享的,任何線程都可以訪問(wèn)到堆中的任何對(duì)象,只要它有這個(gè)對(duì)象的引用。此外,堆的大小只受到物理內(nèi)存大小的限制,可以容納大量的對(duì)象。

以下是一個(gè)簡(jiǎn)單的代碼示例,展示了在堆中創(chuàng)建一個(gè)新對(duì)象:

public class Main {
    public static void main(String[] args) {
        String str = new String("Hello, world!");  // 在堆上分配一個(gè)新的 String 對(duì)象
        // ...
    }
}

在這個(gè)示例中,我們使用 new 關(guān)鍵字在堆上創(chuàng)建了一個(gè)新的 String 對(duì)象。然后我們?cè)跅I系?main方法幀中保存了一個(gè)對(duì)這個(gè)對(duì)象的引用。

3、對(duì)象的逃逸分析和標(biāo)量替換

然而,Java虛擬機(jī)不總是在堆上分配對(duì)象。有一種被稱為“逃逸分析”(Escape Analysis)的技術(shù),可以幫助JVM判斷一個(gè)新創(chuàng)建的對(duì)象的引用是否會(huì)逃逸出方法(即是否可能被其他方法或線程引用)。如果一個(gè)對(duì)象只在一個(gè)方法中使用,并且不會(huì)逃逸出這個(gè)方法,那么JVM可能會(huì)選擇在棧上分配這個(gè)對(duì)象。

另外一種叫做"標(biāo)量替換"(Scalar Replacement)的優(yōu)化手段,如果一個(gè)對(duì)象不可能逃逸出方法,并且這個(gè)對(duì)象的所有字段都可以被訪問(wèn)到,那么JVM可能會(huì)選擇拆解這個(gè)對(duì)象,直接在棧上創(chuàng)建一些對(duì)應(yīng)的基本類型變量。

然而,這些都取決于JVM的實(shí)現(xiàn)和具體的運(yùn)行情況,所以并不能保證在所有情況下都有效。此外,這些優(yōu)化通常需要啟動(dòng)JVM的-server模式才能生效。

4、Java堆和棧的對(duì)比

堆和棧在Java內(nèi)存模型中扮演著非常重要的角色,它們各自有著自己的特性和用途。簡(jiǎn)單來(lái)說(shuō):

  • 堆(Heap) :Java堆是所有線程共享的一塊內(nèi)存區(qū)域,主要用于存放對(duì)象實(shí)例和數(shù)組。堆是動(dòng)態(tài)分配的,大小不固定,只受物理內(nèi)存大小限制。
  • 棧(Stack) :Java棧是線程私有的,每個(gè)方法執(zhí)行都會(huì)創(chuàng)建一個(gè)新的棧幀。棧幀用于存儲(chǔ)局部變量、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。棧的大小在虛擬機(jī)啟動(dòng)時(shí)就已經(jīng)確定。

在Java中,對(duì)象的分配主要依賴于它們是否可能被其他方法或線程所引用,即是否會(huì)“逃逸”。

  • 如果一個(gè)對(duì)象的生命周期僅限于一個(gè)方法,并且不會(huì)被其他方法或線程引用,那么它可能在棧上分配。這通常是通過(guò)逃逸分析實(shí)現(xiàn)的。
  • 如果一個(gè)對(duì)象可能被多個(gè)線程共享,或者它的生命周期可能超過(guò)創(chuàng)建它的方法,那么它會(huì)被分配在堆上。

5、實(shí)際應(yīng)用和優(yōu)化

在實(shí)際的編程實(shí)踐中,我們通常不需要關(guān)心對(duì)象是在堆上分配還是在棧上分配,因?yàn)檫@是由JVM自動(dòng)管理的。然而,理解這些概念有助于我們編寫(xiě)出更高效、更優(yōu)化的代碼。

例如,我們可以盡量限制對(duì)象的作用域,讓它們只在一個(gè)方法中存在,這樣就增加了它們?cè)跅I戏峙涞目赡苄浴_@樣做的另一個(gè)好處是提高了代碼的可讀性和可維護(hù)性。

JIT編譯器也會(huì)進(jìn)行一些優(yōu)化,比如通過(guò)逃逸分析和標(biāo)量替換技術(shù),來(lái)提高代碼的運(yùn)行效率。理解這些優(yōu)化策略可以幫助我們更好地理解代碼的執(zhí)行過(guò)程,提高我們的編程技能。

6、結(jié)論

通過(guò)以上的討論,我們可以回答這個(gè)問(wèn)題:Java中的對(duì)象一定在堆中分配嗎?

答案是: 不一定 。

在Java中,對(duì)象通常是在堆上分配的,因?yàn)槎咽且粋€(gè)由所有線程共享的內(nèi)存區(qū)域,它可以容納大量的對(duì)象。但是,如果JVM通過(guò)逃逸分析發(fā)現(xiàn)一個(gè)對(duì)象只在一個(gè)方法中使用,并且不會(huì)逃逸出這個(gè)方法,那么它可能會(huì)選擇在棧上分配這個(gè)對(duì)象。同樣的,如果一個(gè)對(duì)象可以被拆解為一些基本類型或引用類型的字段,并且這些字段都只在一個(gè)方法中使用,那么JVM可能會(huì)選擇進(jìn)行標(biāo)量替換,將這個(gè)對(duì)象拆解并在棧上分配。

這些優(yōu)化策略取決于JVM的具體實(shí)現(xiàn)和運(yùn)行情況,因此并不是在所有情況下都有效。在實(shí)際的編程實(shí)踐中,我們通常不需要關(guān)心對(duì)象是在堆上分配還是在棧上分配,因?yàn)檫@是由JVM自動(dòng)管理的。然而,理解這些概念和優(yōu)化策略可以幫助我們編寫(xiě)出更高效、更優(yōu)化的代碼。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 存儲(chǔ)
    +關(guān)注

    關(guān)注

    13

    文章

    4314

    瀏覽量

    85853
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2967

    瀏覽量

    104763
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3616

    瀏覽量

    93739
  • 模型
    +關(guān)注

    關(guān)注

    1

    文章

    3244

    瀏覽量

    48847
  • 線程
    +關(guān)注

    關(guān)注

    0

    文章

    504

    瀏覽量

    19687
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    在這幾個(gè)配置目錄stack size分配的區(qū)別在哪,mem中分配,tskmanageer分配,和tsk中分配的,是不是mem中分配的是最大的?

    本帖最后由 只耳朵怪 于 2018-5-29 09:49 編輯 我想問(wèn)下,在這幾個(gè)配置目錄stack size分配的區(qū)別在哪,mem中分配,tskmanageer
    發(fā)表于 05-28 12:10

    如何用java映射創(chuàng)建java對(duì)象和調(diào)用java對(duì)象

    java種解析語(yǔ)言,java程序是通過(guò)java虛擬機(jī)解析.class的方式運(yùn)行起來(lái)。因此,java中就存在
    發(fā)表于 04-11 14:43

    如何用java映射創(chuàng)建java對(duì)象和調(diào)用java對(duì)象的方法

    java種解析語(yǔ)言,java程序是通過(guò)java虛擬機(jī)解析.class的方式運(yùn)行起來(lái)。因此,java中就存在
    發(fā)表于 07-28 16:11

    如何使用鏈接腳本刪除分配?

    在我參與的項(xiàng)目中,我們使用的是運(yùn)行 FreeRTOS 作為 os 的 nxp 產(chǎn)品。由于我們的應(yīng)用程序不使用普通 mallocs 分配動(dòng)態(tài)內(nèi)存,我想刪除鏈接器放入二進(jìn)制文件分配。
    發(fā)表于 03-23 07:05

    如何使用mermoc () 從中分配EBI SRAM

    的堆積空間以定位于 EBI SRAM 的地址。此示例使用 mermoc () 從中分配內(nèi)存, 即 EBI SRAM 。 您可以在下列時(shí)間下載樣本代碼http://www.nuvoton.com/resources-downlo. 1218165312。 nuvoto
    發(fā)表于 08-23 06:35

    JAVA教程之存儲(chǔ)與讀取對(duì)象

    JAVA教程之存儲(chǔ)與讀取對(duì)象,很好的JAVA的資料,快來(lái)學(xué)習(xí)吧
    發(fā)表于 04-11 17:28 ?7次下載

    Java開(kāi)發(fā)者必須了解的外內(nèi)存技術(shù)

    先來(lái)看個(gè) Demo:在 Demo 中分配外內(nèi)存用的是 allocateDirect 方法,但其內(nèi)部調(diào)用的是 DirectByteBuffer,換言之,DirectByteBuffer 才是實(shí)際操作
    發(fā)表于 07-01 10:19 ?3788次閱讀
    <b class='flag-5'>Java</b>開(kāi)發(fā)者必須了解的<b class='flag-5'>堆</b>外內(nèi)存技術(shù)

    明確區(qū)分與棧,和棧究竟有什么區(qū)別?

    定在中分配內(nèi)存的大小,然后調(diào)用operator new分配內(nèi)存,然后返回這塊內(nèi)存的首地址,放入棧,他在VC6下的匯編代碼如下:
    的頭像 發(fā)表于 04-09 09:45 ?4428次閱讀
    明確區(qū)分<b class='flag-5'>堆</b>與棧,<b class='flag-5'>堆</b>和棧究竟有什么區(qū)別?

    Java教程之Java面向對(duì)象程序設(shè)計(jì)維數(shù)組的使用

    本文檔的主要內(nèi)容詳細(xì)介紹的是Java教程之Java面向對(duì)象程序設(shè)計(jì)維數(shù)組的使用。
    發(fā)表于 01-09 18:15 ?3次下載
    <b class='flag-5'>Java</b>教程之<b class='flag-5'>Java</b>面向<b class='flag-5'>對(duì)象</b>程序設(shè)計(jì)<b class='flag-5'>一</b>維數(shù)組的使用

    Java實(shí)驗(yàn):類和對(duì)象的擴(kuò)展

    Java實(shí)驗(yàn):類和對(duì)象的擴(kuò)展
    發(fā)表于 07-08 15:36 ?20次下載
    <b class='flag-5'>Java</b>實(shí)驗(yàn):類和<b class='flag-5'>對(duì)象</b>的擴(kuò)展

    文詳解Java對(duì)象的內(nèi)存布局

    這個(gè)實(shí)例對(duì)象是以怎樣的形態(tài)存在內(nèi)存的? 個(gè)Object對(duì)象在內(nèi)存占用多大? 對(duì)象
    發(fā)表于 09-30 14:38 ?1210次閱讀
    <b class='flag-5'>一</b>文詳解<b class='flag-5'>Java</b><b class='flag-5'>對(duì)象</b>的內(nèi)存布局

    什么是內(nèi)存?內(nèi)存是如何分配的?

    般的編譯系統(tǒng),內(nèi)存的分配方向和棧內(nèi)存是相反的。當(dāng)棧內(nèi)存從高地址向低地址增長(zhǎng)的時(shí)候,內(nèi)存從低地址向高地址
    的頭像 發(fā)表于 07-05 17:58 ?9994次閱讀

    Java創(chuàng)建對(duì)象有哪些方式

    1 問(wèn)題 作為Java開(kāi)發(fā)者,經(jīng)常創(chuàng)建很多對(duì)象,你是否知道Java創(chuàng)建對(duì)象有哪些方式呢?
    的頭像 發(fā)表于 02-24 10:29 ?1053次閱讀

    java內(nèi)存溢出排查方法

    Java內(nèi)存溢出(Memory overflow)是指Java虛擬機(jī)(JVM)內(nèi)存無(wú)法滿足對(duì)象分配
    的頭像 發(fā)表于 11-23 14:46 ?3242次閱讀

    java虛擬機(jī)內(nèi)存包括遠(yuǎn)空間內(nèi)存嗎

    詳細(xì)介紹JVM內(nèi)存的各個(gè)部分及其作用。 Java(Heap) Java是JVM管理的最大塊內(nèi)存區(qū)域,用于存放
    的頭像 發(fā)表于 12-05 14:15 ?409次閱讀