垃圾收集器与内存分配策略

前言:本文基于《深入java虚拟机》再加上个人的理解以及其他相关资料,对内容进行整理浓缩总结。本文中的图来自网络,感谢图的作者。如果有不正确的地方,欢迎指出。

目录

回顾

  • 上文介绍了jvm的内存区域以及介绍了内存的溢出情况。
  • jvm区域分为5个,线程独有:虚拟机栈,本地方法栈,程序计数器。线程共享:方法区,堆
  • 两种溢出:栈溢出(StackOverflowError),OutOfMemoryError(OOM)

为什么学习垃圾收集

  • 看起来jvm好像一切帮你做好,但是当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对这种自动化的技术进行监控和调节。
  • 根据实际应用需求,选择最优的收集方式才能更高的性能。

垃圾收集的区域

  • 虚拟机栈,本地方法栈,程序计数器是线程私有的,和线程同生共死,当线程销毁时,内存自然回收,所以这部分不是考虑的重点。
  • 所以研究重点应该在方法区和堆,而方法区的回收效率较低,重点在堆。

确定回收对象

引用计数

  • 对象有一个引用计数器 new String()
  • 有引用,计数器加一,String jiajun=new String()
  • 引用失效,计数器减一,jiajun=null;
  • 出现一个问题
  1. Person jia=new Persoon();
  2. Person jun=new Person();
  3. jia.bro=jun;
  4. jun.bro=jia;
  5. jia=null;
  6. jun=null;
  • 此时虽然我们没有办法用jia和jun这两个对象,也就是这个是垃圾了,但是两个对象又互相引用,如果用这种算法是无法进行回收

可达性分析

  • GC Roots的对象:虚拟机栈中引用的对象(比如方法中定义的对象),本地方法栈中引用的对象,方法区中类静态属性引用的对象(static),方法区中常量应用的对象(final)
  • 通过GC Roots对象作为起点,当GC Roots到一个对象没有引用链的话,那么就证明这个而对象不可用

垃圾收集算法

标记清除

  • 确定回收对象,进行标记,然后回收标记的对象
  • 一个问题是标记清除效率不高
  • 一个问题是标记清除后产生大量不连续的内存碎片,但需要分配较大对象的时候,因为没有足够大的连续空间分配给此对象,此时会触发另一次垃圾收集

复制

  • 解决内存碎片的问题
  • 将内存分为等大的A区和B区,创建对象时,存放于A区,当A区空间用完之后,将A区存活的对象复制到B区,然后A区清空,这样的话,就避免了内存碎片的问题
  • 但是又有一个问题,这个时候我们只用到了一半的空间,所以我们要向办法提高空间利用率
  • 研究发现,新生代对象大多都是朝生夕死,也就是存活的对象不多,那么我们就没必要分配一半的空间用于“粘贴”
  • 所以虚拟机将新生代分为Eden和Surivior两个区,比例为8:1:1,可用的内存为一个伊甸区个一个存活区,一个存活区用于“粘贴,”,也就是新生代可用内存空间为容量的90%,这样的话就提高了空间了利用率
  • 但是又有一个问题,如果这一个存活区存放不了复制的的对象,那么怎么办?于是,如果这些对象放不下,将直接进入另一块区域老年代

标记整理

  • 对于朝生夕死的新生代来说,复制算法是不错的选择。但是对于存活率高的对象不是很好的选择,因为要进行较多的复制操作,效率会降低
  • 过程:确定回收对象,进行标记,让存活的对象向一端移动,然后清理掉端边界以外的内存
  • 那么这样的话,不会有内存碎片的问题,也没有复制过多效率降低的问题

分代收集

  • 上面几种方法的综合利用
  • 根据新生代和老年代的特点,分别选用适用的算法。
  • 新生代存活率,那么可以选择复制算法
  • 老年代存活率高,可以采用标记清理或标记整理算法

垃圾收集器

Stop the world(STW)

  • 意思指GC时,停顿所有java执行线程
  • 因为在进行可达性分析时候,如果同时对象引用进行变化,那么这样可达性分析就不正确。这是stop the world 的重要原因

