转载:简单介绍JVM的GC过程

在说到java虚拟机的垃圾回收机制前,应该先知道虚拟机里面有什么区域,哪些区域要在运行过程过程中时不时的对其进行垃圾清除。

有哪些区域

1、程序计数器

占用虚拟机内存很小,功能是给字节码解释器寻址用的。在它工作时通过修改计数器值来选取下一条需要执行的字节码指令地址。像分支、循环,跳转、异常处理、线程恢复等功能都需要计数器完成。

程序计数器属于“线程私有”的内存。虚拟机的多线程是通过线程轮流切换并分配CPU的执行时间的方式来实现的,任何某个时刻,CPU中一个内核都只会执行一条线程中的指令。为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,这样就互不影响,独立存储。

如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器值则为空(Undefined)。也不会出现OOM的异常区域。

废话太多,就记得它类似CPU里面的程序计数器,负责指出下一条指令的地址。

2、Java堆

堆这块区域算是虚拟机中最大的一块内存区域了,属于线程共享区域。虽然是线程共享区域,但内部可能会划分出多个线程私有的分配缓冲区(TLAB)。Java堆内存空间在物理空间可以不连续,但是逻辑空间上必须连续。

堆区域主要是存放对象实例和数组,细分一下Java堆会分为新生代(Eden、FromSurvivor、ToSurvivor)和老年代。分代是为了更方便的在Java运行过程中产生的垃圾进行回收。内存不够用会出现OOM异常。

这个区域很重要,因为比较特别,我们垃圾回收主要都是在这个区域操作。

  1. -Xms20m 设置堆最小内存20MB
  2. -Xmx20M  设置堆最大内存20MB
  3. 当-Xms和-Xmx值相同表示不允许堆内存进行扩展
  4. -Xmn5m  表示新生代内存为5MB
  5. -XX:NewRatio=3  表示新生代占堆内存1/3
  6. -XX:SurvivorRatio=8  表示Eden:2个Survivor = 8:2

上面是对jvm的部分区域内存进行数值设定,这些参数都是有main函数的String[] args去接收的。在IDEA中则要在一个类的VM options处填写即可。

3、Java虚拟机栈

该内存也是线程私有,生命周期和线程一致。描述的是 Java方法执行的内存模型:每个方法在执行时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。

局部变量表:存放了编译期可知的各种基本类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型)和 returnAddress类型(指向了一条字节码指令的地址)。会出现StackOverflowError和OutOfMemoryError。

  1. -Xss128k 表示设置栈内存设置为128KB
  2. 该参数和本地方法栈参数共用

4、本地方法栈

类似上面Java虚拟机栈,这块内存区域不是给Java方法用的,是给Native方法使用的。在以前Java刚出来的时候,为了方便和其他语言相互调用而在虚拟机区域划分了一小块内存。

例如我们使用JNI方法去调用C、C++等语言编写的代码时使用。

5、方法区

Jdk1.8版本之前很多人都说这是永久代,该区域属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

其中运行时常量池是属于方法区一部分,用于存放编译期生成的各种字面量和符号引用。编译器和运行期(String的 intern())都可以将常量放入池中。内存不够用也会抛出OOM异常。

  1. -XX:PermSize=10M 表示方法区最小内存10MB
  2. -XX:MaxPermSize=10M 表示最大内存10MB

回收哪些区域

废话,当然都是要收集的呀。因此要进行垃圾收集(GC)的区域很多,但是最主要也最不好GC的地方是Java堆区域。

Java虚拟机栈都是局部变量,方法结束的时候已经没有存在的意义,它会自动喊GC器过来帮它处理掉。但是我们Java堆的存放的共享变量,有数组有类对象实例,不能像栈那样用完就扔,因为我们也不太清楚谁用过了就没用了,谁还没用也可以当垃圾处理。

所以JDK每个版本更多都是在对堆内存做优化GC算法、以及新的GC器设计。

如何判断对象是垃圾

1、引用计数法

给对象中加一个计数器,有人引用它时就count++;引用失效时就count--,当系统开始GC的那一时刻,如果对象的计数器为0就准备被处理了。

