《剑指JVM》——第7章——堆空间分代原因与堆中对象的分配策略——堆7——运行时数据区篇18

🌈hello,你好鸭,我是Ethan,西安电子科技大学大三在读,很高兴你能来阅读。

✔️目前博客主要更新Java系列、项目案例、计算机必学四件套等。
🏃人生之义,在于追求,不在成败,勤通大道。加油呀!

🔥个人主页:Ethan Yankang
🔥推荐:史上最强八股文||一分钟看完我的几百篇博客

🔥温馨提示:划到文末发现专栏彩蛋   点击这里直接传送

🔥本篇概览:详细讲解了堆空间分代思想与堆中对象的分配策略以及详细讲解了jstat的栏目意义。。🌈⭕🔥


【计算机领域一切迷惑的源头都是基本概念的模糊,算法除外】


🔥 《剑指JVM》序言-CSDN博客

🔥 《剑指JVM》全书-CSDN博客


🌈章节引出

前一篇章:《剑指JVM》——第7章——图解对象分配到消亡的全过程8大步——堆5——运行时数据区篇17-CSDN博客

🌈章节速览


7.6 堆空间分代思想


为什么需要把Java 堆分代?不分代就不能正常工作了吗?经研究,不同的对象生命周期不同。70%~ 99%的对象是临时对象。其实不分代完全可以,分代的唯一理由就是优化性能

如果没有分代,那所有的对象都在一块,就如同把一个学校的人都关在一个教室。GC的时候要找到哪些对象没用,这样就需要对堆的所有区域进行扫描。而很多对象都是“朝生夕死”的,如果分代的话,把新创建的对象放到某一地方,当GC的时候先把这块存储“朝生夕死”对象的区域进行回收,这样就会腾出很大空间。


7.7 堆中对象的分配策略


如果对象在 Eden 区出生,并经过第一次 MinorGC后仍然存活,并且能被 Survivor 区容纳的话,将被移动到 Survivor 区中,并将对象年龄设为1。对象在 Survivor 区中每经过一次 Minor GC年龄就增加1岁,当它的年龄增加到一定程度时(默认为15岁,其实每个JVM、每个GC都有所不同),就会被晋升到老年代中。对象晋升老年代的年龄值,可以通过“-XX:MaTenuringThreshold”来设置,也会有其他情况直接分配对象到老年代

对象分配策略如下所示:

  • (1)优先分配到 Eden 区。
  • (2)大对象直接分配到老年代,在开发过程中应尽量避免程序中出现过多的大对象
  • (3)长期存活的对象分配到老年代。
  • (4)通过动态对象年龄判断,如果Survivor区中相同年龄的所有对象的大小总和大于 Survivor区的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无须等MaxTenuringThreshold 中要求的年龄。

代码演示:

下面我们通过代码演示大对象直接进入老年代的情景:

/**测试:大对象直接进入老年代
 *-Xms60m -Xmx60m -XX:NewRatio=2-XX:SurvivorRatio=8 -XX:+PrintGCDetails
 */

