jvm源码阅读笔记[2]:你不知道的晋升阈值TenuringThreshold详解

【寄存器模型】二、前门访问与后门访问 1.后门访问可以更好的完成前门访问能够完成的事情:后门访问不消耗仿真时间,且消耗的运行时间也远小于前门访问的运行时间。 2.后门访问可以完成前门访问不能完成的事:对于某些特定的场景,如网络通信系统中的一些只读计数器,当要验证计数器进位归零的场景时,前门访问无法完成写寄存器操作,只能通过消耗仿真时间来使计数器到达特定的计数值。而后门访问可以直接对只读寄存器赋一个初值...... 阅读详情

    从零开始看源码,旨在从源码验证书上的结论,探索书上未知的细节。有疑问欢迎留言探讨
    个人源码地址:https://github.com/FlashLightNing/openjdk-notes
    还有一个openjdk6,7,8,9的地址:https://github.com/dmlloyd/openjdk
    jvm源码阅读笔记[1]:如何触发一次CMS回收
    jvm源码阅读笔记[2]:你不知道的晋升阈值TenuringThreshold详解
    jvm源码阅读笔记[3]:从内存分配到触发GC的细节
    jvm源码阅读笔记[4]:从GC说到vm operation
    jvm源码阅读笔记[5]:内存分配失败触发的GC究竟对内存做了什么?

    大家都知道年轻代中经历了多次GC之后仍然没有被回收的对象就会晋升到老年代中。它引入了一个“年龄”的状态:每经历一次GC,对象的年龄就会+1。而到了某一时刻,如果对象的年龄大于设置的一个晋升的阈值,该对象就会晋升到老年代中。对象的年龄最大只能是15,因为jvm中使用4个字节来表示对象的年龄。
    那么,问题来了,阈值都是怎么计算的呢?《深入理解Java虚拟机》中告诉我们,有这么一个参数:

-XX:MaxTenuringThreshold=6

表示设置对象最多经过6次GC而没有被回收的话,就会晋升到老年代中。
    但是正如这个参数中的max,它只是设置最大的阈值。而在运行的过程中,虚拟机会动态计算晋升的阈值。那么,它又是怎么来计算的呢?来看看下面的代码:
(地址:https://github.com/FlashLightNing/openjdk-notes/blob/master/src/share/vm/gc_implementation/shared/ageTable.cpp)
    

uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {
  //TargetSurvivorRatio默认50,意思是:在回收之后希望survivor区的占用率达到这个比例
  size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);
  size_t total = 0;
  uint age = 1;
  assert(sizes[0] == 0, "no objects with age zero should be recorded");
  while (age < table_size) {//table_size=16
    total += sizes[age];
    //如果加上这个年龄的所有对象的大小之后,占用量>期望的大小,就设置age为新的晋升阈值
    if (total > desired_survivor_size) break;
    age++;
  }

  uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;

//可以看到加了PrintTenuringDistribution参数,会打印age分布图
  if (PrintTenuringDistribution || UsePerfData) {

    //打印期望的survivor的大小以及新计算出来的阈值,和设置的最大阈值
    if (PrintTenuringDistribution) {
      gclog_or_tty->cr();
      gclog_or_tty->print_cr("Desired survivor size " SIZE_FORMAT " bytes, new threshold %u (max %u)",
        desired_survivor_size*oopSize, result, (int) MaxTenuringThreshold);
    }

    total = 0;
    age = 1;
    while (age < table_size) {
      total += sizes[age];
      if (sizes[age] > 0) {
        if (PrintTenuringDistribution) {//打印各个年龄所占的空间
          gclog_or_tty->print_cr("- age %3u: " SIZE_FORMAT_W(10) " bytes, " SIZE_FORMAT_W(10) " total",
                                        age,    sizes[age]*oopSize,          total*oopSize);
        }
      }
      if (UsePerfData) {
        _perf_sizes[age]->set_value(sizes[age]*oopSize);
      }
      age++;
    }
    if (UsePerfData) {
      SharedHeap* sh = SharedHeap::heap();
      CollectorPolicy* policy = sh->collector_policy();
      GCPolicyCounters* gc_counters = policy->counters();
      gc_counters->tenuring_threshold()->set_value(result);
      gc_counters->desired_survivor_size()->set_value(
        desired_survivor_size*oopSize);
    }
  }

  return result;//最终返回计算的阈值
}

    
    所以呢,代码是不会欺骗你的。从所有年龄=0的对象占的空间开始累加,如果加上年龄=n的所有对象的空间之后,大于survivor区的大小*TargetSurvivorRatio/100(TargetSurvivorRatio默认值为50),若大于这个大小,则结束循环,将n和MaxTenuringThreshold比较,若n小,则阈值为n。若n大,则只能去设置的最大阈值MaxTenuringThreshold。
    
    好,接下来让我们测试一下
    参数及代码:

