検索
ホームページJava&#&チュートリアルJava ガベージ コレクションのオーバーヘッドを削減する 5 つのヒント

GC オーバーヘッドを低く保つためのヒントは何ですか?

Java 9 のリリースが遅れたことにより、G1 (「ガベージ ファースト」) ガベージ コレクターが HotSpot 仮想マシンのデフォルトのガベージ コレクターになります。シリアル ガベージ コレクターから CMS コレクターまで、JVM は多くの GC 実装を目撃しており、G1 はその次世代のガベージ コレクターになります。

ガベージ コレクターの開発により、GC の各世代は前世代と比較して大きな進歩と改善をもたらしました。シリアル GC と比較して、パラレル GC では、ガベージ コレクターがマルチスレッド方式で動作し、マルチコア コンピューターの計算能力を最大限に活用できます。並列 GC と比較して、CMS (「同時マークスイープ」) コレクターはリサイクル プロセスを複数の段階に分割するため、アプリケーション スレッドの実行中に収集作業を同時に完了でき、「停止」の頻繁な実行が大幅に改善されます。 -世界」の状況。 G1 は、大量のヒープ メモリを備えた JVM のパフォーマンスが向上し、より予測可能で均一な一時停止プロセスを備えています。

ヒント #1: コレクション容量を予測する

カスタムおよび拡張実装 (Trove や Google の Guava など) を含むすべての標準 Java コレクションは、内部で配列 (ネイティブ データ型またはオブジェクトベースの型のいずれか) を使用します。配列が一度割り当てられると、そのサイズは不変であるため、コレクションに要素を追加すると、ほとんどの場合、古い配列 (コレクションの基礎となる実装)。

コレクション初期化のサイズが指定されていない場合でも、ほとんどのコレクション実装は、配列の再割り当て処理を最適化し、そのオーバーヘッドを最小限に抑えようとします。ただし、コレクションを構築するときにサイズを指定すると、最良の結果が得られます。

簡単な例として次のコードを分析してみましょう:

public static List reverse(List & lt; ? extends T & gt; list) {    List result = new ArrayList();
    for (int i = list.size() - 1; i & gt; = 0; i--) {
        result.add(list.get(i));
    }    return result;
}

このメソッドは、新しい配列を割り当て、それから別のリストの項目を逆の順序でのみ埋めます。次に、その配列に別のリストの要素を埋めます。要素の番号順が変わります。

この処理方法では、パフォーマンスに大きな代償を払う可能性があります。最適化ポイントは、新しいリストに要素を追加するコード行です。 各要素が追加されると、リストはその基になる配列に新しい要素を収容するのに十分なスペースがあることを確認する必要があります。空きスロットがある場合、新しい要素は次の空きスロットに単純に格納されます。そうでない場合は、新しい基になる配列が割り当てられ、古い配列の内容が新しい配列にコピーされ、新しい要素が追加されます。これにより、配列が複数回割り当てられることになり、残った古い配列は最終的に GC によって回収されます。

コレクションを構築するときに、基礎となる配列に格納する要素の数を知らせることで、これらの冗長な割り当てを回避できます

public static List reverse(List & lt; ? extends T & gt; list) {    List result = new ArrayList(list.size());
    for (int i = list.size() - 1; i & gt; = 0; i--) {
        result.add(list.get(i));
    }    return result;
}

上記のコードは、ArrayList 要素のコンストラクターを通じて list.size() を格納するのに十分な大きさのスペースを指定します。割り当ては初期化中に完了します。つまり、List は反復中にメモリを再度割り当てる必要がありません。

Guava のコレクション クラスはさらに一歩進んで、コレクションの初期化時に予期される要素の数を明示的に指定したり、予測値を指定したりすることができます。

List result = Lists.newArrayListWithCapacity(list.size());List result = Lists.newArrayListWithExpectedSize(list.size());

上記のコードでは、前者はコレクションに格納される要素の数がすでに正確にわかっている場合に使用され、後者は誤った推定を考慮した方法で割り当てられます。

