国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

ホームページ Java &#&ベース JVM 學(xué)習(xí) Java メモリ構(gòu)造

JVM 學(xué)習(xí) Java メモリ構(gòu)造

Feb 01, 2021 pm 05:41 PM

JVM 學(xué)習(xí) Java メモリ構(gòu)造

Java メモリ構(gòu)造

  • 1.JVM の概要
  • 2.プログラム カウンター
    • 2.1.定義
    • 2.2.機(jī)能と特徴の説明
  • 3.仮想マシンスタック
    • 3.1. スタックの特性
    • 3.2. スタックのデモンストレーション
    • 3.3. スタックの問題の分析
    • 3.4. スタックのスレッド セーフティの問題
    • 3.5.スタック メモリ オーバーフロー (StackOverflowError)
    • 3.6. スレッド実行診斷
      • 3.6.1. ケース 1: CPU 使用率が高すぎる (例として Linux システム)
      • 3.6.2. ケース 2: スレッド診斷_長期間結(jié)果が得られない
  • 4.ローカル メソッド スタック
  • 5.Heap
    • #5.1.定義
    • ##5.2.ヒープメモリオーバーフロー(OutOfMemoryError:Javaヒープスペース)
    • ##5.3.ヒープメモリ診斷
    • ##6.メソッド領(lǐng)域
  • 6.1.定義
  • 6.2.定義
    • 6.3.メソッド領(lǐng)域のメモリオーバーフロー(OutOfMemoryError:メタスペース)
    • 6.4. 定數(shù)プール
    1.JVM の概要

定義:JVM正式名は Java 仮想マシンです - Java プログラムの実行環(huán)境 (Java バイナリ バイトコードの実行環(huán)境)


#利點(diǎn):

一度書いたらどこでも実行できます (クロス) -platform)

自動メモリ管理、ガベージコレクション機(jī)能
  • 配列添字範(fàn)囲外チェック
  • ポリモーフィズム
  • Compare JVM、JRE、JDK
  • の接続と相違點(diǎn)を図で説明します。

JVM アーキテクチャ
図に示すように、JVM 學(xué)習(xí) Java メモリ構(gòu)造
a クラスが Java ソース コード (.java ファイル) から Java バイナリ バイトコードにコンパイルされた後、クラスは、実行する前に JVM にロードされる前にクラス ローダーを通過する必要があります。 通常、クラスはメソッド領(lǐng)域に配置します。今後クラスによって作成されるオブジェクトはヒープに配置され、ヒープ內(nèi)のオブジェクトは、メソッドを呼び出すときにローカル開発だけでなく、仮想マシンのスタックやプログラム カウンターも使用します。
メソッドが実行されると、コードの各行が実行エンジンのインタープリターによって 1 行ずつ実行されます。メソッド內(nèi)のホット コードは、頻繁に呼び出され、ジャストインタイム コンパイラによってコンパイルおよび実行されるコードです。 GC はガベージを収集します。 JVM 學(xué)習(xí) Java メモリ構(gòu)造 ローカル メソッド インターフェイスを通じて、オペレーティング システムが提供する関數(shù)を呼び出すことができます。


JVM のメモリ構(gòu)造には次のものが含まれます。
1. メソッド領(lǐng)域

2. プログラム カウンタ

3. 仮想マシン スタック 4. ローカル メソッド スタック
5 .Heap



2.Program Counter

2.1.Definition

Program Counter RegisterProgram Counter( Register) 関數(shù):

次の jvm 命令の実行アドレスを記憶するためです

機(jī)能 スレッドに対してプライベートです
メモリ オーバーフローはありません
(メモリ構(gòu)造の中で唯一メモリオーバーフローしない構(gòu)造)

2.2ではプログラムカウンタの機(jī)能と特徴について説明します。

2.2. 関數(shù)と機(jī)能の説明

