什么是JVM?

JDK、JRE、JVM三者之间的关系?

首先JDK它是咱们整个Java的核心,包括了Java运行时环境JRE和一些Java开发工具(比如说编译器和调试器之类的)。
然后JRE是运行JAVA程序所必须的环境的集合,它包含JVM和Java的核心类库。
JVM呢它是整个Java实现跨平台的最核心的部分,它能够运行以Java语言写的程序。

什么是JVM?

JVM其实它就是字节码的运行环境,它会把字节码文件装载到它的内部,、
后解释器/编译器会将JVM字节码指令转换为对应平台的机器指令执行,这样一来就屏蔽了不同操作系统在底层硬件和指令上的区别。
这也是Java语言一次编译,到处运行实现跨平台的原因。
3)如何查看字节码(Class)文件
一般来说我们可以使用javap命令或者是一些可视化工具比如jclasslib都可以查看字节码文件的内容,字节码文件主要包含了四部分信息:
第一部分就是字节码的基本信息(比如说最后一次修改时间,版本号,大小,从哪个源文件编译过来的)
第二部分就是常量池
第三部分就是构造函数的信息
第四部分就是类里面的方法相关的信息
4)JVM的体系结构?
我了解到的整个JVM是由五大部分组成的:
 第一部分就是类加载器子系统:它会将Class文件加载到JVM内存中。
然后它的整个加载过程还划分为了三步:加载 > 链接(验证、准备、解析)> 初始化。
第一阶段加载阶段主要是通过类的全限定名获取到对应的字节流,然后将字节流代表的静态存储结构转化为方法区的运行时数据结构,
最后在堆中生成这个类的Class对象实例,作为访问方法区这些数据结构的入口。
第二阶段链接阶段就是为类变量(静态变量)分配内存并且设置该类变量的默认初始值,即零值。这里不包含用final修饰的static,
因为final在编译的时候就会分配了。这里也不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到堆里面。
第三阶段初始化阶段就是执行类构造器方法 <clinit[cl以内特]>() 的过程,clinit这个方法不是咱们自己定义的,而是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。
如果这个类有父类的话,JVM会保证子类的< clinit >()方法执行前,父类的< clinit >()已经执行完毕。而且JVM会保证一个类的clinit方法只会被调用一次。
 第二部分就是运行时数据区:它也叫JVM内存模型,里面主要用于保存就是Java程序在运行过程中产生的数据。它里面又分为堆、虚拟机栈、本地方法栈、方法区、程序计数器。
其中这个堆和方法区是所有线程共享的区域,
虚拟机栈、本地方法栈、程序计数器呢是每个线程私有的区域。
我记得程序计数器里面保存的就是下一套要执行的JVM指令的地址, JVM里面的执行引擎会通过程序计数器读取下一条JVM指令。
虚拟机栈呢是在线程创建的时候创建,线程结束的时候就会被销毁,所以对于虚拟机栈来说它不存在垃圾回收问题,只要线程一结束这个虚拟机栈也就结束了,生命周期和线程一致,是线程私有的。
虚拟机栈里面放的又是栈帧。就是线程在调用每一个的方法的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
 局部变量里放的就是方法中的局部变量,方法的参数也算是局部变量。它放的是8种基础数据类型的变量和对象的引用。
