Javaコードの仕様を通じてプログラムを最適化し、メモリの使用量を最適化し、メモリの漏れを防ぎます
利用できるリソース(メモリ、CPU時間、ネットワーク帯域幅など)は、最適化の目的が限られています。通常、最適化には2つの側面が含まれます。コードのサイズを縮小し、コードの実行効率を改善します。この記事では、主にコードの効率を改善する方法について説明します。
Javaプログラムでは、パフォーマンスの問題の理由のほとんどはJava言語ではなく、プログラム自体にあります。プログラムのパフォーマンスを大幅に改善できるJava.lang.Stringクラスとjava.util.vectorクラスを正しく巧妙に適用するなど、優れたコードライティング習慣を開発することが非常に重要です。この問題を以下に詳細に分析しましょう。
1.最終的な修飾子を備えたクラスの最終的な修飾子が導出されていないことを指定してください。
Java Core APIには、Java.lang.Stringなど、ファイナルを適用する多くの例があります。文字列クラスのファイナルを指定すると、人々は長さ()メソッドを上書きすることができなくなります。さらに、クラスが最終として指定されている場合、そのクラスのすべての方法が最終的です。 Javaコンパイラは、すべての最終的な方法をインラインでインラインする機会を探します(これは特定のコンパイラの実装に関連しています)。この動きは、パフォーマンスを平均50%改善できます。
2。オブジェクトを再利用してみてください。
特に文字列オブジェクトを使用する場合、文字列の連結が発生したときに文字列バッファが代わりに使用されます。システムはオブジェクトを生成するのに時間がかかるだけでなく、将来これらのオブジェクトを収集して処理するのに時間がかかる場合があります。したがって、あまりにも多くのオブジェクトを生成すると、プログラムのパフォーマンスに大きな影響があります。
3.ローカル変数を使用してみてください。メソッドを呼び出したときに渡され、コールで作成された一時変数がスタック(スタック)に保存されます。
静的変数、インスタンス変数などの他の変数は、ヒープで作成され、遅いです。さらに、特定のコンパイラ/JVMに応じて、ローカル変数をさらに最適化できます。 「可能な限りスタック変数を使用」を参照してください。
4.変数の初期化を繰り返さないでください<br />デフォルトでは、クラスコンストラクターを呼び出すと、Javaは変数を特定の値に初期化します。すべてのオブジェクトはnull、integer変数(byte、short、int、long)setに設定されます。 0まで、フロート変数と二重変数は0.0に設定され、論理値はfalsに設定されます。これは、クラスが別のクラスから派生した場合に特に注意する必要があります。これは、新しいキーワードでオブジェクトが作成された場合、コンストラクターチェーンのすべてのコンストラクターが自動的に呼び出されるためです。
5。Java + Oracle Application Systemの開発では、 Javaで大文字のフォームを使用して、Oracle Parserの解析負担を軽減してみてください。
6. Javaプログラミング中に、データベース接続とI/Oストリーミング操作を実行するときは、それらを閉じてください。
これらの大きなオブジェクトを動作させると、大規模なシステムオーバーヘッドが発生し、注意が必要ない場合は深刻な結果につながります。
7. JVMには独自のGCメカニズムがあるため、プログラム開発者からの考慮事項はあまり必要ありません。システムでは、深刻な場合には、メモリの漏れが発生するため、期限切れのオブジェクトのタイムリーなリサイクルを確保することは非常に重要です。
JVMがゴミをリサイクルする条件は、オブジェクトが参照されていないことです。したがって、オブジェクトを使用した後に手動でnullに設定することをお勧めします。
8.同期メカニズムを使用する場合、コードブロック同期の代わりにメソッド同期を使用してみてください。
9.変数の繰り返し計算を最小化<Br />例:for(int i = 0; i <list.size; i ++){for
…
}
置き換える必要があります。
for(int i = 0、int len = list.size(); i <len; i ++){
…
}
10。怠zyなロード戦略を採用してみてください。つまり、必要に応じて作成を開始します。
例:string str =“ aaa”;
if(i == 1){
list.add(str);
}
置き換える必要があります。
if(i == 1){
string str =“ aaa”;
list.add(str);
}
11.慎重に異常を使用します
異常はパフォーマンスには良くありません。例外をスローするには、最初に新しいオブジェクトを作成する必要があります。 Throwable Interfaceのコンストラクターは、FillinStackTrace()という名前のローカル(ネイティブ)メソッドを呼び出し、FillinStackTrace()メソッドはスタックをチェックし、コールトレース情報を収集します。例外がスローされている限り、VMは処理中に新しいオブジェクトが作成されるため、コールスタックを調整する必要があります。 例外はエラー処理にのみ使用でき、プログラムの流れを制御するために使用しないでください。
12。ループで使用しないでください:
試す {
} catch(){
}
最も外側の層に配置する必要があります。
13。stringbufferの使用:
StringBufferは、変数の書き込み文字列を表します。
3つの建設方法があります。
stringbuffer(); // 16文字のスペースを割り当てます
stringbuffer(int size);
stringbuffer(string str);
ここで説明したコンストラクターはStringBuffer(int length)であり、長さパラメーターは現在のStringBufferが保持できる文字の数を示します。また、StringBufferオブジェクトが作成された後、EnsureCapacity(int minimut capacity)メソッドを使用して容量を設定することもできます。まず、StringBufferのデフォルトの動作を見てから、パフォーマンスを改善するためのより良い方法を見つけましょう。
StringBufferは、デフォルトのコンストラクターを使用してStringBufferオブジェクトを作成すると、StringBufferの容量が16文字になります。 StringBufferが最大容量に達すると、その容量が現在の容量の2倍に増加し、2、つまり(2*old値+2)を追加します。初期化後、デフォルト値を使用すると、16番目の文字に追加されます。 70(2*34+2)。 StringBufferが最大容量に達する限り、新しい文字アレイを作成し、古い文字と新しいキャラクターの両方を再共有する必要があります。したがって、StringBufferの合理的な初期化容量値を常に設定することは間違っていません。これにより、即時のパフォーマンスが得られます。これは、StringBuffer初期化プロセスを調整する役割を示しています。したがって、適切な容量値を使用してStringBufferを初期化することは、常に最適な提案です。
14。Javaクラスjava.util.vectorを合理的に使用します。
簡単に言えば、ベクトルはjava.lang.Objectインスタンスの配列です。ベクトルは配列に似ており、その要素は整数の形式でインデックスを介してアクセスできます。ただし、ベクトルタイプのオブジェクトを作成した後、オブジェクトのサイズは、要素の追加または削除に応じて拡張および削減できます。ベクトルに要素を追加する次の例を検討してください。
オブジェクトbj = new object();
ベクトルv = new Vector(100000);
for(int i = 0;
I <100000;
毎回ベクターの前に新しい要素を挿入する必要があると絶対に十分な理由がない限り、上記のコードはパフォーマンスに悪いです。デフォルトのコンストラクターでは、ベクトルの初期ストレージ容量は10の要素である場合、新しい要素を追加すると、将来的にはストレージ容量が2倍になります。ベクトルクラスは、ストレージ容量が拡張されるたびに、すべての既存の要素を新しいストレージスペースにコピーする必要があります。次のコードスニペットは、前の例よりも桁違いに桁違いになります。
オブジェクトbj = new object();
ベクトルv = new Vector(100000);
for(int i = 0; i <100000; i ++){v.add(obj)}
同じルールは、ベクトルクラスのremove()メソッドに適用されます。ベクトル内の各要素には各要素間に「空間」を含めることができないため、最後の要素を除く他の要素を削除すると、削除された要素が前方に進みます。つまり、ベクトルから最後の要素を削除することは、最初の要素を削除するよりも数倍の「オーバーヘッド」です。
前のベクトルからすべての要素を削除したいと仮定すると、このコードを使用できます。
for(int i = 0; i <100000; i ++)
{
V.Remove(0);
}
ただし、次のコードと比較して、前のコードは桁違いに遅くなります。
for(int i = 0; i <100000; i ++)
{
V.Remove(v.size() - 1);
}
タイプベクトルのオブジェクトVからすべての要素を削除する最良の方法は次のとおりです。
V.RemovealLelements();
タイプベクトルのオブジェクトVに文字列「hello」が含まれているとします。次のコードを検討してください。このコードでは、このベクトルから「ハロー」文字列を削除します。
文字列s = "hello";
int i = v.indexof(s);
if(i!= -1)v.remove(s);
コードは何も悪いようには見えませんが、パフォーマンスにも悪いことです。このコードでは、indexof()メソッドがVを順番に検索して文字列「hello」を見つけ、削除方法も同じ順序で検索する必要があります。改良されたバージョンは次のとおりです。
文字列s = "hello";
int i = v.indexof(s);
if(i!= -1)v.remove(i);
このバージョンでは、remove()メソッドで削除する要素の正確なインデックス位置を直接指定し、2番目の検索を回避します。より良いバージョンは次のとおりです。
文字列s = "hello";
最後に、ベクトルクラスに関するコードスニペットを見てみましょう。
for(int i = 0; i ++; i <v.length)
vに100,000の要素が含まれている場合、このコードスニペットはv.size()メソッドを100,000回呼び出します。サイズの方法は単純な方法ですが、メソッド呼び出しのオーバーヘッドが必要ですが、少なくともJVMはスタック環境を構成してクリアする必要があります。ここで、forループ内のコードは、ベクトルタイプオブジェクトVのサイズを決して変更しないため、上記のコードは次の形式に書き換えます。
int size = v.size();
これは単純な変更ですが、それでもパフォーマンスを獲得しています。結局のところ、すべてのCPUサイクルは貴重です。
15.大量のデータをコピーするときは、System.ArrayCopy()コマンドを使用します。
16。コードリファクタリング:コードの読みやすさを強化します。
例えば:
public class shopcart {private list carts;…public void add(object item){if(carts == null){carts = new arrayList();} crts.add(item);} public void remove(object item){if if (carts。contains(item)){carts.remove(item);}} public list getCarts(){// return read-only list return collections.unmodifiablelist(carts);} //この方法は推奨されません//これは推奨されません。 17.新しいキーワードを使用せずにクラスのインスタンスを作成する
新しいキーワードを使用してクラスのインスタンスを作成すると、コンストラクターチェーンのすべてのコンストラクターが自動的に呼ばれます。ただし、オブジェクトがクローン可能なインターフェイスを実装する場合、そのclone()メソッドを呼び出すことができます。 clone()メソッドは、クラスコンストラクターを呼び出しません。
設計パターンを使用する場合、ファクトリーモードを使用してオブジェクトを作成する場合、Clone()メソッドを使用して新しいオブジェクトインスタンスを作成するのは非常に簡単です。たとえば、以下は工場パターンの典型的な実装です。
Public Static Credit getNewcreDit(){
新しいクレジット()を返します。
}
改良されたコードは、次のようにclone()メソッドを使用します。
private Static Credit Basecredit = new Credit();
Public Static Credit getNewcreDit(){
return(クレジット)basecredit.clone();
}
上記のアイデアは、配列処理にも非常に役立ちます。
18.乗算と分割
次のコードを検討してください。
for(val = 0; val <100000; val += 5){
startx = val * 8;
}
乗算をシフト操作に置き換えると、パフォーマンスが大幅に向上する可能性があります。これが変更されたコードです:
for(val = 0; val <100000; val += 5){
startx = val << 3;
}
修正されたコードは8を増やすのではなく、代わりに3ビットの左シフトを使用し、左シフトあたり1ビットに相当する1ビットは2を掛けます。したがって、1ビット操作による適切なシフトは、2で割ることに相当します。シフト操作は高速ですが、コードの理解が困難になる可能性があるため、コメントを追加するのが最善であることに言及する価値があります。
19。JSPページで役に立たないセッションを閉じます。
一般的な誤解は、クライアントアクセスがあるときにセッションが作成されることです、<>を使用して、JSPファイルがサーブレットにコンパイルされると、このステートメントはhttpsersed sessionに追加されます。セッションはメモリリソースを消費するため、セッションを使用する予定がない場合は、すべてのJSPで閉じる必要があります。
セッションステータスを追跡する必要がないページの場合、自動的に作成されたセッションを閉じると、リソースを節約できます。次のページ指令を使用します:<%@ページセッション= "false"%>
20。JDBCおよびI/O
アプリケーションが大規模なデータセットにアクセスする必要がある場合は、ブロック抽出の使用を検討する必要があります。デフォルトでは、JDBCは毎回32行のデータを抽出します。たとえば、5000行のレコードセットを通過すると仮定すると、JDBCはすべてのデータを抽出する前にデータベースを157回呼び出す必要があります。ブロックサイズが512に変更された場合、データベースへの呼び出し数は10倍に減少します。
21.サーブレットとメモリの使用<br />多くの開発者は、自由にユーザーセッションに大量の情報を保存します。セッションに保存されているオブジェクトは、時間内にガベージコレクションメカニズムによってリサイクルされない場合があります。パフォーマンスの観点から見ると、典型的な症状は、ユーザーがシステムが定期的に減速していると感じているが、その理由を特定のコンポーネントに帰することはできないことです。 JVMのヒープスペースを監視すると、異常なメモリ使用量の変動として現れます。
このタイプのメモリ問題を解決するための主な方法は2つあります。最初の方法は、セッションの範囲を持つすべての豆にhttpsessionbindinglistenerインターフェイスを実装することです。このように、ValueUnbound()メソッドが実装されている限り、Beanが使用するリソースを明示的にリリースできます。
別の方法は、セッションをできるだけ早く無効にすることです。ほとんどのアプリケーションサーバーには、セッションの無効な間隔を設定するオプションがあります。さらに、セッションのSetMaxinActiveInterval()メソッドは、プログラムでも使用されます。
22.バッファマークを使用します
一部のアプリケーションサーバーには、JSPにバッファマーキング機能が追加されています。たとえば、BeaのWeblogic Serverは、バージョン6.0以来のこの機能をサポートしており、Open Symphony Projectもこの機能もサポートしています。 JSPバッファリングタグは、ページフラグメントとページ全体の両方をバッファリングできます。 JSPページが実行されると、ターゲットフラグメントがすでにバッファに含まれている場合、フラグメントを生成するコードを実行する必要がなくなります。ページレベルのバッファリングは、指定されたURLへのリクエストをキャプチャし、結果ページ全体をバッファリングします。この機能は、ショッピングバスケット、カタログ、ポータルホームページに非常に役立ちます。このようなアプリケーションでは、ページレベルのバッファリングは、後続のリクエストに対してページ実行の結果を保存できます。
23。適切な引用メカニズムを選択します
典型的なJSPアプリケーションシステムでは、ヘッダーとフッターの部品がしばしば抽出され、必要に応じてヘッダーとフッターが導入されます。現在、JSPページに外部リソースを導入する2つの主な方法があります。指令を含め、アクションを含める。
includeディレクティブ:たとえば、<%@ include file = "Copyright.html"%>。この指令は、コンパイル時に指定されたリソースを導入します。コンパイルの前に、インクルードディレクティブと指定されたリソースを含むページがファイルに統合されます。参照される外部リソースはコンパイル時に決定されます。これは、実行時にリソースを決定するよりも効率的です。
アクションを含める:たとえば<jsp:include page = "Copyright.jsp" />。このアクションは、指定されたページが実行された後に生成された結果を導入します。実行時に完了するため、出力結果の制御はより柔軟です。ただし、引用されたコンテンツが頻繁に変更される場合、またはメインページのリクエストが表示される前に参照されたページを決定できない場合のアクションを含めるのは費用対効果が高いです。
24.クリアは、時間内に必要なセッションを必要としなくなりました
もはやアクティブではないセッションをクリアするために、多くのアプリケーションサーバーにはデフォルトのセッションタイムアウト(通常は30分)があります。アプリケーションサーバーがより多くのセッションを保存する必要がある場合、メモリ容量が不十分な場合、オペレーティングシステムはメモリデータの一部をディスクに転送する場合があります。ディスクに保管し、「メモリから外れた」例外を投げることさえあります。大規模なシステムでは、シリアル化セッションは高価です。セッションが不要になったら、httpsession.invalidate()メソッドを時間内に呼び出してセッションをクリアする必要があります。通常、httpsession.invalidate()メソッドは、アプリケーションの終了ページで呼び出すことができます。
25。配列を次のように宣言しないでください:public static final。
26。ハッシュマップの横断効率に関する議論
多くの場合、ハッシュマップのキーペアとバリューのペアに横断操作があり、2つの方法があります。Map<String、String []> paramap = new
Hashmap <String、String []>(); ............/最初のループセット<String> appfieldDefids = paramap.keyset(); ] values = paramap.get(appfielddefid); ......} // 2番目のループ(entry <string、string []> entry:paramap.entryset()){string appfielddefid = entry.getKey( ); string [] values = entry.getValue(); .......}最初の実装は、2番目の実装よりも効率が大幅に低くなります。
分析は次のとおりです。<string> appfielddefids = paramap.keyset();
コードは次のとおりです。
public set <k> keyset(){set <k> ks = keyset; return(ks!= null?ks :( keyset = new keyset());}プライベートクラスのkeyset extends abstractset <k> {public iterator <k > iterator(){return newKeyiterator();} public int size(){return size;} public boolean contains(object o){return containskey(o);} public boolean remove(object o){return hashmap.this.removeentryforkeykey (o)!= null;} public void clear(){hashmap.this.clear();}}}実際、プライベートクラスのキーズセットを返します。これは、抽象セットから継承され、設定されたインターフェイスを実装します。
for/inループの構文を見てみましょう
for(宣言:表現)
声明
実行段階では、次の式に変換されます
for(iterator <e>#i =(expression).iterator();#i.hashnext();){
宣言=#i.next();
声明
}
したがって、hashmap.keyset()。iterator()は、(string appfielddefid:appfielddefids)のステートメントの最初のもので呼び出されます。
このメソッドはnewKeyiterator()を呼び出します
iterator <k> newKeyiterator(){
新しいkeyiterator()を返します。
}
プライベートクラスのキーテーターはハシテレーターを拡張します<K> {
public k next(){
return nextentry()。getKey();
}
}
したがって、2番目のループで使用されるイテレーター(entry <string、string []> entry:paramap.entryset())は次のように呼び出されます。
親切
プライベートクラスの入力者はハシテレーター<map.entry <k、v >> {{
public map.entry <k、v> next(){
return nextentry();
}
}
この時点で、最初のループがキーを取得し、2番目のループがハッシュマップのエントリ効率を取得します。したがって、ループに直接反映されることがあります。 HashmapのGET(オブジェクトキー)を使用して値を取得します。Hashmap'sGet(オブジェクトキー)メソッドを見てください
public v get(オブジェクトキー){
オブジェクトk = masknull(key);
int hash = hash(k);
int i = indexfor(hash、table.length);
エントリ<k、v> e = table;
while(true){
if(e == null)
nullを返します。
if(e.hash == hash && eq(k、e.key))
e.valueを返します。
e = e.next;
}
}
実際、ハッシュ値を使用して対応するエントリを再度取得して、最初のループを使用することは、ハッシュマップを2回入力するのと同等です。
2番目のループでは、エントリ値を取得し、キーと値を直接取得します。これは、最初のループよりも効率的です。実際、MAPの概念によれば、2番目のループを使用する方が良いはずです。
27。アレイ(アレイ)とArrylistの使用
配列([]):最も効率的です。
アレイリスト:容量は動的に成長する可能性があります。
効率とタイプの検証に基づいて、配列はアレイサイズを決定できない場合にのみ使用する必要があります。
ArrayListは、Arrayの複雑なバージョンです
ArrayListは、オブジェクトタイプの配列をカプセル化します配列の方法。
ArrayListがオブジェクトを保存すると、タイプ情報が破棄され、すべてのオブジェクトがコンピレーション中にチェックされませんが、実行時にエラーが報告されます。
注:JDK5はジェネリックのサポートを追加しており、ArrayListを使用するときにタイプチェックを実行できます。
この観点から、アレイリストとアレイの違いは、主に動的容量の増加の効率によるものです。
28. HashmapとArrayListを使用してみてください。
29。StringBufferとStringBuilderの違い:
java.lang.stringbufferスレッドセーフ可変文字シーケンス。文字列のような文字列バッファーですが、変更できません。
StringBuilder。このクラスと比較して、Java.lang.StringBuilderクラスは、すべて同じ操作をサポートするため、通常は優先される必要がありますが、同期を実行しないため高速です。パフォーマンスを向上させるには、StirngbufferまたはStirngbuilderの容量をできるだけ指定する必要があります。もちろん、操作する文字列の長さが16文字を超えない場合、必要はありません。 同じケースでは、StringBuilderを使用すると、StringBufferを使用するのと比較して、パフォーマンスの改善は約10%〜15%しか達成できませんが、マルチスレッドが不安定なリスクが必要です。実際のモジュラープログラミングでは、特定のモジュールを担当するプログラマーは、モジュールがマルチスレッド環境に配置されるかどうかを明確に判断できない場合があります。モジュールがマルチスレッドモードで実行されないことを確認し、それ以外の場合はStringBufferを使用してください。
他のサプリメント:
1。時間内に使用されなくなり、nullに設定されているオブジェクトをクリアします
2。最終、静的、その他のキーワードを可能な限り使用してください
3.可能な限り緩衝オブジェクトを使用します
Javaソースファイルとコンパイルされたクラスファイルを小さくするためにコードを最適化する方法
1継承を使用すると、継承する方法が多いほど、書きたいコードが少なくなります。
2 Javaコンパイラの最適化オプションを開く:javac -oこのオプションは、クラスファイルの行番号を削除し、インラインメソッド呼び出しとしてプライベート、静的、および最終小セグメントメソッドを宣言します
3共通コードを抽出します
4大きな配列を初期化しないでください。配列の初期化はJavaコードのコードの量にすぎませんが、コンパイルされたコードは、コードの行で配列の要素を挿入するためです。配列に保存されて、最初にこのデータを文字列に入れてから、ランタイム中に文字列を配列に解析できます
5。日付タイプのオブジェクトは、多くの日付オブジェクトを保存する場合、それらを保存することを検討できます。
長いタイプ、次に使用すると日付タイプに変換します
6.クラス名、メソッド名、変数名に短い名前を使用してみてください。
7静的最終型の変数をインターフェイスに定義します
8左 /右の動きに算術操作を使用できる場合、 *および /操作は複数回使用しないでください。
2。変数を2回初期化しないでください
Javaは、一意のクラスコンストラクターを呼び出すことにより、デフォルトで変数を既知の値に初期化します。すべてのオブジェクトはnull、integer(byte、short、int、long)に設定され、フロートとダブルは0.0に設定され、ブール変数はfalseに設定されます。これは、新しいキーワードを使用してオブジェクトを作成するときにすべての一連のコンストラクターが自動的に呼び出されるように、他のクラスから拡張するクラスにとって特に重要です。
3.可能な限りクラスファイナルを作成します
ファイナルとマークされたクラスを拡張することはできません。 Java.lang.Stringなど、Core Java APIには、このテクノロジーの例がたくさんあります。 Stringクラスを最終的にマークすると、開発者が自分自身を実装する長さの方法を作成することを防ぎます。
より深く言うと、クラスが最終的な場合、クラスのすべての方法が最終的です。 Javaコンパイラは、すべてのメソッドをインライン化することができます(これはコンパイラの実装に依存します)。私のテストでは、パフォーマンスの平均増加が50%増加しました。
9.例外は、トライキャッチを統合できる場合にスローされます。
try {some.method1(); (method2exception e){//例外2} try {some.method3();ダウンロードされたコードは、コンパイラによって最適化されやすいです
try {some.method1(); catch(method3exception e){//例外3を処理する
10。ループの最適化
交換する…
for(int i = 0; i <collection.size(); i ++){
...
}
と…
for(int i = 0、n = collection.size(); i <n; i ++){
...
}
5。Java + Oracleアプリケーションシステムの開発では、Javaに大文字の埋め込みSQLステートメントを使用して、Oracle Parserの解析負荷を減らすようにしてください。
10。怠zyなロード戦略を採用してみてください。つまり、必要に応じて作成を開始します。
例:string str =“ aaa”;
if(i == 1){
list.add(str);
}
置き換える必要があります。
if(i == 1){
string str =“ aaa”;
list.add(str);
}
12。ループで使用しないでください:
試す {
} catch(){
}
最も外側の層に配置する必要があります
上記は、この記事についてのすべてです。
記事を友達と共有するか、コメントを残してください。ご支援ありがとうございます!