面接で必ず聞くべき質問 | JVM パフォーマンスチューニングについてお話ししましょう。

面接で必ず聞くべき質問 | JVM パフォーマンスチューニングについてお話ししましょう。

[[438664]]

数日考えた後、私は[バイトコードプログラミング]の更新を当面保留し、まず面接でよく聞かれるいくつかの知識ポイントを更新して、友人たちが面接で習得する必要のある知識とスキルを体系的に整理するのを助けることにしました。

主なアプローチは、面接でよく聞かれるさまざまな知識のポイントを、面接の観点から深く議論することです。

3 年ほど働いている Java プログラマーの場合、大企業での面接プロセスでは、面接官は、これまでにいくつのプロジェクトをこなしたか、CRUD レベルはどの程度かということにあまり注意を払わないかもしれません。特定の技術的なポイントに対する理解の深さに重点を置いています。そのため、3年くらい働いている友人は技術の深さに着目する必要があります。

今日は、JVM パフォーマンス チューニングについてお話します。この記事の主な構成は次のとおりです。

面接でよく聞かれる質問

JVM に関する面接でよくある質問は、「Java で作成されたオブジェクトは JVM のどの領域に保存されますか?」です。

たとえば、ここでは次のようにコード行をリストするだけです。

  1. ユーザー ユーザー= 新しいユーザー();

上記のコードに関して、作成された User オブジェクトが JVM のヒープ領域に配置され、User オブジェクトの参照がスタックに配置されるということは多くの友人が知っています。しかし、この程度の理解しかできなければ、面接官はあなたの理解が表面的すぎると考え、面接官の要求を満たしていない可能性があります。実際、面接官はあなたが JVM についてより深く理解しているかどうかを知りたいと思っています。

この質問を面接官の視点で見ると、作成された User オブジェクトは JVM のヒープ領域に配置される、と答えるのは間違いではありません。しかし、JVM のヒープメモリ領域は若い世代と古い世代に分かれており、若い世代は Eden 領域と Survivor 領域に分かれています。 JVM ヒープ領域の論理構造を次の図に示します。

面接官がさらに知りたいのは、作成されたオブジェクトが JVM ヒープ スペースのどの特定の領域に格納されるかがわかるかどうかです。

JVM 内では、ヒープ領域全体が若い世代と古い世代に分割されます。デフォルトでは、若い世代はヒープ メモリ空間全体の 1/3 を占有し、古い世代はヒープ メモリ空間全体の 2/3 を占有します。若い世代はエデンエリアと 2 つのサバイバーエリアに分かれています。それらのデフォルトの比率は、Eden: Survivor1: Survivor2 = 8:1:1 です。

新しく作成された User オブジェクトは、JVM ヒープ スペース内の若い世代の Eden 領域に格納されますと答えることができれば、面接官は感心してあなたを見るでしょう。もちろん、ここでは JVM のエスケープ解析は考慮されていません。 JVM のエスケープ解析の詳細については、「エスケープ解析」の記事を参照してください。

JVM アーキテクチャ

JVM は主に、クラス ローダー サブシステム、ランタイム データ領域 (メモリ構造)、バイトコード実行エンジンの 3 つのサブシステムで構成されています。

JVM の全体的なアーキテクチャを確認するには、まず図を見てみましょう。

Java プログラムを開発する場合、まず .java ファイルを作成し、次に .java ファイルを .class ファイルにコンパイルします。

JVM では、.class ファイルの内容がクラス ローディング サブシステムを介して JVM のランタイム データ領域にロードされ、JVM のランタイム データ領域はメソッド領域、ヒープ、スタック、ローカル メソッド スタック、プログラム カウンターの複数の部分に分割されます。

クラス ファイルの内容をロードする場合、クラス ファイルの内容は複数の部分に分割され、それぞれ JVM ランタイム データ領域の複数の部分にロードされます。その中でも、プログラム カウンターの機能は、プログラムによって実行される次の命令のアドレスを記録することであることは注目に値します。

メソッド領域はメタスペースとも呼ばれ、主にランタイム定数プール、型情報、フィールド情報、メソッド情報、クラスローダー参照、対応するクラスインスタンス参照などの情報が含まれます。

