JVM内存分配与GC

2 篇文章 0 订阅

=_=

本来这部分是不想写的,后面想想还是算了,毕竟是最早开始研究又算是相对了解的地方,这次就顺便总结一下看书的心得吧

JVM内存区域

JVM提程序员管理了内存区域,免去了不少麻烦,但我们还是需要去了解的。这个内存区域分为了两大类五个区:方法区与Java堆、虚拟机栈与本地方法栈与程序计数器。具体的解释在很久之前研究String常量池的位置时已经有解释过了,不再赘述

JVM内存分配

说到内存分配,还是得先了解一下JVM内存的分代管理,很久之前的一次面试有提到过这个分代机制,不过当时是懵逼的。

JVM的分代机制

为了方便管理Java内存中的对象,JVM采用了分代管理的机制,即把整个内存区分为了年轻代、年老代和永久代

  • 年轻代(young gen):这里的Java对象存活时间非常短,属于经常发生GC的区域
  • 年老代(old gen):这里的对象是占用内存较大或者是存活时间很长的,一般这里发生的GC就是内存空间真的不足了
  • 永久代(perm gen):其实这部分内存空间就是JVM内存区域中的方法区,里面存放了类的加载信息、静态变量、常量以及代码等信息,这里一般不会发生GC(不代表没有),一般我们所说的GC是不考虑这个区域的

GC

垃圾回收,这部分是JVM关键又重要的一个部分,虽然不需要我们直接参与管理,但一个Java程序员不懂垃圾回收是说不过去的,况且我们可以自由的设置JVM的GC来更好的提高程序运行的性能

什么时候开始GC

如何开始GC是我刚开始了解GC到一定程度时考虑的东西,毕竟说是说当内存空间不足时JVM便自动开始GC并回收内存区域,但如何得知JVM内存空间够不够呢?
查阅资料得知原来JVM对每个对象都有监视对象的监视器,用来监视对象的地址、大小以及使用情况,当该对象已死后,便会对该对象进行回收
呐,总结一下发生GC的条件:

  • 内存空间不足,又创建新对象并分配内存时
  • 主动调用方法来控制GC
  • 系统配置的某些参数改变,例如系统内存空间不足

主动触发GC的方法

  • 对象调用finalize()方法,不保证该方法一定会执行,且最多执行一次
  • 调用System.gc()方法,会触发Full GC
  • Runtime.getRuntime().gc()

需要注意的是,尽管有上述方法可以主动触发GC,但JVM并不会立即去执行,所以上面的方法也就是相当与通知JVM尽快执行GC,很有可能JVM还没来及GC,程序就已经结束了(笑死)

如何判断对象“已死”

如何判断对象“已死”是一个值得说道的地方,正确判断对象是否存货是开始GC的关键。按照常规定义,我们认为一个没有引用指向的对象便是一个已经死去的对象。这里一般有两种方法来判断对象是否还有引用指向:

  • 引用计数法:每有一个引用指向该对象,则其引用计数就加一
  • 可达性分析:从GC Roots为起始点开始向下遍历,从引用链搜索引用对象(由JVM自己的监视器来负责)

主流的商用语言都采用可达性分析的方法来判断对象是否存活,其实这有一个相对详细的过程,JVM会在对象被会收前进行两次标记,两次标记完成后,该对象才是真正意义上的死亡。

两次标记与对象的复活

JVM宣告一个对象真正死亡是需要对其进行两次标记的:如果对象经历过可达性分析后没有与GC Roots相连接的引用链,便会对其进行第一次标记,并根据其是否需要执行finalize()方法为条件进行筛选,如果需要执行便会调用finalize()方法,便会被放到一个F-Queue队列中,排队准备执行;如果不需要执行,直接进行第二次标记。

说到执行finalize()方法,这就是对象复活的最后一个机会,只要在执行的方法中重新建立一个引用关联即可。否则会被当作死亡对象进行第二次标记。但JVM不保证finalize()方法执行完毕,所以最好不用这个方法来进行GC,交给JVM管理就好

综上猜想,finalize()方法就是给该对象进行第二次标记并通知JVM回收

可作为GC Roots的对象

阿里的一次面试问到了这里,但当时没有记清楚,所有没有答好

  • 虚拟机栈(线程私有栈)中引用的对象
  • 方法区中类的静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(Java Native Interface)引用的对象

OopMap

这个地方也是面试时提到了,但是当时把它跟GC Roots的知识点记混了,尴尬,小哥很nice的说那我们不说这个了(尬笑

HotSpot在使用GC Roots的理念时,采用了新的实现方式,因为原来的方式一是任务量大而是GC时间长,因而使用OopMap数据结构来记录栈上不同位置上对象的情况,其本身不存在与栈中,但是保有栈中对象的使用信息。JVM通过查询OopMap就可以得知对象的存活情况

GC使用的算法

简单说就三种:标记-清理、复制算法和标记-整理算法
年轻代使用的是复制算法,年老代会使用标记-清理算法或者标记-整理算法

GC的回收器

年轻代:Serial、ParNew和Parallel Scavenge
年老代:Serial Old、Parallel Old和CMS
JDK1.8正式投入商用的G1

GC的种类

上面讲过的JVM内存分配机制只是泛泛而谈,算是一个引入,这里再结合内存分配机制与GC来看一下更为具体的内存分配机制

  • monitor GC:发生在新生代的GC,(具体的GC过程日后再补
  • Full GC:发生在新生代和年老代中的GC,算是对整个活动内存的清理

发生Monitor GC的条件

年轻代的GC是Monitor GC,针对Eden区满的时候触发,有时Monitor GC会直接转成Full GC,且Full GC的耗时会是Monitor GC的10倍以上。至于为什么会转成Full GC,是因为在Monitor GC时,会将存活较久的对象复制进年老代,如果复制前检测到年老代中剩余的空间不足以接受这些对象,则会触发Full GC来进行整个年轻代和年老代的GC。

发生Full GC的条件

Full GC的触发条件较多:

  • System.gc()的调用
  • 年老代空间不足
  • 永久代空间不足
  • CMS收集器出现promotion failedconcurrent mode failure异常
  • 统计得到Monitor GC晋升到老年代的平均大小大于老年代剩余空间
  • 堆中分配很大的对象,剩余空间不足

JVM内存分配策略

JVM负责管理内存的使用,主要遵循以下规则:

  • 对象优先在Eden区分配
  • 大对象直接进入老年代
  • 长期存活的对象将进入老年代
  • 动态对象年龄判定
  • 空间的分配担保

In The End

处理完这个,JVM的垃圾回收机制和内存分配机制就差不多了,本来这个是很久之前就应该写的,现在才补上,主要是觉得blog中JVM这个板块的东西有点少且有一点细节记混了,趁着端午放假,正好把JVM剩下的类加载和并发也都处理处理吧

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值