Javaコンパイラとランタイムの最適化の最適化:JVM仮想マシン8の深い理解

Javaコンパイラの最適化

川や湖] [Javaの技術のJava、テクニカルエンジニアアリステーションのマイクロチャネル公共番号。ジャワにコミット学習経験を、共有するためにSSM、SpringBoot、MySQLの、分散、ミドルウェア、クラスタ、Linuxでは、ネットワーク、マルチスレッド、時折話すポイントドッカー、ELKだけでなく、ドライ商品や技術著者:黄斜めには、Javaの関連技術に焦点を当てフルスタックの開発!(国民の関心の後には、返信の「Java」Javaは、基本的な高度、およびプロジェクトの建築家や他の無料の学習教材、より多くのデータベース、分散、サービス及びその他の人気のマイクロ学習ビデオ技術、豊富なコンテンツ、理論と実践の両方を受け取ることはできませんまた、研究ガイドのジャワ、Javaプログラマのインタビューガイドおよびその他の呉服資源の原作者となります提示)

 

                     9eedaaa588bef997bef63a7160fa349134bdb78c

それは、次の3つのカテゴリのコンパイルプロセスに分けることができるため、不確実性の運転期間中に実際にJava言語をコンパイルします
:1.フロントコンパイルに.javaファイルを .classファイル、ファイル
のバックエンドコンパイラ2.:バイトコードにマシンコード
3.静的先にコンパイル:* .javaファイルを直接ネイティブマシンコードにコンパイルされたファイル
JDK1.3から仮想マシンがチームのパフォーマンスを最適化するように設計されて起動しますが、javacがそれらをない作ることができるので、集中管理バックエンド時のコンパイルにかかりましたコンパイラの最適化によって生成された(例えばJRubyの、Groovyと他の言語のクラスファイルなど)のクラスファイルもの恩恵享受することができ
、最適化プロセスが実行して、フロントエンドのリアルタイムのためのより重要である実行時にJavaコンパイラをとでコンパイル近い関係のプログラム・コードをコンパイルするためのプロセスの最適化    

早期(コンパイル時に)最適化

早期编译过程主要分为3个部分:
1.解析与填充符号表过程:词法、语法分析;填充符号表  
2.插入式注解处理器的注解处理过程  
3.语义分析与字节码生成过程:标注检查、数据与控制流分析、解语法糖、字节码生成
ジェネリック医薬品と型消去

一般的なJava言語プログラムのソースはコンパイルされたバイトコードファイルで、元が元の型に置き換えられている存在し、適切な場所に挿入されたコードをキャスト

泛型擦除前的例子    
public static void main( String[] args ) { Map<String,String> map = new HashMap<String, String>(); map.put("hello","你好"); System.out.println(map.get("hello")); } 泛型擦除后的例子 public static void main( String[] args ) { Map map = new HashMap(); map.put("hello","你好"); System.out.println((String)map.get("hello")); }
自動梱包、開梱およびループを通ります

自動包装は、コンパイル後の開梱は、Integer.valueOf()とInteger.intValue()のように、対応するパッケージング及び還元方法に変換するが、ループイテレータ実装なる、可変引数を介してバックコードを配置しますこれは、配列型のパラメータとなります。
しかしながら、ラッパークラス「==」は自動的に演算を経験することなく動作を開梱しない、およびそれらのequals()メソッドは、データ変換関係を処理しません。

条件付きコンパイル

Java言語は、条件付きコンパイル方法することができif文の定数条件を使用することです、それはコンパイル時に「実行」になります。

public static void main(String[] args) { if(true){ System.out.println("block 1"); } else{ System.out.println("block 2"); } } 编译后Class文件的反编译结果: public static void main(String[] args) { System.out.println("block 1"); }

シンタックスシュガーのJava言語である文は、trueとfalseのブール定数値によると、ブランチはコンパイラがコードのブロックを除去することができます保持していない場合にのみ、条件が一定であります

後期(実行時)の最適化

インタプリタとコンパイラ

Javaプログラムが最初にコンパイルする時間の必要性を排除し、インタプリタ、プログラムはすぐに起動して実行する必要があるときに、インタプリタは最初に再生することができますによって解釈され、すぐに実行し、プログラムが実行されると、時間をかけて、徐々にコンパイル高効率化を達成するために、ネイティブコードにコンパイルされ、より多くのコードをもたらすことに役割を果たしています。効率を高めるために、メモリ、コンパイラの実装保存解釈。 ラジカルの仮定が成立しないとき、ほとんどの時間のいくつかを選択する確率に基づいて、コンパイラは最適化手法、最適化の速度を高めることができるように、コンパイラの最適化は、「ハッチをエスケープ」とき同時に、インタプリタは、脱最適化によりに戻り、その後、ラジカルとして使用することができます継続する状態を説明します。

HotSpot仮想マシンは、クライアントコンパイラ(C1コンパイラ)とサーバーコンパイラ(C2コンパイラ)、デフォルトの方法と呼ばれる2つのタイムコンパイラ、に組み込まれたもので通訳や仕事との直接のコンパイラ、コンパイラの使用仮想マシンの走行モードに応じて、あなたも自分自身を指定することができます。強制した場合、仮想マシンが実行中で、「インタプリタモード」された「編集モード」で実行中の仮想マシン、優先コンパイルモード実行プログラムに強制すると、作業中に全く関与コンパイラは、まだコンパイルした場合の実施に関与するインタプリタが行うことができませんプロセス。

レイヤードコンパイル戦略
分层编译策略作为默认编译策略在JDK1.7的Server模式虚拟机中被开启,其中包括:
第0层:程序解释执行,解释器不开启性能监控功能,可触发第1层编译;
第1层:C1编译,将字节码编译成本地代码,进行简单可靠的优化,如有必要将加入性能监控的逻辑;
第2层:C2编译,也是将字节码编译成本地代码,但是会启动一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。 实施分层编译后,C1和C2将会同时工作,C1获取更高的编译速度,C2获取更好的编译质量,在解释执行的时候也无须再承担性能监控信息的任务。 
ホットコードプローブ
在运行过程中会被即时编译器编译的“热点代码”有两类:
1.被多次调用的方法:由方法调用触发的编译,属于JIT编译方式
2.被多次执行的循环体:也以整个方法作为编译对象,因为编译发生在方法执行过程中,因此成为栈上替换(OSR编译)

热点探测判定方式有两种:
1.基于采样的热点探测:虚拟机周期性的检查各个线程的栈顶,如果某个方法经常出现在栈顶,则判定为“热点方法”。(简单高效,可以获取方法的调用关系,但容易受线程阻塞或别的外界因素影响扰乱热点探测)
2.基于计数的热点探测:虚拟机为每个方法建立一个计数器,统计方法的执行次数,超过一定阈值就是“热点方法”。(需要为每个方法维护计数器,不能直接获取方法的调用关系,但是统计结果精确严谨)  

第二の使用のHotSpot仮想マシンは、それが各方法のカウンタの二種類用意:メソッド呼び出しカウンタとバックカウンタ側を、次の図は、メソッド呼び出しトリガカウンタタイムコンパイルを表します。

如果不做任何设置,执行引擎会继续进入解释器按照解释方式执行字节码,直到提交的请求被编译器编译完成,下次调用才会使用已编译的版本。另外,方法调用计数器的值也不是一个绝对次数,而是一段时间之内被调用的次数,超过这个时间,次数就减半,这称为计数器热度的衰减。

下图表示回边计数器触发即时编译:

回边计数器没有计数器热度衰减的过程,因此统计的就是绝对次数,并且当计数器溢出时,它还会把方法计数器的值也调整到溢出状态,这样下次进入该方法的时候就会执行标准编译过程。

编译优化技术

虚拟机设计团队几乎把对代码的所有优化措施都集中在了即时编译器之中,那么在编译器编译的过程中,到底做了些什么事情呢?下面将介绍几种最有代表性的优化技术:
公共子表达式消除
如果一个表达式E已经计算过了,并且先前的计算到现在E中所有变量的值都没有发生变化,那么E的这次出现就成为了公共表达式,可以直接用之前的结果替换。
例:int d = (c * b) * 12 + a + (a + b * c) => int d = E * 12 + a + (a + E)

数组边界检查消除
Java语言中访问数组元素都要进行上下界的范围检查,每次读写都有一次条件判定操作,这无疑是一种负担。编译器只要通过数据流分析就可以判定循环变量的取值范围永远在数组长度以内,那么整个循环中就可以把上下界检查消除,这样可以省很多次的条件判断操作。

另一种方法叫做隐式异常处理,Java中空指针的判断和算术运算中除数为0的检查都采用了这个思路:

if(foo != null){
    return foo.value;
}else{ throw new NullPointException(); } 使用隐式异常优化以后: try{ return foo.value; }catch(segment_fault){ uncommon_trap(); } 当foo极少为空时,隐式异常优化是值得的,但是foo经常为空,这样的优化反而会让程序变慢,而HotSpot虚拟机会根据运行期收集到的Profile信息自动选择最优方案。

連合内のメソッド
の方法では、このように様々なコンパイラの中に、一般的にシーケンスの最前方位置の最適化に最適化に関連するが、Javaオブジェクトためになる、コスト関連するメソッドの呼び出しを削除するだけでなく、他の最適化のための良い基盤を確立することができますデフォルトのメソッドは仮想メソッドなので、メソッド呼び出しがインライン仮想メソッドを、問題を解決するために、実行時ポリモーフィズムで選択する必要があり、第1の技術「タイプの継承関係の分析(CHA)」を導入しています。

1.在内联时,若是非虚方法,则可以直接内联  
2.遇到虚方法,首先根据CHA判断此方法是否有多个目标版本,若只有一个,可以直接内联,但是需要预留一个“逃生门”,称为守护内联,若在程序的后续执行过程中,加载了导致继承关系发生变化的新类,就需要抛弃已经编译的代码,退回到解释状态执行,或者重新编译。
3.若CHA判断此方法有多个目标版本,则编译器会使用“内联缓存”,第一次调用缓存记录下方法接收者的版本信息,并且每次调用都比较版本,若一致则可以一直使用,若不一致则取消内联,查找虚方法表进行方法分派。

エスケープ解析
オブジェクトが方法、エスケープと呼ばれる方法の外に参照されたときに、基本的な動作分析対象エスケープ分析動的範囲は、ある外部のスレッドによってアクセスされた場合、スレッドはエスケープと呼ばれます。我々は、オブジェクトがメソッドまたはプロセス外で参照することはできませんことを証明できる場合は、いくつかの最適化変数ことを考えることがあります。

1.栈上分配:如果确定一个对象不会逃逸,则可以让它分配在栈上,对象所占用的内存空间就可以随栈帧出栈而销毁。这样可以减小垃圾收集系统的压力。  
2.同步消除:线程同步相对耗时,如果确定一个变量不会逃逸出线程,那这个变量的读写不会有竞争,则对这个变量实施的同步措施也就可以消除掉。  
3.标量替换:如果逃逸分析证明一个对象不会被外部访问,并且这个对象可以被拆散的话,那么程序真正执行的时候可以不创建这个对象,改为直接创建它的成员变量,这样就可以在栈上分配。

しかし、そこにそれが消費よりもパフォーマンスの向上の分析が高くなければなりません脱出保証はありませんので、技術は非常に成熟していないです。

C / C ++コンパイラの比較でのJava

Java虚拟机的即时编译器与C/C++的静态编译器相比,可能会由于下面的原因导致输出的本地代码有一些劣势:
1.即时编译器运行占用的是用户程序的运行时间,具有很大的时间压力,因此不敢随便引入大规模的优化技术;
2.Java语言是动态的类型安全语言,虚拟器需要频繁的进行动态检查,如空指针,上下界范围,继承关系等;
3.Java中使用虚方法频率远高于C++,则需要进行多态选择的频率远高于C++;
4.Java是可以动态扩展的语言,运行时加载新的类可能改变原有的继承关系,许多全局的优化措施只能以激进优化的方式来完成;
5.Java语言的对象内存都在堆上分配,垃圾回收的压力比C++大

然而,Java语言这些性能上的劣势换取了开发效率上的优势,并且由于C++编译器所有优化都是在编译期完成的,以运行期性能监控为基础的优化措施都无法进行,这也是Java编译器独有的优势。

川や湖] [Javaの技術のJava、テクニカルエンジニアアリステーションのマイクロチャネル公共番号。(国民の関心の後には、返信の「Java」Javaは、基本的な高度、およびプロジェクトの建築家や他の無料の学習教材、より多くのデータベース、分散、サービス及びその他の人気のマイクロ学習ビデオ技術、豊富なコンテンツ、理論と実践の両方を受け取ることはできませんまた、研究ガイドのジャワ、Javaプログラマのインタビューガイドおよびその他の呉服資源の原作者となります提示)

おすすめ

転載: www.cnblogs.com/xll1025/p/11369843.html