検索
ホームページJava&#&ベースJava定数プールの詳細なグラフィックとテキストの説明

Java定数プールの詳細なグラフィックとテキストの説明

Java 定数プールは永遠のトピックであり、面接官のお気に入りです。さまざまな質問があります。Xiaocai は定数プールについてすでに聞いているので、今回はそれをうまくまとめます。

推奨: java ビデオ チュートリアル

jvm 仮想メモリの配布:

Java定数プールの詳細なグラフィックとテキストの説明

プログラム カウンターは、 jvm でプログラムを実行する パイプラインにはいくつかのジャンプ命令が格納されていますが、これは高度すぎて Xiao Cai には理解できません。

ローカル メソッド スタックは、jvm がオペレーティング システムのメソッドを呼び出すために使用するスタックです。

仮想マシン スタックは、Java コードを実行するために jvm によって使用されるスタックです。

メソッド領域には、いくつかの定数、静的変数、クラス情報などが格納されます。これは、メモリ内のクラス ファイルの格納場所として理解できます。

仮想マシン ヒープは、Java コードを実行するために jvm によって使用されるヒープです。

Java の定数プールは、実際には、静的定数プールと実行時定数プールの 2 つの形式に分かれています。

いわゆる静的定数プールは、*.class ファイル内の定数プールです。クラス ファイル内の定数プールには、文字列 (数値) リテラルだけでなく、クラスとメソッドの情報も含まれており、絶対に占有されます。クラスファイルの一部ではなく、スペースの大部分。

ランタイム定数プールとは、jvm 仮想マシンがクラスのロード操作を完了した後、クラス ファイル内の定数プールをメモリにロードし、メソッド領域に保存することを意味します。これを定数プールと呼ぶことがよくあります。 . は、メソッド領域のランタイム定数プールを指します。

次に、インターネット上でよく知られている定数プールの例をいくつか引用して説明します。

String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
String s5 = new String("Hello");
String s6 = s5.intern();
String s7 = "H";
String s8 = "ello";
String s9 = s7 + s8;
          
System.out.println(s1 == s2);  // true
System.out.println(s1 == s3);  // true
System.out.println(s1 == s4);  // false
System.out.println(s1 == s9);  // false
System.out.println(s4 == s5);  // false
System.out.println(s1 == s6);  // true

まず、Java で == 演算子を直接使用する場合、比較されるのは 2 つの文字列の内容ではなく、参照アドレスであることを説明します。内容を比較します。

s1 == s2 は非常に分かりやすいです。s1 と s2 に値を代入するとき、どちらも文字列リテラルを使用します。平たく言えば、文字列を直接書き出すことを意味します。コンパイル時に、この種のリテラルは、再利用を実現するためにクラス ファイルの定数プールに直接配置されます。ランタイム定数プールをロードした後、s1 と s2 は同じメモリ アドレスを指すため、それらは等しくなります。

s1 == s3 には落とし穴があります。s3 は動的に結合された文字列ですが、結合に関係するすべての部分は既知のリテラルです。コンパイル中に、この結合は最適化されます。コンパイラはそれを直接綴ります。つまり、クラスファイルでは String s3 = "Hel" "lo"; が String s3 = "Hello"; に最適化され、s1 == s3 が成立します。

s1 == s4 は確かに等しくありません。s4 も結合されていますが、新しい String("lo") 部分は既知のリテラルではなく、予測不可能な部分です。コンパイラはそれを最適化しません。結果文字列不変定理と組み合わせると、s4 がどこに割り当てられるかは誰にも分からないため、アドレスは異なるはずです。アイデアを明確にするために簡単な図を示します:

Java定数プールの詳細なグラフィックとテキストの説明s1 == s9 は等しくありません、そして理由は同様です。s7 と s8 は値を割り当てるときに文字列リテラルを使用しますが、それらが結合されるときはs9 に, 2 つの変数として, s7 と s8 は予測できません. 結局のところ, コンパイラはコンパイラであり, インタプリタとして使用できないため, 最適化は行われません. 実行されると, 新しい文字列は s7 と s8 によって結合されますヒープ内のアドレスになりますが、メソッド領域の定数プール内の s1 アドレスと同じにすることはできません。

Java定数プールの詳細なグラフィックとテキストの説明