?二進(jìn)制字節(jié)碼			JVM指令					Java源代碼?0:?getstatic?????#20?????????????????//?PrintStream?out?=?System.out;?
?3:?astore_1??????????????????????????//?-?
?4:?aload_1???????????????????????????//?out.println(1);?
?5:?iconst_1??????????????????????????//?-?
?6:?invokevirtual?#26?????????????????//?-?
?9:?aload_1???????????????????????????//?out.println(2);?
?10:?iconst_2??????????????????????????//?-
?11:?invokevirtual?#26?????????????????//?-
?14:?aload_1???????????????????????????//?out.println(3);?
?15:?iconst_3??????????????????????????//?-
?16:?invokevirtual?#26?????????????????//?-
?19:?aload_1???????????????????????????//?out.println(4);?
?20:?iconst_4??????????????????????????//?-
?21:?invokevirtual?#26?????????????????//?-
?24:?aload_1???????????????????????????//?out.println(5);?
?25:?iconst_5??????????????????????????//?-
?26:?invokevirtual?#26?????????????????//?-
?29:?return

System.out の最初の行では、変數(shù)に値を代入し、4 でそれを呼び出しています。 : println() メソッド。次に、1、2、3、4、5 を順番に出力します。これらの命令は実行のために CPU に直接渡すことができず、インタプリタを経由する必要があります。バイトコード命令を 1 つずつマシン コードに解釈し、そのマシン コードを CPU に渡して実行することができます。 つまり、

バイナリ バイトコード -> インタプリタ -> マシン コード -> CPU


実際、プログラム カウンターの機(jī)能は、命令の実行中に記録することです。 . 次の JVM 命令の実行アドレスをライブします。
上記のバイナリ バイトコードの前にある數(shù)字 0、3、4 は、アドレスとして理解できます。このアドレス情報に基づいて、実行するコマンドを見つけることができます。 命令が実行のために CPU に與えられるたびに、プログラム カウンタは次の命令のアドレスをプログラム カウンタに入れます。命令の実行が完了すると、インタプリタはプログラムに移動します。カウンタ 次の命令のアドレスを取得します。次に、それはインタプリタによってマシンコードに解釈され、実行のために CPU に渡されます。その後、このプロセスを繰り返してください。

物理的には、プログラム カウンタはレジスタを通じて実裝されます。レジスタは、CPU コンポーネントの中で最も高速に読み取るストレージ ユニットです。

程序計(jì)數(shù)器是線程私有的
假如說上述代碼都在線程1中運(yùn)行,同時運(yùn)行的還有線程2和線程3,多個線程運(yùn)行的時候,CPU會給每個線程分配時間片,給線程1分配時間片,如果線程1在指定的時間沒有運(yùn)行完,它就會把狀態(tài)暫存,切換到線程2,線程2執(zhí)行自己的代碼。線程2執(zhí)行完了,再繼續(xù)執(zhí)行線程1的代碼,在線程切換的過程中,我們要記住下一條指令的執(zhí)行地址。就需要用到程序計(jì)數(shù)器。假如說線程1剛開始執(zhí)行到第9行代碼,恰好這個時候時間片用完,CPU切換到線程2去執(zhí)行,這時它就會把下一條指令的地址10記錄到程序計(jì)數(shù)器里面,而且程序計(jì)數(shù)器是線程私有的,它是屬于線程1的,等線程2代碼執(zhí)行完了,線程1搶到了時間片,它就會從自己的程序計(jì)數(shù)器里面取出下一行代碼。每個線程都有自己的程序計(jì)數(shù)器

3.虛擬機(jī)棧

3.1.棧的特點(diǎn)

棧類似現(xiàn)實(shí)生活中的子彈夾。棧最重要的特點(diǎn)是后進(jìn)先出。
JVM 學(xué)習(xí) Java メモリ構(gòu)造
如圖,1是最先進(jìn)入棧中的,3是最后進(jìn)入棧中的,但是在出棧的時候,3最先出棧,1最后出棧。即他們按照1,2,3的順序入棧,按照3,2,1的順序出棧

虛擬機(jī)棧就是我們線程運(yùn)行時需要的內(nèi)存空間,一個線程運(yùn)行時需要一個棧。如果將來有多個線程的話,它就會有多個虛擬機(jī)棧。
每個棧可以看成是由多個棧幀組成,例如上圖中每個元素1,2,3都可以看成是棧幀。
一個棧幀就對應(yīng)著Java中一個方法的調(diào)用,即棧幀就是每個方法運(yùn)行時需要的內(nèi)存。每個方法運(yùn)行時需要的內(nèi)存一般有參數(shù),局部變量,返回地址,這些都需要占用內(nèi)存,所以每個方法執(zhí)行時,都要預(yù)先把這些內(nèi)存分配好。
當(dāng)我們調(diào)用第一個方法棧幀時,它就會給第一個方法分配棧幀空間,并且壓入棧內(nèi),當(dāng)這個方法執(zhí)行完了,就會把這個方法棧幀出棧,釋放這個方法所占用的內(nèi)存。
一個棧內(nèi)可能有多個棧幀存在。

