Java虚拟机学习12 | 垃圾回收(下)

https://time.geekbang.org/column/article/13137

Java虚拟机的堆划分

Java虚拟机将堆划分为新生代和老生代. 其中新生代还被划分为Eden区和两个大小相等的Survivor区

Java虚拟机采用动态分配的策略,根据生成对象的速率,以及Survivor区的使用情况动态调整Eden区和Survivor区的比例

 

通常当我们调用new指令时,它会在Eden区中划分一段内存来存储对象. 但是堆内存时线程共享的,因此在这里划分内存时需要同步的,否则会有多个对象公用一段内存的情况

对此Java虚拟机的做法是每个线程申请一段连续的内存,例如:2048字节,作为线程私有的TLAB; 这个操作需要加锁,线程需要维护两个指针,一个是直线TLAB中空余内存的起始位置,另一个指向TLAB的末尾

接下来new指令只需要通过指针加法(bump the pointer)来实现,即把指向空余内存的指针加上所请求的字节数; 当指针加法后指向空余内存的指针仍然小于指向末尾的指针,则分配内存成功;否则就失败,线程需要重新申请TLAB

当Eden区空间耗尽时,Java虚拟机会触发Minor GC,回收垃圾,存活下来的对象会移到Survivor区中

当Minor GC执行时:Eden区和from指向的Survivor区的存活对象会复制到to指针指向的Survivor,然后交换from和to指针的指向,保证to指针指向的Survivor区一定是空的

当一个对象被复制的次数达到15次时会被移动到老生代,或者当Survivor占用空间达到50%时,会将复制次数较高的对象移动到老生代

卡表

当垃圾回收时,会碰到一种情况是老生代中的对象可能引用新生代的对象.这种情况下每次遍历老生代对象查找所有的存活对象,并且,通常来说这样的引用是非常少的.

对于这种情况HotSot的解决方案是卡表(Card Talbe)技术,该技术将堆分为一个个512字节的卡,并维护一个卡表存储每张卡的一个标识位,这个标识位表示对应的卡是否可能存在对新生代对象的引用,如果可能存在,就认为对应卡是脏的.

因此在Minor GC时,不再需要扫描整个老生代,,而是在卡表中寻找脏卡,并将脏卡中的对象加入到 Minor GC 的 GC Roots 里. 当完成所有脏卡的扫描之后,Java 虚拟机便会将所有脏卡的标识位清零.同时在GC过程中伴随着新生代对象的复制,因此需要更新对象的引用,在此时维护卡表就保证了卡表里的的脏卡都是对新生代对象的引用

Java虚拟机中的垃圾回收器

  • Serial:使用标记-复制算法,单线程,新生代回收器
  • Parallel Scavenge:Serial的多线程版本,新生代回收器
  • Parallel New:和Parallel Scavenge类似,吞吐量更强,新生代回收器
  • Serial Old:使用标记-压缩算法,单线程,老生代
  • Parallel Old:Serial Old的多线程版本,老生代
  • CMS:标记-清除算法,多线程,除了少数动作需要Stop-the-World外,可以在应用程序运行的情况下进行垃圾回收.当并发收集失败,会使用另外两个老生代回收器进行一次垃圾回收,在Java9中废弃
  • G1(Garbage first):横跨新生代和老生代的垃圾收集器,它将整个堆分为若干个区域,优先回收死亡对象多的区域.它采用标记-压缩算法,能够在程序运行过程中回收垃圾
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值