参考:http://www.jianshu.com/p/f91fde4628a5
-verbose:gc -Xmx200M -Xmn50M -XX:TargetSurvivorRatio=60  -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:MaxTenuringThreshold=6 

    打印的GC日志截图:
    

    
    可以看到,阈值一开始是6,后面变成了2,然后又变成了6,最后又变成了4。
    让我们来计算一下是不是这样呢?
    年轻代是50m,然后eden和survivor区的比例默认是8:1:1.所以计算出来的survivor区的size=50*1024*1024*0.1=5242880byte。
    图中打印的Desired survivor size=3145728byte.
    计算出来的:5242880byte*TargetSurvivorRatio/100=5242880*60/100=3145728!
    正好相等,没有一点点偏差!!
    接下来算一下为什么第2次GC的时候,阈值变成2了呢?
    age=1的大小+age=2的大小=2622024+2115896=4737920>3145728(Desired survivor size)。所以参考上面的代码,阈值=2。
再来看第3次为什么=6了?
    第3次GC时,只有一个age=3的对象,所占大小=2622024<3145728。所以在循环添加时会一直到age最大值15。注意是个while循环和break的条件。

while (age < table_size) {//table_size=16
    total += sizes[age];
    //如果加上这个年龄的所有对象的大小之后,占用量>期望的大小,就设置age为新的晋升阈值
    if (total > desired_survivor_size) break;
    age++;
  }

    
    然后又根据以下代码:
    

uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;

    所以阈值变成了6。
    后面的就不一一分析了。
    在这里,总结一下就是:

年轻代晋升到老年代的年龄阈值是由参数TargetSurvivorRatio和参数MaxTenuringThreshold影响的。
MaxTenuringThreshold决定了晋升的最大阈值。
TargetSurvivorRatio影响运行过程中虚拟机计算的晋升阈值。
假设晋升阈值为n,那么一定满足以下条件:所有年龄<=n的对象大小之和 < survivor区的大小*TargetSurvivorRatio%

    不知道说到这里,我讲清楚没有…

    但是,这种调整晋升阈值的方式只适用于defNew,parNew年轻代收集器。对于ParallelScavenge收集器,还另有一番天地。