Serial收集器

  • 用一条线程去完成垃圾收集工作
  • 垃圾收集时,需要暂停其他工作线程,显然这是不好,所以缩短线程停顿的时间是一个研究重点
  • 采用复制算法,用于新生代

Serial Old收集器

  • 与Serial收集器类似
  • 采用标记整理算法,用于老年代

ParNew收集器

  • 用多条线程去完成收集工作
  • 垃圾收集时,需要暂停其他工作线程
  • 默认开启的收集线程数和cpu数量一样,ParallelGCThreads参数可以用来设置线程数
  • 用于新生代,复制算法

Parallel Scavenge收集器

  • 新生代收集器,使用复制算法
  • 关注吞吐量,吞吐量=代码运行时间/(代码运行时间+垃圾收集时间),也就是高效率利用cpu时间,尽快完成程序的运算任务
  • MaxGCPauseMillis参数设置最大停顿时间,GCTimeRatio设置吞吐量大小

Parllel Old收集器

  • Parallel Scavenge的老年代版
  • 使用标记整理算法

CMS收集器

  • 用于老年代
  • 关注停顿时间,希望获取最短回收停顿时间
  • 基于标记清除算法,会产生内存碎片
  • 总体来说和用户线程一起并发执行
  • 对cpu资源敏感,也就是会占用较多cpu资源
  • 无法处理浮动垃圾,由于是和用户线程并发执行,所以并发时用户线程产生的的新垃圾(浮动垃圾)无法收集。

G1收集器

  • 使用多个cpu来缩短STW(stop the world )的停顿时间
  • 分代收集
  • 不会产生内存空间碎片
  • 可以建立可预测的停顿时间模型,能够让使用者指定M毫秒时间段内,垃圾收集的时间不超过N毫秒

流行的收集器组合

新生代 老年代
Serial Serial Old
Serial CMS
ParNew CMS
ParNew Serial Old
Parallel Scavenge Serial Old
Parallel Scavenge Parallel Old
G1 G1

垃圾收集参数总结

参数 描述
UseSerialGC 虚拟机运行在Client 模式下的默认值,打开此开关后,使用Serial +Serial Old 的收集器组合进行内存回收
UseParNewGC 打开此开关后,使用ParNew + Serial Old 的收集器组合进行内存回收
UseConcMarkSweepGC 打开此开关后,使用ParNew + CMS + Serial Old 的收集器组合进行内存回收。Serial Old 收集器将作为CMS 收集器出现Concurrent Mode Failure失败后的后备收集器使用
UseParallelGC 虚拟机运行在Server 模式下的默认值,打开此开关后,使用ParallelScavenge + Serial Old(PS MarkSweep)的收集器组合进行内存回收
UseParallelOldGC 打开此开关后,使用Parallel Scavenge + Parallel Old 的收集器组合进行内存回收
SurvivorRatio 新生代中Eden 区域与Survivor 区域的容量比值, 默认为8, 代表Eden :Survivor=8∶1
PretenureSizeThreshold 直接晋升到老年代的对象大小,设置这个参数后,大于这个参数的对象将直接在老年代分配
MaxTenuringThreshold 晋升到老年代的对象年龄。每个对象在坚持过一次Minor GC 之后,年龄就加1,当超过这个参数值时就进入老年代
UseAdaptiveSizePolicy 动态调整Java 堆中各个区域的大小以及进入老年代的年龄
HandlePromotionFailure 是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个Eden 和Survivor 区的所有对象都存活的极端情况
ParallelGCThreads 设置并行GC 时进行内存回收的线程数
GCTimeRatio GC 时间占总时间的比率,默认值为99,即允许1% 的GC 时间。仅在使用Parallel Scavenge 收集器时生效
MaxGCPauseMillis 设置GC 的最大停顿时间。仅在使用Parallel Scavenge 收集器时生效
CMSInitiatingOccupancyFraction 设置CMS 收集器在老年代空间被使用多少后触发垃圾收集。默认值为68%,仅在使用CMS 收集器时生效
UseCMSCompactAtFullCollection 设置CMS 收集器在完成垃圾收集后是否要进行一次内存碎片整理。仅在使用CMS 收集器时生效
CMSFullGCsBeforeCompaction 设置CMS 收集器在进行若干次垃圾收集后再启动一次内存碎片整理。仅在使用CMS 收集器时生效