ヒント #2: データ ストリームを直接処理する

ファイルからのデータの読み取りやネットワークからのデータのダウンロードなど、データ ストリームを処理する場合、次のコードが非常に一般的です:

byte[] fileData = readFileToByteArray(new File("myfile.txt"));

結果のバイト配列は、解析された XML ドキュメントである可能性があります。 、JSON オブジェクトまたはプロトコルのバッファリングされたメッセージと、いくつかの一般的なオプション。

大きなファイルや予測不可能なサイズのファイルを扱う場合、JVM が実際のファイルを処理するためのバッファを割り当てることができない場合、OutOfMemoryErrors が発生するため、上記のアプローチは非常に賢明ではありません。

たとえデータのサイズが管理可能な場合でも、上記のパターンを使用すると、ファイル データを保存するためにヒープ内に非常に大きな領域が割り当てられるため、ガベージ コレクションに関しては依然として大きなオーバーヘッドが発生します。

より良い方法は、ファイル全体を一度にバイト配列に読み取るのではなく、適切な InputStream (この例では FileInputStream など) を使用してパーサーに直接渡すことです。すべての主流のオープン ソース ライブラリは、次のような、入力ストリームを直接受け入れるための対応する API を提供しています。詳細に入る必要すらありません。ただし、ガベージ コレクションに影響を与える注目すべき利点があります。

不変オブジェクトの属性は、オブジェクトの作成後に変更することはできません (この例では参照データ型の属性を使用しています)。たとえば、次のとおりです。

FileInputStream fis = new FileInputStream(fileName);
MyProtoBufMessage msg = MyProtoBufMessage.parseFrom(fis);

上記のクラスをインスタンス化した後、不変オブジェクトが生成されます - そのすべての属性は、final で変更され、構築完了後に変更することはできません。

不可变性意味着所有被一个不可变容器所引用的对象,在容器构造完成前对象就已经被创建。就 GC 而言:这个容器年轻程度至少和其所持有的最年轻的引用一样。这意味着当在年轻代执行垃圾回收的过程中,GC 因为不可变对象处于老年代而跳过它们,直到确定这些不可变对象在老年代中不被任何对象所引用时,才完成对它们的回收。

更少的扫描对象意味着对内存页更少的扫描,越少的扫描内存页就意味着更短的 GC 生命周期,也意味着更短的 GC 暂停和更好的总吞吐量。

Tip #4: 小心字符串拼接

字符串可能是在所有基于 JVM 应用程序中最常用的非原生数据结构。然而,由于其隐式地开销负担和简便的使用,非常容易成为占用大量内存的罪归祸首。

这个问题很明显不在于字符串字面值,而是在运行时分配内存初始化产生的。让我们快速看一下动态构建字符串的例子:

public static String toString(T[] array) {    
   String result = "[";    
   for (int i = 0; i & lt; array.length; i++) {
        result += (array[i] == array ? "this" : array[i]);        
        if (i & lt; array.length - 1) {
            result += ", ";
        }
    }
    result += "]";
    return result;
}

这是个看似不错的方法,接收一个字符数组然后返回一个字符串。但是这对于对象内存分配却是灾难性的。

很难看清这语法糖的背后,但是幕后的实际情况是这样的:

public static String toString(T[] array) {    
     String result = "[";    
     for (int i = 0; i & lt; array.length; i++) {
        StringBuilder sb1 = new StringBuilder(result);
        sb1.append(array[i] == array ? "this" : array[i]);
        result = sb1.toString();        
        
        if (i & lt; array.length - 1) {
            StringBuilder sb2 = new StringBuilder(result);
            sb2.append(", ");
            result = sb2.toString();
        }
    }
    StringBuilder sb3 = new StringBuilder(result);
    sb3.append("]");
    result = sb3.toString();
    return result;
}

