Java垃圾回收与对象创建,内存分配

2018-06-10  本文已影响0人  _Once1
垃圾回收.png

垃圾回收主要针对Java堆和方法区进行
如何判断对象是否存活,需要回收?


Java引用分类


回收过程
一个对象,需要经过两次标记,才可能会被回收

  1. 发现其不可达,会被标记一次,并进行一次筛选,条件是该对象是否有必要执行finalize方法
  2. 若有必要执行,则该对象会被放入一个队列中,然后其finalize会被执行
  3. gc会对队列中的对象进行第二次标记,如果对象在finalize方法中将自身与引用链上的任何一个对象建立关联,则该对象会被移出即将回收的集合;但是若其并没有实现上述关联,则会被回收掉
    注意:任何一个对象的finalize方法,都只会被系统自动调用一次!
    该方法不确定性较大,应该尽量避免使用

方法区的回收
永久代垃圾回收主要包括两部分:废弃常量和无用的类
判断是否是无用的类:
必须同时满足下面3个条件


垃圾收集算法

  1. 标记-清除算法
  2. 复制算法
    原始复制算法是将内存对半分,根据新生代对象的特点,将内存分为一块较大的edon空间和两块较小的survivor空间,每次使用一块edon和一块survivor,回收时,将上述两者中还存活的对象复制到另一块survivor中,然后再执行清理操作,hotspot虚拟机上述比例:8:1:1
    当剩余的一块survivor内存不够用时,需要依赖老年代进行分配担保,从而将剩余的存活对象直接进入老年代中
    缺点:对象存活率高时,复制操作较多,效率变低,并且需要分配担保,老年代无法直接采用该方法
  3. 标记-整理算法
    标记之后,让所有存活的对象都向一端移动,然后清理到另一端的内存,适用于老年代
  4. 分代收集

hotspot算法实现

  1. gc时,需要对可达性分析,此时,这个工作必须能够在一个确保一致性的环境中进行,因此,gc时需要停顿所有的Java线程。在系统停顿后,可以通过一个OopMap的数据结构,知道引用位置等信息。
  2. 安全点
    程序在执行至安全点时,才可以停顿下来开始gc
    如何在gc发生时,让所有线程都执行至最近的安全点才停顿呢?有以下两种方法:
  1. 安全区域
    指当进入一段代码片段之后,引用关系不会再发生变化,再该区域中任意地方开始gc都是安全的
    逻辑:线程进入安全区时,标识自己已经进入,则发起gc时,就不用再管这种状态的线程了;当线程要离开安全区时,要检查系统是否已经完成了根节点的枚举(或整个gc过程),若已经完成,那线程就继续执行,否则,就等到接收到可以离开安全区的信号为止

垃圾收集器
见导图


内存分配

内存分配.png

对象的内存分配,就是指在堆上分配,对象主要分配在新生代的edon分区上,如果启动了TLAB,则优先分配其上。
以下是几条最普遍的内存分配规则:

  1. 对象优先在edon分配
    当edon区没有足够的空间进行分配时,虚拟机会发起一次Minor GC
  2. 大对象直接进入老年代
    大对象是指需要大量连续内存空间的Java对象,例如很长的字符串和数组
  3. 长期存活的对象进入老年代
    如何判断长期存活呢?
    虚拟机给每一个对象定义了一个对象年龄计数器,如果对象出生在edon区,并且经过第一次minor gc后依然存活,并且能够被survivor容纳,将被移动至survivor中;并且对象年龄设置为1,对象在survivor分区中每经过一次minor gc,年龄就+1,当其年龄到一定程度(默认为15时),就会晋升至老年代。
  4. 空间分配担保
    发生minor gc之前,会先检查老年代最大可用连续空间是否大于新生代所有对象的总空间,如果大于,则minor gc是安全的;
    否则,虚拟机会查看HandlePromotionFailure值是否允许担保失败,若允许,则会检查上述最大值是否大于历次晋升至老年代对象的平均大小,若大于,则会冒险进行一次minor gc,若小于或者上面的Boolean值为不允许,则改为进行一次full gc
    注意,在jkd 6之后,HandlePromotionFailure参数已经没有作用了,也就是说,上述过程已无需对该参数进行判断。
上一篇 下一篇

猜你喜欢

热点阅读