『壹』 java里的靜態成員變數是放在了堆內存還是棧內
這個要看對象是否為逃逸對象,如果為非逃逸對象,會根據標量替換來把對象分解成若干個被這個方法使用的成員變數所代替,這些代替的成員變數在棧幀或寄存器上分配空間
『貳』 java靜態變數存儲在哪個區
java程序在內存中的存儲分配情況:
一、堆區:
1.存儲的全部是對象,每個對象都包含一個與之對應的class的信息。(class的目的是得到操作指令)
2.jvm只有一個堆區(heap)被所有線程共享,堆中不存放基本類型和對象引用,只存放對象本身 ujiuye
棧區:
1.每個線程包含一個棧區,棧中只保存基礎數據類型的對象和自定義對象的引用(不是對象),對象都存放在堆區中
2.每個棧中的數據(原始類型和對象引用)都是私有的,其他棧不能訪問。
3.棧分為3個部分:基本類型變數區、執行環境上下文、操作指令區(存放操作指令)。 ujiuye
方法區:
1.又叫靜態區,跟堆一樣,被所有的線程共享。方法區包含所有的class和static變數。
2.方法區中包含的都是在整個程序中永遠唯一的元素,如class,static變數。
二、內存分區
而內存分為四個區:stack segment,heap segment,data segment,code segment;
stack 區存放函數參數和局部變數;heap 區存放對象;data 區存放static 的變數或者字元串常量; code 區存放類中的方法;
因此,靜態變數是存放在data區的 !
『叄』 誰能解釋一下java中的棧內存和堆內存
2.1 內存分配策略
按照編譯原理的觀點,程序運行時的內存分配有三種策略,分別是靜態的,棧式的,和堆式的.
靜態存儲分配是指在編譯時就能確定每個數據目標在運行時刻的存儲空間需求,因而在編譯時就可以給他們分配固定的內存空間.這種分配策略要求程序代碼中不允許有可變數據結構(比如可變數組)的存在,也不允許有嵌套或者遞歸的結構出現,因為它們都會導致編譯程序無法計算準確的存儲空間需求.
棧式存儲分配也可稱為動態存儲分配,是由一個類似於堆棧的運行棧來實現的.和靜態存儲分配相反,在棧式存儲方案中,程序對數據區的需求在編譯時是完全未知的,只有到運行的時候才能夠知道,但是規定在運行中進入一個程序模塊時,必須知道該程序模塊所需的數據區大小才能夠為其分配內存.和我們在數據結構所熟知的棧一樣,棧式存儲分配按照先進後出的原則進行分配。
靜態存儲分配要求在編譯時能知道所有變數的存儲要求,棧式存儲分配要求在過程的入口處必須知道所有的存儲要求,而堆式存儲分配則專門負責在編譯時或運行時模塊入口處都無法確定存儲要求的數據結構的內存分配,比如可變長度串和對象實例.堆由大片的可利用塊或空閑塊組成,堆中的內存可以按照任意順序分配和釋放.
2.2 堆和棧的比較
上面的定義從編譯原理的教材中總結而來,除靜態存儲分配之外,都顯得很呆板和難以理解,下面撇開靜態存儲分配,集中比較堆和棧:
從堆和棧的功能和作用來通俗的比較,堆主要用來存放對象的,棧主要是用來執行程序的.而這種不同又主要是由於堆和棧的特點決定的:
在編程中,例如C/C++中,所有的方法調用都是通過棧來進行的,所有的局部變數,形式參數都是從棧中分配內存空間的。實際上也不是什麼分配,只是從棧頂向上用就行,就好像工廠中的傳送帶(conveyor belt)一樣,Stack Pointer會自動指引你到放東西的位置,你所要做的只是把東西放下來就行.退出函數的時候,修改棧指針就可以把棧中的內容銷毀.這樣的模式速度最快,當然要用來運行程序了.需要注意的是,在分配的時候,比如為一個即將要調用的程序模塊分配數據區時,應事先知道這個數據區的大小,也就說是雖然分配是在程序運行時進行的,但是分配的大小多少是確定的,不變的,而這個"大小多少"是在編譯時確定的,不是在運行時.
堆是應用程序在運行的時候請求操作系統分配給自己內存,由於從操作系統管理的內存分配,所以在分配和銷毀時都要佔用時間,因此用堆的效率非常低.但是堆的優點在於,編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數據要在堆里停留多長的時間,因此,用堆保存數據時會得到更大的靈活性。事實上,面向對象的多態性,堆內存分配是必不可少的,因為多態變數所需的存儲空間只有在運行時創建了對象之後才能確定.在C++中,要求創建一個對象時,只需用new命令編制相關的代碼即可。執行這些代碼時,會在堆里自動進行數據的保存.當然,為達到這種靈活性,必然會付出一定的代價:在堆里分配存儲空間時會花掉更長的時間!這也正是導致我們剛才所說的效率低的原因,看來列寧同志說的好,人的優點往往也是人的缺點,人的缺點往往也是人的優點(暈~).
2.3 JVM中的堆和棧
JVM是基於堆棧的虛擬機.JVM為每個新創建的線程都分配一個堆棧.也就是說,對於一個Java程序來說,它的運行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態。JVM對堆棧只進行兩種操作:以幀為單位的壓棧和出棧操作。
我們知道,某個線程正在執行的方法稱為此線程的當前方法.我們可能不知道,當前方法使用的幀稱為當前幀。當線程激活一個Java方法,JVM就會在線程的Java堆棧里新壓入一個幀。這個幀自然成為了當前幀.在此方法執行期間,這個幀將用來保存參數,局部變數,中間計算過程和其他數據.這個幀在這里和編譯原理中的活動紀錄的概念是差不多的.
從Java的這種分配機制來看,堆棧又可以這樣理解:堆棧(Stack)是操作系統在建立某個進程時或者線程(在支持多線程的操作系統中是線程)為這個線程建立的存儲區域,該區域具有先進後出的特性。
每一個Java應用都唯一對應一個JVM實例,每一個實例唯一對應一個堆。應用程序在運行中所創建的所有類實例或數組都放在這個堆中,並由應用所有的線程共享.跟C/C++不同,Java中分配堆內存是自動初始化的。Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在堆棧中分配,也就是說在建立一個對象時從兩個地方都分配內存,在堆中分配的內存實際建立這個對象,而在堆棧中分配的內存只是一個指向這個堆對象的指針(引用)而已。
『肆』 深入Java對象及元素的存儲區域
在JAVA平台上開發應用程序的時候,有一個很大的特點就是其是在應用程序運行的時候才建立對象。換句話說,在程序運行的時候,才會最終確定對象的歸屬,即對象應該存儲在什麼地方。由於存儲在不同的區域,其在性能上會有所不同。為此作為Java程序開發人員需要了解各個存儲區域的特點以及對性能的影響。然後再根據需要來調整應用程序的區域分配。總的來說,在操作系統中有五個地方可以用來保存應用程序運行中的數據。這類區域的特點以及對性能的影響分析如下。
存儲區域一:寄存器
雖然同在內存中,但是不同的區域由於用途不同,其性能也有所不同。如就拿Java應用程序來說,寄存器由於其處於處理器的內部,為此這個區域存取數據最快。跟內存中的其他存儲區域有著天壤之別。那麼我們把所有對象都放到這個區域內,不就可以提高Java應用程序的性能了嗎?理論上是如此,但是在現實中是行不通的。因為這個寄存器的數量是非常有限的。在內存中的寄存器區域是由編譯器根據需要來分配的。我們程序開發人員不能夠通過代碼來控制這個寄存器的分配。所以說,這第一個存儲區域寄存器,我們只能夠看看,而不能夠對其產生任何的影響。
存儲區域二:堆棧
對象的創建有兩種方式,一是在應用程序開發的過程中就創建對象;二是在程序運行的過程中要用到對象的時候再來創建對象。前者比後者性能要高,而後者比前者要靈活。這主要是因為前者創建對象的時候,就是這個堆棧中創建的。雖然其創建的對象沒有保存在寄存器中,但是通過這個對象的推棧指針可以直接從處理器哪裡獲得相關的支持。如堆棧指針往上移動的時候,則釋放原有對象佔用的內存;如堆棧指針向下移動時,則為對象分配新的內存。所以,如果把對象存放在這個堆棧中,雖然性能沒有像存放在寄存器中那麼理想,但是仍然比存儲在其他地方要好的多。
由於Java程序是在程序運行過程中才根據需要來創建對象。為此對象就不能夠保存在這個堆棧中。不過Java應用程序也不能夠白白的浪費這個寶貴的空間。為此雖然Java對象本身沒有保存在這個堆棧中(不是不保存而是這里沒有他的容身之地),但是還是應該把一些可以放的內容放到這個堆棧中,以提高應用程序的性能。如可以把一些對象引用存放在這個堆棧中。
另外對於一些基本的數據類型對象,Java程序也往往把他們放置在堆棧中,以提高數據處理的性能。如一些整數型、字元型的數據對象,這些對象有些共同的特點,如對象比較小、是Java程序提供的標准對象等等。對於這些對象由於每個應用程序基本上都需要用到,而且我們程序開發人員只能夠引用這些對象,而不能夠對其進行更改弊衡胡。為此Java程序在處理的時候,往往一開始就創建了對象(即直接在堆棧中創建對象並保存),而不像其他對象一樣,在需要的時候才創建。只所以在堆棧中創建這些對象,還有一個重要的原因。因為如果在堆棧中創建對象的話,Java編輯器必須知道存儲在堆棧內所有數據的確切大小和生命周期。為了得到這些信息,必須產生相關的代碼來獲得這些信息,以便其操作堆棧指針。普通的對象大小、生命周期等等難以預先獲得,為此在堆棧中創建普通的對象,對於Java應用程序來說並不是很合適。相反,這些Java編譯器預定義的對象大小並不會隨著機器硬體架構的變化和用戶需求的變化而變化;而且這些對象往往從始之終都會存在的,所以也不存在生命周期的問題。所以把這些對象放置在堆棧中是合理的,也是可實現的。如此處理,不僅不會影響到對象的靈活性,而且還可以提供比較好的性能。
存儲區域三:堆
堆雖然跟堆棧一樣,都是隨機訪問存儲器中的區域,但是兩者有很大的不同。因為在堆中,沒有堆棧指針,為此也就無法直接從處理器那邊獲得支持。為此其性能跟堆棧比起來,就有一定的差距。通常情況下,除上面所說的一些預定義對象之外,其他的對象都是保存在這個堆中的。或者說,利用new關鍵字創建的對象都是保存在堆中的。保存在堆中其好處也是顯而易見的。如Java編譯器不需要知道從堆里需要分配多少存儲區域,也不必知道存儲的數據在堆里會存活多長時間。所以在攔磨堆里分配存儲有很大的靈活性。當需要對象時,我們可以使用New關鍵字建立一個對象。然後系統會自動給這個對象在堆中分配一個區域讓其作為歸宿。不過其最大的不足之處,就是在堆中創建租攔對象與分配存儲區域,要比在堆棧中慢許多。魚與熊掌不能兼得呀。
存儲區域四:靜態存儲區域與常量存儲區域
在Java對象中有一些特殊的元素。如有些元素是比較特別的(如利用關鍵字Static定義的變數)。這些變數對於其他對象來說,可能就是靜態的。為了更好的管理這些變數,Java在內存中專門劃分了一個靜態存儲區域來管理這些元素。這里的靜態存儲區域就是指在固定的位置存放應用程序運行時一直存在的數據。這里需要明確的一點就是,Java對象是不保存在這個地方的,而只是把對象中的一些特殊元素放置這里。由於位置固定,所以下次調用的時候就省去了查找的麻煩。為此其對於提供應用程序的性能是有利的。作為我們程序開發人員來說,在書寫代碼的時候,就需要靈活應用Static這個關鍵字。筆者的意見是,能用則用;不能用的時候也要想著法兒用。特別是有些元素用不用Static關鍵字時對於程序功能沒有影響,此時我們要理直氣壯的在元素前面加上Static關鍵字。
在Java對象中還有一類特殊的元素,我們叫做常量。由於常量的值是穩定不變的,如圓周率。為此把他們放在代碼的內部是可行的。不過有些時候,在進行一些嵌入式系統開發的時候,我們往往不這么做。而是會把常量元素跟代碼分開來保存。如我們會根據情況把常量的值存放在一些只讀存儲器中。這主要是為了一些特殊的功能考慮的。如出於版權控制的需要。如在列印機上為了保護原裝耗材的版權,往往把常量跟代碼分開存放。
存儲區域五:非RAM存儲
有時候,有些程序運行所需要的數據我們還會放置在其他地方。如在一些系統中需要用到流對象,這個對象的數據並沒有保存在上面所談到的任何一個存儲區域,這個對象直接被轉為為位元組流,發送到其他的主機上去了。另外有一種叫做持久化的對象,其是被存儲在硬碟中的。這些對象平時在應用程序開發過程中用到的並不是很多,大家只需要了解有這些對象的存在即可。等到需要用到的時候,再去深入研究也不遲。
從上面的分析中我們可以看到,對象的歸屬我們程序開發人員很難控制。寄存器是編譯器來管理的。而堆與堆棧又基本上受到開發平台的限制,我們程序人員也沒有這個能耐來干涉他們。其實我們主要能夠調整與控制的就是第四個存儲區域,即靜態存儲與常量存儲。筆者的建議是,對於非嵌入式程序,能夠利用靜態存儲來實現的,就盡量採用靜態存儲。而對於常量來說,需要根據需要實現的功能來判斷是否需要把常量存儲在只讀存儲器中。有時候對於版權的保護等等需要用到這個只讀存儲器。
『伍』 java中,靜態方法被調用是,存儲在內存的哪個區域是棧還是放大區還是兩者都有
在JDK8之前,靜態成員(靜態變數和靜態方法)都是存儲在方法區(永久代)中的靜態區中(這里指類被載入後,靜態成員的存儲位置)。但在JDK8之後,永久代被移除了,取而代之的是元空間(metaspace)。但元空間中存儲的主要是.class文件的元數據信息,靜態成員的存儲位置由方法區轉到了堆內存(heap)中。
不過,不管是JDK8,還是更早的版本中,靜態方法的執行(不僅僅是靜態方法,還有普通的成員方法)都是在棧內存(stack)中進行的。每個線程都會在棧內存中開辟一個棧,在調用方法時,對應的方法都會在執行這個方法的線程的棧中創建一個「棧幀」,棧幀中保存了局部變數表(基本數據類型和對象引用)、操作數棧、動態連接和返回地址等信息。等到方法執行完畢,棧幀被銷毀,對應的內存也將被釋放。