我遇到了一个奇怪的问题,即我正在从事的凹槽应用程序成长以消耗更多内存(远远超出了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.
发布评论
评论(1)
如果您阅读它说:
解决方案是调用 flush (您调用System.gc)或重复使用图像,而不是为每个油漆操作重新创建它。
If you read the documentation for VolatileImage it says:
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.