總結(jié)
Java Virtual Machine Stacks(Java虛擬機(jī)棧)

  • 每個線程運(yùn)行時所需要的內(nèi)存,稱為虛擬機(jī)棧
  • 每個棧由多個棧幀(Frame)組成,對應(yīng)著每次方法調(diào)用時所占用的內(nèi)存
  • 每個線程只能有一個活動棧幀,對應(yīng)著當(dāng)前正在執(zhí)行的那個方法(位于棧頂)

活動棧幀表示線程正在執(zhí)行的方法。

3.2.棧的演示

public?class?teststacks?{
	public?static?void?main(String[]?args)?throws?InterruptedException{
		method1();
	}
	public?static?void?method1(){
		method2(1,2);
	}
	public?static?int?method2(int?a,int?b){
		int?c=a+b;
		return?c;
	}}

可以自行調(diào)試以上代碼來觀察棧中的變化情況。
入棧順序:main->method1->method2
出棧順序:method2->method1->main

3.3.棧的問題辨析

  1. 垃圾回收是否涉及棧內(nèi)存?
    不涉及,垃圾回收只是回收堆內(nèi)存中的無用對象,棧內(nèi)存不需要對它執(zhí)行垃圾回收,隨著方法的調(diào)用結(jié)束,棧內(nèi)存就釋放了。
  2. 棧內(nèi)存分配越大越好嗎?
    首先棧內(nèi)存可以指定:-Xss size(如果不指定棧內(nèi)存大小,不同系統(tǒng)會有一個不同的默認(rèn)值)
    其次由于電腦內(nèi)存一定,假如有100Mb,如果給棧內(nèi)存指定為2Mb,則最多只能存在50個線程,所以并不是越大越好,棧內(nèi)存較大一般是可以進(jìn)行較多次的方法遞歸調(diào)用,而不會增強(qiáng)線程效率,反而會使線程數(shù)量減少,一般使用默認(rèn)大小

3.4.棧的線程安全問題

