我的编程空间,编程开发者的网络收藏夹
学习永远不晚

简单说说JVM堆区的相关知识

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

简单说说JVM堆区的相关知识

一、堆概述

  • 一个jvm实例(进程)只存在一个堆内存,堆也是java内存管理的核心区域。
  • java 堆区在jvm启动时即被创建,其空间大小也就被确定了
  • 《java虚拟机规范》规定,堆可以处于物理上不连续的内存空间,但在逻辑上它应该被称为连续的
  • 所有线程共享java堆,在这里和可以划分线程私有的缓冲区(tlab)
  • 所有对象实例以及数组都应在运行时分配在堆中
  • 方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集时候才会被移除
  • 堆是gc执行垃圾回收的重点区域

1.1 堆内存细分

现代垃圾收集器大部分基于分代收集理论设计,堆空间细分为:

  • java 7 之前堆内存逻辑分为:新生区+老年区+永久区
  • java 8 之后内存逻辑上分为:新生区+老年区+元空间

使用下面命令设置堆空间初始化 10m,最大空间 10m


-Xms10m -Xmx10m

使用java visual 查看 visual gc

可以看出通过参数设置的内存大小 只与新生代(Eden+s0+s1 ),老年代有关,而在逻辑上还要加上元空间。

1.2 堆空间大小的设置

1.2.1 通过参数设置


-Xms 用来设置堆空间(年轻代+老年代)的初始内存大小
  -X 是jvm的运行参数
  ms 是memory start
  
-Xmx 用来设置堆空间(年轻代+老年代)的初始内存大小
  • 一旦堆区中的内存大小超过“-Xmx”所指定的最大内存时,将会抛出OutOfMemoryError异常。
  • 通常会将 -Xms 和 -Xmx 两个参数配置相同的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能

1.2.2 默认空间大小

  • 初始化内存大小为物理内存的 1/64
  • 最大内存大小为物理内存大小 1/4

使用一下代码查看 当前jvm初始化内存与最大内存



public class Test9 {

    public static void main(String[] args) {
        //返回jvm中的内存总量(字节)
          long initialMemory = Runtime.getRuntime().totalMemory()/1024/1024;
        //虚拟机将尝试使用最大堆内存
         long maxMemory = Runtime.getRuntime().maxMemory()/1024/1024;
        System.out.println("-Xms:"+initialMemory+"m");

        System.out.println("-Xmx:"+maxMemory+"m");


        System.out.println("系统大小:"+initialMemory*64/1024+"G");
        System.out.println("系统大小:"+maxMemory*4/1024+"G");

    }

}

结果(本机运行内存为 8g)

1.2.3 通过参数设置堆空间大小后内存不一致问题

设置300

查看



public class Test9 {

    public static void main(String[] args) {
        //返回jvm中的内存总量(字节)
        long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
        //虚拟机将尝试使用最大堆内存
        long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
        
        System.out.println("-Xms:" + initialMemory + "m");

        System.out.println("-Xmx:" + maxMemory + "m");
        
    }

}

结果

分析

在vm参数设置里面加上


-XX:+PrintGCDetails 

再次运行程序查看

原理

新生代的s0 和 s1 只能有一个生效

1.3 年轻代与年老代

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

一类是生命周期较短的瞬时对象。

另外一类是对象生命周期非常长。

  • jvm堆区再次进行细分可以分为 年轻代与老年代
  • 其中年轻代可以划分为Eden空间,survivor0空间和survivor1空间(有时也叫 from区,to区)

  • 新生代与老年代空间大小 默认是 1:2

可以通过 -XX:NewRation设置新生代与老年代的比例,默认值是2.(一般都不会去设置)

  • Eden与s0,s1 的内存默认分配比例为 8:1:1

1.4 对象分配过程

1.new的对象先放伊甸区,此区有大小限制

2.当伊甸区满的时候,程序需要创建时,jvm的垃圾回收将对伊甸园区进行垃圾回收(minor gc)

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

4.如果再次触发垃圾回收,上次幸存下来的放到幸存者0区,没有回收,就会放到幸存者1区

5.再次经历垃圾回收会重新放回辛存者0区

6.当在辛存者区达到15次时,就可以去老年区了

