Java堆内存溢出的问题

  • 引言
  • 堆内存工作原理
  • 移除永久代?
  • 分代是什么?
  • 为什么分代?
  • 为什么Survivor分为两块相等大小的幸存空间?
  • JVM堆内存常用参数
  • 垃圾回收算法
  • 垃圾收集器
    • 串行收集器
    • 并行收集器
    • CMS收集器
    • G1收集器
  • 垃圾收集器参数
  • 为什么会堆内存溢出
    • OOM常见的几个原因
  • 总结

引言

现在做的这个项目后端代码用的是Java语言的,我们用的Tomcat来盛放Java代码。这段时间老是遇到Java堆内存溢出的问题。所以我就查了很多知识,赶紧过来先总结一下,分享交流一下。

堆内存工作原理

Java堆内存管理是影响性性能主要因素之一。堆内存溢出又是Java项目非常常见的故障。在解决问题之前,必须先了解Java堆内存的工作流程。

  • JVM内存划分分为堆内存和非堆内存。堆内存分为年轻代、老年代;非堆内存一个永久代。
  • 年轻代有分为Eden和Survivor区。Survivor区由FromSpace和ToSpace组成。Eden区占的容量大,Survivor两个区占的容量小。默认比例是8:1:1。
  • 堆内存的作用就是用来存放对象。垃圾收集器就是用来收集这些对象,然后根据GC算法回收。
  • 非对内存堆放的是永久代,也称为方法区,存储程序运行时长期存活的对象。

在JDK1.8版本废弃了永久代,替代的时元空间(MetaSpace),元空间与永久代类似,都是方法区的实现,他们最大的区别是:元空间并不在JVM中,而是使用本地内存。
元空间由注意有两个参数:

  • Metaspacesize:初始化元空间大小,控制发生GC阀值
  • MaxMetaspacesize:限制元空间大小上限。防止异常占用过多物理内存。

移除永久代?

移除永久代:
为了融合HotSpot JVM与JRockit VM而做出的改变,因为JRockit没有永久代。有了元空间就可以有效防止出现永久代OOM问题

分代是什么?

新生成的对象首先放到年轻代Eden区,当Eden空间满了,就会触发Minor GC,存活下来的对象移动到Survivor0区,Surviver0满了会再次触发Minor GC,Survivor区存货对象移动到Survivor1区,这样保证了一段时间总有一个Survivor区为空。经过多次Minor GC仍然存活的对象移动到老年代。
老年代存储长期存货的对象,占满时会触发Major GC=Full GC,GC期间会停止所有线程等待GC完成,所以对响应要求高的应用尽量减少发生Major GC,避免响应超时。
Minor GC:清理年轻代
Major GC:清理老年代
Full GC:清理整个堆空间,包括年轻代和永久代
所以GC都会停止应用所有线程

为什么分代?

将对象根据存活概率进行分类,堆存活时间长的对象,放到固定区,从而减少扫描垃圾时间及GC频率。针对分类进行不同的垃圾回收算法,对算法扬长避短。

为什么Survivor分为两块相等大小的幸存空间?

主要为了解决碎片化。如果内存碎片化严重,也就是两个对象占用不连续的内存,已有的连续内存不够新对象存放,就会主动触发GC

JVM堆内存常用参数

参数 描述
-Xms 堆内存初始大小,单位m、g
-Xmx(MaxHeapSize) 堆内存最大允许大小,一般不要大于物理内存的80%
-XX:PermSize 非堆内存初始大小,一般应用设置初始化200m,最大1024m就够了
-XX:MaxPermSize 非堆内存最大允许大小
-XX:NewSize(-Xns) 年轻代内存初始大小
-XX:MaxNewSize(-Xmn) 年轻代内存最大允许大小,也可以缩写
-XX:SurvivorRatio=8 年轻代中Eden区与Survivor区的容量比例值,默认为8,即8:1
-Xss 堆栈内存大小

垃圾回收算法

  • 标记-清除(Mark-Sweep)

GC分为两个阶段,标记和清除。首先标记所有可回收的对象,在标记完成后统一回收所有标记的对象,同时会产生连续的内存碎片化。碎片过多会导致以后程序运行时需要分配较大对象时,无法找到足够的连续内存,而不得再次触发GC

  • 复制

将内存按容量划分为两块,每次只使用其中一块。当这块内存用完了,就将存活的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。这样使得每次都是对半个内存区回收,也不用考虑内存碎片化问题,简单高效。缺点需要两倍的内存空间。

  • 标记-整理

