java虚拟机的基本原理

java虚拟机的基本原理

1:什么是jvm
是运行所有Java程序的抽象计算机,运行所有Java程序的抽象计算机,是Java语言的运行环境,它是Java 最具吸引力的特性之一。

java的跨平台是必须要有jvm的支持,就是不同平台支持jvm,然后才能一份java程序在不同平台运行。

参考: java虚拟机

2:进程角度
虚拟机jvm就是一个操作系统中的进程实例

jvm在操作系统中运行,进程是操作系统的执行单位,启动一个java的程序,就是一个JVM进程实例,虚拟机进程启动就绪,然后由虚拟机中的类加载器加载必要的class文件,包括jdk中的基础类(如String和Object等),然后由虚拟机进程解释class字节码指令,把这些字节码指令翻译成本机cpu能够识别的指令,才能在cpu上运行。

3: jvm如何吃进java语言编写的程序?
java虚拟机内部,有一个叫做类加载器的子系统,这个子系统用来在运行时根据需要加载类,

“根据需要”
在Java虚拟机执行过程中,只有他需要一个类的时候,才会调用类加载器来加载这个类,并不会在开始运行时加载所有的类。

4:jvm如何处理java的字节码文件?
由虚拟机加载的类,被加载到Java虚拟机内存中之后,虚拟机会读取并执行它里面存在的字节码指令。虚拟机中执行字节码指令的部分叫做执行引擎。Java虚拟机会进行自动内存管理。具体说来就是自动释放没有用的对象,而不需要程序员编写代码来释放分配的内存。这部分工作由垃圾收集子系统负责。
5.jvm基本原理图
类加载器,执行引擎,垃圾收集
在这里插入图片描述
6.内存原理图
在这里插入图片描述

  1. 如何判断一个对象是否该被回收
    主要通过对象是否还有其他引用或关联使该对象处于存活的状态。判断对象是否存活有两种比较常见方法:

(1)引用计数法
在堆中存储对象时,在对象头维护一个counter计数器,若一个对象增加了一个引用与之相连,则counter++,如一个引用关系失效则counter–,若一个对象的counter变为0,则说明该对象已被废弃,不处于存活状态。

存在的问题:

1)jdk1.2开始增加了多种引用方式,在不同引用情况下,程序应采用不同的操作,只采用一个引用计数法无法准确区分这么多种引用的情况。

2)引用计数无法解决操作中死锁的循环持有。

(2)对象可达性分析算法
通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链(从GC Roots到这个对象不可达)时,则证明此对象是不可用的。它们到GC Roots是不可达的,将会判断为是可回收的对象。

可作为GC Roots的对象:
1)虚拟机栈(栈帧中的本地变量表)中引用的对象;

2)方法区中类静态属性引用的对象;

3)方法区中常量引用的对象;

4)本地方法栈中JNI(即一般说的Native方法)引用的对象;

GC Roots即指对象的引用,对对象的操作是通过引用来实现的,引用是指向堆内存对象的指针,如果当前对象没有引用指向,那该对象无法被操作,被视为垃圾。可达性分析算法主要从对象的引用出发,寻找对象是否存在引用,若不存在进行标识处理,为GC做准备。

  1. HotSpot虚拟机如何实现可达性算法
    HotSpot需要枚举所有的GC Roots根节点,虚拟机栈空间不大,但方法区的空间很可能有数百M,遍历一次需要很久,而且遍历所有的GC Roots根节点时,需要暂停所有用户线程,因此需要一个此时的“虚所机快照”,若不暂停用户线程,则虚拟机仍处运行态,无法确保正确遍历所有的根节点。

(1)OopMap数据结构
HotSpot实现了一种OopMap的数据结构,它会在类加载完时把对象内什么偏移量是什么类型计算出来,在JIT编译时,也会记录栈和寄存器中哪些位置是引用,当需要遍历根结点时访问所有OopMap即可。

(2)安全点
HotSpot在OopMap帮助下可以快速且准确完成GCRoots枚举,但运行中非常多的指令会导致引用关系变化,且为这些指令都生成OopMap,需要的空间成本太高。故只在特定位置记录OopMap引用关系,这些位置称为安全点。选定标准“是否具有让程序长时间执行的特征”,如方法调用、循环跳转、循环的末尾、异常跳转等,只有具有这些功能的指令才会产生Safepoint.

(3)如何在安全点上停顿
A)抢先式中断

不需要线程主动配合,GC发生时,先中断所有线程,如果发现不在Safepoint上的线程,就恢复让其运行到Safepoint上;

B)主动式中断

在GC发生时,不直接操作线程中断,仅简单设置一个标志,让各线程执行时主动去轮询这个标志,发现中断标志为真时就自己中断挂起;

  1. 判断一个对象生存还是死亡
    要真正宣告一个对象死亡,至少需要经历两次标记过程:

(1)第一次标记
在可达性分析后发现到GC Roots没有任何引用链相连时,被第一次标记,进行一次筛选,此对象是否有必要执行finalize()方法

