1.jvm基础知识
1.什么是 jvm ?
JVM全称Java Virtual Machine,翻译过来也就是Java虚拟机。JVM的实际功能也是和翻译一样,类似于一台虚拟操作系统,是通过在实际的计算机上模拟各种计算功能来实现的。JVM有着自己完善的硬件架构,例如处理器、堆栈、寄存器等,还具有相应的指令系统。
JVM是JRE(Java Runtime Environment)的一部分,安装了JRE其实也就是相当于安装了JVM,可以运行Java程序了。
2.常见的 jvm
HotSpot JVM HotSpot VM是绝对的主流。大家用它的时候很可能就没想过还有别的选择,或者是为了迁就依赖了Oracle/Sun JDK某些具体实现的烂代码而选择用HotSpot VM省点心。
Oracle / Sun JDK、OpenJDK的各种变种(例如IcedTea、Zulu),用的都是相同核心的HotSpot VM。当大家说起“Java性能如何如何”、“Java有多少种GC”、“JVM如何调优”云云,经常默认说的就是特指HotSpot VM。可见其“主流性”。
JDK8的HotSpot VM已经是以前的HotSpot VM与JRockit VM的合并版,也就是传说中的“HotRockit”,只是产品里名字还是叫HotSpot VM。这个合并并不是要把JRockit的部分代码插进HotSpot里,而是把前者一些有价值的功能在后者里重新实现一遍。移除PermGen、Java Flight Recorder、jcmd等都属于合并项目的一部分
不过要留意的是,这里的HotSpot VM特指“正常配置”版,而不包括“Zero / Shark”版。Wikipedia那个页面上把后者称为“Zero Port”。用这个版本的人应该相当少,很多时候它的release版都build不成功
J9是IBM开发的一个高度模块化的JVM。在许多平台上,IBM J9 VM都只能跟IBM产品一起使用。这不是技术限制,而是许可证限制。例如说在Windows上IBM JDK不是免费公开的,而是要跟IBM其它产品一起捆绑发布的;
使用IBM Rational、IBM WebSphere的话都有机会用到J9 VM(也可以自己选择配置使用别的Java SE JVM)。根据许可证,这种捆绑在产品里的J9 VM不应该用于运行别的Java程序…大家有没有自己“偷偷的”拿来跑别的程序IBM也没力气管
咳咳而在一些IBM的硬件平台上,很少客户是只买硬件不买配套软件的,IBM给一整套解决方案,里面可能就包括了IBM JDK。这样自然而然就用上了J9 VM。所以J9 VM得算在主流里,虽然很少是大家主动选择的首选。
J9 VM的性能水平大致跟HotSpot VM是一个档次的。有时HotSpot快些,有时J9快些。
不过J9 VM有一些HotSpot VM在JDK8还不支持的功能,最显著的一个就是J9支持AOT编译和更强大的class data sharing,JRockitJRockit以前Java SE的主流JVM中还有JRockit,跟HotSpot与J9一起并称三大主流JVM。这三家的性能水平基本都在一个水平上,竞争很激烈。
自从Oracle把BEA和Sun都收购了之后,Java SE JVM只能二选一,JRockit就炮灰了。JRockit最后发布的大版本是R28,只到JDK6;原本在开发中的R29及JDK7的对应功能都没来得及完成项目就被终止了。
2.ClassFileFormat
3.类加载器-类的初始化
1.加载过程
2.类的初始化:
在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值。
在程序中,静态变量的初始化有两种途径:
1.在静态变量的声明处进行初始化;
2.在静态代码块中进行初始化。
没有经过显式初始化的静态变量将原有的值。
package demo01day;
public class Initialization {
public static void main(String[] args) {
System.out.println(T.count);
}
}
class T {
public static int count = 2;//0
public static T t = new T();//null
private T(){
count++;
}
}
可见将生成对象的语句放在两个位置,输出是不一样的(相应位置的输出已在程序注释中标明)。
这是因为初始化语句是按照顺序来执行的。
静态变量的声明语句,以及静态代码块都被看做类的初始化语句,Java虚拟机会按照初始化语句在类文件中的先后顺序来依次执行它们。
3.类的初始化步骤
1.loading读取加载
1.双亲委派机制,主要是处于安全角度考虑
2.lazyLoading五种情况
· -new getstatic putstatic invokestatic指令,访问final变量除外
-初始化子类的时候,父类首先初始化
-虚拟机启动时,被执行的主类必须初始化
-动态语言支持java.lang.invoke.MethodHandle解析的结果为REF getstatic REF putstatic
REF inv okestatic的方法句柄时,该类必须初始化
3.ClassLoader源码
1. findInCAche -> parent.loaderClass - > findClass()
4.自定义类加载器
1.extends ClassLoader
2.overwrite findClass() -> defineClass(byte[] -> Class clazz)
3.加密
5.混合执行 编译执行 解释执行
1.监测特点代码: -XX:CompileThreshold = 10000
2.Linking初始化
1.Verification
1.验证文件是否符合JVM规定
2.preparation
· 1.静态成员变量赋默认值
3.Resolution
3.initializing
调用类初始化代码 <clinit> ,给静态成员变量赋初始值
对于我们开发人员,我认为应该具体了解一下初始化阶段什么时候在开始。JVM规范对此做了严格规范,有且只有以下5种情况必须对类进行初始化:
-
遇到new、getstatic、putstatic或invokestatic这四条字节码指令时,如果类没有被初始化过,就需要先进行初始化。对于字节码指令不了解的同学,可能就是一脸蒙圈了。我们来说人话,就是:使用new关键字实例化对象的时候、读取和设置一个类的静态字段(不被final修饰的)和调用一个类的静态方法的时候。这样说更容易被理解一些。
-
JVM会先初始化要执行的主类,也是包含main()方法的那个类。
-
当使用JDK 1.7的动态语言支持时,如果java.lang.invoke.MethodHandle实例最后的解析结果是REF_getStatic(使用MethodHandle读取类的静态字段)、REF_putStatic(使用MethodHandle设置类的静态字段)、REF_invokeStatic(使用MethodHandle调用类的静态方法)的方法句柄时,如果这个方法句柄没有被初始化过,就需要先进行初始化。
注意事项:
接口也有初始化过程,和类是一致的。不过接口中不能使用“static{}”语句块,但编译器仍然会为接口生成“clinit()”类构造器,用于初始化接口中所定义的成员变量。
接口初始化的时机,基本和之前提到的类的5种情况基本一致,唯一不一样的是第3种情况:在一个类被初始化时,它的父类也必须被初始化,但是一个接口被初始化时,它的父接口并不要求被初始化。只有在真正使用到父接口时才会被初始化,比如:引用父接口中定义的常量。
4.对象创建过程:
1.class loading
2. class linking(verification,preparation,resolution)
3.class initializing
4.申请对象内存
5.成员变量赋默认值
(1)成员变量顺序赋初始值
(2)执行构造方法语句 VB
对象在内存中的存储布局
普通对象
1. 对象头:markword 8
2.Classpoint指针:-XX:+UserCompressedOops为4字节 不开启为8字节
3.实例数据
1.引用类型:-XX+UseCompressedOops为 4 字节 不开启 8 字节
2.Okps Ordinary Object Promters
4.Padding对齐,8 的倍数
数组对象
数组对象只是比普通对象多一个数组的长度罢了,多了 一个数组长度:4字节
对象头:
jvm对象头信息是与对象自身定义的数据无关的额外存储的信息,由于它存在于对象中,jvm规范中安装对象类型,分两种类型:
- 普通对象包含:Mark Word、元数据指针(Klass Pointer)
- 数组对象包含:Mark Word、元数据指针(Klass Pointer)、Array Length
32位JVM-->Mark Word
mark word里存放的是对象运行时的信息,不同状态的对象里mark word 存放的信息是不同的,如下:
存储内容(30bit) 锁状态
(2bit)identify_hashcode:25|age:4|biased_lock:1 (01)无锁
threadId:23 | age:4 | epoch:2 | biased_lock:1 (01)偏向锁
ptr_to_lock_record:30 (00) (00)轻量级锁
ptr_to_heavyweight_monitor:30 (10)重量级锁
gc_info:30 (11)GC标记
64位JVM-->Mark Word
存储内容(62bit) 锁状态(2bit)
unused:25 | identify_hashcode:25 | unused:1 | age:4 | biased_lock:1 (01)无锁
unused:25 | identify_hashcode:25 | unused:1 | age:4 | biased_lock:1 (01)无锁锁
record:62(00)轻量级锁 ptr_to_heavyweight_monitor:62 (10)重量级锁
gc_info:62 (11)GC标记
说明:
1、名词:
元数据指针(Klass Pointer):类型指针存放的是该对象对应的类的指针。即该指针应该指向方法区的内存区域。
Array Length:数组长度只在数组类型的对象中存在。用于记录数组的长度。避免获取数组长度时,动态计算。以空间换时间的做法
5.JVM(四)-Runtime Data Area
解释:运行时数据区 -就是Java虚拟机定义的在程序运行期间使用各种运行数据区域。其中一部分数据区就是Java虚拟机上创建的,只有当Java虚拟机启动和退出的时,该数据区域才会创建和销毁。另外一部分数据区域为每个线程创建。这部分运行时数据区域在线程运行时创建,线程退出时销毁。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。