JVM中的垃圾收集算法与内存分配

JVM中的垃圾收集算法

介绍什么是垃圾,怎么回收垃圾。本文的理论依据是《深入理解java虚拟机》第二部分 自动内存管理。如有错误,感谢指正。

垃圾收集

垃圾

什么是垃圾对象呢?那些创建出来用了一下 以后再也不会再用的对象,他还在内存里,但在业务和伦理层面我们认为他已经死了,变成了垃圾。既然死了那就别再占用宝贵的内存空间了,于是有了垃圾收集算法。这一节看一下怎么判断一个对象是垃圾。

引用计数算法

给对象添加一个引用计数器,每次引用就加1,引用失效就减1。
那循环引用呢?这种算法看似简单但实际上需要配合更复杂的逻辑处理特殊情况。
java并不用这个算法来标记垃圾对象

可达性分析

从一系列GC Roots往下连接这每一个对象,当对象没有连接上根的时候,说明这个对象已经死了。
GC Roots那么GCRoots都是哪些呢:

  • 虚拟机栈引用的对象
  • 方法区静态属性引用的对象
  • 方法区常量引用的对象
  • 本地方法栈引用的对象
  • 系统类
  • 锁持有的对象
  • 等等

根节点枚举的过程会Stop The World.

引用

  • 强引用 不会被回收
  • 软引用 内存不足时,回收
  • 弱引用 在下一次GC将会被回收
  • 虚引用 被虚引用指向的对象在被回收时得到一个通知

回收垃圾

被可达性分析认定为垃圾的对象不会立马被回收,而是经历两轮标记。

  • 第一轮标记看对象有没有实现Object中的finalize()方法,如果没有,或者finalize()已经被虚拟机调用过,则标记为不需要调用finalize()。
  • 第二轮虚拟机将触发finalize()方法,如果这次方法中没有将对象和GC Roots上的节点关联,则下一次将会被回收。从上面的第一轮可以看出,对象只能在这个finalize()中自救一次,延迟一次GC的死亡时间。

分代收集

根据大多数对象都会频繁创建和消亡的频率进行分代。根据对象的“分代年龄”将对象存储在不同的内存分区。

一般情况下,分为

  • 新生代
  • 老年代

在新生代中不停有对象死去(判断对象已死也是有算法的,比如可达性分析算法),当分代年龄达到一定数值的对象晋升到老年代。

垃圾收集

根据深入理解java虚拟机给出的定义,垃圾收集的说法如下:

  • Partial GC 部分收集
    • Minor GC/Young GC 新生代收集
    • Major GC/Old GC 老年代收集(只有CMS会单独收集老年代)
    • Mixed GC 混合收集
  • Full GC 整堆收集

默认情况下 新生代中的Eden和两个Survivor区的占比是8:1:1, 当新生代中无法再给对象分配堆空间将会触发一次Minor GC。

标记-清除算法

顾名思义,首先将已经判定为垃圾的对象标记为可清除,然后一次性去除。这样会在内存中留下许多内存碎片。

标记-复制算法

标记复制算法是为了解决标记清除算法的不足的。例如内存碎片和效率低下。
首先将内存等分为两块,每次GC将垃圾标记为可回收,然后将不可回收的对象复制到另一半内存中,将这边的内存一次性清除。很明显这种算法是拿空间换时间,太过浪费。且在存活率高的时候,复制的次数太多。

标记-整理算法

在老年代中对象的存活率很高,就不能使用标记复制算法,于是出来了个标记整理算法。

先标记,然后对存活的对象进行移动归整内存。

内存分配

一般情况下,对象都会分配在堆空间里,这里讨论的内存分配也指的是堆空间的内存分配。

整体看一下jvm:
jvm

堆内存情况

HotSpot虚拟机为例对堆内存站台讨论。

堆内存的细分

HotSpot虚拟机中,堆内存被划分为三个部分,新生代、老年代、永久代(次概念jdk1.8被移除)。

  • 新生代(Young Generation):占用整个堆空间的一部分,用于存放新创建的对象,分为Eden区和两个Survivor区。

  • 老年代(Old Generation):经过多次垃圾回收仍然存活的对象会被移到老年代,如果该对象所在的Eden区或Survivor区已满,则直接在老年代上分配空间。

  • 永久代(Permanent Generation):用于存放JVM自身的类信息、方法信息、运行时常量池等。

其中新生代的划分在上面提到过,再做描述。

新生代详解

新生代划分成了三个部分,他们分别是Eden区、From Survivor区、To Survivor区。

在java中大部分对象都是短命鬼,来到java世界也许只用了一次就死了。能长生不老的对象并不多。所以需要给堆空间分区,其中在这些短命鬼中也不乏有人能成功一步步晋升长生不老,所以给新生代也划分了不同区域,用来选拔对象。

HotSpot中,Eden区和两个Survivor的占比是8:1:1.
新生代

当对象被首次创建的时候会进入到Eden区。
那么From Survivor区、To Survivor区是干嘛的呢?
From Survivor用来保存在 Minor GC中存活下来的对象(对象每存活一次分代年龄就会+1 )。
那么为什么要有两个 Survivor呢?
为了归整,每次新生代GC后,会将From Survivor中的对象一次放入To Survivor中,然后互换From SurvivorTo Survivor.这样不会产生太多的内存碎片。

思考:如果survivor区的大小不足了,那么存活下来的对象需要晋升怎么办呢?
默认情况下会开启-XX:HandlePromotionFailure让老年代通过一定的策略为此次GC做担保,老年代认为可以容纳晋升的对象的时候会根据情况选择是否Full GC腾出更多空间存放晋升的对象。如果将-XX:HandlePromotionFailure=false将会不允许担保,那么每次发生这种情况都会Full GC

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值