JVM では、プログラムの実行は実行エンジンを通じて行われ、実行エンジンはネイティブ メソッド インターフェイスを呼び出してネイティブ メソッド ライブラリを実行し、それによってプログラム ロジック全体の実行が完了します。

よく話題になるガベージ コレクターは実行エンジンに含まれています。プログラムの実行中、実行エンジンはガベージ コレクターを起動し、バックグラウンドで実行します。ガベージ コレクターは、プログラムの実行中に生成されたメモリ ガベージ情報を継続的に監視し、対応する戦略に従ってガベージ情報をクリーンアップします。

ここで、誰もが次の点に注意する必要があります。スタック、ローカル メソッド スタック、プログラム カウンターは実行時に各スレッド専用ですが、メソッド領域とヒープはすべてのスレッドで共有されます。したがって、スタック、ローカル メソッド スタック、およびプログラム カウンターにはスレッド セーフティの問題はありませんが、メソッド領域とヒープにはスレッド セーフティの問題があります。

メソッド領域(メタスペース)

多くの友人が「メソッド領域」という 3 つの単語を見ると、最初に思い浮かぶのはメソッドが格納されている場所かもしれません。

実際、メソッド領域の別名はメタスペースです。皆さんの多くは、メタスペースについてある程度聞いたことがあると思います。この領域は JDK1.8 で分割されました。主に、ランタイム定数プール、型情報、フィールド情報、メソッド情報、クラスローダー参照、対応するクラスインスタンス参照などの情報が含まれます。メソッド領域の情報は複数のスレッドで共有できます。

たとえば、プログラム内で宣言された定数、静的変数、クラス情報への参照はすべてメソッド領域に格納され、これらの参照によって指される特定のオブジェクトは通常、ヒープ内の別の領域に格納されますが、直接メモリに格納される場合もあります。

ヒープ

ヒープには、実際に作成されたオブジェクト、つまり new キーワードによって作成されたオブジェクトが主に格納されます。ヒープ内のオブジェクトは複数のスレッドで共有できます。ヒープ内のデータは事前​​に明確な有効期間を持つ必要がなく、メモリを動的に割り当てることができます。使用されなくなったデータとオブジェクトは、JVM の GC メカニズムによって自動的に回収されます。 JVM パフォーマンス チューニングには通常、ヒープ メモリのチューニングが含まれます。

Java の基本型 (Byte、Short、Integer、Long、Float、Double、Boolean、Character 型データ) のラッパー クラスがヒープ内に格納されます。

ヒープは一般的に若い世代と古い世代に分けられます。若い世代はさらに 1 つのエデン エリアと 2 つのサバイバー エリアに分割されます。メモリ割り当てに関しては、デフォルト構成が維持されている場合、若い世代と古い世代のメモリサイズ比は 1:2 であり、若い世代の 1 つの Eden 領域と 2 つの Survivor 領域のメモリサイズ比は 8:1:1 です。

スタック

スタックは一般的にスレッド スタックまたは仮想マシン スタックと呼ばれ、通常はローカル変数を格納します。 Java では、各スレッドには個別のスタック領域があり、各スタック内の要素はプライベートであり、他のスタックからアクセスすることはできません。スタック内のデータサイズと存続期間は固定されており、アクセス速度は比較的高速です。

Java では、すべての基本データ型 (byte、short、int、long、float、double、boolean、char) と参照変数 (オブジェクト参照) がスタック上にあります。通常、スレッドまたはメソッドが終了すると、スタック内のデータは自動的にクリアされます。

プログラムの実行中に、スタック内にメソッドごとに異なるスタック フレームが作成されます。スタック フレームには、ローカル変数テーブル、オペランド スタック、動的リンク、メソッド終了が含まれます。

ローカル変数テーブル、オペランド スタック、動的リンク、メソッド終了の具体的な機能については、Advanced Architect シリーズの今後の記事で詳しく説明します。

スタックには通常、オブジェクトへの参照が格納されます。これらの参照によって指し示される特定のオブジェクトは、通常、ヒープ内の別のアドレス空間に格納されますが、直接メモリに格納される場合もあります。

注: ここで言及されているのは、これらの参照によって指し示される特定のオブジェクトは、通常、ヒープ内の別のアドレス空間に格納されるか、または直接メモリに格納される可能性があるということです。

