JVM:非主存储器会导致OutofMemoryError,因为垃圾收集器没有运行。我在做什么错?

发布于 2025-01-29 08:07:56 字数 3536 浏览 2 评论 0 原文

我遇到了一个奇怪的问题,即我正在从事的凹槽应用程序成长以消耗更多内存(远远超出了xmx-argument的范围,所以它不可能是堆)这是JVM以两种不同方式之一的行为之一 - 它突然释放了(几乎)它所采用的所有内存,或者它随着欧元的爆发而崩溃。可以通过定期调用system.gc()避免此问题。

似乎发生在我身上的是,尽管分配了越来越多的记忆,但JVM并未称为垃圾收集器。目前尚不清楚一旦计算机耗尽了RAM,并且有时会成功,或者有时是否会在不调用GC的情况下抛出OOME(即使这会违反规格),它是否总是(试图)将其调用。可能会注意到,ResourceMonitor确实不是报告java.exe实例提交更多内存(它停留在〜500MB),但是提交费用却相同。

我要做的唯一要做的事情是让计时器每33ms启动一个新线程,以调用Jcomponent的重新启动()。我听说每个新线程都在堆之外分配了一些内存,因此我怀疑问题可能是从未收集过的记忆,但是我可能是错的(我真的感觉到我在这里的深度,TBH)。

显然,我可以通过定期使用计时器Invoke System.gc(尽管每隔几秒钟一次)来解决问题,但这对我来说似乎很糟糕,我诚实地希望我有东西' m做错了,而不是这是JVM的一些奇怪问题。

我当时正在运行的唯一代码是下面的代码(我删除了一些注释和一些记录到控制台)。当然,还有一整件代码,但是唯一活跃的是,如前所述,计时器调用Repaver()。

//snip: package and imports
    
@groovy.transform.CompileStatic
class MapWindow extends BasicWindow {
//BasicWindow provides a constructor that stores its two arguments as windowX and windowY (and set dimensions accordingly) and creates and stores a basic frame
//It also overrides setVisible() to call frame.setVisibile() as well. It does nothing else.

    int xPos
    int yPos

    MapWindow(int x, int y) {
        super(x, y)
        frame.setTitle("EFG")
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
        frame.pack()
    }

    @Override
    public void paint(Graphics gA) {
        VolatileImage img = createVolatileImage(windowX, windowY)
        Graphics2D g = (Graphics2D) img.getGraphics()

        VolatileImage tmp = getTestImage()

        g.drawImage(tmp, 0, 0, null)
        g.dispose()
        gA.drawImage(img, 0, 0, windowX, windowY, null)

        if (Game.game.rnd.nextInt(100) == 0) {
            //System.gc()  <--If I uncomment this, things work
        }
    }

    VolatileImage getTestImage() {
        VolatileImage img = createVolatileImage(windowX, windowY)
        Graphics2D g = img.createGraphics()

        for (int x = 0; x < tileSet.x; x++) {
            for (int y = 0; y < tileSet.y; y++) {
                g.drawImage(tileSet.images[x][y], x * tileSet.resolution, y * tileSet.resolution, null)
            }
        }

        char[] msg = "test complete".toCharArray()
        for (int x = 0; x < msg.length; x++) {
            char c = msg[x]
            if (!c.isWhitespace()) {
                g.drawImage(tileSet.getImage("symbol.$c"), x * tileSet.resolution, tileSet.resolution * tileSet.y, null)
            }
        }

        g.dispose()

        return img
    }
}

    //Located in a different class, called once during startup. It also subject to a @CompileStatic
    void startTimer() {
        timer = new java.util.Timer()
        int period = config.getInt("framePeriod")
        boolean fixed = config.getBoolean("frameFixedRate")
        if (fixed) {
            timer.scheduleAtFixedRate(new TimerTask() {
                @Override
                public void run() {
                    activeWindow?.frame?.repaint()
                }
            }, period, period)
        } else {
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    activeWindow?.frame?.repaint()
                }
            }, period, period)
        }
    }

如果需要,我可以提供更多的代码/信息,但是我不想通过发布整个程序来堵塞这一点。似乎很可能在这里的某个地方,可能在Paint()或GetTestImage()(或JVM)中。

Windows 7 64位
16GB RAM,无页表
SDK 1.8.0_25(也用13.0.1确认了问题)
Groovy 2.5.8
我使用Intellij Idea,但是如果我构建.jar并独立运行它,也会发生问题。

编辑: Ewramner指出,我应该在(或重复使用)挥发性上调用flush(),因此我接受了该解决方案。如果有人能解释为什么GC不早些时候采取行动,尤其是当它导致JVM与OOME坠毁时,我仍然很感兴趣。