看一個變量是否線程安全,首先就是看這個變量對多個線程是共享的還是私有的,共享的變量需要考慮線程安全。
其次局部變量也不能保證是線程安全的,需要看此變量是否逃離了方法的作用范圍(作為參數(shù)和返回值逃出方法作用范圍時需要考慮線程安全問題)
例如:
以下代碼中局部變量是私有的,是線程安全的

	//多個線程同時執(zhí)行該方法,會不會造成x值混亂呢?
	//不會,因?yàn)閤是方法內(nèi)的局部變量,是線程私有的,互不干擾
	static?void?m1(){
		int?x=0;
		for(int?i=0;i<p>但是如果我們把變量的類型改為static,此時就大不一樣了,x是靜態(tài)變量,線程1和線程2同時擁有同一個x,static變量針對多個線程是一個共享的,不加安全保護(hù)的話,就會出現(xiàn)線程安全問題。</p><pre class="brush:php;toolbar:false">	static?void?m1(){
		static?int?x=0;
		for(int?i=0;i<p>我們再看幾個方法</p><pre class="brush:php;toolbar:false">public?static?void?main(String[]?args)?{
????????StringBuilder?sb?=?new?StringBuilder();
????????sb.append(4);
????????sb.append(5);
????????sb.append(6);
????????new?Thread(()->{
????????????m2(sb);
????????}).start();
????}
????public?static?void?m1()?{
????????StringBuilder?sb?=?new?StringBuilder();
????????sb.append(1);
????????sb.append(2);
????????sb.append(3);
????????System.out.println(sb.toString());
????}
????public?static?void?m2(StringBuilder?sb)?{
????????sb.append(1);
????????sb.append(2);
????????sb.append(3);
????????System.out.println(sb.toString());
????}
????public?static?StringBuilder?m3()?{
????????StringBuilder?sb?=?new?StringBuilder();
????????sb.append(1);
????????sb.append(2);
????????sb.append(3);
????????return?sb;
????}

m1是線程安全的:m1中的sb是線程中的局部變量,它是屬于線程私有的
m2線程不安全:sb它是方法的參數(shù),有可能有其它的線程訪問到它,它就不再是線程私有的了,它對多個線程是共享的。
m3不是線程安全的:它被當(dāng)成返回結(jié)果返回了,返回了有可能其它的線程拿到這個對象,從而并發(fā)的修改。

3.5.棧內(nèi)存溢出(StackOverflowError)

什么情況下會導(dǎo)致棧內(nèi)存溢出吶?
1.棧幀過多導(dǎo)致棧內(nèi)存溢出(一般遞歸調(diào)用次數(shù)太多,進(jìn)棧太多導(dǎo)致溢出)
這里最容易出現(xiàn)的場景是函數(shù)的遞歸調(diào)用。
2.棧幀過大導(dǎo)致棧內(nèi)存溢出(不太容易出現(xiàn))

棧內(nèi)存溢出代碼演示1(自己開發(fā)):
測試以下的程序,其中遞歸函數(shù)沒有遞歸邊界

public?class?Demo1_2?{
	private?static?int?count;
????public?static?void?main(String[]?args)?{
????????try?{
????????????method1();
????????}?catch?(Throwable?e)?{
????????????e.printStackTrace();
????????????System.out.println(count);
????????}
????}
????private?static?void?method1()?{
????????count++;
????????method1();
????}}

運(yùn)行結(jié)果如下
JVM 學(xué)習(xí) Java メモリ構(gòu)造

JVM 學(xué)習(xí) Java メモリ構(gòu)造
這里報了錯誤StackOverflowError
總共進(jìn)行了22846次遞歸調(diào)用

idea中設(shè)置棧內(nèi)存大?。?/strong>
JVM 學(xué)習(xí) Java メモリ構(gòu)造
將棧內(nèi)存設(shè)置的小一點(diǎn),發(fā)現(xiàn)5000多次遞歸調(diào)用就溢出了。

棧內(nèi)存溢出代碼演示2(第三方依賴庫出現(xiàn)):
JVM 學(xué)習(xí) Java メモリ構(gòu)造
本案例可以使用JsonIgnore注解解決循環(huán)依賴,數(shù)據(jù)轉(zhuǎn)換時,只讓部門類去關(guān)聯(lián)員工類,員工類不再關(guān)聯(lián)部門類,在員工類的部門屬性(dept)上加@JsonIgnore注解。具體使用詳情可以點(diǎn)擊此處查看

3.6.線程運(yùn)行診斷

3.6.1.案例1:cpu占用過多(linux系統(tǒng)為例)

排查步驟:

1.在linux中使用top命令,去查看后臺進(jìn)程對cpu的占用情況
注意,在這之前我們運(yùn)行了一道Java程序
JVM 學(xué)習(xí) Java メモリ構(gòu)造
Java代碼占用了CPU的99.3%.top命令只能定位到進(jìn)程,而無法定位到線程。

2.查看線程對cpu的占用情況:ps H -eo pid,tid,%cpu
如果顯示過多,可使用ps H -eo pid,tid,%cpu | grep 進(jìn)程id,過濾掉不想看的部分進(jìn)程

注意:ps不僅可以查看進(jìn)程,也可以查看線程對CPU的占用情況。H把進(jìn)程中的線程所有信息都展示出來。-eo規(guī)定輸出感興趣的內(nèi)容,這里我們想看看pid,tid和CPU的占用情況%cpu
JVM 學(xué)習(xí) Java メモリ構(gòu)造
當(dāng)線程數(shù)太多,排查不方便的話,我們可以用grep pid來進(jìn)行篩選,過濾掉不感興趣的進(jìn)程
ps H -eo pid,tid,%cpu |grep 32655

3.定位到是哪個線程占用內(nèi)存過高后,再使用Jdk提供的命令(jstack+進(jìn)程id)去查看進(jìn)程中各線程的運(yùn)行信息,需要把第二步中查到的線程id(十進(jìn)制)轉(zhuǎn)為十六進(jìn)制,然后進(jìn)行比較查詢到位置后判斷異常信息。
JVM 學(xué)習(xí) Java メモリ構(gòu)造
thread1,thread2,thread3是我們自己定義的線程。
可以根據(jù)線程id,找到有問題的線程,進(jìn)一步定位到問題代碼的源碼行號
JVM 學(xué)習(xí) Java メモリ構(gòu)造

3.6.2.案例2:線程診斷_遲遲得不到結(jié)果

仍然通過jdk提供的 jstack+進(jìn)程id的方式,去查看進(jìn)程中各個線程的運(yùn)行信息
JVM 學(xué)習(xí) Java メモリ構(gòu)造
JVM 學(xué)習(xí) Java メモリ構(gòu)造

4.本地方法棧

含義:Java虛擬機(jī)調(diào)用本地方法時,需要給本地方法提供的一些內(nèi)存空間
本地方法不是由Java編寫的代碼,由于Java有時不能直接和操作系統(tǒng)打交道,所以需要用C/C++語言來與操作系統(tǒng)打交道,那么Java就可以通過調(diào)用本地方法來獲得這些功能。本地方法非常的多,如Object類的clone(),hashCode方法,wait方法,notify方法等

public?native?int?hashCode();

5.堆

5.1.定義

1.虛擬機(jī)棧,程序計(jì)數(shù)器,本地方法棧,這些都是線程私有的,而堆和方法區(qū),是線程公用的一塊內(nèi)存區(qū)域。
2.通過new關(guān)鍵字創(chuàng)建的對象都會使用堆內(nèi)存
3.由于堆是線程共享的,堆內(nèi)的對象都要考慮線程安全問題(也有一些例外)
4.堆有垃圾回收機(jī)制,不再被引用的對象會被回收

5.2.堆內(nèi)存溢出(OutOfMemoryError:Java heap space)

對象一直存在于堆中未被回收,且占用內(nèi)存越來越大,最終導(dǎo)致堆內(nèi)存溢出(雖然堆中有垃圾回收機(jī)制,但垃圾回收機(jī)制不是回收所有的對象)
我們可以看看下面的代碼

public?static?void?main(String[]?args)?{
????????int?i?=?0;
????????try?{
????????????List<string>?list?=?new?ArrayList();
????????????String?a?=?"hello";
????????????while?(true)?{
????????????????list.add(a);?//?hello,?hellohello,?hellohellohellohello?...
????????????????a?=?a?+?a;??//?hellohellohellohello
????????????????i++;
????????????}
????????}?catch?(Throwable?e)?{
????????????e.printStackTrace();
????????????System.out.println(i);
????????}}</string>

JVM 學(xué)習(xí) Java メモリ構(gòu)造
報了錯誤java.lang.OutOfMemoryError
代碼中每次都拼接一個hello,由于定義的list集合創(chuàng)建在try語句里面,所以在for循環(huán)不斷執(zhí)行過程中,list集合是不會被回收的,只要程序還沒到catch之前,它就一直有效。而字符串對象都被追加到了集合內(nèi)部,字符串對象由于一直被使用,所以不會被回收。
我們可以通過-Xmx來設(shè)置堆空間大小。
JVM 學(xué)習(xí) Java メモリ構(gòu)造
我們把堆內(nèi)存改成8M(之前內(nèi)存是4G),此時只運(yùn)行了17次。

5.3.堆內(nèi)存診斷

1.jps工具:jps,查看當(dāng)前進(jìn)程中有哪些Java進(jìn)程,并將進(jìn)程id顯示出來(idea中通過terminal命令行輸入命令)
2.jmap工具:jmap -heap 進(jìn)程id 查詢某一個時刻堆內(nèi)存的占用情況
3.jconsole工具:圖形界面的,多功能監(jiān)測工具,可連續(xù)監(jiān)測,使用流程圖如下(1-2-3):

6.方法區(qū)

6.1.定義

方法區(qū)(Method Area)與Java堆一樣,是各個線程共享的內(nèi)存區(qū)域,他用于存儲已被虛擬機(jī)加載的類信息、常量、靜態(tài)常量、即時編譯器編譯后的代碼等數(shù)據(jù)。(與類有關(guān)的信息)。雖然Java虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個邏輯部分,但是他卻有一個別名叫做Non-Heap(非堆),目的應(yīng)該是與Java堆區(qū)分開來方法區(qū)在虛擬機(jī)啟動時創(chuàng)建。
對于習(xí)慣在HotSpot虛擬機(jī)上開發(fā)、部署程序的開發(fā)者來說,很多都更愿意把方法取稱為“永久代”(Permanent Generation),本質(zhì)上兩者并不等價,僅僅是因?yàn)镠otSpot虛擬機(jī)的設(shè)計(jì)團(tuán)隊(duì)選擇把GC分代收集擴(kuò)展至方法區(qū),或者說使用永久代來實(shí)現(xiàn)方法區(qū)而已,這樣HotSpot的垃圾收集器可以像管理Java堆一樣管理這部分內(nèi)存,能夠省去專門為方法區(qū)編寫內(nèi)存管理代碼的工作。對于其他虛擬機(jī)(如BEA JRockit、IBM J9等)來說是不存在永久代的概念的。原則上,如何實(shí)現(xiàn)方法區(qū)屬于虛擬機(jī)實(shí)現(xiàn)細(xì)節(jié),不受虛擬機(jī)規(guī)范約束,但使用永久代來實(shí)現(xiàn)方法區(qū),現(xiàn)在看來并不是一個好主意,因?yàn)檫@樣更容易遇到內(nèi)存溢出問題(永久代有-XX:MaxPermSize的上限,J9和JRockit只要沒有觸碰到進(jìn)程可用內(nèi)存的上限,例如32位系統(tǒng)中的4GB,就不會出現(xiàn)問題),而且有極少數(shù)方法(例如String.intern())會因這個原因?qū)е虏煌摂M機(jī)下有不同的表現(xiàn)。因此,對于HotSpot虛擬機(jī),根據(jù)官方發(fā)布的路線圖信息,現(xiàn)在也已放棄永久代并逐步改為采用Navtive Memory來實(shí)現(xiàn)方法區(qū)的規(guī)劃,在JDK1.7的HostSpot中,已經(jīng)把原本放在永久代的字符串常量池移出,jdk1.8中后稱作元空間,用的操作系統(tǒng)內(nèi)存。
Java虛擬機(jī)規(guī)范對方法區(qū)的限制非常寬松,除了和Java堆一樣不需要連續(xù)的內(nèi)存和可以喧囂而固定大小或者可擴(kuò)展外,還可以選擇不實(shí)現(xiàn)垃圾收集。相對而言,垃圾收集行為在這個區(qū)域是比較少出現(xiàn)的,但并非數(shù)據(jù)進(jìn)入了方法區(qū)就如永久代的名字一樣“永久”存在了。這區(qū)域的內(nèi)存回收目標(biāo)主要是針對常量池的回收和對類型的卸載,一般來說,這個區(qū)域的回收“成績”比較難以令人滿意,尤其是類型的卸載,條件相當(dāng)苛刻,但是這部分區(qū)域的回收確實(shí)是必要的。在Sun公司的BUG列表中,曾出現(xiàn)過的若干個嚴(yán)重的BUG就是由于低版本的HotSpot虛擬機(jī)對此區(qū)域未完全回收而導(dǎo)致內(nèi)存泄漏。
根據(jù)Java虛擬機(jī)規(guī)范的規(guī)定,當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時,將拋出OutOfMemoryError異常。

文原文關(guān)于虛擬機(jī)的定義:

JVM 學(xué)習(xí) Java メモリ構(gòu)造

6.2.定義

jdk1.8之前,方法區(qū)是用的堆內(nèi)存,1.8之后,方法區(qū)用的操作系統(tǒng)內(nèi)存。
這塊不是太清晰,可以參考下此篇博客點(diǎn)擊查看
常量池分為靜態(tài)常量池和動態(tài)常量池,下圖中的常量池指的是動態(tài)常量池,因?yàn)樗鼈円呀?jīng)被讀入內(nèi)存中去,而靜態(tài)常量池存在于class文件中
JVM 學(xué)習(xí) Java メモリ構(gòu)造

