Java クラウド ネイティブの実践におけるメモリの問題の解釈

Java クラウド ネイティブの実践におけるメモリの問題の解釈

Java は、活発なオープン ソース コミュニティと完全な環境上の利点のおかげで、過去 20 年間で最も人気のあるプログラミング言語の 1 つとなっています。クラウドネイティブ時代に入り、急成長するクラウドネイティブテクノロジーは、クラウドコンピューティングの利益を解き放ち、ビジネスのクラウドネイティブ変革を促進し、企業のデジタル変革を加速します。

しかし、Java のクラウド ネイティブ変換には大きな課題が伴います。 Java の動作メカニズムとクラウドネイティブの特性の間には多くの矛盾があります。企業はクラウドネイティブテクノロジーを使用して徹底的なコスト最適化を実施し、リソースコスト管理はかつてないレベルにまで引き上げられました。パブリック クラウド上のリソースは使用量に基づいて課金されるため、ユーザーはリソース使用量に非常に敏感です。メモリ使用量の点では、Java 仮想マシンに基づく実行メカニズムにより、どの Java プログラムにも固定の基本メモリ オーバーヘッドが発生します。 C++/Golangなどのネイティブ言語と比較すると、Javaアプリケーションは膨大な量のメモリを占有し、「メモリイーター」と呼ばれます。したがって、Java アプリケーションをクラウドに移行するにはコストがかかります。さらに、アプリケーションがクラウドに統合されると、システムの複雑さが増します。一般ユーザーは、クラウド上の Java アプリケーションのメモリを明確に理解しておらず、アプリケーションのメモリを適切に構成する方法を知りません。 OOM 問題が発生すると、トラブルシューティングが困難になり、多くの問題が発生します。

ヒープメモリが Xmx を超えていないのに OOM が発生するのはなぜですか?オペレーティング システムと JVM 間のメモリ関係を理解するにはどうすればよいでしょうか?プログラムが占有するメモリが Xmx を超えるのはなぜですか?メモリはどこで使用されますか?オンライン コンテナー内のプログラムにより多くのメモリが必要なのはなぜですか?この記事では、クラウドネイティブ Java アプリケーションの進化の実践において EDAS ユーザーが遭遇する問題を分析し、クラウドネイティブ Java アプリケーションのメモリ構成に関する推奨事項を示します。

1. 背景知識

K8s アプリケーションのリソース構成

クラウドネイティブ アーキテクチャは K8s に基づいています。アプリケーションは K8s にデプロイされ、コンテナ グループの形式で実行されます。 K8s リソース モデルには、リソース要求とリソース制限の 2 つの定義があります。 K8s は、コンテナに要求された数のリソースがあることを保証しますが、コンテナが制限数を超えるリソースを使用することを許可しません。次のメモリ構成を例にとると、コンテナは少なくとも 1024Mi のメモリ リソースを取得できますが、4096Mi を超えることはできません。メモリ使用量が制限を超えると、コンテナは OOM 状態になり、K8s コントローラによって再起動されます。

仕様:
コンテナ:
-名前:エダス
画像:アリババ/エダス
リソース
リクエスト:
メモリ 「1024Mi」
制限:
メモリ 「4096Mi」
コマンド: [ "java" "-jar" "edas.jar" ]

コンテナOOM

コンテナの OOM メカニズムについては、まずコンテナの概念を確認する必要があります。コンテナについて話すとき、これはサンドボックス テクノロジであると言われます。サンドボックスとして、コンテナは内部で比較的独立しており、境界とサイズがあります。コンテナ内の独立した動作環境は、Linux 名前空間メカニズムを通じて実装されます。コンテナ内の PID、マウント、UTS、IPD、ネットワークなどの名前空間は隠されているため、ホストの名前空間や他のコンテナの名前空間はコンテナ内では見えません。いわゆるコンテナの境界とサイズは、コンテナによる CPU、メモリ、IO、その他のリソースの使用に対する制約を指します。そうしないと、単一のコンテナが多くのリソースを占有しすぎて、他のコンテナの実行速度が低下したり、異常な動作をしたりする可能性があります。 cgroup は、Linux カーネルによって提供されるメカニズムであり、単一のプロセスまたは複数のプロセスによって使用されるリソースを制限できます。これは、コンテナ リソース制約を実装するためのコア テクノロジーでもあります。コンテナは、オペレーティング システムの観点から見ると、単なる特別なプロセスにすぎません。プロセスのリソース使用量は、Cgroup の制約に従います。プロセスによって使用されるメモリの量が Cgroup 制限を超えると、システムの OOM Killer によって容赦なくプロセスが強制終了されます。

