首页 > 系统相关 >4 内存分配和回收策略

4 内存分配和回收策略

时间:2022-10-17 20:11:31浏览次数:78  
标签:eden 对象 回收 XX GC 内存 分配

目录

扩展:Gc日志分析工具
GC日志分析工具-GCEasy
GC日志分析神器-GCEasy详解

1 自动内存管理

Java技术体系的自动内存管理,最根本的目标是自动化地解决两个问题:分配与回收

  1. 自动给对象分配内存
  2. 自动回收分配给对象的内存

Java的对象内存分配的一般规则:

  1. 一般情况下,给对象分配堆内存;即时编译下,也会间接地分配栈内存
  2. 新生对象通常会分配在新生代中,少数情况下(例如对象大小超过一定阈值)也可能会直接分配在老年代。
  3. 《Java虚拟机规范》并未规定新对象的创建和存储细节,这取决于虚拟机当前使用的是哪一种垃圾收集器,以及虚拟机中与内存相关的参数的设定。

本章节所以用的JVM环境为:

JDK8默认:
    垃圾收集器:
        新生代使用 Parallel Scavenge:标记-复制算法
        老年代配合使用的是 Serial Old:标记-整理算法

2 对象优先在Eden分配

2.1 总结

  1. 新生代分为一个Eden区和两个survivor区,默认比例为是 8:1:1。(设置参数-XX:-UseAdaptiveSizePolicy可固定该比例)
  2. 对象优先被分配在eden区,eden区满了后会触发minor gc,把剩余存活的对象挪到为空的那块survivor区,下一次eden区满了后又会触发minor gc,把eden区和survivor区垃圾对象回收,把剩余存活的对象一次性挪动到另外一块为空的survivor区。
  3. 如果survivor区空间不足,通过担保机制挪动到老年代

2.2 验证过程

jvm参数

-Xms30M -Xmx30M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:-UseAdaptiveSizePolicy -XX:+PrintCommandLineFlags -XX:+PrintGCDateStamps

-Xms30M    #固定堆内存大小为30M
-Xmx30M
-Xmn10M   #新生代为分配10M,实际可用为9M
-XX:+PrintGCDetails
-XX:SurvivorRatio=8
-XX:-UseAdaptiveSizePolicy  #固定8:1:1
-XX:+PrintCommandLineFlags  #打印运行时jvm参数
-XX:+PrintGCDateStamps

特别注意:
未手动分配空间前,运行main方法占用:1476KB,即1.44MB

测试代码

public class GcDemo {
    public static void main(String[] args) {
        byte[] a1 = new byte[5 * 1024 * 1024];
        byte[] a2 = new byte[2 * 1024 * 1024];
    }
}