public class YoungOldAreaTest {
    public static void main(String[] args) {
        byte[] buffer = new byte[1024 * 1024 * 20]; // 20M
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

如图,下面的old uesr=21504>20480=20M,所有对象直接是分配到老年代,Eden存不下。


讲解jstat的栏目

 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC 
 CCSU   YGC     YGCT    FGC    FGCT     GCT 

这些是与 Java 虚拟机(JVM)的垃圾回收(GC)和内存管理相关的一些指标和参数:

  • s0c:年轻代中第一个 survivor(幸存区)的容量(单位:kb)。
  • s1c:年轻代中第二个 survivor(幸存区)的容量(单位:kb)。
  • s0u:年轻代中第一个 survivor(幸存区)目前已使用空间(单位:kb)。
  • s1u:年轻代中第二个 survivor(幸存区)目前已使用空间(单位:kb)。
  • ec:年轻代中 eden(伊甸园)的容量(单位:kb)。
  • eu:年轻代中 eden(伊甸园)目前已使用空间(单位:kb)。
  • oc:old 代(老年代)的容量(单位:kb)。
  • ou:old 代目前已使用空间(单位:kb)。
  • mc元数据区(metaspace)已经 committed 的内存空间。
  • mu:元数据区的使用量。
  • ccsc:压缩 class(compressed class space)committed 的内存空间。
  • ccsu压缩 class 已使用的内存空间。
  • ygc:从应用程序启动到采样时年轻代中垃圾回收(GC)的次数。
  • ygct:从应用程序启动到采样时年轻代中垃圾回收所用的时间(单位:s)。
  • fgc:从应用程序启动到采样时 old 代(全 GC)垃圾回收的次数。
  • fgct:从应用程序启动到采样时 old 代垃圾回收所用的时间(单位:s)。
  • gct:从应用程序启动到采样时垃圾回收用的总时间(单位:s)。


详细讲解CCSC、CCSU

在 64 位平台上,HotSpot 使用了两项压缩优化技术,分别是compressed object pointers(“compressed oops”)和compressed class pointers

compressed class pointers指的是,每个 Java 对象的头部有一个引用指向元空间(metaspace)中的klass结构。当使用了compressed class pointers时,这个引用是 32 位的值。为了找到真正的 64 位地址,需要加上一个基准值(base 值)。

然而,这项技术对klass的分配带来了问题:由于 32 位地址只能访问到 4GB 的空间,所以最大只允许 4GB 的klass地址。这意味着 JVM 需要向元空间分配一个连续的地址空间。但当通过调用系统接口malloc(3)mmap(3)从系统申请内存时,操作系统可能返回任意一个地址值,它并不能保证在 4GB 的范围内。

因此,只能用一个mmap()来申请一个区域单独用来存放klass对象。由于需要提前知道这个区域的大小,而且不能超过 4GB,这种方式是不能扩展的,因为这个地址后面的内存可能是被占用的。

只有klass结构有这个限制,对于其他的类元数据(class metadata)则没有必要,因为只有klass实例是通过 Java 对象头部中的压缩指针访问的,其他的元数据都是通过 64 位的地址进行访问,所以它们可以被放到任意的地址上。

基于此,将元空间分为了两个区域:

  • non-class part:包含其他所有的元数据;
  • class part:存放klass对象,需要一个连续的不超过 4GB 的内存,这个区域被称作compressed class space

compressed class space空间的大小可以通过-XX:CompressedClassSpaceSize指定,其默认值是 1GB。需要注意的是,这里的 1GB 并不是真正使用了操作系统的 1GB 物理内存,而是虚拟地址映射。

另外,有两个相关的开关:

  • -XX:+UseCompressedOops:允许对象指针压缩;
  • -XX:+UseCompressedClassPointers:允许类指针压缩。

它们默认都是开启的,也可以手动关闭。如果不允许类指针压缩,那么将没有compressed class space这个空间,并且-XX:CompressedClassSpaceSize这个参数无效。

同时,-XX:-UseCompressedClassPointers需要搭配-XX:+UseCompressedOops,但反过来则不是必须的。也就是说,可以只压缩对象指针,而不压缩类指针。这可能是因为从直觉上来说,压缩对象指针能获得较大的收益,相对更为重要。

此外,对象指针压缩要求堆小于 32GB,如果堆大于等于 32GB,那么对象指针压缩和类指针压缩都会被关闭。

在使用jstat命令查看 Java 虚拟机的统计信息时,ccsc(Compressed Class Space Committed)表示压缩类空间(compressed class space)已提交的内存空间大小。它反映了当前实际为compressed class space分配的内存量。通过观察ccsc的值,可以了解到compressed class space的使用情况,以便进行内存管理和优化。如果发现ccsc的值接近或达到了设置的上限,可能需要考虑调整相关的内存参数,或者优化类的数量和大小等,以避免出现内存不足的情况。



💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖

热门专栏推荐

🌈🌈计算机科学入门系列                     关注走一波💕💕

🌈🌈CSAPP深入理解计算机原理        关注走一波💕💕

🌈🌈微服务项目之黑马头条                 关注走一波💕💕

🌈🌈redis深度项目之黑马点评            关注走一波💕💕

🌈🌈JAVA面试八股文系列专栏           关注走一波💕💕

🌈🌈JAVA基础试题集精讲                  关注走一波💕💕   

🌈🌈代码随想录精讲200题                  关注走一波💕💕


总栏

🌈🌈JAVA基础要夯牢                         关注走一波💕💕  

🌈🌈​​​​​​JAVA后端技术栈                          关注走一波💕💕  

🌈🌈JAVA面试八股文​​​​​​                          关注走一波💕💕  

🌈🌈JAVA项目(含源码深度剖析)    关注走一波💕💕  

🌈🌈计算机四件套                               关注走一波💕💕  

🌈🌈数据结构与算法                           ​关注走一波💕💕  

🌈🌈必知必会工具集                           关注走一波💕💕

🌈🌈书籍网课笔记汇总                       关注走一波💕💕         



📣非常感谢你阅读到这里,如果这篇文章对你有帮助,希望能留下你的点赞👍 关注❤收藏✅ 评论💬,大佬三连必回哦!thanks!!!
📚愿大家都能学有所得,功不唐捐!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值