JVM では、エスケープ解析とスカラー置換がオンになっていると、ヒープ上にオブジェクトが作成されなくなり、オブジェクトがスタックに直接割り当てられる可能性があるためです。または、オブジェクトは作成されなくなりますが、オブジェクト内のメンバー変数がさらに分解され、スタック上に直接スペースが割り当てられ、値が割り当てられる場合があります。

ネイティブメソッドスタック

ローカル メソッド スタックは比較的単純で、ネイティブ メソッド エントリ領域のアドレスを保存します。

たとえば、Java でスレッドを作成し、Thread オブジェクトの start() メソッドを呼び出すと、ローカル メソッド start0() を通じてオペレーティング システムのスレッド作成メソッドが呼び出されます。この時点で、ローカル メソッド スタックは、start0() メソッドが入る領域のメモリ アドレスを保存します。

プログラムカウンタ

プログラム カウンター (PC カウンターとも呼ばれます) は、次に実行される命令のアドレスを格納します。

親の委任メカニズム

親の委任とは何ですか?

JVM では、クラスは親委任メカニズムを通じてロードされます。では、親委任メカニズムとは何でしょうか?まずは写真を見てみましょう。

JVM がクラスをロードする場合、現在のクラス ローダーを直接使用してクラスをロードすることはありません。まず、ロードするターゲット クラスを見つけるために親ローダーに委任します。ターゲット クラスが見つからない場合は、上位の親ローダーにそのクラスのロードを委任します。ブートストラップ クラス ローダーもロードするターゲット クラスを見つけられなくなるまで、独自のクラス ロード パスでターゲット クラスを検索してロードします。

簡単に言うと、親委任メカニズムは、まず親ローダーを使用してロードし、親ローダーがロードするターゲット クラスを見つけられない場合は、子ローダーを使用してそのクラス自体をロードするというものです。

親の委任を使用する理由は何ですか?

ここで、次のような疑問について考えたことはありますか。なぜ JVM は親委任メカニズムを使用するのでしょうか。

問題をわかりやすく説明するために、次に示すように、java.lang パッケージを作成し、その java.lang パッケージの下に String クラスを作成します。

  1. パッケージ java.lang;
  2.  
  3. /**
  4. * @author binghe (公開アカウント: Binghe Technology)
  5. * @バージョン 1.0.0
  6. * @description テスト中の親委任メカニズム
  7. */
  8. パブリッククラスString{
  9. 公共 静的void main(String[] args){
  10. システム。 out .println( "カスタマイズされた文字列クラス" );
  11. }
  12. }

ここでは、java.lang.String クラスを独自に作成しますが、JDK にも java.lang.String クラスがあります。自分で作成した java.lang.String を実行すると何が起こるでしょうか?以下のエラーメッセージが出力されます。

エラー: java.lang.String クラスに main メソッドが見つかりませんでした。main メソッドを次のように定義してください:

  1. 公共 静的void main(String[] args)

それ以外の場合、JavaFXアプリケーションクラスはjavafx.application.Applicationを拡張する必要があります。

では、なぜ JVM は親委任メカニズムを使用するのでしょうか?私たちが自分で書いたクラスが JDK のクラスを簡単に上書きできるとしたら、JDK のコードにはまったくセキュリティがないことになるのでしょうか?そうです、JVM はコード セキュリティに親委任メカニズム、つまりサンドボックス セキュリティ メカニズムを使用します。

さらに、親委任メカニズムを使用すると、同じバイトコードの複数のコピーが JVM メモリに出現するのを防ぐこともできます。たとえば、2 つのクラス A と B の両方で System クラスをロードする必要があります。 JVM が親委任メカニズムを提供しない場合、クラス A と B はそれぞれシステム バイトコードのコピーをロードし、システム バイトコードが JVM メモリに表示されます。

逆に、JVM が親委任メカニズムを提供する場合、System クラスをロードするプロセスで、親ローダーから再帰的に検索してロードし、プロセス全体で BootStrapClassLoader ローダー (通常、ブート クラス ローダーと呼ばれる) が優先されます。見つからない場合は、サブローダーを使用して段階的にロードします。

System クラスは BootStrapClassLoader でロードできます。 System クラスがクラス A の参照を通じてロードされている場合、クラス B も System クラスをロードする必要があり、BootStrapClassLoader から System クラスのロードも開始します。このとき、BootStrapClassLoader は System クラスがロードされたことを検出し、再ロードせずにメモリ内の System に直接戻ります。

