专栏首页阿Q说代码醒酒菜:动画图解核心内存区--堆

醒酒菜:动画图解核心内存区--堆

端午佳节一下子就过完了,大家是不是还沉迷在假期的欢乐气氛中无法自拔?今天阿Q为大家准备了上好的“醒酒菜”——JVM运行时数据区的核心内存区——堆。

堆的概述

一般来说:

  • 一个Java程序的运行对应一个进程;
  • 一个进程对应着一个JVM实例(JVM的启动由引导类加载器加载启动),同时也对应着多个线程;
  • 一个JVM实例拥有一个运行时数据区(Runtime类,为饿汉式单例类);
  • 一个运行时数据区中的堆和方法区是多线程共享的,而本地方法栈、虚拟机栈、程序计数器是线程私有的。

堆空间差不多是最大的内存空间,也是运行时数据区最重要的内存空间。堆可以处于物理上不连续的内存空间,但在逻辑上它应该被视为连续的。

在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除。堆,是GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域。

堆内存大小设置

堆一旦被创建,它的大小也就确定了,初始内存默认为电脑物理内存大小的1/64,最大内存默认为电脑物理内存的1/4,但是堆空间的大小是可以调节,接下来我们来演示一下。

准备工具

JDK自带内存分析的工具:在已安装JDKbin目录下找到jvisualvm.exe。打开该软件,下载插件Visual GC,一定要点击检查最新版本,否则会导致安装失败。

安装完重启jvisualvm

代码样例

public class HeapDemo {
    public static void main(String[] args) {
        System.out.println("start...");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end...");
    }
}

IDEA设置

  • -Xms10m用于表示堆区的起始内存为10m,等价于-XX:InitialHeapSize
  • -Xmx10m用于表示堆区的最大内存为10m,等价于-XX:MaxHeapSize
  • 其中-XJVM的运行参数,msmemory start

❝通常会将-Xms-Xmx两个参数配置相同的值,其目的就是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能。 ❞

启动程序

启动程序之后去jvisualvm查看

一旦堆区中的内存大小超过-Xmx所指定的最大内存时,将会抛出OOM(Out Of MemoryError)异常。

堆的分代

存储在JVM中的java对象可以被划分为两类:

  • 一类是生命周期较短的瞬时对象,这类对象的创建和消亡都非常迅速;
  • 另一类是生命周期非常长,在某些情况下还能与JVM的生命周期保持一致;

堆区分代

经研究表明70%-99%的对象属于临时对象,为了提高GC的性能,Hotspot虚拟机又将堆区进行了进一步划分。

如图所示,堆区又分为年轻代(YoungGen)和老年代(OldGen);其中年轻代又分为伊甸园区(Eden)和幸存者区(Survivor);幸存者区分为幸存者0区(Survivor0,S0)和幸存者1区(Survivor1,S1),有时也叫from区和to区。

❝分代完成之后,GC时主要检测新生代Eden区。 ❞

「统一概念:」 新生区<=>新生代<=>年轻代 养老区<=>老年区<=>老年代

几乎所有的Java对象都是在Eden区被new出来的,有的大对象在该区存不下可直接进入老年代。绝大部分的Java对象都销毁在新生代了(IBM公司的专门研究表明,新生代80%的对象都是“朝生夕死”的)。

新生代与老年代在堆结构的占比

  • 默认参数-XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的1/3;
  • 可以修改-XX:NewRatio=4,表示新生代占1,老年代占4,新生代占整个堆的1/5;

❝该参数在开发中一般不会调整,如果生命周期长的对象偏多时可以选择调整。 ❞

Eden与Survivor在堆结构的占比

HotSpot中,Eden空间和另外两个Survivor空间所占的比例是8:1:1(测试的时候是6:1:1),开发人员可以通过选项-XX:SurvivorRatio调整空间比例,如-XX:SurvivorRatio=8

