最近经常遇到jvm内存问题,觉得还是有必要整理下jvm内存的相关逻辑,这里只描述jvm堆内存,对外内存暂不阐述。

jvm内存简图

  1. jvm内存分为堆内存和非堆内存,堆内存分为年轻代、老年代,非堆内存里只有个永久代。

  2. 年轻代分为生成区(Eden)和幸存区(Survivor),幸存区由FromSpace和Tospace两部分组成,默认情况下,内存大小比例:Eden:FromSpace:ToSpace 为 8:1:1。

  3. 堆内存存放的是对象,垃圾收集器回收的就是这里的对象,不同区域的对象根据不同的GC算法回收,比如年轻代对应Minor GC,老年代对应Major GC。

  4. 非堆内存即永久代,也称为方法区,存储的是程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。

  5. 在jdk1.8废弃了永久代,使用元空间(MetaSpace)取而代之,元空间存储的对象与永久代相同,区别是:元空间并不在jvm中,使用的是本地内存。

    为什么移除永久代呢

​ 为融合HotSpot JVM与JRockit VM(新JVM技术)而做出的改变,因为JRockit没有永久代。

分代概念

首先,GC是Garbage Collection,即垃圾回收。

​ 新生成的对象首先存放在生成区,当生成区满了,触发Minor GC,存活下来的对象转移到Survivor0,即FromSpace,Survivor0区满后触发执行Minor GC,存活对象移动到Suvivor1区,即ToSpace,经过多次Minor GC仍然存活的对象转移到老年代。

​ 所以老年代存储的是长期活动的对象,当老年代满了会触发Major GC。

​ Minor GC和Major GC是俗称,有些情况下Major GC和Full GC是等价的,如果出发了Full GC,那么所有线程必须等待GC完成才能继续(见GC分类和算法)。

分代原因

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

为什么幸存区分为大小相同的两部分:S0,S1

​ 主要为了解决碎片化,因为回收一部分对象后,剩余对象占用的内存不连续,也就是碎片化,过于严重的话,当前连续的内存不够新对象存放就会触发GC,这样会提高GC的次数,降低性能,当S0 GC后存活对象转移到S1后存活对象占用的就是连续的内存。

GC分类和相关算法

我们来看下GC分类,才能清楚什么时候触发Full GC、和非Full GC,GC大致分为两种:

  • Partial GC:并不收集整个GC堆的模式,即可以理解为非Full GC

    • Young GC:只收集young gen的GC
    • Old GC:只收集old gen的GC。只有CMS有这个模式
    • Mixed GC:收集整个young gen以及部分old gen的GC。只有G1有这个模式
  • Full GC:收集整个堆,包括young gen、old gen、perm gen(如果存在的话)等所有部分的模式。

上面说的CMS和G1都是GC的算法,相关GC算法如下:

  1. Serial GC算法:Serial Young GC + Serial Old GC (实际上它是全局范围的Full GC);

  2. Parallel GC算法:Parallel Young GC + 非并行的PS MarkSweep GC / 并行的Parallel Old GC(这俩实际上也是全局范围的Full GC),选PS MarkSweep GC 还是 Parallel Old GC 由参数UseParallelOldGC来控制;

  3. CMS算法:ParNew(Young)GC + CMS(Old)GC + Full GC for CMS算法;

  4. G1 GC算法:Young GC + mixed GC(新生代,再加上部分老生代)+ Full GC for G1 GC算法(应对G1 GC算法某些时候的不赶趟,开销很大);

GC触发条件

  • Young GC:各种Young GC触发的条件都是Eden区满了。

  • Serial Old GC/PS MarkSweep GC/Parallel Old GC:当准备要触发一次young GC时,如果发现统计数据说之前young GC的平均晋升大小比目前old gen剩余的空间大,则不会触发young GC而是转为触发full GC。

  • Full GC for CMS:老年代使用比率超过某个值。

  • Full GC for G1 GC:Heap使用比率超过某个值。

  • 如果有perm gen的话,要在perm gen分配空间但已经没有足够空间时,也要触发一次full GC。

    小结:不同算法对应的GC回收条件是不同的。

    GC方式

    标记-清除(Mark-Sweep)

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

    红色代表被标记的可回收对象,绿色代表存活对象



    清除后如下:



    复制(Copy)

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

清除前后如下:





标记-整理(Mark-Compact)

​ 也分为两个阶段,首先标记可回收的对象,再将存活的对象都向一端移动,然后清理掉边界以外的内存。此方 法避免标记-清除算法的碎片问题,同时也避免了复制算法的空间问题。

​ 一般年轻代中执行GC后,会有少量的对象存活,就会选用复制算法,只要付出少量的存活对象复制成本就可以 完成收集。而老年代中因为对象存活率高,没有额外过多内存空间分配,就需要使用标记-清理或者标记-整理算法来进行回收。

清除前后如下:



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表示直接进入老年代

OOM原因

