JVM 运行时内存空间详解——堆

JVM 运行时内存空间详解——堆

通过上一篇文章,我们大体了解了JVM的整体架构,其分为:元数据(JDK7是方法区)、堆、虚拟机栈、本地方法栈、程序计数器几个部分。

本篇文章,咱们对堆进行剖析,一探究竟。

1. 什么是Java 堆

对于Java应用程序来说, Java堆(Java Heap) 是虚拟机所管理的内存中最大的一块。 Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。

此内存区域的唯一目的就是存放对象实例java堆内存设置,Java世界里“几乎”所有的对象实例都在这里分配内存。

“几乎”是指从实现角度来看JVM 运行时内存空间详解——堆, 随着Java语言的发展,现在已经能看到些许迹象表明日后可能出现值类型的支持, 即使只考虑现在, 由于即时编译技术的进步, 尤其是逃逸分析技术的日渐强大, 栈上分配、标量替换优化手段已经导致一些微妙的变化悄然发生, 所以说Java对象实例都分配在堆上也渐渐变得不是那么绝对了。

2. 堆的特点 是Java虚拟机所管理的内存中最大的一块。堆是jvm所有线程共享的。

堆中也包含私有的线程缓冲区 (TLAB)。在虚拟机启动的时候创建。唯一目的就是存放对象实例,几乎所有的对象实例以及数组都要在这里分配内存。Java堆是垃圾收集器管理的主要区域。因此很多时候java堆也被称为“GC堆”( Heap)。从内存回收的角度来看,由于现在收集器基本都采用分代收集算法,所以Java堆还可以细分为:新生代和老年代;新生代又可以分为:Eden 空间、From 空间、To 空间。Java堆是计算机物理存储上不连续的、逻辑上是连续的,也是大小可调节的(通过-Xms和-Xmx控制)。方法结束后,堆中对象不会马上移出仅仅在垃圾回收的时候时候才移除。如果在堆中没有内存完成实例的分配,并且堆也无法再扩展时,将会抛出异常 3. 如何设置堆空间的大小

内存大小 -Xmx(最大)/-Xms(最小)

使用示例: – -Xms5m

说明: 当下Java应用最大可用内存为20M, 最小内存为5M

测试代码:

