垃圾回收

标签(空格分隔): Java


那些对象需要回收

JVM内存结构主要包括: 方法区, 堆区, 程序计数器, 本地方法区, 虚拟机栈. 其中的程序计数器, 本地方法区, 虚拟机栈这三个区域是的生命周期随线程生灭, 所以不需要过多考虑这方面的GC问题.


引用

在JDK1.2之后, Java对引用的概念进行了补充, 总体分为四类: 强引用, 软引用, 弱引用, 虚引用. 这四种引用强度逐渐减弱.

  • 强引用 : 指在代码中普遍存在的, 类似 Object object = new Object();这类的引用, 只有强引用还存在, GC就永远不会收集被引用的对象.
  • 软引用 : 指一些还有用但并且非必须的对象. 直到内存空间不够时(抛出OutofMemoryError之前), 才会被垃圾回收. 采用SoftReference类来实现软引用.
  • 弱引用 : 用来描述非必须对象. 当垃圾收集器工作时就会回收掉此类对象. 采用WeakReference类来实现弱引用.
  • 虚引用 : 一个对象是否有虚引用的存在, 完全不会对其生存空间构成影响, 唯一目的就是能在这个对象被回收的时候收到一个系统通知, 采用PhantomRenference类实现.

如何判断对象已死


1. 引用计数法

为每一个对象的的数据结构添加一个引用计数器, 用于统计指向该对象的引用的数量, 每当多一个引用的时候, 其引用计数器就加一, 当引用不再指向该地址之后计数器减一. 一旦计数器的值为0,则表示没有引用指向该对象, 则对象已经死亡 ( <<寻梦环游记>> 说的: 人真正的死亡是所有活着的人的都忘了你的时候).

优点:

  1. 效率很高.

缺点:

  1. 每个对象都需要在其原有的数据结构之上添加一个引用计数器的属性.
  2. 循环引用的话就永远不会被标记为死亡.( 对象A引用了对象B, 对象B也引用了对象A )

2. 可达性分析.

目前JVM的主流垃圾回收器采取的可达性分析算法, 这个算法的实质在于将一系列GC Roots作为初始的存活对象的集合(Live Set), 然后从该集合出发, 探索所有能过被集合直接或间接引用的对象, 并且将其加入集合之中, 这个过程就是标记过程. 最终, 未被探索到的对象便是死亡, 是可以回收的.

什么是GC Roots, 其实就是由堆外指向堆内的引用.

  1. JMM虚拟机栈 栈帧中的局部变量.
  2. JMM方法区中的静态属性, 常量引用对象.
  3. JMM本地方法区中的, JNI中引用的对象.

可达性分析, 可以解决循环引用问题, 但是自身也存在一些问题, 比如说在多线程的情况下, 其他线程可能会更新已经访问过的对象的引用, 造成漏删. 解决方案是 进行两次可达性分析, 如果两次某对象都被标记则进行删除.


如何删除无效对象.


1. 标记清除算法

由名可得: 标记->清除. 得到需要清除的对象之后就直接进行清除.

优点: 速度快

缺点: 多次GC之后, 造成大量的碎片空间. 对于需要连续存储的较大对象无法存储, OutofMemoryError


2. 标记整理算法

由名可得: 标记->清除->移动整理. 对标记清除算法的一次改进, 但是因为移动操作, 所以时间成本较高.

优点: 没有内存碎片.

缺点: 时间成本较高.


3. 复制算法.

将可用的内存按容量分为大小两块, 每次只是用其中一块, 当这一块的内存用完了, 就将还存活着的对象复制到另一块内存上, 然后再把已使用的内存空间清理掉.

每次当内存分配的时候空间不够的时候, 都进行复制算法进行内存整理.

优点: 实现简单, 效率高. 解决了标记清除算法导致的内存碎片问题.

缺点: 代价太大, 将内存缩小了一半. 效率随对象的存活率升高而降低.

3.1 HotSpot虚拟机的改良算法

  1. 弱代理论
  • 分代垃圾收集基于弱代理论, 具体描述如下:
  • 大多分配了内存的对象并不会存活太长时间, 在处于年轻时代的时候就会死掉.
  • 很少有对象会从老年代变为年轻代.
  • 其中IBM研究表明: 新生代中的98%的对象都是 朝生夕死 ; 所以不需要按照1:1比例来划分内存.
  1. Hotspot虚拟机新生代内存布局以及算法.
  • 新生代内存分配一块较大的Eden和两块较小的Survivor空间.
  • 每次使用Eden和其中Survivor空间.
  • 回收时将EdenSurvivor空间中存活的对象一次性的复制到另一块Survivor空间上.
  • 最后清理掉Eden和使用过的Survivor空间.

Hotspot默认EdenSurvivor的大小比例是8:1 .

分配担保:

如果另一块Survivor空间没有足够的内存来存放上一次新生代收集下来的存活对象, 那么这些对象则直接通过担保机制进入老年代.


4. 分代收集算法