可以设置参数:-XX:MaxTenuringThreshold=

7.当养老区内存不足时,再次触发GC:major GC,进行养老区的内存处理

8.若养老区进行处理后,依然无法进行对象的保存,就会产生00m异常


java.lang.outofMemoryError:java heap space

9.总结

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

10.流程

当Eden满时,会触发MinorGC算法来回收memory,旨在清理掉再无引用的数据(在内存里是Tree),意图存储到S0. 若此时S0也满了,会再次MinorGC意图回收S0无引用的数据,把有引用的数据移动到S1。如果S1够用,此时会清空S0;如果S1满了,会回滚刚存入S1的数据,直接把本次GC的数据存入Old区,S0保持刚刚MinorGC时的状态。延伸:如果Old也满了,会触发MajorGC,如果还是不够,则存入Permanent Generate,不幸这里也满了,会在允许的范围内按照内置的规则自动增长,可能不会发生GC,也可能会。当增长的量不够存时,会触发Full GC。若FullGC后还是不够存,自动增长的量也超过了允许的范围,则发生内存溢出。还有一种情况,就是分配的线程栈处于很深的递归或死循环时,会发生栈内存溢出。

二、对象分配过程:Tlab

2.1 为什么要有tlab

  • 堆区是线程共享的,任何线程都可以访问到堆区中的共享数据
  • 由于对象实例的创建在jvm中非常频繁,因此在并发环境下从堆区中划分内存空间是不安全的
  • 而加锁会影响分配速度

2.2什么是tlab

  • 从内存模型而不是垃圾收集角度,对eden区域进行划分,jvm为每个线程分配了一个私有缓存区域
  • 多线程同时分配内存时,使用tlab可以避免非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们将其称为 快速分配策略
  • jvm将tlab作为内存分配的首选
  • 在程序中可以通过参数-XX:UseTLAB设置是否开启(默认是开启的)
  • tlab仅占有eden空间大小的1%,可以通过-XX:TLABWasteTargetPercent设置tlab空间所占用eden空间的大小
  • 使用tlab空间分配内存失败时,jvm会使用加锁机制确保操作的原子性

对象分配流程

对象分配流程

三、堆空间常用参数设置

  •  -XX:+PrintFlagsInitial: 查看所有参数的默认初始值
  • -XX:+PrintFlagFinal: 查看所有参数的最终值
  • -Xms::初始化堆空间内存(默认为物理内存的1/64)
  • -Xmx: 最大堆空间内存(默认为物理内存的1/4)
  • -Xmn:设置新生代的大小(初始值及最大值)
  • -XX:NewRatio: 配置新生代与老年代在堆结构的占比
  • -XX:SurvivorRatio:设置新生代中 eden和s0、s1空间的比例
  • -XX:MaxTenuringThreshold:设置新生代垃圾最大的年龄
  • -XX:+PrintGCDetails:输出详细的gc处理日志
  • -XX:+PrintGC:输出简要的gc处理日志

四、堆是分配对象的唯一选择吗

  1. 随着jit编译期的发展与逃逸分析技术成熟,栈上分配与标量替换优化技术导致所有对象都分配到堆上不那么绝对了
  2. 如果经过逃逸分析后发现,一个对象并没有逃逸出方法的话,那么可能被优化成栈上分配

4.1 逃逸分析

  • 当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸。
  • 当一个对象在方法中被定义后,被外部方法所引用,则认为发生逃逸了

判断逃逸的方法:看new 的对象实体是否有可能在方法外被调用

在这里插入图片描述

4.2 代码优化

  • 栈上分配。将堆分配转化为栈分配。
  • 同步省略。如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步
  • 分离对象或标量替换。有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分可以不存储在内存,而是存储在cpu寄存器中。
  •  标量是指一个无法在分解成更小的数据,相对的其它还可以在分解的称为聚合量
  • 在jit阶段进行逃逸分析,发现对象不会被外界访问(没有逃逸发生),经过jit优化,就回把对象分解为成员变量来代替,这个过程就是标量替换

到此这篇关于简单说说JVM堆区的相关知识的文章就介绍到这了,更多相关JVM堆区内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

简单说说JVM堆区的相关知识

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录