字符串是不可变的,这意味着每发生一次拼接时,它们本身不会被修改,而是依次分配新的字符串。此外,编译器使用了标准的 StringBuilder 类来执行这些拼接操作。这就会有问题了,因为每一次迭代,既隐式地分配了一个临时字符串,又隐式分配了一个临时的 StringBuilder 对象来帮助构建最终的结果。

最佳的方式是避免上面的情况,使用 StringBuilder 和直接的追加,以取代本地拼接操作符(“+”)。下面是一个例子:

public static String toString(T[] array) {
    StringBuilder sb = new StringBuilder("[");    
    for (int i = 0; i & lt; array.length; i++) {
        sb.append(array[i] == array ? "this" : array[i]);        
        if (i & lt; array.length - 1) {
            sb.append(", ");
        }
    }
    sb.append("]");    
    return sb.toString();
}

这里,我们只在方法开始的时候分配了唯一的一个 StringBuilder。至此,所有的字符串和 list 中的元素都被追加到单独的一个StringBuilder中。最终使用 toString() 方法一次性将其转成成字符串返回。

Tip #5: 使用特定的原生类型的集合

Java 标准的集合库简单且支持泛型,允许在使用集合时对类型进行半静态地绑定。比如想要创建一个只存放字符串的 Set 或者存储 Map

TIntDoubleMap map = new TIntDoubleHashMap();
map.put(5, 7.0);
map.put(-1, 9.999);...

Trove 的底层实现使用了原生类型的数组,所以当操作集合的时候不会发生元素的装箱(int->Integer)或者拆箱(Integer->int), 没有存储对象,因为底层使用原生数据类型存储。

最后

随着垃圾收集器持续的改进,以及运行时的优化和 JIT 编译器也变得越来越智能。我们作为开发者将会发现越来越少地考虑如何编写 GC 友好的代码。然而,就目前阶段,不论 G1 如何改进,我们仍然有很多可以做的事来帮 JVM 提升性能。


声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
JVMは、さまざまなプラットフォームでガベージコレクションをどのように管理していますか?JVMは、さまざまなプラットフォームでガベージコレクションをどのように管理していますか?Apr 28, 2025 am 12:23 AM

jvmmanagesgarbagecollectionacrossplatformseftivivivivitybyusagenerationalaphadadadaptingtosandhardwaredefferences.itemployscollectorslikeserial、parallel、cms、andg1、各sutitedfordifferentscenarios

なぜJavaコードは変更せずに異なるオペレーティングシステムで実行できるのですか?なぜJavaコードは変更せずに異なるオペレーティングシステムで実行できるのですか?Apr 28, 2025 am 12:14 AM

Javaは、Javaの「Write and Averywherewhere」という哲学がJava Virtual Machine(JVM)によって実装されているため、変更なしで異なるオペレーティングシステムで実行できます。コンパイルされたJavaバイトコードとオペレーティングシステムの間の仲介者として、JVMはバイトコードを特定のマシン命令に変換し、JVMがインストールされた任意のプラットフォームでプログラムが独立して実行できることを確認します。

Javaプログラムをコンパイルして実行するプロセスを説明し、プラットフォームの独立性を強調します。Javaプログラムをコンパイルして実行するプロセスを説明し、プラットフォームの独立性を強調します。Apr 28, 2025 am 12:08 AM

Javaプログラムの編集と実行は、BytecodeとJVMを通じ​​てプラットフォームの独立性を達成します。 1)Javaソースコードを書き、それをbytecodeにコンパイルします。 2)JVMを使用して、任意のプラットフォームでByteCodeを実行して、コードがプラットフォーム間で実行されるようにします。

基礎となるハードウェアアーキテクチャは、Javaのパフォーマンスにどのように影響しますか?基礎となるハードウェアアーキテクチャは、Javaのパフォーマンスにどのように影響しますか?Apr 28, 2025 am 12:05 AM