❝可以在cmd中通过jps 查询进程号-> jinfo -flag NewRatio(SurvivorRatio) + 进程号 查询配置信息 ❞

-Xmn设置新生代最大内存大小(默认就好),如果既设置了该参数,又设置了NewRatio的值,则以该参数设置为准。

查看设置的参数

以上边的代码为例:设置启动参数-XX:+PrintGCDetails;可在cmd窗口中输入jps查询进程号,然后通过jstat -gc 进程id指令查看进程的内存使用情况。

图解对象分配过程

对象分配过程

  1. new的对象先放伊甸园区,此区有大小限制;
  2. 当伊甸园的空间填满时,程序继续创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC,也叫YGC):将伊甸园区中的不再被其他对象所引用的对象进行销毁,将未被销毁的对象移动到幸存者0区并分配age
  3. 然后再加载新的对象放到伊甸园区;
  4. 如果再次触发垃圾回收,将此次未被销毁的对象和上一次放在幸存者0区且此次也未被销毁的对象一齐移动到幸存者一区,此时新对象的age为1,上次的对象的age加1变为2;
  5. 如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区,age也随之增加;
  6. 默认当age为15时,未被回收的对象将移动到老年区。可以通过设置参数来更改默认配置:-XX:MaxTenuringThreshold=<N>;该过程称为晋升(promotion);
  7. 在养老区,相对悠闲,当老年区内存不足时,再次触发GC(Major GC),进行养老区的内存清理;
  8. 若养老区执行了Major GC之后发现依然无法进行对象的保存,就会产生OOM异常。

❝S0,S1满时不会触发YGC,但是YGC会回收S0,S1的对象。 ❞

「总结」

  • 针对幸存者s0,s1区:复制之后有交换,谁空谁是to;
  • 关于垃圾回收:频繁在新生区收集,很少在养老区收集,几乎不再永久区/元空间收集。

对象特殊情况分配过程

  1. 新对象申请内存,如果Eden放的下,则直接存入Eden;如果存不下则进行YGC
  2. YGC之后如果能存下则放入Eden,如果还存不下(为超大对象),则尝试存入Old区;
  3. 如果Old区可以存放,则存入;如果不能存入,则进行Full GC
  4. Full GC之后如果可以存入Old区,则存入;如果内存空间还不够,则OOM
  5. 图右侧为YGC的流程图:当YGC之后未销毁的对象放入幸存者区,此时如果幸存者区的空间可以装下该对象,则存入幸存者区,否则,直接存入老年代;
  6. 当在幸存者区的对象超过阈值时,可以晋升为老年代,未达到阈值的依旧在幸存者区复制交换。

内存分配策略

针对不同年龄段的对象分配原则如下:

  1. 优先分配到Eden
  2. 大对象直接分配到老年代:尽量避免程序中出现过多的大对象;
  3. 长期存活的对象分配到老年代;
  4. 动态对象年龄判断:如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入到老年代。无需等到MaxTenuringThreshold中要求的年龄;

数值变小原理

代码样例,设置参数:-Xms600m,-Xmx600m

