内存溢出和内存泄漏

  • 内存溢出(Out Of Memory)

    就是申请内存时,JVM没有足够的内存空间。通俗说法就是去蹲坑发现坑位满了。
  • 内存泄露 (Memory Leak)

    就是申请了内存,但是没有释放,导致内存空间浪费。通俗说法就是有人占着茅坑不拉屎。

垃圾定义

没有任何引用指向的一个对象或者多个对象(循环引用)。

如何定位垃圾

  • 引用计数(ReferenceCount)

    在对象头中分配一个空间来保存该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加一,如果删除对该对象的引用,那么它的引用计数就减一,当该对象的引用计数为0时,那么该对象就会被回收。

  • 根可达算法(RootSearching)

    通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

    • 在Java语言中,可以作为GCRoots的对象包括下面几种:

      • 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。
      • 方法区中的类静态属性引用的对象。
      • 方法区中常量引用的对象。
      • 本地方法栈中JNI(Native方法)引用的对象。

常见的垃圾回收算法

  • 标记清除(mark sweep)

    • 标记阶段:

      collector从mutator根对象开始进行遍历,对从mutator根对象可以访问到的对象都打上一个标识,一般是在对象的header中,将其记录为可达对象。
    • 清除阶段:

      collector对堆内存(heap memory)从头到尾进行线性的遍历,如果发现某个对象没有标记为可达对象-通过读取对象的header信息,则就将其回收。

collector: 垃圾收集器;

mutator: 指除了垃圾收集器之外的部分,比如说我们应用程序本身。

  • 拷贝算法 (copying)

    将内存平均分成A区、B区两块,进行复制+清除垃圾的操作.

    算法过程:

    • 新生对象被分配到A块中未使用的内存当中。当A块的内存用完了, 把A块的存活对象复制到B块。
    • 清理A块所有对象。
    • 新生对象被分配到B块中未使用的内存当中。当B块的内存用完了, 把B块的存活对象复制到A块。
    • 清理B块所有对象。
    • 循环1。
  • 标记压缩(mark compact)

    分为两个阶段,一个是标记(mark),一个是压缩(compact). 其中标记阶段跟标记-清除算法中的标记阶段是一样的。

    • 压缩阶段

      它的工作就是移动所有的可达对象到堆内存的同一个区域中,使他们紧凑的排列在一起,从而将所有非可达对象释放出来的空闲内存都集中在一起,通过这样的方式来达到减少内存碎片的目的。
    • 移动对象时的顺序,一般分为下面三种:
      • 任意顺序 - 即不考虑原先对象的排列顺序,也不考虑对象间的引用关系,随意的移动可达对象,这样可能会有内存访问的局部性问题。
      • 线性顺序 - 在重新排列对象时,会考虑对象间的引用关系,比如A对象引用了B对象,那么就会尽可能的将A,B对象排列在一起。
      • 滑动顺序 - 顾名思义,就是在重新排列对象时,将对象按照原先堆内存中的排列顺序滑动到堆的一端。

TLAB

新对象都是在Eden区分配空间,这块空间是在多线程间共享的。那么考虑一下,多线程是可能同时创建新对象的,这时候必然需要一种同步机制。使用队列?或者通过互斥?这些方式确实都可以。不过,我们还有一种更好的方式,TLAB,它全称是thread local allocation buffer,这是eden区里的一块空间。每个线程都有它自己的tlab,因此,只要对象分配是发生在tlab里面,这时候就不需要进行同步控制。

其实分配的时候,是让每个线程拥有了私有的分配指针,实际的对象存储的地方,依然是公共可访问的。

参考 https://blog.csdn.net/shuipinglp/article/details/90240377

常见的垃圾回收器

  • 新生代收集器

    Serial、ParNew、Parallel Scavenge。
  • 老年代收集器

    Serial Old、CMS、Parallel Old
  • 堆内存垃圾收集器(即物理内存上不分新生代和老年代)

    G1

新生代垃圾收集器

Serial 收集器

Serial 是一款用于新生代的单线程收集器,采用复制算法进行垃圾收集。Serial 进行垃圾收集时,不仅只用一条线程执行垃圾收集工作,它在收集的同时,所有的用户线程必须暂停(Stop The World)。

