如何使JVM挂起一段时间?

3

我正在尝试在本地环境中模拟垃圾回收。有没有办法让JVM挂起5分钟?


2
@duffymo 对于在执行过程中生成的其他JVM线程呢?它们不会处于睡眠状态。 - Kon
什么是'挂起'的意思?你可以使用Thread.sleep()命令暂停当前线程。 - Brett Walker
当JVM处理垃圾回收时会发生什么?我正在本地环境中尝试相同的情况。Thread.sleep只能让一个线程休眠,那其他线程呢? - Seeta Ramayya Vadali
我知道这不是你的问题,但过去我曾看到通过调整垃圾回收参数来解决与GC相关的问题。正确的参数可以产生很大的差异。例如(这是2010年的例子,但你可以得到一些想法):http://www.tikalk.com/java/garbage-collection-serial-vs-parallel-vs-concurrent-mark-sweep/ - Mecon
5个回答

5
如果你正在使用Linux系统(大多数其他的UNIX系统也有此功能),可以执行以下操作:
pkill -STOP java; sleep $(( 5 * 60 )); pkill -CONT java

暂停所有Java进程5分钟。

pkill -TSTP java; sleep $(( 5 * 60 )); pkill -CONT java

略微减少了侵入性。

但这不会像垃圾收集器断开一样。


这听起来是个非常好的主意。我会在周一尝试一下。非常感谢你的帮助。 - Seeta Ramayya Vadali
我觉得它不会按照你的需求工作...通过停止进程来模拟GC暂停对你没有任何帮助,我个人认为。相反,可以模拟内存泄漏。 - Has QUIT--Anony-Mousse
在GC期间,JVM将使用所有可用的处理器,因此这不是一个很好的长时间GC模拟。 - Ivan Mamontov

3
你可以在几乎所有的HotSpot JVM上模拟非常长的停顿事件。 HotSpot不会在计数整数循环中放置安全点,因为它假定它们将以足够快的速度终止(在这种情况下,服务器编译器将生成更优化的循环代码)。即使是停止整个世界也必须等待这个循环完成。在这种方法中,我们有一个非常紧密的循环,它进行小但昂贵的计算而没有安全点轮询。
public static double slowpoke(int iterations) {
    double d = 0;
    for (int j = 1; j < iterations; j++) {
        d += Math.log(Math.E * j);
    }
    return d;
}

例如,您可以使用此代码:
public class SafepointTest {

    public static double slowpoke(int iterations) {
        double d = 0;
        for (int j = 1; j < iterations; j++) {
            d += Math.log(Math.E * j);
        }
        return d;
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                double sideEffect = 0;
                for (int i = 0; i < 10000; i++) {
                    sideEffect = slowpoke(999999999);
                }
                System.out.println("result = " + sideEffect);
            }
        };
        thread.start();

        new Thread(){
            @Override
            public void run() {
                long timestamp = System.currentTimeMillis();
                while (true){
                    System.out.println("Delay " + (System.currentTimeMillis() - timestamp));
                    timestamp = System.currentTimeMillis();
                    //trigger stop-the-world 
                    System.gc();
                }
            }
        }.start();
        thread.join();
    }
}

结果是:
Delay 5
Delay 4
Delay 30782
Delay 21819
Delay 21966
Delay 22812
Delay 22264
Delay 21988

为了增加延迟,只需更改slowpoke(int iterations)函数的参数值。
以下是有用的诊断命令:
  • -XX:+PrintGCApplicationStoppedTime,这将在GC日志中实际报告所有安全点的暂停时间。不幸的是,此选项的输出缺少时间戳。
  • -XX:+PrintSafepointStatistics –XX:PrintSafepointStatisticsCount=1 这两个选项将强制JVM在每个安全点后报告原因和时间。

0

开发构建的热点JVM有一个名为UseLoopSafepoints的选项。

使用此选项,您可以执行以下操作:

  1. 禁用循环安全点
  2. 编写一个自旋等待循环来递增一个long。这样可以防止虚拟机在循环退出之前达到安全点
  3. 另一个线程启动GC或调用Thread.getAllStackTraces(),这将触发一个安全点

通过这些操作,所有的虚拟机线程都将被挂起,因为它们试图达到一个安全点,但是由于自旋循环的阻塞,安全点将被阻塞。 实际上,这应该类似于由于过多的GC工作而导致的长时间GC安全点。

注意:我还没有测试过这个。


我为任何HotSpot JVM提供了一个有用的示例。 - Ivan Mamontov

0

-1

JVM GC的设计是为了应用程序不会卡住,但据我理解,您希望所有线程都睡眠:

Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
for(Thread thread : threadSet) {
    if(thread == Thread.currentThread) //Make sure this one doesn't sleep.
        continue;
    thread.sleep({{Time in ms}});
}
Thread.sleep({{Time in ms}}); //Now we can make this thread sleep.

完全未经测试。 您还可以强制进行垃圾回收:System.gc(); 编辑:经过一些研究,我认为这是不可能的: 如何使另一个线程在Java中休眠

你不能强制进行垃圾回收;这只是一个建议。 - duffymo
1TB的内存?垃圾回收器只会释放这么多。 - Thomas Nairn
thread.sleep()不起作用,因为你实际上没有调用一个实例方法,而是调用了一个静态方法(这应该会引发编译器警告)。thread.suspend()可能会起作用。 - the8472
@SeetaRamayyaVadali,1TB的内存是非常大的。在良好条件下,热点最先进的收集器可以处理大约100GB的内存(取决于内存带宽和可用线程数),而不会出现过长的暂停时间。如果你需要处理如此巨大的堆内存,你可能想要了解Azul的Zing虚拟机和他们的无暂停收集器。 - the8472
@the8472 我们知道并且我们正在考虑Azul部分。 - Seeta Ramayya Vadali

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接