JVM实战:JVM内存分配策略

2023-10-27

请添加图片描述

JVM运行时数据区

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。Java虚拟机所管理的内存将会包括以下几个运行时数据区域
在这里插入图片描述
其中方法区和堆是所有线程共享的数据区。程序计数器,虚拟机栈,本地方法栈是线程隔离的数据区

虚拟机栈和本地方法栈中的空间会随着方法的调用和完成而不断分配和释放。应用中创建的对象则会被分配到堆中,当对象不在使用的时候会被回收。今天我们就先聊一下内存分配的过程
请添加图片描述
JVM的堆在1.8之后主要分为2个部分新生代和老年代。新生代和老年代默认的比例为1:3
其中新生代又分为Eden区,From区(Survivor S0区), To区(Survivor S1区)

堆空间为什么要分代呢?

为了更高效的进行垃圾回收
在这里插入图片描述

JVM内存分配的整体流程

在这里插入图片描述

YGC日志如何查看

本文的内容总结自《深入理解Java虚拟机》,后面涉及的结论如果有想复现的小伙伴可直接看这本书,因为在书中没有介绍YGC的日志如何查看,我写个例子来介绍一下,这样你就能看懂书中的Demo了

private static final int _1MB = 1024 * 1024;

// -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
public static void testAllocation() {
	byte[] allocation1, allocation2, allocation3, allocation4;
	allocation1 = new byte[2 * _1MB];
	allocation2 = new byte[2 * _1MB];
	allocation3 = new byte[2 * _1MB];
	allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC
}
参数 含义
-Xms20M 堆最小内存为20M
-Xmx20M 堆最大内存为20M
-Xmn10M 新生代大小为10M
-XX:+PrintGCDetails 打印日志详情
-XX:SurvivorRatio=8 Eden和一个Survivor的空间比例为8:1

最终新生代大小为10M(其中Eden区为8M,一个Survivor区为1M),老年代大小为10M
在这里插入图片描述
我们先来分析一下这个 Young GC 日志应该怎么看?

// 内存分配失败,发生GC
[GC (Allocation Failure)
// PSYoungGen 使用的垃圾收集器为 Parallel Scavenge
// 8025K->1014K(9216K) YGC前新生代内存 -> YGC后新生代内存(新生代总内存大小)
// 8025K->5118K(19456K) YGC前JVM堆内存 -> YGC后JVM堆内存(JVM堆总内存大小)
// 0.0040891 secs YGC耗时
[PSYoungGen: 8025K->1014K(9216K)] 8025K->5118K(19456K), 0.0040891 secs]

为什么新生代总内存大小为9216K?

9216k=9M,Eden(8M)+Survivor(1M)=9M,因为新生代中只有一个Survivor区可以存放对象

我们在启动参数中并没有设置垃圾收集器,为什么使用了 Parallel Scavenge 收集器呢?

因为当没有设置时,用了默认的垃圾收集器,相当于启动参数加了如下参数

-XX:+UseParallelGC 年轻代使用 Parallel Scavenge 垃圾收集器
-XX:+UseParallelOldGC 老年代使用 Parallel Old 垃圾收集器
// 本次 GC 耗时
[Times: user=0.01 sys=0.01, real=0.01 secs] 

这段日志是JVM退出时打印出来当前堆内存的使用情况

Heap
 PSYoungGen      total 9216K, used 7395K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 8192K, 77% used [0x00000007bf600000,0x00000007bfc3b660,0x00000007bfe00000)
  from space 1024K, 99% used [0x00000007bfe00000,0x00000007bfefd818,0x00000007bff00000)
  to   space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
 ParOldGen       total 10240K, used 4104K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
  object space 10240K, 40% used [0x00000007bec00000,0x00000007bf002020,0x00000007bf600000)
 Metaspace       used 3342K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 363K, capacity 388K, committed 512K, reserved 1048576K
// PSYoungGen Parallel Scavenge 垃圾收集器
// toal 9216K 年轻代总共有 9216K(9MB)
// used 7395K 目前使用了 7395K
PSYoungGen      total 9216K, used 7395K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
// eden大小为 8192K(8MB) 使用了 77%
eden space 8192K, 77% used [0x00000007bf600000,0x00000007bfc3b660,0x00000007bfe00000)

