Module java.management

Interface MemoryPoolMXBean

所有超级接口:
PlatformManagedObject

public interface MemoryPoolMXBean extends PlatformManagedObject
内存池的管理接口。内存池代表Java虚拟机管理的内存资源,并由一个或多个内存管理器管理。

Java虚拟机具有一个或多个此接口实现类的实例。实现此接口的实例是一个MXBean,可通过调用ManagementFactory.getMemoryPoolMXBeans()方法或从platform MBeanServer方法获取。

用于在中唯一标识内存池的MXBean的ObjectName是:

java.lang:type=MemoryPool,name=池的名称
可通过调用PlatformManagedObject.getObjectName()方法获取。

内存类型

Java虚拟机具有用于对象分配的堆,还维护方法区和Java虚拟机执行的非堆内存。Java虚拟机可以有一个或多个内存池。每个内存池代表以下类型之一的内存区域:

内存使用监控

内存池具有以下属性:

1. 内存使用情况

getUsage()方法提供了内存池当前使用情况的估计。对于垃圾回收内存池,已使用内存量包括池中所有对象占用的内存,包括可达不可达对象。

通常,此方法是用于获取近似内存使用情况的轻量级操作。对于某些内存池,例如,当对象不是连续打包时,此方法可能是一个需要一些计算来确定当前内存使用情况的昂贵操作。实现应在这种情况下记录。

2. 峰值内存使用情况

Java虚拟机自启动或重置峰值以来维护内存池的峰值内存使用情况。峰值内存使用情况由getPeakUsage()方法返回,并通过调用resetPeakUsage()方法重置。

3. 使用阈值

每个内存池都有一个可管理的属性称为使用阈值,其默认值由Java虚拟机提供。默认值取决于平台。使用阈值可以通过setUsageThreshold方法设置。如果将阈值设置为正值,则在此内存池中启用使用阈值交叉检查。如果将使用阈值设置为零,则在此内存池上禁用使用阈值交叉检查。可以使用isUsageThresholdSupported()方法确定是否支持此功能。

Java虚拟机会在最合适的时间(通常在垃圾回收时)基于内存池执行使用阈值交叉检查。每个内存池维护一个使用阈值计数,每当Java虚拟机检测到内存池使用量超过阈值时,该计数将递增。

此可管理的使用阈值属性旨在监视内存使用量的增长趋势,开销较低。对于某些内存池,使用阈值可能不合适。例如,许多Java虚拟机实现中使用的常见垃圾回收算法是分代垃圾收集器,通过年龄将对象分隔为两个或更多代。大多数对象分配在最年轻代(例如幼儿园内存池)中。幼儿园内存池设计为填满,并在收集幼儿园内存池时将释放大部分内存空间,因为预计它主要包含短寿命对象,大部分在垃圾回收时不可达。在这种情况下,幼儿园内存池不支持使用阈值更为合适。此外,如果一个内存池中对象分配的成本非常低(例如,只是原子指针交换),则Java虚拟机可能不支持该内存池的使用阈值,因为与阈值比较的开销高于对象分配的成本。