s4 == s5 は説明の必要はありません。絶対に等しくありません。両方ともヒープ内にありますが、アドレスが異なります。

s1 == s6 これら 2 つの等価性は、完全にインターン メソッドによるものです。s5 はヒープ内にあり、コンテンツは Hello です。インターン メソッドは、Hello 文字列を定数プールに追加して、それを返そうとします。定数プールにはすでに Hello 文字列が存在するため、インターン メソッドはアドレスを直接返し、s1 はコンパイル中にすでに定数プールを指しているため、s1 と s6 は同じアドレスを指しており、等しくなります。

この時点で、非常に重要な 3 つの結論を導き出すことができます。

定数プールをよりよく理解するには、コンパイル時の動作に注意を払う必要があります。

ランタイム定数プールの定数は、基本的に各クラス ファイルの定数プールから取得されます。

プログラムの実行中、定数が定数プールに手動で追加されない限り (インターン メソッドの呼び出しなど)、jvm は定数を定数プールに自動的に追加しません。

上記には文字列定数プールのみが関係します。実際には、整数定数プール、浮動小数点定数プールなどもありますが、それらはすべて似ています。ただし、数値定数プールに手動で定数を追加することはできません。タイプ定数プールです。定数プールの定数はプログラムの起動時に決定されます。たとえば、整数定数プールの定数範囲は -128 ~ 127 です。定数プールではこの範囲の数値のみを使用できます。

実践

理論についてはここまで述べたので、実際の定数プールについて触れてみましょう。

前に述べたように、クラス ファイルには静的定数プールがあります。この定数プールはコンパイラによって生成され、Java ソース ファイルにリテラルを格納するために使用されます (この記事ではリテラルのみに焦点を当てます)。次の Java コードがあります:

 String s = "hi";

为了方便起见,就这么简单,没错!将代码编译成class文件后,用winhex打开二进制格式的class文件。如图:

Java定数プールの詳細なグラフィックとテキストの説明

简单讲解一下class文件的结构,开头的4个字节是class文件魔数,用来标识这是一个class文件,说白话点就是文件头,既:CA FE BA BE。

紧接着4个字节是java的版本号,这里的版本号是34,因为笔者是用jdk8编译的,版本号的高低和jdk版本的高低相对应,高版本可以兼容低版本,但低版本无法执行高版本。所以,如果哪天读者想知道别人的class文件是用什么jdk版本编译的,就可以看这4个字节。

接下来就是常量池入口,入口处用2个字节标识常量池常量数量,本例中数值为00 1A,翻译成十进制是26,也就是有25个常量,其中第0个常量是特殊值,所以只有25个常量。

常量池中存放了各种类型的常量,他们都有自己的类型,并且都有自己的存储规范,本文只关注字符串常量,字符串常量以01开头(1个字节),接着用2个字节记录字符串长度,然后就是字符串实际内容。本例中为:01 00 02 68 69。

接下来再说说运行时常量池,由于运行时常量池在方法区中,我们可以通过jvm参数:-XX:PermSize、-XX:MaxPermSize来设置方法区大小,从而间接限制常量池大小。

假设jvm启动参数为:-XX:PermSize=2M -XX:MaxPermSize=2M,然后运行如下代码:

//保持引用,防止自动垃圾回收
List<String> list = new ArrayList<String>();
        
int i = 0;
        
while(true){
    //通过intern方法向常量池中手动添加常量
    list.add(String.valueOf(i++).intern());
}

程序立刻会抛出:Exception in thread "main" java.lang.outOfMemoryError: PermGen space异常。PermGen space正是方法区,足以说明常量池在方法区中。

在jdk8中,移除了方法区,转而用Metaspace区域替代,所以我们需要使用新的jvm参数:-XX:MaxMetaspaceSize=2M,依然运行如上代码,抛出:java.lang.OutOfMemoryError: Metaspace异常。同理说明运行时常量池是划分在Metaspace区域中。具体关于Metaspace区域的知识,请读者自行搜索。

更多java知识请关注java基础教程栏目。

以上がJava定数プールの詳細なグラフィックとテキストの説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事は博客园で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

SublimeText3 中国語版

SublimeText3 中国語版

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

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

mPDF

mPDF

mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境