作業がますます多くのコードが書かれているため、プログラムはますます肥大化しており、効率がますます少なくなっています。これは、完璧を追求する私のようなプログラマーにとって絶対に許可されていません。したがって、プログラムの構造を絶えず最適化することに加えて、メモリの最適化とパフォーマンスのチューニングが私の通常の「トリック」になりました。
Javaプログラムのメモリとパフォーマンスを最適化および調整するには、仮想マシン(またはより厳密な仕様)の内部原則を理解しないことは間違いなく不可能です。ここに良い本「詳細なJava Virtual Machine(第2版)」(Cao XiaogangとJiang Jingが翻訳したBill Vennersによる」もちろん、Java仮想マシンを理解することの利点は、上記の2つの利点に限定されません。より深い技術的な観点から、Java仮想マシンの仕様と実装を理解することは、効率的で安定したJavaコードを書くために役立ちます。たとえば、Java仮想マシンのメモリモデルと仮想マシンのメモリリサイクルメカニズムを理解した場合、必要に応じて明示的に「メモリをリリース」することはありません(Javaコードはメモリを明示的にリリースできませんが、オブジェクトをリサイクルすることでオブジェクトをリサイクルする必要があることをガーベッジコレクターに通知できます。 Javaスタックがどのように機能するかを理解すれば、再帰層の数とループの数を減らすことで、スタックオーバーフローのリスクを減らすことができます。アプリケーション開発者の場合、これらのJava仮想マシンの基礎となる実装の作業を直接関与させることはできませんが、この背景知識を理解することで、私たちが書いたプログラムに微妙かつ良い影響があります。
この記事では、Java仮想マシンのアーキテクチャとメモリモデルについて簡単に説明します。不適切な言葉や不正確な説明がある場合は、それらを修正してください。私はとても光栄です!
Java仮想マシンアーキテクチャ
クラスロードサブシステム
Java仮想マシン用の2つのクラスローダー、つまりスタートアップクラスローダーとユーザー定義のローダーがあります。
クラスのロードサブシステムは、クラスの完全な資格のある名前(パッケージ名とクラス名、ネットワークマウントもURLを含む)を介してランタイムデータ領域にクラスをロードします。ロードされた各タイプについて、Java仮想マシンは、java.lang.classクラスのインスタンスを作成して、メモリ内のヒープ領域に配置されたタイプを表すタイプを表し、ロードされたタイプ情報は他のすべてのオブジェクトと同じメソッド領域にあります。
タイプをロードする前に、クラスロードサブシステムは、対応するバイナリクラスファイルを見つけてインポートするだけでなく、インポートされたクラスの正確性を検証し、クラス変数にメモリを割り当てて初期化し、シンボル参照を直接参照として解析する必要があります。これらのアクションは、厳密に次の順序で行われます。
1)ロード - タイプのバイナリデータを見つけてロードします。
2)接続 - 検証、準備、解析を実行する(オプション)
3)インポートされたタイプの正しさを確認するために確認してください
4)クラス変数にメモリを割り当て、デフォルト値に初期化する準備をします
5)タイプのシンボリックリファレンスを分析して、アプリケーションを直接
メソッド領域
クラスロードサブシステムによってロードされた各タイプについて、仮想マシンは次のデータをメソッド領域に保存します。
1。タイプの完全な資格のある名前
2。タイプのスーパークラスの完全な資格のある名前(java.lang.objectにはスーパークラスがありません)
3。タイプAクラスタイプまたはインターフェイスタイプです
4.アクセス修飾子を入力します
5。直接的なハイパーインターフェイスの完全資格の名前注文リスト
上記の基本タイプ情報に加えて、次の情報も保存されます。
6.定数プールと入力します
7。フィールド情報(フィールド名、フィールドタイプ、フィールド修飾子を含む)
8。メソッド情報(メソッド名、返品タイプ、数字、およびパラメーターのタイプ、メソッド修飾子を含む。メソッドが抽象的でローカルではない場合、メソッドバイテコード、オペランドスタック、メソッドスタックフレームのローカル変数領域のサイズと例外テーブルも保存されます)
9.定数を除くすべてのクラス変数(実際、それらはクラスの静的変数です。静的変数はすべてのインスタンスで共有され、タイプに直接関連しているため、クラスレベルの変数であり、クラスのメンバーとしてメソッド領域に保存されます)
10。クラスローダーへの参照
//返されたのは、クラスローダーリファレンスstring.class.getClassLoader()です。これは今すぐ保存されています。クラスクラスへの参照//これは、今すぐ保存されているクラスクラスの参照文字列を返します。
メソッド領域は、ゴミコレクターによってリサイクルすることもできることに注意してください。
ヒープ
実行時にJavaプログラムによって作成されたすべてのクラスインスタンスまたは配列は同じヒープに配置され、各Java仮想マシンにもヒープスペースがあり、すべてのスレッドがヒープを共有しています(これが、マルチスレッドJavaプログラムがオブジェクトアクセスで同期問題を引き起こす理由です)。
各Java仮想マシンには、仮想マシンの仕様の実装が異なるため、各Java仮想マシンがヒープ内のオブジェクトインスタンスを表すフォームがわかりませんが、以下の可能な実装を垣間見ることができます。
プログラムカウンター
Javaプログラムを実行するには、各スレッドには独自のPC(プログラムカウンター)レジスタがあります。これは、スレッドが開始されたときに1つの単語のサイズで作成され、実行する必要がある次のコード行の場所を保存するために使用されます。
Java Stack
各スレッドにはJavaスタックがあり、スタックフレームの単位でスレッドの実行状態を保存します。 Javaスタックには、仮想マシンの操作には2種類の操作があります。スタックプレスとスタッキングと、どちらもフレームがあります。スタックフレームは、着信パラメーター、ローカル変数、中間操作結果などのデータを保存します。これらは、メソッドが完了してからリリースされたときにポップアップされます。
2つのローカル変数が一緒に追加されたときに、スタックフレームのメモリスナップショットを見てください
ローカルメソッドスタック
Javaは、JNI(Javaネイティブインターフェイス、Javaローカルインターフェイス)の実装に使用されるオペレーティングシステムのローカルライブラリを呼び出す場所です。
実行エンジン
Java Virtual Machineのコアは、Java Bytecodeと解析の読み込みを制御します。 Javaプログラムを実行するために、各スレッドは独立した仮想マシン実行エンジンのインスタンスです。スレッドライフサイクルの最初から最後まで、Bytecodeを実行するか、ローカルメソッドを実行します。
ローカルインターフェイス
ローカルメソッドスタックおよびオペレーティングシステムライブラリに接続します。
注:記事に記載されているすべての場所は、「JavaeeおよびJavaseプラットフォームのJava仮想マシン仕様」を参照しています。
仮想マシンメモリ最適化の練習
メモリが言及されているため、メモリリークに言及する必要があります。誰もが知っているように、JavaはC ++の基礎から開発されました。C++プログラムの大きな問題は、メモリリークを解決するのが難しいことです。 JavaのJVMには、メモリをリサイクルする独自のゴミ収集メカニズムがありますが、多くの場合、Javaプログラム開発者はあまり心配する必要はありませんが、漏れの問題もありますが、C ++よりも少し小さくなっています。たとえば、プログラムには参照されているが役に立たないオブジェクトがあります。プログラムがオブジェクトを参照しますが、将来使用しないか、使用できない場合、それが取り上げるメモリ空間は無駄になります。
まず、GCの仕組みを見てみましょう。アプリケーション、引用、引用、割り当てなどを含む各オブジェクトの実行ステータスを監視します。オブジェクトが引用されなくなったら、オブジェクトをリリースします(GCの焦点は説明しすぎません)。多くのJavaプログラマーはGCに依存しすぎていますが、問題の鍵は、JVMのごみ収集メカニズムがどれほど優れていても、メモリは常に限られたリソースであることです。したがって、たとえGCがごみ収集の大部分を私たちのために完了したとしても、エンコードプロセス中にメモリの最適化に適切に注意を払う必要があります。これにより、メモリの利用を改善し、プログラムの効率を最大化しながら、GCの数を効果的に削減できます。
全体として、Java仮想マシンのメモリ最適化は、Java仮想マシンとJavaアプリケーションの2つの側面から開始する必要があります。前者は、仮想マシンのメモリがプログラムのメモリ要件を補完するように、アプリケーションの設計に従って仮想マシンパラメーターを介して仮想マシンの論理メモリパーティションのサイズを制御することを指します。後者は、プログラムアルゴリズムを最適化し、GCの負担を軽減し、GCリサイクルの成功率を改善することを指します。
パラメーターを使用して仮想マシンメモリを最適化するためのパラメーターは次のとおりです。
xms
初期ヒープサイズ
xmx
Javaヒープ最大値
1mn
若い世代のヒープサイズ
XSS
各スレッドのスタックサイズ
上記は、より一般的に使用される3つのパラメーターで、一部は次のとおりです。
xx:minheapfreeratio = 40
拡張を避けるために、GC後のヒープフリーの最小割合。
xx:maxheapfreeratio = 70
GC後のヒープフリーの最大割合は、縮小を避けます。
XX:NewRatio = 2
新しい/古い世代サイズの比率。 [SPARC -Client:8; x86 -Server:8; x86 -Client:12。] - クライアント:8(1.3.1+)、x86:12]
XX:Newsize = 2.125M
新しい世代のデフォルトサイズ(バイト単位)[5.0および新しい:64ビットVMが30%大きくスケーリングされます。 x86:1m; x86、5.0以上:640k]
xx:maxnewsize =
新世代の最大サイズ(バイト単位)。 1.4以来、MaxNewsizeはNewRatioの関数として計算されます。
XX:Survivorratio = 25
Eden/Survivor Spaceサイズの比[1.3.1:25のSPARC; 5.0以前の他のSolarisプラットフォーム:32]
xx:permsize =
永久生成の初期サイズ
xx:maxpermsize = 64m
恒久的な世代のサイズ。 [5.0および新しい:64ビットVMは30%大きくスケーリングされます。 1.4 AMD64:96M; 1.3.1-クライアント:32m。]
プログラムアルゴリズムを最適化することにより、メモリの利用を改善し、メモリリスクを減らすために以下に言及していることは、完全に経験的であり、参照のみです。不適切なものがあれば、私を修正してください、ありがとう!
1.役に立たないオブジェクトの参照をできるだけ早くリリースします(xx = null;)
コードを見てください:
パブリックリスト<Pagedata> parse(htmlpageページ){list <pagedata> list = null; try {list valuelist = page.getByxPath(config.getContentXPath()); if(valuelist == null || valuelist.isempty()){return list; } //必要に応じてオブジェクトを作成し、メモリを保存し、効率リスト= new ArrayList <Pagedata>(); pagedata pagedata = new Pagedata(); StringBuilder値= new StringBuilder(); for(int i = 0; i <valuelist.size(); i ++){htmlelement content =(htmlelement)valuelist.get(i); domnodelist <htmlelement> imgs = content.getElementsByTagname( "img"); if(imgs!= null &&!imgsempty()){for(htmlelement img:imgs){try {htmlimage image =(htmlimage)img; string path = image.getsrcattribute(); string format = path.substring(path.lastindexof( "。")、path.length()); string localpath = "d:/images/" + md5helper.md5(path).replace( "//"、 "、")。ファイルlocalfile = new file(localpath); if(!localfile.exists()){localfile.createNewfile(); image.saveas(localfile); } image.setattribute( "src"、 "file:///" + localpath); localfile = null;画像= null; img = null; } catch(Exception E){}} //このオブジェクトは将来使用されません。参照をクリアすることは、GCに事前に伝えることに相当します。オブジェクトはimgs = nullをリサイクルできます。 } string text = content.asxml(); value.append(text).append( "<br/>"); valuelist = null; content = null; text = null; } pagedata.setContent(value.toString()); pagedata.setcharset(page.getPageEncoding()); list.add(pagedata); // pagedata = null;リストはまだオブジェクトへの参照を保持しており、GCは値= nullをリサイクルしないため、役に立ちません。 //ここにリストはありません= null;リストはメソッドの返品値であるため、メソッドから得られる返品値は常に空になります。この種のエラーは、発見または除外するのは簡単ではありません} catch(例外e){} returnリスト。 }2。配列、ツリー、グラフ、リンクリスト、その他のデータ構造など、コレクションデータ型を慎重に使用します。これらのデータ構造は、GCのリサイクルがより複雑です。
3.アレイスペースを明示的に申請しないでください。明示的に適用する必要がある場合は、その合理的な価値を可能な限り正確に推定するようにしてください。
4.クラスのデフォルトコンストラクターに多数のオブジェクトの作成と初期化を避け、クラスの独自のコンストラクターを呼び出す際にメモリリソースの不必要な無駄を防ぎます。
5.強制システムがゴミのメモリをリサイクルするのを避け、システム内のゴミリサイクルの最終時間を増やすようにしてください
6.リモートの発信者がインスタント値変数の値を取得する必要がない限り、リモートメソッドコールアプリケーションを開発するときは、インスタント値変数を使用してみてください。
7.適切なシナリオでオブジェクトプーリングテクノロジーを使用して、システムのパフォーマンスを向上させてみてください