也分为两个阶段,首先标记可回收的对象,再将存活的对象都向一端移动,然后清理掉边界以外的内存。此方法避免标记-清除算法的碎片化问题,同时也避免了复制算法的空间问题。
一般年轻代中执行GC后,会有少量的对象存活,就会选用复制算法,只要付出少量的存活对象复制成本就可以完成收集、而老年代中因为对象存活率高,没有额外过多的内存空间分配,就需要使用标记-清理活着标记-整理算法来进行回收。

垃圾收集器

串行收集器

比较老的收集器,单线程。收集时,必须暂停应用的工作线程,直到收集结束。

并行收集器

多条垃圾收集线程并行工作,在多核CPU下效率更高,应用线程仍然处于等待状态。

CMS收集器

CMS收集器是缩短暂停应用时间为目标而设计的,是基于标记-清除算法实现,整个过程分为4个步骤,包括:

  • 初始化标记
  • 并发标记
  • 重新标记
  • 并发清除

其中,初始标记、重新标记这两个步骤仍然需要暂停应用线程。初始标记只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段是标记可回收对象,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作导致标记产生变动的那一部分对象的标记记录,这个阶段暂停时间比初始标记阶段稍长一点,但远比并发标记时间段。
由于整个过程中消耗最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,CMS收集器内存回收与用户一起并发执行的,大大减少了暂停时间。

G1收集器

G1收集器将堆内存划分多个大小相等的独立区域(Region),并且能预测暂停时间,能预测原因它能避免对整个堆进行全区收集。G1跟踪各个Region里的垃圾堆积价值大小(所获得空间大小以及回收所需时间),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region,从而保证了再有限时间内获得更高的收集效率。
G1收集器工作工程分为4个步骤,包括:

  • 初始化标记
  • 并发标记
  • 最终标记
  • 筛选回收
    初始标记与CMS一样,标记一下GC Roots能直接关联到的对象。并发标记从GC Root开始标记存活对象,这个阶段耗时比较长,但也可以与应用线程并发执行。而最终标记也是为了修正在并发标记期间因用户程序继续运作而导致标记产生变化的那一部分标记记录。最后在筛选回收阶段对各个Region回收价值和成本进行排序,根据用户所期望的GC暂停时间来执行回收。

垃圾收集器参数

参数 描述
-XX:+UseSerialGC 串行收集器
-XX:+UseParallelGC 并行收集器
-XX:+UseParallelGCThreads=8 并行收集器线程数,同时有多少个线程进行垃圾回收,一般与CPU数量相等
-XX:+UseParallelOldGC 指定老年代为并行收集
-XX:+UseConcMarkSweepGC CMS收集器(并发收集器)
-XX:+UseCMSCompactAtFullCollection 开启内存空间压缩和整理,防止过多内存碎片
-XX:CMSFullGCsBeforeCompaction=0 表示多少次Full GC后开始压缩和整理,0表示每次Full GC后立即执行压缩和整理
-XX:CMSInitiatingOccupancyFraction=80% 表示老年代内存空间使用80%时开始执行CMS收集,防止过多的Full GC
-XX:+UseG1GC G1收集器
-XX:MaxTenuringThreshold=0 在年轻代经过几次GC后还存活,就进入老年代,0表示直接进入老年代

为什么会堆内存溢出

在年轻代中经过GC后还存活的对象会被复制到老年代中。当老年代空间不足,JVM会对老年代进行完全的垃圾回收Full GC。如果Full GC后还是无法存放从Survivor区复制过来的对象,就会出现OOM

OOM常见的几个原因

1)老年代内存不足:java.lang.OutOfMemoryError:Javaheapspace
2)永久代内存不足:java.lang.OutOfMemoryError:PermGenspace
3)代码bug,占用内存无法及时回收。

OOM在这几个内存区都有可能出现,实际遇到OOM时,能根据异常信息定位到哪个区的内存溢出。
可以通过添加个参数-XX:+HeapDumpOnOutMemoryError,让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后分析。

下面是对JAVA应用启动选项调优配置:

JAVA_OPTS="-server -Xms512m -Xmx2g -XX:+UseG1GC -XX:SurvivorRatio=6 -XX:MaxGCPauseMillis=400 -XX:G1ReservePercent=15 -XX:ParallelGCThreads=4 -XX:
ConcGCThreads=1 -XX:InitiatingHeapOccupancyPercent=40 -XX:+PrintGCDetails  -XX:+PrintGCTimeStamps -Xloggc:../logs/gc.log"
  • 设置堆内存最小和最大值,最大值参考历史利用率设置
  • 设置GC垃圾收集器为G1
  • 用GC日志,方便后期分析

总结

  • 选择高效的GC算法,可有效减少停止应用线程时间。
  • Full GC会增加暂停时间和CPU使用率,可以加大老年代空间大小降低Full GC,但会增加回收时间,根据业务适当取舍。

