JVM の内部: Java 仮想マシンの詳細な説明

JVM の内部: Java 仮想マシンの詳細な説明

[[325305]]

この記事では、Java 仮想マシン (JVM) の内部アーキテクチャについて説明します。次の図は、Java SE 7 仕様に準拠した一般的な JVM コアの内部を示しています。

上の図に示されているコンポーネントは、 2 つのセクションで説明されています。最初の章では各スレッド用に作成されるコンポーネントについて説明し、2 番目の章ではスレッドに依存しないコンポーネントについて説明します。

  • スレッド
  • JVM システムスレッド
  • 各スレッドは関連している
  • プログラムカウンタ
  • スタック
  • ローカルスタック
  • スタック制限
  • スタックフレーム
  • ローカル変数配列
  • オペランドスタック
  • 動的リンク
  • スレッド共有
  • ヒープ
  • メモリ管理
  • 非ヒープメモリ
  • ジャストインタイムコンパイル
  • 方法領域
  • クラスファイルの構造
  • クラスローダー
  • より高速なクラス読み込み
  • メソッド領域はどこですか?
  • クラスローダーリファレンス
  • ランタイム定数プール
  • 例外テーブル
  • シンボルテーブル
  • インターンされた文字列

スレッド

ここで言うスレッドとは、プログラム実行プロセス中のスレッド エンティティを指します。 JVM を使用すると、アプリケーションは複数のスレッドを同時に実行できます。 Hotspot JVM の Java スレッドは、ネイティブ オペレーティング システム スレッドに直接マッピングされます。スレッド ローカル ストレージ、バッファー割り当て、同期オブジェクト、スタック、プログラム カウンターなどが準備されると、オペレーティング システムのネイティブ スレッドが作成されます。 Java スレッドが終了すると、ネイティブ スレッドがリサイクルされます。オペレーティング システムは、すべてのスレッドをスケジュールし、使用可能な CPU に割り当てる役割を担います。ネイティブ スレッドが初期化されると、Java スレッドの run() メソッドが呼び出されます。 run() が返されたときに、例外がキャッチされない場合、ネイティブ スレッドは、その終了により JVM プロセスを終了するかどうかを確認します (たとえば、このスレッドは最後の非デーモン スレッドです)。スレッドが終了すると、ネイティブ スレッドと Java スレッドのすべてのリソースが解放されます。

JVM システムスレッド

jconsole または別のデバッガーを使用すると、バックグラウンドで多数のスレッドが実行されていることがわかります。これらのバックグラウンド スレッドは、public static void main(String[]) 関数をトリガーするメイン スレッドや、メイン スレッドによって作成された他のスレッドと一緒に実行されます。 Hotspot JVM のバックグラウンドで実行されるシステム スレッドは主に次のとおりです。

スレッド関連コンポーネント

実行中の各スレッドは次のコンポーネントで構成されます。

プログラムカウンタ (PC)

PC は、ローカル命令を除き、現在の命令 (またはオペコード) のアドレスを参照します。現在のメソッドがネイティブ メソッドの場合、PC の値は未定義になります。すべてのCPUにはPCがあります。通常、PC は実行される各命令ごとに増加するため、PC は実行される次の命令のアドレスを格納します。 JVM は PC を使用して命令実行の場所を追跡します。 PC は実際にはメソッド領域内のメモリ アドレスを指します。

スタック

各スレッドには独自のスタックがあり、その中に各メソッド実行のスタック フレームが含まれています。スタックは後入れ先出し (LIFO) データ構造であるため、現在実行中のメソッドがスタックの一番上にあります。メソッドが呼び出されるたびに、新しいスタック フレームが作成され、スタックの先頭にプッシュされます。メソッドが正常に返されるか、キャッチされない例外がスローされると、スタック フレームがポップされます。スタック フレームのプッシュとポップ以外に、スタックを直接操作することはできません。したがって、スタック フレームはヒープ上に割り当てることができ、連続したメモリを必要としません。

ネイティブスタック