public class TestVm {
    public static void main(String[] args) {
        // 堆最大空间(如有设置,则和设置的最大值相同)
        System.out.print("Xmx=");
        System.out.println(Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");
        // 空闲空间
        System.out.print("free mem=");
        System.out.println(Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");
        // 总共使用空间
        System.out.print("total mem=");
        System.out.println(Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");
    }
}

在IDEA中设置堆空间:

执行结果:

可以看到,最大20M, 空闲4M多,当前使用了 6M

现在再补充两行代码,给数组分配5M,再看一下结果如何:

public class TestVm {
    public static void main(String[] args) {
        // 补充, 分配5M给数组
        byte[] b=new byte[5*1024*1024];
        System.out.println("分配了5M空间给数组");
        // 堆最大空间(如有设置,则和设置的最大值相同)
        System.out.print("Xmx=");
        System.out.println(Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");
        // 空闲空间
        System.out.print("free mem=");
        System.out.println(Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");
        // 总共申请空间
        System.out.print("total mem=");
        System.out.println(Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");
    }
}

执行结果:

可以看到,最大还是20M, 空闲4M多,当前使用变成了12M。

那么再将分配数组的5M改成10M呢?

        // 补充, 给数组分配10M
        byte[] b=new byte[10*1024*1024];
        System.out.println("分配了10M空间给数组");

可以看到,最大还是20M, 空闲4M多,当前使用变成了17M。

那么,如果再给数组分配加大到20M呢?

  // 补充, 给数组分配20M
        byte[] b=new byte[20*1024*1024];
        System.out.println("分配了20M空间给数组");

可以看到,因为堆的总大小设置的是20M, 如果再给数组分配20M, 此处自然就会报内存溢出异常。

讲了那么多,有必要解释下代码中的三个核心方法,

1.()

2.()

默认值:

含义:相当于未手动设置-Xmx和-Xms的情况下,操作系统共有4050M可以给java虚拟机申请。虚拟机挖了254M内存出来,挖出来的内存中,真正使用了4M左右,还剩余250M未使用。

以上,可以发现,其实JVM在分配内存过程中是动态的, 按需来分配的。

4. 堆的分类

现在垃圾回收器都使用分代理论,堆空间也分类如下:

在Java7 虚拟机中将Java堆内存分为3个部分:

青年代Young :Eden(伊甸元区)、S0(幸存者0区,存放伊甸元区无法销毁的实例)、S1(幸存者1区,存放伊甸元区无法销毁的实例)

老年代Old :S1区经过很多轮GC后,超过一定阈值,实例就会进入老年代,说明这个对象的生命周期比较长。

永久代 : JDK7才有,JDK8换成了元空间。

在Java8以后,由于方法区的内存不再分配在Java堆上,而是存储于本地内存元空间中,所以永久代就不存在了JVM 运行时内存空间详解——堆,在2018年9月25日正式发布以后,在关于中垃圾收集器的官方文档中没有提到“永久代”,而只有青年代和老年代。

通过日志查看堆空间:

public class TestVm {
    public static void main(String[] args) {
        // 堆最大空间(如有设置,则和设置的最大值相同)
        System.out.print("Xmx=");
        System.out.println(Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");
        // 空闲空间
        System.out.print("free mem=");
        System.out.println(Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");
        // 总共使用空间
        System.out.print("total mem=");
        System.out.println(Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");
    }
}

在如上代码中,设置GC日志打印:-XX:+

选择JRE11运行

可以看到,有元空间大小的显示,每个JDK版本打印可能不相同,下面是JDK8的控制台显示

同理java堆内存设置,将JRE换成1.7,就没有元空间了

5. 年轻代和老年代

1.JVM中存储java对象可以被分为两类

1)年轻代(Young Gen):年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁。年轻代分成1个Eden Space和2个 Space(from 和to)。

2)年老代( Gen):年老代主要存放JVM认为生命周期比较长的对象(经过几次的Young Gen的垃圾回收后仍然存在),内存大小相对会比较大,垃圾回收也相对没有那么频繁。

2.配置新生代和老年代堆结构占比

默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX: 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小。

其中,新生代 ( Young ) 被细分为 Eden 和 两个 区域,这两个 区域分别被命名为 from 和 to,以示区分。

默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX: 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to= 1/10 的新生代空间大小。

JVM 每次只会使用 Eden 和其中的一块 区域来为对象服务,所以无论什么时候,总是有一块 区域 是空闲着的。因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。

案例:

/**
 * 1.设置年轻代和老年代的比例 1 : 4
 * 2.设置 伊甸园  from  to  8: 1: 1
 */
public class YoungOldTest {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("hello");
        Thread.sleep(3000000);
    }
}

设置以上程序的堆空间比例分配:年轻代和老年代分配比例为1:4,年轻代Eden区和区占比8:1:1

采用JDK1.8的工具 .exe查看JVM堆的大小(直接点开即可)

安装插件 GC

运行程序后,点开这里的对应程序,查看 GC窗口中堆的占用情况:

6. 对象分配过程

分配过程

1.new的对象先放在伊甸园区,该区域有大小限制 ;

2.当伊甸园区域填满时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园预期进行垃圾回收(Minor GC),将伊甸园区域中不再被其他对象引用的额对象进行销毁,再加载新的对象放到伊甸园区;

3.然后将伊甸园区中的剩余对象移动到幸存者0区;

tips:

Minor GC:YGC(Young GC),即年轻代的垃圾回收。

4.如果再次触发垃圾回收(Minor GC),Eden区的对象直接放到空的幸存者1区。然后上次幸存下来放在幸存者0区的对象,如果还没有回收,也会放到幸存者1区,并且计数加1 。此时幸存者0区就空着了。

tips:

5.如果再次触发垃圾回收(Minor GC),Eden区的对象直接放到空的幸存者0区。然后上次幸存下来放在幸存者1区的对象,如果还没有回收,也会放到幸存者0区,并且计数加1 。此时幸存者1区就空着了。

6.如果累计次数到达默认的15次,这会进入养老区。

可以通过设置参数,调整阈值 -XX:=N

7.养老区内存不足时,会再次出发GC:Major GC 进行养老区的内存清理

8.如果养老区执行了Major GC后仍然没有办法进行对象的保存,就会报OOM异常。

tips:

Major GC:即老年代的垃圾回收。

图解分配对象的流程:

描述:

申请对象时,先判断Eden区域是否装得下,装不下则进行YGC(YGC后保留的对象会在S0和S1区切换存放,直至达到阈值放入老年代),进行YGC后,还装不下,则判断老年代是否装的下java堆内存设置,老年代装不下,再进行FGC(FUll GC),进行FGC后,还装不下,则报OOM异常。

7.堆GC

Java 中的堆也是 GC 收集垃圾的主要区域。GC 分为两种:一种是部分收集器( GC)另一类是整堆收集器(Full GC)

部分收集器: 不是完整收集java堆的的收集器,它又分为:

年轻代GC触发条件:

老年代GC (Major GC)触发机制

触发机制:

注意:Full GC也会引发stop the world,时间非常长。调优过程中,尽量避免的执行

公告:
1. 本站所有资源来源于用户上传和网络,如有侵权请联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系站长处理!
6. 本站不售卖代码,资源标价只是站长收集整理的辛苦费!如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
7. 站长QQ号码 2205675299

资源库 - 资源分享下载网 » JVM 运行时内存空间详解——堆

常见问题FAQ

关于资源售价和售后服务的说明?
本站所有资源的标价均为本站收集资源的辛苦费,不代表资源本身的价值。软件是高智慧高价值的商品,不可能是白菜价。本站资源标价只是赞助费用,收取的赞助费仅用来维持本站的日常运营!毕竟收集整理资料需要投入云计算资源和站长大量的精力。
代码有没有售后服务和技术支持?
由于代码的运行具有不可预见性,本站不保证代码完整可运行,不提供技术支持和售后服务。 本站原创代码都是站长自己开发的,可以有偿提供技术支持服务。 网站里标明【亲测】的代码都是站长亲测过的,其他的代码由于精力有限,没有一一测试,不能保证代码就一定能够使用,更没有技术支持服务,下载前请自行斟酌。
有没有搭建服务?
由于搭建服务比较费时费力,所以本站除了原创代码外均不提供搭建服务。本站分享代码纯属兴趣爱好,不以盈利为目的,请勿咨询有没有搭建服务,谢谢理解。
链接地址失效了怎么办?
请带上资源链接地址联系客服,工作时间内我们看到后将第一时间回复。
关于解压密码
本站资源一般都没有加密,如果发现需要解压密码的,那么就输入 hao.35dc.com 试试。

发表评论

资源库,由老程序员细心甄别、精心筛选,只为提供优质的源码资源

关于我们 联系我们