この方法では、JVM メモリ内に System クラスのバイトコードのコピーが 1 つだけ存在することになります。

クラスローダーの親子関係

クラスローダーの親子関係を確認するにはどうすればいいですか?ここで、以下に示すサンプルコードを見てみましょう。

  1. /**
  2. * @author binghe (公開アカウント: Binghe Technology)
  3. * @バージョン 1.0.0
  4. * @description クラスの親委任メカニズム
  5. */
  6. パブリッククラスUser {
  7. 公共 静的void main(String[] args){
  8. ユーザー ユーザー= 新しいユーザー();
  9. システム。出力.println(ユーザー.getClass().getClassLoader());
  10. システム。出力.println(ユーザー.getClass().getClassLoader().getParent());
  11. システム。出力.println(ユーザー.getClass().getClassLoader().getParent().getParent());
  12. }
  13. }

このコードも比較的単純です。 User オブジェクトを作成し、User オブジェクトのクラス ローダー、親クラス ローダー、および上位親ローダーを出力します。上記のコードを IDEA で実行すると、次の情報が出力されます。

  1. sun.misc.Launcher$AppClassLoader@18b4aac2
  2.  
  3. sun.misc.Launcher$ExtClassLoader@135fbaa4
  4.  
  5. ヌル 

ご覧のとおり、User オブジェクトのクラス ローダーは AppClassLoader であり、親ローダーは ExtClassLoader です。出力 null は実際には BootStrapClassLoader であり、BootStrapClassLoader は上位の親ローダーです。

このように、クラスローダーの親子関係が生まれます。AppClassLoader の親ローダーは ExtClassLoader であり、ExtClassLoader の親ローダーは BootStrapClassLoader です。

ここで、親ローダーは親クラスではないことに注意することが重要です。

クラスローダーによってロードされたクラス

  • BootstrapClassLoader: %JAVA_HOME%/jre/lib ディレクトリまたは -Xbootclasspath パラメータで指定されたパス内のすべての jar パッケージをロードする役割を担います。
  • 拡張クラス ローダー (ExtClassLoader): %JAVA_HOME%/jre/lib/ext ディレクトリまたは java.ext.dirs パラメータで指定されたパス内のすべての jar パッケージをロードします。
  • アプリケーション クラス ローダー (AppClassLoader): ユーザー クラス パスで指定されたクラス ライブラリをロードする役割を担います。

注: ブートストラップ クラス ローダーと拡張クラス ローダーによってロードされるクラスは事前にロードされますが、アプリケーション クラス ローダーは、アプリケーション プロジェクトのクラスと lib の下のクラス ライブラリをロードするために使用されます。宣言されるだけで、事前に JVM メモリにロードされることはありません。使用される場合にのみ JVM メモリにロードされます。

クラスのロードプロセス

JVM でのクラスの読み込みプロセスは、通常、読み込み、検証、準備、解析、初期化の順に実行されます。

ロード: 主に、コンピュータ ディスク上の IO ストリームを通じてバイトコード ファイル (.class ファイル) を読み取ります。プログラムが特定のクラスを使用する必要がある場合、たとえば、プログラム内でクラスの静的メソッドを呼び出したり、new キーワードを使用してクラスのオブジェクトを作成したりすることで、クラスがロードされます。ロード フェーズでは、このクラスを表す Class オブジェクトが JVM のヒープ メモリに生成されることがよくあります。このオブジェクトは、JVM メソッド領域に格納されているこのクラスのさまざまなデータへのアクセス エントリとして機能し、アクセス ハンドルとも呼ばれます。

  • 検証: 主な機能は、バイトコードの正確性と JVM 仕様に準拠しているかどうかを検証することです。
  • 準備: クラスの静的変数に対応するメモリを割り当て、デフォルト値を割り当てます。
  • 分析: プログラム内のシンボリック参照を直接参照に置き換えます。ここでのシンボリック参照には、静的メソッドなどが含まれます。この段階では、静的メソッドなどの一部のシンボリック参照を、データが配置されているメモリ アドレスを指すポインターに置き換えます。これらのポインタは直接参照です。クラスのロード プロセス中にシンボリック参照から直接参照への置き換えが完了した場合、この置き換えプロセスは静的リンク プロセスと呼ばれます。実行時にシンボリック参照から直接参照への置き換えが完了した場合、その置き換えプロセスは動的リンクと呼ばれます。
  • 初期化: クラスの静的変数を初期化し、プログラムで指定された値を割り当てて、静的コード ブロック内のコードを実行します。