优点:实现简单,判定效率高,大部分情况下是一个不错的算法

缺点:很难解决对象之间相互循环引用的问题

2、可达性分析

类似老鹰捉小鸡游戏,通过一系列的 “GCRoots’”的对象当作老母鸡,从这些节点用引用链连着的节点是存活的,就像小鸡躲在老母鸡后面。当一个对象到“GCRoots”没有任何引用链相连的时候说明对象不可用,就像掉队的小鸡会被老鹰***掉。

GC Roots对象:虚拟机栈的引用对象;方法区类静态属性引用对象或常量引用的对象;本地方法栈Native方法引用的对象。

小鸡E、小鸡F和小鸡G将要被老鹰***掉,注意,这里是将要。实际过程中GC器的线程是一个优先级很低的线程。一般cpu很少去调用它,所以掉队的对象都会被放在垃圾队列中准备被清理。在等待死神到达前,如果有其他对象把Object5对象引用回来就不用被处理了。就好比老母鸡发现小鸡掉队赶紧过去让小鸡进队。其实后续还有很多步骤,这里不再深入。

GC过程

为什么要进行GC,因为在程序运行过程中由于内存不够用呀。现在的Java虚拟机在GC过程中都是分代GC的,分代就像我们面对不同的人会以不同的态度去面对他们,面对新生代和老年代我们也要用不同的处理方式去处理。

新生代的GC叫MinorGC,频繁但回收快;老年代的GC叫做MajorGC/Full GC,需要停止所有虚拟机线程(stop the world)去GC,次数少但耗时。

新生代里面的大部分对象存活时间超短,基本都会在下一次MinorGC触发前就没用了。当Eden(伊甸园)区满的时候就触发MinorGC时,GC掉那些用可达性分析算法判断出来的垃圾对象。老年代的对象在老年代堆内存满的时候也会进行GC,那老年代对象怎么来的?

新生代对象如何进入老年代

1、超大对象直接进入老年代,避免在MinorGC使用复制算法时浪费时间;想象成你公司老板的儿子来公司上班,身为HR的你是让他去打杂(新生代)呢还是做办公室喝茶(老年代)呢?

  1. -XX:PretenureSizeThreshold=3145728
  2. 表示对象超过3MB直接送入老年代

2、在多次Mainor GC没被处理的对象,熬到一定岁数就给这类对象移动到老年代中,

-XX:MaxTenuringThreshold=10  表示躲过10次Mainor GC就直接进入老年代;想象成你在国企熬了十几年终于熬到经理职位了,不用担心被辞职了。为啥说太,因为老年代也会被GC的。

  1. -XX:MaxTenuringThreshold=10
  2. 表示躲过10次MainorGC就直接进入老年代

3、如果在 Survivor空间中相同年龄所有对象大小的总和超过Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到规定年龄才能进入。

GC器

老鹰种类有点多,内容很枯燥,这里不做介绍了

案例分析

公司服务器是一个4核8G的配置的服务器,拿出4g内存给JVM用,其中3g内存分给堆内存。部署的项目是一个网上商城,项目是使用分布式微服务框架,功能就是用来卖东西的,在高峰期每秒下单超300个。请问下面的配置方式可以吗?

-Xms:3072m –Xmx:3072m –Xss1m –XX:PerSize=256m-XX:MaxPermSize=256m

3g的堆内存去应对每秒产生的300个订单对象,会出现问题不?

下面数据是按最大值选取,这样才能保证我们系统不出意外。买东西下单用的是订单系统,订单类里面要包含订单id,订单号,订单生成时间,卖家、买家等乱七八糟的基本属性,int类型、String类型、Long等等。我们假设有1024个字节凑成1KB,一个订单对象占内存1KB,高峰期每秒产生300KB的订单对象。

订单系统在生成订单对象是还需要其他系统的对象做支持,像库存呀、有没有优惠呀、买送多少积分金币呀,会员打折吗等等其他系统对象,我们放大20倍(为啥放大20倍,因为要凑数才能让后面GC出问题)。