如下是 Serial 收集器和 Serial Old 收集器结合进行垃圾收集的示意图,当用户线程都执行到安全点时,所有线程暂停执行,Serial 收集器以单线程,采用复制算法进行垃圾收集工作,收集完之后,用户线程继续开始执行。

  • 图例

ParNew 收集器

ParNew 就是一个 Serial 的多线程版本,其它与Serial并无区别。ParNew 在单核 CPU 环境并不会比 Serial 收集器达到更好的效果,它默认开启的收集线程数和 CPU 数量一致,可以通过 -XX:ParallelGCThreads 来设置垃圾收集的线程数。

如下是 ParNew 收集器和 Serial Old 收集器结合进行垃圾收集的示意图,当用户线程都执行到安全点时,所有线程暂停执行,ParNew 收集器以多线程,采用复制算法进行垃圾收集工作,收集完之后,用户线程继续开始执行。

  • 图例

Parallel Scavenge 收集器

Parallel Scavenge 也是一款用于新生代的多线程收集器,与 ParNew 的不同之处是ParNew 的目标是尽可能缩短垃圾收集时用户线程的停顿时间,Parallel Scavenge 的目标是达到一个可控制的吞吐量。

吞吐量就是 CPU 执行用户线程的的时间与 CPU 执行总时间的比值【吞吐量 = 运行用户代代码时间/(运行用户代码时间+垃圾收集时间)】,比如虚拟机一共运行了 100 分钟,其中垃圾收集花费了 1 分钟,那吞吐量就是99%。

如下是 Parallel Scavenge 收集器和 Parallel Old 收集器结合进行垃圾收集的示意图(这也是 JDK1.8 默认的方式):

老年代垃圾收集器

Serial Old 收集器

Serial Old 收集器是 Serial 的老年代版本,同样是一个单线程收集器,采用标记-整理算法。

如下图是 Serial 收集器和 Serial Old 收集器结合进行垃圾收集的示意图:

CMS(Concurrent Mark Sweep) 收集器

CMS收集器是一种以最短回收停顿时间为目标的收集器,以"最短用户线程停顿时间"著称。整个垃圾收集过程分为4个步骤:

① 初始标记:标记一下 GC Roots 能直接关联到的对象,速度较快。

② 并发标记:进行 GC Roots Tracing,标记出全部的垃圾对象,耗时较长。

③ 重新标记:修正并发标记阶段引用户程序继续运行而导致变化的对象的标记记录,耗时较短。

④ 并发清除:用标记-清除算法清除垃圾对象,耗时较长。

整个过程耗时最长的并发标记和并发清除都是和用户线程一起工作,所以从总体上来说,CMS 收集器垃圾收集可以看做是和用户线程并发执行的。

  • 图例

  • CMS收集器存在的一些缺点

    对CPU资源敏感:默认分配的垃圾收集线程数为(CPU 数+3)/4,随着CPU数量下降,占用CPU资源越多,吞吐量越小;无法处理浮动垃圾(在并发清理阶段,用户线程还在运行时产生的垃圾)

  • 适用场景

    重视服务器响应速度,要求系统停顿时间最短。可以使用 -XX:+UserConMarkSweepGC 来选择 CMS 作为老年代收集器。

Parallel Old 收集器

Parallel Old收集器是Parallel Scavenge的老年代版本,是一个多线程收集器,采用标记-整理算法。可以与Parallel Scavenge收集器搭配,可以充分利用多核 CPU 的计算能力。

  • 图例

  • 适用场景

    与Parallel Scavenge 收集器搭配使用;注重吞吐量。jdk7、jdk8 默认使用该收集器作为老年代收集器

G1

https://www.cnblogs.com/xiaofengshan/p/12817608.html

对象在内存分代中如何流转

年轻代

大部分对象刚创建的时候都会分配在年轻代的Eden区,如果内存不够,部分对象进入老年代,部分对象继续被分配到Survivor区,当年轻代空间不够就会触发MinorGC(YGC,只回收年轻代内存)。

  • MinorGC步骤

    • YGC回收之后,大多数的对象会被回收,活着的进入s0
    • 再次YGC,活着的对象eden + s0 -> s1
    • 再次YGC,eden + s1 -> s0
    • 年龄足够 -> 老年代 (15 CMS 6)
    • s 区装不下 -> 老年代

老年代