public class HeapSpaceInitial {
    public static void main(String[] args) {

        //返回Java虚拟机中的堆内存总量
        long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
        //返回Java虚拟机试图使用的最大堆内存量
        long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;

        System.out.println("-Xms : " + initialMemory + "M");
        System.out.println("-Xmx : " + maxMemory + "M");
        
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//执行结果
-Xms : 575M
-Xmx : 575M

明明设置的600M,怎么变成575M了呢?这是因为在堆内存存取数据时,新生代里边只有伊甸园和幸存者1区或者是幸存者2区存储对象,所以会少一个幸存者区的内存空间。

GC

JVM进行GC时,并非每次都对新生代、老年代、方法区(永久代、元空间)这三个区域一起回收,大部分回收是指新生代。

针对HotSpot VM的实现,它里面的GC按照回收区域又分为两大种类型:一种是部分收集(Partial GC),一种是整堆收集(Full GC

Partial GC

部分收集:不是完整收集整个Java堆的垃圾收集。其中又分为:

  • 新生代收集(Minor GC/Young GC):只是新生代的垃圾收集;
  • 老年代收集(Major GC/Old GC):只是老年代的垃圾收集;
  • 混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集,只有G1 GC (按照region划分新生代和老年代的数据)会有这种行为。

目前,只有CMS GC会有单独收集老年代的行为;很多时候Major GC会和Full GC 混淆使用,需要具体分辨是老年代回收还是整堆回收。

Full GC

整堆收集(Full GC):整个java堆和方法区的垃圾收集。

触发机制

年轻代GC(Minor GC)触发机制
  1. 当年轻代空间不足时,就会触发Minor GC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC。(每次Minor GC会清理年轻代的内存,Survivor是被动GC,不会主动GC)
  2. 因为Java对象大多都具备“朝生夕灭”的特性,所以Minor GC非常频繁,一般回收速度也比较快。
  3. Minor GC会引发STWStop The World),暂停其他用户的线程,等垃圾回收结束,用户线程才恢复运行。
老年代GC(Major GC/Full GC)触发机制
  1. 指发生在老年代的GC,对象从老年代消失时,Major GC或者Full GC发生了;
  2. 出现了Major GC,经常会伴随至少一次的Minor GC(不是绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程),也就是老年代空间不足时,会先尝试触发Minor GC。如果之后空间还不足,则触发Major GC
  3. Major GC速度一般会比Minor GC慢10倍以上,STW时间更长;
  4. 如果Major GC后,内存还不足,就报OOM了。
Full GC触发机制

触发Full GC执行的情况有以下五种:

  1. 调用System.gc()时,系统建议执行Full GC,但是不必然执行;
  2. 老年代空间不足;
  3. 方法区空间不足;
  4. 通过Minor GC后进入老年代的平均大小小于老年代的可用内存;
  5. Eden区,Survivor S0from)区向S1to)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。

Full GC是开发或调优中尽量要避免的,这样暂停时间会短一些。 ❞

以上就是今天的所有内容了,如果你有不同的意见或者更好的idea,欢迎联系阿Q:qingqing-4132,阿Q期待你的到来!

后台留言领取java干货资料:学习笔记与大厂面试题