当订单对象在其他对象帮助下,终于生成了,这时每秒就产生了6000KB的内存。当订单生成的时候,用户不一定下单,可以会去看其他商品然后再回来查询自己的订单或者取消订单等这些操作,再放大10倍(接着凑数)。

此时每秒系统产生了约60MB的内存对象,然后这些对象都先放在Eden区,我Eden区800多MB的不怕,但是过了13s左右差不多就满了。这时候触发MainorGC,这个过程很快,毫秒级别的。案例应该对系统没啥影响呀。

影响在于GC的时候出问题了,因为第一次GC,会把Eden有用的对象放在From区,可能有60MB的对象在GC开始的时刻还没断开GCRoots就直接被复制到了From区,其他都被清理了。按照新生代进入老年代的第三条规定,这60MB的对象是直接被移到了老年代里面了。

换句话说,大约每隔13s左右有60MB下一秒就是垃圾的对象被移动到了老年代。大约7分多钟老年代就满了,老年代开始full gc了。Full GC很可怕,stop the world。过程很慢,而且系统这段时间什么外部操作都不能执行,就是卡住了。

一般网点高峰期都是中午或者晚上上班族不上班的时候,这段时间都是持续几十分钟或者更长,每隔7分多钟系统卡住1-2秒。我的天!!!准备辞职吧。

解决办法就是在新生代GC的时候不让那60MB进入老年代,方法就是扩大From区内存,不然它进入老年代。

-Xms:3072m –Xmx:3072m –Xmn2048m –Xss1m –XX:PerSize=256m -XX:MaxPermSize=256m

配置就是在原来基础上添加了一个 -Xmn2048m,作用是设置堆内存中新生代占2GB的内存,这样老年代默认就是1GB。

Edne变成2倍,也就是26秒左右Edne满了,GC的时候60MB对象跑到了From中,但是不会到老年代去。然后第二次GC的时候,这个在From区的60MB早就脱离GCRoots被清理,然后新生成的60MB对象放在了To区。每次新生代GCFrom和To只能有一个被使用。

最后,我们再给系统设置一个定时任务,每天大半夜用户差不多都在睡觉的时候让系统自动full GC一下。这就是为啥项目去生产环境做新功能上线的时候都要在大半夜去,没啥人。

黑嘴喜鹊:

黑嘴喜鹊:黑嘴喜鹊是喜鹊属的一种鸟类。与欧亚大陆常见的喜鹊属于两个物种。主要分布于北美洲阿拉斯加南部、加拿大西南部及美国中西部。黑、白两色,尾有蓝绿色的虹彩。头、颈、背至尾均为黑色,并自前往后分别呈现紫色、绿蓝色、绿色等光泽。双翅黑色而在翼肩有一大形白斑。

长期以来,喜鹊在美国一直受到人类的迫害,它们被视为害鸟,或者猎禽的对手。有几个州甚至为捕杀喜鹊提供赏金。尽管现在它们还未被列为濒危物种,但其数量已经大幅度下降。。

