FULL PRODUCT VERSION : java version "1.7.0" Java(TM) SE Runtime Environment (build 1.7.0-b147) Java HotSpot(TM) Client VM (build 21.0-b17, mixed mode, sharing) (I verified that this is still a problem in: http://hg.openjdk.java.net/jdk7u/jdk7u/) FULL OS VERSION : This is a problem on ALL OS's. A DESCRIPTION OF THE PROBLEM : Consider the following in hotspot/src/share/vm/services: When a garbage collection is complete, GCMemoryManager::gc_end gets called (in memoryManager.cpp). This method completes the stats within the _current_gc_stat object. As the method proceeds, it makes a call to GCNotifier::pushNotification (in gcNotifier.cpp) BEFORE it moves _current_gc_stat into _last_gc_stat. The problem with this is that GCNotifier::pushNotification makes a call to GCMemoryManager::get_last_gc_stat, which always has the LAST set of stats (and bogus data if it's the first call). Any java app that subscribes for GC notifications consistently gets stats that is 1 collection old (with the 1st set of stats bogus). THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: Did not try THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Did not try STEPS TO FOLLOW TO REPRODUCE THE PROBLEM : Run the provided source code: java -XX:+PrintGC -XX:+PrintGCTimeStamps -XX:+TraceGen0Time -XX:+TraceGen1Time GCTest EXPECTED VERSUS ACTUAL BEHAVIOR : Notice in the following output that the id and secs consistently apply to the PREVIOUS collection, with the very 1st collection pointing to bogus data: 13:24:38.797 13:24:39.000 0.266: [GC 2457K->203K(15872K), 0.0011547 secs] 13:24:39.000 [ name:Copy, action:end of minor GC, cause:Allocation Failure, id:3932536, secs:0.000000 ] 13:24:40.000 13:24:41.000 2.266: [GC 4615K->269K(15872K), 0.0010946 secs] 13:24:41.000 [ name:Copy, action:end of minor GC, cause:Allocation Failure, id:1, secs:0.001194 ] 13:24:42.000 13:24:43.000 4.266: [GC 4444K->271K(15872K), 0.0009936 secs] 13:24:43.000 [ name:Copy, action:end of minor GC, cause:Allocation Failure, id:2, secs:0.001124 ] 13:24:44.000 13:24:45.000 6.266: [GC 4419K->272K(15872K), 0.0006629 secs] 13:24:45.000 [ name:Copy, action:end of minor GC, cause:Allocation Failure, id:3, secs:0.001024 ] 13:24:46.000 13:24:47.000 8.266: [GC 4422K->274K(15872K), 0.0004875 secs] 13:24:47.000 [ name:Copy, action:end of minor GC, cause:Allocation Failure, id:4, secs:0.000692 ] 13:24:48.000 [Accumulated GC generation 0 time 0.0045320 secs, 5 GC's, avg GC time 0.0009064] [Accumulated GC generation 1 time 0.0000000 secs, 0 GC's, avg GC time 0.0000000] REPRODUCIBILITY : This bug can be reproduced always. ---------- BEGIN SOURCE ---------- import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import javax.management.Notification; import javax.management.NotificationBroadcaster; import javax.management.NotificationListener; import javax.management.openmbean.CompositeData; import sun.misc.Perf; public class GcTest { private static final long FREQ = Perf.getPerf().highResFrequency(); private static void handleGcNotification(Notification notification) { if (notification == null) return; Object data = notification.getUserData(); if (!(data instanceof CompositeData)) return; long curTimeMs = System.currentTimeMillis(); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); StringBuilder sb = new StringBuilder(); sb.append(sdf.format(new Date(curTimeMs))); CompositeData cd = (CompositeData) data; sb.append(" [ name:"); sb.append(String.valueOf(cd.get("gcName"))); sb.append(", action:"); sb.append(String.valueOf(cd.get("gcAction"))); sb.append(", cause:"); sb.append(String.valueOf(cd.get("gcCause"))); Object gcInfo = cd.get("gcInfo"); if (gcInfo instanceof CompositeData) { cd = (CompositeData) gcInfo; sb.append(", id:"); sb.append(String.valueOf(cd.get("id"))); // NOTE: duration is in ticks, NOT millis. Bug: 7087969 Object duration = cd.get("duration"); double ticks = (duration instanceof Number) ? ((Number) duration).doubleValue() : -1; double secs = ticks > -1 ? ticks / FREQ : -1; sb.append(", secs:"); sb.append(String.format("%.6f", Double.valueOf(secs))); } sb.append(" ]"); System.out.println(sb); } public static void main(String[] args) { NotificationListener nl = new NotificationListener() { @Override public void handleNotification(Notification notification, Object handback) { handleGcNotification(notification); } }; List<GarbageCollectorMXBean> list = ManagementFactory.getGarbageCollectorMXBeans(); for (GarbageCollectorMXBean bean : list) { if (bean instanceof NotificationBroadcaster) ((NotificationBroadcaster) bean).addNotificationListener(nl, null, null); } int runtimeSecs = 10; int bytesSize = 1024 * 1024 * 2; SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); long startMs = System.currentTimeMillis(); long lastSec = 0; for (;;) { long curTimeMs = System.currentTimeMillis(); if (curTimeMs - startMs >= runtimeSecs * 1000) break; if (lastSec != curTimeMs / 1000) { System.out.println(sdf.format(new Date(curTimeMs))); lastSec = curTimeMs / 1000; byte[] bytes = new byte[bytesSize]; } } } } ---------- END SOURCE ----------
|