すべての JVM 実装がネイティブ メソッドをサポートしているわけではありません。ネイティブ メソッドをサポートする実装では、通常、スレッドごとにネイティブ メソッド スタックが作成されます。 JVM が C リンク モデルを使用して JNI (Java Native Invocation) を実装する場合、ネイティブ スタックは C スタックになります。この場合、ネイティブ メソッド スタック内の引数と戻り値の順序は、一般的な C プログラムと同じです。ネイティブ メソッドは通常 (JVM 実装によって異なりますが)、JVM 内の Java メソッドを順番に呼び出すことができます。この Java へのネイティブ メソッド呼び出しはスタック (通常は Java スタック) 上で発生します。スレッドはネイティブ メソッド スタックを離れ、Java スタック上に新しいスタック フレームを開きます。

スタックの制限

スタックは動的に割り当てることも、固定サイズにすることもできます。スレッドが許容範囲を超えるスペースを要求した場合、StackOverflowError がスローされます。スレッドに新しいスタック フレームが必要なのに、割り当てるのに十分なメモリがない場合は、OutOfMemoryError がスローされます。

フレーム

各メソッド呼び出しは新しいスタック フレームを作成し、それをスタックの先頭にプッシュします。メソッドが正常に返されるか、呼び出し中にキャッチされない例外がスローされると、スタック フレームがポップされます。例外処理の詳細については、以下の「例外情報テーブル」セクションを参照してください。

各スタック フレームには次のものが含まれます。

  • ローカル変数配列
  • 戻り値
  • オペランドスタック
  • クラスの現在のメソッドのランタイム定数プール参照

ローカル変数配列

ローカル変数配列には、この参照、すべてのメソッド パラメーター、およびその他のローカル変数など、メソッド実行中のすべての変数が含まれます。クラス メソッド (つまり、静的メソッド) の場合、メソッド パラメータは添え字 0 から始まります。オブジェクト メソッドの場合、位置 0 はこのために予約されています。

次のローカル変数があります。

  • ブール値
  • バイト
  • 文字
  • 長さ
  • 短い
  • 整数
  • フロート
  • ダブル
  • 参照
  • 戻りアドレス

long 型と double 型を除き、すべての変数型はローカル変数配列内の 1 つの位置を占めます。 long と double は 64 ビット倍精度であり、他の型は 32 ビット単精度であるため、ローカル変数配列内で 2 つの連続した位置を占める必要があります。

オペランドスタック

オペランド スタックは、ネイティブ CPU レジスタと同様の方法でバイトコード命令の実行中に使用されます。ほとんどの JVM バイトコードは、オペランド スタック操作 (プッシュ、ポップ、コピー、スワップ、変数の作成と使用) に時間を費やします。そのため、ローカル変数配列とオペランドスタック間で変数を交換する命令操作は、バイトコードを通じて頻繁に実行されます。たとえば、単純な変数初期化ステートメントは、オペランド スタックと対話する 2 つのバイトコードを生成します。

  1. 整数i;

次のバイトコードにコンパイルされます。

  1. 0: iconst_0 // 0プッシュ トップ オペランドスタック
  2. 1: istore_1 // 値ポップ トップ オペランドスタック格納  ローカル変数 1

ローカル変数配列、オペランド スタック、およびランタイム定数プール間の相互作用の詳細については、「クラス ファイル構造」セクションを参照してください。

動的リンク

各スタック フレームには、ランタイム定数プールへの参照があります。この参照は、スタック フレームの現在実行中のメソッドが配置されているクラスの定数プールを指します。この参照を通じて動的リンクがサポートされます。

C/C++ コードは通常、オブジェクト ファイルにコンパイルされ、その後複数のオブジェクト ファイルがリンクされて実行可能ファイルまたは dll が生成されます。リンク フェーズでは、各オブジェクト ファイルのシンボリック参照が、最終的な実行可能ファイルの相対オフセット メモリ アドレスに置き換えられます。 Java では、リンク フェーズは実行時に動的に実行されます。

