JVM 垃圾回收

JVM 垃圾回收

在C/C++中我们创建一个对象,为其分配内存空间之后,是需要程序员手动释放内存,清理不需要的对象(垃圾)。而在java中JVM虚拟机是会有后台的GC线程帮我们自动清理的。

一、什么是垃圾

Java程序中每new一个对象,都会在堆或者栈上分配对应的内存空间。

A a = new A()

那么如何确定这个对象什么时候变为垃圾呢?

垃圾:程序中的一块内存没有被任何变量持有引用,导致这块内存无法被这个程序再次访问时,这块内存被称为垃圾。

二、怎么找到垃圾

引用计数

过去java通过引用计算,即一个对象被引用一次,引用计数加一,某个变量不再引用这个对象,计数减一,当引用计数为0时,则将这个对象视为垃圾。

存在问题:两个对象互相持有对方的引用,但是没有任何其他变量引用这两个对象,则两个对象引用计数都为1,但是这两个对象其实已经无法被程序所访问到了,所以已经是垃圾了

可达性分析算法(Root Searching)

GC定义了一些根(Roots) 从这些根对象开始搜索,能被搜索到的引用对象就不是垃圾,反之这视作垃圾。

GC Roots:虚拟机栈(栈帧中的局部变量表所引用的对象),方法区中静态属性引用的对象(即static修饰的一些变量),方法区中常量引用的对象,本地方法引用的对象(Native方法),存活的线程中的对象等

三、垃圾回收算法

标记清除算法

首先遍历内存中所有的对象,标记垃圾对象,第二次遍历时将垃圾清除。

缺点:

  1. 需要遍历两次
  2. 内存被切割的很分散,可能会造成有足够的空间,但是无法为后续的大对象分配空间

优点:

  1. 算法容易理解,实现简单
  2. 垃圾较少时,效率非常高

拷贝算法

将内存分为两部分,给对象分配空间分配在其中一部分,GC时将存活对象拷贝到另外一部分,之前一部分全部回收

缺点:

  1. 可使用内存空间减少
  2. GC时需要拷贝对象,并且调整存活对象的引用

优点:

  1. 不会产生内存碎片
  2. 只扫描一次,效率高,尤其在垃圾多时

标记整理算法

先扫描一遍内存,标记垃圾对象,回收时,先将垃圾回收,然后将存活对象向内存一端移动

缺点:

  1. 需要扫描两次
  2. 需要移动对象,并且调整存活对象的引用

优点

  1. 不会产生内存碎片
  2. 不会减少可用内存空间

并发标记中错标情况

1:并发标记过程本来标记为垃圾的对象没有被标记,产生浮动垃圾,影响较小,下一次GC时清除即可

2:并发标记过程中本该存活的对象被标记为垃圾,对象被错误的回收,产生程序错误

三色标记

白色:对象尚未被垃圾收集器访问过,可达性分析阶段刚开始所有对象都是白色,结束仍然为白色则标记为垃圾
黑色:已经访问且所有引用都被访问
灰色:本身被访问但是存在至少一个引用未被访问

对象消失出现满足条件,两条同时满足
1:存在一条或多条黑色对象到白色对象的引用
2:所有灰色对象到该白色对象的引用都被删除了

解决办法

1:增量更新:并发标记过程中,出现的新的黑色对象对白色对象的引用记录下来,标记结束之后,再次对这些记录的黑色对象再次扫描
2:原始快照:并发标记过程中,灰色对象删除了对白色对象的引用时,将其记录下来,标记结束之后,,再次对这些记录的灰色对象再次扫描

四、内存分代模型

一种内存管理模型,不同内存区域运用不同的GC算法,提供内存利用率和回收效率。内存模型如下图:
在这里插入图片描述

JVM将内存中堆分为新生代和老年代

默认比例是1:2,我们也可以自己设置这个比例(通过 -Xms 初始化堆的大小,通过 -Xmx 设置堆最大分配的内存大小,通过 -Xmn 设置新生代的内存大小)

新生代又分为一个eden和两个suivivor
(eden和suivivor的默认比例是 8:1:1,通过 -XX:SurvivorRatio 可以自定义此比例)。

对象存活时间较长则放在老年代,反之则放在新生代,通过对象头设置的对象年龄来确定(最大为15,因为只预留了四位),

对象被new时首先会分配在eden,经历GC还存活就会移动到suivivor区中一个,后面每次GC都存活的话,会在两个suivivor区来回移动,每次GC,对象如果存活,年龄+1。当到达一定年龄则会转移到老年区。

新生代转移到老年代的年龄根据垃圾回收器的类型而有所不同,CMS(Concurrent Mark Sweep,一种垃圾回收器) 设置的默认年龄是 6,其他的垃圾回收器默认年龄都是 15。这个年龄我们可以自己设置(通过参数-XX:MaxTenuringThreshold 配置)

新生代的 GC 被称之为 YGC(Young Garbage Collector,年轻代垃圾回收)或者 MinorGC(Minor Garbage Collector,次要垃圾回收)

一般采用拷贝算法,因为新生代中的对象每次GC都存活下来一小部分,所以每次清除时都是,预留两个suivivor中一个,将eden和另外一个suivivor存活对象移动到其中来,然后整个回收内存。

老年代的 GC 采用的是 标记清除 或者 标记整理,因为老年代的空间较大,所以老年代的 GC 并不像新生代那样频繁。

整个内存回收称之为 FGC(Full Garbage Collector,完整垃圾回收),或者 MajorGC (Major Garbage Collector,重要垃圾回收)。YGC/MinorGC 在新生代空间耗尽时触发。FGC/MajorGC 在老年代空间耗尽时触发,FGC/MajorGC 触发时,新生代和老年代会同时进行 GC。在 Java 程序中,也可以通过 System.gc() 来手动调用 FGC。

五、垃圾收集器

STW:Stop The World GC线程运行,停止所有用户线程。

垃圾收集器优化理念就是让STW时间尽可能短,或者实现与用户线程并发执行。

Serial收集器

新生代收集器
最基础,历史最悠久,单线程工作,适用于单核处理器或者核心数较少,管理的内存较少,对于运行在客户端模式下的虚拟机适用。

ParNew收集器

新生代收集器,实质上就是Serial的多线程版本

Parallel Scavenge收集器

新生代收集器,基于标记-复制算法
其他垃圾收集器关注的是尽可能缩短用户线程停顿时间,而此垃圾收集器关注的是达到一个可控制的吞吐量,即用户代码执行时间比上用户代码执行时间+GC时间

Serlal Old收集器

单线程工作,基于标记-整理算法,老年代收集器,适用于客户端模式下的虚拟机

Parallel Old

Parallel收集器的老年代版本,支持多线程收集

CMS收集器

基于标记-清除算法,适用于服务器端运行的虚拟机

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值