注意: クラスの静的変数には、準備フェーズと初期化フェーズの両方で値が割り当てられます。違いは、準備フェーズではクラスの静的変数にデフォルト値が割り当てられ、初期化フェーズではクラスの静的変数に実際の値が割り当てられることです。

例えば、プログラム内には次の静的変数があります。

  1. 公共 静的 整数 カウント= 100;

準備フェーズでは、count にデフォルト値 0 が割り当てられ、初期化フェーズでは、count に実際に値 100 が割り当てられます。

JVM チューニングパラメータ

JVM では、パフォーマンス チューニングは主にヒープ (新しい世代)、メソッド領域、スタックに対して実行されます。各リージョンのチューニングパラメータは次のとおりです。

  • ヒープ: -Xms、-Xmx
  • 新世代: -Xmn
  • メソッド領域 (メタスペース): -XX:MetaspaceSize、-XX:MaxMetaspaceSize
  • スタック(スレッド): -Xss

より直感的な説明のために、JVM のメモリ領域と対応するチューニング パラメータを次の図のようにまとめます。

JVM 起動パラメータを設定するときは、メソッド領域 (メタスペース) のパラメータ設定に特に注意する必要があります。

メソッド領域 (メタスペース) には、-XX:MetaspaceSize と -XX:MaxMetaspaceSize という 2 つの主要な JVM パラメーターがあります。

-XX:MetaspaceSize: フル GC をトリガーするメソッド領域 (メタスペース) の初期メモリ サイズ (バイト単位) を示します (メソッド領域の初期メモリ サイズは固定されていません)。デフォルトは 21M です。設定された値に達すると、Full GC がトリガーされ、ガベージ コレクターがこの値を変更します。

Full GC が発生したときに大量のメモリ領域が再利用される場合、ガベージ コレクターはこの値のサイズを適切に縮小します。フル GC が発生したときに解放されるスペースが少ない場合、この値は、設定された -XX:MetaspaceSize 値を超えずに適切に増加されます。また、-XX:MetaspaceSize 値が設定されていない場合は 21M を超えないように増加されます。

-XX:MaxMetaspaceSize: メソッド領域 (メタスペース) の最大値を参照します。デフォルト値は -1 であり、ヒープ メモリ サイズによって制限されません。現時点では、ローカル メモリのサイズによってのみ制限されます。

最後に、メソッド領域 (メタスペース) のサイズを調整すると、非常にコストのかかる操作である Full GC が発生することに注意することが重要です。アプリケーションの起動時に Full GC が発生する場合は、メソッド領域 (メタスペース) のサイズが動的に調整されている可能性が非常に高くなります。

そのため、JVM がメソッド領域 (メタスペース) のサイズを動的に調整して Full GC が頻繁に発生するのを防ぐために、-XX:MetaspaceSize と -XX:MaxMetaspaceSize は通常同じ値に設定されます。例えば、物理メモリが8Gの場合、これら2つの値を256Mに設定できます。

最後に、8G の物理メモリでアプリケーションを起動するときに設定できる JVM パラメータを見てみましょう。もちろん、ここで示すのは経験的な値です。実際に本番環境に導入する場合は、最適なパラメータ値を見つけるためにストレス テストが必要になります。

  • SpringBootを起動する
  1. java ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐jar xxx.jar
  • Tomcat を起動する (Linux)

Tomcat bin ディレクトリの catalina.sh ファイルで設定します。

  1. ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:メタスペースサイズ=256M ‐XX:最大メタスペースサイズ=256M
  • Tomcat を起動する (Windows)

Tomcat bin ディレクトリの catalina.bat ファイルで設定します。

  1. ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:メタスペースサイズ=256M ‐XX:最大メタスペースサイズ=256M

要約する

この記事では、面接の文脈で JVM に関する一般的な質問について説明します。この記事は、大手インターネット企業の面接における JVM の重要性を説明するために、一般的な面接の質問の例から始まります。次に、メソッド領域 (メタスペース)、ヒープ、スタック、ローカル メソッド スタック、プログラム カウンターを含む JVM アーキテクチャについて説明します。