Java クラス ファイルがコンパイルされると、すべての変数およびメソッド参照がこのクラスの定数プールにシンボリック参照として保存されます。シンボリック参照は、実際には物理メモリ アドレスを指していない論理参照です。 JVM は、シンボリック参照をいつ解決するかを選択できます。 1 つのオプションは、クラス ファイルが読み込まれて検証された後に解決することです。この解決方法はハングリー法と呼ばれます。もう 1 つは、シンボル参照が初めて使用されるときに解決されることです。この解析方法は遅延メソッドと呼ばれます。いずれの場合でも、JVM は、シンボリック参照が初めて使用されるときに解決を完了し、発生する可能性のある解決エラーをスローする必要があります。バインディングは、オブジェクト フィールド、メソッド、クラスへのシンボリック参照を直接参照に置き換えるプロセスです。バインディングは一度だけ行われます。バインドされると、シンボル参照は完全に置き換えられます。クラスへのシンボリック参照がまだ解決されていない場合は、クラスがロードされます。各直接参照は、ランタイム変数またはメソッドの場所に関連付けられたストレージ構造を基準としたオフセットとして格納されます。

スレッド間の共有

ヒープ

ヒープは、実行時にクラス インスタンスと配列を割り当てるために使用されます。配列とオブジェクトはスタックに格納できません。スタック フレームは、作成後にサイズを変更できないように設計されているためです。スタック フレームには、ヒープ内のオブジェクトまたは配列への参照のみが格納されます。ローカル変数配列 (各スタック フレーム内) のプリミティブ型や参照型とは異なり、オブジェクトは常にヒープ上に格納されるため、メソッドが終了しても削除されません。オブジェクトはガベージ コレクターによってのみ削除できます。

ガベージ コレクションをサポートするために、ヒープは次の 3 つの領域に分割されます。

  • 新世代
  • エデンとサバイバーに分けられることが多い
  • 旧世代
  • 永久世代

メモリ管理

オブジェクトと配列は明示的に回収されることはありませんが、ガベージ コレクターによって自動的に回収されます。通常、プロセスは次のようになります。

  • 新しいオブジェクトと配列が作成され、古い世代に配置されます。
  • 新しい世代では、マイナーなガベージ コレクションが実行されます。まだ生きているオブジェクトは、エデン スペースからサバイバー スペースに移動されます。
  • メジャー ガベージ コレクションでは、通常、アプリケーション プロセスが一時停止し、オブジェクトが 3 つの領域間で移動されます。まだ生きているオブジェクトは、若い世代から古い世代に移動されます。
  • 古い世代がリサイクルされるたびに、永久世代もリサイクルされます。いっぱいになったらリサイクルされます。

非ヒープメモリ

非ヒープ メモリとは、論理的には JVM の一部であるが、実際にはヒープ上に作成されないオブジェクトを指します。

  • 非ヒープ メモリには次のものが含まれます。
  • 永続的な世代には以下が含まれます:
  • 方法領域
  • インターンされた文字列

コード キャッシュ: JIT コンパイラによってネイティブ コードにコンパイルされるメソッドをコンパイルして保存するために使用されます。

ジャストインタイムコンパイル (JIT)

Java バイトコードは解釈されて実行されますが、JVM ホストでネイティブ コードを直接実行するほど高速ではありません。パフォーマンスを向上させるために、Oracle Hotspot VM は最も頻繁に実行されるバイトコード スニペットを検出し、それをネイティブ マシン コードにコンパイルします。コンパイルされたネイティブ マシン コードは、非ヒープ メモリ内のコード キャッシュに保存されます。このアプローチにより、Hotspot VM は、バイトコードをネイティブ コードにコンパイルするために必要な追加時間と、バイトコードの解釈と実行に費やされる追加時間のバランスをとります。

方法領域

メソッド領域には、次のような各クラスに関する情報が格納されます。

  • クラスローダーリファレンス
  • ランタイム定数プール
  • 数値定数
  • フィールド参照
  • メソッド参照
  • 財産
  • フィールドデータ
  • フィールド名
  • タイプ
  • 修飾子
  • 属性
  • 各フィールドの情報
  • メソッドデータ
  • メソッド名
  • 戻り値の型
  • パラメータタイプ(順不同)
  • 修飾子
  • 財産
  • 各方法
  • メソッドコード
  • バイトコード
  • オペランドスタックサイズ
  • ローカル変数のサイズ
  • ローカル変数テーブル
  • 例外テーブル
  • 各例外ハンドラ
  • 出発点
  • 終点
  • 例外処理コードのプログラムカウンタ(PC)オフセット
  • キャッチされた例外クラスに対応する定数プールの添え字
  • 各方法

