深入理解Java虚拟机(2)对象创建与GC

对象创建与GC

在该系列的第一节中介绍了Java的内存分布,在上一节可以看出,对象的创建与回收都主要发生在Java堆中。本节主要讨论对象在内存中的创建以及Java虚拟机中的垃圾回收

Java堆分为新生代和老年代,新生代发生的GC称为Minor GC,老年代发生的GC称为Major GC,新生代和老年代都进行GC成为Full GC。

对象内存分配算法

Java堆为对象分配内存时,有两种分配方法。

  1. 指针碰撞
    这种分配方法,每次为对象分配内存时,指针由已分配的区域向未分配的区域移动,以此来为对象分配内存,这种分配方法是一种规整的选择,即内存分配后不会出现内存碎片
  2. 空闲列表
    这种分配方法,不保证不会出现内存碎片,因此维护了一张空闲列表,记录未被分配的内存,每次为对象分配内存,就从空闲列表中挑选出一块足够的空间为其分配,不可避免地会产生内存碎片。

如何判断对象已死

  1. 引用计数算法,对象被引用时,引用数+1,接触引用时-1,当该对象引用数为0时看作不会再被用到,则回收,这种方法简便效率高,但是无法处理一些情况。

  2. 可达性分析算法,将一系列称为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,即为不可达,则该对象不可用,会被判定为可回收的对象。
    一般来说,在Java堆内存溢出时,会分析其dump文件,查看各对象的引用链,即是否存在从该对象到GC Roots的路径,分析是否有不可被回收的对象,以此来找出内存溢出的原因。
    可以被看作是GC Roots的对象有:1. 虚拟机栈中的对象引用(局部变量表引用的对象);2. 方法区中类静态属性引用的对象; 3. 方法区中常量引用的对象; 4. 本地方法栈中的Native方法引用的对象。

方法区的垃圾回收

垃圾回收主要发生在Java堆中,但方法区中也会进行一些垃圾回收,方法区中存放的是类信息、常量池等信息,常量池的垃圾回收与堆中对象的垃圾回收相似,比如,对于字符串“abc”,如果没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用这个字面量,则回收该常量,即清理出常量池。

方法区中类的回收,三个条件:1. Java堆中不存在该类的任何实例,即该类的任何实例都已经被回收;2. 加载该类的ClassLoader(类加载器)已经被回收;3. 该类对应的java.long.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。 当满足这三个条件时,该类可以被回收*(即卸载该类,防止方法区内存溢出)

垃圾收集算法

  1. 标记-清楚算法:先标记可回收的对象,然后统一回收。
    缺点:效率低,内存碎片不连续(未压缩)

  2. 复制算法:将内存分为两部分,一次只使用其中一半,当回收对象时,把其中一半的存活对象直接拷贝到另一半内存中。
    这种方法效率高,不会产生内存碎片,但是,如上述的那样,会使内存减半。实际上一般是分为3部分,一个E区和两个S区,比例为8:1:1,其中一个S区充当复制存活对象的区域,这样实际只会使内存减少10%。(这种方法通常在新生代使用,因为新生代垃圾回收时,对象回收率较大,存活下来的对象较少)

  3. 标记-整理算法:同标记-清除算法,只是先标记,不清除,标记后把所有对象向一端移动,然后清除,这样就实现了压缩,不会产生内存碎片。

  4. 分代回收: 如复制算法中提到的那样,新生代对象存活率低,选择复制算法,老年代对象存活率高,选择标记-清除或者标记-整理算法。

安全点和安全区域

  1. 安全点
    当发生GC时,所有线程都要在安全点,因为处于安全点时不会引起对象引用变化,即不会产生新的垃圾,这里分为抢先式和主动式,a. 抢先式是当发生GC时,所有线程先中断,然后不在安全点上的线程恢复运行跑到最近的安全点上;b. 主动式是设置标志位,标志位和安全点重合(外加创建对象分配内存的地方),每个线程不断轮询标志位,发生中断标志为真就中断。

  2. 安全区域
    安全点适用于运行的线程,安全区域用于没运行的线程,是一段不会引起对象引用变化的代码片段,相当于安全点的扩展。

对象内存分配细节

  1. 优先分配在新生代的E区(1个E区和两个S区,8:1:1,新生代使用复制算法进行垃圾回收)。
  2. 大对象直接进入老年代。比如byte[]数组。
  3. 长期存活的对象将进入老年代。如果对象在E区出生,在经历一次Minor GC后仍然存活,且S区装得下该对象,则该对象会被移入S区,对象年龄+1,以后每次Minor GC后对象年龄都+1,加到一定程度(默认为15)时会被移入老年代。
  4. 动态对象年龄判定,如果S区中某些相同年龄的对象所占内存总和大于S区的一半,则年龄大于等于该年龄的对象将直接进入老年代。
  5. 空间分配担保,老年代为新生代提供担保,在新生代进行Minor GC前,会检测老年代的连续内存空间是否大于新生代所有对象的总和,如果大于,则本次Minor GC无风险,否则有风险,再根据参数看是否允许有风险,如果允许,则查看老年代连续内存是否大于历来晋升到老年代对象的平均值,如果大于,则尝试进行Minor GC,尽管会有风险,如果小于或者不允许有风险会进行一次Major GC(即老年代进行垃圾回收)。如果仍担保失败,则在失败后会重新进行一次Full GC。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值