(地址:https://github.com/FlashLightNing/openjdk-notes/blob/master/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.cpp)
    PSAdaptiveSizePolicy.cpp中有这样一段计算晋升阈值的代码

    
    以及这么一段加减的代码:
    

    可以看到,对于ps来说,调整晋升的阈值是通过判断收集器young gc和full gc的耗时的。如果young gc太频繁,虚拟机认为可以降低晋升阈值从而减少年轻代的对象,让更多的对象晋升到老年代,达到减少young gc的时间。
    反之,如果full gc太频繁了,虚拟机就提高晋升的阈值,减少晋升到老年代的对象,从而控制full gc的时间。
_threshold_tolerance_percent=1.0 + ThresholdTolerance/100.0.
ThresholdTolerance默认值为10.也就是说

young_gc_time>full_gc_time*1.1,则threshold降低。
full_gc_time>young_gc_time*1.1,则threshold提高。

    但是需要注意的坑是:如果你的应用在没有用PS之前只是young gc频繁,而基本没有full gc的话,在用了PS之后,因为young gc频繁(比如1秒钟或者几秒钟一次)而导致该阈值会不断减少,最终减到1.这会导致什么问题呢?所有年龄>1的对象都会晋升到老年代,引起没必要的full gc。也许只有等到full gc很频繁之后,才会提高晋升的阈值。
    参考这篇文章:http://blog.youkuaiyun.com/lirenzuo/article/details/77529025

    有没有办法解决呢?有。关闭UseAdaptiveSizePolicy即可。

-XX:-UseAdaptiveSizePolicy

    顺便要说的是,如果用的ps,那么因为没有用ageTable里面的那种计算方法,就算加了PrintTenuringDistribution也就不会打印对象age分布图。而在用CMS的时候,加上该参数,因为年轻代用的parNew收集器,阈值计算用的是ageTable的算法,所以会打印age分布图。还有一点是,在cms下,UseAdaptiveSizePolicy参数是默认关闭的。而在PS下是打开的。
    

    从零开始看源码,旨在从源码验证书上的结论,探索书上未知的细节。有疑问欢迎留言探讨
    个人源码地址:https://github.com/FlashLightNing/openjdk-notes
    还有一个openjdk6,7,8,9的地址:https://github.com/dmlloyd/openjdk
    jvm源码阅读笔记[1]:如何触发一次CMS回收
    jvm源码阅读笔记[2]:你不知道的晋升阈值TenuringThreshold详解
    jvm源码阅读笔记[3]:从内存分配到触发GC的细节
    jvm源码阅读笔记[4]:从GC说到vm operation
    jvm源码阅读笔记[5]:内存分配失败触发的GC究竟对内存做了什么?

机器学习(十九):梯度提升回归(GBR) 机器学习中的梯度提升回归算法原理与案例实战。 阅读详情

相关推荐

【TELEMAC V8P4】TELEMAC-2D 用户手册核心概念与应用场景速览

本文深入解析了TELEMAC-2D水动力模型的核心概念与应用。作为基于二维浅水方程的开源数值模拟工具,TELEMAC-2D广泛应用于河流动力学、洪水演进、污染物输运及泥沙运动模拟。文章结合用户手册与实战经验,详细拆解了其理论框架、关键物理过程(如湍流模型、干湿网格处理),并提供了从模型搭建、文件配置到典型场景(如工程评估、污染应急、洪水模拟)的实用指南,帮助用户快速掌握这一强大工具。

weixin_29200741的博客 163

MaxTenuringThreshold 和 TargetSurvivorRatio参数说明

-XX:MaxTenuringThreshold   晋升年龄最大阈值,默认15。在新生代中对象存活次数(经过YGC的次数)后仍然存活,就会晋升到老年代。每经过一次YGC,年龄加1,当survivor区的对象年龄达到TenuringThreshold时,表示该对象是长存活对象,就会直接晋升到老年代。 -XX:TargetSurvivorRatio   设定survivor区的目标使用率。默认...

z69183787的专栏 1795

phaser3适配微信小游戏

phaser3适配微信小游戏

SilenzioX的博客 1805

(源码分析)关于gc日志中Desired Survivor的疑问和对象晋升老年代的小结

问题背景 (下面的所有内容都是根据书上的Serial/Serial Old收集器下的情况) 在《深入理解JVM》一书中的——3.6.3长期存活的对象将进入老年代的介绍中, 一个例子的jvm参数中加了这一行 -XX:+printTenuringDestribution 意思是希望每次新生代gc后,可以跟踪Survivor区中的对象的年龄分布。 然后还设置了 -XX:MaxTenur...

z69183787的专栏 2907

揭秘Synopsys EDA中的AI黑科技:DSO.ai如何改变传统芯片设计流程

本文深入探讨了Synopsys EDA中的AI黑科技DSO.ai如何革新传统芯片设计流程。通过数据驱动的方法论、功耗优化革命和时序收敛智能加速,DSO.ai显著提升了设计效率和质量,实现了从经验驱动到AI驱动的范式转变。文章还提供了实战指南,帮助团队跨越AI EDA的实施鸿沟。

weixin_29296241的博客 527

5G射频到底是啥?方案怎么搭?工作流程、SAW选型、PA选型一口气讲透(工程师秒懂版)

很多人一提 5G 射频,脑子里先冒出来的是几个零件名:👉 PA👉 LNA👉 SAW👉 天线👉 开关但真正做方案时,这些东西不是一堆分立器件,而是一条完整的“信号流水线”。更准确地说,。Qualcomm 对 RFFE 的定义就是“把信息转换成可发送的射频信号,并把接收到的信号转换回有用信息”的那一整套组件。

weixin_43199439的博客 536

Java 新生代晋升为老年代的四种情况

大内存对象. 当某个对象占用的内存达到阈值时, 可能会被晋升老年代 年龄大的对象. 对象年龄达到阈值后, 会被晋升老年代 survivor 区满了, 对象会晋升为老年代. minor gc 后, eden 和 survivor 区会拷贝到另外一个 survivor 区 动态年龄. 当某个年龄和以下年龄的对象占比超过 survivor 50%, 那么该年龄及以上的对象会晋升为老年代 ...

艾斯比 1849

ZW3D 二次开发-制作插件

1.Windows 7 SP1 或 Windows 10 或 Windows 11(推荐 Windows 11)2.Visual studio 2017-2022 (或 C/C++的任何其他集成开发环境)(推荐 Visual studio 2022)3.ZW3D 2023 或以上版本(建议使用与 ZW3D 一致的 API 的版本)4.Qt5.9.7(for Windows)博主学习使用环境的是:Windows 11 Version 24H2 + Visual studio 2022 + Qt5.9.7(fo

fhqiwmc的博客 812

JVM实用参数(五)新生代垃圾回收

本部分,我们将关注堆(heap) 中一个主要区域,新生代(young generation)。首先我们会讨论为什么调整新生代的参数会对应用的性能如此重要,接着我们将学习新生代相关的JVM参数。 单纯从JVM的功能考虑,并不需要新生代,完全可以针对整个堆进行操作。新生代存在的唯一理由是优化垃圾回收(GC)的性能。更具体说,把堆划分为新生代和老年代有2个好处:简化了新对象的分配(只在新生代分配内存),可以更有效的清除不再需要的对象(即死对象)(新生代和老年代使用不同的GC算法) 通过广泛研究面向对象实现的应

feel369的博客 602

动态规划之【树形DP】第3课:树形DP应用案例实践2

动态规划之【树形DP】第3课:树形DP应用案例实践2

王老师青少年编程 766

Windows版TASSEL5的下载及安装

TASSEL5是一款用于研究表型和基因型关系的软件,由美国康奈尔大学Buckler实验室所研发。,选择Windows环境安装包下载并安装。1.安装成功后TASSEL5界面。

沉香best的博客 8188

资源收集贴

一、IntelliJ IDEA导航特性Top20 (介绍了很多使用IntelliJ IDEA的实用方法) IntelliJ IDEA导航特性Top20 二、提高mysql千万级大数据SQL查询优化30条经验(Mysql优化需要考虑的点) 提高mysql千万级大数据SQL查询优化30条经验(Mysql索引优化注意) ...

让兔子飞得更高 397

【四足机器人项目实战】四足机器人支撑腿反作用力规划之VMC解耦控制方法

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录系列文章目录前言一、基于模型的工作空间控制框图二、原理三、缺点 前言 认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长! 本文先对未简化的动力学模型规划反作用力+运动学模型雅可比+虚功原理规划关节扭矩方法做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章 提示:以下是本篇文章正文内容 一、基于模型的工作空间控制框图 . . 二、原理 基于机器人的动力学模

qq_35635374的博客 6598

jvisualvm安装Visual GC插件

2.1)Compile Time(编译时间):6368compiles 表示编译总数,4.407s表示编译累计时间。一个脉冲表示一次JIT编译,窄脉冲表示持续时间短,宽脉冲表示持续时间长。2.2)Class Loader Time(类加载时间): 20869loaded表示加载类数量, 139 unloaded表示卸载的类数量,40.630s表示类加载花费的时间2.3)GC Time(GC Time):2392collections表示垃圾收集的总次数,37.454s表示垃圾。