1)老年代内存不足:java.lang.OutOfMemoryError:Javaheapspace

2)永久代内存不足:java.lang.OutOfMemoryError:PermGenspace

3)代码bug,占用内存无法及时回收。

​ 可以通过添加个参数-XX:+HeapDumpOnOutMemoryError,让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后分析。

jvm堆内存和GC简介的更多相关文章

  1. 【转】JVM 堆内存设置原理

    堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...

  2. JVM堆内存设置

    今天碰到了一个题目,讲的是关于堆内存的问题,题目如下   下面哪种情况会导致持久区jvm堆内存溢出? A.循环上万次的字符串处理 B.在一段代码内申请上百M甚至上G的内存 C.使用CGLib技术直接操 ...

  3. JVM堆内存监测的一种方式,性能调优依旧任重道远

    上月,由极客邦.InfoQ和听云联合主办2016 APMCon中国应用性能管理大会圆满落下帷幕.会上,Java冠军Martijn Verburg进行了一场Java and the Machine的分享 ...

  4. 巩固java(二)----JVM堆内存结构及垃圾回收机制

    前言:        我们在运行程序时,有时会碰到内存溢出(OutOfMemoryError)的问题,为了解决这种问题,我们有必要了解JVM的内存结构和垃圾回收机制. 正文: 1.JVM堆内存结构   ...

  5. JDK8中JVM堆内存划分

    一:JVM中内存 JVM中内存通常划分为两个部分,分别为堆内存与栈内存,栈内存主要用运行线程方法 存放本地暂时变量与线程中方法运行时候须要的引用对象地址. JVM全部的对象信息都 存放在堆内存中.相比 ...

  6. [转]JVM 堆内存设置原理

    堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...

  7. JVM 堆内存设置原理

    堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...

  8. 【转】JDK5.0中JVM堆模型、GC垃圾收集详细解析

    基本概念 堆/Heap JVM管理的内存叫堆:在32Bit操作系统上有4G的限制,一般来说Windows下为2G,而Linux下为3G:64Bit的就没有这个限制.JVM初始分配的内存由-Xms指定, ...

  9. JVM 堆内存设置原理(转)

    堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...

随机推荐

  1. Ajax跨域解决方案大全

    题纲 关于跨域,有N种类型,本文只专注于ajax请求跨域(,ajax跨域只是属于浏览器"同源策略"中的一部分,其它的还有Cookie跨域iframe跨域,LocalStorage跨 ...

  2. 深入理解Go系列一之指针变量

    指针变量 基本概念 &a,代表 a 变量的内存地址 p,代表一个句柄引用(句柄由实例数据指针与实例类型指针两部分组成) ,句柄的好处是,当实例数据值改变时,不需要改动实例数据指针与实例类型指针 ...

  3. leetcode刷题-53最大子序和

    题目 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 思路 动态规划:求整个数组的连续子数组的最大和,可以求出每个位置的连续子数组的最大和,返回 ...

  4. c++基础 写二进制文件

    问题描述 有许多数据待拟合,需要从 root 中提取出来,写成文本文件数据量过大,想转成二进制文件. 解决 #include "TString.h" #include " ...

  5. Linux远程ssh执行命令expect使用及几种方法

    expect命令实现脚本免交互 一.Linux下SSH无密码认证远程执行命令 在客户端使用ssh-keygen生成密钥对,然后把公钥复制到服务端(authorized_keys). 实现步骤: 1.客 ...

  6. springboot2.x基础教程:动手制作一个starter包

    上一篇博客介绍了springboot自动装配的原理.springboot本身有丰富的spring-boot-starter-xx集成组件,这一篇趁热打铁加深理解,我们利用springboot自动装配的 ...

  7. vue父子传值与非父子传值

    大概梳理下传值的几种方式 一:父子组件传值 props方式 子组件对外暴露方法并向父组件传递由于触发方法导致的值的变化,父组件接收子组件传递来的值:子组件接收父组件传递来的值,并根据传递来的值在子组件 ...

  8. 认证授权:IdentityServer4 - 各种授权模式应用

    前言: 前面介绍了IdentityServer4 的简单应用,本篇将继续讲解IdentityServer4 的各种授权模式使用示例 授权模式: 环境准备 a)调整项目结构如下:   b)调整cz.Id ...

  9. Win10安装Tensorflow-gpu遇到Could not install packages due to an EnvironmentError: [WinError 5] 拒绝访问

    最近因为上课需要安装Anaconda和Tensorflow,安装Anaconda后再使用 Tensorflow官网提供的pip安装Tensorflow-GPU方法会出现如下错误: 解决方法:在安装命令 ...

  10. java安全编码指南之:字符串和编码

    目录 简介 使用变长编码的不完全字符来创建字符串 char不能表示所有的Unicode 注意Locale的使用 文件读写中的编码格式 不要将非字符数据编码为字符串 简介 字符串是我们日常编码过程中使用 ...