内存分配策略

对象优先在Eden分配

在这里插入图片描述

将新生代分为Eden区,From区,To区是基于其所用的垃圾回收决定的(标记复制算法)

这3个区的内存分配过程如下

  1. 对象优先在Eden区分配,当进行YGC时,会将存活的对象放到From区,To区空着不用哈。
  2. 当第二次进行YGC时,会将From区和Eden区存活的对象复制到To区,此时Eden区和From区就为空哈。
  3. 当第二次进行YGC时,会将To区和Eden区存活的对象复制到From区,此时Eden区和To就为空哈。
  4. 按照此规律,循环往复下去

大对象直接进入老年代

JVM中有这样一个参数 -XX: PretenureSizeThreshold ,指定大于该设置值的对象直接在老年代分配,这样做的目的就是避免在Eden区以及2个Survivor区之间来回复制,产生大量的内存复制操作

长期存活的对象将进入老年代

对象通常在Eden区诞生,如果经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,该对象会被移动到Survivor中,并且将其对象设为1岁,对象在Survivor区中每熬过一次Minor GC,年龄就增加一岁,当它的年龄增加到一定程度(默认15),就会被晋升到老年代中,对象晋升老年代的年龄阈值, 可以通过参数-XX:MaxTenuringThreshold设置

动态对象年龄判定

HotSpot虚拟机并不是永远要求对象的年龄必须达到-XX:MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代

空间分配担保

如果在Minor GC之后发现剩余的存活对象太多了,没办法放入另一块Survivor区怎么办?

这个时候就必须把这些对象直接转移到老年代去

但是这样就会引入新的问题了,如果老年代也放不下这些对象该怎么办?

只要老年代的连续空间大于新生代对象的总大小或者历次晋升到老年代的对象的平均大小就进行MinorGC。

否则FullGC,对老年代进行垃圾回收,尽量腾出一些空间,然后执行Minor GC,如果Full GC 过后,老年代还是没有足够的空间存放Minor GC过后剩余的存活对象,就会导致内存溢出

参考博客

[1]https://www.oolongbox.com/box/df0ed28e/#%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E7%AD%96%E7%95%A5
[2]https://blog.csdn.net/weixin_42147245/article/details/104783221
[3]https://www.jianshu.com/p/f5f178b37873
[4]https://www.jianshu.com/p/bf5d20d8be19

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JVM实战:JVM内存分配策略 的相关文章