可以使用轮询阈值通知机制监视系统的内存使用情况。

  1. 轮询

    应用程序可以通过调用所有内存池的getUsage()方法或支持使用阈值的内存池的isUsageThresholdExceeded()方法来持续监视其内存使用情况。下面是一个示例代码,其中有一个专门用于任务分发和处理的线程。在每个间隔,它将根据其内存使用情况确定是否应接收和处理新任务。如果内存使用超过其使用阈值,它将重新分配所有未完成的任务给其他虚拟机,并在内存使用量返回低于其使用阈值之前停止接收新任务。

           // 假设此池支持使用阈值。
           // 将阈值设置为myThreshold,超过该阈值时不应接受新任务。
           pool.setUsageThreshold(myThreshold);
           ....
    
           boolean lowMemory = false;
           while (true) {
              if (pool.isUsageThresholdExceeded()) {
                  // 可能内存不足,因此将任务重新分配给其他虚拟机
                  lowMemory = true;
                  redistributeTasks();
                  // 停止接收新任务
                  stopReceivingTasks();
              } else {
                  if (lowMemory) {
                      // 恢复接收任务
                      lowMemory = false;
                      resumeReceivingTasks();
                  }
                  // 处理未完成的任务
                  ...
              }
              // 休眠一段时间
              try {
                  Thread.sleep(sometime);
              } catch (InterruptedException e) {
                  ...
              }
           }
           

    上面的示例没有区分内存使用情况暂时下降到使用阈值以下的情况和内存使用情况在两次迭代之间仍然保持在阈值以上的情况。getUsageThresholdCount()方法返回的使用阈值计数可用于确定内存使用情况是否在两次轮询之间返回到阈值以下。

    下面展示另一个示例,如果内存池处于低内存状态并且在操作处理时间内忽略内存使用量的变化。

           // 假设此池支持使用阈值。
           // 将阈值设置为myThreshold,以确定在低内存条件下应用程序是否会采取某些操作。
           pool.setUsageThreshold(myThreshold);
    
           int prevCrossingCount = 0;
           while (true) {
               // 一个繁忙的循环,用于检测内存使用情况是否超过阈值。
               while (!pool.isUsageThresholdExceeded() ||
                      pool.getUsageThresholdCount() == prevCrossingCount) {
                   try {
                       Thread.sleep(sometime)
                   } catch (InterruptException e) {
                       ....
                   }
               }
    
               // 执行一些处理,例如检查内存使用情况并发出警告
               ....
    
               // 获取当前阈值计数。然后繁忙循环将忽略在处理过程中发生的任何阈值交叉。
               prevCrossingCount = pool.getUsageThresholdCount();
           }
           

  2. 使用阈值通知

    使用阈值通知将由MemoryMXBean发出。当Java虚拟机检测到内存池的内存使用量已达到或超过使用阈值时,虚拟机将触发MemoryMXBean发出一个使用阈值超出通知。直到使用量下降并再次超过阈值后,不会生成另一个使用阈值超出通知。

    下面是一个实现与上面第一个示例相同逻辑的示例代码,但使用使用阈值通知机制来检测低内存条件而不是轮询。在此示例代码中,收到通知后,通知侦听器会通知另一个线程执行实际操作,例如重新分配未完成的任务、停止接收任务或恢复接收任务。handleNotification方法应设计为执行非常少量的工作并立即返回,以避免延迟传递后续通知。耗时的操作应由单独的线程执行。通知侦听器可能会被多个线程同时调用;因此,侦听器执行的任务应适当同步。

           class MyListener implements javax.management.NotificationListener {
                public void handleNotification(Notification notification, Object handback)  {
                    String notifType = notification.getType();
                    if (notifType.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
                        // 可能内存不足,通知另一个线程
                        // 重新分配未完成的任务给其他虚拟机
                        // 并停止接收新任务。
                        lowMemory = true;
                        notifyAnotherThread(lowMemory);
                    }
                }
           }
    
           // 使用MemoryMXBean注册MyListener
           MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
           NotificationEmitter emitter = (NotificationEmitter) mbean;
           MyListener listener = new MyListener();
           emitter.addNotificationListener(listener, null, null);
    
           // 假设此池支持使用阈值。
           // 将阈值设置为myThreshold,超过该阈值时不应接受新任务。
           pool.setUsageThreshold(myThreshold);
    
           // 使用阈值检测已启用,并且通知将由MyListener处理。继续进行其他处理。
           ....
    
           

    MemoryMXBean何时发出阈值通知以及通知何时传递没有保证。当调用通知侦听器时,内存池的内存使用量可能已经超过使用阈值多次。MemoryNotificationInfo.getCount()方法返回通知构造时内存使用量已经超过使用阈值的次数。可以将其与getUsageThresholdCount()方法返回的当前使用阈值计数进行比较,以确定是否发生了这种情况。

4. 集合使用阈值

集合使用阈值是仅适用于某些垃圾收集内存池的可管理属性。在Java虚拟机在垃圾收集时通过回收内存池中未使用的对象来努力回收内存空间后,一些垃圾收集的内存池中仍将有一些字节处于使用状态。集合使用阈值允许为这些字节设置一个值,如果超过阈值,集合使用阈值超出通知将由MemoryMXBean发出。此外,集合使用阈值计数将递增。