Java堆内存溢出解决方案相关推荐

  1. 垃圾回收算法与实现系列-Java堆内存溢出原因

    导语   内存一直是所有开发人员探索的一片天地,再JVM中,内存往往会被分为几块,了解不同的内存区域对编写出优质的代码有很大的帮助.堆内存作为JVM中比较重要的区域,有很多值得我们探索的地方.下面就来 ...

  2. 模拟JAVA堆内存溢出和栈内存溢出

    文章目录 1. 模拟堆内存溢出 2. 模拟栈内存溢出 1. 模拟堆内存溢出 为了更快的出现堆内存溢出,可以限制Java堆的大小为10MB(不限制也可以).代码如下(可直接复制使用): package ...

  3. java堆内存溢出的一般原因是什么_中软国际:Java堆内存溢出的本质是什么

    了解内存溢出错误的本质 事实证明,无论是什么情况,只要了解它的基本情况比如基本概念,解决起来相对得心应手些.如何去评估和了解一个内存溢出错误?最先做的事情应该是观察内存增长特征.根据情况做出可能性的评 ...

  4. Java堆内存溢出代码示例

    不断创建对象会导致堆内存溢出:

  5. matlab java堆内存溢出,matlab内存溢出的解决方案

    (1) 增加虚拟内存:cmd -> taskmgr 打开任务管理器,查看物理内存和虚拟内存,可观察matlab在运行过程中是否超过物理内存和虚拟内存.若超过,增加虚拟内存的方法是不可行的.物理内 ...

  6. Java堆内存溢出造成OS卡顿/服务中断的一种情况

    前提 top看内存情况 目标 测试内存临界情况下,内存溢出对已运行Java服务的影响 过程 1.制造麻烦 public static void main(String[] argv) {Thread. ...

  7. 记录一次大对象导致的Java堆内存溢出问题

    问题描述 前几天早上出现一后台项目无法登陆的情况,排查发现新生代和老年代都占用100%,FullGC次数大概有100多次,最终出现OOM. 重启Tomcat后,至13点,FullGC的次数达到31次. ...

  8. OutOfMemoryError/OOM/内存溢出异常实例分析--堆内存溢出

    Java堆内存溢出 只要不断创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象, 那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常,代码如下: import ...

  9. Java 堆内存泄漏分析的一个例子

    Java 程序开发者,多多少少都遇到过 Java 堆内存溢出的错误,错误发生时可以在日志文件中看到 java.lang.OutOfMemoryError: Java heap space 字样.不像  ...

最新文章

  1. Openresty最佳案例 | 第5篇:http和C_json模块
  2. Pymol BioPython | PDB文件中氨基酸序列的提取
  3. BZOJ-1878: [SDOI2009]HH的项链(莫队算法)
  4. 字节跳动再扩招1000人,招聘要求让人窒息
  5. jpa 查找最后一条数据_查找数据的最后1条记录,你用了2小时,同事1分钟就搞定了...
  6. PMCAFF微课堂 | 阿里高级产品专家: 揭秘B类App如何在高速生长期凝结含金量
  7. N的阶乘末尾有多少个0
  8. linux性能优化--cpu篇
  9. 使用Docker容器和Java EE进行持续交付
  10. css网格_我如何记住CSS网格属性
  11. 相关与卷积、各种误差
  12. JVM优化之系统CPU飙高和GC频繁
  13. OpenGL 法线贴图 切线空间 整理
  14. Part1: Specification of Required Functions
  15. 排序系列 之 堆排序算法 —— Java实现
  16. js 阻止冒泡 在控件的 onxxx 事件中调用就可以阻止父控件也响应消息了
  17. 老式计算机如何设置u盘启动,老主板电脑没有USB-HDD如何设置从U盘启动装系统
  18. 阿里云RPA(机器人流程自动化)干货系列之五:业务流程梳理方法
  19. 在安卓中实现Zigbee串口设备采集模块
  20. 混合波束成形| MIMO系统的DFT码本

热门文章

  1. 站帮微管家和其他微信营销软件的区别
  2. 几种常见的安全设备(防火墙、IDS、IPS等)
  3. 微信营销推广的意义体现在哪些方面呢?
  4. 前端写作能力提升之词句
  5. linux内核奇遇记之md源代码解读之八阵列同步二
  6. canvas生成图片toDataURL报错(Uncaught DOMException: Failed to execute ‘toDataURL‘ on ‘HTMLCanvasEl)
  7. 取消Halcon连接相机在外触发模式下的超时时间
  8. 离线数据统计及词云图显示
  9. 人工智能python线上培训系统_Python人工智能学习线路
  10. 新格局 新智造——2021中德(台州)智能制造大会成功召开