本文分享自微信公众号 - 阿Q说代码(AQ_Shuo),作者:阿Q

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-06-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【趣味】数据挖掘(7)——团拜会与鸡尾酒会上的聚类

    在硕博士生的数据挖掘课程中,聚类是难点,一文难尽。此文用宴会上的见闻,用异于传统的方式,从讲课PPT上取些素材(这样比较快),来说明聚类的一些概念,为下篇...

    机器学习AI算法工程
  • 揭穿机器学习“皇帝的新装”

    我们常把机器学习描述为一种使用数据模式标记事物的神奇技术。听起来艰涩,但事实上,拨开层层概念,机器学习的核心简单到令人尴尬。

    大数据文摘
  • 美团在本地生活终于迎来了真正对手

    在过去一年中,凭借着美团外卖、到店酒旅两条核心业务线,美团的市值一路摸高,来到了2000亿美元的关口,一举坐稳了中国互联网上市公司市值第三把交椅。

    刘旷
  • 产品需求文档:C端生鲜电商APP

    (1)在居民生活品质意识加强及物流产业发展日渐成熟的推动下,中国冷链物流市场规模不断扩大,2019年冷链物流市场规模已达3780亿元,并预计在2020年达到48...

    物流IT圈
  • Clover引导简明教程

    选择 Boot macOS with selected options 启动 出现错误画面拍照发群里寻求帮助。

    慕白
  • 全网最硬核 JVM TLAB 分析 1. 内存分配思想引入

    本期内容比较硬核,非常全面,涉及到了设计思想到实现原理以及源码,并且还给出了相应的日志以及监控方式,如果有不清楚或者有疑问的地方,欢迎留言。

    干货满满张哈希
  • FusionCharts参数说明补充

    功能特性 animation                    是否动画显示数据,默认为1(True) showNames                 ...

    javascript.shop
  • Spring WebFlux开门迎客,却来了一位特殊客人

    话说Spring WebFlux已经出现有一段时间了,但是知道他的人并不是很多。这让他很是闷闷不乐。 还有更惨的是,那些敢于吃螃蟹的人在尝试了他之后,有的竟把...

    Java3y
  • 大数据越来越数据,数据到你只知道是个数据?

    ?   也许这真是一个大数据的年代。你看,各大论坛上专家们在演讲大数据时,哪个不是一脸憧憬、慷慨陈词?但是呢?听众却总是一头雾水、不知所云,只道专家牛逼,越是...

    小莹莹
  • Colab笔记本能用英伟达Tesla T4了,谷歌的羊毛薅到酸爽

    连英伟达最新一代机器学习GPU:Tesla T4都能免费蹭,穷苦羊毛党也顿时高端了起来。

    量子位
  • Colab笔记本能用英伟达Tesla T4了,谷歌的羊毛薅到酸爽

    连英伟达最新一代机器学习GPU:Tesla T4都能免费蹭,穷苦羊毛党也顿时高端了起来。

    磐创AI
  • 美团无人车落地顺义海淀!3天完成优化,单次配送降至28分钟内

    好不容易在美团上抢到了肉,今天终于可以开荤了!正在电脑前搜索菜谱的时候,手机就收到了一条短信,提醒配送已到达。

    大数据文摘
  • 构建会员体系,如何实现从0到1?

    流量红利衰退,挖掘存量用户的额外价值,愈发成为促进企业增长的关键点。为了应对全新的市场变化,大量企业开始将目光锁定在打造更完善的会员体验上。

    iCDO互联网数据官
  • “风味人间”与计算机程序设计艺术《禅与计算机程序设计艺术》 / 陈光剑

    浮华随风去,一菜一江湖。无论置身繁华闹市,还是身居乡野陋巷,世上的滋味,就这样流转于方寸餐桌,交织在冷暖人间。我们说五味杂陈,这五味就是酸、苦、甘、辛、咸。

    一个会写诗的程序员
  • 吹弹牛皮之Unity UI管理系统

    在19年年中小菜的原计划是打算将自己点滴的研究学习的内容,汇总成一个完全属于自己的Unity游戏架构。后来由于个人的半途而废,没能贯彻坚持下去。本篇就把自己在早...

    用户7698595
  • 新疆学子的腾讯后台开发的面经

    4月26日收到了腾讯的offer,终于安心了,很多小伙伴们要我写面经介绍下,其实自己能拿到腾讯的offer 99%是运气~, 这里就介绍下自己的面经跟总结自己的...

    牛客网
  • 创意视觉应用︱基于深度学习的CVaaS计算机视觉即服务案例

    CVaaS 就是 Computer Vision as a Service, 我们把 CV 的部分标准化成为了一种服务,而每一个行业可以在这里找到自己行业需要的...

    素质
  • 微信识图之面向多源异构数据的检测器设计

    ? 作者:breezecheng、morajiang、lyleleeli,腾讯 WXG 应用研究员 微信识图已经在微信的扫一扫识物,微信聊天/朋友圈/公众号...

    腾讯技术工程官方号
  • 新闻动态|腾讯优图助力2019微信公开课Pro版 打造智慧生活极致体验

    不带钱,不用手机,靠脸吃饭的时代真的来了。1月9日-10日,主题为“同行 WITH US”的2019微信公开课PRO版在广州保利博览馆举行。大会不仅涵盖微信大数...

    优图实验室

扫码关注云+社区

领取腾讯云代金券