当前商业虚拟机的垃圾收集器都是采用分代收集算法, 根据对象存活周期的不同将内存划分为几块. 一般把Java堆分为新生代, 老年代. JVM根据各个年代的特点采用不同的手机算法.

  • 新生代中, 每次进行垃圾回收都会发现大量对象死去, 只有少量存活, 因此比较适合复制算法. 只需要付出少量的存活对象的复制成本就可以完成收集.
  • 老年代中, 因为对象的存活率比较高, 没有额外的空间进行分配担保, 所以比较适合标记-清理, 标记-整理算法来进行回收.

Java堆是JVM所管理的内存的最大的一块, 也是GC主要的工作区. 其主要分为两个区 年轻代老年代, 其中年轻代又分为EdenSurvivor区, 其中Survivor区又分为FROMTo两个区. 可能这个时候大家又会有疑问, 为什么需要Survivor区, 为什么Survivor还要分为两个区?

Eden区 : IBM表示有98%的对象是朝生夕死, 所以针对这一现状, 大多数情况下, 对象会在新生代Eden区中进行分配, 当Eden区没有足够的空间进行分配的时候, 虚拟机会发起一次GC. 通过这次GC之后,Eden会被清空, Eden区中绝大部分的对象会被回收, 而那些无需回收的存货对象, 将会进到SurvivorFROM区(若FROM不够, 则直接进入Old区).

Survivor区: 相当于是Eden区和Old区的一个缓冲, 类似于我们交通中的黄灯. Survivor又分为两个区, 一个为From区, 一个是To区. 每次执行Minor GC 会将Eden区和FROM存活的对象放到SurvivorTo区(如果To则直接进入Old区).

不是新生代到老年代么, 直接中EdenOld不好了么,为什么要这么复杂. 如果没有Survivor区, Eden区每一次GC, 存活的对象就会被送到老年代, 老年代很快就会被填满, 而虽然有很多对象虽然一次没有被消灭掉,但是也存活不了太多次, 这个时候将其移入Old区会很快的将其填满.

所以Survivor的存在意义就是减少被送到老年代的对象, 进而减少GC的发生. Survivor的筛选保证, 只有经历16此的GC还能再新生代存活的对象,才会被送到老年代.

Old区占据着2/3的堆内存空间,只有在Marjor GC的时候才会进行清理, 每次GC都会出发stop-the-world. 内存越大, SWT的时间也越长, 所以内存也不仅仅是越大越好, 由于复制算法的对象存活率较高的老年代会进行很多次的复制操作, 效率很低, 所以老年代这里采用的是 标记整理算法.


除了上述所说, 在内存担保机制的情况下, 无法安置的对象也会直接进入老年代, 以下几种情况也会进入老年代.

  • 大对象: 指需要大量连续内存空间的对象, 这部分对象不论是不是"朝生夕死", 都会直接进入到老年代, 这样做主要是为了避免在Eden区以及两个Survivor区之间发生大量的内存复制, 当你的系统有非常多的"朝生夕死"的大对象的时候, 就值得注意了.
  • 长期存活对象: 虚拟机给每个对象定义了一个对象年龄(Age)计数器. 正常情况下的对象会不断的在SurvivorFROMTo之间进行移动, 对象在每经历一次Minor GC, 年龄就会自增1, 当年龄增加到15岁的时候, 就会被移入老年代. 这里的15是可以进行自定义的.
  • 动态对象年龄: 虚拟机并不重视对象的年龄必须大于15岁, 才会放入老年区, 如果Survivor空间中相同年龄所有对象的总和大于Survivor空间的一般, 年龄大于等于该年龄的对象就可以直接进入老年区.