内存分配与回收策略

GC种类

  • Minor GC,新生代GC,回收速度快
  • Major GC/ Full GC , 收集整个堆,包括yong gen,old gen,perm gen,回收速度慢

对象优先分配在新生代中的Eden

  • 大多数情况下,对象分配在Eden区,如果Eden区空间不够,将发起一次MinorGC

大对象直接进入老年代

  • 大对象指需要大量连续内存空间的对象
  • 经常出现大对象,由于需要连续的空间,容易导致内存还有不少空间就提前触发垃圾收集
  • 通过PretenureSizeThreshold参数设置限制,超过这个限制的对象直接分配在老年代

长时间存活的对象将进入老年代

  • 每个对象有一个对象年龄计数器,经过第一次MinorGC进入存活区,年龄为一,以后经过MinorGC还能继续在存活区的话,年龄加一,当通过限制(MaxTenuringThreshold)后会晋升到老年代

动态对象年龄判定

  • 存活区空间相同年龄所有对象大小的总和大于存活区的空间一半,大于等于该年龄的可以直接进入老年代

空间分配担保

  • Minor GC之前,虚拟机检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果可以,可以安全进行
  • 如果不行,看是否允许担保失败,不允许的话进行Full GC
  • 允许的话,看老年代的最大可用连续空间是否大于历次晋升老年代对象的平均大小(将之前的作为经验值),如果大于,进行MinorGC,否则进行FullGC

方法区回收

回收内容

  • 废弃常量
  • 无用的类
  • 回收的效率不高

废弃常量

  • 没有任何地方引用常量池的字符串常量,必要的话,这个字符串会被清除常量池。常量池中的类 方法 字段的符号引用也是类似

无用的类

  • 不存在该类的任何对象
  • 加载该类的ClassLoader已经被回收
  • 该类的对应的Class对象没有被引用,无法在任何地方通过反射访问该类的方法

gc触发条件

ygc

  • 当eden空间不足的时候会触发ygc

full gc

  • 创建大对象时,eden区空间不足,会尝试分配到老年代,如果老年代空间也不足就会触发full gc
  • 触发yong gc的时候,会检查老年代的最大连续空间是否大于新生代所有对象总空间,如果大于的话,说明这次yong gc是安全的。如果小于的话,看是否允许担保,不允许担保的话,那么进行full gc。如果允许担保的话,那么参考一下历次晋升到老年代的对象的平均大小,如果老年代连续空间大于这个参考值,那么尝试进行ygc。如果小于这个参考值的话,没办法只能进行full gc。
  • 当系统要加载的类,反射的类和调用的方法较多,并且永久代空间不足的话,进行full gc
  • 当to区的空间不足的时候,会把对象复制到老年代,如果老年代空间不足会进行full gc

我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引发自己内心的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)

作者:jiajun 出处: http://www.cnblogs.com/-new/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。

