一、JVM是什么?
JVM的基本概念:对于java来说JVM是一个可运行java代码的假想计算机
JVM里面有一套 字节码指令集,一组寄存器,一个栈,一个垃圾回收器,堆和一个存储方法域。他运行在操作系统上,与硬件没有直接交互。
二、JVM的内存区域
1.思维导图
2.JVM运行时的内存
java的堆从GC角度分为新生代和老年代,比例是新生代1/3,老年代2/3。
因为频繁的创建对象所以新生代会频繁触发MinorGC进行(小范围GC回收)。
所以新生代分为 Eden区,ServivorFrom区,ServivorTo区。
Eden区,对象刚创建的时候,如果对象很大的话会直接进入老年代。当Eden区内存不够就会触发一次GC,对新生代进行一次垃圾回收。
ServivorFrom区,上一次GC的幸存者,会被当做这次GC的扫描者。(这里我的猜想是上次GC没有回收他但是会标记一下他,方便下次回收。)
ServivorTo区,保留了一次MinorGC过程中的幸存者(这里你看的会有点懵,不着急慢慢往下看)
MinorGC的垃圾回收算法
MinorGC采用的是复制算法,首先把Eden区和ServivorFrom区中存活的对象复制到ServivorTo区去(如果有对象的年龄到达老年代的标准放入老年代),然后清空Eden和ServivorFrom中的对象,然后在把ServivorFrom区和ServivorTo区互换,原 ServicorTo 成为下一次 GC 时的ServicorFrom 区了。
什么是老年代
老年代主要存放生命周期长的内存对象。老年代的对象比较稳定,所以MajorGC不会频繁执行,一般执行MajorGC的时候新生代都会执行一次MinorGC使新生代的对象晋升老年代,然后导致老年代空间不足引发MajorGC。也有一种情况当无法为一个大对象分配一块连续空间时候就会触发MajorGC清理出一块空间。
什么是永久代
内存永久保存的区域,主要存放class和元数据(1.8以后叫这个名字)的信息,class在被加载的时候就会被存放入永久代,GC不会在主程序运行期间对永久代区域进行清理。所以这也导致永久代区域会随着class的增多然后爆满,最终出现OOM异常。
3.JVM如何确定垃圾?
1.引用计数法
java中引用和对象是有关联的(强弱软虚四种引用这里不做解释,大家可以百度),如果要操作对象必须要通过引用来操作,因此看这个对象有没有被引用,如果没有被引用哪这个对象就是可以被回收的的对象。
这里会有 一个循环引用的问题,就是两个对象之间互相引用,但是没有其他的对象引用他们。这种情况下引用计数法就没有办法回收这两个对象了。
2.根可达法
这个方法就是解决引用计数法里面的循环引用的问题。java通过一些列GC root根对象作为起点去搜索。如果GC root和一个对象之间不可达,那么这个对象就是不可达对象,注意!!!并不是不可达对象就要被回收,这里还要经历再次标记才能判断该不可达对象是否可以回收!
第一次标记筛选
筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize方法,或者finzlize方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”,对象被回收。
第二次标记
如果这个对象被判定为有必要执行finalize()方法,那么这个对象会被放入一个F-Queue的队列中。
finalize方法是对象逃脱死亡的最后一步,GC会对F-Queue队列中的对象进行二次小规模的回收,如果这个时候对象和GC root引用链上的任何一个对象挂钩的话那么就逃离了死亡。第二次标记的时候就会把这个对象移除“即将回收”的集合,如果没有逃脱那么就死翘翘了。
4.JVM的垃圾回收算法
标记清除算法
最基础的垃圾回收算法,分为两步,第一步标记,标记出需要被回收的对象,然后执行第二步清除。
这个算法缺点,造成内存碎片化严重,后续大对象没办法找到可以利用的空间。
复制算法
为了解决标记清除算法的内存碎片化严重的问题,出现了复制算法。原理,把当前的内
存区域一分为二,只使用一半,当那一半内存满了后把存活的对象复制进另外一半,然
后把已使用的那一半内存清干净。就这样来回倒。
缺点内存使用率只有原来的一般了,如果对象过多效率会很慢。
标记整理算法
结合上面两个算法,先标记存活的对象然后把他们都整理到一端,然后把另一端的对象全部清理掉
效率高,内存使用率高
分代收集法
因为jvm会根据对象存活的生命周期划分不同的区域,分为新生代和老年代
新生代会采用复制算法,因为新生代每次回收大部分对象,需要被复制的少而且新生代会被划分Eden区和两个Servivor区域,每次只使用Eden区和一块Servivor区域。
老年代采用标记整理算法,老年代每次回收只回收少量对象。
分区收集法
分区算法则将整个堆空间划分为连续的不同小区间, 每个小区间独立使用, 独立回收. 这样做的好处是可以控制一次回收多少个小区间 , 根据目标停顿时间, 每次合理地回收若干个小区间(而不是整个堆), 从而减少一次 GC 所产生的停顿