外部进程是否可以强制JVM抛出一个;java.lang.OutOfMemoryError:超出GC开销限制;

外部进程是否可以强制JVM抛出一个;java.lang.OutOfMemoryError:超出GC开销限制;,java,jvm,out-of-memory,Java,Jvm,Out Of Memory,在同一操作系统和硬件上运行的另一个进程(java Order not)是否可能触发 java.lang.OutOfMemoryError: GC overhead limit exceeded 通过消耗RAM和/或大量CPU负载,或者通过其他方式 从 详细消息“超出了GC开销限制”表明垃圾收集器一直在运行,Java程序进展非常缓慢。在垃圾收集之后,如果Java进程花费大约98%的时间进行垃圾收集,并且恢复的堆少于2% 我知道这是时间敏感的。然而,对于这98%的人所指的内容,它似乎缺乏适当的规

在同一操作系统和硬件上运行的另一个进程(java Order not)是否可能触发

java.lang.OutOfMemoryError: GC overhead limit exceeded
通过消耗RAM和/或大量CPU负载,或者通过其他方式


详细消息“超出了GC开销限制”表明垃圾收集器一直在运行,Java程序进展非常缓慢。在垃圾收集之后,如果Java进程花费大约98%的时间进行垃圾收集,并且恢复的堆少于2%

我知道这是时间敏感的。然而,对于这98%的人所指的内容,它似乎缺乏适当的规范


编辑20201008:添加了

是的,但这在现实生活中不太可能

要使JVM抛出
java.lang.OutOfMemoryError:GC开销超出了限制
,必须满足两个条件:

  • GC循环回收的堆空间小于
    gcheapfleelimit
    (2%)
  • JVM在GC上花费的时间超过了GCTimeLimit(98%)
  • 外部进程几乎不能影响第一个条件,除非它直接与目标应用程序交互。这意味着JVM应该已经处于“几乎内存不足”状态,错误才会发生

    另一个过程可能影响的是时间安排。如果这个进程大量使用共享的CPU资源,那么它可以通过与JVM竞争CPU时间来降低GC的运行速度。较慢的GC意味着更长的GC周期,因此花费在GC中的时间百分比更高

    当另一个进程使JVM抛出超出GC开销限制时,我能够创建一个人工示例,但这确实很棘手

    考虑下面的Java程序

    import java.util.ArrayList;
    
    public class GCOverheadLimit {
        static ArrayList<Object> garbage = new ArrayList<>();
        static byte[] reserve = new byte[100_000];
    
        static void fillHeap() {
            try {
                while (true) {
                    garbage.add(new byte[10_000]);
                }
            } catch (OutOfMemoryError e) {
                reserve = null;
            }
        }
    
        public static void main(String[] args) throws Exception {
            System.out.println("Filling heap");
            fillHeap();
    
            System.out.println("Starting GC loop");
            while (true) {
                garbage.add(new byte[10_000]);
                garbage.remove(garbage.size() - 1);
                Thread.sleep(20);
            }
        }
    }
    
    我在一个有CPU配额的cgroup中运行这个程序。我的机器有4个内核,但我让JVM每100毫秒只使用200毫秒的CPU时间

    mkdir /sys/fs/cgroup/cpu/test
    echo 200000 > /sys/fs/cgroup/cpu/test/cpu.cfs_quota_us
    echo $JAVA_PID > /sys/fs/cgroup/cpu/test/cgroup.procs
    
    到目前为止,该计划运作良好。现在,我在同一个cgroup中运行一个或两个CPU燃烧进程:

    sha1sum /dev/zero &
    echo $! > /sys/fs/cgroup/cpu/test/cgroup.procs
    
    由于超出了配额,操作系统开始限制进程。GC次数增加,JVM最终抛出
    java.lang.OutOfMemoryError:超出了GC开销限制


    注意:重现问题需要仔细选择参数(堆大小、延迟、配额)。其他机器和其他环境的参数将不同。我的观点是-这个问题在理论上是可能的,但在实践中可能永远不会发生,因为有太多的因素需要匹配在一起。

    当然可以,检查一下如何简单地重现这个问题error@MichalDrozd这个问题是关于触发错误的另一个过程哦,伙计!我尝试了同样的事情(非常接近这个),但没有成功,这非常非常好。但这是否意味着另一个过程会触发错误?这看起来确实像是另一个进程影响了JVM。我不确定这只是我的问题,还是这里的问题是可以相互改变的。@Eugene对我来说,“强制JVM抛出错误”和“触发错误”是可以互换的。事实上,这是一个很好的例子。因此,基本上这归结为机器上的一些沉重的CPU(过度)负载,同时GC承受压力。在一台更大的机器上,即30+HT内核和300+GB的RAM,并行GC本身可能会施加相当多的CPU负载。竞争进程需要多少额外的CPU负载才能“扭转局面”?这仅仅是因为并行GC(大概)将其时间计算基于已用的实时/墙壁时间而不是CPU时间吗?@Andreas Right,GC开销策略计算暂停时间,而不是CPU时间。我不能说大约有30多个内核,但我的观点是——尽管理论上可能是另一个进程导致JVM抛出“GC开销限制”错误,但实际上这并不重要:当可用内存非常低时,无论如何都会有问题。
    sha1sum /dev/zero &
    echo $! > /sys/fs/cgroup/cpu/test/cgroup.procs