jvm系列 (二) ---垃圾收集器与内存分配策略的更多相关文章

  1. JVM性能优化系列-(2) 垃圾收集器与内存分配策略

    2. 垃圾收集器与内存分配策略 垃圾收集(Garbage Collection, GC)是JVM实现里非常重要的一环,JVM成熟的内存动态分配与回收技术使Java(当然还有其他运行在JVM上的语言,如 ...

  2. 【JVM.2】垃圾收集器与内存分配策略

    垃圾收集器需要完成的3件事情: 哪些内存需要回收? 什么时候回收? 如何回收? 在前一节中介绍了java内存运行时区域的各个部分,其中程序计数器.虚拟机栈.本地方法栈3个区域随线程而生,随线程而灭:栈 ...

  3. java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...

  4. JVM学习笔记-第三章-垃圾收集器与内存分配策略

    JVM学习笔记-第三章-垃圾收集器与内存分配策略 tips:对于3.4之前的章节可见博客:https://blog.csdn.net/sanhewuyang/article/details/95380 ...

  5. GC之一--GC 的算法分析、垃圾收集器、内存分配策略介绍

    一.概述 垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了. jvm 中,程序计数器.虚拟机栈.本 ...

  6. 《深入理解Java虚拟机》(三)垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 详解 3.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第三章 ,为了整理思路,简单记录一下,方便后期查阅. 3.2 对象已死吗 在垃圾收集器进行回收 ...

  7. Java虚拟机垃圾收集器与内存分配策略

    Java虚拟机垃圾收集器与内存分配策略 概述 那些内存须要回收,什么时候回收.怎样回收是GC须要完毕的3件事情. 程序计数器.虚拟机栈与本地方法栈这三个区域都是线程私有的,内存的分配与回收都具有确定性 ...

  8. 深入了解Java虚拟机(2)垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 由于JVM中对象的频繁操作是在堆中,所以主要回收的是堆内存,方法区中的回收也有,但是比较谨慎 一.对象死亡判断方法 1.引用计数法 就是如果对象被引用一次,就给计数器+1,否 ...

  9. 深入理解java虚拟机_第三章(上)----->垃圾收集器与内存分配策略

    1.  前言 这一版块内容比较多,分为两篇文章来做笔记.本文讲述上半部分垃圾收集部分;下一篇文章写内存分配部分. 概述 对象已死吗? 引用技术算法 可达性分析算法 再谈引用 两次标记 回收方法区 2. ...

随机推荐

  1. 浅谈js中的正则表达式

    很多时候多会被正则表达式搞的晕头转向,最近抽出时间对正则表达式进行了系统的学习,整理如下: 正则表达式的创建 两种方法,一种是直接写,由包含在斜杠之间的模式组成:另一种是调用RegExp对象的构造函数 ...

  2. 原生js实现Ajax的原理。

    Ajax(Asynchronous JavaScript and XML)表示异步的js与xml. 有别于传统web的同步开发方式. 原理:通过XMLHttpRequest对象向服务器发送异步请求,从 ...

  3. 第14章 Linux开机详细流程

    本文目录: 14.1 按下电源和bios阶段 14.2 MBR和各种bootloader阶段 14.2.1 boot loader 14.2.2 分区表 14.2.3 采用VBR/EBR方式引导操作系 ...

  4. workday2

    今天是实习的第二天 看了一天对smarty模板的介绍,进一步加深了对mvc框架的理解,但是对model认识还是非常的模糊的,可能是之前做的一些项目都是比较小的 对比laravel5,smarty模板显 ...

  5. URL和HTTP协议(无图片)

    URL...... 示例: http://localhost/phpwind/searcher.php?keyword=phpwind&type=thread 协议部分:一般是指URL中第一个 ...

  6. js实现类似iphone的秒表-添加平均数功能

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...

  7. 序列、视图、索引(面试看这个就GO了)

    oracle内置对象 序列.视图.索引 序列 create sequence aaa start with 1; 使用 视图 创建好之后 然后直接用 就OK了 有了视图可以代替子查询,使得sql简洁 ...

  8. nth-child 和nth-type的区别

    一.深呼吸,直接内容:nth-child和:nth-of-type都是CSS3中的伪类选择器,其作用近似却又不完全一样,对于不熟悉的人对其可能不是很区分,本文就将介绍两者的不同,以便于大家正确灵活使用 ...

  9. POWERSHELL将域中的计算机移动到指定OU

    POWERSHELL处理域中计算机的过程 由于集团公司规模较大,存在几个分公司并处在不同地理位置.采用域集中管理,各分公司都有自己的域控制器,分别负责各分公司的DNS解析,DHCP地址分配,及客户端登 ...

  10. cobbler简介+安装

    (介绍部分的内容部分是借鉴网上的非原创) 回顾pxe+kickstart PXE        PXE(preboot execute environment,预启动执行环境) PXE启动原理: 当计 ...