随机推荐

  • Linux逻辑卷管理(LVM)

    一 逻辑卷 LV 卷组 VG 物理卷 PV 关系 逻辑卷 LV 是卷组 VG 的一部分 可以在卷组大小内动态增加 每个卷组可分为多个逻辑卷 卷组由多个物理卷 PV 组成 每个物理卷是一个块设备 磁盘分区等 二 LVM存储 1 准备物理设备
  • VSCode自动格式化Vue代码

    VSCode自动格式化Vue代码 Prettier Code formatter 配置代码格式化文件 配置setting json VsCode关于Vue的格式化插件非常多 网上的资料也五花八门 可以使用的非常少 经过多次探索 终于找到一个
  • 剑指offer第45题:扑克牌顺子

    剑指offer第45题 扑克牌顺子 题目描述 扑克牌顺子 源码 题目描述 扑克牌顺子 LL今天心情特别好 因为他去买了一副扑克牌 发现里面居然有2个大王 2个小王 一副牌原本是54张 他随机从中抽出了5张牌 想测测自己的手气 看看能不能抽到
  • python,tensorflow,keras,pandas,numpy版本不匹配的问题(已解决)

    事情的起因 因为自己手残 更新了我搭建的tensorflow虚拟环境中numpy的版本 更高 导致import pandas 时出现版本不匹配的问题 于是将pandas一起更新 这时 pandas和numpy可以使用 但是由于tensorf
  • Linux 学习笔记2 常用命令

    Linux 学习笔记1 安装linux详细教程 O丶ne丨柒夜的博客 CSDN博客 查看使用7007端口的进程 ps aux grep 7007 Linux 目录结构 基本介绍 1 liux的文件系统是采用级层式的树状目录结构 在此结构中的
  • 五种边缘检测算法(附代码)

    一 Roberts 边缘检测算子 Roberts边缘检测算子根据任意一对互相垂直方向上的差分可用来计算梯度的原理 采用对角线方向相邻两像素之差 即 然后根据式 1 9 2 计算出Roberts的梯度幅度值 它们的卷积算子为 Roberts检
  • mysql tmp下#sql_xxx_0.MYD 类文件占满空间的经历

    作为一名全职运维 随时会碰到各种问题 今天晚上收到紧报警 一台数据库服务器磁盘空间使用快速从80 使用率到90 我们的数据库都是 gt 2T的磁盘 意识到这肯定是比较严重问题马上上线处理 状况如下 root mysql node1 tmp
  • 实用高效的寻路算法——A*寻路算法的实现及优化思路

    前言 寻路是游戏比较重要的一个组成部分 因为不仅AI还有很多地方 例如RTS游戏里操控人物点到地图某个点 然后人物自动寻路走过去 都需要用到自动寻路的功能 本文将介绍一个经常被使用且效率理想的寻路方法 A 寻路算法 并且提供额外的优化思路
  • CMake中list的使用

    CMake中的list命令用于列表操作 其格式如下 Reading list LENGTH
  • Windows部署Alpaca-lora

    Alpaca Lora 是一个轻量级的语言模型 可以实现基本的语言类对话 本文先后在两台机器上进行了配置 行文旨在介绍将此模型部署至Windows系统上时所遭遇的各种困扰 机器1配置 NVIDIA TITAN Xp 机器2配置 RTX309
  • Spring是什么?

    什么是Spring 我知道你现在可能迫不及待地想要开始编写Spring应用了 我向你保证 在本章结束之前 你肯定能够开发一个简单的Spring应用 但首先 我将使用Spring的一些基础概念为你搭建一个舞台 帮助你理解Spring是如何运转
  • ubuntu下nginx停止、启动、重启

    转载自 https www cnblogs com xiaoL p 6964217 html nginx s reload 修改配置后重新加载生效 nginx s reopen 重新打开日志文件 nginx t c path to ngin
  • python标书制作辅助docx

    from docx import Document def handle path r d a docx 表格在哪个位置 文件路径 document Document path 读入表格文件 tables document tables 获
  • 通向Golang的捷径【7. 数组和 slice】

    从本章开始 将学习一些可包含一组元素的数据结构 也被称为数据集合 比如数组 切片 slice 和 map 这很显然是受到 Python 语言的影响 数组类型会使用 符号 这也是大多数编程语言的基本类型 Go 语言的数组与其他语言基本类似 但
  • android常见的monkey命令

    adb shell monkey p 包名 v 9000000 adb shell ps grep monkey 查找到monkey pid adb shell kill 刚才查到的进程号 指定一个包 adb shell monkey p
  • PySpark 连接Hive

    文章目录 简介 环境搭建与效果演示 更细节的搭建方法 搭建HDFS Spark或hive的前提 已经有了远程可访问的测试集群 搭建hadoop2 7 2 修改hadoop配置 格式化hdfs 测试 搭建spark 2 4 5 解压hive
  • C++成员函数末尾const关键字…

    原文地址 C 成员函数末尾const关键字的作用 作者 olym 1 gt 尽管函数名和参数列表都相同 void foo const成员函数是可以与void foo 并存的 可以形成重载 我们假设调用语句为obj foo 如果obj为non
  • git常用命令与常见面试题总结

    目录 1 git框架介绍 2 列举工作中常用的几个git命令 3 提交时发生冲突 如何解决 4 新建git功能分支的步骤 5 说明GIT合并的两种方法以及区别 6 Git提交代码的步骤 7 idea集成git 7 1 File gt set
  • IDEA开发及运行第一个Android项目

    IDEA自动下载SDK Gradle 保证能访问网络 原来eclipse能使用的sdk 配到idea报错 就换成自动下载最新的了 之前没成功可能是我防火墙禁用了上网 新建项目 提示安装SDK 等待下载完成 继续建项目 选择手机或平板及目标设
  • JVM实战:JVM内存分配策略

    JVM运行时数据区 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域 这些区域都有各自的用途 以及创建和销毁的时间 有的区域随着虚拟机进程的启动而存在 有些区域则依赖用户线程的启动和结束而建立和销毁 Ja