本番環境におけるJDK1.6によるピット

この記事は、Huawei Cloud Community「【高同時実行性】本番環境のJDK1.6によって引き起こされるピットを覚えておいてください!」から共有されたものです。"、著者: 氷河。

最近、友人が混乱に遭遇しました。彼が作成したプログラムは、テスト環境ではまったく問題ないのに、本番環境に送信するとメモリ オーバーフローが頻繁に発生します。この問題は一週間以上彼を悩ませている。

その後、トラブルシューティングの過程で、この小さなパートナーが使用していた JDK がまだバージョン 1.6 であることがわかりました。最初はあまり深く考えず、彼の書いたコードをチェックし続けましたが、特に問題は見つかりませんでした。しかし、実稼働環境でプログラムが開始されると、JVM がメモリ オーバーフロー例外をスローするまでにそれほど時間はかかりませんでした。

これは変です、何が起こっているのですか?

プログラムの起動時に適切な JVM パラメータを追加しても、問題は依然として存在します。

まさか、彼のコードを続けて見てください! 偶然にも、彼が書いたコードでは、文字列をインターセプトするために String クラスの substring() メソッドが広範囲に使用されていることがわかりました。そこで、JDK のコードに従って、渡されたパラメーターを確認しました。

うっかりクリックして確認してみたところ、問題が見つかりました。

JDK1.6のStringクラスの穴

分析した結果、JDK1.6 の String クラスに大きな穴があることがわかりました。なぜ穴だと言えるのですか?それは、その substring() メソッドが人々を不幸にするからです。あまり言うことはありませんが、まず JDK1.6 の String クラスの substring() メソッドを見てみましょう。