したがって、いわゆるコンテナ OOM は、実際には Linux システム上で実行されているコンテナ プロセスに OOM があることを意味します。 Cgroup は、あまり知られていないテクノロジーではありません。 Linux ではこれをファイル システムとして実装しており、これはすべてがファイルであるという Unix の哲学と一致しています。 Cgroup V1 の場合、コンテナ内の /sys/fs/cgroup/ ディレクトリで現在のコンテナの Cgroup 構成を直接表示できます。

コンテナ メモリの場合、memory.limit_in_bytes と memory.usage_in_bytes は、メモリ制御グループで最も重要な 2 つのパラメーターです。前者は現在のコンテナ プロセス グループで使用できるメモリの最大値を識別し、後者は現在のコンテナ プロセス グループで実際に使用されているメモリの合計です。一般的に、使用値が最大値に近いほど、OOM のリスクが高くなります。

 # 現在のコンテナのメモリ制限
$ cat /sys/fs/cgroup/memory/memory.limit_in_bytes
4294967296
# 現在の実際のコンテナメモリ使用量
$ cat /sys/fs/cgroup/memory/memory.usage_in_bytes
39215104

JVM の OOM

OOM に関しては、Java 開発者は JVM OOM に精通しています。 JVM にオブジェクト用のスペースを割り当てるための十分なメモリがなく、ガベージ コレクターに再利用するスペースがない場合、java.lang.OutOfMemoryError がスローされます。 JVM 仕様によれば、プログラム カウンターを除く他のメモリ領域で OOM が発生する可能性があります。最も一般的な JVM OOM 状況は次のとおりです。

  • java.lang.OutOfMemoryError: Java ヒープ スペースのヒープ メモリがオーバーフローしました。このエラーは、ヒープ メモリに新しく作成されたオブジェクトを格納するための十分なスペースがない場合にスローされます。これは通常、メモリ リークまたは不適切なヒープ サイズ設定によって発生します。メモリ リークの場合は、メモリ監視ソフトウェアを使用してプログラム内のリークしているコードを見つける必要があります。ヒープ サイズは、-Xms、-Xmx などのパラメータを使用して変更できます。
  • java.lang.OutOfMemoryError: PermGen スペース / Metaspace 永続生成 / メタスペース オーバーフロー。永続世代には、クラス情報や定数などのオブジェクトが格納されます。 JDK 1.8 では、永続世代が Metaspace に置き換えられました。このエラーは通常、ロードされるクラスが多すぎるか、クラスのサイズが大きすぎるためにスローされます。 -XX:MaxPermSize または -XX:MaxMetaspaceSize 起動パラメータを変更することで、永続世代/メタスペースのサイズを増やすことができます。
  • java.lang.OutOfMemoryError: 新しいネイティブ スレッドを作成できません。新しいスレッドを作成できません。各 Java スレッドには一定量のメモリ領域が必要です。 JVM が基盤となるオペレーティング システムに新しいネイティブ スレッドの作成を要求すると、十分なリソースが割り当てられていない場合にこのタイプのエラーが報告されます。考えられる理由としては、ネイティブ メモリの不足、スレッド リークによりスレッド数がオペレーティング システムの最大スレッド数の ulimit 制限を超えていること、またはスレッド数が kernel.pid_max を超えていることなどが挙げられます。状況に応じて、リソースのアップグレード、スレッド プール サイズの制限、スレッド スタック サイズの削減などの操作を行う必要があります。

2. ヒープメモリが Xmx を超えていないのに OOM が発生するのはなぜですか?

多くの人がこのシナリオに遭遇したことがあると思います。 K8s にデプロイされた Java アプリケーションは頻繁に再起動され、コンテナの終了ステータスは終了コード 137、理由: OOM Killed となります。すべての情報は明らかな OOM を示しています。ただし、JVM 監視データは、ヒープ メモリ使用量が最大ヒープ メモリ制限 Xmx を超えていないことを示しています。 OOM 自動ヒープダンプ パラメータが設定されると、OOM が発生してもダンプ ファイルは生成されません。

上記の背景知識によれば、コンテナ内の Java アプリケーションでは、JVM OOM とコンテナ OOM という 2 種類の OOM 例外が発生する可能性があります。 JVM OOM は、JVM メモリ領域のスペース不足によって発生するエラーです。 JVM は積極的にエラーをスローし、プロセスを終了します。データを観察すると、メモリ使用量が制限を超えていることがわかります。JVM は対応するエラー レコードを残します。コンテナの OOM はシステムの動作です。コンテナ プロセス グループ全体で使用されるメモリが Cgroup 制限を超え、システム OOM Killer によって強制終了されます。関連する記録はシステム ログと K8s イベントに残されます。

一般に、Java プログラムのメモリ使用量は、JVM と Cgroup の両方からの制限を受けます。 Java ヒープ メモリは Xmx パラメータによって制限されます。制限を超えると、JVM OOM が発生します。プロセス全体のメモリは、コンテナのメモリ制限値によって制限されます。制限を超えると、コンテナ OOM が発生します。 OOM を識別してトラブルシューティングし、必要に応じて構成を調整するには、観測データ、JVM エラー レコード、システム ログ、K8s イベントを組み合わせる必要があります。

3. オペレーティング システムと JVM 間のメモリ関係を理解するにはどうすればよいでしょうか?

前述のように、Java コンテナの OOM は、実際には、Java プロセスによって使用されるメモリが Cgroup 制限を超え、オペレーティング システムの OOM Killer によって強制終了されることを意味します。では、オペレーティング システムの観点から、Java プロセスのメモリをどのように見ればよいのでしょうか?オペレーティング システムと JVM にはそれぞれ独自のメモリ モデルがあります。これら 2 つはどのようにマッピングされますか? Java プロセスにおける OOM の問題を調査するには、JVM とオペレーティング システム間のメモリ関係を理解することが重要です。

最も一般的に使用される OpenJDK を例にとると、JVM は基本的にオペレーティング システム上で実行される C++ プロセスであるため、そのメモリ モデルも Linux プロセスの一般的な特性を備えています。 Linux プロセスの仮想アドレス空間は、カーネル空間とユーザー空間に分かれています。ユーザー空間はさらに多くのセグメントに分割されます。ここでは、この記事に関連性の高いいくつかのセグメントを選択して、JVM メモリとプロセス メモリのマッピング関係について説明します。

  • コードスニペット。一般的にはメモリ内のプログラム コードのマッピングを指しますが、ここでは Java コードではなく JVM 独自のコードであることが特に指摘されています。
  • データ セグメント。プログラムの先頭で変数に初期化されたデータは、JVM 自体のデータです。
  • ヒープスペース。ランタイム ヒープは、Java プロセスと通常のプロセスの間で最も異なるメモリ セグメントです。 Linux プロセス メモリ モデルのヒープは、実行時にプロセスによって動的に割り当てられるオブジェクト用のメモリ領域を提供します。また、JVM メモリ モデルのほぼすべては、実行時に JVM プロセスによって作成される新しいオブジェクトです。 JVM メモリ モデル内の Java ヒープは、JVM によってプロセス ヒープ領域上に作成される論理領域にすぎません。
  • スタックスペース。プロセスの実行スタックはここに保存されます。これは、JVM メモリ モデル内のスレッド スタックではなく、JVM 自体を実行するときにオペレーティング システムが保持する必要がある実行データです。

前述のように、ヒープ スペースは Linux プロセス メモリ レイアウトと JVM メモリ レイアウトの両方に共通する概念です。それは最も混乱を招き、最も異なる概念です。 Java ヒープは Linux プロセス ヒープよりも小さくなります。これは、JVM によってプロセス ヒープ領域上に作成される論理領域です。プロセス ヒープ スペースには、Java スレッド スタック、コード キャッシュ、GC、コンパイラ データなど、JVM 仮想マシンの動作をサポートするメモリ データも含まれます。

4. プログラムが占有するメモリが Xmx を超えるのはなぜですか?メモリはどこで使用されますか?

Java 開発者の観点から見ると、Java コード実行中に作成されたオブジェクトは Java ヒープに配置されるため、多くの人が Java ヒープ メモリを Java プロセス メモリと同一視し、Java ヒープ メモリ制限パラメータ Xmx をプロセス メモリ制限パラメータとして使用し、コンテナ メモリ制限を Xmx と同じサイズに設定し、残念ながらコンテナが OOM されていることに気付きます。

実は、誰もがよく知っているヒープ メモリに加えて、JVM には非ヒープ メモリと呼ばれるものもあります。 JVM によって管理されるメモリに加えて、JVM をバイパスせずに直接開かれるローカル メモリもあります。 Java プロセスのメモリ使用量は、次のように簡単にまとめることができます。

JDK8 では、JVM の内部メモリ使用量を追跡できるネイティブ メモリ トラッキング (NMT) 機能が導入されました。デフォルトでは、NMT は無効になっています。 JVM パラメータを使用して有効にします: -XX:NativeMemoryTracking=[off |概要 |詳細]

 $ java - Xms300m - Xmx300m - XX : + UseG1GC - XX : NativeMemoryTracking = summary - jar app .jar

ここでは、最大ヒープメモリは 300M に制限され、GC アルゴリズムとして G1 が使用され、プロセスのメモリ使用量を追跡するために NMT が有効になっています。

注意: NMT を有効にすると、5% ~ 10% のパフォーマンス オーバーヘッドが発生します。

NMT を有効にすると、jcmd コマンドを使用して JVM メモリ使用量を出力できます。ここではメモリの概要情報のみを表示でき、設定単位は MB です。

 $ jcmd < pid > VM .native_memoryサマリー スケール= MB 

JVM 合計メモリ

ネイティブメモリトラッキング:
合計:予約済み= 1764 MB コミット済み= 534 MB

NMT レポートによると、プロセスには現在 1764 MB の予約済みメモリと 534 MB のコミット済みメモリがあり、これは最大ヒープ メモリの 300 MB を大幅に上回っています。予約とは、プロセスが使用できるメモリの量として理解できる、プロセスに対して連続した仮想アドレス メモリを開くことを指します。送信とは、仮想アドレスを物理メモリにマッピングすることを指し、これはプロセスによって現在占有されているメモリの量として理解できます。

NMT によってカウントされるメモリは、オペレーティング システムによってカウントされるメモリとは異なることに注意してください。 Linux はメモリを割り当てるときに遅延割り当てメカニズムに従います。プロセスが実際にアクセスした場合にのみ、メモリ ページを物理メモリにスワップします。したがって、top コマンドで表示されるプロセスの物理メモリ使用量は、NMT レポートで表示されるものとは異なります。ここでは、JVM の観点からメモリ使用量を示すために NMT のみが使用されます。

Javaヒープ

 Java ヒープ(予約済み= 300 MB コミット済み= 300 MB ) ( mmap :予約済み= 300 MB コミット済み= 300 MB )

Java ヒープメモリはそのまま設定され、実際に 300M のメモリ空間が解放されます。

メタスペース

