Skip to content

Instantly share code, notes, and snippets.

@rednaxelafx
Created December 12, 2011 06:40
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rednaxelafx/1465445 to your computer and use it in GitHub Desktop.
Save rednaxelafx/1465445 to your computer and use it in GitHub Desktop.
Java 7's GC notification notes
$ groovysh
Groovy Shell (1.7.7, JVM: 1.7.0_01)
Type 'help' or '\h' for help.
----------------------------------------------------------------------------------------------------------------------------
groovy:000> import java.lang.management.*; import javax.management.*
===> [import java.lang.management.*; import javax.management.*]
groovy:000> ManagementFactory.methods.name.findAll { it =~ /Bean/ }
===> [getClassLoadingMXBean, getCompilationMXBean, getGarbageCollectorMXBeans, newPlatformMXBeanProxy, getThreadMXBean, getMemoryMXBean, getMemoryManagerMXBeans, getMemoryPoolMXBeans, getOperatingSystemMXBean, getPlatformMBeanServer, getPlatformMXBean, getPlatformMXBean, getPlatformMXBeans, getPlatformMXBeans, getRuntimeMXBean]
groovy:000> psScavengeBean = ManagementFactory.garbageCollectorMXBeans[0]
===> sun.management.GarbageCollectorImpl@12701389
groovy:000> psScavengeBean.class.methods.findAll { it.name =~ /Noti/ }
===> [public synchronized void sun.management.GarbageCollectorImpl.addNotificationListener(javax.management.NotificationListener,javax.management.NotificationFilter,java.lang.Object), public synchronized void sun.management.GarbageCollectorImpl.removeNotificationListener(javax.management.NotificationListener) throws javax.management.ListenerNotFoundException, public synchronized void sun.management.GarbageCollectorImpl.removeNotificationListener(javax.management.NotificationListener,javax.management.NotificationFilter,java.lang.Object) throws javax.management.ListenerNotFoundException, public javax.management.MBeanNotificationInfo[] sun.management.GarbageCollectorImpl.getNotificationInfo()]
groovy:000> psScavengeBean.addNotificationListener({ Notification n, Object o -> println Thread.currentThread(); Thread.dumpStack() } as NotificationListener, null, null)
===> null
groovy:000> 50000.times { [:] }
===> null
groovy:000> Thread[Service Thread,9,system]
java.lang.Exception: Stack trace
at java.lang.Thread.dumpStack(Thread.java:1342)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:88)
at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite$StaticMetaMethodSiteNoUnwrapNoCoerce.invoke(StaticMetaMethodSite.java:148)
at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite.call(StaticMetaMethodSite.java:88)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:40)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
at groovysh_evaluate$_run_closure1.doCall(groovysh_evaluate:3)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:88)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:273)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:886)
at groovy.lang.Closure.call(Closure.java:282)
at org.codehaus.groovy.runtime.ConvertedClosure.invokeCustom(ConvertedClosure.java:51)
at org.codehaus.groovy.runtime.ConversionHandler.invoke(ConversionHandler.java:82)
at $Proxy5.handleNotification(Unknown Source)
at sun.management.NotificationEmitterSupport.sendNotification(NotificationEmitterSupport.java:156)
at sun.management.GarbageCollectorImpl.createGCNotification(GarbageCollectorImpl.java:150)
quit

GC notifications are sent to the ServiceThread, as shown in the Groovy shell example in this gist. The ServiceThread is a JavaThread, so it can execute Java code.

To see GC notification in action, refer to a conversation in hotspot-gc-dev. Another thread of interest is the discussions on 7110173: GCNotifier::pushNotification publishes stale data.

In the HotSpot VM, GC notifications are pushed with GCNotifier::pushNotification(), in GCMemoryManager::gc_end(), which is in turn called by MemoryService::gc_end().

hotspot/src/share/vm/runtime/serviceThread.cpp