public String substring(int bedinIndex, int endIndex){ 
    if(beginIndex < 0){ 
        throw new StringIndexOutOfBoundsException(beginIndex); 
    if 
    (endIndex > count){ 
        throw new StringIndexOutOfBoundsException(endIndex); 
    if(beginIndex > endIndex){ 
          throw new StringIndexOutOfBoundsException(endIndex - beginIndex) 
    ; 
    return 
    ((beginIndex == 0) && (endIndex == count)) ? this : new String(offset + beginIndex, endIndex - beginIndex, value); 
}

次に、JDK1.6におけるStringクラスの構築メソッドを以下に示します。

String(int オフセット, int カウント, char[] 値){ 
    this.value = 値; 
    this.offset = オフセット; 
    this.count = カウント; 
}

ここで、注意深い友人が問題を発見したと思います。問題の犯人は次のコード行です。

this.value = 値;

JDK1.6 では、String クラスのコンストラクターを使用して部分文字列を作成する場合、それは必要なオブジェクトの単なるコピーではなく、値全体が毎回引用符で囲まれます。元の文字列が比較的大きい場合、文字列が使用されなくなったとしても、文字列によって割り当てられたメモリは解放されません。 これは私が長い間コードを分析した結果得た結論でもありますが、本当に残念です。

問題が見つかったので、それを修正しましょう。

JDKをアップグレードする

JDK1.6 の String クラスには非常に大きな穴があるため、最も直接的かつ効果的な方法は JDK をアップグレードすることです。そこで友人に状況を説明し、JDKをJDK1.8にアップグレードしてもらうように頼みました。

同様に、JDK1.8 の String クラスの substring() メソッドを見てみましょう。

public String substring(int beginIndex, int endIndex) { 
    if (beginIndex < 0) { 
        throw new StringIndexOutOfBoundsException(beginIndex); 
    if (endIndex > 
    value.length) { 
        throw new StringIndexOutOfBoundsException(endIndex); 
    int 
    subLen = endIndex - beginIndex; 
    if (subLen < 0) { 
        throw new StringIndexOutOfBoundsException(subLen); 
    return 
    ((beginIndex == 0) && (endIndex == value.length)) ? this 
        : new String(value, beginIndex, subLen); 
}

JDK1.8のStringクラスのsubstring()メソッドでも、部分文字列を生成するためにStringクラスの構築メソッドを呼び出していますが、この構築メソッドを以下に見てみましょう。

public String(char value[], int offset, int count) { 
    if (offset < 0) { 
        throw new StringIndexOutOfBoundsException(offset); 
    if (count <= 0) { 
        if (count < 0) { 
    throw 
            new StringIndexOutOfBoundsException(count); 
        if 
        (オフセット <= value.length) { 
            this.value = "".value; 
            戻る; 
        } 
    } 
    // 注: オフセットまたはカウントは -1>>>1 に近い可能性があります。
    if (オフセット > 値.長さ - カウント) { 
        throw new StringIndexOutOfBoundsException(オフセット + カウント); 
    this.value 
    = Arrays.copyOfRange(値, オフセット, オフセット+カウント); 
}

JDK1.8 では、部分文字列が必要な場合、substring はコンストラクターの Arrays.copyOfRange 関数によって構築される新しい文字列を生成します。これは問題ありません。

JVM 起動パラメータを最適化する

ここで、システムのパフォーマンスをより向上させるために、私はこの小さなパートナーが JVM 起動パラメーターを最適化するのにも協力しました。

友人の許可を得て、 私は彼らのビジネス規模とサーバー構成を簡単にリストしました。システム全体は分散アーキテクチャを採用し、アーキテクチャ内の各ビジネス サービスはクラスター展開を採用しており、1 日の平均訪問量は数億、1 日の平均トランザクションは数億です。 50W~100Wのオーダー、およびオーダー システムの各サーバーノードは4コア8Gとして構成されます。現在、JDK はバージョン 1.8 にアップグレードされています。

上記の条件に従って、JVMチューニング後のパラメータ設定を与えます。

-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:メタスペース サイズ = 256M -XX:最大メタスペース サイズ = 256M

なぜ上記のような JVM パラメータ設定になっているのかについては、実際のビジネス シナリオに合わせて JVM パラメータをチューニングする方法を具体的に分析するために、後で別の記事を書きます。

問題を分析して解決した後、小さなパートナーのプログラムは本番環境でスムーズに実行され、少なくともメモリ オーバーフローの状況はまだ発生していません。

結論は

プログラム内で比較的大きなオブジェクトが作成され、この大きなオブジェクトに基づいて他の情報を生成する場合、この時点でこの大きなオブジェクトとの参照関係を解放する必要があります。そうしないと、メモリ オーバーフローの隠れた危険が埋もれてしまいます。

JVM 最適化の目標は、できる限り新しい世代にオブジェクトを割り当ててリサイクルし、あまりにも多くのオブジェクトが古い世代に頻繁に入らないようにし、古い世代のガベージ コレクションが頻繁に行われないようにすると同時に、システムに負荷を与えることです。新しい世代の頻繁なガベージ コレクションを回避するのに十分なメモリ サイズ。

クリックしてフォローして、Huawei Cloudの最新テクノロジーについて初めて学びましょう~

 

プロトコル バッファよりも無限に高速です. オープン ソースの 10 年を経て、Cap'n Proto 1.0 がついにリリースされました. 華中科技大学の博士研究員が LK-99 磁気浮上現象を再現しました. Loongson Zhongke は新世代の開発に成功しましたプロセッサの Loongson 3A6000 ミニブリンク バージョン 108。 世界最小の Chromium コア ChromeOS は、ブラウザとオペレーティング システムを独立した 1TB ソリッド ステート ドライブに分割し、テスラ チャイナ モールで価格 2,720 元 ファーウェイは HarmonyOS 4 のセキュリティ アップグレード バージョンを 正式にリリースし、 すべての Electron ベースのアプリケーションがフリーズ AWS は来年 IPv4 パブリック ネットワーク アドレスのサポートを開始 命令 型プログラミング言語 Nim v2.0の正式リリース
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/4526289/blog/10092960