讲一下Java的虚拟机

讲一下Java的虚拟机

       这个问题这样问太大了,今天看到有问这个问题,我在想应该怎么回答,我认为可以先对JVM内存区域进行介绍,讲到堆以后就顺带着讲垃圾回收算法,既然要垃圾回收,那么怎么判断对象已死?,那么接着可以讲一下常见或者自己熟悉的垃圾回收器。我觉得这样应该就差不多了

首先对于JVM的内存区域来说,分为程序计数器,虚拟机栈,本地方法栈,堆和永久代,在JDK1.8中取消了永久代(方法区的实现),增加了元空间区域,

  1. 程序计数器:字节码的行数指令器,线程私有。
  2. 虚拟机栈:线程私有,一个方法从执行到结束伴随的过程就是一个栈帧从入栈到出栈的过程,栈帧包括局部变量表,方法出口,操作数栈,动态链接等。
  3. 本地方方法栈:线程私有,与虚拟机栈不同的是它执行的是Java的native方法。
  4. 堆:线程共享,一个对象从出生到死亡的区域,也是垃圾回收的重点区域,堆分为初生代和老年代,默认比例1:2,初生代又分为Eden,From survivor,To survivor,默认比例为8:1:1,对象刚开始一般出生在Eden区域(大对象会直接一定到老年代),每经历一次minor gc,每次使用Eden和其中的一个survivor区域,将判断存活的对象移动到另外一个survivor区域,存活的对象年龄计数器+1,默认为增加到15就移动到老年代,如果老年代快要满的时候那么便会引发Full GC,当然还存在另外一种情况,只要survivor区域的对象相同年龄的占了一半以上,那么就将他们全部直接移动到老年代。
  5. 元空间:线程共享,1.8取消了永久代,增加了元空间,那么原本属于永久代的方法区就移动到了元数据区,元空间区域使用系统内存,理论上可以和系统内存一样大,默认最大是4G,但是初始值是12M左右,它的大小随着程序运行会动态调整。在JDK1.8中方法区的实现也就由元空间实现,方法区中存在运行时常量池。

接着说垃圾回收算法,有标记清理,标记整理,复制算法。

  1. 标记清理:对于已死对象,将可回收的对象直接回收,缺点:清理后内存空间不完整。
  2. 复制算法:将内存区域分为2部分,每次只使用其中的一块,每次将存活的对象全部移动到另外一块内存区域,这样解决了清理后内存空间不完整的问题,例如新生代就是采用这种策略。
  3. 标记整理:清理后会对空间进行整理。

目前jvm使用的是分代回收策略,将内存区域分为初生代和老年代,初生代对象朝生夕死,所以采用复制算法,老年代没有人可以给他内存担保,所以使用标记清理或者标记整理。

那么如何判断对象已死可以回收呢?

两种方法,一种是引用计数法,一种是可达性分析算法。

  1. 引用计数法:给对象添加一个引用计数器,每次被引用,计数器+1,引用失效-1,等于0回收,但是这种方法存在缺陷,如果是循环引用,A引用B,B引用A,那么AB都无法进行回收。(引用计数法缺陷
  2. JVM采用的是可达性分析算法,一些对象被认定为GC Roots,从这些对象节点开始遍历,如果存在某个对象和roots之间存在引用链,那么就说明对象有效,不用进行回收,如果不存在,那么就可以进行回收。可以作为GC Roots的对象有:1虚拟机栈中引用的对象,2,本地方法栈中引用的对象,3方法区中常量引用的对象,4方法区中类静态属性所引用的对象。(可以作为GC Roots的对象

既然说到引用,那么就可以提一下四种引用

  1. 强引用:只要强引用还在,那么就不会被GC。
  2. 软引用:等到内存不够用了会被GC。
  3. 弱引用:只能活到下次GC,下次GC会直接被GC。
  4. 虚引用:设置虚引用只是为了在被回收的时候系统得到一个通知。

接着说自己熟悉的垃圾回收器,一般说CMS和G1就够用了。

垃圾回收器CMS:

CMS垃圾回收器基于标记清理算法,它分为四个阶段

  1. 初始标记:需要stop the world(stw)(暂停所有用户线程),这个阶段仅仅标记一下GC Roots可以关联到的对象,速度很快。
  2. 并发标记:这个阶段是GC Tracing的阶段,用户线程和标记线程一起运行。(耗时较多)
  3. 重新标记:需要stw,标记在并发标记期间用户线程运行时所变动的那部分标记记录。
  4. 并发清理:和用户线程一起,边清理,用户线程边运行。(耗时较多)

CMS垃圾回收器所存在的问题:

  1. 对CPU资源非常敏感,CMS默认开启的回收线程总数为(CPU数量+3)/4,所以可以看出,在2核CPU的情况下,回收线程占了50%左右,这是完全不能接受的,所以这对于CPU核数越多越好。
  2. 由于采用的是标记清理算法,所以有可能在GC后由于连续空间不足又引发下一次GC。
  3. 无法清理浮动垃圾,这是因为在第四阶段的并行清理过程中,由于用户线程也在运行,所以这部分产生的垃圾只能在下一次清理掉。也是由于这个问题,所以GMS回收器不能等到老年代满了再进行垃圾回收,必须给老年代预留一部分空间来供于并发清理中用户线程使用,如果预留的空间不足,那么可能会引发”concurrent mode failure”。

G1垃圾回收器主要具有以下特性:

  1. 采用空间整理,而不是CMS的标记清理算法,这样做的好处就是不会因为在分配大对象的时候而存在空间不足,而又提前引起下一次GC。
  2. 可预测的停顿:这是G1相对于CMS的一大优势,使用者可以明确指定在长度为M毫秒的时间内,消耗在垃圾回收上的时间不得超过N秒,能实现这个功能,是因为G1垃圾回收器将整个Java堆分为了多个大小相等的region区域,虽然还保留新生代和老年代的概念,但是新生代和老年代不再是物理隔离的了,它们都是一部分region的集合。G1在进行垃圾回收时,它会跟踪每个region里面的垃圾回收的价值(回收所获得的空间以及所花费的时间),在后台维护一个优先列表,每次根据允许的收集时间优先回收价值最大的region。

G1垃圾回收器大致可以分为以下几个步骤:

  1. 初始标记:标记一下GC roots能够直接关联到的对象,需要stw,时间很短
  2. 并发标记:是从GC Root开始对堆中的对象进行可达性分析,找出存货对象,耗时较长,但是和用户线程并发执行。
  3. 最终标记:为了修正在并发标记期间因为用户线程继续运行而导致标记发生变动的那一部分记录,需要stw,但是是最终标记并行执行的,
  4. 筛选回收:这个阶段优先回收价值高的region,根据用户所期望的停顿时间来执行回收计划,这个过程其实也可以做到和用户线程并发执行,但是停顿用户线程可以大幅提高回收效率。这个阶段需要stw。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值