isCollectionUsageThresholdSupported()方法可用于确定是否支持此功能。

Java虚拟机按内存池进行集合使用阈值检查。如果将集合使用阈值设置为正值,则启用此检查。如果将集合使用阈值设置为零,则在此内存池上禁用此检查。默认值为零。Java虚拟机在垃圾收集时执行集合使用阈值检查。

一些垃圾收集的内存池可能选择不支持集合使用阈值。例如,某个内存池仅由连续并发垃圾收集器管理。对象可以由某个线程在此内存池中分配,同时未使用的对象同时由并发垃圾收集器回收。除非存在明确定义的垃圾收集时间,即检查内存使用情况的最佳适当时间,否则不应支持集合使用阈值。

集合使用阈值旨在监视Java虚拟机在努力回收内存空间后的内存使用情况。集合使用情况也可以通过上述用于使用阈值的轮询和阈值通知机制类似的方式进行监视。

自1.5版本起:
1.5
参见:
  • Method Details

    • getName

      String getName()
      返回表示此内存池的名称。
      返回:
      此内存池的名称。
    • getType

      MemoryType getType()
      返回此内存池的类型。

      MBeanServer 访问:
      MemoryType的映射类型为String,值为MemoryType的名称。

      返回:
      此内存池的类型。
    • getUsage

      MemoryUsage getUsage()
      返回此内存池的内存使用估计。如果此内存池无效(即不再存在),则此方法返回null

      此方法请求Java虚拟机尽最大努力估计此内存池当前的内存使用情况。对于某些内存池,此方法可能是一个需要计算的昂贵操作以确定估计值。实现应在这种情况下记录。

      此方法设计用于监视系统内存使用情况和检测低内存条件。

      MBeanServer 访问:
      MemoryUsage的映射类型为CompositeData,具有MemoryUsage中指定的属性。

      返回:
      一个MemoryUsage对象;如果此内存池无效,则返回null
    • getPeakUsage

      MemoryUsage getPeakUsage()
      返回自Java虚拟机启动或重置峰值以来此内存池的峰值内存使用情况。如果此内存池无效(即不再存在),则此方法返回null

      MBeanServer 访问:
      MemoryUsage的映射类型为CompositeData,具有MemoryUsage中指定的属性。

      返回:
      代表峰值内存使用的MemoryUsage对象;如果此内存池无效,则返回null
    • resetPeakUsage

      void resetPeakUsage()
      将此内存池的峰值内存使用统计数据重置为当前内存使用量。
      抛出:
      SecurityException - 如果存在安全管理器且调用者没有ManagementPermission("control")。
    • isValid

      boolean isValid()
      检查此内存池在Java虚拟机中是否有效。一旦Java虚拟机将内存池从内存系统中移除,内存池将变为无效。
      返回:
      如果内存池在运行中的Java虚拟机中有效,则返回true;否则返回false
    • getMemoryManagerNames

      String[] getMemoryManagerNames()
      返回管理此内存池的内存管理器的名称。每个内存池至少由一个内存管理器管理。
      返回:
      一个String对象数组,每个对象是管理此内存池的内存管理器的名称。
    • getUsageThreshold

      long getUsageThreshold()
      返回此内存池的使用阈值值(以字节为单位)。每个内存池都有一个平台相关的默认阈值值。可以通过setUsageThreshold方法更改当前使用阈值。
      返回:
      此内存池的使用阈值值(以字节为单位)。
      抛出:
      UnsupportedOperationException - 如果此内存池不支持使用阈值。
      参见:
    • setUsageThreshold

      void setUsageThreshold(long threshold)
      如果此内存池支持使用阈值,则将此内存池的阈值设置为给定的threshold值。如果将阈值设置为正值,则在此内存池中启用使用阈值交叉检查。如果设置为零,则禁用使用阈值交叉检查。
      参数:
      threshold - 以字节为单位的新阈值值。必须为非负数。
      抛出:
      IllegalArgumentException - 如果threshold为负数或大于定义的此内存池的最大内存量。
      UnsupportedOperationException - 如果此内存池不支持使用阈值。
      SecurityException - 如果存在安全管理器且调用者没有ManagementPermission("control")。
      参见:
    • isUsageThresholdExceeded

      boolean isUsageThresholdExceeded()
      检查此内存池的内存使用是否达到或超过其使用阈值值。
      返回:
      如果此内存池的内存使用达到或超过阈值值,则返回true;否则返回false
      抛出:
      UnsupportedOperationException - 如果此内存池不支持使用阈值。
    • getUsageThresholdCount

      long getUsageThresholdCount()
      返回内存使用已超过使用阈值的次数。
      返回:
      内存使用已超过其使用阈值值的次数。
      抛出:
      UnsupportedOperationException - 如果此内存池不支持使用阈值。
    • isUsageThresholdSupported

      boolean isUsageThresholdSupported()
      检查此内存池是否支持使用阈值。
      返回:
      如果此内存池支持使用阈值,则返回true;否则返回false
    • getCollectionUsageThreshold

      long getCollectionUsageThreshold()
      返回此内存池的集合使用阈值值(以字节为单位)。默认值为零。可以通过setCollectionUsageThreshold方法更改集合使用阈值。
      返回:
      此内存池的集合使用阈值(以字节为单位)。
      抛出:
      UnsupportedOperationException - 如果此内存池不支持集合使用阈值。
      参见:
    • setCollectionUsageThreshold

      void setCollectionUsageThreshold(long threshold)
      将此内存池的集合使用阈值设置为给定的threshold值。当此阈值设置为正值时,Java虚拟机将在尽最适当的时间检查内存使用情况,以便在回收此内存池中未使用对象的努力后。

      如果将阈值设置为正值,则在此内存池中启用集合使用阈值交叉检查。如果设置为零,则禁用集合使用阈值交叉检查。

      参数:
      threshold - 新的集合使用阈值,以字节为单位。必须是非负数。
      抛出:
      IllegalArgumentException - 如果threshold为负数或大于此内存池的最大内存量(如果已定义)。
      UnsupportedOperationException - 如果此内存池不支持集合使用阈值。
      SecurityException - 如果存在安全管理器且调用者没有ManagementPermission("control")。
      参见:
    • isCollectionUsageThresholdExceeded

      boolean isCollectionUsageThresholdExceeded()
      测试在Java虚拟机最近努力回收的最近一次收集后,此内存池的内存使用量是否达到或超过其集合使用阈值。此方法不会请求Java虚拟机执行除其正常自动内存管理之外的任何垃圾回收。
      返回:
      如果此内存池的内存使用量在最近一次收集中达到或超过集合使用阈值,则返回true; 否则返回false
      抛出:
      UnsupportedOperationException - 如果此内存池不支持使用阈值。
    • getCollectionUsageThresholdCount

      long getCollectionUsageThresholdCount()
      返回Java虚拟机检测到内存使用量达到或超过集合使用阈值的次数。
      返回:
      内存使用量达到或超过集合使用阈值的次数。
      抛出:
      UnsupportedOperationException - 如果此内存池不支持集合使用阈值。
      参见:
    • getCollectionUsage

      MemoryUsage getCollectionUsage()
      返回Java虚拟机最近在此内存池中回收未使用对象时的内存使用情况。此方法不会请求Java虚拟机执行除其正常自动内存管理之外的任何垃圾回收。如果Java虚拟机不支持此方法,则返回null

      MBeanServer访问:
      MemoryUsage的映射类型是CompositeData,其属性如MemoryUsage中所指定。

      返回:
      表示Java虚拟机最近在回收未使用对象后此内存池的内存使用情况的MemoryUsage; 如果不支持此方法,则返回null
    • isCollectionUsageThresholdSupported

      boolean isCollectionUsageThresholdSupported()
      测试此内存池是否支持集合使用阈值。
      返回:
      如果此内存池支持集合使用阈值,则返回true; 否则返回false