运行时数据区
实际案例:
class文件常量池,主要存放字面量(例如字符串、final修饰的常量),符号引用(例如类和接口的全限定名、字段的名称和描述符、方法的名称和描述符)
当类加载到内存后,JVM就会将静态常量池中的内容存放到运行时常量池中,运行时常量池里面存储的主要是编译器间生成的字面量、符号引用等等
字符串常量池,也可以理解为运行时常量池分出来的一部分,类加载到内存的时候,字符串,会存到字符串常量池里面
每调用一次方法,就会压入一个元素(栈帧),当方法返回的时候,则会把这个元素弹出。
每个一个栈帧里面存储元素:
局部变量、操作数栈(存放临时数据)、指向运行时常量池的引用、方法返回地址、动态连接
与虚拟机栈类似,本地方法管理的是Native 方法
用来记录各个线程的字节码地址,分支、循环、跳转等等操作,都需要依赖程序计数器。Java是线程语法,CPU之间要进行切换。可以为每个线程分配一个程序计数器。
.java ->.class->加载 ->连接(验证-准备-解析)-> 初始化->使用->卸载
一个class文件包含以下部分:
描述信息(class文件存储地址,文件大小,MD5值,类信息)
常量池
字段信息
方法信息
读取类的二进制,转为方法区数据结构,并存放到方法区,在Java堆中产生Java。lang.Class对象
为类的静态变量分配内存,初始化为系统的初始值
1.验证class文件是不是符合规范,文件格式验证(是否以0xCAxxxxx开头,版本号是否合理)
2.元数据验证,是否有父类,是否继承了final类(final类不能被继承,如果继承了就有问题了),非抽象类实现了所有抽象方法
3.字节码验证,运行检查,栈数据类型和操作码参数吻合(比如栈空间只有2个字节,但是其实却需要大于2个字节,此时认为这个字节码是有问题的),跳转指令指向合理的位置
4.符号引用验证,常量池中描述类是否存在,访问的方法或字段是否存在且有足够的权限。可以使用-Xverify:none关闭验证
为类的静态变量分配内存,初始化为系统的初始值,final static 修饰的变量:直接赋值为用户定义的值,比如private final static int value = 123,直接赋值123,
private static int vlaue =123,该阶段的值依然是0
符号引用转换层直接引用
执行<clinit>方法,clinit方法由编译器自动收集类里面的所有静态变量的赋值动作以及静态语句块合并而成,也叫类构造器方法。
1.初始化的顺序和源文件中的顺序一致
2.子类的<clinit>被调用前,会先调用父类的<clinit>
3.JVM会保证clinit方法的线程安全性
初始化时候,如果实例化一个新对象,会调用<init>方法对实例变量进行初始化,并执行对应的构造方法内的代码
优势在于没有编译的等待时间,性能相对差一些
运行效率会高很多,一般认为比解释执行快一个数量级,带来额外开销
Java -version ,mixed mode 混合运行
-Xint:设置JVM的执行模式为解释执行模式
-Xcomp:JVM优先以编译模式运行,不能编译的,以解释模式运行
-Xminxed:混合模式运行
一般情况下,一开始一般由解析器解释执行。当虚拟机发现某个方法或代码块的运行特别繁琐的时候,就会认为这些代码是“热点代码”。为了提高热点代码的执行效率,会用即时编译器(JIT)把这些热点代码编程成本地平台相关的机器码,并进行各层次的优化
是一个简单快速的编译器,主要关注局部性的优化,适用于执行时间较短或对启动性能有要求的程序。例如:GUI应用对界面启动速度就有一定要求,也被称为是Client Compiler
是为长期运行的服务器端应用程序做性能调优的编译器,适用于执行时间较长或对峰值性能有要求的程序,也被称为Server Compiler
0.解释执行
1.简单C1编译:会用C1编译器进行一些简单的优化,不开启profiling
2.受限的C1编译器,仅执行带方法调用次数以及循环回边执行次数profiling的C1编译器
3.完全的C1编译,会执行带所有profiling的C1代码
4.C2编译器,使用C2编译器进行优化,该级别会启用一些编译耗时较长的优化,一些情况下会根据性能监控信息进行一些非常激进的性能优化
总结:级别越高,应用启动越慢,优化的开销越高,峰值性能也越高。
只想开启C2:-XX:-TieredCompilation (禁用中间编译层123层)
只想开启C1:-XX:+TieredCompilation -XX:TieredStopAtLevel=1
基于采样的热点探测
基于计数器的热点探测
用于统计方法被调用的次数,在不开启分层编译的情况下,在C1编译器下的默认阈值是1500次,在C2模式下是10000次也可以使用-XX:CompileThreshold=X指定阈值
用于统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边”。在不开启分层编译的情况下,C1编译器下的默认阈值13995,C2默认为10700,可以用户-XX:OnStackReplacePercentage=x指定阈值
原文:https://blog.51cto.com/liuyj/2775608