博客 2149

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

jvm面试

jvm面试常问

a250353831的博客 1310

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

收藏:

XX:MaxTenuringThreshold   晋升年龄最大阈值,默认15。在新生代中对象存活次数(经过YGC的次数)后仍然存活,就会晋升到老年代。每经过一次YGC,年龄加1,当survivor区的对象年龄达到TenuringThreshold时,表示该对象是长存活对象,就会直接晋升到老年代。 -XX:TargetSurvivorRatio   设定survivor区的目标使用率。默认50,...

jackyrongvip的专栏 723

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

JVM - 虚拟机参数

一、堆内存 1、什么是堆内存 对于大多数应用来说,Java 堆(Java Heap)是Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。 此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。 堆内存是所有线程共有的,可以分为两个部分:年轻代和老年代。 下图中的Perm代表的是永久代,但是注意永久代并不属于堆内存中的一部分...

liu20111590的专栏 434

JVM调优工具

前言 给一个系统定位问题的时候,知识、经验是关键基础,数据是依据,工具是运用知识处理数据的手段。这里说的数据包括:运行日志、异常堆栈、GC日志、线程快照、堆转储快照等。经常使用适当的虚拟机监控和分析的工具可以加快我们分析数据、定位解决问题的速度。 目录 一、jps:虚拟机进程状况工具 二、jstat:虚拟机统计信息监视工具 三、jmap:Java内存印象工具 四、jhat:虚拟机堆转储快照分析工具...

damokelisijian866的博客 513

Hotspot JVM常用选项