6.3.方法區(qū)內(nèi)存溢出(OutOfMemoryError: Metaspace)

1.8以前會導(dǎo)致永久代內(nèi)存溢出

1.8以后會導(dǎo)致元空間內(nèi)存溢出

/**
?*?演示元空間內(nèi)存溢出?java.lang.OutOfMemoryError:?Metaspace
?*?-XX:MaxMetaspaceSize=8m
?*/public?class?Demo1_8?extends?ClassLoader?{?//?可以用來加載類的二進(jìn)制字節(jié)碼
????public?static?void?main(String[]?args)?{
????????int?j?=?0;
????????try?{
????????????Demo1_8?test?=?new?Demo1_8();
????????????
????????????for?(int?i?=?0;?i?<p><strong>jdk1.8以后, 默認(rèn)情況下,方法區(qū)用的是系統(tǒng)內(nèi)存,所以加大還是不會導(dǎo)致內(nèi)存溢出,循環(huán)很多次都運(yùn)行成功。</strong><br><strong>當(dāng)設(shè)置了-XX:MaxMetaspaceSize=8m,到了5411次就溢出了。報的是java.lang.OutOfMemoryError: Metaspace錯誤</strong></p><p>而1.8以前永久代溢出報的錯誤是java.lang.OutOfMemoryError:PermGen space</p><p><strong>6.4.常量池</strong></p><p><img src="/static/imghw/default1.png" data-src="https://img.php.cn/upload/article/000/000/052/b93dd11973d3f8094228c146b3131c12-17.png" class="lazy" alt="JVM 學(xué)習(xí) Java メモリ構(gòu)造"></p><p><strong>常量池,就是一張表,虛擬機(jī)指令根據(jù)這站常量表找到要執(zhí)行的類名、方法名、參數(shù)類型、字面量信息(如字符串常量、true和false)</strong>。<br><em><em>運(yùn)行時常量池,常量池是</em>.class文件中的,當(dāng)該類被加載,它的常量池信息就會放入運(yùn)行時常量池,并把里面的符號地址變?yōu)檎鎸?shí)地址</em>*。</p><pre class="brush:php;toolbar:false">public?class?HelloWorld?{
	public?static?void?main(String[]?args)?{
		System.out.println("hello,world");
	}}

以上是一個helloworld程序,helloworld要運(yùn)行,肯定要先編譯成一個二進(jìn)制字節(jié)碼。
二進(jìn)制字節(jié)碼由類的基本信息、常量池、類方法定義(包含了虛擬機(jī)指令)。
反編譯HelloWorld(之前需要運(yùn)行將.java文件編譯成.class文件)
使用idea工具
JVM 學(xué)習(xí) Java メモリ構(gòu)造

F:\IDEA\projects\jvm>javap?-v?F:\IDEA\projects\jvm\out\production\untitled\HelloWorld.class

F:\IDEA\projects\jvm\out\production\untitled\是HelloWorld.class所在的路徑

顯示類的詳細(xì)信息

Classfile?/F:/IDEA/projects/jvm/out/production/untitled/HelloWorld.class
??Last?modified?2021-1-30;?size?533?bytes
??MD5?checksum?82d075eb7217b4d23706f6cfbd44f8f1
??Compiled?from?"HelloWorld.java"public?class?HelloWorld
??minor?version:?0
??major?version:?52
??flags:?ACC_PUBLIC,?ACC_SUPER

可以看到類的文件,最后修改時間,簽名。以及版本等等。有的還有訪問修飾符、父類和接口等詳細(xì)信息。

顯示常量池

Constant?pool:
???#1?=?Methodref??????????#6.#20?????????//?java/lang/Object."<init>":()V
???#2?=?Fieldref???????????#21.#22????????//?java/lang/System.out:Ljava/io/PrintStream;
???#3?=?String?????????????#23????????????//?hello,world
???#4?=?Methodref??????????#24.#25????????//?java/io/PrintStream.println:(Ljava/lang/String;)V
???#5?=?Class??????????????#26????????????//?HelloWorld
???#6?=?Class??????????????#27????????????//?java/lang/Object
???#7?=?Utf8???????????????<init>
???#8?=?Utf8???????????????()V
???#9?=?Utf8???????????????Code
??#10?=?Utf8???????????????LineNumberTable
??#11?=?Utf8???????????????LocalVariableTable
??#12?=?Utf8???????????????this
??#13?=?Utf8???????????????LHelloWorld;
??#14?=?Utf8???????????????main
??#15?=?Utf8???????????????([Ljava/lang/String;)V
??#16?=?Utf8???????????????args
??#17?=?Utf8???????????????[Ljava/lang/String;
??#18?=?Utf8???????????????SourceFile
??#19?=?Utf8???????????????HelloWorld.java
??#20?=?NameAndType????????#7:#8??????????//?"<init>":()V
??#21?=?Class??????????????#28????????????//?java/lang/System
??#22?=?NameAndType????????#29:#30????????//?out:Ljava/io/PrintStream;
??#23?=?Utf8???????????????hello,world
??#24?=?Class??????????????#31????????????//?java/io/PrintStream
??#25?=?NameAndType????????#32:#33????????//?println:(Ljava/lang/String;)V
??#26?=?Utf8???????????????HelloWorld
??#27?=?Utf8???????????????java/lang/Object
??#28?=?Utf8???????????????java/lang/System
??#29?=?Utf8???????????????out
??#30?=?Utf8???????????????Ljava/io/PrintStream;
??#31?=?Utf8???????????????java/io/PrintStream
??#32?=?Utf8???????????????println
??#33?=?Utf8???????????????(Ljava/lang/String;)V</init></init></init>

顯示方法定義

{
??public?HelloWorld();
????descriptor:?()V
????flags:?ACC_PUBLIC
????Code:
??????stack=1,?locals=1,?args_size=1
?????????0:?aload_0?????????1:?invokespecial?#1??????????????????//?Method?java/lang/Object."<init>":()V
?????????4:?return
??????LineNumberTable:
????????line?1:?0
??????LocalVariableTable:
????????Start??Length??Slot??Name???Signature????????????0???????5?????0??this???LHelloWorld;

??public?static?void?main(java.lang.String[]);
????descriptor:?([Ljava/lang/String;)V
????flags:?ACC_PUBLIC,?ACC_STATIC
????Code:
??????stack=2,?locals=1,?args_size=1
?????????0:?getstatic?????#2??????????????????//?Field?java/lang/System.out:Ljava/io/PrintStream;
?????????3:?ldc???????????#3??????????????????//?String?hello,world
?????????5:?invokevirtual?#4??????????????????//?Method?java/io/PrintStream.println:(Ljava/lang/String;)V
?????????8:?return
??????LineNumberTable:
????????line?3:?0
????????line?4:?8
??????LocalVariableTable:
????????Start??Length??Slot??Name???Signature????????????0???????9?????0??args???[Ljava/lang/String;}</init>

第一個方法是public HelloWorld();它是編譯器自動為我們構(gòu)造的無參構(gòu)造方法。
第二個是public static void main(java.lang.String[]);即main方法
方噶里面就包括了虛擬機(jī)的指令了。
getstatic獲取一個靜態(tài)變量,即獲取System.out靜態(tài)變量
ldc是加載一個參數(shù),參數(shù)是字符串hello,world
invokevirtual虛方法調(diào)用,println方法
return執(zhí)行結(jié)束。
我們getstatic、ldc、invokevirtual后面都有一個#2,#3,#4。在解釋器翻譯這些虛擬機(jī)指令的時候,它會把這些#2,#3,#4進(jìn)行一個查表翻譯。比如getstatic #2,就去查常量池的表。在常量池中
#2 = Fieldref #21.#22 引用的是成員變量#21,#22.
#21 = Class #28 // java/lang/System
#22 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
然后再去找#28.29,30
#28 = Utf8 java/lang/System
#29 = Utf8 out
#30 = Utf8 Ljava/io/PrintStream;
所以現(xiàn)在我就知道了,我是要找到j(luò)ava.lang.system類下叫out的成員變量,類型是java/io。
同理,ldc是找#3 = String #23 Utf8 hello,world,它是虛擬機(jī)常量池的一個字符串。把helloworld常量變成字符串對象加載進(jìn)來。
invokevirtual #4 Methodref #24.#25 等等
所以常量池的作用就是給我們指令提供一些常量符號,根據(jù)這些常量符號,我們就可以根據(jù)查表的方式去找到它,這樣虛擬機(jī)才能成功的執(zhí)行它。

相關(guān)免費(fèi)學(xué)習(xí)推薦:java基礎(chǔ)教程

以上がJVM 學(xué)習(xí) Java メモリ構(gòu)造の詳細(xì)內(nèi)容です。詳細(xì)については、PHP 中國語 Web サイトの他の関連記事を參照してください。

このウェブサイトの聲明
この記事の內(nèi)容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰屬します。このサイトは、それに相當(dāng)する法的責(zé)任を負(fù)いません。盜作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡(luò)ください。

ホットAIツール

Undress AI Tool

Undress AI Tool

脫衣畫像を無料で

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード寫真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

寫真から衣服を削除するオンライン AI ツール。

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中國語版

SublimeText3 中國語版

中國語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強(qiáng)力な PHP 統(tǒng)合開発環(huán)境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)