简单介绍JVM的GC过程相关推荐

  1. 垃圾回收算法简单介绍——JVM读书笔记lt;二gt;

    垃圾回收的过程主要包含两部分:找出已死去的对象.移除已死去的对象. 确定哪些对象存活有两种方式:引用计数算法.可达性分析算法. 方案一:引用计数算法 给对象中加入一个引用计数器.每当有一个地方引用它时 ...

  2. 垃圾分代回收机制简单介绍

    针对GC的简单介绍 JVM对自己的内存进行了划分5个区域,分别是堆,栈,方法区,本地方法栈,程序计数器.Java中对每一种类型都规定了具体的不可变的大小.所以所有的内存都是由JVM自动分配,所有的内存 ...

  3. 简单介绍 ghost封装过程

    简单介绍 ghost封装过程 图片: 描述:1 图片: 描述:2 图片: 描述:3 图片: 描述:4 图片: 描述:5 图片: 描述:8 图片: 描述:11 图片: 描述:12 图片: 描述:13 图 ...

  4. JVM: GC过程总结(minor GC 和 Full GC)

    一 minorGC 和 Full GC区别 新生代 GC(Minor GC):指发生新生代的的垃圾收集动作,Minor GC 非常频繁,回收速度一般也比较快. 老年代 GC(Major GC/Full ...

  5. Java中的GC简单介绍

    文章目录 一.GC是什么? 二.为什么要GC? 三.怎么GC? 一.GC是什么? GC英文全称为Garbage Collection,即垃圾回收. Java中的GC就是对内存的GC. Java的内存管 ...

  6. JVM内功心法-GC垃圾回收之GC垃圾回收过程

    JVM内功心法-GC垃圾回收之GC垃圾回收算法 GC 全称garbagecollection,垃圾回收.JAVA 为了屏蔽操作系统和平台之间的差异.选择的是采用 java 虚拟机来运行 java 应用 ...

  7. [面试]:能简单介绍下您现在所做项目和过程中的技术难点或有遇到瓶颈吗?

    思路转载自Hollis的知识星球,有兴趣的可以搜一下,很不错. 业务介绍: 1.痛点:解决的问题,用户痛点 2.正确性 3.可用性 4.大规模:量级 思考问题: 宏观,不局限于业务 这种问题直接回答高 ...

  8. 深入理解JVM(2)——GC算法与内存分配策略

    说起垃圾收集(Garbage Collection, GC),想必大家都不陌生,它是JVM实现里非常重要的一环,JVM成熟的内存动态分配与回收技术使Java(当然还有其他运行在JVM上的语言,如Sca ...

  9. JVM、GC看这一篇就够了!

    Class类加载 - 加载Loading 通过全限定类名,把一个类从二进制的Class文件加载到内存当中 - 类加载器 Bootstrap:启动类加载器.加载lib/rt.jar charset.ja ...

  10. Synchronized详细介绍之锁升级过程

    Synchronized详细介绍之锁升级过程 前言 线程与进程的区别 进程 线程 区别 协程 JVM线程调度原理 JVM线程调用过程 JAVA线程与内核线程的关系 源码分析 线程状态 Synchron ...

最新文章

  1. .NET Core ASP.NET Core 1.0在Redhat峰会上正式发布
  2. 黄仁勋从煤气灶下取出最新GPU:7nm全新安培架构,售价20万美元,训练性能顶6张V100...
  3. Centos系统搭建LAMP
  4. JavaScript——易班优课YOOC课群课程视频立刻完成解决方案
  5. gpio驱动广播Android,[RK3288][Android6.0] 调试笔记 --- 通用GPIO驱动控制LED【转】
  6. JavaScriptCore API 和V8 API
  7. php 安全基础 第七章 验证与授权 密码嗅探
  8. 两个列向量相乘怎么计算_机器学习 线性代数基础 | 1.4 矩阵乘向量的新视角:变换基底...
  9. 用Hibernate tool从实体对象生成数据库表
  10. thinkphp 删除该表的最后一行
  11. (王道408考研操作系统)第四章文件管理-第一节8:文件保护
  12. JAVA中的通用文件下载接口
  13. 后台给前台传值 php,前后台传值的几种方式(html,js,php)
  14. swift项目调用OC库 和OC项目 在swift文件里面全局调用OC库
  15. java解析xml文件:创建、读取、遍历、增删查改、保存
  16. pdf expert使用教程:如何在mac上给PDF怎么调整页面顺序
  17. 激光清洗的优点和实际案例
  18. 通过ScrollView实现滚动效果
  19. linux memwatch的内存检测-double-free
  20. chmod +x 与chmod 777的区别

热门文章

  1. 关于HTML学习的第一周周记
  2. 【FLY】Android(12)源码目录结构
  3. 瑞斯康达raisecom olt运维常用命令
  4. 二进制漏洞挖掘技术实战
  5. opencv绘制图形轮廓并筛选面积操作
  6. 货币市场基金基础知识
  7. 表格列宽怎么设置?(excel表格)
  8. 探真无阻塞加载javascript脚本技术
  9. VBA字符处理 特殊符号
  10. 与门非门在电子计算机中的应用,与非门电路