没有必要执行情况:对象没有覆盖finalize()方法;finalize()方法已被JVM调用过一次;这两种情况可认为对象已死,可以回收;

有必要执行:对有必要执行finalize()方法的对象被放入F-Queue队列中,稍后JVM自动建立、低优先级的Finalizer线程(可能多个线程)中触发此方法;

(2)第二次标记
GC将F-Queue队列中的对象进行第二次小规模标记,finalize()方法是对象逃脱死亡的最后一次机会

A)若对象在finalize()方法中重新与引用链上任何一个对象建立关联,第二次标记时会将其移出“即将回收”的集合;

B)若对象没有,也可认为对象已死,可以回收了。

finalize()方法执行时间不确定,甚至是否被执行也不确定(Java程序不正常退出),且运行代价高昂,无法保证各对象调用顺序。

  1. GC触发条件
    (1)Minor GC触发条件
    当Eden区满时,触发Minor GC

(2)Major GC触发条件
老年代空间不足时,触发Major GC,许多Major GC是由Minor GC触发的。

(3)Full GC触发条件
A)调用System.gc时,系统建议执行FullGC,但不是必然执行

B)老年代空间不足

C)方法区空间不足

D)为避免新生代晋升到老年代失败,当MinorGC后进入老年代的对象占用内存空间平均大小大于老年代的可用内存;

F)年代晋升失败,如由Eden区存活的对象晋升到S区放不下,又尝试晋升到Old区又放不下,会触发FullGC.

  1. Minor GC\MajorGC\Full GC区别
    HotSpot堆结构

(1)新生代(Young Generation)
绝大多数最新被创建的对象会被分配到这里,由于大部分对象在创建后会变得不可达,所以很多对象创建在新生代,用完被回收。对象从Young Generation区域被回收的过程称为minor GC, 即Minor GC cleans the Young Generation

新生代用来保存那些第一次被创建的对象,它可以被分为三个空间

A)Eden空间,内存被调用的起点

B)Survivor 0\Survivor1 S0àS1,age++

MinorGC回收操作:
YoungGen区空间不足时,会触发MinorGC,这会把存活的对象转移进入Survivor区。采用复制整理算法进行回收,先扫描出存活的对象,并复制到一块新的完全未使用的空间中,对于新生代,就是在Eden和From Space或To Space之间的复制。新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,检查空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从Eden到Survivor,最后到老年代。

(2)老年代(Old Generation)
当对象在Survivor区熬过一定次数的Minor GC后,就会晋升到老年代。空间比新生代大,发生在老年代上的GC比新生代少,对象从老年代中回收的过程称为Major GC。

MajorGC对象的回收操作:
由于老年代对象存活时间较长、较稳定,因此它采用标记(Mark)算法来进行回收,先扫描出存活的对象,再进行回收未标记的对象,回收后对空出的空间要么进行合并、要么标记出来便于下次进行分配,总之目的就是要减少内存碎片带来的效率损耗。

(3)永久代(permanent generation)
也称方法区,主要存放.class等文件,类方法、类名、常量池,数组中的引用等。FullGC发生时,永久代也可能会被回收。

(4)MinorGC\MajorGC\FullGC区别
Minor GC是清理年轻代的

Major GC是清理老年代的

Full GC是清理整个堆空间,包括年轻代和永久代

  1. 符合什么条件的对象会进入老年代
    (1)大对象
    大对象是指需要大量连续内存空间的Java对象,典型的大对象包括很长的字符中、数组等,大对象对虚拟机的内存分配来说不是个好消息,尤其是一些朝生夕死的大对象。

(2)长期存活的对象
虚拟机给每个对象定义了一个对象年龄计数器,如果对象在Eden出生并经过一次MinorGC后仍存活,且能被Survivor容纳,被移到Survivor区,并将年龄设为1。对象在Survivor区中每熬过一次MinorGC,年龄就增加1,当它年龄增加到一定程度(默认15)时,就会晋升到老年代中。对象晋升老年代的年龄阈值可通过参数-XX:MaxTenuringThreshold来设置。

(3)动态对象年龄判定
为能更好适应不同程度的内存状况,虚拟机并不是永远要求对象必须达到MaxTenuringThreshold才能晋升到老年代,若在Survivor空间中相同年龄的所有对象大小总和大于Survivor空间一半,年龄大于或等于此年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

Java虚拟机的内存区域分成五块,其中三个是线程私有的:程序计数器、Java虚拟机栈、本地方法栈;另两个是线程共享的:Java堆、方法区。线程私有的区域等到线程结束时(栈帧出栈时)会自动被释放,空间较易被清理。而线程共享的Java堆和方法区中空间较大且没有线程回收容易,GC垃圾回收主要负责这部分。

Java堆和方法区主要存放各种类型的对象(方法区也存储一些静态变量和全局常等信息),故在使用GC对其进行回收时首先要考虑的就是如何判断一个对象是否应该被回收。
————————————————

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值