JVM运行时内存管理之线程共享

  接上JVM运行时内存管理之线程私有继续分享JVM运行时内存管理中线程共享部分:

  一、堆

    对于Java应用程序来说, Java堆(Java Heap) 是虚拟机所管理的内存中最大的一块。 Java堆是被所 有线程共享的一块内存区域, 在虚拟机启动时创建。 此内存区域的唯一目的就是存放对象实例, Java 世界里几乎所有的对象实例都在这里分配内存。
                

    1、堆大小设置

      内存大小-Xmx/-Xms,使用示例: -Xmx20m -Xms5m

                

                

      在测试代码中新增如下语句,申请内存分配:

                

                

    2、堆的分类

    JVM中存储java对象可以被分为两类:

      1)年轻代(Young Gen)年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁。年轻代分成1Eden Space2Suvivor Spacefrom to)。

       2)年老代(Tenured Gen)年老代主要存放JVM认为生命周期比较长的对象(经过几次的Young Gen的垃圾回收后仍然存在),内存大小相对会比较大,垃圾回收也相对没有那么频繁。

                

    通过命令jstat(可以参考JVM调优维护之常用命令)可以看到如下图信息:jstat -gc pid  

   

      S0C: Current survivor space 0 capacity (KB).

      S1C: Current survivor space 1 capacity (KB).

      S0U: Survivor space 0 utilization (KB).

      S1U: Survivor space 1 utilization (KB).

      EC: Current eden space capacity (KB).

      EU: Eden space utilization (KB).

      OC: Current old space capacity (KB).

      OU: Old space utilization (KB).

      MC: Metaspace Committed Size (KB).

      MU: Metaspace utilization (KB).

      CCSC: Compressed class committed size (KB).

      CCSU: Compressed class space used (KB).

      YGC: Number of young generation garbage collection (GC) events.

      YGCT: Young generation garbage collection time.

      FGC: Number of full GC events.

      FGCT: Full garbage collection time.

      GCT: Total garbage collection time.

    其中新生代:

                

    老年代:  

                

    配置新生代和老年代堆结构占比:

      默认 -XX:NewRatio=2 , 标识新生代占1 , 老年代占2 ,新生代占整个堆的1/3

      修改占比 -XX:NewPatio=4 , 标识新生代占1 , 老年代占4 , 新生代占整个堆的1/5

      Eden空间和另外两个Survivor空间占比分别为8:1:1,可以通过操作选项 -XX:SurvivorRatio 调整这个空间比例。 比如 -XX:SurvivorRatio=8。

      几乎所有的java对象都在Eden区创建, 80%的对象生命周期都很短,创建出来就会被销毁。

    3、堆分配——分配对象的流程:

                

                

                

    4、GC

      上面分析堆的分类,总是出现gc。这是因为Java 中的堆也是 GC 收集垃圾的主要区域(参考https://docs.oracle.com/en/graalvm/jdk/17/docs/reference-manual/native-image/optimizations-and-performance/MemoryManagement/)。

  二、Metaspace

    其参数设置如下:

    -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。

    -XX:MaxMetaspaceSize,最大空间,默认是没有限制的。如果没有使用该参数来设置类的元数据的大小,其最大可利用空间是整个系统内存的可用空间JVM也可以增加本地内存空间来满足类元数据信息的存储。但是如果没有设置最大值,则可能存在bug导致Metaspace的空间在不停的扩展,会导致机器的内存不足;进而可能出现swap内存被耗尽;最终导致进程直接被系统直接kill掉。如果设置了该参数,当Metaspace剩余空间不足,会抛出:java.lang.OutOfMemoryError: Metaspace space

    -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集

    -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

     查看该区值:

                

                

    最大空间是不是很大?因为其占用的是操作系统本地内存(而非JVM设置的内存大小),最大可利用空间是整个系统内存的可用空间。

    还可以通过jstat命令,如下:jstat -gcmetacapacity

                

    如上图可以看到元数据区的分类,如下:

      MCMN: Minimum metaspace capacity (KB).

      MCMX: Maximum metaspace capacity (KB).

      MC: Metaspace Committed Size (KB).

      CCSMN: Compressed class space minimum capacity (KB).

      CCSMX: Compressed class space maximum capacity (KB).

      YGC: Number of young generation GC events.

      FGC: Number of full GC events.

      FGCT: Full garbage collection time.

      GCT: Total garbage collection time.

    元数据区存放什么内容呢?

      运行时常量池

      方法元信息,JVM必须保存所有方法的以下信息,同域信息一样包括声明顺序如下:

         1. 方法名称方法的返回类型(或void

         2. 方法参数的数量和类型(按顺序)

         3. 方法的修饰符publicprivateprotectedstaticfinalsynchronizednative,abstract的一个子集

         4. 方法的字节码bytecodes、操作数栈、局部变量表及大小( abstractnative方法除外)

         5. 异常表( abstractnative方法除外)。每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引。

       类元信息:

        对每个加载的类型(类Class、接口 interface、枚举enum、注解 annotation),JVM必须在方法区中存储以下类型信息:

          ① 这个类型的完整有效名称(全名 = 包名.类名)

          ② 这个类型直接父类的完整有效名(对于 interface或是java.lang. Object,都没有父类)

          ③ 这个类型的修饰符( public, abstractfinal的某个子集)

          ④ 这个类型直接接口的一个有序列表

        域信息,即为类的属性,成员变量。JVM必须在方法区中保存类所有的成员变量相关信息及声明顺序,域的相关信息包括:域名称、域类型、域修饰符(pυblicprivateprotectedstaticfinalvolatiletransient的某个子集)。

  三、常量池

    上述提到了运行时常量池,那么两者有何关系?

      常量池:存放编译期间生成的各种字面量与符号引用;运行时常量池:常量池表在运行时的表现形式

      编译后的字节码文件中包含了类型信息、域信息、方法信息等。通过ClassLoader字节码文件的常量池中的信息加载到内存中,存储在了Metaspace的运行时常量池中。

               

posted on 2024-05-15 14:57  池塘里洗澡的鸭子  阅读(67)  评论(0编辑  收藏  举报