GC日志及分析

  1. [GC (Allocation Failure) :收集类型是:Minor GC (GC代表MinorGC,System.gc()代表Full GC)

  2. [PSYoungGen: 6432K->640K(9216K)] 翻译为 [新生代:新生代收集前内存使用量->新生代垃圾收集后内存使用量(新生代代可用内存总大小)],垃圾收集前新生代已使用 6432K(6.28M),垃圾收集后已使用640K,总可用大小为 9216K(9M) 。

    1. 6.28M:初始占用的1.44m+a1先被分配在eden区
    2. a2分配时,eden剩余空间不足,触发MinorGC,此时有1476K-640K被回收,a1仍被使用,不被回收。
    3. 9216K,MinorGC后,总共可用内存为eden+1个survivor区
  3. MinorGC后内存占用情况:

   PSYoungGen      total 9216K, used 2770K      #新生代已使用2770K包含:对象a2+剩余未回收的640K
     eden space 8192K, 26% used     # eden区总大小8192K,已使用26%,包含对象a2
     from space 1024K, 62% used   #S0总大小1024K,已使用62%,包含 剩余未回收的640K
     to   space 1024K, 0% used      #S1总大小1024K,已使用0%
 ParOldGen       total 20480K, used 5128K   #对象a1被搬移到老年代
    object space 20480K, 25% 

综上

  1. 未分配任何对象时,系统默认占用了1476KB
  2. 创建a1时,a1被分配在eden区
  3. 创建a2时,eden区剩余空间不足,触发MinorGC,同时survivor空间不足以存放a1,所以通过分配担保机制将a1提前转移到老年代去。同时将回收了一部分默认占用的1476K,未被回收的640K存放在其中一个survivor

用图例所示

3 大对象直接进入老年代

3.1 总结

  1. 在Java虚拟机中要避免大对象的原因是,在分配空间时,它容易导致内存明明还有不少空间时就提前触发垃圾收集,以获取足够的连续空间才能安置好它们,而当复 制对象时,大对象就意味着高额的内存复制开销。
  2. HotSpot虚拟机提供了-XX:PretenureSizeThreshold 参数,指定大于该设置值的对象直接在老年代分配,这样做的目的就是避免在Eden区及两个Survivor区 之间来回复制,产生大量的内存复制操作。

3.2 验证过程

如上:并未触发gc,而是直接分配在老年代

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

4.1 总结

  1. 虚拟机给每个对象定义了一个对 象年龄(Age)计数器,存储在对象头中
  2. 对象通常在Eden区里诞生,如果经过第一次 Minor GC后仍然存活,并且能被Survivor容纳的话,该对象会被移动到Survivor空间中,并且将其对象 年龄设为1岁
  3. 对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程 度(默认为15),就会被晋升到老年代中。
  4. 可以通过参数-XX: MaxTenuringThreshold 设置“年龄”最大值

4.2 验证过程

todo

5 动态对象年龄判定

5.1 总结

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

5.2 验证过程

todo

6 空间分配担保

6.1 总结

  1. 在发生Minor GC之前,虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那这一次Minor GC可以确保是安全的
  2. 如果不成立,则虚拟机会先查看- XX:HandlePromotionFailure参数的设置值是否允许担保失败;如果允许,那会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次Minor GC;尽管这次Minor GC是有风险的;如果小于,或者-XX: HandlePromotionFailure设置不允许冒险,那这时就要改为进行一次Full GC。

6.2 验证过程

todo

标签:eden,对象,回收,XX,GC,内存,分配
From: https://www.cnblogs.com/knowledgeispower/p/16800434.html

相关文章

  • k8s限制cpu和内存的策略
    1.概要最近发现一个服务cpu占用了100%,导致整个服务器崩溃的悲剧,我就想k8s如何能够限制cpu/内存等的时候了。研究了一下发现还真的可以,这篇做个记录2.POD级别2.1.配置d......
  • 内存凭什么比SSD贵?折腾多年DIY后我终于懂了
    虽然内存和SSD都是存储产品,但两者的待遇却完全不一样:8G内存可以轻松卖到200元的价格,而200元可以买到256GB的SSD,价格差达到了30倍,为什么内存有勇气卖这么贵呢?电商报价¥139¥2......
  • JAVA 服务内存占用过高的一次排查过程
    1.缘由一台线上机器内存耗尽,OOM导致服务注册的Mesh客户端被干掉了,大量服务调用异常。运维同事查看机器负载,发现我们组内一个Java服务占用的内存有点异常,启动命令-Xmx......
  • 人为提升服务器CPU、内存、硬盘使用率
    一、CPU使用率vikyd/go-cpu-load:GenerateCPUloadonWindows/Linux/Mac(github.com)所有CPU核心负载30%运行10秒钟./cpu-p30-t10所有CPU核心负载30%持续运......
  • 夯实基础之内存回收
    内核在给应用程序分配物理内存的时候,如果空闲物理内存不够,那么就会进行内存回收的工作,主要有两种方式:后台内存回收:在物理内存紧张的时候,会唤醒kswapd内核线程来回收内存......
  • 为什么Linux需要虚拟内存 [转载好文]
    操作系统中的CPU和主内存(Mainmemory)都是稀缺资源,所有运行在当前操作系统的进程会共享系统中的CPU和内存资源,操作系统会使用CPU调度器分配CPU时间1并引入虚拟内存......
  • 内存空间和内存地址
    内存地址:一个编号表示一个内存空间内存空间:计算器中存储器的容量是以字节(Byte)为单位,也就是说一个内存地址代表一个字节(8bit)的存储空间(1Byte==8bit)bit:位Byte:......
  • 第二季:6.GC垃圾回收算法和垃圾收集器的关系?分别是什么请你谈谈【Java面试题】
    第二季:6.GC垃圾回收算法和垃圾收集器的关系?分别是什么请你谈谈【Java面试题】​​前言​​​​推荐​​​​6.GC垃圾回收算法和垃圾收集器的关系?分别是什么请你谈谈​​​......
  • 【第五篇】Camunda系列-任务分配
    任务分配1.固定分配  在指派​​用户任务​​的审批人时。我们是直接指派的固定账号。但是为了保证流程设计审批的灵活性。我们需要各种不同的分配方式,所以这节我们就详细......
  • C++内存泄漏
        程序在堆中申请的动态内存,在程序使用完成时没有得到及时的释放。当这些变量的生命周期已结束时,该变量在堆中所占用的内存未能得到释放,从而就导致了堆中可使用的......