操作数栈就是方法在运行过程种临时保存数据的一块区域。
方法的返回地址种保存的就是方法被调用时PC寄存器的值。
动态链接就是在程序运行过程中将符号引用转换为直接引用的过程。
有一部分符号引用在类加载阶段的解析阶段就已经被转为直接引用了。本地方法栈(Native Method Stacks)呢和虚拟机栈类似,
只不过虚拟机栈是为Java方法服务,而本地方法栈它是本地Native方法服务。
方法区与Java堆一样,是各个线程共享的内存区域,它里面保存的就是类信息、常量、静态变量等数据。
最后我来说一下堆吧,堆内存是整个JVM内存中占用较大的一块区域,咱们的java对象都保存在堆里面。
在堆中,又分为新生代及老年代,新生代中又分两个区域,分别是Eden[依den]去和Survivor[涩歪沃]区, Survivor[涩歪沃]区又分为from和to,
堆内存也是JVM调优的重点区域。
像我们new出来的对象一般都放在Eden区,但是一个程序只要在运行,那么它就会不停的new对象,那么总有一天Eden区会放满,那么一旦Eden区被放满之后,虚拟机会干什么呢?
没错,就是GC,不过这里的GC属于Minor[毛内] GC(指发生在新生代的垃圾回收)。在经历了第一次Minor GC后,没有被回收的对象就会被移到From区。
另外JVM中对象的存储模型中,在对象头的Mark Word中有存储GC分代年龄,一个对象每经历一次GC,那么它的GC分代年龄就会+1。
那么如果第二次新的对象又把Eden区放满了,那么又会执行Minor GC,但是这次会连着From区一起GC,然后将Eden区和From区存活的对象都移到To区域,对象头中分代年龄都+1。
那么当第三次Eden区又满的时候,Minor GC就是回收Eden区和To区域了,Eden区和To区域还活着的对象就会都移到From区,如上图。说白了就是Survivor区中总有一块区域是空着的,
存活的对象存放是在From区和To区轮流存放,也就是互相复制拷贝,这也就是垃圾回收算法中的复制算法。如果一个对象经历了一个15次GC都没死,就会移至老年代。
还有一种情况,对象也会直接晋升到老年代,比如说From区或者To区域放不下了,就会直接挪到老年代。
当然随着咱们程序的不断运行,老年代最终也是会满的,那么此时也会进行GC,此时的GC就是Full[for] GC(Full GC是发生在老年代的垃圾回收)了。
执行引擎:里面包含了解释器、JIT(Just In Time)即时编译器、垃圾收集器。解释器就是逐条读入字节码指令,逐条解释执行的。
JIT即时编译器会第一次使用某段代码的时候会把所有的字节码编译成本地代码,以后再执行该段代码的时候就不用再重新编译了。垃圾收集器就是进行垃圾回收的。
本地方法接口:这个好像是和本地方法调用有关系的,没有过深的了解过。
本地方法库:这个好像也是和本地方法调用有关系的,没有过深的了解过。
5)常用的类加载器有哪些?
引导类加载器(Bootstrap ClassLoader[布特丝chua普克拉斯捞得])、
扩展类加载器(Extension ClassLoader[A克丝ten薪克拉斯捞得])、
系统(应用程序)类加载器(AppClassLoader[A屁屁克拉斯捞得])。
其中系统类加载器的父类加载器是扩展类加载器,扩展类加载器的父类加载器是引导类加载器,引导类加载器是根类加载器,没有父类加载器。
6)如何自定义类加载器?
继承ClassLoader类然后重写findClass方法。
7)双亲委派机制
当一个类加载器收到一个加载类的请求时,并不会自己先去加载,而是把请求委托给它的父类加载器,
如果它的父类加载器也有父类加载器,那么这个请求将会继续向上委托,直到委托到顶层类加载器,顶层类加载器会尝试加载此类,如果加载失败,则会把请求向下委托,
如果向下经过层层委托之后所有的类加载器都无法加载此类,那么就会抛出异常(ClassNotFoundException)。这就是双亲委派模式。它的好处就是:
防止类重复加载:一旦一个类被父类加载器加载过了,子类加载器就不用再重新加载。
安全:防止Java里面的核心API被篡改,比如说有人自己定义了一个类Integer,包名是java.lang,在加载一个类时,加载类的请求会被层层向上委托至顶层类加载器
,那么顶层类加载器也就是启动类加载器会尝试加载此类,但是它加载的此类是Java核心包下的java.lang.Integer,而不是用户自己定义的java.lang.Integer,这样就防止了核心API被篡改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值