Javaのパフォーマンスはハードウェアアーキテクチャと密接に関連しており、この関係を理解することでプログラミング機能を大幅に改善できます。 1)JVMは、CPUアーキテクチャの影響を受けるJITコンピレーションを介して、Java Bytecodeを機械命令に変換します。 2)メモリ管理とゴミ収集は、RAMとメモリバスの速度の影響を受けます。 3)キャッシュとブランチ予測Javaコードの実行を最適化します。 4)マルチスレッドと並列処理がマルチコアシステムのパフォーマンスを改善します。

ネイティブライブラリがJavaのプラットフォームの独立性を破ることができる理由を説明してください。ネイティブライブラリがJavaのプラットフォームの独立性を破ることができる理由を説明してください。Apr 28, 2025 am 12:02 AM

ネイティブライブラリを使用すると、これらのライブラリはオペレーティングシステムごとに個別にコンパイルする必要があるため、Javaのプラットフォームの独立性が破壊されます。 1)ネイティブライブラリはJNIを介してJavaと対話し、Javaが直接実装できない機能を提供します。 2)ネイティブライブラリを使用すると、プロジェクトの複雑さが増し、さまざまなプラットフォームのライブラリファイルの管理が必要です。 3)ネイティブライブラリはパフォーマンスを改善できますが、それらは注意して使用し、クロスプラットフォームテストを実施する必要があります。

JVMはオペレーティングシステムAPIの違いをどのように処理しますか?JVMはオペレーティングシステムAPIの違いをどのように処理しますか?Apr 27, 2025 am 12:18 AM

JVMは、JavanativeInterface(JNI)およびJava Standard Libraryを介してオペレーティングシステムのAPIの違いを処理します。1。JNIでは、Javaコードがローカルコードを呼び出し、オペレーティングシステムAPIと直接対話できます。 2. Java Standard Libraryは統一されたAPIを提供します。これは、異なるオペレーティングシステムAPIに内部的にマッピングされ、コードがプラットフォーム間で実行されるようにします。

Java 9で導入されたモジュール性は、プラットフォームの独立性にどのように影響しますか?Java 9で導入されたモジュール性は、プラットフォームの独立性にどのように影響しますか?Apr 27, 2025 am 12:15 AM

modularitydoesnotdirectlyectlyectjava'splatformindepensence.java'splatformendepenceismaindainededainededainededaindainedaindained bythejvm、butmodularityinfluencesApplucationStructure andmanagement、間接的なインパクチャプラット形成依存性.1)

ByteCodeとは何ですか?また、Javaのプラットフォームの独立性とどのように関係していますか?ByteCodeとは何ですか?また、Javaのプラットフォームの独立性とどのように関係していますか?Apr 27, 2025 am 12:06 AM

bytecodeinjavaisthe intermediaterepresentationthateNablesplatformindepence.1)javacodeis compiledintobytecodestoredin.classfiles.2)thejvminterpretsorcompilesthisbytecodeintomachinecodeatime、

See all articles

ホット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衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

EditPlus 中国語クラック版

EditPlus 中国語クラック版

サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

SublimeText3 英語版

SublimeText3 英語版

推奨: Win バージョン、コードプロンプトをサポート!

Dreamweaver Mac版

Dreamweaver Mac版

ビジュアル Web 開発ツール

WebStorm Mac版

WebStorm Mac版

便利なJavaScript開発ツール

SecLists

SecLists

SecLists は、セキュリティ テスターの究極の相棒です。これは、セキュリティ評価中に頻繁に使用されるさまざまな種類のリストを 1 か所にまとめたものです。 SecLists は、セキュリティ テスターが必要とする可能性のあるすべてのリストを便利に提供することで、セキュリティ テストをより効率的かつ生産的にするのに役立ちます。リストの種類には、ユーザー名、パスワード、URL、ファジング ペイロード、機密データ パターン、Web シェルなどが含まれます。テスターはこのリポジトリを新しいテスト マシンにプルするだけで、必要なあらゆる種類のリストにアクセスできるようになります。