Java-GC-标记清除算法的更多相关文章

  1. Java GC 标记/清除算法

    1) 标记/清除算法是怎么来的? 我们在程序运行期间如果想进行垃圾回收,就必须让GC线程与程序当中的线程互相配合,才能在不影响程序运行的前提下,顺利的将垃圾进行回收. 为了达到这个目的,标记/清除算法 ...

  2. 《垃圾回收的算法与实现》——GC标记-清除算法

    基本算法 标记-清除算法由 ==标记阶段== 和 ==清除阶段== 构成. 标记即将所有活动的对象打上标记. 清除即将那些没有标记的对象进行回收. 标记与清除 遍历GC root引用,递归标记(设置对 ...

  3. 1. GC标记-清除算法(Mark Sweep GC)

    世界上第一个GC算法,由 JohnMcCarthy 在1960年发布. 标记-清除算法由标记阶段和清除阶段构成. 标记阶段就是把所有的活动对象都做上标记的阶段. 标记阶段就是"遍历对象并标记 ...

  4. JVM内存管理------GC算法精解(五分钟让你彻底明白标记/清除算法)

    相信不少猿友看到标题就认为LZ是标题党了,不过既然您已经被LZ忽悠进来了,那就好好的享受一顿算法大餐吧.不过LZ丑话说前面哦,这篇文章应该能让各位彻底理解标记/清除算法,不过倘若各位猿友不能在五分钟内 ...

  5. GC算法精解(五分钟让你彻底明白标记/清除算法)

    GC算法精解(五分钟让你彻底明白标记/清除算法) 相信不少猿友看到标题就认为LZ是标题党了,不过既然您已经被LZ忽悠进来了,那就好好的享受一顿算法大餐吧.不过LZ丑话说前面哦,这篇文章应该能让各位彻底 ...

  6. JVM内存管理之GC算法精解(五分钟让你彻底明白标记/清除算法)

    相信不少猿友看到标题就认为LZ是标题党了,不过既然您已经被LZ忽悠进来了,那就好好的享受一顿算法大餐吧.不过LZ丑话说前面哦,这篇文章应该能让各位彻底理解标记/清除算法,不过倘若各位猿友不能在五分钟内 ...

  7. (转)jvm具体gc算法介绍标记整理--标记清除算法

    转自:https://www.cnblogs.com/ityouknow/p/5614961.html GC算法 垃圾收集器 概述 垃圾收集 Garbage Collection 通常被称为“GC”, ...

  8. JVM之GC算法、垃圾收集算法——标记-清除算法、复制算法、标记-整理算法、分代收集算法

    标记-清除算法 此垃圾收集算法分为“标记”和“清除”两个阶段: 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记对象,它的标记过程前面已经说过——如何判断对象是否存活/死去 死去的对象就会 ...

  9. Java GC 垃圾回收算法 内存分配

    垃圾回收(Garbage Collection, GC)是Java不同于c与c++的重要特性之一. 他帮助Java自动清空堆中不再使用的对象. 由于不需要手动释放内存,程序员在编程中也可以减少犯错的机 ...

  10. JVM-GC算法(一)-标记清除算法

    首先,我们得知道根搜索算法,它可以解决我们应该回收哪些对象的问题,但是它显然还不能承担垃圾搜集的重任,因为我们在程序(程序也就是指我们运行在JVM上的JAVA程序)运行期间如果想进行垃圾回收,就必须让 ...

随机推荐

  1. Linux-3.14.12内存管理笔记【内存泄漏检测kmemleak示例】【转】

    本文转载自:http://blog.chinaunix.net/uid-26859697-id-5758037.html 分析完kmemleak实现后,照常实验一下,以确定功能正常. 如kmemche ...

  2. npm使用淘宝镜像安装包

    npm使用registry这个属性指定仓库,因此配置这个属性即可.修改npm配置属性的几种方法详见官方文档. 这里只贴出修改registry的方法,以下三种任意一种即可: 修改~/.npmrc文件(没 ...

  3. Trustzone——利用硬件对数据加密,秘钥存在芯片里

    我是看 https://zhuanlan.zhihu.com/p/26441212 这个文章知道trustzone自身会存储秘钥,这个秘钥可以来自用户指纹,也可以来自云端下发的key. Truztzo ...

  4. C#多线程编程介绍——使用thread、threadpool、timer

    C#多线程编程介绍——使用thread.threadpool.timer 在system.threading 命名空间提供一些使得能进行多线程编程的类和接口,其中线程的创建有以下三种方法:thread ...

  5. 前端多媒体(4)—— video标签全面分析

    测试地址:https://young-cowboy.github.io/gallery/html5_video/index.html 属性 一些属性是只读的,一些属性是可以修改从而影响视频播放的. a ...

  6. linux命令学习笔记(33):df 命令

    linux中df命令的功能是用来检查linux服务器的文件系统的磁盘空间占用情况.可以利用该命令来获取硬盘被占用了 多少空间,目前还剩下多少空间等信息. .命令格式: df [选项] [文件] .命令 ...

  7. CentOS 6以下版本 支持Ext4

    CentOS默认是不支持Ext4.所以你需要处理一下才行. 使用环境使用的是CentOS5.8 内核是  2.6.18-238.19.1.el5 其实CentOS 5.8 里面是有 ext4 模块的, ...

  8. HihoCoder1656 : 前缀后缀查询([Offer收割]编程练习赛39)(字典树+小技巧)

    描述 给定一个包含N个单词的字典:{W1, W2, W3, ... WN},其中第i个单词Wi有具有一个权值Vi. 现在小Hi要进行M次查询,每次查询包含一个前缀字符串Pi和一个后缀字符串Si.他希望 ...

  9. dubbo的防痴呆设计

    项目中也经常会遇到各种因为配置而引入的问题,很多技术支持解决不掉就找开发,结果发现大部分还是配置错误或网络不通等.如果在设计之初就能考虑到并针对这些问题做出应对设计,甚至给出异常的解决方案,确实可以减 ...

  10. Jenkins持续集成环境搭建

    1部署Jenkins Jenkins部署很简单,只需建立一个新应用,将Jenkins的war包部署进去,再分配好权限就可以了. 1.1创建应用 建立一个新Nginx应用:jenkins.com 1.2 ...