I've encountered a weird issue where the Groovy application I'm working on grows to consume more memory (far beyond the limits of the xmx-argument, so it can't be the heap) until the computer runs out of RAM, at which point the JVM behaves in one of two different ways - it either suddenly frees (almost) all the memory it has taken, or it crashes with an OutOfMemoryError. This problem can be avoided by regularly calling System.gc().

What seems to happen to me is, despite allocating more and more memory, the JVM does not call the Garbage Collector. It is not clear whether it always (tries to) call it once the computer runs out of RAM and only sometimes succeeds, or whether it sometimes throws an OOME without calling the GC (even though that would violate the specifications). It might be interesting to note that the ResourceMonitor does not report the java.exe instance commiting more memory (it stays at ~500MB), but the commit charge goes up all the same.

The only thing I'm doing while this is going on is to have a Timer start a new thread every 33ms to call a repaint() of a JComponent. I have heard that each new thread is allocated some memory outside the heap, so I suspect that the issue may be that that memory is never collected, but I could be wrong (I really feel out of my depth here, TBH).

I could obviously solve things by just having a Timer invoke System.gc() regularly (though no less often than once every few seconds), but that seems very bad practice to me, and I'm honestly hoping that there is something I'm doing wrong, rather than this being some weird issue with the JVM.

The only code I'm running at the time is the one below (I've removed some comments and some logging to the console). There is, of course, a whole bunch more code, but the only thing active is, as mentioned, the Timer calling repaint().

//snip: package and imports
    
@groovy.transform.CompileStatic
class MapWindow extends BasicWindow {
//BasicWindow provides a constructor that stores its two arguments as windowX and windowY (and set dimensions accordingly) and creates and stores a basic frame
//It also overrides setVisible() to call frame.setVisibile() as well. It does nothing else.

    int xPos
    int yPos

    MapWindow(int x, int y) {
        super(x, y)
        frame.setTitle("EFG")
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
        frame.pack()
    }

    @Override
    public void paint(Graphics gA) {
        VolatileImage img = createVolatileImage(windowX, windowY)
        Graphics2D g = (Graphics2D) img.getGraphics()

        VolatileImage tmp = getTestImage()

        g.drawImage(tmp, 0, 0, null)
        g.dispose()
        gA.drawImage(img, 0, 0, windowX, windowY, null)

        if (Game.game.rnd.nextInt(100) == 0) {
            //System.gc()  <--If I uncomment this, things work
        }
    }

    VolatileImage getTestImage() {
        VolatileImage img = createVolatileImage(windowX, windowY)
        Graphics2D g = img.createGraphics()

        for (int x = 0; x < tileSet.x; x++) {
            for (int y = 0; y < tileSet.y; y++) {
                g.drawImage(tileSet.images[x][y], x * tileSet.resolution, y * tileSet.resolution, null)
            }
        }

        char[] msg = "test complete".toCharArray()
        for (int x = 0; x < msg.length; x++) {
            char c = msg[x]
            if (!c.isWhitespace()) {
                g.drawImage(tileSet.getImage("symbol.$c"), x * tileSet.resolution, tileSet.resolution * tileSet.y, null)
            }
        }

        g.dispose()

        return img
    }
}

    //Located in a different class, called once during startup. It also subject to a @CompileStatic
    void startTimer() {
        timer = new java.util.Timer()
        int period = config.getInt("framePeriod")
        boolean fixed = config.getBoolean("frameFixedRate")
        if (fixed) {
            timer.scheduleAtFixedRate(new TimerTask() {
                @Override
                public void run() {
                    activeWindow?.frame?.repaint()
                }
            }, period, period)
        } else {
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    activeWindow?.frame?.repaint()
                }
            }, period, period)
        }
    }

I can provide more code/info if needed, but I didn't want to clog this up by essentially posting the entire program. It seems exceedingly likely the issue is somewhere here, probably in paint() or getTestImage() (or the JVM).

Windows 7 64-bit
16GB RAM, no pagefile
SDK 1.8.0_25 (issue also confirmed with 13.0.1)
Groovy 2.5.8
I use IntelliJ IDEA, but the issue also occurs if I build a .jar and run it independently.

EDIT:
ewramner has pointed out that I should call flush() on (or reuse) the VolatileImages, so I've accepted that as the solution. I'd still be interested if anyone could explain why the GC doesn't act earlier, though, especially if it leads to the JVM crashing with an OOME.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

许久 2025-02-05 08:07:56

如果您阅读它说:

创建挥发性图表时,可以分配有限的系统资源,例如视频内存(VRAM)以支持图像。如果不再使用挥发性图形对象,则可能会收集垃圾,并且将返回这些系统资源,但是此过程不会在保证的时间发生。创建许多挥发性对象的应用程序(例如,调整大小窗口可能会随着尺寸变化而迫使其后缓冲区的重建)可能会耗尽新的volatileimage对象的最佳系统资源,仅仅是因为尚未从系统中删除旧对象。

解决方案是调用 flush (您调用System.gc)或重复使用图像,而不是为每个油漆操作重新创建它。

If you read the documentation for VolatileImage it says:

When a VolatileImage object is created, limited system resources such as video memory (VRAM) may be allocated in order to support the image. When a VolatileImage object is no longer used, it may be garbage-collected and those system resources will be returned, but this process does not happen at guaranteed times. Applications that create many VolatileImage objects (for example, a resizing window may force recreation of its back buffer as the size changes) may run out of optimal system resources for new VolatileImage objects simply because the old objects have not yet been removed from the system.

The solution is to call flush (where you call System.gc) or perhaps to reuse the image instead of re-creating it for every paint operation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文