老年代的对象都是从年轻代根据一定的规则流转过来的,当OldGen区内存不够,则会进行FullGC。

  • 老年代的对象流转规则

    • 超过指定年龄(参数-XX:MaxTenuringThreshold 配置,默认15)的没有被垃圾回收,流转到老年代。
    • 大对象直接进入,超过参数指定字节数(-XX:PretenureSizeThreshold)设置的字节数的大对象会直接进入老年代,这是因为对象越大,复制开销就越大。
    • 动态年龄判断规则进入,意思是不一定要到指定年龄再流转到15,如果某一年龄以上的对象到达一定大小,也会提前进入老年代。当躲过一轮GC的对象加起来超过surrvivor区50%,如年龄1+年龄2+年龄n一直累加,直到年龄n的时候发现加起来超过了surrvivor空间的50%,则年龄n以上的对象直接进入老年代。
    • minorGC发生时,suprivor区放不下,超过的部分转移到老年代。

GC常用参数

  • -Xmn -Xms -Xmx -Xss

    年轻代 最小堆 最大堆 栈空间
  • -XX:+UseTLAB

    使用TLAB,默认打开
  • -XX:+PrintTLAB

    打印TLAB的使用情况
  • -XX:TLABSize

    设置TLAB大小
  • -XX:+DisableExplictGC

    System.gc()不管用 ,FGC
  • -XX:+PrintGC

    打印GC简要信息
  • -XX:+PrintGCDetails

    打印GC详细信息
  • -XX:+PrintHeapAtGC

    打印GC前后堆的概况
  • -XX:+PrintGCTimeStamps

    打印CG发生的时间戳
  • -XX:+PrintGCApplicationConcurrentTime (低)

    打印应用程序时间
  • -XX:+PrintGCApplicationStoppedTime (低)

    打印暂停时长
  • -XX:+PrintReferenceGC (重要性低)

    记录回收了多少种不同引用类型的引用
  • -verbose:class

    类加载详细过程
  • -XX:+PrintVMOptions
  • -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial

    必须会用
  • -Xloggc:opt/log/gc.log
  • -XX:MaxTenuringThreshold

    升代年龄,最大值15
  • 锁自旋次数 -XX:PreBlockSpin 热点代码检测参数-XX:CompileThreshold 逃逸分析 标量替换 ...

    这些不建议设置

Parallel常用参数

  • -XX:SurvivorRatio
  • -XX:PreTenureSizeThreshold

    大对象到底多大
  • -XX:MaxTenuringThreshold
  • -XX:+ParallelGCThreads

    并行收集器的线程数,同样适用于CMS,一般设为和CPU核数相同
  • -XX:+UseAdaptiveSizePolicy

    自动选择各区大小比例

CMS常用参数

  • -XX:+UseConcMarkSweepGC
  • -XX:ParallelCMSThreads

    CMS线程数量
  • -XX:CMSInitiatingOccupancyFraction

    使用多少比例的老年代后开始CMS收集,默认是68%(近似值),如果频繁发生SerialOld卡顿,应该调小,(频繁CMS回收)
  • -XX:+UseCMSCompactAtFullCollection

    在FGC时进行压缩
  • -XX:CMSFullGCsBeforeCompaction

    多少次FGC之后进行压缩
  • -XX:+CMSClassUnloadingEnabled
  • -XX:CMSInitiatingPermOccupancyFraction

    达到什么比例时进行Perm回收
  • GCTimeRatio

    设置GC时间占用程序运行时间的百分比
  • -XX:MaxGCPauseMillis

    停顿时间,是一个建议时间,GC会尝试用各种手段达到这个时间,比如减小年轻代

参考

https://blog.csdn.net/chuobenggu7592/article/details/100978957

https://blog.csdn.net/weixin_38106322/article/details/109122814

https://blog.csdn.net/weixin_43228814/article/details/88934939