すべてのスレッドは同じメソッド領域を共有するため、メソッド領域データおよび動的にリンクされたプロセスへのアクセスはスレッドセーフである必要があります。 2 つのスレッドが、ロードされていないクラスのフィールドまたはメソッドにアクセスしようとする場合、そのフィールドまたはメソッドは 1 回だけロードされ、両方のスレッドはロードされるまで待機してから実行を続行する必要があります。

クラスファイルの構造

コンパイルされたクラス ファイルには、次の構造が含まれます。

javap を使用して、コンパイルされた Java クラス ファイルのバイトコードを表示できます。

次の単純なクラスをコンパイルすると:

  1. パッケージ org.jvminternals;
  2. パブリッククラスSimpleClass{
  3. パブリックvoid sayHello() {
  4. システム。 .println ( "こんにちは" );
  5. }
  6. }

次のコマンドを実行すると、次の出力が得られます: javap -v -p -s -sysinfo -constants classes/org/jvminternals/SimpleClass.class。

このクラス ファイルには、定数プール、コンストラクター メソッド、および sayHello メソッドという 3 つの主要な部分があります。

  • 定数プール: 以下に説明するように、シンボル テーブルによって通常提供されるものと同じ情報を提供します。
  • 方法: 各方法は4つの領域から構成され、
  • 署名とアクセスタグ
  • バイトコード
  • LineNumberTable: ソース コードの各行に対応するバイトコード情報をデバッガーに提供します。上記の例では、Java ソース コードの 6 行目は sayHello 関数のバイトコード番号 0 に関連付けられ、7 行目はバイトコード番号 8 に関連付けられています。
  • LocalVariableTable: すべてのスタック フレーム内のローカル変数を一覧表示します。上記の 2 つの例では、唯一のローカル変数は this です。

このクラス ファイルでは、次のバイトコード演算子を使用します。

一般的なバイトコードと同様に、オペランドとローカル変数、オペランド スタック、およびランタイム定数プールとの主な相互作用を以下に示します。

コンストラクター関数には 2 つの命令が含まれています。まず、 this 変数がオペランド スタックにプッシュされ、次に親クラスのコンストラクター関数が呼び出され、このコンストラクターが this を消費し、次に this がオペランド スタックからポップされます。

前に説明したように、 sayHello() メソッドは、ランタイム定数プールからのシンボリック参照への実際の参照を使用する必要があるため、より複雑です。最初のオペコード getstatic は、System クラスからの出力静的変数をオペランド スタックにプッシュします。次のオペコード ldc は、文字列「Hello」をオペランド スタックにプッシュします。最後に、invokevirtual 演算子は System.out 変数の println メソッドを呼び出し、オペランド スタックから "Hello" 変数を println のパラメーターとしてポップし、現在のスレッドで新しいスタック フレームを開きます。

クラスローダー

JVM が起動すると、ブートストラップ クラス ローダーを使用して初期化クラスがロードされ、このクラスは、public static void main(String[]) が呼び出される前にリンクと初期化を完了します。このメソッドを実行すると、必要な追加のクラスとインターフェースが読み込まれ、リンクされ、初期化されます。

ロードとは、クラスを表すクラス ファイルを検索するか、特定の名前に基づいてインターフェイス タイプを検索し、それをバイト配列に読み込むプロセスです。次に、バイトが解析され、クラス オブジェクトを表し、正しいメジャー バージョン情報とマイナー バージョン情報が含まれていることが確認されます。直下の親クラスのクラスとインターフェースもロードされます。これらの操作が完了すると、バイナリ表現からクラスまたはインターフェース オブジェクトが作成されます。

リンクは、クラスまたはインターフェースを検証し、型と親クラスおよび親インターフェースを準備するプロセスです。リンク プロセスは、検証、準備、およびオプションで解決の 3 つのステップで構成されます。