一、选项的分类 Hotspot JVM提供以下三大类选项: 1. 标准选项:这类选项的功能是很稳定的,在后续版本中也不太会发生变化,即使有变化也必须保证向后兼容。运行java或java -help可以看到所有的标准选项。所有的标准选项都是以“-”开头,比如-version,-server等。 2. X选项:比如-Xms。这类选项以“-X”开头,它们也被称为X选项。运行java -X命令可以看...

Saintyyu的博客 788

JVM常用调优参数 ——JVM篇

JVM常用性能调优参数详解 ​ 在学习完整个JVM内容后,其实目标不仅是学习了解整个JVM的基础知识,而是为了进行JVM性能调优做准备,所以以下的内容就是来说说JVM性能调优的知识。 一、性能调优 ​ 性能调优包含多个层次,比如:架构调优、代码调优、JVM调优、数据库调优、操作系统调优等等。 ​ 架构调优和代码调优是JVM调优的基础,其中架构调优是对系统影响最大的。 性能调优基本上按照以下步骤进行: 明确优化目标 发现性能瓶颈 性能调优 通过监控及数据统计工具获得数据 确认是否达到目标 二、何时进

YING—country的博客 2万+

二、垃圾回收与内存分配, 来自《深入理解Java虚拟机》

所有内容均来自《深入理解Java虚拟机》, 部分比较拗口的内容用自己能明白理解的白话转述了下, 书本讲的非常详细, 这里只做简单引导试写出来, 供快速了解大致内容, 如果对原书知识理解有错, 请大家指出, 必虚心接受 1. 垃圾回收与内存分配 1.1 可达性分析算法 这个算法的基本思路就是通过一系列被称为GC Roots的根对象作为起始节点, 从这些节点开始, 根据引用关系向下搜索, 搜索过程走过的路径称为"引用链", 如果某个对象到根节点没有引用链相连接, 那么则判断这个对象已死. 1.1.1 那些可

yyyangyong996的博客 236

intellij java heap space_Java中9种常见的CMS GC问题分析与解决(二)

目前,互联网上 Java 的 GC 资料要么是主要讲解理论,要么就是针对单一场景的 GC 问题进行了剖析,对整个体系总结的资料少之又少。前车之鉴,后事之师,美团的几位工程师历时一年多的时间,搜集了内部各种 GC 问题的分析文章,并结合个人的理解做了一些总结,希望能起到“抛砖引玉”的作用。写在前面| 本文主要针对 Hotspot VM 中“CMS + ParNew”组合的一些使用场景进行总结。重点通...

weixin_39915308的博客 446

深入JVM系列(二)之GC机制、收集器与GC调优

一、回顾JVM内存分配   需要了解更多内存模式与内存分配的,请看 深入JVM系列(一)之内存模型与内存分配     1.1、内存分配: 1、对象优先在EDEN分配2、大对象直接进入老年代 3、长期存活的对象将进入老年代 4、适龄对象也可能进入老年代:动态对象年龄判断 动态对象年龄判断: 虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold才能晋升到老年代,...

weixin_33910137的博客 412

JVM性能调优

JVM性能调优 一、JVM内存组成及分配 组成图 方法栈&本地方法栈:线程创建时产生,方法执行时生成栈帧 方法区:存储类的元数据信息 常量等 堆:java代码中所有的new操作 native Memory(C heap):Direct Bytebuffer JNI Compile GC; 堆内存组 组成 详解 Young...

爱测未来团队博客 2069

JVM参数jdk1.8.0_202

jdk1.8.0_202\bin\java.exe" -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial [Global flags] intx ActiveProcessorCount = -1 {product} uintx AdaptiveSizeDecrementScaleFactor = 4

bytedance的博客 1049

JVM 动态年龄判断规则

JVM 动态年龄判断规则 对象进入老年代的动态年龄判断规则(动态晋升年龄计算阈值):Minor GC 时,Survivor 中年龄 1 到 N 的对象大小超过 Survivor 的 50% 时,则将大于等于年龄 N 的对象放入老年代。 《深入理解 Java 虚拟机》书中对动态年龄判断规则的解释大致是:同龄对象大小超过 50% 时,则将大于等于该年龄的对象放入老年代。 以前看到这块的时候一直觉得有点不太理解,因为如果是同龄对象的话,那么这批放入老年代的对象就必然是 1 岁,因为如果在 1 岁的时候没有达到 5

chinese_cai的博客 3069
上一篇: jvm源码阅读笔记[1]:如何触发一次CMS回收
下一篇: jvm源码阅读笔记[3]:从内存分配到触发GC的细节
foolishAndStupid
博客等级 码龄12年 53粉丝 50原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值