GC基础知识的更多相关文章

  1. 基础知识漫谈(2):从设计UI框架开始

    说UI能延展出一丢丢的东西来,光java就有swing,swt/jface乃至javafx等等UI toolkit,在桌面上它们甚至都不是主流,在web端又有canvas.svg等等. 基于这些UI工 ...

  2. JVM 基础知识

    JVM 基础知识(GC) 2013-12-10 00:16 3190人阅读 评论(1) 收藏 举报 分类: Java(49) 目录(?)[+] 几年前写过一篇关于JVM调优的文章,前段时间拿出来看了看 ...

  3. css+js+html基础知识总结

    css+js+html基础知识总结 一.CSS相关 1.css的盒子模型:IE盒子模型.标准W3C盒子模型: 2.CSS优先级机制: 选择器的优先权:!important>style(内联样式) ...

  4. JAVA面试题集之基础知识

                           JAVA面试题集之基础知识 基础知识:  1.C 或Java中的异常处理机制的简单原理和应用. 当JAVA程序违反了JAVA的语义规则时,JAVA虚拟机就 ...

  5. JVM基础知识(1)-JVM内存区域与内存溢出

    JVM基础知识(1)-JVM内存区域与内存溢出 0. 目录 什么是JVM 运行时数据区域 HotSpot虚拟机对象探秘 OutOfMemoryError异常 1. 什么是JVM 1.1. 什么是JVM ...

  6. Java 基础知识总结

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.数据类型:  数据类型:1>.基本数据类型:1).数值型: 1}.整型类型(byte  8位   (by ...

  7. Java基础知识二次学习--第三章 面向对象

    第三章 面向对象   时间:2017年4月24日17:51:37~2017年4月25日13:52:34 章节:03章_01节 03章_02节 视频长度:30:11 + 21:44 内容:面向对象设计思 ...

  8. 【RAC】RAC相关基础知识

    [RAC]RAC相关基础知识 1.CRS简介    从Oracle 10G开始,oracle引进一套完整的集群管理解决方案—-Cluster-Ready Services,它包括集群连通性.消息和锁. ...

  9. java基础知识-笔记整理

    1.查看已安装jdk文件路径 CMD输入java -verbose.   2.java学习提升路线 java学习视屏地址: http://www.icoolxue.com/album/show/38 ...

随机推荐

  1. wiodows /linux CMD

    windows : netstat -ano      查看所有端口使用情况 netstat -ano |findstr "端口号"     查看特定端口号 tasklist |f ...

  2. Redis 的 3 种集群方案对比

    数据持久化 主从复制 自动故障恢复 集群化 数据持久化本质上是为了做数据备份,有了数据持久化,当Redis宕机时,我们可以把数据从磁盘上恢复回来,但在数据恢复之前,服务是不可用的,而且数据恢复的时间取 ...

  3. element 表格只展开一行(点击下一行上一行关闭)

    源码:第一步 <el-table :data="tableData" border :row-class-name="tableRowClassName" ...

  4. TKE 用户故事 | 作业帮 Kubernetes 原生调度器优化实践

    作者 吕亚霖,2019年加入作业帮,作业帮架构研发负责人,在作业帮期间主导了云原生架构演进.推动实施容器化改造.服务治理.GO微服务框架.DevOps的落地实践. 简介 调度系统的本质是为计算服务/任 ...

  5. CSS基础 装饰 元素本身隐藏和显示效果及案例

    1.visibility:hidden; 2.display: none: 区别: 1.visibility:hidden 隐藏元素本身,且在网页中 占位置 2.display:none; 隐藏元素本 ...

  6. for update未提交导致锁表

    select for update 是为了在查询时,避免其他用户以该表进行插入,修改或删除等操作,造成表的不一致性. 应用场景: 什么时候需要使用for update?就是那些需要业务层面数据独占时, ...

  7. django 项目中使用markdown编辑器

     第一步: 修改models.py文件下要显示字段的类型为TextField 第二步:运行命令: python manage.py makemigrations 和 python manage.py ...

  8. List<FieldModelBase> 转 DataTable

    // List<FieldModelBase> 转 DataTable private DataTable ListToDataTable(List<FieldModelBase&g ...

  9. Java数据类型 long 与 Long 的区别 和 正确用法

    1.区别 (1) long  是 基本类型  [类似于 int] Long 是 对象类型  [类似于Integer] (2) long 默认值是 0 Long 默认值是 null 2.比较方法 (1) ...

  10. JSR-303规范

    规范链接 CONSTRAINT 详细信息 @Valid 被注释的元素是一个对象,需要检查此对象的所有字段值 @Null 被注释的元素必须为 null @NotNull 被注释的元素必须不为 null ...