検証では、クラスまたはインターフェースの表現が構造的に正しく、Java 言語および JVM のセマンティック要件に準拠しているかどうかが検証されます。たとえば、次のチェックが実行されます。

  • 一貫性があり、正しくフォーマットされたシンボルテーブル
  • 最終メソッドとクラスはオーバーライドされない
  • メソッドはアクセス制御キーワードに従います
  • メソッドパラメータの数とタイプは正しい
  • バイトコードはスタックデータを不適切に操作しない
  • 変数は読み取られる前に初期化されました。
  • 変数の値は正しい型です

検証フェーズ中にこれらのチェックを実行すると、実行フェーズ中にチェックを実行する必要がなくなります。リンク段階でチェックするとクラスの読み込みが遅くなりますが、これらのバイトコードを実行するときに複数のチェックを回避できます。

準備プロセスには、JVM で使用される静的ストレージとデータ構造 (メソッド テーブルなど) 用のメモリ領域の割り当てが含まれます。静的変数は作成され、デフォルト値に初期化されますが、初期化コードは初期化プロセスの一部であるため、この段階では実行されません。

解析はオプションのフェーズです。参照されているクラスとインターフェースをロードして、これらのシンボル参照が正しいことを確認します。この段階で発生しない場合は、バイトコード命令が参照を使用するまで、シンボリック参照の解決は発生しません。

クラスまたはインターフェースの初期化は、クラスまたはインターフェースの初期化メソッドによって行われます。実行構成。

JVM には複数のクラスローダーがあり、それぞれ異なる役割を果たします。各クラス ローダーは、その親クラス ローダーによってロードされます。例外は、すべてのクラスローダーの中で最上位のクラスローダーであるブートストラップローダーです。

  • ブートストラップ ローダーは、JVM がロードされた後の初期段階で初期化されるため、通常はネイティブ コードによって実装されます。ブートストラップ ローダーは、rt.jar を含む基本的な Java API をロードする役割を担います。起動パスで見つかった信頼レベルの高いクラスのみをロードするため、通常のクラスに必要な多くの検証作業が省略されます。
  • 拡張ローダーは、セキュリティ拡張機能などの標準 Java 拡張 API のクラスをロードします。
  • システム ローダーはアプリケーションのデフォルトのクラス ローダーです。つまり、クラスパスからアプリケーション クラスをロードします。
  • ユーザー定義のクラスローダーを使用してアプリケーションクラスをロードすることもできます。カスタム クラス ローダーを使用する特別な理由は多数あります。実行時にクラスを再ロードしたり、ロードされたクラスを異なるグループに分離したりします。典型的な使用例としては、Web サーバー Tomcat などがあります。

クラスの読み込みを高速化する

共有クラス データ (CDS) は、Hotspot JVM 5.0 で導入された新しい機能です。 JVM インストール プロセス中に、インストール プロセスはコア JVM クラスのセット (rt.jar など) を共有メモリ マップ領域にロードします。 CDS は、これらのクラスのロードに必要な時間を短縮し、JVM の起動速度を向上させ、これらのクラスを異なる JVM インスタンスで共有できるようにし、メモリ消費も削減します。

メソッド領域はどこですか?

Java 仮想マシン仕様 Java SE 7 エディションでは、次のように明確に述べられています。「メソッド領域は論理的にはヒープ領域の一部ですが、単純な実装では、メソッド領域を再利用または圧縮しないことを選択できます。」 Oracle JVM の jconsle では、メソッド領域とコード キャッシュ領域が非ヒープ メモリとして扱われていることが示されていますが、OpenJDK では、CodeCache が VM 内の ObjectHeap の別のドメインとして扱われていることが示されています。

クラスローダーリファレンス

ロードされたすべてのクラスには、それらをロードしたローダーへの参照が含まれており、各クラス ローダーには、ロードするすべてのクラスへの参照が含まれています。

ランタイム定数プール

JVM は、シンボル テーブルに似たランタイム データ構造である、型固有の定数プールを維持します。より多くのデータが含まれています。 Java バイトコードにはデータが必要です。このデータはバイトコードに直接保存するには大きすぎる場合が多く、代わりにバイトコードに参照が含まれる定数プールに保存されます。ランタイム定数プールは、上記の動的リンクに使用されます。

定数プールにはさまざまな種類のデータを保存できます。

  • デジタル
  • クラスリファレンス
  • ドメイン参照
  • メソッド参照

サンプルコードは次のとおりです。

  1. オブジェクト foo = 新しい Object();

バイトコードとして記述すると次のようになります。

  1. 0: 新しい #2 // クラス java/lang/Object
  2. 1: 重複
  3. 2:invokespecial #3 // メソッド java/lang/Object "<init>" ( ) V

新しいオペコードの後に​​はオペランド #2 が続きます。このオペランドは定数プールへのインデックスであり、定数プールの 2 番目のエントリを指します。 2 番目のエンティティはクラスへの参照であり、そのクラスは定数プール内の UTF8 でエンコードされた文字列クラス名を含む別のエンティティを参照します (// Class java/lang/Object)。このシンボリック参照は、java.lang.Object クラスを検索するために使用されます。新しいオペコードはクラスのインスタンスを作成し、その変数を初期化します。新しいクラス インスタンスへの参照がオペランド スタックに追加されます。 dup オペコードは、オペランド スタックの最上位要素への参照の追加コピーを作成します。最後に、invokespecial を使用して、2 行目のインスタンス初期化メソッドを呼び出します。オペコードには、定数プールへの参照も含まれています。初期化メソッドは、メソッドへのパラメーターとしてポップされたオペランド スタックの最上部への参照を受け取ります。最後に、この新しいオブジェクトには参照が 1 つだけあり、オブジェクトが作成され、初期化されました。

次のクラスをコンパイルすると:

  1. パッケージ org.jvminternals;
  2. パブリッククラスSimpleClass{
  3.   
  4. パブリックvoid sayHello() {
  5. システム。 .println ( "こんにちは" );
  6. }
  7.   
  8. }

生成されたクラス ファイル定数プールは次のようになります。

この定数プールには次の型が含まれます。

例外テーブル

例外テーブルには、各例外処理情報が次のように格納されます。

  • 出発地
  • 終点
  • 例外処理コードの PC オフセット
  • キャッチされた例外の定数プールインデックス

メソッドに try-catch または try-finally 例外ハンドラーが定義されている場合は、例外テーブルが作成されます。ハンドラによってカバーされるコード ブロック領域や処理される例外の種類など、各例外ハンドラとコード ブロックに必要な情報を格納します。

メソッドが例外をスローすると、JVM は一致する例外ハンドラーを探します。見つからない場合、メソッドは直ちに終了し、現在のスタック フレームがポップされ、このメソッドを呼び出したメソッド (新しいスタック フレーム内) に例外が再スローされます。すべてのスタック フレームがポップされ、一致する例外ハンドラーが見つからない場合、スレッドは終了します。この例外が最後の非デーモン プロセスでスローされた場合 (たとえば、このスレッドがメイン スレッドである場合)、JVM プロセスが終了する可能性もあります。

Finally 例外ハンドラーはすべての例外タイプに一致し、どの例外がスローされても finally コード ブロックが実行されます。この場合、例外がスローされなければ、メソッドの最後に finally コード ブロックが実行されます。これは、コードが戻る前に finally ブロックにジャンプすることによって行われます。

シンボルテーブル

Hotspot JVM には、タイプ別に分割されたランタイム定数プールに加えて、永続世代のシンボル テーブルも含まれています。このシンボルテーブルは、シンボルポインタとシンボル間のマッピング関係を格納するハッシュテーブルです(つまり、ハッシュテーブル

参照カウントは、シンボル テーブルからシンボルを削除するプロセスを制御するために使用されます。たとえば、クラスがアンロードされると、定数プール内でそのクラスが所有するすべてのシンボルの参照カウントが減少します。シンボル テーブル内のシンボル参照カウントが 0 に達すると、シンボル テーブルはシンボルが参照されなくなったと見なし、シンボル テーブルからアンロードされます。後述するシンボル テーブルと文字列テーブルは、効率性を向上させ、各インスタンスが 1 回だけ出現するようにするために、正規化された構造で格納されます。

文字列テーブル

Java 言語仕様では、同一の (つまり、同じ Unicode ポインター シーケンスのシーケンスを含む) 文字列リテラルは同じ String インスタンスを指す必要があると規定されています。これに加えて、文字列インスタンスで String.intern() メソッドを呼び出すことによって返される参照は、文字列がリテラルである場合と同じである必要があります。したがって、次のコードは true を返します。

  1. ( "j" + "v" + "m" ).intern() == "jvm"  

Hotspot JVM では、インターン化された文字列は文字列テーブルに格納されます。文字列テーブルは、オブジェクトポインタとシンボル間のマッピング関係を格納するハッシュテーブルです(つまり、ハッシュテーブル

クラスがロードされると、文字列リテラルはコンパイラによって自動的にインターン化され、シンボル テーブルに追加されます。さらに、String クラスのインスタンスは、String.intern() を呼び出すことによって明示的にインターン化できます。 String.intern() メソッドが呼び出されると、シンボル テーブルにすでに文字列が含まれている場合は、シンボル テーブル内の参照が返されます。そうでない場合、文字列は文字列テーブルに追加され、参照が返されます。

<<:  ∑co 時間 |企業の支援 - ファーウェイと合肥シティクラウドが協力し、企業が新たなオンライン経済フォーマットを構築できるよう支援

>>:  VMware のブリッジ ネットワークと NAT ネットワーク モードを理解する方法

推薦する

net2hosting $9.95/年 有料ウェブホスティング

net2hosting は新しいホスティング プロバイダーです。10 ドルを支払っても構わないなら、...

v.ps: 香港 VPS、年間 35 ユーロ、香港 CMI ライン、1G メモリ/1 コア/15g NVMe/600g トラフィック/500M 帯域幅

v.ps は現在、香港のデータ センターで香港 VPS を宣伝しています。デフォルトのアップストリー...

ガートナーは、組織によるクラウド移行の加速がパブリッククラウド支出の急増につながると予測している

[[396264]]分析会社ガートナーが発表した最近の調査および予測データによると、パブリッククラウ...

#11.11# cmivps: 香港の無制限トラフィックVPSが6.64ドルから​​、香港CMI 3ネットワーク直結回線

cmivps は 11.11 向けに香港 VPS プロモーションも開始しました。比較的高構成の香港 ...

コンテンツが王様の時代にオリジナル記事を作成する方法

業界に入る前から、SEO では「外部リンクが重要、内部リンクが重要」ということが強調されていることを...

検索+ウェブサイト+ユーザー:これが中国の検索の生態学的連鎖である

最近、インターネット上ではアリババのエコロジカルチェーンについてよく耳にします。JD.comも電子商...

FinOps 面接で答えるべき 9 つの質問

FinOps は重要な機能へと進化しています。企業では、専任の FinOps 担当者を採用するケース...

今日の小売業者がクラウドに移行する必要がある理由

数多くの小売業者とのコミュニケーションや会話の中で、多くの小売業者が管理コストの増加と従来のエンター...

Baidu SEO と Google SEO には違いがありますか?ザックは、検索エンジンは良いウェブサイトを好むと言った。

百度がネガティブなニュースに登場するたびに、多くの人がグーグルを懐かしみ、グーグルが来てくれたら最高...

SEOとは何だと思いますか?

SEO はコンバージョン率の向上、ランキングの向上、ウェブサイトの重量増加を目的としていると言う人も...

VPS歴10年以上の私が初心者におすすめする、使いやすいアメリカVPS(格安VPS)20選

推奨されるアメリカの VPS、安価な VPS の推奨事項。以下の 20 の最高のアメリカの VPS ...

避けられないトレンドであるハイブリッドクラウドにとって、管理上の問題が障害となるのはなぜでしょうか?

エンタープライズ クラウド コンピューティングは止められない開発トレンドとなっています。工業情報化部...

彼は言った:SEOの専門家はアヒルを育てるために田舎に戻った

建国記念日が終わったら、また一生懸命働き始めなければなりません。これは、ほとんどの SEO 担当者が...

INIZ-$5.7/kvm/2cpu/1g メモリ/20gSSD/2T トラフィック/ロンドン/500gDDOS 保護

iniz.com が Hostcat に登場してから 1 年が経ちました。今日は、ロンドン データ ...

SEO対策をしないウェブサイト運営について

SEO はウェブサイト運営に欠かせない要素であり、オンライン マーケティングにおける「氷山の一角」で...