次に、JVM の親委任メカニズムが紹介され、親委任とは何か、親委任メカニズムが使用される理由、クラスローダーの親子関係について説明しました。ここで言うクラスローダーの親子関係は、親クラスと子クラスの関係ではないことに注意してください。次に、各クラスローダーがどのクラスをロードするかを紹介します。

次に、主にロード、検証、準備、解析、初期化の手順を含むクラス ロード プロセスについて説明します。同時に、各ステップの主な機能についても説明します。

最後に、JVM でよく使用されるチューニング パラメータについて紹介します。ヒープ、新しい世代、メソッド領域 (メタスペース)、スタック (スレッド) など、よく使用されるチューニング パラメータについて説明します。 Tomcat チューニングを例に、これらのチューニング パラメータの使用方法を詳しく説明します。

これらすべてをマスターしましたか?

この記事はWeChatの公開アカウント「Glacier Technology」から転載したものです。下のQRコードからフォローできます。この記事を転載する場合は、Glacier Technology 公式アカウントまでご連絡ください。

<<:  クラウドネイティブセキュリティは必須ですか?

>>:  企業が今すぐデータセンターからクラウドプラットフォームに移行すべき理由

推薦する

クラウドとオンプレミス間のセキュリティギャップを埋める方法

ただし、東西トラフィック、つまり内部ネットワークとデータセンターを通過するがネットワーク境界を越えな...

B2Cウェブサイト向けの小さなWeibo、大規模なマーケティング

現在、多くのウェブサイト所有者は、タオバオの代理店であろうと、他のオンライン収益ウェブサイトの所有者...

Zhihu 創設者 周元: オンライン コミュニティはどのようにして 0 から 400 万に成長したのでしょうか?

周元氏は、知乎の核となる機能は、ユーザーが(問題について)議論して知乎に投稿すると、コミュニティ全体...

【WOT2018】Shen Jian: 58 Expressアーキテクチャの分離とマイクロサービスの実践

[51CTO.comより引用] 2018年5月18日〜19日、51CTO主催のグローバルソフトウェア...

クラウドナビゲーション: 適切なプロバイダーを選択する方法

コンピュータ システム リソースをホストするサードパーティ インフラストラクチャを探す場合、クラウド...

ftpit: バレンタインデーに50%オフ、ウェブサイト構築におすすめ、安価で安定したVPSベンダー

ftpit はバレンタインデー プロモーションを実施しています。これはこれまでで最大の割引となるはず...

SEOがウェブサイトを分析する方法

SEO を行うには、競合他社の Web サイト (つまり、検索で上位にランクされているサイト) を分...

重要な機能! Borei Data APMは、企業がクラウドネイティブアーキテクチャの進化に冷静に対応できるよう支援します。

最近、Bonree Data のアプリケーション パフォーマンス モニタリング製品である Bonre...

ウェブサイトのオリジナルコンテンツを深く掘り下げて分析する

今日の SEO は独創性の時代です。Web サイトのオリジナル コンテンツが多ければ多いほど、SEO...

ウェブサイト上のglobal.asaトロイの木馬の被害と解決策

この期間中、顧客の Web サイトがハッキングされたケースがいくつか見つかりました。これらの Web...

SEO の基礎 第 11 章: ランキングに影響を与える要因

1. タイトルに使われているキーワードキーワードランキングに影響を与える最大の要因はタイトルに使用さ...

SEO実践体験:ウェブサイトがKになる問題を解決する方法

1. ブロックされたウェブサイトとは何ですか?ウェブサイトがブロックされると、ウェブマスターはそれを...

hostyun による、日本大阪の AMD-IIJ シリーズ VPS (月額 20 元、500M 帯域幅) の簡単な評価

hostyun が最近日本の大阪で使用したサーバーは、ハイエンドの AMD プラットフォームを使用し...

デジタルオーシャンはどうですか? [年] Digitalocean のニューヨーク データ センターの簡単なレビュー

1年が経ちました。あなたのデジタルオーシャンはいかがですか? DigitalOcean のネットワー...

3つの共同購入会社を経験した内部関係者は「美団を尊敬している」と語った。

私は2011年2月14日のバレンタインデーに共同購入サイトWoWotuanに参加し、その後24qua...