How to detect long gc from JVM?

How can I detect a GC ( Change or any stall) that exceeds a certain application timeout so that I can log a warning (or dynamically increase the timeout)?

Edit . I am not asking for alternatives or workarounds like monitoring. I am writing a library and I have no control over the environment or settings. While I will clearly document that library users should set an appropriate timeout, I still expect people to overlook that a few years later they change their jvm heap settings and forget to increase the timeouts. Support will be easier if I can warn the log of a possible pause that exceeds the configured timeouts. It doesn't have to be a perfect "good enough" detection to cut down on time spent on library users without setting a reasonable timeout.

Edit , and to be clear, the library works fine even if there is a large GC, but there is a good reason to have a well-chosen timeout that is designed to detect failure, so the library tries to connect to an alternate peer.

+1


source to share


4 answers


You can use the notification bean management and subscribe to GARBAGE_COLLECTION_NOTIFICATION , which in turn provide objects GcInfo

with the required statistics.



javax.management

the javadocs package
provides a high-level overview of how to use these services.

+4


source


First of all, what I'm going to say is not about real-time systems, so let's get rid of that right away: if you want to build a real-time system with tight constraints, then Java may not be in the way.

Now, if you are not creating a real-time system, I would advise not to worry too much about the possibility that the GC can slow down your program, delay your program, freeze your program, etc.

Garbage collection on modern garbage collections like java is highly optimized, it runs on a separate branch, it does as much of its work as possible in small chunks, and the chances of you witnessing freezes due to garbage collection are very thin.

On the other hand, in any modern non-real-time system, there are so many different things that can happen that can slow down or temporarily freeze your program (most importantly, paging) that the GC contribution will be negligible and most likely lost in noise.

Amendment

After correcting your question, it turns out that you need to determine if your runtime is experiencing high irregularities in the allocation of computing resources (CPU). This is a much more general problem than GC latency detection. (GC is just one possible source of such delays, and not even one of the first suspects.) So, to address the overall goal, consider the following approach:



Create a separate thread that performs the following operations in a loop:

1. record the current time.
2. sleep for a specific number of milliseconds. (Say, 50.)
3. record the current time again.

      

In a smoothly running system, the difference between the first and second time should be very close to the amount of sleep. If your system is experiencing unevenness, then this time will be very different. Such wild variations persist over a significant period of time means that you have a system that is not running smoothly.

If you are really obsessed with GC hijacking by freezing your program, you can do some memory allocation between steps 2 and 3 above. Presumably if the GC froze your Java VM it will take some time before this memory allocation is done. Trust me it won't happen, but if it gives you peace of mind then go and check it out.

You can also continue developing this method, keeping it in sync with the main logic of your program to ensure that the main logic is alive and well.

+3


source


Based on the pointers above at @ 8484, I created a somewhat more complete sample for registering GC from within the JVM (and thus discovered it). Hope this saves a little :)

package fi.pelam.gclogutil;
import java.lang.management.*;
import java.util.Map;
import javax.management.openmbean.CompositeData;
import javax.management.*;

import com.sun.management.GarbageCollectionNotificationInfo;
import com.sun.management.GcInfo;

public class GcLogUtil {
    static public void startLoggingGc() {
        // http://www.programcreek.com/java-api-examples/index.php?class=javax.management.MBeanServerConnection&method=addNotificationListener
        // https://docs.oracle.com/javase/8/docs/jre/api/management/extension/com/sun/management/GarbageCollectionNotificationInfo.html#GARBAGE_COLLECTION_NOTIFICATION
        for (GarbageCollectorMXBean gcMbean : ManagementFactory.getGarbageCollectorMXBeans()) {
            try {
                ManagementFactory.getPlatformMBeanServer().
                        addNotificationListener(gcMbean.getObjectName(), listener, null,null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    static private NotificationListener listener = new NotificationListener() {
        @Override
        public void handleNotification(Notification notification, Object handback) {
            if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
                // https://docs.oracle.com/javase/8/docs/jre/api/management/extension/com/sun/management/GarbageCollectionNotificationInfo.html
                CompositeData cd = (CompositeData) notification.getUserData();
                GarbageCollectionNotificationInfo gcNotificationInfo = GarbageCollectionNotificationInfo.from(cd);
                GcInfo gcInfo = gcNotificationInfo.getGcInfo();
                System.out.println("GarbageCollection: "+
                        gcNotificationInfo.getGcAction() + " " +
                        gcNotificationInfo.getGcName() +
                        " duration: " + gcInfo.getDuration() + "ms" +
                        " used: " + sumUsedMb(gcInfo.getMemoryUsageBeforeGc()) + "MB" +
                        " -> " + sumUsedMb(gcInfo.getMemoryUsageAfterGc()) + "MB");
            }
        }
    };

    static private long sumUsedMb(Map<String, MemoryUsage> memUsages) {
        long sum = 0;
        for (MemoryUsage memoryUsage : memUsages.values()) {
            sum += memoryUsage.getUsed();
        }
        return sum / (1024 * 1024);
    }
}

      

+1


source


regarding handling any timeout, you can run your task in the future http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html you then create another thread to control the Future start which checks if it's done, if it wasn't done with the timeout you specified, you issue a warning in the log or something.

ExecutorService svc = Executors.newFixedThreadPool( 1 ) ;
        Future<?> submit = svc.submit(r); 

      

// hibernate standby.

if(!submit.isDone())
{
  log.warn("action is not done");
}

      

you can return the response to the task with submit.get

or without a timeout.

0


source







All Articles