クラス(予約済み= 1078 MB コミット済み= 61 MB ) (クラス #11183 ) ( malloc = 2 MB #19375 ) ( mmap :予約済み= 1076 MB コミット済み= 60 MB )

ロードされたクラスは Metaspace に保存され、11,183 個のクラスがロードされ、約 1G が保持され、61M が送信されます。

ロードするクラスが増えるほど、Metaspace の使用量も増えます。メタスペースのサイズは、-XX:MaxMetaspaceSize (デフォルトでは無制限) と -XX:CompressedClassSpaceSize (デフォルトでは 1G) によって制限されます。

スレッド(予約済み= 60 MB コミット済み= 60 MB ) (スレッド #61 ) (スタック:予約済み= 60 MB コミット済み= 60 MB )

JVM スレッド スタックにも一定量のスペースが必要です。ここでは、61 個のスレッドが 60M のスペースを占有し、各スレッドのデフォルトのスタック サイズは約 1M です。スタック サイズは -Xss パラメータによって制御されます。

コードキャッシュ

コード(予約済み= 250 MB コミット済み= 36 MB ) ( malloc = 6 MB #9546 ) ( mmap :予約済み= 244 MB コミット済み= 30 MB )

コード キャッシュは主に、JIT コンパイラとネイティブ メソッドによってコンパイルされたコードを格納するために使用されます。現在、36M のコードがキャッシュされています。コード キャッシュの容量は、-XX:ReservedCodeCacheSize パラメータを使用して設定できます。

GC

 GC (予約済み = 47MB、コミット済み = 47MB)
(malloc=4MB #11696)
(mmap: 予約済み = 43MB、コミット済み = 43MB)

GC ガベージ コレクターは、GC 操作をサポートするために、ある程度のメモリ領域も必要とします。 GC によって占有されるスペースは、選択された特定の GC アルゴリズムに関連しています。ここでの GC アルゴリズムは 47M を使用します。他の構成は同じまま、SerialGC に切り替えます。

 GC (予約済み = 1MB、コミット済み = 1MB)
(mmap: 予約済み = 1MB、コミット済み = 1MB)

SerialGC アルゴリズムは 1M のメモリのみを使用することがわかります。これは、SerialGC が単純なデータ構造と少量の計算データを含む単純なシリアル アルゴリズムであるため、メモリ使用量も少ないためです。ただし、単純な GC アルゴリズムはパフォーマンスの低下を招く可能性があるため、選択を行う前にパフォーマンスとメモリパフォーマンスのバランスを取る必要があります。

シンボル

シンボル (予約済み = 15MB、コミット済み = 15MB)
(malloc=11MB #113566)
(アリーナ=3MB #1)

JVM のシンボルには、15M を占めるシンボル テーブルと文字列テーブルが含まれています。

非JVMメモリ

NMT は JVM 内のメモリのみをカウントでき、一部のメモリは JVM によって管理されません。 JVM 管理メモリに加えて、プログラムはオフヒープ メモリ ByteBuffer.allocateDirect を明示的に要求することもできます。これは -XX:MaxDirectMemorySize パラメータによって制限されます (デフォルトは -Xmx に等しい)。 System.loadLibrary によってロードされた JNI モジュールは、JVM 制御なしでオフヒープ メモリに適用することもできます。

要約すると、Java プロセスのメモリ使用量を正確に推定できるモデルは実際には存在しません。できるだけ多くの要素を考慮するしかありません。これらのメモリ領域の一部は、コード キャッシュやメタスペースなどの JVM パラメータによって容量が制限される場合がありますが、一部のメモリ領域は JVM によって制御されず、特定のアプリケーションのコードに関連しています。

合計メモリ = ヒープ + コードキャッシュ + メタスペース + スレッドスタック +
シンボル + GC + 直接バッファ + JNI + ...

5. オンライン コンテナーではローカル テストよりも多くのメモリが必要なのはなぜですか?

同じコードをオンライン コンテナーで実行すると、ローカルで実行するよりも常に多くのメモリが消費され、OOM が発生する可能性がある理由について、ユーザーからよく報告されます。考えられる状況は次のとおりです。

コンテナ対応のJVMバージョンを使用していない

一般的な物理マシンまたは仮想マシンでは、-Xmx パラメータが設定されていない場合、JVM は一般的な場所 (Linux の /proc ディレクトリなど) から使用できる最大メモリ量を検索し、ホストの最大メモリの 1/4 をデフォルトの JVM 最大ヒープ メモリとして使用します。初期の JVM バージョンはコンテナに適合していませんでした。コンテナ内で実行する場合、JVM の最大ヒープはホスト メモリの 1/4 に設定されていました。一般的なクラスター ノードのホスト メモリは、ローカル開発マシンのホスト メモリよりもはるかに大きくなります。コンテナ内の Java プロセスのヒープ領域が大きいほど、消費するメモリも多くなります。同時に、コンテナは Cgroup リソース制限の対象となります。コンテナプロセスグループのメモリ使用量が Cgroup 制限を超えると、OOM になります。このため、OpenJDK 8u191 以降では、デフォルトで有効になっている UseContainerSupport パラメータが導入され、コンテナ内の JVM がコンテナのメモリ制限を認識し、Cgroup メモリ制限の 1/4 に応じて最大ヒープ メモリを設定できるようになりました。

オンラインサービスはより多くのメモリを消費する

外部にサービスを提供するビジネスでは、新しいオブジェクトの作成や実行スレッドの開始など、よりアクティブなメモリ割り当てアクションが発生することがよくあります。これらの操作にはメモリ領域の割り当てが必要となるため、オンライン ビジネスでは多くのメモリが消費されることがよくあります。より多くのメモリを消費します。そのため、サービス品質を確保するためには、自社の業務トラフィックに応じてアプリケーションメモリ構成を適宜拡張する必要があります。

6. クラウドネイティブJavaアプリケーションのメモリ構成に関する推奨事項

  1. コンテナ対応の JDK バージョンを使用します。 Cgroup V1 を使用するクラスターの場合は、8u191+、Java 9、Java 10 以降にアップグレードする必要があります。 Cgroup V2 を使用するクラスターの場合は、8u372+ または Java 15 以降にアップグレードする必要があります。
  2. NativeMemoryTracking (NMT) を使用して、アプリケーションの JVM メモリ使用量を把握します。 NMT は JVM のメモリ使用量を追跡できます。テストフェーズでは、NMT を使用して、プログラム JVM によって使用されるメモリのおおよその分布を把握し、メモリ容量構成の参照として使用できます。 NMT を有効にするには、JVM パラメータ -XX:NativeMemoryTracking を使用します。 NMT を有効にすると、jcmd コマンドを使用して JVM メモリ使用量を出力できます。
  3. Java プログラムのメモリ使用量に基づいてコンテナのメモリ制限を設定します。コンテナの Cgroup メモリ制限値は、コンテナに設定されたメモリ制限値から取得されます。コンテナ プロセスが使用するメモリが制限を超えると、コンテナ OOM が発生します。通常のプログラム操作中や業務変動時に OOM が発生しないようにするには、コンテナのメモリ制限を Java プロセスが使用するメモリより 20% ~ 30% 高く設定する必要があります。プログラムを初めて実行していて、実際のメモリ使用量がわからない場合は、より大きな制限を設定してプログラムをしばらく実行し、観察されたプロセスのメモリ使用量に応じてコンテナのメモリ制限を調整することができます。
  4. OOM が発生したときにメモリ スナップショットを自動的にダンプし、PVC を使用して hostPath、OSS、または NAS にマウントするなど、ダンプ ファイル用の永続ストレージを構成して、その後のトラブルシューティングをサポートするためにオンサイト データを可能な限り保持します。​

<<:  クラウドネイティブのビッグデータについて1つの記事で学ぶ

>>:  クラスターノードの弾性スケーリング

推薦する

abelohost: オランダの Windows VPS、苦情防止、40G 防御、無制限のトラフィック

オランダのサーバー業者 abelohost が新製品を発表しました。オランダの Windows VP...

サイトを定期的かつ定量的に更新します。まだ続けている人はいますか?

ウェブサイトの最適化は、多くの分野からなる非常に包括的なタスクです。しかし、最も基本的な最適化作業と...

広告のコンバージョン率を向上させるための中核要素:テーマ、効果、場所

現在、ほとんどのウェブサイトの主な収入源は広告です。しかし、多くのウェブマスターは、広告収入が低いこ...

群集心理に基づく SEO 最適化の効果は良くない - A5 Webmaster Network

Baidu は新たな変更を加えました。検索結果を収集、共有、報告できるようになりました。これは LE...

蘇寧社や美団社が相次いでクラウド市場から撤退する中、中小メーカーはどうやって生き残ることができるのだろうか?

最近、蘇寧クラウドと美団クラウドが相次いでクラウド市場からの撤退を発表し、それぞれ長年続いたクラウド...

クラウド向けの 3 つの Linux 暗号化ツール

[51CTO.com クイック翻訳] セキュリティの観点から、クラウド ストレージは存在すべきではあ...

ベアメタルクラウドサービスはクラウドコンピューティングの次のトレンドです

クラウド コンピューティング サービス、特に Infrastructure as a Service...

社内インキュベーションから外部クラウドへ: Tencent TAPD の 12 年間の歩み

[51CTO.comより引用] 最近、テンセントの新たな構造調整後の最大規模のカンファレンスである2...

RCIOL-韓国データセンター 8コア8G独立サーバー 300人民元

rciol.com は香港に登録されているホスティング会社です (Smarton (Hong Kon...

企業のウェブサイトはWeiboを使ってクレイジーなWeiboマーケティングをどのように実行できるのでしょうか?

今、Weibo はますます人気が高まっています。Weibo の魅力に感心するしかありません。140 ...

Kafka が高速である 6 つの理由

[[335450]]この記事はWeChatの公開アカウント「JavaKeeper」から転載したもので...

最適化された企業ウェブサイトのホームページデザインスケッチ

著者は以前、「企業ウェブサイトの最適化におけるいくつかの重要なポイントの簡単な分析」という記事を書い...

企業がWeiboをマーケティングツールとして有効活用する方法に関する私の意見

近年、WeChatの登場により、Sina Weiboに取って代わる傾向が見られます。多くのネットユー...

テンセントはデジタル広東の構築に積極的に参加し、「デジタル政府」改革の成果が現れ始めている

5月23日、広州で、広東省人民政府を指導機関として、広東省経済情報化委員会とテンセントが共催する20...