void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
  while (true) {
    bool sensors_changed = false;
    bool has_jvmti_events = false;
    bool has_gc_notification_event = false;
    JvmtiDeferredEvent jvmti_event;
    {
      // Need state transition ThreadBlockInVM so that this thread
      // will be handled by safepoint correctly when this thread is
      // notified at a safepoint.

      // This ThreadBlockInVM object is not also considered to be
      // suspend-equivalent because ServiceThread is not visible to
      // external suspension.

      ThreadBlockInVM tbivm(jt);

      MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
      while (!(sensors_changed = LowMemoryDetector::has_pending_requests()) &&
             !(has_jvmti_events = JvmtiDeferredEventQueue::has_events()) &&
              !(has_gc_notification_event = GCNotifier::has_event())) {
        // wait until one of the sensors has pending requests, or there is a
        // pending JVMTI event or JMX GC notification to post
        Service_lock->wait(Mutex::_no_safepoint_check_flag);
      }

      if (has_jvmti_events) {
        jvmti_event = JvmtiDeferredEventQueue::dequeue();
      }
    }

    if (has_jvmti_events) {
      jvmti_event.post();
    }

    if (sensors_changed) {
      LowMemoryDetector::process_sensor_changes(jt);
    }

    if(has_gc_notification_event) {
        GCNotifier::sendNotification(CHECK);
    }
  }
}

hotspot/src/share/vm/services/gcNotifier.cpp

void GCNotifier::sendNotification(TRAPS) {
  ResourceMark rm(THREAD);
  GCNotificationRequest *request = getRequest();
  if(request != NULL) {
    Handle objGcInfo = createGcInfo(request->gcManager,request->gcStatInfo,THREAD);

    Handle objName = java_lang_String::create_from_platform_dependent_str(request->gcManager->name(), CHECK);
    Handle objAction = java_lang_String::create_from_platform_dependent_str(request->gcAction, CHECK);
    Handle objCause = java_lang_String::create_from_platform_dependent_str(request->gcCause, CHECK);

    klassOop k = Management::sun_management_GarbageCollectorImpl_klass(CHECK);
    instanceKlassHandle gc_mbean_klass (THREAD, k);

    instanceOop gc_mbean = request->gcManager->get_memory_manager_instance(THREAD);
    instanceHandle gc_mbean_h(THREAD, gc_mbean);
    if (!gc_mbean_h->is_a(k)) {
      THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
                "This GCMemoryManager doesn't have a GarbageCollectorMXBean");
    }

    JavaValue result(T_VOID);
    JavaCallArguments args(gc_mbean_h);
    args.push_long(request->timestamp);
    args.push_oop(objName);
    args.push_oop(objAction);
    args.push_oop(objCause);
    args.push_oop(objGcInfo);

    JavaCalls::call_virtual(&result,
                            gc_mbean_klass,
                            vmSymbols::createGCNotification_name(),
                            vmSymbols::createGCNotification_signature(),
                            &args,
                            CHECK);
    if (HAS_PENDING_EXCEPTION) {
      CLEAR_PENDING_EXCEPTION;
    }

    delete request;
  }
}
package sun.management;
// ...

/**
 * Implementation class for the garbage collector.
 * Standard and committed hotspot-specific metrics if any.
 *
 * ManagementFactory.getGarbageCollectorMXBeans() returns a list
 * of instances of this class.
 */
class GarbageCollectorImpl extends MemoryManagerImpl
    implements GarbageCollectorMXBean {

    //...

    void createGCNotification(long timestamp,
                              String gcName,
                              String gcAction,
                              String gcCause,
                              GcInfo gcInfo)  {

        if (!hasListeners()) {
            return;
        }

        Notification notif = new Notification(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION,
                                              getObjectName(),
                                              getNextSeqNumber(),
                                              timestamp,
                                              gcName);
        GarbageCollectionNotificationInfo info =
            new GarbageCollectionNotificationInfo(gcName,
                                                  gcAction,
                                                  gcCause,
                                                  gcInfo);

        CompositeData cd =
            GarbageCollectionNotifInfoCompositeData.toCompositeData(info);
        notif.setUserData(cd);
        sendNotification(notif);
    }
package sun.management;
// ...

class MemoryManagerImpl extends NotificationEmitterSupport
    implements MemoryManagerMXBean
package sun.management;
// ...

/**
 * Abstract helper class for notification emitter support.
 */
abstract class NotificationEmitterSupport implements NotificationEmitter {

    // ...

    void sendNotification(Notification notification) {

        if (notification == null) {
            return;
        }

        List<ListenerInfo> currentList;
        synchronized (listenerLock) {
            currentList = listenerList;
        }

        final int size = currentList.size();
        for (int i = 0; i < size; i++) {
            ListenerInfo li = (ListenerInfo) currentList.get(i);

            if (li.filter == null
                || li.filter.isNotificationEnabled(notification)) {
                try {
                    li.listener.handleNotification(notification, li.handback);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new AssertionError("Error in invoking listener");
                }
            }
        }
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment